Newer
Older
Martin Christoph Hierholzer
committed
/*
* testTestFacilities.cc
*
* Created on: Feb 20, 2017
* Author: Martin Hierholzer
*/
#include <future>
#include <chrono>
#define BOOST_TEST_MODULE testTestFacilities
#include <boost/test/included/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <boost/mpl/list.hpp>
Martin Christoph Hierholzer
committed
#include <mtca4u/ExperimentalFeatures.h>
Martin Christoph Hierholzer
committed
#include "Application.h"
#include "ScalarAccessor.h"
#include "ApplicationModule.h"
#include "DeviceModule.h"
#include "ControlSystemModule.h"
#include "TestDecoratorRegisterAccessor.h"
using namespace boost::unit_test_framework;
namespace ctk = ChimeraTK;
#define CHECK_TIMEOUT(condition, maxMilliseconds) \
{ \
std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); \
while(!(condition)) { \
bool timeout_reached = (std::chrono::steady_clock::now()-t0) > std::chrono::milliseconds(maxMilliseconds); \
BOOST_CHECK( !timeout_reached ); \
if(timeout_reached) break; \
usleep(1000); \
} \
}
// list of user types the accessors are tested with
typedef boost::mpl::list<int8_t,uint8_t,
int16_t,uint16_t,
int32_t,uint32_t,
float,double> test_types;
/*********************************************************************************************************************/
Martin Christoph Hierholzer
committed
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/* the NoLoopTestModule is a dummy test module with an empty mainLoop */
template<typename T>
struct NoLoopTestModule : public ctk::ApplicationModule {
NoLoopTestModule(ctk::EntityOwner *owner, const std::string &name) : ctk::ApplicationModule(owner,name) {}
ctk::ScalarPushInput<T> someInput{this, "someInput", "cm", "This is just some input for testing"};
ctk::ScalarOutput<T> someOutput{this, "someOutput", "cm", "Description"};
void mainLoop() {
}
};
/*********************************************************************************************************************/
/* the BlockingReadTestModule blockingly reads its input in the main loop and writes the result to its output */
template<typename T>
struct BlockingReadTestModule : public ctk::ApplicationModule {
BlockingReadTestModule(ctk::EntityOwner *owner, const std::string &name) : ctk::ApplicationModule(owner,name) {}
ctk::ScalarPushInput<T> someInput{this, "someInput", "cm", "This is just some input for testing"};
ctk::ScalarOutput<T> someOutput{this, "someOutput", "cm", "Description"};
void mainLoop() {
while(true) {
someInput.read();
T val = someInput;
someOutput = val;
someOutput.write();
}
}
};
/*********************************************************************************************************************/
/* the AsyncReadTestModule asynchronously reads its input in the main loop and writes the result to its output */
Martin Christoph Hierholzer
committed
template<typename T>
Martin Christoph Hierholzer
committed
struct AsyncReadTestModule : public ctk::ApplicationModule {
AsyncReadTestModule(ctk::EntityOwner *owner, const std::string &name) : ctk::ApplicationModule(owner,name) {}
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
ctk::ScalarPushInput<T> someInput{this, "someInput", "cm", "This is just some input for testing"};
Martin Christoph Hierholzer
committed
ctk::ScalarOutput<T> someOutput{this, "someOutput", "cm", "Description"};
Martin Christoph Hierholzer
committed
void mainLoop() {
while(true) {
auto &future = someInput.readAsync();
future.wait();
T val = someInput;
someOutput = val;
someOutput.write();
}
}
Martin Christoph Hierholzer
committed
};
/*********************************************************************************************************************/
/* dummy application */
template<typename T>
struct TestApplication : public ctk::Application {
Martin Christoph Hierholzer
committed
TestApplication() : Application("test application") {
ChimeraTK::ExperimentalFeatures::enable();
}
Martin Christoph Hierholzer
committed
~TestApplication() { shutdown(); }
using Application::makeConnections; // we call makeConnections() manually in the tests to catch exceptions etc.
void defineConnections() {} // the setup is done in the tests
Martin Christoph Hierholzer
committed
NoLoopTestModule<T> noLoopTestModule{this,"noLoopTestModule"};
BlockingReadTestModule<T> blockingReadTestModule{this,"blockingReadTestModule"};
AsyncReadTestModule<T> asyncReadTestModule{this,"asyncReadTestModule"};
Martin Christoph Hierholzer
committed
};
/*********************************************************************************************************************/
/* test that no TestDecoratorRegisterAccessor is used if the testable mode is not enabled */
BOOST_AUTO_TEST_CASE_TEMPLATE( testNoDecorator, T, test_types ) {
std::cout << "*********************************************************************************************************************" << std::endl;
std::cout << "==> testNoDecorator<" << typeid(T).name() << ">" << std::endl;
TestApplication<T> app;
Martin Christoph Hierholzer
committed
app.noLoopTestModule.someOutput >> app.noLoopTestModule.someInput;
app.blockingReadTestModule.someOutput >> app.blockingReadTestModule.someInput; // just to avoid runtime warning
app.asyncReadTestModule.someOutput >> app.asyncReadTestModule.someInput; // just to avoid runtime warning
Martin Christoph Hierholzer
committed
app.initialise();
app.run();
// check if we got the decorator for the input
Martin Christoph Hierholzer
committed
auto hlinput = app.noLoopTestModule.someInput.getHighLevelImplElement();
Martin Christoph Hierholzer
committed
BOOST_CHECK( boost::dynamic_pointer_cast<ctk::TestDecoratorRegisterAccessor<T>>(hlinput) == nullptr );
// check that we did not get the decorator for the output
Martin Christoph Hierholzer
committed
auto hloutput = app.noLoopTestModule.someOutput.getHighLevelImplElement();
Martin Christoph Hierholzer
committed
BOOST_CHECK( boost::dynamic_pointer_cast<ctk::TestDecoratorRegisterAccessor<T>>(hloutput) == nullptr );
}
/*********************************************************************************************************************/
/* simply test the TestDecoratorRegisterAccessor if it is used and if it properly works as a decorator */
BOOST_AUTO_TEST_CASE_TEMPLATE( testDecorator, T, test_types ) {
std::cout << "*********************************************************************************************************************" << std::endl;
std::cout << "==> testDecorator<" << typeid(T).name() << ">" << std::endl;
TestApplication<T> app;
Martin Christoph Hierholzer
committed
app.noLoopTestModule.someOutput >> app.noLoopTestModule.someInput;
app.blockingReadTestModule.someOutput >> app.blockingReadTestModule.someInput; // just to avoid runtime warning
app.asyncReadTestModule.someOutput >> app.asyncReadTestModule.someInput; // just to avoid runtime warning
Martin Christoph Hierholzer
committed
app.enableTestableMode();
app.initialise();
app.run();
Martin Christoph Hierholzer
committed
app.resumeApplication(); // don't take control in this test
Martin Christoph Hierholzer
committed
// check if we got the decorator for the input
Martin Christoph Hierholzer
committed
auto hlinput = app.noLoopTestModule.someInput.getHighLevelImplElement();
Martin Christoph Hierholzer
committed
BOOST_CHECK( boost::dynamic_pointer_cast<ctk::TestDecoratorRegisterAccessor<T>>(hlinput) != nullptr );
// check that we did not get the decorator for the output
Martin Christoph Hierholzer
committed
auto hloutput = app.noLoopTestModule.someOutput.getHighLevelImplElement();
Martin Christoph Hierholzer
committed
BOOST_CHECK( boost::dynamic_pointer_cast<ctk::TestDecoratorRegisterAccessor<T>>(hloutput) == nullptr );
Martin Christoph Hierholzer
committed
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
// check meta-data
BOOST_CHECK(app.noLoopTestModule.someInput.isReadOnly());
BOOST_CHECK(app.noLoopTestModule.someInput.isReadable());
BOOST_CHECK(!app.noLoopTestModule.someInput.isWriteable());
// test non blocking read
app.noLoopTestModule.someInput = 41;
for(int i=0; i<5; ++i) {
app.noLoopTestModule.someOutput = 42+i;
bool ret = app.noLoopTestModule.someInput.readNonBlocking();
BOOST_CHECK(ret == false);
int val = app.noLoopTestModule.someInput;
BOOST_CHECK(val == 42+i-1);
app.noLoopTestModule.someOutput.write();
ret = app.noLoopTestModule.someInput.readNonBlocking();
BOOST_CHECK(ret == true);
val = app.noLoopTestModule.someInput;
BOOST_CHECK(val == 42+i);
}
bool ret = app.noLoopTestModule.someInput.readNonBlocking();
BOOST_CHECK(ret == false);
// test blocking read
for(int i=0; i<5; ++i) {
auto future = std::async(std::launch::async, [&app] { app.noLoopTestModule.someInput.read(); });
app.noLoopTestModule.someOutput = 120+i;
BOOST_CHECK(future.wait_for(std::chrono::milliseconds(10)) == std::future_status::timeout);
app.noLoopTestModule.someOutput.write();
BOOST_CHECK(future.wait_for(std::chrono::milliseconds(100)) == std::future_status::ready);
int val = app.noLoopTestModule.someInput;
BOOST_CHECK(val == 120+i);
}
}
/*********************************************************************************************************************/
/* test blocking read in test mode */
BOOST_AUTO_TEST_CASE_TEMPLATE( testBlockingRead, T, test_types ) {
std::cout << "*********************************************************************************************************************" << std::endl;
std::cout << "==> testBlockingRead<" << typeid(T).name() << ">" << std::endl;
TestApplication<T> app;
app.noLoopTestModule.someOutput >> app.blockingReadTestModule.someInput;
app.blockingReadTestModule.someOutput >> app.noLoopTestModule.someInput;
app.asyncReadTestModule.someOutput >> app.asyncReadTestModule.someInput; // just to avoid runtime warning
app.enableTestableMode();
app.initialise();
app.run();
// test blocking read when taking control in the test thread
for(int i=0; i<5; ++i) {
app.noLoopTestModule.someOutput = 120+i;
app.noLoopTestModule.someOutput.write();
usleep(10000);
BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
app.stepApplication();
CHECK_TIMEOUT(app.noLoopTestModule.someInput.readNonBlocking() == true, 200);
int val = app.noLoopTestModule.someInput;
BOOST_CHECK(val == 120+i);
}
}
/*********************************************************************************************************************/
/* test async read in test mode */
BOOST_AUTO_TEST_CASE_TEMPLATE( testAsyncRead, T, test_types ) {
std::cout << "*********************************************************************************************************************" << std::endl;
std::cout << "==> testAsyncRead<" << typeid(T).name() << ">" << std::endl;
TestApplication<T> app;
app.noLoopTestModule.someOutput >> app.asyncReadTestModule.someInput;
app.asyncReadTestModule.someOutput >> app.noLoopTestModule.someInput;
app.blockingReadTestModule.someOutput >> app.blockingReadTestModule.someInput; // just to avoid runtime warning
app.enableTestableMode();
app.initialise();
app.run();
// test blocking read when taking control in the test thread
for(int i=0; i<5; ++i) {
app.noLoopTestModule.someOutput = 120+i;
app.noLoopTestModule.someOutput.write();
usleep(10000);
BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
app.stepApplication();
CHECK_TIMEOUT(app.noLoopTestModule.someInput.readNonBlocking() == true, 200);
int val = app.noLoopTestModule.someInput;
BOOST_CHECK(val == 120+i);
}