Improve key-value tree gmx dump support
authorTeemu Murtola <teemu.murtola@gmail.com>
Sun, 26 Mar 2017 11:31:08 +0000 (14:31 +0300)
committerTeemu Murtola <teemu.murtola@gmail.com>
Tue, 11 Apr 2017 16:36:35 +0000 (18:36 +0200)
Add test coverage for basic option types with the functionality used by
gmx dump, and implement missing types.  Move the dump functionality into
a free function.  Expose the to-string conversion for basic Variant
values outside keyvaluetree.cpp, as that will be at least transitionally
needed elsewhere, and it can also be useful for debugging.

Change-Id: I8d85ea308bec9369557b54528622948ead3fbed5

14 files changed:
src/gromacs/mdtypes/inputrec.cpp
src/gromacs/options/tests/refdata/TreeValueSupportTest_SupportsBooleanOption.xml
src/gromacs/options/tests/refdata/TreeValueSupportTest_SupportsDoubleOption.xml
src/gromacs/options/tests/refdata/TreeValueSupportTest_SupportsEnumIntOption.xml
src/gromacs/options/tests/refdata/TreeValueSupportTest_SupportsEnumOption.xml
src/gromacs/options/tests/refdata/TreeValueSupportTest_SupportsFloatOption.xml
src/gromacs/options/tests/refdata/TreeValueSupportTest_SupportsInt64Option.xml
src/gromacs/options/tests/refdata/TreeValueSupportTest_SupportsIntegerOption.xml
src/gromacs/options/tests/refdata/TreeValueSupportTest_SupportsStringOption.xml
src/gromacs/options/tests/treesupport.cpp
src/gromacs/utility/keyvaluetree.cpp
src/gromacs/utility/keyvaluetree.h
src/gromacs/utility/variant.cpp [new file with mode: 0644]
src/gromacs/utility/variant.h

index 028582d1883c1a959cad9696b890c9087a82142d..3efefc3fe1a9ca53e48049da915f03d3b098123c 100644 (file)
@@ -1001,7 +1001,7 @@ void pr_inputrec(FILE *fp, int indent, const char *title, const t_inputrec *ir,
         {
             gmx::TextWriter writer(fp);
             writer.wrapperSettings().setIndent(indent);
-            ir->params->writeUsing(&writer);
+            gmx::dumpKeyValueTree(&writer, *ir->params);
         }
 
         pr_grp_opts(fp, indent, "grpopts", &(ir->opts), bMDPformat);
index ae2a0b96ea47fd1d6629ac568293002bc3b3abf7..a3dce1faa00d40864ca79046a36f18bdfe8640ec 100644 (file)
@@ -5,4 +5,7 @@
   <Object Name="Adjusted">
     <Bool Name="a">true</Bool>
   </Object>
+  <String Name="Dumped"><![CDATA[
+a                                 = true
+]]></String>
 </ReferenceData>
index 03e296ee08f610d5dd03f2453fc3072c0469ecbb..5f80ef757a99fa7daa1e37f41aa4a4d88932c1d5 100644 (file)
@@ -5,4 +5,7 @@
   <Object Name="Adjusted">
     <Real Name="a">1.5</Real>
   </Object>
+  <String Name="Dumped"><![CDATA[
+a                                 = 1.5
+]]></String>
 </ReferenceData>
index db293136ae96d85a79e6917d08491783d8531b34..69986bb018b67cd57df1fe239fca10d729d55b9c 100644 (file)
@@ -5,4 +5,7 @@
   <Object Name="Adjusted">
     <String Name="a">foo</String>
   </Object>
+  <String Name="Dumped"><![CDATA[
+a                                 = foo
+]]></String>
 </ReferenceData>
index db293136ae96d85a79e6917d08491783d8531b34..69986bb018b67cd57df1fe239fca10d729d55b9c 100644 (file)
@@ -5,4 +5,7 @@
   <Object Name="Adjusted">
     <String Name="a">foo</String>
   </Object>
+  <String Name="Dumped"><![CDATA[
+a                                 = foo
+]]></String>
 </ReferenceData>
index 03e296ee08f610d5dd03f2453fc3072c0469ecbb..5f80ef757a99fa7daa1e37f41aa4a4d88932c1d5 100644 (file)
@@ -5,4 +5,7 @@
   <Object Name="Adjusted">
     <Real Name="a">1.5</Real>
   </Object>
+  <String Name="Dumped"><![CDATA[
+a                                 = 1.5
+]]></String>
 </ReferenceData>
index d8e09c71662be8f811258e61ce84158bd760c625..af8c78c1492509cabb057f99c93c8b8f76da6151 100644 (file)
@@ -5,4 +5,7 @@
   <Object Name="Adjusted">
     <Int64 Name="a">2</Int64>
   </Object>
+  <String Name="Dumped"><![CDATA[
+a                                 = 2
+]]></String>
 </ReferenceData>
index bed5abe4f351b1c8caa0e96edf2ab83136107b4d..4d4d9dc5aa3de005d37b1c7b4e2d3d02acd749bc 100644 (file)
@@ -5,4 +5,7 @@
   <Object Name="Adjusted">
     <Int Name="a">2</Int>
   </Object>
+  <String Name="Dumped"><![CDATA[
+a                                 = 2
+]]></String>
 </ReferenceData>
index de8351cf222a006b05d2f89e969d3cd59010be16..5ca950c426cd8f2db3a95fe45ed7339e7f6fa889 100644 (file)
@@ -5,4 +5,7 @@
   <Object Name="Adjusted">
     <String Name="a">s</String>
   </Object>
+  <String Name="Dumped"><![CDATA[
+a                                 = s
+]]></String>
 </ReferenceData>
index 390a375f231b1c52f8b9111c8a4cc15517f0ccd4..53568c774ad2cacfcf6550cb27b388a47a74cd51 100644 (file)
 #include "gromacs/options/optionsection.h"
 #include "gromacs/options/repeatingsection.h"
 #include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/inmemoryserializer.h"
 #include "gromacs/utility/keyvaluetree.h"
 #include "gromacs/utility/keyvaluetreebuilder.h"
+#include "gromacs/utility/keyvaluetreeserializer.h"
+#include "gromacs/utility/stringstream.h"
+#include "gromacs/utility/textwriter.h"
 
 #include "testutils/refdata.h"
 #include "testutils/testasserts.h"
@@ -231,8 +235,17 @@ class TreeValueSupportTest : public ::testing::Test
             gmx::test::TestReferenceChecker checker(refdata.rootChecker());
             gmx::KeyValueTreeObject         tree(builder_.build());
             checker.checkKeyValueTreeObject(tree, "Input");
+            // Check that adjustment works.
             ASSERT_NO_THROW_GMX(tree = gmx::adjustKeyValueTreeFromOptions(tree, options_));
             checker.checkKeyValueTreeObject(tree, "Adjusted");
+            // Check that assignment works.
+            ASSERT_NO_THROW_GMX(gmx::assignOptionsFromKeyValueTree(&options_, tree, nullptr));
+            {
+                gmx::StringOutputStream stream;
+                gmx::TextWriter         writer(&stream);
+                ASSERT_NO_THROW_GMX(gmx::dumpKeyValueTree(&writer, tree));
+                checker.checkTextBlock(stream.toString(), "Dumped");
+            }
         }
 
         gmx::Options              options_;
index c3791a72cf443c71ce69846f2c4d854d35e1f61e..942e60d53320b4cca6ec266efa2366aac40b4c60 100644 (file)
@@ -41,6 +41,7 @@
 
 #include "gromacs/utility/compare.h"
 #include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/strconvert.h"
 #include "gromacs/utility/stringutil.h"
 #include "gromacs/utility/textwriter.h"
 
@@ -58,21 +59,6 @@ std::vector<std::string> splitPathElements(const std::string &path)
     return splitDelimitedString(path.substr(1), '/');
 }
 
-//! Helper function to format a simple KeyValueTreeValue.
-std::string formatSingleValue(const KeyValueTreeValue &value)
-{
-    if (value.isType<float>())
-    {
-        return formatString("%g", value.cast<float>());
-    }
-    else if (value.isType<double>())
-    {
-        return formatString("%g", value.cast<double>());
-    }
-    GMX_RELEASE_ASSERT(false, "Unknown value type");
-    return std::string();
-}
-
 }   // namespace
 
 /********************************************************************
@@ -116,9 +102,14 @@ bool KeyValueTreeObject::hasDistinctProperties(const KeyValueTreeObject &obj) co
     return true;
 }
 
-void KeyValueTreeObject::writeUsing(TextWriter *writer) const
+/********************************************************************
+ * Key value tree dump
+ */
+
+//! \cond libapi
+void dumpKeyValueTree(TextWriter *writer, const KeyValueTreeObject &tree)
 {
-    for (const auto &prop : properties())
+    for (const auto &prop : tree.properties())
     {
         const auto &value = prop.value();
         if (value.isObject())
@@ -127,7 +118,7 @@ void KeyValueTreeObject::writeUsing(TextWriter *writer) const
             writer->writeLine(":");
             int oldIndent = writer->wrapperSettings().indent();
             writer->wrapperSettings().setIndent(oldIndent + 2);
-            value.asObject().writeUsing(writer);
+            dumpKeyValueTree(writer, value.asObject());
             writer->wrapperSettings().setIndent(oldIndent);
         }
         else
@@ -143,18 +134,19 @@ void KeyValueTreeObject::writeUsing(TextWriter *writer) const
                     GMX_RELEASE_ASSERT(!elem.isObject() && !elem.isArray(),
                                        "Arrays of objects not currently implemented");
                     writer->writeString(" ");
-                    writer->writeString(formatSingleValue(elem));
+                    writer->writeString(simpleValueToString(elem));
                 }
                 writer->writeString(" ]");
             }
             else
             {
-                writer->writeString(formatSingleValue(value));
+                writer->writeString(simpleValueToString(value));
             }
             writer->writeLine();
         }
     }
 }
+//! \endcond
 
 /********************************************************************
  * Key value tree comparison
@@ -284,7 +276,7 @@ class CompareHelper
             {
                 return "present";
             }
-            return formatSingleValue(value);
+            return simpleValueToString(value);
         }
 
         KeyValueTreePath  currentPath_;
index b7afb9dc88a1104a5c697159edf07b784952f8dd..61f4bdab41a277aab38fa8c563e6b0290da29dac 100644 (file)
@@ -259,15 +259,6 @@ class KeyValueTreeObject
          */
         bool hasDistinctProperties(const KeyValueTreeObject &obj) const;
 
-        /*! \brief
-         * Writes a string representation of the object with given writer.
-         *
-         * The output format is designed to be readable by humans; if some
-         * particular machine-readable format is needed, that should be
-         * implemented outside the generic key-value tree code.
-         */
-        void writeUsing(TextWriter *writer) const;
-
     private:
         //! Keeps the properties by key.
         std::map<std::string, KeyValueTreeValue> valueMap_;
@@ -307,6 +298,17 @@ inline KeyValueTreeObject &KeyValueTreeValue::asObject()
 }
 
 //! \cond libapi
+/*! \brief
+ * Writes a human-readable representation of the tree with given writer.
+ *
+ * The output format is designed to be readable by humans; if some
+ * particular machine-readable format is needed, that should be
+ * implemented outside the generic key-value tree code.
+ *
+ * \ingroup module_utility
+ */
+void dumpKeyValueTree(TextWriter *writer, const KeyValueTreeObject &tree);
+
 /*! \brief
  * Compares two KeyValueTrees and prints any differences.
  *
@@ -317,6 +319,14 @@ void compareKeyValueTrees(TextWriter               *writer,
                           const KeyValueTreeObject &tree2,
                           real                      ftol,
                           real                      abstol);
+
+//! Helper function to format a simple KeyValueTreeValue.
+static inline std::string
+simpleValueToString(const KeyValueTreeValue &value)
+{
+    return simpleValueToString(value.asVariant());
+}
+
 //! \endcond
 
 } // namespace gmx
diff --git a/src/gromacs/utility/variant.cpp b/src/gromacs/utility/variant.cpp
new file mode 100644 (file)
index 0000000..b5601af
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements functionality from variant.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_utility
+ */
+#include "gmxpre.h"
+
+#include "variant.h"
+
+#include <string>
+
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/strconvert.h"
+
+namespace gmx
+{
+
+//! \cond libapi
+std::string simpleValueToString(const Variant &value)
+{
+    if (value.isType<bool>())
+    {
+        // TODO: Consider if this would be better as yes/no instead of
+        // true/false.
+        return toString(value.cast<bool>());
+    }
+    else if (value.isType<float>())
+    {
+        return toString(value.cast<float>());
+    }
+    else if (value.isType<double>())
+    {
+        return toString(value.cast<double>());
+    }
+    else if (value.isType<int>())
+    {
+        return toString(value.cast<int>());
+    }
+    else if (value.isType<gmx_int64_t>())
+    {
+        return toString(value.cast<gmx_int64_t>());
+    }
+    else if (value.isType<std::string>())
+    {
+        return value.cast<std::string>();
+    }
+    GMX_RELEASE_ASSERT(false, "Unknown value type");
+    return std::string();
+}
+//! \endcond
+
+} // namespace gmx
index c130bdb7dce3520581032f22328adee7f6320720..9d1b37de28a752e37be25b34dd4e2c27b4aeb332 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -44,6 +44,7 @@
 #define GMX_UTILITY_VARIANT_H
 
 #include <memory>
+#include <string>
 #include <type_traits>
 #include <typeindex>
 #include <typeinfo>
@@ -248,6 +249,18 @@ class Variant
         std::unique_ptr<IContent> content_;
 };
 
+//! \cond libapi
+/*! \brief
+ * Converts a Variant value to a string.
+ *
+ * As the name suggests, only some types of "simple" values (such as int) are
+ * supported.  Asserts for unsupported types.
+ *
+ * \ingroup module_utility
+ */
+std::string simpleValueToString(const Variant &value);
+//! \endcond
+
 } // namespace gmx
 
 #endif