ChimeraTK-ApplicationCore 04.06.00
Loading...
Searching...
No Matches
testConfigReader.cc
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3#define BOOST_TEST_MODULE testConfigReader
4
5#include <boost/test/included/unit_test.hpp>
6using namespace boost::unit_test_framework;
7
8#include "Application.h"
9#include "ApplicationModule.h"
10#include "ArrayAccessor.h"
11#include "ConfigReader.h"
12#include "DeviceModule.h"
13#include "ScalarAccessor.h"
14#include "TestFacility.h"
15#include "VariableGroup.h"
16
17namespace ctk = ChimeraTK;
18
19constexpr std::string_view cdd{"(dummy?map=configReaderDevice.map)"};
20
22
23 /********************************************************************************************************************/
24 /* Module to receive the config values */
25
27 using ctk::ApplicationModule::ApplicationModule;
28
29 ctk::ConfigReader* theConfigReader; // just to compare if the correct instance is returned
30 bool appConfigHasThrown{false};
31
32 ctk::ScalarPushInput<int8_t> var8{this, "var8", "MV/m", "Desc"};
33 ctk::ScalarPushInput<uint8_t> var8u{this, "var8u", "MV/m", "Desc"};
34 ctk::ScalarPushInput<int16_t> var16{this, "var16", "MV/m", "Desc"};
35 ctk::ScalarPushInput<uint16_t> var16u{this, "var16u", "MV/m", "Desc"};
36 ctk::ScalarPushInput<int32_t> var32{this, "var32", "MV/m", "Desc"};
37 ctk::ScalarPushInput<uint32_t> var32u{this, "var32u", "MV/m", "Desc"};
38 ctk::ScalarPushInput<int64_t> var64{this, "var64", "MV/m", "Desc"};
39 ctk::ScalarPushInput<uint64_t> var64u{this, "var64u", "MV/m", "Desc"};
40 ctk::ScalarPushInput<float> varFloat{this, "varFloat", "MV/m", "Desc"};
41 ctk::ScalarPushInput<double> varDouble{this, "varDouble", "MV/m", "Desc"};
42 ctk::ScalarPushInput<std::string> varString{this, "varString", "MV/m", "Desc"};
43 ctk::ScalarPushInput<int32_t> varAnotherInt{this, "varAnotherInt", "MV/m", "Desc"};
44 ctk::ArrayPushInput<int32_t> intArray{this, "intArray", "MV/m", 10, "Desc"};
45 ctk::ArrayPushInput<std::string> stringArray{this, "stringArray", "", 8, "Desc"};
46
48 using ctk::VariableGroup::VariableGroup;
49 ctk::ScalarPushInput<int16_t> var16{this, "var16", "MV/m", "Desc"};
50 ctk::ScalarPushInput<uint16_t> var16u{this, "var16u", "MV/m", "Desc"};
51 ctk::ScalarPushInput<int32_t> var32{this, "var32", "MV/m", "Desc"};
52 ctk::ScalarPushInput<uint32_t> var32u{this, "var32u", "MV/m", "Desc"};
53 ctk::ScalarPushInput<std::string> varString{this, "varString", "MV/m", "Desc"};
54
56 using ctk::VariableGroup::VariableGroup;
57 ctk::ScalarPushInput<uint32_t> var32u{this, "var32u", "MV/m", "Desc"};
58 ctk::ArrayPushInput<int32_t> intArray{this, "intArray", "MV/m", 10, "Desc"};
59 ctk::ArrayPushInput<std::string> stringArray{this, "stringArray", "", 8, "Desc"};
60
62 using ctk::VariableGroup::VariableGroup;
63 ctk::ScalarPushInput<int32_t> var32{this, "var32", "MV/m", "Desc"};
64 ctk::ScalarPushInput<uint32_t> var32u{this, "var32u", "MV/m", "Desc"};
65 } subsubmodule{this, "subsubmodule", ""};
66 } submodule{this, "submodule", ""};
67
68 } module1{this, "module1", ""};
69
71 using ctk::VariableGroup::VariableGroup;
72
74 using ctk::VariableGroup::VariableGroup;
75 ctk::ScalarPushInput<double> var1{this, "var1", "m", "Desc"};
76 ctk::ScalarPushInput<double> var2{this, "var2", "kg", "Desc"};
77 } submodule1{this, "submodule1", ""}, submodule2{this, "submodule2", ""};
78 } module2{this, "module2", ""};
79
80 std::atomic<bool> done{false};
81
82 void mainLoop() override {
83 // values should be available right away
84 BOOST_CHECK_EQUAL(int8_t(var8), -123);
85 BOOST_CHECK_EQUAL(uint8_t(var8u), 34);
86 BOOST_CHECK_EQUAL(int16_t(var16), -567);
87 BOOST_CHECK_EQUAL(uint16_t(var16u), 678);
88 BOOST_CHECK_EQUAL(int32_t(var32), -345678);
89 BOOST_CHECK_EQUAL(uint32_t(var32u), 234567);
90 BOOST_CHECK_EQUAL(int64_t(var64), -2345678901234567890);
91 BOOST_CHECK_EQUAL(uint64_t(var64u), 12345678901234567890U);
92 BOOST_CHECK_CLOSE(float(varFloat), 3.1415, 0.000001);
93 BOOST_CHECK_CLOSE(double(varDouble), -2.8, 0.000001);
94 BOOST_CHECK_EQUAL(std::string(varString), "My dear mister singing club!");
95
96 BOOST_CHECK_EQUAL(intArray.getNElements(), 10);
97 for(unsigned int i = 0; i < 10; ++i) {
98 BOOST_CHECK_EQUAL(intArray[i], 10 - i);
99 }
100
101 BOOST_CHECK_EQUAL(stringArray.getNElements(), 8);
102 for(unsigned int i = 0; i < 8; ++i) {
103 BOOST_CHECK_EQUAL(stringArray[i], "Hallo" + std::to_string(i + 1));
104 }
105
106 BOOST_CHECK_EQUAL(int16_t(module1.var16), -567);
107 BOOST_CHECK_EQUAL(uint16_t(module1.var16u), 678);
108 BOOST_CHECK_EQUAL(int32_t(module1.var32), -345678);
109 BOOST_CHECK_EQUAL(uint32_t(module1.var32u), 234567);
110 BOOST_CHECK_EQUAL(uint32_t(module1.submodule.var32u), 234567);
111
112 BOOST_CHECK_EQUAL(module1.submodule.intArray.getNElements(), 10);
113 for(unsigned int i = 0; i < 10; ++i) {
114 BOOST_CHECK_EQUAL(module1.submodule.intArray[i], 10 - i);
115 }
116
117 BOOST_CHECK_EQUAL(module1.submodule.stringArray.getNElements(), 8);
118 for(unsigned int i = 0; i < 8; ++i) {
119 BOOST_CHECK_EQUAL(module1.submodule.stringArray[i], "Hallo" + std::to_string(i + 1));
120 }
121
122 // no further update shall be received
123 usleep(1000000); // 1 second
124 BOOST_CHECK(!var8.readNonBlocking());
125 BOOST_CHECK(!var8u.readNonBlocking());
126 BOOST_CHECK(!var16.readNonBlocking());
127 BOOST_CHECK(!var16u.readNonBlocking());
128 BOOST_CHECK(!var32.readNonBlocking());
129 BOOST_CHECK(!var32u.readNonBlocking());
130 BOOST_CHECK(!var64.readNonBlocking());
131 BOOST_CHECK(!var64u.readNonBlocking());
132 BOOST_CHECK(!varFloat.readNonBlocking());
133 BOOST_CHECK(!varDouble.readNonBlocking());
134 BOOST_CHECK(!varString.readNonBlocking());
135 BOOST_CHECK(!intArray.readNonBlocking());
136
137 BOOST_CHECK(!module1.var16.readNonBlocking());
138 BOOST_CHECK(!module1.var16u.readNonBlocking());
139 BOOST_CHECK(!module1.var32.readNonBlocking());
140 BOOST_CHECK(!module1.var32u.readNonBlocking());
141 BOOST_CHECK(!module1.submodule.var32u.readNonBlocking());
142 BOOST_CHECK(!module1.submodule.intArray.readNonBlocking());
143 BOOST_CHECK(!module1.submodule.stringArray.readNonBlocking());
144
145 // inform main thread that we are done
146 done = true;
147 }
148 };
149
150 /********************************************************************************************************************/
151 /* dummy application */
152
154 TestApplication(const std::string& name = "valid") : Application(name) {}
155 ~TestApplication() override { shutdown(); }
156
157 TestModule testModule{this, "/", "The test module"};
158 };
159
160 /********************************************************************************************************************/
161 /* dummy application with two config readers (to check the exception in ApplicationModule::appConfig()) */
162
164 TestApplicationTwoConfigs() : Application("TestApplicationTwoConfigs") {}
166
167#pragma GCC diagnostic push
168#pragma GCC diagnostic ignored "-Wdeprecated"
169#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
170 ctk::ConfigReader config{this, "config", "validConfig.xml", {"MyTAG"}};
171 ctk::ConfigReader config2{this, "config2", "validConfig.xml"};
172#pragma GCC diagnostic pop
173 };
174
175 /********************************************************************************************************************/
176 /* dummy application with default config readers, but no matching config file */
177
179 TestApplicationNoConfigs() : Application("TestApplicationNoConfigs") {}
181 };
182
183 /********************************************************************************************************************/
184 /* dummy application with deprecated config that is invalid */
185
187 TestApplicationInvalidConfig() : Application("TestApplicationInvalidConfig") {}
189
190#pragma GCC diagnostic push
191#pragma GCC diagnostic ignored "-Wdeprecated"
192#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
193 ctk::ConfigReader config{this, ".", "InValidConfig.xml", {"MyTAG"}};
194#pragma GCC diagnostic pop
195 };
196
197 /********************************************************************************************************************/
198 /* dummy application which directly connects config reader variables to a device */
199
206
207 /********************************************************************************************************************/
208 /* test trigger by app variable when connecting a polled device register to an
209 * app variable */
210
211 BOOST_AUTO_TEST_CASE(testConfigReader) {
212 std::cout << "==> testConfigReader" << std::endl;
213
214 TestApplication app;
215 auto& config = app.getConfigReader();
216
217 BOOST_TEST(config.getOwner() != nullptr);
218
219 // check if values are already accessible
220 BOOST_CHECK_EQUAL(config.get<int8_t>("var8"), -123);
221 BOOST_CHECK_EQUAL(config.get<uint8_t>("var8u"), 34);
222 BOOST_CHECK_EQUAL(config.get<int16_t>("var16"), -567);
223 BOOST_CHECK_EQUAL(config.get<uint16_t>("var16u"), 678);
224 BOOST_CHECK_EQUAL(config.get<int32_t>("var32"), -345678);
225 BOOST_CHECK_EQUAL(config.get<uint32_t>("var32u"), 234567);
226 BOOST_CHECK_EQUAL(config.get<int64_t>("var64"), -2345678901234567890);
227 BOOST_CHECK_EQUAL(config.get<uint64_t>("var64u"), 12345678901234567890U);
228 BOOST_CHECK_CLOSE(config.get<float>("varFloat"), 3.1415, 0.000001);
229 BOOST_CHECK_CLOSE(config.get<double>("varDouble"), -2.8, 0.000001);
230 BOOST_CHECK_EQUAL(config.get<std::string>("varString"), "My dear mister singing club!");
231
232 std::vector<int> arrayValue = config.get<std::vector<int>>("intArray");
233 BOOST_CHECK_EQUAL(arrayValue.size(), 10);
234 for(size_t i = 0; i < 10; ++i) {
235 BOOST_CHECK_EQUAL(arrayValue[i], 10 - i);
236 }
237
238 std::vector<std::string> arrayValueString = config.get<std::vector<std::string>>("stringArray");
239 BOOST_CHECK_EQUAL(arrayValueString.size(), 8);
240 for(size_t i = 0; i < 8; ++i) {
241 BOOST_CHECK_EQUAL(arrayValueString[i], "Hallo" + std::to_string(i + 1));
242 }
243
244 BOOST_CHECK_EQUAL(config.get<int16_t>("module1/var16"), -567);
245 BOOST_CHECK_EQUAL(config.get<uint16_t>("module1/var16u"), 678);
246 BOOST_CHECK_EQUAL(config.get<int32_t>("module1/var32"), -345678);
247 BOOST_CHECK_EQUAL(config.get<uint32_t>("module1/var32u"), 234567);
248 BOOST_CHECK_EQUAL(config.get<uint32_t>("module1/submodule/var32u"), 234567);
249 BOOST_CHECK_EQUAL(config.get<uint32_t>("module1/submodule/subsubmodule/var32u"), 234568);
250
251 arrayValue = config.get<std::vector<int>>("module1/submodule/intArray");
252 BOOST_CHECK_EQUAL(arrayValue.size(), 10);
253 for(size_t i = 0; i < 10; ++i) {
254 BOOST_CHECK_EQUAL(arrayValue[i], 10 - i);
255 }
256
257 arrayValueString = config.get<std::vector<std::string>>("module1/submodule/stringArray");
258 BOOST_CHECK_EQUAL(arrayValueString.size(), 8);
259 for(size_t i = 0; i < 8; ++i) {
260 BOOST_CHECK_EQUAL(arrayValueString[i], "Hallo" + std::to_string(i + 1));
261 }
262
263 // app.config.virtualise().dump();
264 // app.config.connectTo(app.testModule);
265
266 // Cheap way to get a PV manager
267 ctk::TestFacility tf{app, false};
268 tf.runApplication();
269
270 // wait until tests in TestModule::mainLoop() are complete
271 while(!app.testModule.done) {
272 usleep(10000);
273 }
274 }
275
276 /********************************************************************************************************************/
277
278 BOOST_AUTO_TEST_CASE(testExceptions) {
279 std::cout << "==> testExceptions" << std::endl;
280 {
281 BOOST_CHECK_THROW(std::make_unique<TestApplicationTwoConfigs>(), ctk::logic_error);
282 }
283 {
284 BOOST_CHECK_THROW(std::make_unique<TestApplicationInvalidConfig>(), ctk::logic_error);
285 }
286 {
287 TestApplication app;
288 auto& config = app.getConfigReader();
289 // Test get with types mismatch
290 BOOST_CHECK_THROW(config.get<uint16_t>("var32u"), ctk::logic_error);
291
292 // Test getting nonexisting varibale
293 BOOST_CHECK_THROW(config.get<int>("nonexistentVariable"), ctk::logic_error);
294
295 // Same for arrays
296 // Test get with types mismatch
297 BOOST_CHECK_THROW(config.get<std::vector<float>>("module1/submodule/intArray"), ctk::logic_error);
298
299 // Test getting nonexisting varibale
300 BOOST_CHECK_THROW(config.get<std::vector<int>>("nonexistentVariable"), ctk::logic_error);
301 }
302 }
303
304 /********************************************************************************************************************/
305
306 BOOST_AUTO_TEST_CASE(testDirectWriteToDevice) {
307 std::cout << "==> testDirectWriteToDevice" << std::endl;
309 ctk::TestFacility test(app);
310 test.runApplication();
311
312 BOOST_TEST(app.getConfigReader().getOwner() != nullptr);
313
314 ctk::Device device(cdd.data());
315
316 auto var32u = device.getScalarRegisterAccessor<uint32_t>("var32u");
317 auto var16 = device.getScalarRegisterAccessor<int16_t>("var16");
318 auto module1var16 = device.getScalarRegisterAccessor<int16_t>("module1/var16");
319 auto intArray = device.getOneDRegisterAccessor<int32_t>("intArray");
320 var32u.read();
321 var16.read();
322 module1var16.read();
323 intArray.read();
324 BOOST_CHECK_EQUAL(var32u, 234567);
325 BOOST_CHECK_EQUAL(var16, -567);
326 BOOST_CHECK_EQUAL(module1var16, -567);
327 BOOST_CHECK_EQUAL(intArray.getNElements(), 10);
328 for(unsigned int i = 0; i < 10; ++i) {
329 BOOST_CHECK_EQUAL(intArray[i], 10 - i);
330 }
331 }
332
333 /********************************************************************************************************************/
334
335 BOOST_AUTO_TEST_CASE(testGetModules) {
336 TestApplication app;
337 auto& config = app.getConfigReader();
338
339 auto modules = config.getModules();
340 BOOST_TEST(std::list<std::string>({"module1", "module2"}) == modules);
341
342 auto modules2 = config.getModules("module1");
343 BOOST_TEST(std::list<std::string>({"submodule"}) == modules2);
344
345 BOOST_TEST(config.getModules("this/should/not/exist") == std::list<std::string>());
346 }
347
348 /********************************************************************************************************************/
349
350 BOOST_AUTO_TEST_CASE(testOverrideTestFacility) {
351 std::cout << "==> testOverrideTestFacility" << std::endl;
352 {
353 // Case 1: Config file exists
354 ctk::TestFacility::setConfigScalar<int8_t>("var8", 12); // override existing scalar value
355 ctk::TestFacility::setConfigScalar<std::string>("varString", "another overridden value");
356
357 ctk::TestFacility::setConfigScalar<int8_t>("newVar8", -42); // add new scalar
358
359 std::vector<int> ref({1, 2, 4, 8, 16, 32, 64, 128, 256, 512});
360 ctk::TestFacility::setConfigArray<int>("module1/submodule/intArray", ref);
361
362 TestApplication app;
363 auto& config = app.getConfigReader();
364
365 BOOST_TEST(config.get<int8_t>("var8") == 12);
366 BOOST_TEST(config.get<uint8_t>("var8u") == 34); // not overridden
367 BOOST_TEST(config.get<std::string>("varString") == "another overridden value");
368 BOOST_TEST(config.get<int8_t>("newVar8") == -42);
369
370 auto arrayValue = config.get<std::vector<int>>("module1/submodule/intArray");
371 BOOST_TEST(arrayValue == ref);
372 }
373 {
374 // Case 2: Config file does not exist exists
375 ctk::TestFacility::setConfigScalar<int8_t>("var8", 12); // override existing scalar value
376 ctk::TestFacility::setConfigScalar<std::string>("varString", "another overridden value");
377
378 ctk::TestFacility::setConfigScalar<int8_t>("newVar8", -42); // add new scalar
379
380 std::vector<int> ref({1, 2, 4, 8, 16, 32, 64, 128, 256, 512});
381 ctk::TestFacility::setConfigArray<int>("module1/submodule/intArray", ref);
382
383 TestApplication app("AppWithoutConfigFile");
384 auto& config = app.getConfigReader();
385
386 BOOST_TEST(config.get<int8_t>("var8") == 12);
387 BOOST_CHECK_THROW(config.get<uint8_t>("var8u"), ChimeraTK::logic_error); // not overridden
388 BOOST_TEST(config.get<std::string>("varString") == "another overridden value");
389 BOOST_TEST(config.get<int8_t>("newVar8") == -42);
390
391 auto arrayValue = config.get<std::vector<int>>("module1/submodule/intArray");
392 BOOST_TEST(arrayValue == ref);
393 }
394 }
395
396 /********************************************************************************************************************/
397
398} // namespace Tests::testConfigReader
ConfigReader & getConfigReader()
void shutdown() override
This will remove the global pointer to the instance and allows creating another instance afterwards.
Convenience class for input array accessors with UpdateMode::push.
Generic module to read an XML config file and provide the defined values as constant variables.
std::list< std::string > getModules(const std::string &path={}) const
Returns a list of names of modules which are direct children of path.
friend class Application
Definition ModuleGroup.h:47
EntityOwner * getOwner() const
Definition Module.h:102
Convenience class for input scalar accessors with UpdateMode::push.
Helper class to facilitate tests of applications based on ApplicationCore.
void runApplication() const
Start the application in testable mode.
InvalidityTracer application module.
BOOST_AUTO_TEST_CASE(testConfigReader)
TestApplication(const std::string &name="valid")
Tests::testConfigReader::TestModule::Module1::SubModule::SubSubModule subsubmodule
ctk::ScalarPushInput< std::string > varString
Tests::testConfigReader::TestModule::Module1::SubModule submodule
ctk::ScalarPushInput< uint32_t > var32u
ctk::ScalarPushInput< uint16_t > var16u
Tests::testConfigReader::TestModule::Module2::AnotherSubModule submodule2
Tests::testConfigReader::TestModule::Module2::AnotherSubModule submodule1
ctk::ScalarPushInput< uint16_t > var16u
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
Tests::testConfigReader::TestModule::Module1 module1
ctk::ScalarPushInput< float > varFloat
ctk::ScalarPushInput< double > varDouble
ctk::ScalarPushInput< uint8_t > var8u
ctk::ScalarPushInput< uint32_t > var32u
ctk::ScalarPushInput< int32_t > varAnotherInt
ctk::ArrayPushInput< int32_t > intArray
ctk::ScalarPushInput< int16_t > var16
ctk::ScalarPushInput< uint64_t > var64u
ctk::ScalarPushInput< int64_t > var64
Tests::testConfigReader::TestModule::Module2 module2
ctk::ScalarPushInput< int32_t > var32
ctk::ArrayPushInput< std::string > stringArray
ctk::ScalarPushInput< std::string > varString
ctk::ScalarPushInput< int8_t > var8
constexpr std::string_view cdd