diff --git a/src/documentation/documentation.cpp b/src/documentation/documentation.cpp index ae86af64f9..83c9fc3c27 100644 --- a/src/documentation/documentation.cpp +++ b/src/documentation/documentation.cpp @@ -24,6 +24,12 @@ #include +#include + +namespace { + const std::string Wildcard = "*"; +} // namespace + namespace std { std::string to_string(std::string value) { return value; @@ -99,22 +105,47 @@ TestResult testSpecification(const Documentation& d, const ghoul::Dictionary& di result.success = true; for (const auto& p : d) { - if (p.optional && !dictionary.hasKey(p.key)) { - // If the key is optional and it doesn't exist, we don't need to check it - // if the key exists, it has to be correct, however - continue; + if (p.key == Wildcard) { + for (const std::string& key : dictionary.keys()) { + Verifier& verifier = *(p.tester); + TestResult res = verifier(dictionary, key); + if (!res.success) { + result.success = false; + result.offenders.insert( + result.offenders.end(), + res.offenders.begin(), + res.offenders.end() + ); + } + } } - Verifier& verifier = *(p.tester); - TestResult res = verifier(dictionary, p.key); - if (!res.success) { - result.success = false; - result.offenders.insert( - result.offenders.end(), - res.offenders.begin(), - res.offenders.end() - ); + else { + if (p.optional && !dictionary.hasKey(p.key)) { + // If the key is optional and it doesn't exist, we don't need to check it + // if the key exists, it has to be correct, however + continue; + } + Verifier& verifier = *(p.tester); + TestResult res = verifier(dictionary, p.key); + if (!res.success) { + result.success = false; + result.offenders.insert( + result.offenders.end(), + res.offenders.begin(), + res.offenders.end() + ); + } } } + + // Make the offenders unique so that they only appear once in the list + std::set uniqueOffenders( + result.offenders.begin(), result.offenders.end() + ); + result.offenders = std::vector( + uniqueOffenders.begin(), uniqueOffenders.end() + ); + return result; } diff --git a/tests/test_documentation.inl b/tests/test_documentation.inl index ba9eff6b76..dfe11b297a 100644 --- a/tests/test_documentation.inl +++ b/tests/test_documentation.inl @@ -250,7 +250,7 @@ TEST_F(DocumentationTest, IntVerifier) { ASSERT_EQ(1, negativeRes.offenders.size()); EXPECT_EQ("Int", negativeRes.offenders[0]); - ghoul::Dictionary negativeExist{ + ghoul::Dictionary negativeExist { { "Int2", 0 } }; negativeRes = testSpecification(doc, negative); @@ -282,7 +282,7 @@ TEST_F(DocumentationTest, StringVerifier) { ASSERT_EQ(1, negativeRes.offenders.size()); EXPECT_EQ("String", negativeRes.offenders[0]); - ghoul::Dictionary negativeExist{ + ghoul::Dictionary negativeExist { { "String2", ""s } }; negativeRes = testSpecification(doc, negative); @@ -1475,3 +1475,116 @@ TEST_F(DocumentationTest, NotInRangeDouble) { ASSERT_EQ(1, negativeRes.offenders.size()); EXPECT_EQ("Double", negativeRes.offenders[0]); } + +TEST_F(DocumentationTest, Wildcard) { + using namespace openspace::documentation; + + Documentation doc { + { "*", new IntVerifier } + }; + + ghoul::Dictionary positive { + { "a", 1 }, + { "b", 2 }, + { "c", 3 } + }; + TestResult positiveRes = testSpecification(doc, positive); + EXPECT_TRUE(positiveRes.success); + EXPECT_EQ(0, positiveRes.offenders.size()); + + ghoul::Dictionary negative { + { "a", false }, + { "b", 2 }, + { "c", 3 } + }; + TestResult negativeRes = testSpecification(doc, negative); + EXPECT_FALSE(negativeRes.success); + ASSERT_EQ(1, negativeRes.offenders.size()); + EXPECT_EQ("a", negativeRes.offenders[0]); + + ghoul::Dictionary negative2 { + { "a", false }, + { "b", false }, + { "c", 3 } + }; + negativeRes = testSpecification(doc, negative2); + EXPECT_FALSE(negativeRes.success); + ASSERT_EQ(2, negativeRes.offenders.size()); + EXPECT_EQ("a", negativeRes.offenders[0]); + EXPECT_EQ("b", negativeRes.offenders[1]); + + ghoul::Dictionary negative3 { + { "a", false }, + { "b", false }, + { "c", false } + }; + negativeRes = testSpecification(doc, negative3); + EXPECT_FALSE(negativeRes.success); + ASSERT_EQ(3, negativeRes.offenders.size()); + EXPECT_EQ("a", negativeRes.offenders[0]); + EXPECT_EQ("b", negativeRes.offenders[1]); + EXPECT_EQ("c", negativeRes.offenders[2]); +} + +TEST_F(DocumentationTest, WildcardMixed) { + using namespace openspace::documentation; + + Documentation doc { + { "*", new IntVerifier }, + { "b", new IntGreaterVerifier(5) } + }; + + ghoul::Dictionary positive { + { "a", 1 }, + { "b", 8 }, + { "c", 3 } + }; + TestResult positiveRes = testSpecification(doc, positive); + EXPECT_TRUE(positiveRes.success); + EXPECT_EQ(0, positiveRes.offenders.size()); + + ghoul::Dictionary negative { + { "a", false }, + { "b", 2 }, + { "c", 3 } + }; + TestResult negativeRes = testSpecification(doc, negative); + EXPECT_FALSE(negativeRes.success); + ASSERT_EQ(2, negativeRes.offenders.size()); + EXPECT_EQ("a", negativeRes.offenders[0]); + EXPECT_EQ("b", negativeRes.offenders[1]); + + ghoul::Dictionary negative2 { + { "a", false }, + { "b", false }, + { "c", 3 } + }; + negativeRes = testSpecification(doc, negative2); + EXPECT_FALSE(negativeRes.success); + ASSERT_EQ(2, negativeRes.offenders.size()); + EXPECT_EQ("a", negativeRes.offenders[0]); + EXPECT_EQ("b", negativeRes.offenders[1]); + + ghoul::Dictionary negative3 { + { "a", false }, + { "b", 1 }, + { "c", false } + }; + negativeRes = testSpecification(doc, negative3); + EXPECT_FALSE(negativeRes.success); + ASSERT_EQ(3, negativeRes.offenders.size()); + EXPECT_EQ("a", negativeRes.offenders[0]); + EXPECT_EQ("b", negativeRes.offenders[1]); + EXPECT_EQ("c", negativeRes.offenders[2]); + + ghoul::Dictionary negative4 { + { "a", false }, + { "b", 10 }, + { "c", false } + }; + negativeRes = testSpecification(doc, negative4); + EXPECT_FALSE(negativeRes.success); + ASSERT_EQ(2, negativeRes.offenders.size()); + EXPECT_EQ("a", negativeRes.offenders[0]); + EXPECT_EQ("c", negativeRes.offenders[1]); +}