ChimeraTK-ApplicationCore 04.06.00
Loading...
Searching...
No Matches
testApplication.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#include <ChimeraTK/Exception.h>
4
5#include <boost/test/tools/old/interface.hpp>
6
7#include <chrono>
8#include <future>
9
10#define BOOST_TEST_MODULE testApplication
11
12#include "Application.h"
13#include "DeviceModule.h"
14#include "Multiplier.h"
15#include "Pipe.h"
16#include "Utilities.h"
17
18#include <libxml++/libxml++.h>
19
20#include <boost/filesystem.hpp>
21#include <boost/mpl/list.hpp>
22#include <boost/test/included/unit_test.hpp>
23#include <boost/thread.hpp>
24
25using namespace boost::unit_test_framework;
26namespace ctk = ChimeraTK;
27
29
30 /********************************************************************************************************************/
31 /* Application without name */
32
33 struct TestApp : public ctk::Application {
34 explicit TestApp(const std::string& name) : ctk::Application(name) {}
35 ~TestApp() override { shutdown(); }
36
37 ctk::ConstMultiplier<double> multiplierD{this, "multiplierD", "Some module", 42};
38 ctk::ScalarPipe<std::string> pipe{this, "pipeIn", "pipeOut", "unit", "Some pipe module"};
39 ctk::ConstMultiplier<uint16_t, uint16_t, 120> multiplierU16{this, "multiplierU16", "Some other module", 42};
40
41 ctk::DeviceModule device{this, "(logicalNameMap?map=empty.xlmap)", "/trigger"};
42 };
43
44 /********************************************************************************************************************/
45 /* test trigger by app variable when connecting a polled device register to an
46 * app variable */
47
48 BOOST_AUTO_TEST_CASE(testApplicationExceptions) {
49 std::cout << "***************************************************************"
50 "******************************************************"
51 << std::endl;
52 std::cout << "==> testApplicationExceptions" << std::endl;
53
54 // zero length name forbidden
55 BOOST_CHECK_THROW(TestApp app(""), ChimeraTK::logic_error);
56
57 // names with spaces and special characters are forbidden
58 BOOST_CHECK_THROW(TestApp app("With space"), ChimeraTK::logic_error); // <<< logic error terminates here
59 BOOST_CHECK_THROW(TestApp app("WithExclamationMark!"), ChimeraTK::logic_error);
60
61 // all allowed characters in the name
62 {
63 BOOST_CHECK_NO_THROW(TestApp app("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz_1234567890"));
64 }
65
66 // repeated characters are allowed
67 {
68 BOOST_CHECK_NO_THROW(TestApp app("AAAAAAA"));
69 }
70
71 // Two apps at the same time are not allowed
72 TestApp app1("FirstInstance");
73 BOOST_CHECK_THROW(TestApp app2("SecondInstance"), ChimeraTK::logic_error);
74 }
75
76 /********************************************************************************************************************/
77 // Helper function for testXmlGeneration:
78 // Obtain a value from an XML node
79 std::string getValueFromNode(const xmlpp::Node* node, const std::string& subnodeName) {
80 xmlpp::Node* theChild = nullptr;
81 for(const auto& child : node->get_children()) {
82 if(child->get_name() == subnodeName) {
83 theChild = child;
84 }
85 }
86 BOOST_REQUIRE(theChild != nullptr); // requested child tag is there
87 auto subChildList = theChild->get_children();
88 if(subChildList.empty()) { // special case: no text in the tag -> return empty string
89 return "";
90 }
91 BOOST_REQUIRE_EQUAL(subChildList.size(),
92 1); // child tag contains only text (no further sub-tags)
93 const xmlpp::TextNode* textNode = dynamic_cast<xmlpp::TextNode*>(subChildList.front());
94 BOOST_REQUIRE(textNode != nullptr);
95 return textNode->get_content();
96 }
97
98 /********************************************************************************************************************/
99 /* test creation of XML file describing the variable tree */
100
101 BOOST_AUTO_TEST_CASE(testXmlGeneration) {
102 std::cout << "***************************************************************"
103 "******************************************************"
104 << std::endl;
105 std::cout << "==> testXmlGeneration" << std::endl;
106
107 // delete XML file if already existing
108 boost::filesystem::remove("TestAppInstance.xml");
109
110 // create app which exports some properties and generate its XML file
111 TestApp app("TestAppInstance");
112 app.generateXML();
113
114 // validate the XML file
115 xmlpp::XsdValidator validator("application.xsd");
116 validator.validate("TestAppInstance.xml");
117
118 // parse XML file
119 xmlpp::DomParser parser;
120 try {
121 parser.parse_file("TestAppInstance.xml");
122 }
123 catch(xmlpp::exception& e) {
124 throw std::runtime_error(std::string("ConfigReader: Error opening the config file "
125 "'TestAppInstance.xml': ") +
126 e.what());
127 }
128
129 // get root element
130 auto* const root = parser.get_document()->get_root_node();
131 BOOST_CHECK_EQUAL(root->get_name(), "application");
132
133 // parsing loop
134 bool found_pipeIn{false};
135 bool found_multiplierDIn{false};
136 bool found_multiplierDOut{false};
137 bool found_multiplierU16In{false};
138 bool found_multiplierU16Out{false};
139 bool found_pipeOut{false};
140 bool foundTrigger{false};
141
142 bool foundDeviceStatus{false};
143 bool foundDeviceMessage{false};
144 bool foundBecameFunctional{false};
145
146 for(const auto& child : root->get_children()) {
147 // cast into element, ignore if not an element (e.g. comment)
148 const auto* element = dynamic_cast<const xmlpp::Element*>(child);
149 if(!element) {
150 continue;
151 }
152
153 if(element->get_name() == "variable") {
154 // obtain attributes from the element
155 auto* xname = element->get_attribute("name");
156 BOOST_REQUIRE(xname != nullptr);
157 std::string name(xname->get_value());
158
159 // obtain values from sub-elements
160 std::string value_type = getValueFromNode(element, "value_type");
161 std::string direction = getValueFromNode(element, "direction");
162 std::string unit = getValueFromNode(element, "unit");
163 std::string description = getValueFromNode(element, "description");
164 std::string numberOfElements = getValueFromNode(element, "numberOfElements");
165
166 // check if variables are described correctly
167 if(name == "pipeOut") {
168 found_pipeIn = true;
169 BOOST_TEST(value_type == "string");
170 BOOST_TEST(direction == "application_to_control_system");
171 BOOST_TEST(unit == "unit");
172 BOOST_TEST(description == "Some pipe module");
173 BOOST_TEST(numberOfElements == "1");
174 }
175 else if(name == "pipeIn") {
176 found_pipeOut = true;
177 BOOST_TEST(value_type == "string");
178 BOOST_CHECK_EQUAL(direction, "control_system_to_application");
179 BOOST_TEST(unit == "unit");
180 BOOST_TEST(description == "Some pipe module");
181 BOOST_TEST(numberOfElements == "1");
182 }
183 else if(name == "trigger") {
184 foundTrigger = true;
185 BOOST_TEST(value_type == "Void");
186 BOOST_CHECK_EQUAL(direction, "control_system_to_application");
187 BOOST_TEST(unit == "n./a.");
188 BOOST_TEST(description == "");
189 BOOST_TEST(numberOfElements == "0");
190 }
191 else {
192 BOOST_ERROR("Wrong variable name found.");
193 }
194 }
195 else if(element->get_name() == "directory") {
196 auto* xname = element->get_attribute("name");
197 BOOST_REQUIRE(xname != nullptr);
198 std::string name(xname->get_value());
199
200 for(const auto& subchild : child->get_children()) {
201 const auto* element2 = dynamic_cast<const xmlpp::Element*>(subchild);
202 if(!element2) {
203 continue;
204 }
205
206 if(element2->get_name() == "directory") {
207 auto* xname2 = element2->get_attribute("name");
208 BOOST_REQUIRE(xname2 != nullptr);
209 std::string name2(xname2->get_value());
210 BOOST_REQUIRE(name2 == ctk::Utilities::escapeName(app.device.getDeviceAliasOrURI(), false));
211
212 for(const auto& devicechild : element2->get_children()) {
213 const auto* deviceChildElement = dynamic_cast<const xmlpp::Element*>(devicechild);
214 if(!deviceChildElement) {
215 continue;
216 }
217 if(deviceChildElement->get_name() == "variable") {
218 // obtain attributes from the element
219 auto* xname3 = deviceChildElement->get_attribute("name");
220 BOOST_REQUIRE(xname3 != nullptr);
221 std::string name3(xname3->get_value());
222
223 // obtain values from sub-elements
224 std::string value_type = getValueFromNode(deviceChildElement, "value_type");
225 std::string direction = getValueFromNode(deviceChildElement, "direction");
226 std::string unit = getValueFromNode(deviceChildElement, "unit");
227 std::string description = getValueFromNode(deviceChildElement, "description");
228 std::string numberOfElements = getValueFromNode(deviceChildElement, "numberOfElements");
229 if(name3 == "status") {
230 foundDeviceStatus = true;
231 BOOST_CHECK_EQUAL(value_type, "int32");
232 BOOST_CHECK_EQUAL(description, "Error status of the device - Error status of the device");
233 BOOST_CHECK_EQUAL(numberOfElements, "1");
234 BOOST_CHECK_EQUAL(direction, "application_to_control_system");
235 BOOST_CHECK_EQUAL(unit, "");
236 }
237 else if(name3 == "status_message") {
238 foundDeviceMessage = true;
239 BOOST_CHECK_EQUAL(value_type, "string");
240 BOOST_CHECK_EQUAL(description, "Error status of the device - status message");
241 BOOST_CHECK_EQUAL(numberOfElements, "1");
242 BOOST_CHECK_EQUAL(direction, "application_to_control_system");
243 BOOST_CHECK_EQUAL(unit, "");
244 }
245 else if(name3 == "deviceBecameFunctional") {
246 foundBecameFunctional = true;
247 BOOST_CHECK_EQUAL(value_type, "Void");
248 BOOST_CHECK_EQUAL(description, "");
249 BOOST_CHECK_EQUAL(numberOfElements, "1");
250 BOOST_CHECK_EQUAL(direction, "application_to_control_system");
251 BOOST_CHECK_EQUAL(unit, "");
252 }
253 else {
254 BOOST_ERROR("Unexpected variable " + name2);
255 }
256 }
257 else {
258 BOOST_ERROR("Wrong tag " + element2->get_name() + " found");
259 }
260 }
261 }
262 else if(element2->get_name() == "variable") {
263 // obtain attributes from the element
264 auto* xname2 = element2->get_attribute("name");
265 BOOST_REQUIRE(xname2 != nullptr);
266 std::string name2(xname2->get_value());
267
268 // obtain values from sub-elements
269 std::string value_type = getValueFromNode(element2, "value_type");
270 std::string direction = getValueFromNode(element2, "direction");
271 std::string unit = getValueFromNode(element2, "unit");
272 std::string description = getValueFromNode(element2, "description");
273 std::string numberOfElements = getValueFromNode(element2, "numberOfElements");
274
275 if(name2 == "input") {
276 if(name == "multiplierD") {
277 found_multiplierDIn = true;
278 BOOST_CHECK_EQUAL(value_type, "double");
279 BOOST_CHECK_EQUAL(description, "Some module");
280 BOOST_CHECK_EQUAL(numberOfElements, "1");
281 }
282 else if(name == "multiplierU16") {
283 found_multiplierU16In = true;
284 BOOST_CHECK_EQUAL(value_type, "uint16");
285 BOOST_CHECK_EQUAL(description, "Some other module");
286 BOOST_CHECK_EQUAL(numberOfElements, "120");
287 }
288 else {
289 BOOST_ERROR("Wrong Directory name found");
290 }
291 BOOST_CHECK_EQUAL(direction, "control_system_to_application");
292 BOOST_CHECK_EQUAL(unit, "");
293 }
294 else if(name2 == "output") {
295 if(name == "multiplierD") {
296 found_multiplierDOut = true;
297 BOOST_CHECK_EQUAL(value_type, "double");
298 BOOST_CHECK_EQUAL(description, "Some module");
299 BOOST_CHECK_EQUAL(numberOfElements, "1");
300 }
301 else if(name == "multiplierU16") {
302 found_multiplierU16Out = true;
303 BOOST_CHECK_EQUAL(value_type, "uint16");
304 BOOST_CHECK_EQUAL(description, "Some other module");
305 BOOST_CHECK_EQUAL(numberOfElements, "120");
306 }
307 else {
308 BOOST_ERROR("Wrong Directory name found");
309 }
310 BOOST_CHECK_EQUAL(direction, "application_to_control_system");
311 BOOST_CHECK_EQUAL(unit, "");
312 }
313 else {
314 BOOST_ERROR("Wrong variable name found.");
315 }
316 }
317 }
318 }
319 else {
320 BOOST_ERROR("Wrong tag found.");
321 }
322 }
323
324 BOOST_CHECK(found_pipeIn);
325 BOOST_CHECK(found_pipeOut);
326 BOOST_CHECK(found_multiplierDIn);
327 BOOST_CHECK(found_multiplierDOut);
328 BOOST_CHECK(found_multiplierU16In);
329 BOOST_CHECK(found_multiplierU16Out);
330 BOOST_CHECK(foundTrigger);
331 BOOST_CHECK(foundDeviceMessage);
332 BOOST_CHECK(foundDeviceStatus);
333 BOOST_CHECK(foundBecameFunctional);
334 }
335
336 BOOST_AUTO_TEST_CASE(testDOTGeneration) {
337 std::cout << "***************************************************************"
338 "******************************************************"
339 << std::endl;
340 std::cout << "==> tesDOTGeneration" << std::endl;
341
342 // delete DOT file if already existing
343 boost::filesystem::remove("TestAppInstance.dot");
344
345 // create app which exports some properties and generate its XML file
346 TestApp app("TestAppInstance");
347 app.generateDOT();
348
349 // check existence
350 bool found_dot{boost::filesystem::exists("TestAppInstance.dot")};
351
352 BOOST_CHECK(found_dot);
353 }
354
355} // namespace Tests::testApplication
void generateXML()
Instead of running the application, just initialise it and output the published variables to an XML f...
void generateDOT()
Instead of running the application, just initialise it and output the published variables to a DOT fi...
void shutdown() override
This will remove the global pointer to the instance and allows creating another instance afterwards.
const std::string & getDeviceAliasOrURI() const
friend class Application
Definition ModuleGroup.h:47
InvalidityTracer application module.
BOOST_AUTO_TEST_CASE(testApplicationExceptions)
std::string getValueFromNode(const xmlpp::Node *node, const std::string &subnodeName)
Generic module to pipe through a scalar value without altering it.
Definition Pipe.h:17
ctk::ScalarPipe< std::string > pipe
TestApp(const std::string &name)
ctk::ConstMultiplier< uint16_t, uint16_t, 120 > multiplierU16
ctk::ConstMultiplier< double > multiplierD