#include "3rd-party/catch.hpp" #include #include "cliargsparser.h" #include "test_helpers/envvar.h" #include "test_helpers/opts.h" #include "test_helpers/stringmaker/optional.h" #include "test_helpers/tempdir.h" using namespace newsboat; TEST_CASE( "Asks to print usage info and exit with failure if unknown option is " "provided", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.should_print_usage()); REQUIRE(args.return_code() == EXIT_FAILURE); }; SECTION("Example No.1") { check({"newsboat", "--some-unknown-option"}); } SECTION("Example No.2") { check({"newsboat", "-s"}); } SECTION("Example No.3") { check({"newsboat", "-m ix"}); } SECTION("Example No.4") { check({"newsboat", "-wtf"}); } } TEST_CASE( "Sets `do_import` and `importfile` if -i/--import-from-opml is " "provided", "[CliArgsParser]") { const std::string filename("blogroll.opml"); auto check = [&filename](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.do_import()); REQUIRE(args.importfile() == filename); }; SECTION("-i") { check({"newsboat", "-i", filename}); } SECTION("--import-from-opml") { check({"newsboat", "--import-from-opml=" + filename}); } } TEST_CASE("Resolves tilde to homedir in -i/--import-from-opml", "[CliArgsParser]") { test_helpers::TempDir tmp; test_helpers::EnvVar home("HOME"); home.set(tmp.get_path()); const std::string filename("feedlist.opml"); const std::string arg = std::string("~/") + filename; auto check = [&filename, &tmp](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.do_import()); REQUIRE(args.importfile() == tmp.get_path() + filename); }; SECTION("-i") { check({"newsboat", "-i", arg}); } SECTION("--import-from-opml") { check({"newsboat", "--import-from-opml", arg}); } } TEST_CASE( "Asks to print usage and exit with failure if both " "-i/--import-from-opml and -e/--export-to-opml are provided", "[CliArgsParser]") { const std::string importf("import.opml"); const std::string exportf("export.opml"); auto check = [](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.should_print_usage()); REQUIRE(args.return_code() == EXIT_FAILURE); }; SECTION("-i first") { check({"newsboat", "-i", importf, "-e", exportf}); } SECTION("-e first") { check({"newsboat", "-e", exportf, "-i", importf}); } } TEST_CASE("Sets `refresh_on_start` if -r/--refresh-on-start is provided", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.refresh_on_start()); }; SECTION("-r") { check({"newsboat", "-r"}); } SECTION("--refresh-on-start") { check({"newsboat", "--refresh-on-start"}); } } TEST_CASE("Requests silent mode if -e/--export-to-opml is provided", "[CliArgsParser]") { const test_helpers::Opts opts{"newsboat", "-e"}; CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.silent()); } TEST_CASE("Sets `do_export` if -e/--export-to-opml is provided", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.do_export()); }; SECTION("-e") { check({"newsboat", "-e"}); } SECTION("--export-to-opml") { check({"newsboat", "--export-to-opml"}); } } TEST_CASE("Asks to print usage and exit with success if -h/--help is provided", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.should_print_usage()); REQUIRE(args.return_code() == EXIT_SUCCESS); }; SECTION("-h") { check({"newsboat", "-h"}); } SECTION("--help") { check({"newsboat", "--help"}); } } TEST_CASE( "Sets `url_file` and `using_nonstandard_configs` if -u/--url-file " "is provided", "[CliArgsParser]") { const std::string filename("urlfile"); auto check = [&filename](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.url_file() == filename); REQUIRE(args.using_nonstandard_configs()); }; SECTION("-u") { check({"newsboat", "-u", filename}); } SECTION("--url-file") { check({"newsboat", "--url-file=" + filename}); } } TEST_CASE("Resolves tilde to homedir in -u/--url-file", "[CliArgsParser]") { test_helpers::TempDir tmp; test_helpers::EnvVar home("HOME"); home.set(tmp.get_path()); const std::string filename("urlfile"); const std::string arg = std::string("~/") + filename; auto check = [&filename, &tmp](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.url_file() == tmp.get_path() + filename); }; SECTION("-u") { check({"newsboat", "-u", arg}); } SECTION("--url-file") { check({"newsboat", "--url-file", arg}); } } TEST_CASE( "Sets `cache_file`, `lock_file`, and `using_nonstandard_configs` " "if -c/--cache-file is provided", "[CliArgsParser]") { const std::string filename("cache.db"); auto check = [&filename](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.cache_file() == filename); REQUIRE(args.lock_file() == filename + ".lock"); REQUIRE(args.using_nonstandard_configs()); }; SECTION("-c") { check({"newsboat", "-c", filename}); } SECTION("--cache-file") { check({"newsboat", "--cache-file=" + filename}); } } TEST_CASE("Supports combined short options", "[CliArgsParser]") { const std::string filename("cache.db"); test_helpers::Opts opts = {"newsboat", "-vc", filename}; CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.cache_file() == filename); REQUIRE(args.lock_file() == filename + ".lock"); REQUIRE(args.using_nonstandard_configs()); REQUIRE(args.show_version() == 1); } TEST_CASE("Supports combined short option and value", "[CliArgsParser]") { const std::string filename("cache.db"); test_helpers::Opts opts = {"newsboat", "-c" + filename}; CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.cache_file() == filename); REQUIRE(args.lock_file() == filename + ".lock"); REQUIRE(args.using_nonstandard_configs()); } TEST_CASE("Supports `=` between short option and value", "[CliArgsParser]") { const std::string filename("cache.db"); test_helpers::Opts opts = {"newsboat", "-c=" + filename}; CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.cache_file() == filename); REQUIRE(args.lock_file() == filename + ".lock"); REQUIRE(args.using_nonstandard_configs()); } TEST_CASE("Resolves tilde to homedir in -c/--cache-file", "[CliArgsParser]") { test_helpers::TempDir tmp; test_helpers::EnvVar home("HOME"); home.set(tmp.get_path()); const std::string filename("mycache.db"); const std::string arg = std::string("~/") + filename; auto check = [&filename, &tmp](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.cache_file() == tmp.get_path() + filename); REQUIRE(args.lock_file() == tmp.get_path() + filename + ".lock"); }; SECTION("-c") { check({"newsboat", "-c", arg}); } SECTION("--cache-file") { check({"newsboat", "--cache-file", arg}); } } TEST_CASE( "Sets `config_file` and `using_nonstandard_configs` if -C/--config-file " "is provided", "[CliArgsParser]") { const std::string filename("config file"); auto check = [&filename](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.config_file() == filename); REQUIRE(args.using_nonstandard_configs()); }; SECTION("-C") { check({"newsboat", "-C", filename}); } SECTION("--config-file") { check({"newsboat", "--config-file=" + filename}); } } TEST_CASE("Resolves tilde to homedir in -C/--config-file", "[CliArgsParser]") { test_helpers::TempDir tmp; test_helpers::EnvVar home("HOME"); home.set(tmp.get_path()); const std::string filename("newsboat-config"); const std::string arg = std::string("~/") + filename; auto check = [&filename, &tmp](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.config_file() == tmp.get_path() + filename); REQUIRE(args.using_nonstandard_configs()); }; SECTION("-C") { check({"newsboat", "-C", arg}); } SECTION("--config-file") { check({"newsboat", "--config-file", arg}); } } TEST_CASE("Sets `do_vacuum` if -X/--vacuum is provided", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.do_vacuum()); }; SECTION("-X") { check({"newsboat", "-X"}); } SECTION("--vacuum") { check({"newsboat", "--vacuum"}); } } TEST_CASE("Sets `do_cleanup` if --cleanup is provided", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.do_cleanup()); }; SECTION("--cleanup") { check({"newsboat", "--cleanup"}); } } TEST_CASE("Increases `show_version` with each -v/-V/--version provided", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts, unsigned int expected_version) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.show_version() == expected_version); }; SECTION("-v => 1") { check({"newsboat", "-v"}, 1); } SECTION("-V => 1") { check({"newsboat", "-V"}, 1); } SECTION("--version => 1") { check({"newsboat", "--version"}, 1); } SECTION("-vV => 2") { check({"newsboat", "-vV"}, 2); } SECTION("--version -v => 2") { check({"newsboat", "--version", "-v"}, 2); } SECTION("-V --version -v => 3") { check({"newsboat", "-V", "--version", "-v"}, 3); } SECTION("-VvVVvvvvV => 9") { check({"newsboat", "-VvVVvvvvV"}, 9); } } TEST_CASE("Requests silent mode if -x/--execute is provided", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.silent()); }; SECTION("-x") { check({"newsboat", "-x", "reload"}); } SECTION("--execute") { check({"newsboat", "--execute", "reload"}); } } TEST_CASE("Sets `execute_cmds` if -x/--execute is provided", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE_FALSE(args.cmds_to_execute().empty()); }; SECTION("-x") { check({"newsboat", "-x", "reload"}); } SECTION("--execute") { check({"newsboat", "--execute", "reload"}); } } TEST_CASE("Inserts commands to cmds_to_execute if -x/--execute is provided", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts, const std::vector& cmds) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.cmds_to_execute() == cmds); }; SECTION("-x reload") { check({"newsboat", "-x", "reload"}, {"reload"}); } SECTION("--execute reload") { check({"newsboat", "--execute", "reload"}, {"reload"}); } SECTION("-x reload print-unread") { check({"newsboat", "-x", "reload", "print-unread"}, {"reload", "print-unread"}); } SECTION("--execute reload print-unread") { check({"newsboat", "--execute", "reload", "print-unread"}, {"reload", "print-unread"}); } SECTION("Multiple occurrences of the option") { check({"newsboat", "-x", "print-unread", "--execute", "reload"}, {"print-unread", "reload"}); } } TEST_CASE("Requests silent mode if -q/--quiet is provided", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.silent()); }; SECTION("-q") { check({"newsboat", "-q"}); } SECTION("--quiet") { check({"newsboat", "--quiet"}); } } TEST_CASE( "Sets `readinfofile` if -I/--import-from-file is provided", "[CliArgsParser]") { const std::string filename("filename"); auto check = [&filename](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.readinfo_import_file() == filename); }; SECTION("-I") { check({"newsboat", "-I", filename}); } SECTION("--import-from-file") { check({"newsboat", "--import-from-file=" + filename}); } } TEST_CASE("Resolves tilde to homedir in -I/--import-from-file", "[CliArgsParser]") { test_helpers::TempDir tmp; test_helpers::EnvVar home("HOME"); home.set(tmp.get_path()); const std::string filename("read.txt"); const std::string arg = std::string("~/") + filename; auto check = [&filename, &tmp](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.readinfo_import_file() == tmp.get_path() + filename); }; SECTION("-I") { check({"newsboat", "-I", arg}); } SECTION("--import-from-file") { check({"newsboat", "--import-from-file", arg}); } } TEST_CASE( "Sets `readinfofile` if -E/--export-to-file is provided", "[CliArgsParser]") { const std::string filename("filename"); auto check = [&filename](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.readinfo_export_file() == filename); }; SECTION("-E") { check({"newsboat", "-E", filename}); } SECTION("--export-from-file") { check({"newsboat", "--export-to-file=" + filename}); } } TEST_CASE("Resolves tilde to homedir in -E/--export-to-file", "[CliArgsParser]") { test_helpers::TempDir tmp; test_helpers::EnvVar home("HOME"); home.set(tmp.get_path()); const std::string filename("read.txt"); const std::string arg = std::string("~/") + filename; auto check = [&filename, &tmp](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.readinfo_export_file() == tmp.get_path() + filename); }; SECTION("-E") { check({"newsboat", "-E", arg}); } SECTION("--export-to-file") { check({"newsboat", "--export-to-file", arg}); } } TEST_CASE( "Asks to print usage info and exit with failure if both " "-I/--import-from-file and -E/--export-to-file are provided", "[CliArgsParser]") { const std::string importf("import.opml"); const std::string exportf("export.opml"); auto check = [](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.should_print_usage()); REQUIRE(args.return_code() == EXIT_FAILURE); }; SECTION("-I first") { check({"newsboat", "-I", importf, "-E", exportf}); } SECTION("-E first") { check({"newsboat", "-E", exportf, "-I", importf}); } } TEST_CASE("Sets `log_file` if -d/--log-file is provided", "[CliArgsParser]") { const std::string filename("log file.txt"); auto check = [&filename](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.log_file() == filename); }; SECTION("-d") { check({"newsboat", "-d", filename}); } SECTION("--log-file") { check({"newsboat", "--log-file=" + filename}); } } TEST_CASE("Resolves tilde to homedir in -d/--log-file", "[CliArgsParser]") { test_helpers::TempDir tmp; test_helpers::EnvVar home("HOME"); home.set(tmp.get_path()); const std::string filename("newsboat.log"); const std::string arg = std::string("~/") + filename; auto check = [&filename, &tmp](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.log_file() == tmp.get_path() + filename); }; SECTION("-d") { check({"newsboat", "-d", arg}); } SECTION("--log-file") { check({"newsboat", "--log-file", arg}); } } TEST_CASE( "Sets `log_level` if argument to -l/--log-level is in range of [1; 6]", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts, Level expected) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.log_level() == expected); }; SECTION("--log-level=1 means USERERROR") { check({"newsboat", "--log-level=1"}, Level::USERERROR); } SECTION("--log-level=2 means CRITICAL") { check({"newsboat", "--log-level=2"}, Level::CRITICAL); } SECTION("-l3 means ERROR") { check({"newsboat", "-l3"}, Level::ERROR); } SECTION("--log-level=4 means WARN") { check({"newsboat", "--log-level=4"}, Level::WARN); } SECTION("-l5 means INFO") { check({"newsboat", "-l5"}, Level::INFO); } SECTION("-l6 means DEBUG") { check({"newsboat", "-l6"}, Level::DEBUG); } } TEST_CASE( "Sets `display_msg` and asks to exit with failure if argument to " "-l/--log-level is outside of [1; 6]", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE_FALSE(args.display_msg() == ""); REQUIRE(args.return_code() == EXIT_FAILURE); }; SECTION("-l0") { check({"newsboat", "-l0"}); } SECTION("--log-level=7") { check({"newsboat", "--log-level=7"}); } SECTION("--log-level=9001") { check({"newsboat", "--log-level=90001"}); } } TEST_CASE("Sets `program_name` to the first string of the options list", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts, std::string expected) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.program_name() == expected); }; check({"newsboat"}, "newsboat"); check({"podboat", "-h"}, "podboat"); check({"something else entirely", "--foo", "--bar", "--baz"}, "something else entirely"); check({"/usr/local/bin/app-with-a-path"}, "/usr/local/bin/app-with-a-path"); } TEST_CASE("Test should fail on equal sign with multiple values", "[CliArgsParser]") { auto check = [](test_helpers::Opts opts, const std::vector& cmds) { CliArgsParser args(opts.argc(), opts.argv()); REQUIRE(args.should_print_usage()); REQUIRE(args.cmds_to_execute() != cmds); }; check({"newsboat", "-x=reload", "print-unread" }, { "reload", "print-unread" }); check({"newsboat", "--execute=reload", "print-unread" }, { "reload", "print-unread" }); } TEST_CASE("Supports combined short options where last has equal sign", "[CliArgsParser]") { test_helpers::Opts opts = {"newsboat", "-rx=reload"}; CliArgsParser args(opts.argc(), opts.argv()); std::vector commands{"reload"}; REQUIRE(args.cmds_to_execute() == commands); }