Support KeyValueTree objects in refdata
authorTeemu Murtola <teemu.murtola@gmail.com>
Fri, 29 Jul 2016 04:45:33 +0000 (07:45 +0300)
committerDavid van der Spoel <davidvanderspoel@gmail.com>
Sat, 27 Aug 2016 17:58:00 +0000 (19:58 +0200)
Make it possible to check KeyValueTreeObject-rooted trees through
TestReferenceData.  This is the simplest way to actually verify the
contents of such a data structure.  For now, the contents can be
inspected through the XML output.

Change-Id: Ic6fc87c4934166acad4e9b5068c6d39c02866a73

src/testutils/refdata.cpp
src/testutils/refdata.h
src/testutils/tests/refdata_tests.cpp

index beb3369ae28a8596d8f4d56e33d886032134ea0c..0824cb93f46ed698a6919651fb3e9b8cb5eb81f2 100644 (file)
 #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"
@@ -394,6 +396,8 @@ class TestReferenceChecker::Impl
         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.
@@ -553,6 +557,7 @@ const char *const TestReferenceChecker::Impl::cUInt64NodeName     = "UInt64";
 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";
 
@@ -958,6 +963,64 @@ void TestReferenceChecker::checkVector(const double value[3], const char *id)
 }
 
 
+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)
 {
index 9314d8bbe3892cad05b6ca2ddd508df242fad0ce..61904f6b40c304978f1a7ba41f413f44ede3aada 100644 (file)
@@ -56,6 +56,9 @@ namespace gmx
 {
 
 class IOptionsContainer;
+class KeyValueTreeObject;
+class KeyValueTreeValue;
+class Variant;
 
 namespace test
 {
@@ -367,6 +370,12 @@ class TestReferenceChecker
         void checkVector(const double value[3], const char *id);
         //! Check a single floating-point value from a string.
         void checkRealFromString(const std::string &value, const char *id);
+        //! Checks a variant value that contains a supported simple type.
+        void checkVariant(const Variant &value, const char *id);
+        //! Checks a key-value tree rooted at a object.
+        void checkKeyValueTreeObject(const KeyValueTreeObject &tree, const char *id);
+        //! Checks a generic key-value tree value.
+        void checkKeyValueTreeValue(const KeyValueTreeValue &value, const char *id);
 
         /*! \name Overloaded versions of simple checker methods
          *
@@ -432,6 +441,11 @@ class TestReferenceChecker
         {
             checkVector(value, id);
         }
+        //! Check a generic key-value tree value.
+        void checkValue(const KeyValueTreeValue &value, const char *id)
+        {
+            checkKeyValueTreeValue(value, id);
+        }
         /*!\}*/
 
         /*! \brief
index 9f00a19dedca9f3d28e3c3ee89ade5c24737e7c2..d89226439aa79c5baef2fce8185e60f5586c52ac 100644 (file)
 
 #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"
 
@@ -339,6 +344,134 @@ TEST(ReferenceDataTest, HandlesUncheckedDataInCompound)
 }
 
 
+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 };