diff --git a/include/openspace/documentation/documentation.h b/include/openspace/documentation/documentation.h index 746cd8d1d5..91f37c0648 100644 --- a/include/openspace/documentation/documentation.h +++ b/include/openspace/documentation/documentation.h @@ -39,6 +39,7 @@ namespace documentation { struct Verifier; using Optional = ghoul::Boolean; +using Exhaustive = ghoul::Boolean; struct TestResult { bool success; @@ -64,11 +65,13 @@ struct DocumentationEntry { using DocumentationEntries = std::vector; struct Documentation { - Documentation(std::string name = "", DocumentationEntries entries = {}); + Documentation(std::string name = "", DocumentationEntries entries = {}, + Exhaustive exhaustive = Exhaustive::No); Documentation(DocumentationEntries entries); std::string name; DocumentationEntries entries; + Exhaustive exhaustive; }; diff --git a/include/openspace/documentation/verifier.h b/include/openspace/documentation/verifier.h index 4d70d895e8..cd37530e22 100644 --- a/include/openspace/documentation/verifier.h +++ b/include/openspace/documentation/verifier.h @@ -71,13 +71,14 @@ struct StringVerifier : public TemplateVerifier { }; struct TableVerifier : public TemplateVerifier { - TableVerifier(std::vector d = {}); + TableVerifier(std::vector d = {}, Exhaustive exhaustive = Exhaustive::No); TestResult operator()(const ghoul::Dictionary& dict, const std::string& key) const override; std::string type() const override; std::vector doc; + Exhaustive exhaustive; }; struct VectorVerifier {}; diff --git a/src/documentation/documentation.cpp b/src/documentation/documentation.cpp index 5f91fc37d0..b908c5d551 100644 --- a/src/documentation/documentation.cpp +++ b/src/documentation/documentation.cpp @@ -52,9 +52,11 @@ DocumentationEntry::DocumentationEntry(std::string key, Verifier* t, std::string , documentation(std::move(doc)) , optional(optional) {} -Documentation::Documentation(std::string name, DocumentationEntries entries) +Documentation::Documentation(std::string name, DocumentationEntries entries, Exhaustive exhaustive) : name(std::move(name)) - , entries(std::move(entries)) {} + , entries(std::move(entries)) + , exhaustive(std::move(exhaustive)) +{} Documentation::Documentation(DocumentationEntries entries) : Documentation("", std::move(entries)) {} @@ -97,6 +99,31 @@ TestResult testSpecification(const Documentation& d, const ghoul::Dictionary& di } } + if (d.exhaustive) { + // If the documentation is exhaustive, we have to check if there are extra values + // in the table that are not covered by the Documentation + + for (const std::string& key : dictionary.keys()) { + auto it = std::find_if( + d.entries.begin(), + d.entries.end(), + [&key](const DocumentationEntry& entry) { + if (entry.key == Wildcard) { + return true; + } + else { + return entry.key == key; + } + } + ); + + if (it == d.entries.end()) { + result.success = false; + result.offenders.push_back(key); + } + } + } + // Remove duplicate offenders that might occur if multiple rules apply to a single // key and more than one of these rules are broken std::set uniqueOffenders( @@ -104,7 +131,7 @@ TestResult testSpecification(const Documentation& d, const ghoul::Dictionary& di ); result.offenders = std::vector( uniqueOffenders.begin(), uniqueOffenders.end() - ); + ); return result; } diff --git a/src/documentation/verifier.cpp b/src/documentation/verifier.cpp index ddbeab184c..c1db34e868 100644 --- a/src/documentation/verifier.cpp +++ b/src/documentation/verifier.cpp @@ -140,8 +140,9 @@ std::string StringVerifier::type() const { return "String"; } -TableVerifier::TableVerifier(std::vector d) +TableVerifier::TableVerifier(std::vector d, Exhaustive exhaustive) : doc(std::move(d)) + , exhaustive(std::move(exhaustive)) {} TestResult TableVerifier::operator()(const ghoul::Dictionary& dict, @@ -149,7 +150,7 @@ TestResult TableVerifier::operator()(const ghoul::Dictionary& dict, { if (dict.hasKeyAndValue(key)) { ghoul::Dictionary d = dict.value(key); - TestResult res = testSpecification(doc, d); + TestResult res = testSpecification({ "", doc, exhaustive }, d); for (std::string& s : res.offenders) { s = key + "." + s; diff --git a/tests/test_documentation.inl b/tests/test_documentation.inl index 773088cad8..34e8d05f6b 100644 --- a/tests/test_documentation.inl +++ b/tests/test_documentation.inl @@ -639,6 +639,70 @@ TEST_F(DocumentationTest, RequiredInOptional) { EXPECT_EQ("a.b", negativeRes.offenders[0]); } +TEST_F(DocumentationTest, Exhaustive) { + using namespace openspace::documentation; + + Documentation doc { + "Test", + {{ "Int", new IntVerifier }}, + Exhaustive::Yes + }; + + ghoul::Dictionary positive { + { "Int" , 1 } + }; + TestResult positiveRes = testSpecification(doc, positive); + EXPECT_TRUE(positiveRes.success); + EXPECT_EQ(0, positiveRes.offenders.size()); + + ghoul::Dictionary negative { + { "False_Int", 1 } + }; + TestResult negativeRes = testSpecification(doc, negative); + EXPECT_FALSE(negativeRes.success); + ASSERT_EQ(2, negativeRes.offenders.size()); + EXPECT_EQ("False_Int", negativeRes.offenders[0]); + EXPECT_EQ("Int", negativeRes.offenders[1]); + + ghoul::Dictionary negative2 { + { "Double", 2.0 } + }; + negativeRes = testSpecification(doc, negative2); + EXPECT_FALSE(negativeRes.success); + ASSERT_EQ(2, negativeRes.offenders.size()); + EXPECT_EQ("Double", negativeRes.offenders[0]); + EXPECT_EQ("Int", negativeRes.offenders[1]); +} + +TEST_F(DocumentationTest, NestedExhaustive) { + using namespace openspace::documentation; + + Documentation doc { + "Test", + {{ "Table", new TableVerifier( + { { "a", new IntVerifier } }, + Exhaustive::Yes + ) + }} + }; + + ghoul::Dictionary positive { + { "Table", ghoul::Dictionary{{ "a", 1 }}} + }; + TestResult positiveRes = testSpecification(doc, positive); + EXPECT_TRUE(positiveRes.success); + EXPECT_EQ(0, positiveRes.offenders.size()); + + ghoul::Dictionary negative { + { "Table", ghoul::Dictionary{{ "b", 2.0 }}} + }; + TestResult negativeRes = testSpecification(doc, negative); + EXPECT_FALSE(negativeRes.success); + ASSERT_EQ(2, negativeRes.offenders.size()); + EXPECT_EQ("Table.a", negativeRes.offenders[0]); + EXPECT_EQ("Table.b", negativeRes.offenders[1]); +} + TEST_F(DocumentationTest, LessInt) { using namespace openspace::documentation;