Skip to content
Snippets Groups Projects
Commit 94ac18d1 authored by Martin Killenberg's avatar Martin Killenberg
Browse files

Merge branch 'master' of github.com:ChimeraTK/ApplicationCore

parents 19eb02f4 633f2632
No related branches found
No related tags found
No related merge requests found
......@@ -40,7 +40,7 @@ namespace ChimeraTK {
\subsection spec_exceptionHandling_behaviour_runtime_errors Runtime error handling
- \anchor b_2 2. When a ChimeraTK::runtime_error has been received by the framework (thrown by a device register accessor):
- 2.1 The exception status is published as a process variable together with an error message.
- \anchor exceptionHandling_b_2_1 2.1 The exception status is published as a process variable together with an error message. [\ref testExceptionHandling_b_2_1 "T"]
- 2.1.1 The variable \c Devices/\<alias\>/status contains a boolean flag whether the device is in an error state.
- 2.1.2 The variable \c Devices/\<alias\>/message contains an error message, if the device is in an error state, or an empty string otherwise.
- \anchor b_2_2 2.2 Read operations will propagate the DataValidity::faulty flag to the owning module / fan out (without changing the actual data value of the process variable):
......
#include <cstring>
#include <future>
#define BOOST_TEST_MODULE testExceptionHandling
......@@ -21,6 +22,146 @@
using namespace boost::unit_test_framework;
namespace ctk = ChimeraTK;
// TODO: extend/rename test application as required/ make new test applications as the exception
// handling cases are covered.
/**************************/
struct Module : ctk::ApplicationModule {
using ctk::ApplicationModule::ApplicationModule;
ctk::ScalarPollInput<int> fromDevice{this, "REG1", "", "", {"DEVICE"}};
ctk::ScalarOutput<int> toCs{this, "o1", "", "", {"CS"}};
void mainLoop() override {
while(true) {
readAll();
toCs = static_cast<int>(fromDevice);
writeAll();
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
}
}
};
struct DummyApplication : ctk::Application {
// +----------+ poll_type_variable +------+
// |Module x<------------------------+Device|
// +----------+ +------+
constexpr static char const* ExceptionDummyCDD1 = "(ExceptionDummy:1?map=test.map)";
DummyApplication() : Application("testFault") {}
~DummyApplication() { shutdown(); }
Module m{this, "", ""};
ctk::ControlSystemModule cs;
ctk::DeviceModule device{this, ExceptionDummyCDD1};
void defineConnections() override {
findTag("CS").connectTo(cs);
findTag("DEVICE").connectTo(device);
}
};
struct Fixture_noTestFacility {
Fixture_noTestFacility()
: deviceBackend(boost::dynamic_pointer_cast<ctk::ExceptionDummy>(
ChimeraTK::BackendFactory::getInstance().createBackend(DummyApplication::ExceptionDummyCDD1))) {
deviceBackend->open();
testFacitiy.runApplication();
//
// As a test precondition, the DeviceModule must have completed its startup procedure. The code
// block below is a workaround to ensure this.
//
// Code below depends on behavior of process variable 'status', where 'status' is initialized
// with 0 as default after which its written with:
// - 1 (before DeviceModule begins opening the device),
// - 0 (once DeviceModule successfully opens the device/device startup is complete).
// This sequence is similar for the associated 'message' variable; replace 0/1 values above
// with an empty/non-empty string.
//
// Thus checking for two writes on these variables is a resonable indication that device startup
// is done. (provided the assumption explained above holds).
/************************************************************************************************/
auto status =
testFacitiy.getScalar<int>(ctk::RegisterPath("/Devices") / DummyApplication::ExceptionDummyCDD1 / "status");
auto message = testFacitiy.getScalar<std::string>(
ctk::RegisterPath("/Devices") / DummyApplication::ExceptionDummyCDD1 / "message");
CHECK_TIMEOUT(status.readNonBlocking() == true, 100000);
CHECK_TIMEOUT(status.readNonBlocking() == true, 100000);
CHECK_TIMEOUT(message.readNonBlocking() == true, 100000);
CHECK_TIMEOUT(message.readNonBlocking() == true, 100000);
/************************************************************************************************/
}
~Fixture_noTestFacility() { deviceBackend->throwExceptionRead = false; }
ctk::TestFacility testFacitiy{false};
boost::shared_ptr<ctk::ExceptionDummy> deviceBackend;
DummyApplication app;
};
/* **********************************/
/*
* This test suite checks behavior on a device related runtime error.
*/
BOOST_AUTO_TEST_SUITE(checkRuntimeErrorHandling)
/*
* Verify the framework creates fault indicator process variables for a device.
*
* These are mapped on the control system as:
* - /Devices/<device_alias or cdd>/status
* - /Devices/<device_alias or cdd>/message
*
* A runtime errror on <device_alias> changes status to 1, with a non empty message
* string.
*
* \anchor testExceptionHandling_b_2_1 \ref exceptionHandling_b_2_1 "B.2.1"
*/
BOOST_FIXTURE_TEST_CASE(testFaultReporting, Fixture_noTestFacility) {
auto status =
testFacitiy.getScalar<int>(ctk::RegisterPath("/Devices") / DummyApplication::ExceptionDummyCDD1 / "status");
auto message = testFacitiy.getScalar<std::string>(
ctk::RegisterPath("/Devices") / DummyApplication::ExceptionDummyCDD1 / "message");
BOOST_CHECK_EQUAL(status, 0);
BOOST_CHECK_EQUAL(static_cast<std::string>(message), "");
deviceBackend->throwExceptionRead = true;
CHECK_TIMEOUT(status.readNonBlocking() == true, 10000);
CHECK_TIMEOUT(message.readNonBlocking() == true, 10000);
BOOST_CHECK_EQUAL(status, 1);
BOOST_CHECK(static_cast<std::string>(message) != "");
deviceBackend->throwExceptionRead = false;
CHECK_TIMEOUT(status.readNonBlocking() == true, 10000);
CHECK_TIMEOUT(message.readNonBlocking() == true, 10000);
BOOST_CHECK_EQUAL(status, 0);
BOOST_CHECK(static_cast<std::string>(message) == "");
}
BOOST_AUTO_TEST_CASE(testBlockingRead) { // wait_for_new_data
// how does the api of the accessor look like
//device1DummyBackend["m1"]("i3")[cs("trigger", typeid(int), 1)] >> cs("i3", typeid(int), 1);
// make one with wait_for_new_data
// The framework decides the accessmode flags based on how the wiring looks like:
//
// we will have to make up wiring to get what we desire.
}
BOOST_AUTO_TEST_CASE(testReadLatest) {}
BOOST_AUTO_TEST_CASE(testReadNonBlocking) {}
BOOST_AUTO_TEST_CASE(testWrite) {}
BOOST_AUTO_TEST_SUITE_END()
constexpr char ExceptionDummyCDD1[] = "(ExceptionDummy:1?map=test3.map)";
constexpr char ExceptionDummyCDD2[] = "(ExceptionDummy:2?map=test3.map)";
constexpr char ExceptionDummyCDD3[] = "(ExceptionDummy:3?map=test4.map)";
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment