#include "gtest/gtest.h" #include "ariba/utility/system/SystemQueue.h" #include // usleep #include using namespace ::testing; using namespace ariba::utility; using namespace std; /** * Tests if the SystemQueue is initialized empty and not running. */ TEST(SystemQueue, Instantiation) { SystemQueue& sysq = SystemQueue::instance(); // cout << &sysq << endl; EXPECT_FALSE( sysq.isRunning() ); EXPECT_TRUE( sysq.isEmpty() ); } /** * Tests whether calling the SystemQueue::instance() always returns the same object. * * NOTE: This is an easy case, since this is the same compile unit.. * But can't hurt to test it anyway. */ TEST(SystemQueue, Singleton) { SystemQueue& sysq = SystemQueue::instance(); SystemQueue& sysq2 = SystemQueue::instance(); // cout << &sysq << endl; EXPECT_TRUE( &sysq == &sysq2 ); } /** * Start and stop the SystemQueue */ TEST(SystemQueue, StartStop) { SystemQueue& sysq = SystemQueue::instance(); EXPECT_FALSE( sysq.isRunning() ); // start sysq.run(); EXPECT_TRUE( sysq.isRunning() ); // stop sysq.cancel(); EXPECT_FALSE( sysq.isRunning() ); } /** * Test fixture * * class that can be called by the SystemQueue */ // To use a test fixture, derive a class from testing::Test. class SystemQueueTest : public testing::Test { public: // sleep time when waiting for the system queue (max total / each step) #define MAX_WAIT 100 // microseconds #define SLEEP_TIME 10 // microseconds SystemQueueTest() : checkmark(false), last_ordered_call(0) { } void Check() { checkmark = true; } void LongRunner() { usleep( MAX_WAIT / 2 ); checkmark = true; } /// wait for the checkmark to be set by a SystemQueue call, (but not too long!) void wait_for_checkmark(int max_wait = MAX_WAIT) { for ( int i = 0; i < max_wait / SLEEP_TIME; i++) { if ( checkmark ) break; cout << "/// sleeping for " << SLEEP_TIME << " microseconds ..." << endl; usleep(SLEEP_TIME); } } /// call that checks wheather it was performed in order void OrderedCall(int num) { // XXX cout << "### OrderedCall num: " << num << endl; // check ordering EXPECT_EQ( num, last_ordered_call + 1); last_ordered_call = num; } /// like OrderedCall, but calls itself to test nested calls void NestedOrderedCall(int from, int to) { // check ordering OrderedCall(from); // nested call if ( from < to ) { SystemQueue::instance().scheduleCall( boost::bind(&SystemQueueTest::NestedOrderedCall, this, from+1, to) ); } else { /// XXX because the current/old SystemQueue does not pass the Threading test, /// we have to signal, that when all nested calls are finished, /// so we need to set the checkmark here.. checkmark = true; } } bool checkmark; int last_ordered_call; }; /** * schedule a call and test whether it is actually performed by the SystemQueue */ TEST_F(SystemQueueTest, ScheduleCall) { SystemQueue& sysq = SystemQueue::instance(); checkmark = false; // just to be sure.. // start sysq.run(); // scheduleCall sysq.scheduleCall( boost::bind(&SystemQueueTest::Check, this) ); // wait for the event to happen wait_for_checkmark(MAX_WAIT); // stop sysq.cancel(); EXPECT_TRUE( checkmark ) << "Function was not called within " << MAX_WAIT << " microseconds."; } /** * This test actually tests two things [sorry, but it's hard to test them separately!] * * - first: the SystemQueue should not consider itself empty, while an event is processed * - second: SystemQueue events should be processed in parallel to the main thread * * NOTE: The timings here are not obvious, maybe they have to be adjusted on very slow machines * * NOTE: !! The current/old SystemQueue does NOT pass this test!! * * That's also why we need the unhandy wait_for_checkmark function, * instead a wait_until_empty function. */ TEST_F(SystemQueueTest, DISABLED_Threading) { SystemQueue& sysq = SystemQueue::instance(); checkmark = false; // just to be sure.. // start sysq.run(); // scheduleCall sysq.scheduleCall( boost::bind(&SystemQueueTest::LongRunner, this) ); // SystemQueue must not be empty as long as the event is not finished if ( sysq.isEmpty() ) { // assert that this test is actually meaningful ASSERT_FALSE( checkmark ) << "NOTE: This is not necessarily a bug, maybe the timing just have to adjusted. Try to increase MAX_WAIT."; EXPECT_TRUE( ! sysq.isEmpty() || checkmark ); } // wait for the event to finish wait_for_checkmark(MAX_WAIT); // stop sysq.cancel(); // even the long-runner should finally finish EXPECT_TRUE( checkmark ) << "Function has not finished within " << MAX_WAIT << " microseconds."; } /** * schedule multiple calls * * each call must be performed, in the correct order * * NOTE: The nested calls are not necessarily in order with calls scheduled from the main thread, * that's fine, therefore we make sure the nested calls are done, before scheduling new ones. */ TEST_F(SystemQueueTest, MultipleCalls) { SystemQueue& sysq = SystemQueue::instance(); // start sysq.run(); sysq.scheduleCall( boost::bind(&SystemQueueTest::OrderedCall, this, 1) ); sysq.scheduleCall( boost::bind(&SystemQueueTest::OrderedCall, this, 2) ); sysq.scheduleCall( boost::bind(&SystemQueueTest::NestedOrderedCall, this, 3, 5) ); // make sure all nested calls are done wait_for_checkmark(MAX_WAIT); // XXX should be "wait_until_empty() ...." checkmark = false; sysq.scheduleCall( boost::bind(&SystemQueueTest::OrderedCall, this, 6) ); // XXX same here... [ wait_until_empty() ] sysq.scheduleCall( boost::bind(&SystemQueueTest::Check, this) ); wait_for_checkmark(MAX_WAIT); // evaluation EXPECT_EQ( 6, last_ordered_call); // stop sysq.cancel(); } /** * This subclass has some special member functions suitable for timing tests */ class SystemQueueTimingTest : public SystemQueueTest { public: /** * typical delay time * * should be long enough for meaningful tests, * but should not lengthen the test excessivly */ #define DELAY_TIME 20 // ms #define DELAY_MARGIN 2000 // microseconds (1/1000 ms) /// constructor SystemQueueTimingTest() : SystemQueueTest() /* super constructor */, sysq( SystemQueue::instance() ) { } virtual void SetUp() { // start SystemQueue sysq.run(); } virtual void TearDown() { // stop SystemQueue sysq.cancel(); } SystemQueue& sysq; }; /** * schedules a delayed call and tests whether it is called (more or less timely..) */ TEST_F(SystemQueueTimingTest, DelayedCall) { // scheduleCall sysq.scheduleCall( boost::bind(&SystemQueueTimingTest::Check, this), DELAY_TIME ); // noting to do until the delay is up.. usleep(DELAY_TIME + DELAY_MARGIN*10); // XXX margin too high (TODO lower when SysQ is reimplemented) // wait for the event to happen wait_for_checkmark(MAX_WAIT); EXPECT_TRUE( checkmark ) << "Delayed function was not called within delaytime (" << DELAY_TIME << " ms) + " << (MAX_WAIT + DELAY_MARGIN) << " microseconds."; } /** * schedules a delayed call and tests whether it is called (more or less timely..) */ TEST_F(SystemQueueTimingTest, MultipleCalls) { // schedule 4th call sysq.scheduleCall( boost::bind(&SystemQueueTest::OrderedCall, this, 4), DELAY_TIME*3 ); // schedule 2nd call sysq.scheduleCall( boost::bind(&SystemQueueTest::OrderedCall, this, 2), DELAY_TIME*1 ); // schedule 3rd call sysq.scheduleCall( boost::bind(&SystemQueueTest::OrderedCall, this, 3), DELAY_TIME*2 ); // schedule 1st call (without delay) sysq.scheduleCall( boost::bind(&SystemQueueTest::OrderedCall, this, 1) ); // XXX the usual bug.. sysq.scheduleCall( boost::bind(&SystemQueueTest::Check, this), DELAY_TIME*4 ); // noting to do until the delay is up.. usleep(DELAY_TIME*4 + DELAY_MARGIN*100); // XXX margin too high // wait for the event to happen wait_for_checkmark(MAX_WAIT); // evaluation EXPECT_EQ( 4, last_ordered_call); }