Project
Loading...
Searching...
No Matches
test_ConfigParamRegistry.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#include <catch_amalgamated.hpp>
16#include "Framework/Array2D.h"
17
18#include <fairmq/ProgOptions.h>
19#include <boost/program_options.hpp>
20
21namespace bpo = boost::program_options;
22using namespace o2::framework;
23
24struct Foo {
25 // Providing a class with a constructor which takes a ptree
26 // allows for getting the object
27 explicit Foo(boost::property_tree::ptree in)
28 : x{in.get<int>("x")},
29 y{in.get<float>("y")}
30 {
31 }
32 int x;
33 float y;
34};
35
36TEST_CASE("TestConfigParamRegistry")
37{
38 bpo::options_description testOptions("Test options");
39 testOptions.add_options() //
40 ("aFloat", bpo::value<float>()->default_value(10.f)) //
41 ("aDouble", bpo::value<double>()->default_value(20.)) //
42 ("anInt", bpo::value<int>()->default_value(1)) //
43 ("anInt8", bpo::value<int8_t>()->default_value(1)) //
44 ("anInt16", bpo::value<int16_t>()->default_value(1)) //
45 ("anUInt8", bpo::value<uint8_t>()->default_value(1)) //
46 ("anUInt16", bpo::value<uint16_t>()->default_value(1)) //
47 ("anUInt32", bpo::value<uint32_t>()->default_value(1)) //
48 ("anUInt64", bpo::value<uint64_t>()->default_value(1)) //
49 ("anInt64", bpo::value<int64_t>()->default_value(1ll)) //
50 ("aBoolean", bpo::value<bool>()->zero_tokens()->default_value(false)) //
51 ("aString,s", bpo::value<std::string>()->default_value("something")) //
52 ("aNested.x", bpo::value<int>()->default_value(2)) //
53 ("aNested.y", bpo::value<float>()->default_value(3.f)); //
54
55 fair::mq::ProgOptions* options = new fair::mq::ProgOptions();
56 options->AddToCmdLineOptions(testOptions);
57 options->ParseAll({"cmd", "--aFloat", "1.0",
58 "--aDouble", "2.0",
59 "--anInt", "10",
60 "--anInt8", "2",
61 "--anInt16", "10",
62 "--anUInt8", "2",
63 "--anUInt16", "10",
64 "--anUInt32", "10",
65 "--anUInt64", "10",
66 "--anInt64", "50000000000000",
67 "--aBoolean",
68 "-s", "somethingelse",
69 "--aNested.x", "1",
70 "--aNested.y", "2"},
71 true);
72 std::vector<ConfigParamSpec> specs{
73 ConfigParamSpec{"anInt", VariantType::Int, 1, {"an int option"}},
74 ConfigParamSpec{"anInt8", VariantType::Int8, static_cast<int8_t>(1), {"an int8 option"}},
75 ConfigParamSpec{"anInt16", VariantType::Int16, static_cast<int16_t>(1), {"an int16 option"}},
76 ConfigParamSpec{"anUInt8", VariantType::UInt8, static_cast<uint8_t>(1u), {"an uint8 option"}},
77 ConfigParamSpec{"anUInt16", VariantType::UInt16, static_cast<uint16_t>(1u), {"an uint16 option"}},
78 ConfigParamSpec{"anUInt32", VariantType::UInt32, 1u, {"an uint32 option"}},
79 ConfigParamSpec{"anUInt64", VariantType::UInt64, static_cast<uint64_t>(1ul), {"an uint64 option"}},
80 ConfigParamSpec{"anInt64", VariantType::Int64, 1ll, {"an int64_t option"}},
81 ConfigParamSpec{"aFloat", VariantType::Float, 2.0f, {"a float option"}},
82 ConfigParamSpec{"aDouble", VariantType::Double, 3., {"a double option"}},
83 ConfigParamSpec{"aString,s", VariantType::String, "foo", {"a string option"}},
84 ConfigParamSpec{"aBoolean", VariantType::Bool, true, {"a boolean option"}},
85 ConfigParamSpec{"aNested.x", VariantType::Int, 2, {"an int option, nested in an object"}},
86 ConfigParamSpec{"aNested.y", VariantType::Float, 3.f, {"a float option, nested in an object"}},
87 ConfigParamSpec{"aNested.z", VariantType::Float, 4.f, {"a float option, nested in an object"}},
88 ConfigParamSpec{"aDict", VariantType::Dict, emptyDict(), {"A dictionary"}}};
89
90 std::vector<std::unique_ptr<ParamRetriever>> retrievers;
91 std::unique_ptr<ParamRetriever> fairmqRetriver{new FairOptionsRetriever(options)};
92 retrievers.emplace_back(std::move(fairmqRetriver));
93
94 auto store = std::make_unique<ConfigParamStore>(specs, std::move(retrievers));
95 store->preload();
96 store->activate();
97 ConfigParamRegistry registry(std::move(store));
98
99 REQUIRE(registry.get<float>("aFloat") == 1.0);
100 REQUIRE(registry.get<double>("aDouble") == 2.0);
101 REQUIRE(registry.get<int>("anInt") == 10);
102 REQUIRE(registry.get<int8_t>("anInt8") == '2');
103 REQUIRE(registry.get<int16_t>("anInt16") == 10);
104 REQUIRE(registry.get<uint8_t>("anUInt8") == '2');
105 REQUIRE(registry.get<uint16_t>("anUInt16") == 10);
106 REQUIRE(registry.get<uint32_t>("anUInt32") == 10);
107 REQUIRE(registry.get<uint64_t>("anUInt64") == 10);
108 REQUIRE(registry.get<int64_t>("anInt64") == 50000000000000ll);
109 REQUIRE(registry.get<bool>("aBoolean") == true);
110 REQUIRE(registry.get<std::string>("aString") == "somethingelse");
111 REQUIRE(registry.get<int>("aNested.x") == 1);
112 REQUIRE(registry.get<float>("aNested.y") == 2.f);
113 REQUIRE(registry.get<float>("aNested.z") == 4.f);
114 // We can get nested objects also via their top-level ptree.
115 auto pt = registry.get<boost::property_tree::ptree>("aNested");
116 auto pt2 = registry.get<boost::property_tree::ptree>("aDict");
117 REQUIRE(pt.get<int>("x") == 1);
118 REQUIRE(pt.get<float>("y") == 2.f);
119 // And we can get it as a generic object as well.
120 Foo obj = registry.get<Foo>("aNested");
121 REQUIRE(obj.x == 1);
122 REQUIRE(obj.y == 2.f);
123}
124
125TEST_CASE("TestConfigParamRegistryOverride")
126{
127 float m[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
128 int l[2][2] = {{1, 2}, {3, 4}};
129 // a Variant cannot be created from a temporary vector, so the defaults are named
130 std::vector<int> defaultVector{1, 2, 3, 4, 5};
131 std::vector<std::string> defaultStringVector{"a", "b", "c"};
132 std::vector<double> defaultNestedVector{0.5, 1.5};
133 // the store keeps a reference to the specs, which have to outlive it
134 std::vector<ConfigParamSpec> specs{
135 ConfigParamSpec{"anInt", VariantType::Int, 1, {"an int option"}},
136 ConfigParamSpec{"aString", VariantType::String, "foo", {"a string option"}},
137 ConfigParamSpec{"aVector", VariantType::ArrayInt, defaultVector, {"an int array option"}},
138 ConfigParamSpec{"aStringVector", VariantType::ArrayString, defaultStringVector, {"a string array option"}},
139 ConfigParamSpec{"anArray2D", VariantType::Array2DFloat, Array2D<float>{&m[0][0], 3, 4}, {"a 2D array option"}},
140 ConfigParamSpec{"aLabeledArray", VariantType::LabeledArrayInt, LabeledArray<int>{&l[0][0], 2, 2, {"r1", "r2"}, {"c1", "c2"}}, {"a labeled array option"}},
141 ConfigParamSpec{"aNested.x", VariantType::Int, 2, {"an int option, nested in an object"}},
142 ConfigParamSpec{"aNested.vec", VariantType::ArrayDouble, defaultNestedVector, {"a double array option, nested in an object"}},
143 };
144 auto store = std::make_unique<ConfigParamStore>(specs, std::vector<std::unique_ptr<ParamRetriever>>{});
145 store->preload();
146 store->activate();
147 ConfigParamRegistry registry(std::move(store));
148 REQUIRE(registry.get<std::vector<int>>("aVector").size() == 5);
149
150 // leaf values keep working
151 registry.override("anInt", 10);
152 REQUIRE(registry.get<int>("anInt") == 10);
153
154 // a shorter vector replaces the whole subtree, no stale elements are left
155 registry.override("aVector", std::vector<int>{7, 8});
156 REQUIRE(registry.get<std::vector<int>>("aVector") == std::vector<int>{7, 8});
157 registry.override("aStringVector", std::vector<std::string>{"x"});
158 REQUIRE(registry.get<std::vector<std::string>>("aStringVector") == std::vector<std::string>{"x"});
159
160 // a smaller 2D array replaces the whole subtree
161 float m2[1][2] = {{0.25, 0.75}};
162 registry.override("anArray2D", Array2D<float>{&m2[0][0], 1, 2});
163 auto a2d = registry.get<Array2D<float>>("anArray2D");
164 REQUIRE(a2d.rows == 1);
165 REQUIRE(a2d.cols == 2);
166 REQUIRE(a2d(0, 1) == 0.75f);
167
168 // labels are replaced together with the values
169 int l2[1][3] = {{5, 6, 7}};
170 registry.override("aLabeledArray", LabeledArray<int>{&l2[0][0], 1, 3, {"row"}, {"x", "y", "z"}});
171 auto la = registry.get<LabeledArray<int>>("aLabeledArray");
172 REQUIRE(la.getLabelsRows() == std::vector<std::string>{"row"});
173 REQUIRE(la.getLabelsCols() == std::vector<std::string>{"x", "y", "z"});
174 REQUIRE(la.get("row", "z") == 7);
175 // an unlabeled value is stored as it is, without merging the previous labels
176 registry.override("aLabeledArray", LabeledArray<int>{&l[0][0], 2, 2});
177 la = registry.get<LabeledArray<int>>("aLabeledArray");
178 REQUIRE(la.getLabelsRows().empty());
179 REQUIRE(la.getLabelsCols().empty());
180 REQUIRE(la.get(1U, 0U) == 3);
181
182 // nested subtrees are replaced without touching their siblings
183 registry.override("aNested.vec", std::vector<double>{2.5});
184 REQUIRE(registry.get<std::vector<double>>("aNested.vec") == std::vector<double>{2.5});
185 REQUIRE(registry.get<int>("aNested.x") == 2);
186 REQUIRE(registry.get<std::string>("aString") == "foo");
187 REQUIRE(registry.get<std::vector<int>>("aVector") == std::vector<int>{7, 8});
188
189 // a key which does not exist yet is created
190 REQUIRE(registry.hasOption("aNewVector") == false);
191 registry.override("aNewVector", std::vector<float>{1.f, 2.f});
192 REQUIRE(registry.hasOption("aNewVector"));
193 REQUIRE(registry.get<std::vector<float>>("aNewVector") == std::vector<float>{1.f, 2.f});
194
195 // a leaf value can be replaced by a subtree
196 registry.override("aString", std::vector<std::string>{"now", "an", "array"});
197 REQUIRE(registry.get<std::vector<std::string>>("aString") == std::vector<std::string>{"now", "an", "array"});
198}
199
200TEST_CASE("TestConfigParamRegistryErase")
201{
202 int l[2][2] = {{1, 2}, {3, 4}};
203 // a Variant cannot be created from a temporary vector, so the defaults are named
204 std::vector<int> defaultVector{1, 2, 3, 4, 5};
205 std::vector<std::string> defaultStringVector{"a", "b", "c"};
206 std::vector<double> defaultNestedVector{0.5, 1.5};
207 // the store keeps a reference to the specs, which have to outlive it
208 std::vector<ConfigParamSpec> specs{
209 ConfigParamSpec{"anInt", VariantType::Int, 1, {"an int option"}},
210 ConfigParamSpec{"aString", VariantType::String, "foo", {"a string option"}},
211 ConfigParamSpec{"aVector", VariantType::ArrayInt, defaultVector, {"an int array option"}},
212 ConfigParamSpec{"aStringVector", VariantType::ArrayString, defaultStringVector, {"a string array option"}},
213 ConfigParamSpec{"aLabeledArray", VariantType::LabeledArrayInt, LabeledArray<int>{&l[0][0], 2, 2, {"r1", "r2"}, {"c1", "c2"}}, {"a labeled array option"}},
214 ConfigParamSpec{"aNested.x", VariantType::Int, 2, {"an int option, nested in an object"}},
215 ConfigParamSpec{"aNested.vec", VariantType::ArrayDouble, defaultNestedVector, {"a double array option, nested in an object"}},
216 };
217 auto store = std::make_unique<ConfigParamStore>(specs, std::vector<std::unique_ptr<ParamRetriever>>{});
218 store->preload();
219 store->activate();
220 ConfigParamRegistry registry(std::move(store));
221
222 // a leaf value is removed
223 REQUIRE(registry.hasOption("anInt"));
224 registry.erase("anInt");
225 REQUIRE(registry.hasOption("anInt") == false);
226 REQUIRE(registry.isSet("anInt") == false);
227 REQUIRE(registry.isDefault("anInt") == false);
228 REQUIRE_THROWS_AS(registry.get<int>("anInt"), std::invalid_argument);
229
230 // arrays are removed as whole subtrees
231 registry.erase("aVector");
232 REQUIRE(registry.hasOption("aVector") == false);
233 REQUIRE(registry.isSet("aVector") == false);
234 registry.erase("aLabeledArray");
235 REQUIRE(registry.hasOption("aLabeledArray") == false);
236 REQUIRE(registry.isSet("aLabeledArray") == false);
237
238 // a nested key is removed without touching its siblings
239 registry.erase("aNested.vec");
240 REQUIRE(registry.hasOption("aNested.vec") == false);
241 REQUIRE(registry.get<int>("aNested.x") == 2);
242
243 // other options are untouched
244 REQUIRE(registry.get<std::string>("aString") == "foo");
245 REQUIRE(registry.get<std::vector<std::string>>("aStringVector") == std::vector<std::string>{"a", "b", "c"});
246
247 // missing keys are ignored
248 REQUIRE_NOTHROW(registry.erase("anInt"));
249 REQUIRE_NOTHROW(registry.erase("noSuchKey"));
250 REQUIRE_NOTHROW(registry.erase("noSuchParent.x"));
251 REQUIRE_NOTHROW(registry.erase("aNested.noSuchKey"));
252 REQUIRE(registry.get<int>("aNested.x") == 2);
253
254 // an erased key can be stored again
255 registry.override("anInt", 3);
256 REQUIRE(registry.get<int>("anInt") == 3);
257 registry.override("aVector", std::vector<int>{7, 8});
258 REQUIRE(registry.get<std::vector<int>>("aVector") == std::vector<int>{7, 8});
259}
unsigned short int uint16_t
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events Show standalone event Run event generator maximum time bin of continuous for triggered for automatic continuous for automatic continuous triggered uint64_t
configStandalone Reconstruction rec_tpc float
bool isDefault(const char *key) const
bool hasOption(const char *key) const
void override(const char *key, ConfigValueType auto const &val) const
GLint GLenum GLint x
Definition glcorearb.h:403
const GLfloat * m
Definition glcorearb.h:4066
GLint y
Definition glcorearb.h:270
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
Variant emptyDict()
Definition Variant.h:435
Foo(boost::property_tree::ptree in)
TEST_CASE("TestConfigParamRegistry")