#include #include #include #include #include #include #include #include #include #include #include namespace { using namespace testing; using namespace OpenMW; namespace bpo = boost::program_options; template std::string makeString(const T (&range)[size]) { static_assert(size > 0); return std::string(std::begin(range), std::end(range) - 1); } template std::vector generateSupportedCharacters(Args&& ... args) { std::vector result; (result.emplace_back(makeString(args)) , ...); for (int i = 1; i <= std::numeric_limits::max(); ++i) if (i != '&' && i != '"' && i != ' ' && i != '\n') result.push_back(std::string(1, i)); return result; } MATCHER_P(IsPath, v, "") { return arg.string() == v; } template void parseArgs(const T& arguments, bpo::variables_map& variables, bpo::options_description& description) { Files::parseArgs(static_cast(arguments.size()), arguments.data(), variables, description); } TEST(OpenMWOptionsFromArguments, should_support_equality_to_separate_flag_and_value) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame=save.omwsave"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); } TEST(OpenMWOptionsFromArguments, should_support_single_word_load_savegame_path) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", "save.omwsave"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); } TEST(OpenMWOptionsFromArguments, should_support_multi_component_load_savegame_path) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", "/home/user/openmw/save.omwsave"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "/home/user/openmw/save.omwsave"); } TEST(OpenMWOptionsFromArguments, should_support_windows_multi_component_load_savegame_path) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", R"(C:\OpenMW\save.omwsave)"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), R"(C:\OpenMW\save.omwsave)"); } TEST(OpenMWOptionsFromArguments, should_support_load_savegame_path_with_spaces) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", "my save.omwsave"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "my save.omwsave"); } TEST(OpenMWOptionsFromArguments, should_support_load_savegame_path_with_octothorpe) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", "my#save.omwsave"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "my#save.omwsave"); } TEST(OpenMWOptionsFromArguments, should_support_load_savegame_path_with_at_sign) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", "my@save.omwsave"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "my@save.omwsave"); } TEST(OpenMWOptionsFromArguments, should_support_load_savegame_path_with_quote) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", R"(my"save.omwsave)"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), R"(my"save.omwsave)"); } TEST(OpenMWOptionsFromArguments, should_support_quoted_load_savegame_path) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", R"("save".omwsave)"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), R"(save)"); } TEST(OpenMWOptionsFromArguments, should_support_quoted_load_savegame_path_with_escaped_quote_by_ampersand) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", R"("save&".omwsave")"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), R"(save".omwsave)"); } TEST(OpenMWOptionsFromArguments, should_support_quoted_load_savegame_path_with_escaped_ampersand) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", R"("save.omwsave&&")"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave&"); } TEST(OpenMWOptionsFromArguments, should_support_load_savegame_path_with_ampersand) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", "save&.omwsave"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "save&.omwsave"); } TEST(OpenMWOptionsFromArguments, should_support_load_savegame_path_with_multiple_quotes) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", R"(my"save".omwsave)"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), R"(my"save".omwsave)"); } TEST(OpenMWOptionsFromArguments, should_compose_data) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--data", "1", "--data", "2"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_THAT(variables["data"].as(), ElementsAre(IsPath("1"), IsPath("2"))); } TEST(OpenMWOptionsFromArguments, should_compose_data_from_single_flag) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--data", "1", "2"}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_THAT(variables["data"].as(), ElementsAre(IsPath("1"), IsPath("2"))); } TEST(OpenMWOptionsFromArguments, should_throw_on_multiple_load_savegame) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", "1.omwsave", "--load-savegame", "2.omwsave"}; bpo::variables_map variables; EXPECT_THROW(parseArgs(arguments, variables, description), std::exception); } struct OpenMWOptionsFromArgumentsStrings : TestWithParam {}; TEST_P(OpenMWOptionsFromArgumentsStrings, should_support_paths_with_certain_characters_in_load_savegame_path) { bpo::options_description description = makeOptionsDescription(); const std::string path = "save_" + std::string(GetParam()) + ".omwsave"; const std::string pathArgument = "\"" + path + "\""; const std::array arguments {"openmw", "--load-savegame", pathArgument.c_str()}; bpo::variables_map variables; parseArgs(arguments, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), path); } INSTANTIATE_TEST_SUITE_P( SupportedCharacters, OpenMWOptionsFromArgumentsStrings, ValuesIn(generateSupportedCharacters(u8"👍", u8"Ъ", u8"Ǽ", "\n")) ); TEST(OpenMWOptionsFromConfig, should_support_single_word_load_savegame_path) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream("load-savegame=save.omwsave"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); } TEST(OpenMWOptionsFromConfig, should_strip_quotes_from_load_savegame_path) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream(R"(load-savegame="save.omwsave")"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); } TEST(OpenMWOptionsFromConfig, should_strip_outer_quotes_from_load_savegame_path) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream(R"(load-savegame=""save".omwsave")"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), ""); } TEST(OpenMWOptionsFromConfig, should_strip_quotes_from_load_savegame_path_with_space) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream(R"(load-savegame="my save.omwsave")"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "my save.omwsave"); } TEST(OpenMWOptionsFromConfig, should_support_quoted_load_savegame_path_with_octothorpe) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream("load-savegame=save#.omwsave"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "save#.omwsave"); } TEST(OpenMWOptionsFromConfig, should_support_quoted_load_savegame_path_with_at_sign) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream("load-savegame=save@.omwsave"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "save@.omwsave"); } TEST(OpenMWOptionsFromConfig, should_support_quoted_load_savegame_path_with_quote) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream(R"(load-savegame=save".omwsave)"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), R"(save".omwsave)"); } TEST(OpenMWOptionsFromConfig, should_support_confusing_savegame_path_with_lots_going_on) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream(R"(load-savegame="one &"two"three".omwsave")"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), R"(one "two)"); } TEST(OpenMWOptionsFromConfig, should_support_confusing_savegame_path_with_even_more_going_on) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream(R"(load-savegame="one &"two"three ".omwsave")"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), R"(one "two)"); } TEST(OpenMWOptionsFromConfig, should_ignore_commented_option) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream("#load-savegame=save.omwsave"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), ""); } TEST(OpenMWOptionsFromConfig, should_ignore_whitespace_prefixed_commented_option) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream(" \t#load-savegame=save.omwsave"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), ""); } TEST(OpenMWOptionsFromConfig, should_support_whitespace_around_option) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream(" load-savegame = save.omwsave "); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); } TEST(OpenMWOptionsFromConfig, should_throw_on_multiple_load_savegame) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream("load-savegame=1.omwsave\nload-savegame=2.omwsave"); bpo::variables_map variables; EXPECT_THROW(Files::parseConfig(stream, variables, description), std::exception); } TEST(OpenMWOptionsFromConfig, should_support_multi_component_load_savegame_path) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream("load-savegame=/home/user/openmw/save.omwsave"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "/home/user/openmw/save.omwsave"); } TEST(OpenMWOptionsFromConfig, should_support_windows_multi_component_load_savegame_path) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream(R"(load-savegame=C:\OpenMW\save.omwsave)"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), R"(C:\OpenMW\save.omwsave)"); } TEST(OpenMWOptionsFromConfig, should_compose_data) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream("data=1\ndata=2"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_THAT(variables["data"].as(), ElementsAre(IsPath("1"), IsPath("2"))); } TEST(OpenMWOptionsFromConfig, should_support_quoted_load_savegame_path_with_escaped_quote_by_ampersand) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream(R"(load-savegame="save&".omwsave")"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), R"(save".omwsave)"); } TEST(OpenMWOptionsFromConfig, should_support_quoted_load_savegame_path_with_escaped_ampersand) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream(R"(load-savegame="save.omwsave&&")"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave&"); } TEST(OpenMWOptionsFromConfig, should_support_load_savegame_path_with_ampersand) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream("load-savegame=save&.omwsave"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), "save&.omwsave"); } struct OpenMWOptionsFromConfigStrings : TestWithParam {}; TEST_P(OpenMWOptionsFromConfigStrings, should_support_paths_with_certain_characters_in_load_savegame_path) { bpo::options_description description = makeOptionsDescription(); const std::string path = "save_" + std::string(GetParam()) + ".omwsave"; std::istringstream stream("load-savegame=\"" + path + "\""); bpo::variables_map variables; Files::parseConfig(stream, variables, description); EXPECT_EQ(variables["load-savegame"].as().string(), path); } INSTANTIATE_TEST_SUITE_P( SupportedCharacters, OpenMWOptionsFromConfigStrings, ValuesIn(generateSupportedCharacters(u8"👍", u8"Ъ", u8"Ǽ")) ); }