ChimeraTK-ApplicationCore 04.06.00
Loading...
Searching...
No Matches
testFanIn.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
4#define BOOST_TEST_MODULE testFanIn
5
6#include "Application.h"
7#include "ApplicationModule.h"
8#include "FanIn.h"
9#include "ScalarAccessor.h"
10#include "TestFacility.h"
11
12#include <boost/test/included/unit_test.hpp>
13
14using namespace boost::unit_test_framework;
15namespace ctk = ChimeraTK;
16
18
19 /********************************************************************************************************************/
20
22 public:
23 using ctk::ApplicationModule::ApplicationModule;
24 ctk::ScalarOutput<int32_t> out{this, "/path/to/fanIn", "", ""};
25 void mainLoop() override {}
26 void prepare() override { out.setAndWrite(1); }
27 };
28
29 /********************************************************************************************************************/
30
32 public:
33 using ctk::ApplicationModule::ApplicationModule;
34
35 void mainLoop() override {
36 auto rag = readAnyGroup();
37 while(true) {
38 change = rag.readAny();
39 }
40 }
41
42 ctk::TransferElementID change;
43 };
44
45 /********************************************************************************************************************/
46 /********************************************************************************************************************/
47
48 /********************************************************************************************************************/
49 // Test result of the aggregated value output
50
52 public:
55
56 TheSender a{this, "a", ""};
57 TheSender b{this, "b", ""};
58
60 public:
61 using TheReceiverBase::TheReceiverBase;
62
63 // just for checking that we can use stateful lambdas as well...
64 int offset{10};
65
66 ctk::ScalarFanIn<int32_t> in{this, "fanIn", "", "", [&](auto id, auto map) { return map[id] + offset; }};
67
68 void mainLoop() override {
69 offset = 17;
71 }
72 };
73 TheReceiver r{this, "/path/to", ""};
74 };
75
76 BOOST_AUTO_TEST_CASE(TestAggregatedValue) {
77 std::cout << "***************************************************************" << std::endl;
78 std::cout << "==> TestAggregatedValue" << std::endl;
79
81
82 ctk::TestFacility test{app};
83
84 auto out = test.getScalar<int32_t>("/path/to/fanIn");
85
86 test.runApplication();
87
88 // initial value (both a and b are sending 1 in prepare())
89 BOOST_TEST(out == 1 + 10); // offset is 10 at the beginning and changed to 17 only after entering the mainloop
90 BOOST_TEST(!out.readNonBlocking());
91 BOOST_TEST(app.r.in == 1 + 10);
92
93 app.b.out.setAndWrite(42);
94 test.stepApplication();
95 BOOST_TEST(app.r.in == 42 + 17);
96 BOOST_TEST(out.readNonBlocking());
97 BOOST_TEST(out == 42 + 17);
98 BOOST_TEST(!out.readNonBlocking());
99
100 app.a.out.setAndWrite(43);
101 test.stepApplication();
102 BOOST_TEST(app.r.in == 43 + 17);
103 BOOST_TEST(out.readNonBlocking());
104 BOOST_TEST(out == 43 + 17);
105 BOOST_TEST(!out.readNonBlocking());
106 }
107
108 /********************************************************************************************************************/
109
111 public:
114
115 TheSender a{this, "a", ""};
116 TheSender b{this, "b", ""};
117
119 public:
120 using TheReceiverBase::TheReceiverBase;
121
123 this, "fanIn", {"myAdditionalInput", "/absolute/path/input"}, "", "", ctk::fanInKeepLastValue};
124 };
125 TheReceiver r{this, "/path/to", ""};
126 };
127
128 BOOST_AUTO_TEST_CASE(TestAdditionalInputs) {
129 std::cout << "***************************************************************" << std::endl;
130 std::cout << "==> TestAdditionalInputs" << std::endl;
131
133
134 ctk::TestFacility test{app};
135
136 auto out = test.getScalar<int32_t>("/path/to/fanIn");
137 auto addIn = test.getScalar<int32_t>("/path/to/myAdditionalInput");
138 auto absIn = test.getScalar<int32_t>("/absolute/path/input");
139
140 // keep all inital values identical, to avoid undefined result
141 test.setScalarDefault("/path/to/myAdditionalInput", 1);
142 test.setScalarDefault("/absolute/path/input", 1);
143
144 test.runApplication();
145
146 // initial value (both a and b are sending 1 in prepare())
147 BOOST_TEST(out == 1);
148 BOOST_TEST(!out.readNonBlocking());
149 BOOST_TEST(app.r.in == 1);
150
151 app.a.out.setAndWrite(42);
152 test.stepApplication();
153 BOOST_TEST(app.r.in == 42);
154
155 addIn.setAndWrite(43);
156 test.stepApplication();
157 BOOST_TEST(app.r.in == 43);
158
159 absIn.setAndWrite(44);
160 test.stepApplication();
161 BOOST_TEST(app.r.in == 44);
162 }
163
164 /********************************************************************************************************************/
165
167 public:
170
171 class MG : public ctk::ModuleGroup {
172 public:
173 using ctk::ModuleGroup::ModuleGroup;
174
176 public:
177 using ctk::ApplicationModule::ApplicationModule;
178 ctk::ScalarOutput<int32_t> out{this, "../path/to/fanIn", "", ""};
179 void mainLoop() override {}
180 void prepare() override { out.setAndWrite(1); }
181 };
182 MySender a{this, "a", ""};
183 MySender b{this, "b", ""};
184
186 public:
187 using TheReceiverBase::TheReceiverBase;
188
189 ctk::ScalarFanIn<int32_t> in{this, "fanIn", "", "", ctk::fanInKeepLastValue};
190 };
191 TheReceiver r{this, "path/to", ""};
192 } mg{this, "/some/directory", ""};
193 };
194
195 BOOST_AUTO_TEST_CASE(TestRelativeSenderNames) {
196 std::cout << "***************************************************************" << std::endl;
197 std::cout << "==> TestRelativeSenderNames" << std::endl;
198
200
201 ctk::TestFacility test{app};
202
203 auto out = test.getScalar<int32_t>("/some/directory/path/to/fanIn");
204
205 test.runApplication();
206
207 // initial value (both a and b are sending 1 in prepare())
208 BOOST_TEST(out == 1);
209 BOOST_TEST(!out.readNonBlocking());
210 BOOST_TEST(app.mg.r.in == 1);
211
212 app.mg.a.out.setAndWrite(42);
213 test.stepApplication();
214 BOOST_TEST(app.mg.r.in == 42);
215 }
216
217 /********************************************************************************************************************/
218
220 public:
223
225 public:
226 using TheReceiverBase::TheReceiverBase;
227
228 static constexpr auto aggregateSum = [](auto, const auto& map) {
229 int result = 0;
230 for(auto& a : map) {
231 result += a.second;
232 }
233 return result;
234 };
235
236 ctk::ScalarFanInWB<int32_t> in{this, "fanIn", {"a", "b"}, "", "", aggregateSum};
239
240 void prepare() override {
241 validator.setErrorFunction([&](const std::string& msg) {
242 std::cout << "---> " << msg << std::endl;
243 err.setAndWrite(msg);
244 });
245
246 validator.add("testOnAggregated", [&] { return in < 10; }, in.inputs());
247 for(auto& acc : in.inputs()) {
248 validator.add("testOnIndividual", [&] { return acc > -10; }, acc);
249 validator.setFallback(acc, 1);
250 }
251 }
252
253 void mainLoop() override {
254 auto rag = readAnyGroup();
255 validator.validate({}); // initial values won't be validated internally by the FanIn
256
257 while(true) {
258 change = rag.readAny();
259
260 // this is not needed here in this case, but shouldn't really hurt (will call validators twice though):
262 }
263 }
264 };
265 TheReceiver r{this, "/path/to", ""};
266 };
267
268 BOOST_AUTO_TEST_CASE(TestUserInputValidation) {
269 std::cout << "***************************************************************" << std::endl;
270 std::cout << "==> TestUserInputValidation" << std::endl;
271
273
274 ctk::TestFacility test{app};
275
276 auto out = test.getScalar<int32_t>("/path/to/fanIn");
277 auto a = test.getScalar<int32_t>("/path/to/a");
278 auto b = test.getScalar<int32_t>("/path/to/b");
279 auto err = test.getScalar<std::string>("/path/to/err");
280
281 test.setScalarDefault<int32_t>("/path/to/a", 1);
282 test.setScalarDefault<int32_t>("/path/to/b", -20);
283
284 test.runApplication();
285
286 // initial value (after rejection of out-of-range initial values)
287 BOOST_TEST(app.r.in == 2);
288 BOOST_TEST(!err.readNonBlocking());
289 BOOST_TEST(out == 2);
290 BOOST_TEST(!out.readNonBlocking());
291
292 a.setAndWrite(20);
293 test.stepApplication();
294 BOOST_TEST(app.r.in == 2);
295 BOOST_TEST(err.readNonBlocking());
296 BOOST_TEST(std::string(err) == "testOnAggregated");
297 BOOST_TEST(out.readNonBlocking());
298 BOOST_TEST(out == 2);
299 BOOST_TEST(!out.readNonBlocking());
300
301 b.setAndWrite(20);
302 test.stepApplication();
303 BOOST_TEST(app.r.in == 2);
304 BOOST_TEST(err.readNonBlocking());
305 BOOST_TEST(std::string(err) == "testOnAggregated");
306 BOOST_TEST(out.readNonBlocking());
307 BOOST_TEST(out == 2);
308 BOOST_TEST(!out.readNonBlocking());
309
310 a.setAndWrite(-20);
311 test.stepApplication();
312 BOOST_TEST(app.r.in == 2);
313 BOOST_TEST(err.readNonBlocking());
314 BOOST_TEST(std::string(err) == "testOnIndividual");
315 BOOST_TEST(out.readNonBlocking());
316 BOOST_TEST(out == 2);
317 BOOST_TEST(!out.readNonBlocking());
318
319 b.setAndWrite(-20);
320 test.stepApplication();
321 BOOST_TEST(app.r.in == 2);
322 BOOST_TEST(err.readNonBlocking());
323 BOOST_TEST(std::string(err) == "testOnIndividual");
324 BOOST_TEST(out.readNonBlocking());
325 BOOST_TEST(out == 2);
326 BOOST_TEST(!out.readNonBlocking());
327
328 a.setAndWrite(3);
329 test.stepApplication();
330 BOOST_TEST(app.r.in == 4);
331 BOOST_TEST(!err.readNonBlocking());
332 BOOST_TEST(out.readNonBlocking());
333 BOOST_TEST(out == 4);
334 BOOST_TEST(!out.readNonBlocking());
335 }
336
337 /********************************************************************************************************************/
338
339} // namespace Tests::testFanIn
void shutdown() override
This will remove the global pointer to the instance and allows creating another instance afterwards.
Special accessor allows multiple incoming connections to the same logical process variable.
Definition FanIn.h:56
friend class Application
Definition ModuleGroup.h:47
ChimeraTK::ReadAnyGroup readAnyGroup()
Create a ChimeraTK::ReadAnyGroup for all readable variables in this Module.
Definition Module.cc:54
void setAndWrite(UserType newValue, VersionNumber versionNumber)=delete
Helper class to facilitate tests of applications based on ApplicationCore.
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
Definition testFanIn.cc:68
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
Definition testFanIn.cc:179
void prepare() override
Prepare the execution of the module.
Definition testFanIn.cc:180
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
Definition testFanIn.cc:253
void prepare() override
Prepare the execution of the module.
Definition testFanIn.cc:240
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
Definition testFanIn.cc:35
ctk::TransferElementID change
Definition testFanIn.cc:42
void prepare() override
Prepare the execution of the module.
Definition testFanIn.cc:26
ctk::ScalarOutput< int32_t > out
Definition testFanIn.cc:24
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
Definition testFanIn.cc:25
InvalidityTracer application module.
BOOST_AUTO_TEST_CASE(TestAggregatedValue)
Definition testFanIn.cc:76
Convenience class for output scalar accessors (always UpdateMode::push)
Class to realise the validation of user input values.
bool validate(const ChimeraTK::TransferElementID &change)
Execute all validations for the given change.
void setErrorFunction(const std::function< void(const std::string &)> &errorFunction)
Define how to report error messages to the user.
void add(const std::string &errorMessage, const std::function< bool(void)> &isValidFunction, ACCESSORTYPES &... accessors)
Add new condition to validate the given accessors against.
void setFallback(Accessor< UserType > &accessor, UserType value)
Provide fallback value for the given accessor.