#include "gromacs/options/ioptionscontainer.h"
#include "gromacs/utility/exceptions.h"
#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/keyvaluetree.h"
#include "gromacs/utility/path.h"
#include "gromacs/utility/real.h"
#include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/variant.h"
#include "testutils/refdata-checkers.h"
#include "testutils/refdata-impl.h"
static const char * const cIdAttrName;
//! String constant for naming compounds for vectors.
static const char * const cVectorType;
+ //! String constant for naming compounds for key-value tree objects.
+ static const char * const cObjectType;
//! String constant for naming compounds for sequences.
static const char * const cSequenceType;
//! String constant for value identifier for sequence length.
const char *const TestReferenceChecker::Impl::cRealNodeName = "Real";
const char *const TestReferenceChecker::Impl::cIdAttrName = "Name";
const char *const TestReferenceChecker::Impl::cVectorType = "Vector";
+const char *const TestReferenceChecker::Impl::cObjectType = "Object";
const char *const TestReferenceChecker::Impl::cSequenceType = "Sequence";
const char *const TestReferenceChecker::Impl::cSequenceLengthName = "Length";
}
+void TestReferenceChecker::checkVariant(const Variant &variant, const char *id)
+{
+ if (variant.isType<bool>())
+ {
+ checkBoolean(variant.cast<bool>(), id);
+ }
+ else if (variant.isType<int>())
+ {
+ checkInteger(variant.cast<int>(), id);
+ }
+ else if (variant.isType<float>())
+ {
+ checkFloat(variant.cast<float>(), id);
+ }
+ else if (variant.isType<double>())
+ {
+ checkDouble(variant.cast<double>(), id);
+ }
+ else if (variant.isType<std::string>())
+ {
+ checkString(variant.cast<std::string>(), id);
+ }
+ else
+ {
+ GMX_THROW(TestException("Unsupported variant type"));
+ }
+}
+
+
+void TestReferenceChecker::checkKeyValueTreeObject(const KeyValueTreeObject &tree, const char *id)
+{
+ TestReferenceChecker compound(checkCompound(Impl::cObjectType, id));
+ for (const auto &prop : tree.properties())
+ {
+ compound.checkKeyValueTreeValue(prop.value(), prop.key().c_str());
+ }
+ compound.checkUnusedEntries();
+}
+
+
+void TestReferenceChecker::checkKeyValueTreeValue(const KeyValueTreeValue &value, const char *id)
+{
+ if (value.isObject())
+ {
+ checkKeyValueTreeObject(value.asObject(), id);
+ }
+ else if (value.isArray())
+ {
+ const auto &values = value.asArray().values();
+ checkSequence(values.begin(), values.end(), id);
+ }
+ else
+ {
+ checkVariant(value.asVariant(), id);
+ }
+}
+
+
TestReferenceChecker
TestReferenceChecker::checkSequenceCompound(const char *id, size_t length)
{
#include "testutils/refdata.h"
+#include <string>
#include <vector>
#include <gtest/gtest.h>
#include <gtest/gtest-spi.h>
+#include "gromacs/utility/keyvaluetree.h"
+#include "gromacs/utility/keyvaluetreebuilder.h"
+#include "gromacs/utility/variant.h"
+
#include "testutils/testasserts.h"
#include "testutils/testexceptions.h"
}
+TEST(ReferenceDataTest, HandlesVariants)
+{
+ using gmx::Variant;
+ {
+ TestReferenceData data(gmx::test::erefdataUpdateAll);
+ TestReferenceChecker checker(data.rootChecker());
+ checker.checkVariant(Variant::create<bool>(true), "bool");
+ checker.checkVariant(Variant::create<int>(1), "int");
+ checker.checkVariant(Variant::create<double>(3.5), "real");
+ checker.checkVariant(Variant::create<std::string>("foo"), "str");
+ }
+ {
+ TestReferenceData data(gmx::test::erefdataCompare);
+ TestReferenceChecker checker(data.rootChecker());
+ checker.checkVariant(Variant::create<bool>(true), "bool");
+ checker.checkVariant(Variant::create<int>(1), "int");
+ checker.checkVariant(Variant::create<double>(3.5), "real");
+ checker.checkVariant(Variant::create<std::string>("foo"), "str");
+ }
+}
+
+//! Helper for building a KeyValueTree for testing.
+gmx::KeyValueTreeObject buildKeyValueTree(bool full)
+{
+ gmx::KeyValueTreeBuilder builder;
+ auto root = builder.rootObject();
+ auto obj = root.addObject("o");
+ obj.addValue<int>("i", 1);
+ if (full)
+ {
+ obj.addValue<std::string>("s", "x");
+ }
+ auto arr = root.addUniformArray<int>("a");
+ arr.addValue(2);
+ arr.addValue(3);
+ root.addValue<std::string>("s", "y");
+ return builder.build();
+}
+
+
+TEST(ReferenceDataTest, HandlesKeyValueTree)
+{
+ gmx::KeyValueTreeObject tree = buildKeyValueTree(true);
+ {
+ TestReferenceData data(gmx::test::erefdataUpdateAll);
+ TestReferenceChecker checker(data.rootChecker());
+ checker.checkKeyValueTreeObject(tree, "tree");
+ }
+ {
+ TestReferenceData data(gmx::test::erefdataCompare);
+ TestReferenceChecker checker(data.rootChecker());
+ checker.checkKeyValueTreeObject(tree, "tree");
+ }
+}
+
+
+TEST(ReferenceDataTest, HandlesKeyValueTreeExtraKey)
+{
+ {
+ TestReferenceData data(gmx::test::erefdataUpdateAll);
+ TestReferenceChecker checker(data.rootChecker());
+ checker.checkKeyValueTreeObject(buildKeyValueTree(false), "tree");
+ }
+ {
+ TestReferenceData data(gmx::test::erefdataCompare);
+ TestReferenceChecker checker(data.rootChecker());
+ EXPECT_NONFATAL_FAILURE(checker.checkKeyValueTreeObject(buildKeyValueTree(true), "tree"), "");
+ }
+}
+
+
+TEST(ReferenceDataTest, HandlesKeyValueTreeMissingKey)
+{
+ {
+ TestReferenceData data(gmx::test::erefdataUpdateAll);
+ TestReferenceChecker checker(data.rootChecker());
+ checker.checkKeyValueTreeObject(buildKeyValueTree(true), "tree");
+ }
+ {
+ TestReferenceData data(gmx::test::erefdataCompare);
+ TestReferenceChecker checker(data.rootChecker());
+ EXPECT_NONFATAL_FAILURE(checker.checkKeyValueTreeObject(buildKeyValueTree(false), "tree"), "");
+ }
+}
+
+
+TEST(ReferenceDataTest, HandlesVariantsWithIncorrectValue)
+{
+ using gmx::Variant;
+ {
+ TestReferenceData data(gmx::test::erefdataUpdateAll);
+ TestReferenceChecker checker(data.rootChecker());
+ checker.checkVariant(Variant::create<bool>(true), "bool");
+ checker.checkVariant(Variant::create<int>(1), "int");
+ checker.checkVariant(Variant::create<double>(3.5), "real");
+ checker.checkVariant(Variant::create<std::string>("foo"), "str");
+ }
+ {
+ TestReferenceData data(gmx::test::erefdataCompare);
+ TestReferenceChecker checker(data.rootChecker());
+ EXPECT_NONFATAL_FAILURE(checker.checkVariant(Variant::create<bool>(false), "bool"), "");
+ EXPECT_NONFATAL_FAILURE(checker.checkVariant(Variant::create<int>(2), "int"), "");
+ EXPECT_NONFATAL_FAILURE(checker.checkVariant(Variant::create<double>(2.5), "real"), "");
+ EXPECT_NONFATAL_FAILURE(checker.checkVariant(Variant::create<std::string>("bar"), "str"), "");
+ }
+}
+
+
+TEST(ReferenceDataTest, HandlesVariantsWithIncorrectType)
+{
+ using gmx::Variant;
+ {
+ TestReferenceData data(gmx::test::erefdataUpdateAll);
+ TestReferenceChecker checker(data.rootChecker());
+ checker.checkVariant(Variant::create<bool>(true), "bool");
+ checker.checkVariant(Variant::create<int>(1), "int");
+ checker.checkVariant(Variant::create<double>(3.5), "real");
+ }
+ {
+ TestReferenceData data(gmx::test::erefdataCompare);
+ TestReferenceChecker checker(data.rootChecker());
+ EXPECT_NONFATAL_FAILURE(checker.checkVariant(Variant::create<int>(1), "bool"), "");
+ EXPECT_NONFATAL_FAILURE(checker.checkVariant(Variant::create<bool>(true), "int"), "");
+ EXPECT_NONFATAL_FAILURE(checker.checkVariant(Variant::create<int>(2), "real"), "");
+ }
+}
+
+
TEST(ReferenceDataTest, HandlesMissingReferenceDataFile)
{
const int seq[5] = { -1, 3, 5, 2, 4 };