Allow Documentations to be exhaustive to not allow extra dictionaries

This commit is contained in:
Alexander Bock
2016-09-16 16:53:50 +02:00
parent 24e5146a8e
commit 2fa6471e98
5 changed files with 103 additions and 7 deletions

View File

@@ -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<documentation::DocumentationEntry>;
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;
};

View File

@@ -71,13 +71,14 @@ struct StringVerifier : public TemplateVerifier<std::string> {
};
struct TableVerifier : public TemplateVerifier<ghoul::Dictionary> {
TableVerifier(std::vector<DocumentationEntry> d = {});
TableVerifier(std::vector<DocumentationEntry> d = {}, Exhaustive exhaustive = Exhaustive::No);
TestResult operator()(const ghoul::Dictionary& dict, const std::string& key) const override;
std::string type() const override;
std::vector<DocumentationEntry> doc;
Exhaustive exhaustive;
};
struct VectorVerifier {};

View File

@@ -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<std::string> uniqueOffenders(
@@ -104,7 +131,7 @@ TestResult testSpecification(const Documentation& d, const ghoul::Dictionary& di
);
result.offenders = std::vector<std::string>(
uniqueOffenders.begin(), uniqueOffenders.end()
);
);
return result;
}

View File

@@ -140,8 +140,9 @@ std::string StringVerifier::type() const {
return "String";
}
TableVerifier::TableVerifier(std::vector<DocumentationEntry> d)
TableVerifier::TableVerifier(std::vector<DocumentationEntry> 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<Type>(key)) {
ghoul::Dictionary d = dict.value<Type>(key);
TestResult res = testSpecification(doc, d);
TestResult res = testSpecification({ "", doc, exhaustive }, d);
for (std::string& s : res.offenders) {
s = key + "." + s;

View File

@@ -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;