From df5032f86fc26e22ff7c08f139e7c7d27a79facc Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Tue, 27 Aug 2024 15:45:13 +0200 Subject: [PATCH 01/10] Add Windows CI. Compilation of zim-tools is broken on Windows. But let's setup the CI to validate the PR. --- .github/workflows/ci.yml | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f5dbcbc9..2aa7bdef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,51 @@ jobs: cd build ninja + Windows: + runs-on: windows-2022 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup python 3.10 + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install packages + run: + choco install pkgconfiglite ninja + + - name: Install python modules + run: pip3 install meson + + - name: Setup MSVC compiler + uses: bus1/cabuild/action/msdevshell@v1 + with: + architecture: x64 + + - name: Install dependencies + uses: kiwix/kiwix-build/actions/dl_deps_archive@main + with: + target_platform: win-x86_64-static + + - name: Compile + shell: cmd + run: | + set PKG_CONFIG_PATH=%cd%\BUILD_win-amd64\INSTALL\lib\pkgconfig + set CPPFLAGS=-I%cd%\BUILD_win-amd64\INSTALL\include + meson.exe setup . build -Dstatic-linkage=true --buildtype=debug + cd build + ninja.exe + + - name: Test + shell: cmd + run: | + cd build + meson.exe test --verbose + env: + WAIT_TIME_FACTOR_TEST: 10 Linux: strategy: From dcf0ca22519c30d354580bbe9fa0f6f793bbabe6 Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Mon, 26 Aug 2024 15:10:31 +0200 Subject: [PATCH 02/10] Correctly compile tests if build without writer (on Windows) --- test/meson.build | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/test/meson.build b/test/meson.build index dcb5e580..704aba53 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,12 +1,20 @@ gtest_dep = dependency('gtest', main:true, fallback:['gtest', 'gtest_main_dep'], required:false) + +test_deps = [gtest_dep, libzim_dep, icu_dep] tests = [ 'metadata-test', - 'tools-test', - 'zimwriterfs-zimcreatorfs', 'zimcheck-test' ] +if with_writer + tests += [ + 'tools-test', + 'zimwriterfs-zimcreatorfs' + ] + test_deps += [gumbo_dep, magic_dep, zlib_dep] +endif + zimwriter_srcs = [ '../src/zimwriterfs/tools.cpp', '../src/zimwriterfs/zimcreatorfs.cpp', '../src/tools.cpp'] @@ -21,7 +29,7 @@ if gtest_dep.found() and not meson.is_cross_build() foreach test_name : tests test_exe = executable(test_name, [test_name+'.cpp'] + tests_src_map[test_name], - dependencies : [gtest_dep, libzim_dep, gumbo_dep, magic_dep, zlib_dep, icu_dep], + dependencies : test_deps, include_directories: inc, build_rpath : '$ORIGIN') From d6d67c22d05438f9ea86c132908e173bf93eb77c Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Mon, 26 Aug 2024 15:42:17 +0200 Subject: [PATCH 03/10] Do not set gcc specific option. Meson already handle werror and wall, let's use it. --- meson.build | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/meson.build b/meson.build index fc44c743..5c9a6d2d 100644 --- a/meson.build +++ b/meson.build @@ -1,9 +1,7 @@ project('zim-tools', ['c', 'cpp'], version : '3.4.2', license : 'GPLv3+', - default_options : ['c_std=c11', 'cpp_std=c++17', 'werror=true']) - -add_global_arguments(['-Werror', '-Wall'], language:'cpp') + default_options : ['c_std=c11', 'cpp_std=c++17', 'werror=true', 'warning_level=1']) add_global_arguments('-DVERSION="@0@"'.format(meson.project_version()), language : 'cpp') From 72fabb308dbd34233799d94ad1b09f4b925672ed Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Mon, 26 Aug 2024 16:15:48 +0200 Subject: [PATCH 04/10] `ssize_t` is not defined on Windows. And anyway the argument is storing the output `std::string::size` which is a `size_t` --- src/zimdump.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/zimdump.cpp b/src/zimdump.cpp index 08e95dc0..e6d68df6 100644 --- a/src/zimdump.cpp +++ b/src/zimdump.cpp @@ -229,7 +229,7 @@ int ZimDumper::listEntriesByNamespace(const std::string ns, bool details) return ret; } -void write_to_error_directory(const std::string& base, const std::string relpath, const char *content, ssize_t size) +void write_to_error_directory(const std::string& base, const std::string relpath, const char *content, size_t size) { createdir(ERRORSDIR, base); std::string url = relpath; @@ -247,7 +247,7 @@ void write_to_error_directory(const std::string& base, const std::string relpath std::cerr << "Error opening file " + fullpath + " cause: " + ::strerror(errno) << std::endl; return ; } - if (write(fd, content, size) != size) { + if ((size_t) write(fd, content, size) != size) { close(fd); std::cerr << "Failed writing: " << fullpath << " - " << ::strerror(errno) << std::endl; } @@ -266,7 +266,7 @@ void write_to_error_directory(const std::string& base, const std::string relpath #endif } -inline void write_to_file(const std::string &base, const std::string& path, const char* data, ssize_t size) { +inline void write_to_file(const std::string &base, const std::string& path, const char* data, size_t size) { std::string fullpath = base + path; #ifdef _WIN32 std::wstring wpath = utf8ToUtf16(fullpath); @@ -279,7 +279,7 @@ inline void write_to_file(const std::string &base, const std::string& path, cons write_to_error_directory(base, path, data, size); return ; } - if (write(fd, data, size) != size) { + if ((size_t) write(fd, data, size) != size) { write_to_error_directory(base, path, data, size); } close(fd); From 37aa9ef99f50f885543b9d9c18bda718f77b39f8 Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Mon, 26 Aug 2024 17:46:28 +0200 Subject: [PATCH 05/10] Remove unused import of dirent.h --- src/tools.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools.cpp b/src/tools.cpp index 95ce7a18..a5c8c160 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -20,7 +20,6 @@ #include "tools.h" -#include #include #include #include From a124217990586c6b00fb01bcb170a838b1413a24 Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Tue, 27 Aug 2024 18:39:29 +0200 Subject: [PATCH 06/10] Correctly convert char to unsigned char before calling isprint. As specified in isprint documentation[1], it is undefined behavior if input cannot represented as unsigned char. Let's convert it as suggested in documentation. [1] https://en.cppreference.com/w/cpp/string/byte/isprint --- src/metadata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/metadata.cpp b/src/metadata.cpp index 52438bab..06c299c4 100644 --- a/src/metadata.cpp +++ b/src/metadata.cpp @@ -125,7 +125,7 @@ std::string escapeNonPrintableChars(const std::string& s) std::ostringstream os; os << std::hex; for (const char c : s) { - if (std::isprint(c)) { + if (std::isprint(static_cast(c))) { os << c; } else { const unsigned int charVal = static_cast(c); From d524aa8041739d7af193da680ea11be22c1f4828 Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Tue, 27 Aug 2024 19:21:42 +0200 Subject: [PATCH 07/10] Fix getTextLength on Windows As explained in comment, I don't know the root cause of all of this. If you have an idea you are welcomed !!!! --- src/metadata.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/metadata.cpp b/src/metadata.cpp index 06c299c4..da58dd4f 100644 --- a/src/metadata.cpp +++ b/src/metadata.cpp @@ -48,7 +48,21 @@ bool searchRegex(const std::string& regexStr, const std::string& text) size_t getTextLength(const std::string& utf8EncodedString) { - return icu::UnicodeString::fromUTF8(utf8EncodedString).length(); + // For some unknown reason implicite convertion from std::string to icu::StringPiece + // is broken on Windows. + // Constructors are definde in stringpiece.h as + // ``` + // StringPiece(const std::string& str) + // : ptr_(str.data()), length_(static_cast(str.size())) { } + // StringPiece(const char* offset, int32_t len) : ptr_(offset), length_(len) { } + // ``` + // However using the first constructor ends with a corrupted StringPiece (wrong ptr) + // and using second one works. Don't ask me why + // This is broken + // icu::StringPiece stringPiece(utf8EncodedString); + // This is not + icu::StringPiece stringPiece(utf8EncodedString.data(), static_cast(utf8EncodedString.size())); + return icu::UnicodeString::fromUTF8(stringPiece).length(); } class MetadataComplexCheckBase From 7745ce71b3de93d32cf31d545f6229103e622c48 Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Tue, 27 Aug 2024 19:23:18 +0200 Subject: [PATCH 08/10] Fix include of unistd.h on Windows --- src/tools.cpp | 2 +- src/zimcheck/zimcheck.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tools.cpp b/src/tools.cpp index a5c8c160..1459eb60 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -38,6 +37,7 @@ #ifdef _WIN32 #define SEPARATOR "\\" #else +#include #define SEPARATOR "/" #endif diff --git a/src/zimcheck/zimcheck.cpp b/src/zimcheck/zimcheck.cpp index 729847e4..2703c923 100644 --- a/src/zimcheck/zimcheck.cpp +++ b/src/zimcheck/zimcheck.cpp @@ -20,7 +20,6 @@ * MA 02110-1301, USA. */ -#include #include #include #include @@ -35,6 +34,10 @@ #include #include +#ifndef _WIN32 +#include +#endif + #include "../progress.h" #include "../version.h" #include "../tools.h" From 86315e0c84ff27576977fcd263fe8dc63af6d9ff Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Wed, 28 Aug 2024 11:19:33 +0200 Subject: [PATCH 09/10] Port zimcheck to docoptcpp We don't have getopt on Windows. Let's move command line parsing to docoptcpp as we already use it. We lost parsing case insensitive options on the way. --- src/zimcheck/meson.build | 2 +- src/zimcheck/zimcheck.cpp | 250 +++++++++++++++----------------------- test/meson.build | 2 +- test/zimcheck-test.cpp | 111 ++++++++--------- 4 files changed, 149 insertions(+), 216 deletions(-) diff --git a/src/zimcheck/meson.build b/src/zimcheck/meson.build index 917e76c1..6871df71 100644 --- a/src/zimcheck/meson.build +++ b/src/zimcheck/meson.build @@ -23,7 +23,7 @@ executable('zimcheck', '../tools.cpp', '../metadata.cpp', include_directories : inc, - dependencies: [libzim_dep, icu_dep, thread_dep], + dependencies: [libzim_dep, icu_dep, thread_dep, docopt_dep], install: true) diff --git a/src/zimcheck/zimcheck.cpp b/src/zimcheck/zimcheck.cpp index 2703c923..28b005fc 100644 --- a/src/zimcheck/zimcheck.cpp +++ b/src/zimcheck/zimcheck.cpp @@ -21,7 +21,7 @@ */ #include -#include +#include #include #include #include @@ -33,6 +33,7 @@ #include #include #include +#include #ifndef _WIN32 #include @@ -43,36 +44,37 @@ #include "../tools.h" #include "checks.h" -void displayHelp() -{ - std::cout<<"\n" - "zimcheck checks the quality of a ZIM file.\n\n" - "Usage: zimcheck [options] zimfile\n" - "options:\n" - "-A , --all run all tests. Default if no flags are given.\n" - "-0 , --empty Empty content\n" - "-C , --checksum Internal CheckSum Test\n" - "-I , --integrity Low-level correctness/integrity checks\n" - "-M , --metadata MetaData Entries\n" - "-F , --favicon Favicon\n" - "-P , --main Main page\n" - "-R , --redundant Redundant data check\n" - "-U , --url_internal URL check - Internal URLs\n" - "-X , --url_external URL check - External URLs\n" - "-D , --details Details of error\n" - "-B , --progress Print progress report\n" - "-J , --json Output in JSON format\n" - "-H , --help Displays Help\n" - "-V , --version Displays software version\n" - "-L , --redirect_loop Checks for the existence of redirect loops\n" - "-W , --threads count of threads to utilize (default: 1)\n" - "examples:\n" - "zimcheck -A wikipedia.zim\n" - "zimcheck --checksum --redundant wikipedia.zim\n" - "zimcheck -F -R wikipedia.zim\n" - "zimcheck -M --favicon wikipedia.zim\n"; - return; -} +static const char USAGE[] = +R"(Zimcheck checks the quality of a ZIM file. + +Usage: + zimcheck [options] [ZIMFILE] + +Options: + -A --all run all tests. Default if no flags are given. + -0 --empty Empty content + -C --checksum Internal CheckSum Test + -I --integrity Low-level correctness/integrity checks + -M --metadata MetaData Entries + -F --favicon Favicon + -P --main Main page + -R --redundant Redundant data check + -U --url_internal URL check - Internal URLs + -X --url_external URL check - External URLs + -D --details Details of error + -B --progress Print progress report + -J --json Output in JSON format + -H --help Displays Help + -V --version Displays software version + -L --redirect_loop Checks for the existence of redirect loops + -W= --threads= count of threads to utilize [default: 1] + +Examples: + zimcheck -A wikipedia.zim + zimcheck --checksum --redundant wikipedia.zim + zimcheck -F -R wikipedia.zim + zimcheck -M --favicon wikipedia.zim)"; + template std::string stringify(const T& x) @@ -82,11 +84,36 @@ std::string stringify(const T& x) return ss.str(); } -int zimcheck (const std::vector& args) -{ - const int argc = args.size(); - const char* const* argv = &args[0]; +int zimcheck(const std::map& args); + +int zimcheck(const std::vector& args) { + std::vector args_string; + bool first = true; + for (auto arg:args) { + if (first) { + first = false; + continue; + } + args_string.emplace_back(arg); + } + docopt::Options parsed_args; + try { + parsed_args = docopt::docopt_parse( + USAGE, + args_string, + false, + false); + } catch (docopt::DocoptArgumentError const& error) { + std::cerr << error.what() << std::endl; + std::cout << USAGE << std::endl; + return 1; + } + return zimcheck(parsed_args); +} + +int zimcheck(const docopt::Options& args) +{ // To calculate the total time taken by the program to run. const auto starttime = std::chrono::steady_clock::now(); @@ -99,140 +126,69 @@ int zimcheck (const std::vector& args) bool error_details = false; bool no_args = true; bool json = false; - bool help = false; int thread_count = 1; std::string filename = ""; ProgressBar progress(1); StatusCode status_code = PASS; - - //Parsing through arguments using getopt_long(). Both long and short arguments are allowed. - optind = 1; // reset getopt_long(), so that zimcheck() works correctly if - // called more than once - opterr = 0; // silence getopt_long() - while (1) - { - static struct option long_options[] = - { - { "all", no_argument, 0, 'A'}, - { "progress", no_argument, 0, 'B'}, - { "empty", no_argument, 0, '0'}, - { "checksum", no_argument, 0, 'C'}, - { "integrity", no_argument, 0, 'I'}, - { "metadata", no_argument, 0, 'M'}, - { "favicon", no_argument, 0, 'F'}, - { "main", no_argument, 0, 'P'}, - { "redundant", no_argument, 0, 'R'}, - { "url_internal", no_argument, 0, 'U'}, - { "url_external", no_argument, 0, 'X'}, - { "details", no_argument, 0, 'D'}, - { "json", no_argument, 0, 'J'}, - { "threads", required_argument, 0, 'w'}, - { "help", no_argument, 0, 'H'}, - { "version", no_argument, 0, 'V'}, - { "redirect_loop",no_argument, 0, 'L'}, - { 0, 0, 0, 0} - }; - int option_index = 0; - int c = getopt_long (argc, const_cast(argv), "ACIJMFPRUXLEDHBVW:acijmfpruxledhbvw:0", - long_options, &option_index); - //c = getopt (argc, argv, "ACMFPRUXED"); - if(c == -1) - break; - switch (c) - { - case 'A': - case 'a': + + for(auto const& arg: args) { + if (arg.first == "--all" && arg.second.asBool()) { run_all = true; no_args = false; - break; - case '0': + } else if (arg.first == "--help" && arg.second.asBool()) { + std::cout << USAGE << std::endl; + return -1; + } else if (arg.first == "--empty" && arg.second.asBool()) { enabled_tests.enable(TestType::EMPTY); no_args = false; - break; - case 'C': - case 'c': + } else if (arg.first == "--checksum" && arg.second.asBool()) { enabled_tests.enable(TestType::CHECKSUM); no_args = false; - break; - case 'I': - case 'i': + } else if (arg.first == "--integrity" && arg.second.asBool()) { enabled_tests.enable(TestType::INTEGRITY); no_args = false; - break; - case 'M': - case 'm': + } else if (arg.first == "--metadata" && arg.second.asBool()) { enabled_tests.enable(TestType::METADATA); no_args = false; - break; - case 'B': - case 'b': - progress.set_progress_report(true); - break; - case 'F': - case 'f': + } else if (arg.first == "--progress") { + progress.set_progress_report(arg.second.asBool()); + } else if (arg.first == "--favicon" && arg.second.asBool()) { enabled_tests.enable(TestType::FAVICON); no_args = false; - break; - case 'P': - case 'p': + } else if (arg.first == "--main" && arg.second.asBool()) { enabled_tests.enable(TestType::MAIN_PAGE); no_args = false; - break; - case 'R': - case 'r': + } else if (arg.first == "--redundant" && arg.second.asBool()) { enabled_tests.enable(TestType::REDUNDANT); no_args = false; - break; - case 'U': - case 'u': + } else if (arg.first == "--url_internal" && arg.second.asBool()) { enabled_tests.enable(TestType::URL_INTERNAL); no_args = false; - break; - case 'X': - case 'x': + } else if (arg.first == "--url_external" && arg.second.asBool()) { enabled_tests.enable(TestType::URL_EXTERNAL); no_args = false; - break; - case 'L': - case 'l': + } else if (arg.first == "--redirect_loop" && arg.second.asBool()) { enabled_tests.enable(TestType::REDIRECT); no_args = false; - break; - case 'D': - case 'd': - error_details = true; - break; - case 'J': - case 'j': - json = true; - break; - case 'W': - case 'w': - thread_count = atoi(optarg); - break; - case 'H': - case 'h': - help=true; - break; - case '?': - std::cerr<<"Unknown option `" << argv[optind-1] << "'\n"; - displayHelp(); - return 1; - case 'V': - case 'v': - printVersions(); - return 0; - default: - abort (); + } else if (arg.first == "--details") { + error_details = arg.second.asBool(); + } else if (arg.first == "--json") { + json = arg.second.asBool(); + } else if (arg.first == "--threads") { + thread_count = arg.second.asLong(); + } else if (arg.first == "ZIMFILE" && arg.second.isString()) { + filename = arg.second.asString(); + } else if (arg.first == "--version" && arg.second.asBool()) { + printVersions(); + return 0; } } - - //Displaying Help for --help argument - if(help) - { - displayHelp(); + + if (filename.empty()) { + std::cerr << "No file provided as argument" << std::endl; + std::cout << USAGE << std::endl; return -1; } @@ -242,22 +198,6 @@ int zimcheck (const std::vector& args) enabled_tests.enableAll(); } - //Obtaining filename from argument list - filename = ""; - for(int i = 0; i < argc; i++) - { - if( (argv[i][0] != '-') && (i != 0)) - { - filename = argv[i]; - } - } - if(filename == "") - { - std::cerr<<"No file provided as argument\n"; - displayHelp(); - return -1; - } - ErrorLogger error(json); error.addInfo("zimcheck_version", std::string(VERSION)); //Tests. diff --git a/test/meson.build b/test/meson.build index 704aba53..5b437c73 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,7 +1,7 @@ gtest_dep = dependency('gtest', main:true, fallback:['gtest', 'gtest_main_dep'], required:false) -test_deps = [gtest_dep, libzim_dep, icu_dep] +test_deps = [gtest_dep, libzim_dep, icu_dep, docopt_dep] tests = [ 'metadata-test', 'zimcheck-test' diff --git a/test/zimcheck-test.cpp b/test/zimcheck-test.cpp index cd5b497a..824efa35 100644 --- a/test/zimcheck-test.cpp +++ b/test/zimcheck-test.cpp @@ -131,41 +131,39 @@ struct CapturedStderr : CapturedStdStream int zimcheck (const std::vector& args); const std::string zimcheck_help_message( - "\n" - "zimcheck checks the quality of a ZIM file.\n\n" - "Usage: zimcheck [options] zimfile\n" - "options:\n" - "-A , --all run all tests. Default if no flags are given.\n" - "-0 , --empty Empty content\n" - "-C , --checksum Internal CheckSum Test\n" - "-I , --integrity Low-level correctness/integrity checks\n" - "-M , --metadata MetaData Entries\n" - "-F , --favicon Favicon\n" - "-P , --main Main page\n" - "-R , --redundant Redundant data check\n" - "-U , --url_internal URL check - Internal URLs\n" - "-X , --url_external URL check - External URLs\n" - "-D , --details Details of error\n" - "-B , --progress Print progress report\n" - "-J , --json Output in JSON format\n" - "-H , --help Displays Help\n" - "-V , --version Displays software version\n" - "-L , --redirect_loop Checks for the existence of redirect loops\n" - "-W , --threads count of threads to utilize (default: 1)\n" - "examples:\n" - "zimcheck -A wikipedia.zim\n" - "zimcheck --checksum --redundant wikipedia.zim\n" - "zimcheck -F -R wikipedia.zim\n" - "zimcheck -M --favicon wikipedia.zim\n" -); +R"(Zimcheck checks the quality of a ZIM file. + +Usage: + zimcheck [options] [ZIMFILE] + +Options: + -A --all run all tests. Default if no flags are given. + -0 --empty Empty content + -C --checksum Internal CheckSum Test + -I --integrity Low-level correctness/integrity checks + -M --metadata MetaData Entries + -F --favicon Favicon + -P --main Main page + -R --redundant Redundant data check + -U --url_internal URL check - Internal URLs + -X --url_external URL check - External URLs + -D --details Details of error + -B --progress Print progress report + -J --json Output in JSON format + -H --help Displays Help + -V --version Displays software version + -L --redirect_loop Checks for the existence of redirect loops + -W= --threads= count of threads to utilize [default: 1] + +Examples: + zimcheck -A wikipedia.zim + zimcheck --checksum --redundant wikipedia.zim + zimcheck -F -R wikipedia.zim + zimcheck -M --favicon wikipedia.zim +)"); TEST(zimcheck, help) { - { - CapturedStdout zimcheck_output; - ASSERT_EQ(-1, zimcheck({"zimcheck", "-h"})); - ASSERT_EQ(zimcheck_help_message, std::string(zimcheck_output)); - } { CapturedStdout zimcheck_output; ASSERT_EQ(-1, zimcheck({"zimcheck", "-H"})); @@ -182,11 +180,6 @@ TEST(zimcheck, version) { std::string version = "zim-tools " + std::string(VERSION); - { - CapturedStdout zimcheck_output; - ASSERT_EQ(0, zimcheck({"zimcheck", "-v"})); - ASSERT_EQ(version, getLine(std::string(zimcheck_output))); - } { CapturedStdout zimcheck_output; ASSERT_EQ(0, zimcheck({"zimcheck", "-V"})); @@ -261,7 +254,7 @@ TEST(zimcheck, integrity_goodzimfile) ); test_zimcheck_single_option( - {"-i", "-I", "--integrity"}, + {"-I", "--integrity"}, GOOD_ZIMFILE, 0, expected_output, @@ -281,7 +274,7 @@ TEST(zimcheck, checksum_goodzimfile) ); test_zimcheck_single_option( - {"-c", "-C", "--checksum"}, + {"-C", "--checksum"}, GOOD_ZIMFILE, 0, expected_output, @@ -301,7 +294,7 @@ TEST(zimcheck, metadata_goodzimfile) ); test_zimcheck_single_option( - {"-m", "-M", "--metadata"}, + {"-M", "--metadata"}, GOOD_ZIMFILE, 0, expected_output, @@ -321,7 +314,7 @@ TEST(zimcheck, favicon_goodzimfile) ); test_zimcheck_single_option( - {"-f", "-F", "--favicon"}, + {"-F", "--favicon"}, GOOD_ZIMFILE, 0, expected_output, @@ -341,7 +334,7 @@ TEST(zimcheck, mainpage_goodzimfile) ); test_zimcheck_single_option( - {"-p", "-P", "--main"}, + {"-P", "--main"}, GOOD_ZIMFILE, 0, expected_output, @@ -363,8 +356,8 @@ TEST(zimcheck, article_content_goodzimfile) test_zimcheck_single_option( { "-0", "--empty", // Any of these options triggers - "-u", "-U", "--url_internal", // checking of the article contents. - "-x", "-X", "--url_external" // For a good ZIM file there is no + "-U", "--url_internal", // checking of the article contents. + "-X", "--url_external" // For a good ZIM file there is no }, // difference in the output. GOOD_ZIMFILE, 0, @@ -387,7 +380,7 @@ TEST(zimcheck, redundant_articles_goodzimfile) ); test_zimcheck_single_option( - {"-r", "-R", "--redundant"}, + {"-R", "--redundant"}, GOOD_ZIMFILE, 0, expected_output, @@ -407,7 +400,7 @@ TEST(zimcheck, redirect_loop_goodzimfile) ); test_zimcheck_single_option( - {"-l", "-L", "--redirect_loop"}, + {"-L", "--redirect_loop"}, GOOD_ZIMFILE, 0, expected_output, @@ -442,7 +435,7 @@ TEST(zimcheck, nooptions_goodzimfile) TEST(zimcheck, all_checks_goodzimfile) { test_zimcheck_single_option( - {"-a", "-A", "--all"}, + {"-A", "--all"}, GOOD_ZIMFILE, 0, ALL_CHECKS_OUTPUT_ON_GOODZIMFILE, @@ -455,8 +448,8 @@ TEST(zimcheck, invalid_option) { CapturedStdout zimcheck_output; CapturedStderr zimcheck_stderr; - ASSERT_EQ(1, zimcheck({"zimcheck", "-z", GOOD_ZIMFILE})); - ASSERT_EQ("Unknown option `-z'\n", std::string(zimcheck_stderr)); + ASSERT_EQ(1, zimcheck({"zimcheck", "-Z", GOOD_ZIMFILE})); + ASSERT_EQ("Unexpected argument: -Z, data/zimfiles/good.zim\n", std::string(zimcheck_stderr)); ASSERT_EQ(zimcheck_help_message, std::string(zimcheck_output)); } } @@ -467,7 +460,7 @@ TEST(zimcheck, invalid_long_option) CapturedStdout zimcheck_output; CapturedStderr zimcheck_stderr; ASSERT_EQ(1, zimcheck({"zimcheck", "--oops", GOOD_ZIMFILE})); - ASSERT_EQ("Unknown option `--oops'\n", std::string(zimcheck_stderr)); + ASSERT_EQ("Unexpected argument: --oops, data/zimfiles/good.zim\n", std::string(zimcheck_stderr)); ASSERT_EQ(zimcheck_help_message, std::string(zimcheck_output)); } } @@ -523,7 +516,7 @@ TEST(zimcheck, bad_checksum) ); test_zimcheck_single_option( - {"-c", "-C", "--checksum"}, + {"-C", "--checksum"}, BAD_CHECKSUM_ZIMFILE, 1, expected_output, @@ -549,7 +542,7 @@ TEST(zimcheck, metadata_poorzimfile) ); test_zimcheck_single_option( - {"-m", "-M", "--metadata"}, + {"-M", "--metadata"}, POOR_ZIMFILE, 1, expected_stdout, @@ -571,7 +564,7 @@ TEST(zimcheck, favicon_poorzimfile) ); test_zimcheck_single_option( - {"-f", "-F", "--favicon"}, + {"-F", "--favicon"}, POOR_ZIMFILE, 1, expected_stdout, @@ -593,7 +586,7 @@ TEST(zimcheck, mainpage_poorzimfile) ); test_zimcheck_single_option( - {"-p", "-P", "--main"}, + {"-P", "--main"}, POOR_ZIMFILE, 1, expected_stdout, @@ -642,7 +635,7 @@ TEST(zimcheck, internal_url_check_poorzimfile) ); test_zimcheck_single_option( - {"-u", "-U", "--url_internal"}, + {"-U", "--url_internal"}, POOR_ZIMFILE, 1, expected_stdout, @@ -666,7 +659,7 @@ TEST(zimcheck, external_url_check_poorzimfile) ); test_zimcheck_single_option( - {"-x", "-X", "--url_external"}, + {"-X", "--url_external"}, POOR_ZIMFILE, 1, expected_stdout, @@ -690,7 +683,7 @@ TEST(zimcheck, redundant_poorzimfile) ); test_zimcheck_single_option( - {"-r", "-R", "--redundant"}, + {"-R", "--redundant"}, POOR_ZIMFILE, 0, expected_stdout, @@ -717,7 +710,7 @@ TEST(zimcheck, redirect_loop_poorzimfile) ); test_zimcheck_single_option( - {"-l", "-L", "--redirect_loop"}, + {"-L", "--redirect_loop"}, POOR_ZIMFILE, 1, expected_output, @@ -784,7 +777,7 @@ TEST(zimcheck, nooptions_poorzimfile) TEST(zimcheck, all_checks_poorzimfile) { test_zimcheck_single_option( - {"-a", "-A", "--all"}, + {"-A", "--all"}, POOR_ZIMFILE, 1, ALL_CHECKS_OUTPUT_ON_POORZIMFILE, From 5a8c6df6fcd2634ae4223eeba56481538c450745 Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Fri, 30 Aug 2024 14:04:16 +0200 Subject: [PATCH 10/10] Support older version of docopt. Older version of docopt doesn't define Options. Let's define it using `using` syntax (as done in recent version of docoptcpp) --- src/zimcheck/zimcheck.cpp | 7 +++++-- src/zimdump.cpp | 4 +++- src/zimsplit.cpp | 8 ++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/zimcheck/zimcheck.cpp b/src/zimcheck/zimcheck.cpp index 28b005fc..fca2f351 100644 --- a/src/zimcheck/zimcheck.cpp +++ b/src/zimcheck/zimcheck.cpp @@ -76,6 +76,9 @@ R"(Zimcheck checks the quality of a ZIM file. zimcheck -M --favicon wikipedia.zim)"; +// Older version of docopt doesn't define Options +using Options = std::map; + template std::string stringify(const T& x) { @@ -97,7 +100,7 @@ int zimcheck(const std::vector& args) { args_string.emplace_back(arg); } - docopt::Options parsed_args; + Options parsed_args; try { parsed_args = docopt::docopt_parse( USAGE, @@ -112,7 +115,7 @@ int zimcheck(const std::vector& args) { return zimcheck(parsed_args); } -int zimcheck(const docopt::Options& args) +int zimcheck(const Options& args) { // To calculate the total time taken by the program to run. const auto starttime = std::chrono::steady_clock::now(); diff --git a/src/zimdump.cpp b/src/zimdump.cpp index e6d68df6..27b7dcbd 100644 --- a/src/zimdump.cpp +++ b/src/zimdump.cpp @@ -386,7 +386,9 @@ Return value: See DIR/dump_errors.log for the listing of the errors. )"; -typedef std::map Options; + +// Older version of docopt doesn't define Options +using Options = std::map; int subcmdInfo(ZimDumper &app, Options &args) { diff --git a/src/zimsplit.cpp b/src/zimsplit.cpp index bc2ea474..eb9440b4 100644 --- a/src/zimsplit.cpp +++ b/src/zimsplit.cpp @@ -180,10 +180,10 @@ int main(int argc, char* argv[]) { std::ostringstream versions; printVersions(versions); - std::map args = docopt::docopt(USAGE, - {argv + 1, argv + argc}, - true, - versions.str()); + auto args = docopt::docopt(USAGE, + {argv + 1, argv + argc}, + true, + versions.str()); std::string prefix = args[""].asString(); if (args["--prefix"])