Fix tune_pme
[gromacs.git] / src / gromacs / options / tests / treesupport.cpp
blob5ff24135c231f632ff2f83b434aba5165b935de7
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2016,2017,2018, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
35 /*! \internal \file
36 * \brief
37 * Tests option support for operations on KeyValueTree.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_options
42 #include "gmxpre.h"
44 #include "gromacs/options/treesupport.h"
46 #include <string>
47 #include <vector>
49 #include <gtest/gtest.h>
51 #include "gromacs/options/basicoptions.h"
52 #include "gromacs/options/options.h"
53 #include "gromacs/options/optionsection.h"
54 #include "gromacs/options/repeatingsection.h"
55 #include "gromacs/utility/exceptions.h"
56 #include "gromacs/utility/inmemoryserializer.h"
57 #include "gromacs/utility/keyvaluetree.h"
58 #include "gromacs/utility/keyvaluetreebuilder.h"
59 #include "gromacs/utility/keyvaluetreeserializer.h"
60 #include "gromacs/utility/stringstream.h"
61 #include "gromacs/utility/stringutil.h"
62 #include "gromacs/utility/textwriter.h"
64 #include "testutils/refdata.h"
65 #include "testutils/testasserts.h"
67 namespace
70 /********************************************************************
71 * Tests for assignOptionsFromKeyValueTree()
74 TEST(TreeValueSupportAssignTest, AssignsFromTree)
76 int a0 = 0, a1 = 0;
77 std::string b1;
79 gmx::Options options;
80 options.addOption(gmx::IntegerOption("a").store(&a0));
81 auto sec = options.addSection(gmx::OptionSection("s"));
82 sec.addOption(gmx::IntegerOption("a").store(&a1));
83 sec.addOption(gmx::StringOption("b").store(&b1));
85 gmx::KeyValueTreeBuilder builder;
86 builder.rootObject().addValue<int>("a", 2);
87 auto obj = builder.rootObject().addObject("s");
88 obj.addValue<int>("a", 1);
89 obj.addValue<std::string>("b", "foo");
90 gmx::KeyValueTreeObject tree = builder.build();
92 ASSERT_NO_THROW_GMX(gmx::assignOptionsFromKeyValueTree(&options, tree, nullptr));
93 EXPECT_NO_THROW_GMX(options.finish());
95 EXPECT_EQ(2, a0);
96 EXPECT_EQ(1, a1);
97 EXPECT_EQ("foo", b1);
100 struct SectionData
102 int a;
105 TEST(TreeValueSupportAssignTest, AssignsFromTreeWithArrays)
107 std::vector<int> a0;
108 std::vector<SectionData> s;
110 gmx::Options options;
111 options.addOption(gmx::IntegerOption("a").storeVector(&a0).multiValue());
112 auto sec = options.addSection(gmx::RepeatingOptionSection<SectionData>("s").storeVector(&s));
113 sec.addOption(gmx::IntegerOption("a").store(&sec.bind().a));
115 gmx::KeyValueTreeBuilder builder;
116 auto array = builder.rootObject().addUniformArray<int>("a");
117 array.addValue(1);
118 array.addValue(2);
119 auto objArray = builder.rootObject().addObjectArray("s");
120 auto obj1 = objArray.addObject();
121 obj1.addValue<int>("a", 3);
122 auto obj2 = objArray.addObject();
123 obj2.addValue<int>("a", 4);
124 gmx::KeyValueTreeObject tree = builder.build();
126 ASSERT_NO_THROW_GMX(gmx::assignOptionsFromKeyValueTree(&options, tree, nullptr));
127 EXPECT_NO_THROW_GMX(options.finish());
129 ASSERT_EQ(2U, a0.size());
130 EXPECT_EQ(1, a0[0]);
131 EXPECT_EQ(2, a0[1]);
132 ASSERT_EQ(2U, s.size());
133 EXPECT_EQ(3, s[0].a);
134 EXPECT_EQ(4, s[1].a);
137 TEST(TreeValueSupportAssignErrorTest, HandlesInvalidValue)
139 int a1 = 0;
141 gmx::Options options;
142 auto sec = options.addSection(gmx::OptionSection("s"));
143 sec.addOption(gmx::IntegerOption("a").store(&a1));
145 gmx::KeyValueTreeBuilder builder;
146 auto obj = builder.rootObject().addObject("s");
147 obj.addValue<std::string>("a", "foo");
148 gmx::KeyValueTreeObject tree = builder.build();
150 EXPECT_THROW_GMX(gmx::assignOptionsFromKeyValueTree(&options, tree, nullptr),
151 gmx::InvalidInputError);
154 /********************************************************************
155 * Tests for checkForUnknownOptionsInKeyValueTree()
158 class TreeValueSupportCheckTest : public ::testing::Test
160 public:
161 TreeValueSupportCheckTest()
163 auto sec1 = options_.addSection(gmx::OptionSection("s"));
164 auto sec2 = options_.addSection(gmx::OptionSection("r"));
165 options_.addOption(gmx::IntegerOption("a"));
166 sec1.addOption(gmx::IntegerOption("a"));
167 sec1.addOption(gmx::IntegerOption("b"));
168 sec2.addOption(gmx::IntegerOption("b"));
171 gmx::Options options_;
172 gmx::KeyValueTreeBuilder builder_;
175 TEST_F(TreeValueSupportCheckTest, HandlesEmpty)
177 EXPECT_NO_THROW_GMX(gmx::checkForUnknownOptionsInKeyValueTree(builder_.build(), options_));
180 TEST_F(TreeValueSupportCheckTest, HandlesMatchingTree)
182 auto root = builder_.rootObject();
183 root.addValue<int>("a", 1);
184 auto obj1 = root.addObject("s");
185 obj1.addValue<int>("a", 1);
186 obj1.addValue<int>("b", 2);
187 auto obj2 = root.addObject("r");
188 obj2.addValue<int>("b", 3);
190 EXPECT_NO_THROW_GMX(gmx::checkForUnknownOptionsInKeyValueTree(builder_.build(), options_));
193 TEST_F(TreeValueSupportCheckTest, HandlesSmallerTree1)
195 auto root = builder_.rootObject();
196 root.addValue<int>("a", 1);
197 auto obj1 = root.addObject("s");
198 obj1.addValue<int>("b", 2);
200 EXPECT_NO_THROW_GMX(gmx::checkForUnknownOptionsInKeyValueTree(builder_.build(), options_));
203 TEST_F(TreeValueSupportCheckTest, HandlesSmallerTree2)
205 auto root = builder_.rootObject();
206 auto obj1 = root.addObject("s");
207 obj1.addValue<int>("a", 1);
208 obj1.addValue<int>("b", 2);
210 EXPECT_NO_THROW_GMX(gmx::checkForUnknownOptionsInKeyValueTree(builder_.build(), options_));
213 TEST_F(TreeValueSupportCheckTest, DetectsExtraValue)
215 auto root = builder_.rootObject();
216 auto obj2 = root.addObject("r");
217 obj2.addValue<int>("a", 1);
218 obj2.addValue<int>("b", 3);
220 EXPECT_THROW_GMX(gmx::checkForUnknownOptionsInKeyValueTree(builder_.build(), options_),
221 gmx::InvalidInputError);
224 /********************************************************************
225 * Tests for adjustKeyValueTreeFromOptions()
228 class TreeValueSupportAdjustTest : public ::testing::Test
230 public:
231 void runTest()
233 gmx::test::TestReferenceData refdata;
234 gmx::test::TestReferenceChecker checker(refdata.rootChecker());
235 gmx::KeyValueTreeObject tree(builder_.build());
236 checker.checkKeyValueTreeObject(tree, "Input");
237 ASSERT_NO_THROW_GMX(tree = gmx::adjustKeyValueTreeFromOptions(tree, options_));
238 checker.checkKeyValueTreeObject(tree, "Output");
241 gmx::Options options_;
242 gmx::KeyValueTreeBuilder builder_;
245 TEST_F(TreeValueSupportAdjustTest, FillsDefaultValues)
247 options_.addOption(gmx::IntegerOption("a").defaultValue(2));
248 runTest();
251 TEST_F(TreeValueSupportAdjustTest, FillsDefaultVectorValues)
253 int v[3] = {1, 2, 3};
254 options_.addOption(gmx::IntegerOption("a").store(v).vector());
255 runTest();
258 TEST_F(TreeValueSupportAdjustTest, FillsDefaultObjectValues)
260 auto sec1 = options_.addSection(gmx::OptionSection("s"));
261 sec1.addOption(gmx::IntegerOption("a").defaultValue(1));
262 auto sec2 = options_.addSection(gmx::OptionSection("r"));
263 sec2.addOption(gmx::IntegerOption("a").defaultValue(2));
264 options_.addOption(gmx::IntegerOption("a").defaultValue(3));
265 runTest();
268 TEST_F(TreeValueSupportAdjustTest, NormalizesValues)
270 options_.addOption(gmx::IntegerOption("a"));
271 builder_.rootObject().addValue<std::string>("a", "2");
272 runTest();
275 TEST_F(TreeValueSupportAdjustTest, MergesDefaultValues)
277 builder_.rootObject().addValue<int>("b", 1);
278 options_.addOption(gmx::IntegerOption("a").defaultValue(2));
279 options_.addOption(gmx::IntegerOption("b").defaultValue(3));
280 runTest();
283 TEST_F(TreeValueSupportAdjustTest, OrdersValues)
285 builder_.rootObject().addValue<int>("a", 1);
286 builder_.rootObject().addValue<int>("c", 1);
287 builder_.rootObject().addValue<int>("b", 1);
288 options_.addOption(gmx::IntegerOption("b").defaultValue(2));
289 options_.addOption(gmx::IntegerOption("a").defaultValue(1));
290 options_.addOption(gmx::IntegerOption("c").defaultValue(3));
291 // TODO: This does not actually test the correct ordering, since the
292 // reference data is not currently order-sensitive, but the order can be
293 // checked manually from the reference data.
294 runTest();
297 /********************************************************************
298 * Support for different option types
301 class TreeValueSupportTest : public ::testing::Test
303 public:
304 void runTest()
306 gmx::test::TestReferenceData refdata;
307 gmx::test::TestReferenceChecker checker(refdata.rootChecker());
308 gmx::KeyValueTreeObject tree(builder_.build());
309 checker.checkKeyValueTreeObject(tree, "Input");
310 // Check that adjustment works.
311 ASSERT_NO_THROW_GMX(tree = gmx::adjustKeyValueTreeFromOptions(tree, options_));
312 checker.checkKeyValueTreeObject(tree, "Adjusted");
313 // Check that assignment works.
314 ASSERT_NO_THROW_GMX(gmx::assignOptionsFromKeyValueTree(&options_, tree, nullptr));
315 // Check that serialization works.
317 std::vector<char> buffer = serializeTree(tree);
318 gmx::InMemoryDeserializer deserializer(buffer);
319 gmx::KeyValueTreeObject output
320 = gmx::deserializeKeyValueTree(&deserializer);
321 SCOPED_TRACE("After serialization/deserialization\n Buffer: "
322 + formatBuffer(buffer));
323 checker.checkKeyValueTreeObject(output, "Adjusted");
325 // Check that dumping works.
327 gmx::StringOutputStream stream;
328 gmx::TextWriter writer(&stream);
329 ASSERT_NO_THROW_GMX(gmx::dumpKeyValueTree(&writer, tree));
330 checker.checkTextBlock(stream.toString(), "Dumped");
332 // Check that comparison works.
334 gmx::StringOutputStream stream;
335 gmx::TextWriter writer(&stream);
336 ASSERT_NO_THROW_GMX(gmx::compareKeyValueTrees(&writer, tree, tree, 0.0, 0.0));
337 checker.checkTextBlock(stream.toString(), "Compared");
339 // Check that comparison works against an empty tree.
341 gmx::StringOutputStream stream;
342 gmx::TextWriter writer(&stream);
343 gmx::KeyValueTreeObject empty;
344 ASSERT_NO_THROW_GMX(gmx::compareKeyValueTrees(&writer, tree, empty, 0.0, 0.0));
345 checker.checkTextBlock(stream.toString(), "ComparedAgainstEmpty");
349 gmx::Options options_;
350 gmx::KeyValueTreeBuilder builder_;
352 private:
353 std::vector<char> serializeTree(const gmx::KeyValueTreeObject &tree)
355 gmx::InMemorySerializer serializer;
356 gmx::serializeKeyValueTree(tree, &serializer);
357 return serializer.finishAndGetBuffer();
360 std::string formatBuffer(const std::vector<char> &buffer)
362 return gmx::formatAndJoin(buffer, " ", [](char c) { return gmx::formatString("%02x", static_cast<unsigned char>(c)); });
366 TEST_F(TreeValueSupportTest, SupportsBooleanOption)
368 options_.addOption(gmx::BooleanOption("a").defaultValue(true));
369 runTest();
372 TEST_F(TreeValueSupportTest, SupportsIntegerOption)
374 options_.addOption(gmx::IntegerOption("a").defaultValue(2));
375 runTest();
378 TEST_F(TreeValueSupportTest, SupportsInt64Option)
380 options_.addOption(gmx::Int64Option("a").defaultValue(2));
381 runTest();
384 TEST_F(TreeValueSupportTest, SupportsStringOption)
386 options_.addOption(gmx::StringOption("a").defaultValue("s"));
387 runTest();
390 TEST_F(TreeValueSupportTest, SupportsFloatOption)
392 options_.addOption(gmx::FloatOption("a").defaultValue(1.5));
393 runTest();
396 TEST_F(TreeValueSupportTest, SupportsDoubleOption)
398 options_.addOption(gmx::DoubleOption("a").defaultValue(1.5));
399 runTest();
402 TEST_F(TreeValueSupportTest, SupportsEnumIntOption)
404 const char *const values[] = {"foo", "bar"};
405 options_.addOption(gmx::EnumIntOption("a").enumValue(values).defaultValue(0));
406 runTest();
409 //! Enum for testing EnumOption.
410 enum class TestEnum
412 Foo, Bar
415 TEST_F(TreeValueSupportTest, SupportsEnumOption)
417 const char *const values[] = {"foo", "bar"};
418 options_.addOption(gmx::EnumOption<TestEnum>("a").enumValue(values)
419 .defaultValue(TestEnum::Foo));
420 runTest();
423 } // namespace