2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2016,2017,2019,2020, 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.
37 #include "gromacs/utility/keyvaluetreetransform.h"
42 #include <gtest/gtest.h>
44 #include "gromacs/utility/exceptions.h"
45 #include "gromacs/utility/keyvaluetree.h"
46 #include "gromacs/utility/keyvaluetreebuilder.h"
47 #include "gromacs/utility/strconvert.h"
48 #include "gromacs/utility/stringcompare.h"
49 #include "gromacs/utility/stringutil.h"
51 #include "testutils/refdata.h"
52 #include "testutils/testasserts.h"
57 class TreeValueTransformTest : public ::testing::Test
60 void testTransform(const gmx::KeyValueTreeObject& input, const gmx::KeyValueTreeTransformer& transform)
62 gmx::KeyValueTreeTransformResult result = transform.transform(input, nullptr);
63 gmx::KeyValueTreeObject object = result.object();
65 gmx::test::TestReferenceData data;
66 gmx::test::TestReferenceChecker checker(data.rootChecker());
67 checker.checkKeyValueTreeObject(input, "Input");
68 auto mappedPaths = transform.mappedPaths();
69 checker.checkSequence(
70 mappedPaths.begin(), mappedPaths.end(), "MappedPaths", &TreeValueTransformTest::checkMappedPath);
71 checker.checkKeyValueTreeObject(object, "Tree");
72 checkBackMapping(&checker, object, result.backMapping());
76 static void checkMappedPath(gmx::test::TestReferenceChecker* checker, const gmx::KeyValueTreePath& path)
78 checker->checkString(path.toString(), nullptr);
80 void checkBackMapping(gmx::test::TestReferenceChecker* checker,
81 const gmx::KeyValueTreeObject& object,
82 const gmx::IKeyValueTreeBackMapping& mapping)
84 auto compound(checker->checkCompound("BackMapping", "Mapping"));
85 checkBackMappingImpl(&compound, object, mapping, gmx::KeyValueTreePath());
88 void checkBackMappingImpl(gmx::test::TestReferenceChecker* checker,
89 const gmx::KeyValueTreeObject& object,
90 const gmx::IKeyValueTreeBackMapping& mapping,
91 const gmx::KeyValueTreePath& prefix)
93 for (const auto& prop : object.properties())
95 gmx::KeyValueTreePath path = prefix;
96 path.append(prop.key());
97 if (prop.value().isObject())
99 checkBackMappingImpl(checker, prop.value().asObject(), mapping, path);
103 gmx::KeyValueTreePath orgPath = mapping.originalPath(path);
104 checker->checkString(orgPath.toString(), path.toString().c_str());
110 TEST_F(TreeValueTransformTest, SimpleTransforms)
112 gmx::KeyValueTreeBuilder builder;
113 builder.rootObject().addValue<std::string>("a", "1");
114 builder.rootObject().addValue<std::string>("b", "2");
115 gmx::KeyValueTreeObject input = builder.build();
117 gmx::KeyValueTreeTransformer transform;
118 transform.rules()->addRule().from<std::string>("/a").to<int>("/i").transformWith(
119 &gmx::fromStdString<int>);
120 transform.rules()->addRule().from<std::string>("/b").to<int>("/j").transformWith(
121 &gmx::fromStdString<int>);
123 testTransform(input, transform);
126 TEST_F(TreeValueTransformTest, SimpleTransformsCaseAndDashInsensitive)
128 gmx::KeyValueTreeBuilder builder;
129 builder.rootObject().addValue<std::string>("a-x", "1");
130 builder.rootObject().addValue<std::string>("by", "2");
131 gmx::KeyValueTreeObject input = builder.build();
133 gmx::KeyValueTreeTransformer transform;
134 transform.rules()->addRule().keyMatchType("/", gmx::StringCompareType::CaseAndDashInsensitive);
135 transform.rules()->addRule().from<std::string>("/Ax").to<int>("/i").transformWith(
136 &gmx::fromStdString<int>);
137 transform.rules()->addRule().from<std::string>("/B-Y").to<int>("/j").transformWith(
138 &gmx::fromStdString<int>);
140 testTransform(input, transform);
143 TEST_F(TreeValueTransformTest, SimpleTransformsToObject)
145 gmx::KeyValueTreeBuilder builder;
146 builder.rootObject().addValue<std::string>("a", "1");
147 builder.rootObject().addValue<std::string>("b", "2");
148 gmx::KeyValueTreeObject input = builder.build();
150 gmx::KeyValueTreeTransformer transform;
151 transform.rules()->addRule().from<std::string>("/a").to<int>("/foo/i").transformWith(
152 &gmx::fromStdString<int>);
153 transform.rules()->addRule().from<std::string>("/b").to<int>("/foo/j").transformWith(
154 &gmx::fromStdString<int>);
156 testTransform(input, transform);
160 TEST_F(TreeValueTransformTest, ObjectFromString)
162 gmx::KeyValueTreeBuilder builder;
163 builder.rootObject().addValue<std::string>("a", "1 2");
164 gmx::KeyValueTreeObject input = builder.build();
166 gmx::KeyValueTreeTransformer transform;
167 transform.rules()->addRule().from<std::string>("/a").toObject("/foo").transformWith(
168 [](gmx::KeyValueTreeObjectBuilder* builder, const std::string& value) {
169 std::vector<std::string> values = gmx::splitString(value);
170 builder->addValue<int>("a", gmx::fromString<int>(values[0]));
171 builder->addValue<int>("b", gmx::fromString<int>(values[1]));
174 testTransform(input, transform);
177 TEST_F(TreeValueTransformTest, ObjectFromMultipleStrings)
179 gmx::KeyValueTreeBuilder builder;
180 builder.rootObject().addValue<std::string>("a", "1");
181 builder.rootObject().addValue<std::string>("b", "2 3");
182 gmx::KeyValueTreeObject input = builder.build();
184 gmx::KeyValueTreeTransformer transform;
185 transform.rules()->addRule().from<std::string>("/a").to<int>("/foo/a").transformWith(
186 &gmx::fromStdString<int>);
187 transform.rules()->addRule().from<std::string>("/b").toObject("/foo").transformWith(
188 [](gmx::KeyValueTreeObjectBuilder* builder, const std::string& value) {
189 std::vector<std::string> values = gmx::splitString(value);
190 builder->addValue<int>("b", gmx::fromString<int>(values[0]));
191 builder->addValue<int>("c", gmx::fromString<int>(values[1]));
194 testTransform(input, transform);
197 TEST_F(TreeValueTransformTest, ScopedTransformRules)
199 gmx::KeyValueTreeBuilder builder;
200 builder.rootObject().addValue<std::string>("a", "1");
201 builder.rootObject().addValue<std::string>("b", "2");
202 gmx::KeyValueTreeObject input = builder.build();
204 gmx::KeyValueTreeTransformer transform;
205 auto scope = transform.rules()->scopedTransform("/foo");
206 scope.rules()->addRule().from<std::string>("/a").to<int>("/i").transformWith(&gmx::fromStdString<int>);
207 scope.rules()->addRule().from<std::string>("/b").to<int>("/j").transformWith(&gmx::fromStdString<int>);
209 testTransform(input, transform);
212 /********************************************************************
216 TEST(TreeValueTransformErrorTest, ConversionError)
218 gmx::KeyValueTreeBuilder builder;
219 builder.rootObject().addValue<std::string>("a", "foo");
220 gmx::KeyValueTreeObject input = builder.build();
222 gmx::KeyValueTreeTransformer transform;
223 transform.rules()->addRule().from<std::string>("/a").to<int>("/i").transformWith(
224 &gmx::fromStdString<int>);
226 EXPECT_THROW_GMX(transform.transform(input, nullptr), gmx::InvalidInputError);