/*
 * 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>

#include <mtca4u/ExperimentalFeatures.h>

#include "Application.h"
#include "ScalarAccessor.h"
#include "ApplicationModule.h"
#include "DeviceModule.h"
#include "ControlSystemModule.h"
#include "TestDecoratorRegisterAccessor.h"
#include "VariableGroup.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;

/*********************************************************************************************************************/
/* 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::ScalarPushInput<uint32_t> someUIntInput{this, "someUIntInput", "", "Unsigned integer"};
    ctk::ScalarOutput<T> someOutput{this, "someOutput", "cm", "Description"};

    struct Outputs : public ctk::VariableGroup {
      using ctk::VariableGroup::VariableGroup;
      ctk::ScalarOutput<T> v1{this, "v1", "cm", "Output 1 for testing"};
      ctk::ScalarOutput<T> v2{this, "v2", "cm", "Output 2 for testing"};
      ctk::ScalarOutput<T> v3{this, "v3", "cm", "Output 3 for testing"};
      ctk::ScalarOutput<T> v4{this, "v4", "cm", "Output 4 for testing"};
    };
    Outputs outputs{this, "outputs"};

    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;
        usleep(10000);  // wait some extra time to make sure we are really blocking the test procedure thread
        someOutput.write();
      }
    }
};

/*********************************************************************************************************************/
/* the AsyncReadTestModule asynchronously reads its input in the main loop and writes the result to its output */

template<typename T>
struct AsyncReadTestModule : public ctk::ApplicationModule {
    AsyncReadTestModule(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) {
        auto &future = someInput.readAsync();
        future.wait();
        T val = someInput;
        someOutput = val;
        usleep(10000);  // wait some extra time to make sure we are really blocking the test procedure thread
        someOutput.write();
      }
    }
};

/*********************************************************************************************************************/
/* the ReadAnyTestModule calls readAny on a bunch of inputs and outputs some information on the received data */

template<typename T>
struct ReadAnyTestModule : public ctk::ApplicationModule {
    ReadAnyTestModule(ctk::EntityOwner *owner, const std::string &name) : ctk::ApplicationModule(owner,name) {}

    struct Inputs : public ctk::VariableGroup {
      using ctk::VariableGroup::VariableGroup;
      ctk::ScalarPushInput<T> v1{this, "v1", "cm", "Input 1 for testing"};
      ctk::ScalarPushInput<T> v2{this, "v2", "cm", "Input 2 for testing"};
      ctk::ScalarPushInput<T> v3{this, "v3", "cm", "Input 3 for testing"};
      ctk::ScalarPushInput<T> v4{this, "v4", "cm", "Input 4 for testing"};
    };
    Inputs inputs{this, "inputs"};
    ctk::ScalarOutput<T> value{this, "value", "cm", "The last value received from any of the inputs"};
    ctk::ScalarOutput<uint32_t> index{this, "index", "", "The index (1..4) of the input where the last value was received"};

    void mainLoop() {
      while(true) {
        auto justRead = inputs.readAny();
        if(inputs.v1.isSameRegister(justRead)) {
          index = 1;
          value = (T)inputs.v1;
        }
        else if(inputs.v2.isSameRegister(justRead)) {
          index = 2;
          value = (T)inputs.v2;
        }
        else if(inputs.v3.isSameRegister(justRead)) {
          index = 3;
          value = (T)inputs.v3;
        }
        else if(inputs.v4.isSameRegister(justRead)) {
          index = 4;
          value = (T)inputs.v4;
        }
        else {
          index = 0;
          value = 0;
        }
        usleep(10000);  // wait some extra time to make sure we are really blocking the test procedure thread
        index.write();
        value.write();
      }
    }
};

/*********************************************************************************************************************/
/* dummy application */

template<typename T>
struct TestApplication : public ctk::Application {
    TestApplication() : Application("test application") {
      ChimeraTK::ExperimentalFeatures::enable();
    }
    ~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

    NoLoopTestModule<T> noLoopTestModule{this,"noLoopTestModule"};
    BlockingReadTestModule<T> blockingReadTestModule{this,"blockingReadTestModule"};
    AsyncReadTestModule<T> asyncReadTestModule{this,"asyncReadTestModule"};
    ReadAnyTestModule<T> readAnyTestModule{this,"readAnyTestModule"};
};

/*********************************************************************************************************************/
/* 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;

  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
  app.noLoopTestModule.outputs >= app.readAnyTestModule.inputs;  // just to avoid runtime warning
  app.readAnyTestModule.value >> ctk::VariableNetworkNode::makeConstant<T>(false, 0, 1); // just to avoid runtime warning
  app.readAnyTestModule.index >> app.noLoopTestModule.someUIntInput; // just to avoid runtime warning
  app.initialise();
  app.run();
  
  // check if we got the decorator for the input
  auto hlinput = app.noLoopTestModule.someInput.getHighLevelImplElement();
  BOOST_CHECK( boost::dynamic_pointer_cast<ctk::TestDecoratorRegisterAccessor<T>>(hlinput) == nullptr );

  // check that we did not get the decorator for the output
  auto hloutput = app.noLoopTestModule.someOutput.getHighLevelImplElement();
  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;

  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
  app.noLoopTestModule.outputs >= app.readAnyTestModule.inputs;  // just to avoid runtime warning
  app.readAnyTestModule.value >> ctk::VariableNetworkNode::makeConstant<T>(false, 0, 1); // just to avoid runtime warning
  app.readAnyTestModule.index >> app.noLoopTestModule.someUIntInput; // just to avoid runtime warning
  app.enableTestableMode();
  app.initialise();
  app.run();
  app.resumeApplication();  // don't take control in this test

  // check if we got the decorator for the input
  auto hlinput = app.noLoopTestModule.someInput.getHighLevelImplElement();
  BOOST_CHECK( boost::dynamic_pointer_cast<ctk::TestDecoratorRegisterAccessor<T>>(hlinput) != nullptr );

  // check that we did not get the decorator for the output
  auto hloutput = app.noLoopTestModule.someOutput.getHighLevelImplElement();
  BOOST_CHECK( boost::dynamic_pointer_cast<ctk::TestDecoratorRegisterAccessor<T>>(hloutput) == nullptr );
  
  // 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.noLoopTestModule.outputs >= app.readAnyTestModule.inputs;  // just to avoid runtime warning
  app.readAnyTestModule.value >> ctk::VariableNetworkNode::makeConstant<T>(false, 0, 1); // just to avoid runtime warning
  app.readAnyTestModule.index >> app.noLoopTestModule.someUIntInput; // 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.noLoopTestModule.outputs >= app.readAnyTestModule.inputs;  // just to avoid runtime warning
  app.readAnyTestModule.value >> ctk::VariableNetworkNode::makeConstant<T>(false, 0, 1); // just to avoid runtime warning
  app.readAnyTestModule.index >> app.noLoopTestModule.someUIntInput; // 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 testReadAny in test mode */

BOOST_AUTO_TEST_CASE_TEMPLATE( testReadAny, T, test_types ) {
  std::cout << "*********************************************************************************************************************" << std::endl;
  std::cout << "==> testReadAny<" << typeid(T).name() << ">" << std::endl;

  TestApplication<T> app;

  app.noLoopTestModule.someOutput >> app.asyncReadTestModule.someInput; // just to avoid runtime warning
  app.asyncReadTestModule.someOutput >> ctk::VariableNetworkNode::makeConstant<T>(false, 0, 1); // just to avoid runtime warning
  app.blockingReadTestModule.someOutput >> app.blockingReadTestModule.someInput; // just to avoid runtime warning
  app.noLoopTestModule.outputs >= app.readAnyTestModule.inputs;
  app.readAnyTestModule.value >> app.noLoopTestModule.someInput;
  app.readAnyTestModule.index >> app.noLoopTestModule.someUIntInput;
  app.enableTestableMode();
  app.initialise();
  app.run();

  // check that we don't receive anything yet
  usleep(10000);
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == false);
  
  // send something to v4
  app.noLoopTestModule.outputs.v4 = 66;
  app.noLoopTestModule.outputs.v4.write();

  // check that we still don't receive anything yet
  usleep(10000);
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == false);
  
  // run the application and check that we got the expected result
  app.stepApplication();
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someInput == 66);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput == 4);
  
  // send something to v1
  app.noLoopTestModule.outputs.v1 = 33;
  app.noLoopTestModule.outputs.v1.write();

  // check that we still don't receive anything yet
  usleep(10000);
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == false);
  
  // run the application and check that we got the expected result
  app.stepApplication();
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someInput == 33);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput == 1);
  
  // send something to v1 again
  app.noLoopTestModule.outputs.v1 = 34;
  app.noLoopTestModule.outputs.v1.write();

  // check that we still don't receive anything yet
  usleep(10000);
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == false);
  
  // run the application and check that we got the expected result
  app.stepApplication();
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someInput == 34);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput == 1);
  
  // send something to v3
  app.noLoopTestModule.outputs.v3 = 40;
  app.noLoopTestModule.outputs.v3.write();

  // check that we still don't receive anything yet
  usleep(10000);
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == false);
  
  // run the application and check that we got the expected result
  app.stepApplication();
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someInput == 40);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput == 3);
  
  // send something to v2
  app.noLoopTestModule.outputs.v2 = 50;
  app.noLoopTestModule.outputs.v2.write();

  // check that we still don't receive anything yet
  usleep(10000);
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == false);
  
  // run the application and check that we got the expected result
  app.stepApplication();
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someInput == 50);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput == 2);
  
  // send something to v1 a 3rd time
  app.noLoopTestModule.outputs.v1 = 35;
  app.noLoopTestModule.outputs.v1.write();

  // check that we still don't receive anything yet
  usleep(10000);
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == false);
  
  // run the application and check that we got the expected result
  app.stepApplication();
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someInput == 35);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput == 1);

  // check that we still don't receive anything anymore, even if we try running the application again
  app.stepApplication();
  usleep(10000);
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == false);
  
}

/*********************************************************************************************************************/
/* test the interplay of multiple chained modules and their threads in test mode */

BOOST_AUTO_TEST_CASE_TEMPLATE( testChainedModules, T, test_types ) {
  std::cout << "*********************************************************************************************************************" << std::endl;
  std::cout << "==> testChainedModules<" << typeid(T).name() << ">" << std::endl;

  TestApplication<T> app;

  // put everything we got into one chain
  app.noLoopTestModule.outputs >= app.readAnyTestModule.inputs;
  app.readAnyTestModule.value >> app.blockingReadTestModule.someInput;
  app.blockingReadTestModule.someOutput >> app.asyncReadTestModule.someInput;
  app.asyncReadTestModule.someOutput >> app.noLoopTestModule.someInput;
  app.readAnyTestModule.index >> app.noLoopTestModule.someUIntInput;
  app.noLoopTestModule.someOutput >> ctk::VariableNetworkNode::makeConstant<T>(false, 0, 1); // just to avoid runtime warning

  app.enableTestableMode();
  app.initialise();
  app.run();

  // check that we don't receive anything yet
  usleep(10000);
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == false);
  
  // send something to v2
  app.noLoopTestModule.outputs.v2 = 11;
  app.noLoopTestModule.outputs.v2.write();

  // check that we still don't receive anything yet
  usleep(10000);
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == false);
  
  // run the application and check that we got the expected result
  app.stepApplication();
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someInput == 11);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput == 2);
  
  // send something to v3
  app.noLoopTestModule.outputs.v3 = 12;
  app.noLoopTestModule.outputs.v3.write();

  // check that we still don't receive anything yet
  usleep(10000);
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == false);
  
  // run the application and check that we got the expected result
  app.stepApplication();
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someInput == 12);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput == 3);
  
  // send something to v3 again
  app.noLoopTestModule.outputs.v3 = 13;
  app.noLoopTestModule.outputs.v3.write();

  // check that we still don't receive anything yet
  usleep(10000);
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == false);
  
  // run the application and check that we got the expected result
  app.stepApplication();
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == true);
  BOOST_CHECK(app.noLoopTestModule.someInput == 13);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput == 3);

  // check that we still don't receive anything anymore, even if we try running the application again
  app.stepApplication();
  usleep(10000);
  BOOST_CHECK(app.noLoopTestModule.someInput.readNonBlocking() == false);
  BOOST_CHECK(app.noLoopTestModule.someUIntInput.readNonBlocking() == false);

}