From 70a291d4d42d33b4e9e4454473c5256efd286dd3 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 22 Dec 2011 21:11:10 +0000 Subject: [PATCH] Add some tests for agi::Options Originally committed to SVN as r6010. --- aegisub/tests/Makefile | 1 + aegisub/tests/libaegisub_option.cpp | 236 ++++++++++++++++++++++- aegisub/tests/options/all_bool.json | 1 + aegisub/tests/options/all_types.json | 14 ++ aegisub/tests/options/array_bool.json | 1 + aegisub/tests/options/array_double.json | 1 + aegisub/tests/options/array_integer.json | 1 + aegisub/tests/options/array_string.json | 1 + aegisub/tests/options/bool.json | 1 + aegisub/tests/options/double.json | 1 + aegisub/tests/options/integer.json | 1 + aegisub/tests/options/null.json | 1 + aegisub/tests/options/string.json | 1 + aegisub/tests/setup.bat | 13 +- aegisub/tests/setup.sh | 13 +- aegisub/tests/util.cpp | 11 +- aegisub/tests/util.h | 3 +- 17 files changed, 270 insertions(+), 31 deletions(-) create mode 100644 aegisub/tests/options/all_bool.json create mode 100644 aegisub/tests/options/all_types.json create mode 100644 aegisub/tests/options/array_bool.json create mode 100644 aegisub/tests/options/array_double.json create mode 100644 aegisub/tests/options/array_integer.json create mode 100644 aegisub/tests/options/array_string.json create mode 100644 aegisub/tests/options/bool.json create mode 100644 aegisub/tests/options/double.json create mode 100644 aegisub/tests/options/integer.json create mode 100644 aegisub/tests/options/null.json create mode 100644 aegisub/tests/options/string.json diff --git a/aegisub/tests/Makefile b/aegisub/tests/Makefile index da7c0f8c9..717f7eb95 100644 --- a/aegisub/tests/Makefile +++ b/aegisub/tests/Makefile @@ -22,6 +22,7 @@ SRC = \ libaegisub_iconv.cpp \ libaegisub_keyframe.cpp \ libaegisub_line_iterator.cpp \ + libaegisub_option.cpp \ libaegisub_mru.cpp \ libaegisub_signals.cpp \ libaegisub_util.cpp \ diff --git a/aegisub/tests/libaegisub_option.cpp b/aegisub/tests/libaegisub_option.cpp index 0dd1bcf3b..6207e989b 100644 --- a/aegisub/tests/libaegisub_option.cpp +++ b/aegisub/tests/libaegisub_option.cpp @@ -1,5 +1,4 @@ - -// Copyright (c) 2010, Amar Takhar + // Copyright (c) 2011, Thomas Goyne // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -20,6 +19,8 @@ /// @ingroup option #include +#include + #include "main.h" #include "util.h" @@ -28,14 +29,235 @@ protected: std::string default_opt; std::string conf_ok; - virtual void SetUp() { + void SetUp() { default_opt = "{\"Valid\" : \"This is valid\"}"; - conf_ok = "./data/option_string.json"; + conf_ok = "data/options/string.json"; } }; - -TEST_F(lagi_option, OptionConstructFromFile) { - EXPECT_NO_THROW(agi::Options *opt = new agi::Options(conf_ok, default_opt)); +TEST_F(lagi_option, construct_from_file) { + EXPECT_NO_THROW(agi::Options(conf_ok, default_opt, agi::Options::FLUSH_SKIP)); } +TEST_F(lagi_option, nonexistent_file) { + EXPECT_NO_THROW(agi::Options("", default_opt, agi::Options::FLUSH_SKIP)); +} + +TEST_F(lagi_option, get_existing_option) { + agi::Options opt(conf_ok, default_opt, agi::Options::FLUSH_SKIP); + ASSERT_NO_THROW(opt.Get("Valid")); + ASSERT_NO_THROW(opt.Get("Valid")->GetString()); + EXPECT_STREQ("This is valid", opt.Get("Valid")->GetString().c_str()); +} + +TEST_F(lagi_option, get_nonexistant_option) { + agi::Options opt(conf_ok, default_opt, agi::Options::FLUSH_SKIP); + EXPECT_THROW(opt.Get("Nonexistant option"), agi::OptionErrorNotFound); +} + +TEST_F(lagi_option, flush_skip) { + util::copy("data/options/string.json", "data/options/tmp"); + { + agi::Options opt("data/options/tmp", default_opt, agi::Options::FLUSH_SKIP); + ASSERT_NO_THROW(opt.Get("Valid")->SetString("")); + } + EXPECT_TRUE(util::compare("data/options/string.json", "data/options/tmp")); +} + +TEST_F(lagi_option, flush_no_skip) { + util::copy("data/options/string.json", "data/options/tmp"); + { + agi::Options opt("data/options/tmp", default_opt); + ASSERT_NO_THROW(opt.Get("Valid")->SetString("")); + } + EXPECT_FALSE(util::compare("data/options/string.json", "data/options/tmp")); +} + +TEST_F(lagi_option, existent_but_invalid_file_uses_default) { + agi::Options opt("data/options/null.json", default_opt, agi::Options::FLUSH_SKIP); + EXPECT_NO_THROW(opt.Get("Valid")->GetString()); + EXPECT_THROW(opt.Get("Null"), agi::Exception); +} + +TEST_F(lagi_option, load_several_config_files) { + agi::Options opt("", default_opt, agi::Options::FLUSH_SKIP); + { std::ifstream f("data/options/string.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); } + { std::ifstream f("data/options/integer.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); } + { std::ifstream f("data/options/double.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); } + { std::ifstream f("data/options/bool.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); } + + EXPECT_NO_THROW(opt.Get("String")->GetString()); + EXPECT_NO_THROW(opt.Get("Integer")->GetInt()); + EXPECT_NO_THROW(opt.Get("Double")->GetDouble()); + EXPECT_NO_THROW(opt.Get("Bool")->GetBool()); +} + +TEST_F(lagi_option, arrays) { + agi::Options opt("", default_opt, agi::Options::FLUSH_SKIP); + { std::ifstream f("data/options/array_string.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); } + { std::ifstream f("data/options/array_integer.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); } + { std::ifstream f("data/options/array_double.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); } + { std::ifstream f("data/options/array_bool.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); } + + EXPECT_NO_THROW(opt.Get("String")->GetListString()); + EXPECT_NO_THROW(opt.Get("Integer")->GetListInt()); + EXPECT_NO_THROW(opt.Get("Double")->GetListDouble()); + EXPECT_NO_THROW(opt.Get("Bool")->GetListBool()); +} + +TEST_F(lagi_option, bad_default_throws_and_null_is_rejected) { + EXPECT_THROW(agi::Options("", "{\"Null\" : null}", agi::Options::FLUSH_SKIP), agi::Exception); +} + +TEST_F(lagi_option, nested_options) { + const char *conf = "{ \"a\" : { \"b\" : { \"c\" : { \"c\" : \"value\" } } } }"; + ASSERT_NO_THROW(agi::Options("", conf, agi::Options::FLUSH_SKIP)); + agi::Options opt("", conf, agi::Options::FLUSH_SKIP); + ASSERT_NO_THROW(opt.Get("a/b/c/c")); + ASSERT_NO_THROW(opt.Get("a/b/c/c")->GetString()); + EXPECT_STREQ("value", opt.Get("a/b/c/c")->GetString().c_str()); +} + +TEST_F(lagi_option, heterogeneous_arrays_rejected) { + EXPECT_NO_THROW(agi::Options("", "{ \"key\" : [ { \"bool\" : true }] }", agi::Options::FLUSH_SKIP)); + EXPECT_THROW(agi::Options("", "{ \"key\" : [ { \"bool\" : true }, { \"double\" : 1.0 } ] }", agi::Options::FLUSH_SKIP), agi::Exception); +} + +TEST_F(lagi_option, flush_roundtrip) { + util::remove("data/options/tmp"); + + { + agi::Options opt("data/options/tmp", "{}"); + { std::ifstream f("data/options/all_types.json"); EXPECT_NO_THROW(opt.ConfigNext(f)); } + EXPECT_NO_THROW(opt.Get("Integer")->SetInt(1)); + EXPECT_NO_THROW(opt.Get("Double")->SetDouble(1.1)); + EXPECT_NO_THROW(opt.Get("String")->SetString("hello")); + EXPECT_NO_THROW(opt.Get("Colour")->SetColour("rgb(255,255,255)")); + EXPECT_NO_THROW(opt.Get("Boolean")->SetBool(true)); + + std::vector int_arr; int_arr.push_back(1); + EXPECT_NO_THROW(opt.Get("Array/Integer")->SetListInt(int_arr)); + std::vector double_arr; double_arr.push_back(1.1); + EXPECT_NO_THROW(opt.Get("Array/Double")->SetListDouble(double_arr)); + std::vector str_arr; str_arr.push_back("hello"); + EXPECT_NO_THROW(opt.Get("Array/String")->SetListString(str_arr)); + std::vector clr_arr; clr_arr.push_back("rgb(255,255,255)"); + EXPECT_NO_THROW(opt.Get("Array/Colour")->SetListColour(clr_arr)); + std::vector bool_arr; bool_arr.push_back(true); + EXPECT_NO_THROW(opt.Get("Array/Boolean")->SetListBool(bool_arr)); + } + + { + agi::Options opt("data/options/tmp", "{}"); + ASSERT_NO_THROW(opt.ConfigUser()); + + EXPECT_EQ(1, opt.Get("Integer")->GetInt()); + EXPECT_EQ(1.1, opt.Get("Double")->GetDouble()); + EXPECT_STREQ("hello", opt.Get("String")->GetString().c_str()); + EXPECT_STREQ("rgb(255,255,255)", opt.Get("Colour")->GetColour().c_str()); + EXPECT_EQ(true, opt.Get("Boolean")->GetBool()); + + EXPECT_EQ(1, opt.Get("Array/Integer")->GetListInt().size()); + EXPECT_EQ(1, opt.Get("Array/Double")->GetListDouble().size()); + EXPECT_EQ(1, opt.Get("Array/String")->GetListString().size()); + EXPECT_EQ(1, opt.Get("Array/Colour")->GetListColour().size()); + EXPECT_EQ(1, opt.Get("Array/Boolean")->GetListBool().size()); + + EXPECT_EQ(1, opt.Get("Array/Integer")->GetListInt().front()); + EXPECT_EQ(1.1, opt.Get("Array/Double")->GetListDouble().front()); + EXPECT_STREQ("hello", opt.Get("Array/String")->GetListString().front().c_str()); + EXPECT_STREQ("rgb(255,255,255)", opt.Get("Array/Colour")->GetListColour().front().c_str()); + EXPECT_EQ(true, opt.Get("Array/Boolean")->GetListBool().front()); + } +} + +TEST_F(lagi_option, mixed_valid_and_invalid_in_user_conf_loads_all_valid) { + const char *def = "{\"1\" : false, \"2\" : 1, \"3\" : false }"; + agi::Options opt("data/options/all_bool.json", def, agi::Options::FLUSH_SKIP); + ASSERT_NO_THROW(opt.ConfigUser()); + EXPECT_EQ(true, opt.Get("1")->GetBool()); + EXPECT_EQ(1, opt.Get("2")->GetInt()); + EXPECT_EQ(true, opt.Get("3")->GetBool()); +} + +TEST_F(lagi_option, empty_array_works) { + EXPECT_NO_THROW(agi::Options("", "{ \"arr\" : [] }", agi::Options::FLUSH_SKIP)); +} + +TEST_F(lagi_option, empty_object_works) { + EXPECT_NO_THROW(agi::Options("", "{ \"obj\" : {} }", agi::Options::FLUSH_SKIP)); +} + +TEST_F(lagi_option, unknown_array_type) { + EXPECT_THROW(agi::Options("", "{ \"arr\" : [ { \"float\" : 5.0 } ] }"), agi::Exception); +} + +TEST_F(lagi_option, malformed_arrays) { + EXPECT_THROW(agi::Options("", "{ \"arr\" : [ {} ] }"), agi::Exception); + EXPECT_THROW(agi::Options("", "{ \"arr\" : [ { \"double\" : 5.0 }, {} ] }"), agi::Exception); + EXPECT_THROW(agi::Options("", "{ \"arr\" : [ { \"double\" : 5.0, \"int\" : 5 } ] }"), agi::Exception); +} + +TEST_F(lagi_option, int_vs_double) { + agi::Options opt("", "{ \"int\" : 5, \"double\" : 5.0 }", agi::Options::FLUSH_SKIP); + EXPECT_NO_THROW(opt.Get("int")->GetInt()); + EXPECT_NO_THROW(opt.Get("double")->GetDouble()); +} + +struct empty_arr_options : public agi::Options { + empty_arr_options() : agi::Options("", "{ \"arr\" : [] }", agi::Options::FLUSH_SKIP) { } +}; + +TEST_F(lagi_option, empty_array_decays_to_first_used_type) { + ASSERT_NO_THROW(empty_arr_options()); + + { + empty_arr_options opt; + EXPECT_NO_THROW(opt.Get("arr")->GetListBool()); + + EXPECT_THROW(opt.Get("arr")->GetListColour(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListString(), agi::OptionValueErrorInvalidListType); + } + + { + empty_arr_options opt; + EXPECT_NO_THROW(opt.Get("arr")->GetListColour()); + + EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListString(), agi::OptionValueErrorInvalidListType); + } + + { + empty_arr_options opt; + EXPECT_NO_THROW(opt.Get("arr")->GetListDouble()); + + EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListColour(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListString(), agi::OptionValueErrorInvalidListType); + } + + { + empty_arr_options opt; + EXPECT_NO_THROW(opt.Get("arr")->GetListInt()); + + EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListColour(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListString(), agi::OptionValueErrorInvalidListType); + } + + { + empty_arr_options opt; + EXPECT_NO_THROW(opt.Get("arr")->GetListString()); + + EXPECT_THROW(opt.Get("arr")->GetListBool(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListColour(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListDouble(), agi::OptionValueErrorInvalidListType); + EXPECT_THROW(opt.Get("arr")->GetListInt(), agi::OptionValueErrorInvalidListType); + } +} diff --git a/aegisub/tests/options/all_bool.json b/aegisub/tests/options/all_bool.json new file mode 100644 index 000000000..73acf3608 --- /dev/null +++ b/aegisub/tests/options/all_bool.json @@ -0,0 +1 @@ +{"1" : true, "2" : true, "3" : true } diff --git a/aegisub/tests/options/all_types.json b/aegisub/tests/options/all_types.json new file mode 100644 index 000000000..ce9679ad0 --- /dev/null +++ b/aegisub/tests/options/all_types.json @@ -0,0 +1,14 @@ +{ + "Integer" : 0, + "Double" : 0.1, + "String" : "", + "Colour" : "rgb(0, 0, 0)", + "Boolean" : false, + "Array" : { + "Integer" : [ { "int" : 0 }, {"int" : 0 } ], + "Double" : [ { "double" : 0.1 }, {"double" : 0.1 } ], + "String" : [ { "string" : "" }, {"string" : "" } ], + "Colour" : [ { "colour" : "rgb(0,0,0)" }, {"colour" : "rgb(0,0,0)" } ], + "Boolean" : [ { "bool" : false }, {"bool" : false } ] + } +} diff --git a/aegisub/tests/options/array_bool.json b/aegisub/tests/options/array_bool.json new file mode 100644 index 000000000..8b5122fcc --- /dev/null +++ b/aegisub/tests/options/array_bool.json @@ -0,0 +1 @@ +{"Bool" : [{"bool" : true}, {"bool" : true}]} diff --git a/aegisub/tests/options/array_double.json b/aegisub/tests/options/array_double.json new file mode 100644 index 000000000..8e0102f90 --- /dev/null +++ b/aegisub/tests/options/array_double.json @@ -0,0 +1 @@ +{"Double" : [{"double" : 2.1}, {"double" : 2.1}]} diff --git a/aegisub/tests/options/array_integer.json b/aegisub/tests/options/array_integer.json new file mode 100644 index 000000000..b6282a061 --- /dev/null +++ b/aegisub/tests/options/array_integer.json @@ -0,0 +1 @@ +{"Integer" : [{"int" : 1}, {"int" : 1}]} diff --git a/aegisub/tests/options/array_string.json b/aegisub/tests/options/array_string.json new file mode 100644 index 000000000..480a475c4 --- /dev/null +++ b/aegisub/tests/options/array_string.json @@ -0,0 +1 @@ +{"String" : [{"string" : "This is a test"}, {"string" : "This is a test"}]} diff --git a/aegisub/tests/options/bool.json b/aegisub/tests/options/bool.json new file mode 100644 index 000000000..5a0d659d7 --- /dev/null +++ b/aegisub/tests/options/bool.json @@ -0,0 +1 @@ +{"Bool" : true} diff --git a/aegisub/tests/options/double.json b/aegisub/tests/options/double.json new file mode 100644 index 000000000..f90527a79 --- /dev/null +++ b/aegisub/tests/options/double.json @@ -0,0 +1 @@ +{"Double" : 2.1} diff --git a/aegisub/tests/options/integer.json b/aegisub/tests/options/integer.json new file mode 100644 index 000000000..2e0b3ebc4 --- /dev/null +++ b/aegisub/tests/options/integer.json @@ -0,0 +1 @@ +{"Integer" : 1} diff --git a/aegisub/tests/options/null.json b/aegisub/tests/options/null.json new file mode 100644 index 000000000..f066f5928 --- /dev/null +++ b/aegisub/tests/options/null.json @@ -0,0 +1 @@ +{"Null" : null} diff --git a/aegisub/tests/options/string.json b/aegisub/tests/options/string.json new file mode 100644 index 000000000..0e86faf14 --- /dev/null +++ b/aegisub/tests/options/string.json @@ -0,0 +1 @@ +{"String" : "This is a test"} diff --git a/aegisub/tests/setup.bat b/aegisub/tests/setup.bat index a0f32c1d4..d1f8ebd93 100755 --- a/aegisub/tests/setup.bat +++ b/aegisub/tests/setup.bat @@ -27,17 +27,8 @@ echo > data/rename_me echo > data/rename_me_overwrite echo > data/rename_me_overwrite_renamed - -echo {"String" : "This is a test"} > data/option_string.json -echo {"Integer" : 1} > data/option_integer.json -echo {"Double" : 2.1} > data/option_double.json -echo {"Bool" : true} > data/option_bool.json -echo {"Null" : null} > data/option_null.json - -echo {"String" : [{"string" : "This is a test"}, {"string" : "This is a test"}]} > data/option_array_string -echo {"Integer" : [{"int" : 1}, {"int" : 1}]} > data/option_array_integer -echo {"Double" : [{"double" : 2.1}, {"double" : 2.1}]} > data/option_array_double -echo {"Bool" : [{"bool" : true}, {"bool" : true}]} > data/option_array_bool +mkdir data\options +xcopy "%~dp0\options" data\options mkdir data\vfr mkdir data\vfr\in diff --git a/aegisub/tests/setup.sh b/aegisub/tests/setup.sh index 4a4a593d9..99b14ff7c 100755 --- a/aegisub/tests/setup.sh +++ b/aegisub/tests/setup.sh @@ -29,17 +29,8 @@ touch data/rename_me touch data/rename_me_overwrite touch data/rename_me_overwrite_renamed - -echo '{"String" : "This is a test"}' > data/option_string.json -echo '{"Integer" : 1}' > data/option_integer.json -echo '{"Double" : 2.1}' > data/option_double.json -echo '{"Bool" : true}' > data/option_bool.json -echo '{"Null" : null}' > data/option_null.json - -echo '{"String" : [{"string" : "This is a test"}, {"string" : "This is a test"}]}' > data/option_array_string -echo '{"Integer" : [{"int" : 1}, {"int" : 1}]}' > data/option_array_integer -echo '{"Double" : [{"double" : 2.1}, {"double" : 2.1}]}' > data/option_array_double -echo '{"Bool" : [{"bool" : true}, {"bool" : true}]}' > data/option_array_bool +mkdir data/options +cp options/* data/options mkdir data/vfr mkdir data/vfr/in diff --git a/aegisub/tests/util.cpp b/aegisub/tests/util.cpp index f6902f130..5f6f12f0f 100644 --- a/aegisub/tests/util.cpp +++ b/aegisub/tests/util.cpp @@ -19,17 +19,26 @@ /// @ingroup util common #include +#include #include "util.h" namespace util { -void copy(const std::string from, const std::string to) { +void copy(const std::string &from, const std::string &to) { std::ifstream ifs(from.c_str(), std::ios::binary); std::ofstream ofs(to.c_str(), std::ios::binary); ofs << ifs.rdbuf(); } +bool compare(const std::string &file1, const std::string &file2) { + std::stringstream ss1, ss2; + std::ifstream if1(file1.c_str(), std::ios::binary), if2(file2.c_str(), std::ios::binary); + ss1 << if1.rdbuf(); + ss2 << if2.rdbuf(); + return ss1.str() == ss2.str(); +} + } // namespace util diff --git a/aegisub/tests/util.h b/aegisub/tests/util.h index a23c47a6f..411d4a38a 100644 --- a/aegisub/tests/util.h +++ b/aegisub/tests/util.h @@ -25,7 +25,8 @@ namespace util { -void copy(const std::string from, const std::string to); +void copy(const std::string &from, const std::string &to); +bool compare(const std::string &file1, const std::string &file2); void remove(const std::string& file); template