source: tests/SystemQueue-tests.cc@ 12765

Last change on this file since 12765 was 12765, checked in by hock@…, 10 years ago

new functionality: SystemQueue::leave() and SystemQueue::join()

( + unit tests)

File size: 15.0 KB
Line 
1#include "gtest/gtest.h"
2#include "ariba/utility/system/SystemQueue.h"
3
4#include <unistd.h> // usleep
5#include <boost/date_time.hpp> // local_time()
6
7#include <ostream>
8
9using namespace ::testing;
10using namespace ariba::utility;
11using namespace std;
12
13using boost::posix_time::microsec_clock;
14
15
16/**
17 * Tests if the SystemQueue is initialized empty and not running.
18 */
19TEST(SystemQueue, Instantiation)
20{
21 SystemQueue& sysq = SystemQueue::instance();
22
23// cout << &sysq << endl;
24
25 EXPECT_FALSE( sysq.isRunning() );
26 EXPECT_TRUE( sysq.isEmpty() );
27}
28
29
30/**
31 * Tests whether calling the SystemQueue::instance() always returns the same object.
32 *
33 * NOTE: This is an easy case, since this is the same compile unit..
34 * But can't hurt to test it anyway.
35 */
36TEST(SystemQueue, Singleton)
37{
38 SystemQueue& sysq = SystemQueue::instance();
39 SystemQueue& sysq2 = SystemQueue::instance();
40
41// cout << &sysq << endl;
42
43 EXPECT_TRUE( &sysq == &sysq2 );
44}
45
46
47/**
48 * Start and stop the SystemQueue
49 */
50TEST(SystemQueue, StartStop)
51{
52 SystemQueue& sysq = SystemQueue::instance();
53
54 EXPECT_FALSE( sysq.isRunning() );
55
56 // start
57 sysq.run();
58
59 EXPECT_TRUE( sysq.isRunning() );
60
61 // stop
62 sysq.cancel();
63
64 // XXX weg, wenn das join klappt!!
65 usleep(100);
66
67 EXPECT_FALSE( sysq.isRunning() );
68}
69
70
71/**
72 * Test fixture
73 *
74 * class that can be called by the SystemQueue
75 */
76// To use a test fixture, derive a class from testing::Test.
77class SystemQueueTest :
78 public testing::Test
79{
80public:
81
82// sleep time when waiting for the system queue (max total / each step)
83#define MAX_WAIT 100 // microseconds
84#define SLEEP_TIME 10 // microseconds
85
86 SystemQueueTest() :
87 checkmark(false),
88 last_ordered_call(0)
89 {
90 }
91
92 void Check()
93 {
94 cout << "### Check ### "<< endl;
95
96 checkmark = true;
97 }
98
99 void Cancel()
100 {
101 ASSERT_THROW(SystemQueue::instance().cancel(), std::logic_error);
102
103 checkmark = true;
104 }
105
106 void Leave()
107 {
108 // let's do something before leaving..
109 this->LongRunner();
110 checkmark = false;
111
112 cout << "### Leaving SysQ ### "<< endl;
113 SystemQueue::instance().leave();
114 }
115
116 void LongRunner()
117 {
118 usleep( MAX_WAIT / 2 );
119
120 checkmark = true;
121 }
122
123
124 /// wait for the checkmark to be set by a SystemQueue call, (but not too long!)
125 void wait_for_checkmark(int max_wait = MAX_WAIT)
126 {
127 for ( int i = 0; i < max_wait / SLEEP_TIME; i++)
128 {
129 if ( checkmark )
130 break;
131
132 cout << "### sleeping for " << SLEEP_TIME << " microseconds ..." << endl;
133 usleep(SLEEP_TIME);
134 }
135 }
136
137 /// call that checks wheather it was performed in order
138 void OrderedCall(int num)
139 {
140 // XXX
141 cout << "### OrderedCall num: " << num << endl;
142
143 // check ordering
144 EXPECT_EQ( num, last_ordered_call + 1);
145
146 last_ordered_call = num;
147 }
148
149 /// like OrderedCall, but calls itself to test nested calls
150 void NestedOrderedCall(int from, int to)
151 {
152 // check ordering
153 OrderedCall(from);
154
155 // nested call
156 if ( from < to )
157 {
158 SystemQueue::instance().scheduleCall(
159 boost::bind(&SystemQueueTest::NestedOrderedCall,
160 this,
161 from+1,
162 to)
163 );
164 }
165 else
166 {
167 /// XXX because the current/old SystemQueue does not pass the Threading test,
168 /// we have to signal, that when all nested calls are finished,
169 /// so we need to set the checkmark here..
170
171 checkmark = true;
172 }
173 }
174
175 bool checkmark;
176 int last_ordered_call;
177};
178
179
180/**
181 * Enqueues an event but then cancels the SystemQueue without running
182 */
183TEST_F(SystemQueueTest, EmptyAfterCancel)
184{
185 SystemQueue& sysq = SystemQueue::instance();
186
187 EXPECT_TRUE( sysq.isEmpty() );
188
189 // enqueue event
190 sysq.scheduleCall(
191 boost::bind(&SystemQueueTest::Check, this)
192 );
193
194 EXPECT_FALSE( sysq.isEmpty() );
195
196 // cancel
197 sysq.cancel();
198
199 EXPECT_TRUE( sysq.isEmpty() );
200}
201
202
203/**
204 * cancels the SystemQueue from inside a scheduled event
205 *
206 * --> and expects that this throws an exception
207 *
208 * NOTE: due to the SystemQueue singleton design, explicit cleanup is necessary!
209 */
210TEST_F(SystemQueueTest, CancelFromInsideEvent)
211{
212 SystemQueue& sysq = SystemQueue::instance();
213 checkmark = false; // just to be sure..
214
215 // start
216 sysq.run();
217
218 // scheduleCall
219 sysq.scheduleCall(
220 boost::bind(&SystemQueueTest::Cancel, this)
221 );
222
223 // wait for the event to happen
224 wait_for_checkmark(MAX_WAIT);
225
226 // CLEANUP
227 sysq.cancel();
228
229 EXPECT_FALSE( sysq.isRunning() );
230 EXPECT_TRUE( sysq.isEmpty() );
231}
232
233
234/**
235 * leaves the SystemQueue from inside a scheduled event (in contrast to cancel)
236 *
237 * NOTE: due to the SystemQueue singleton design, explicit cleanup is necessary!
238 *
239 * [ NOTE: the old System Queue does not support this. ]
240 */
241TEST_F(SystemQueueTest, LeaveFromInsideEvent)
242{
243 SystemQueue& sysq = SystemQueue::instance();
244
245 // start
246 sysq.run();
247
248 // scheduleCall
249 sysq.scheduleCall(
250 boost::bind(&SystemQueueTest::Leave, this)
251 );
252
253 // schedule another which must NOT be performed
254 sysq.scheduleCall(
255 boost::bind(&SystemQueueTest::Check, this)
256 );
257
258 // sleep until the SystemQueue thread has finished
259 sysq.join();
260
261 EXPECT_FALSE( sysq.isRunning() );
262
263 // ensure Check() was not perfomed
264 EXPECT_FALSE( sysq.isEmpty() );
265 EXPECT_FALSE( checkmark );
266
267 // CLEANUP
268 sysq.cancel();
269
270 EXPECT_FALSE( sysq.isRunning() );
271 EXPECT_TRUE( sysq.isEmpty() );
272}
273
274
275/**
276 * schedule a call and test whether it is actually performed by the SystemQueue
277 */
278TEST_F(SystemQueueTest, ScheduleCall)
279{
280 SystemQueue& sysq = SystemQueue::instance();
281 checkmark = false; // just to be sure..
282
283 // start
284 sysq.run();
285
286 // scheduleCall
287 sysq.scheduleCall(
288 boost::bind(&SystemQueueTest::Check, this)
289 );
290
291 // wait for the event to happen
292 wait_for_checkmark(MAX_WAIT);
293
294 // stop
295 sysq.cancel();
296
297 EXPECT_TRUE( checkmark ) << "Function was not called within " << MAX_WAIT << " microseconds.";
298}
299
300
301/**
302 * This test actually tests two things [sorry, but it's hard to test them separately!]
303 *
304 * - first: the SystemQueue should not consider itself empty, while an event is processed
305 * - second: SystemQueue events should be processed in parallel to the main thread
306 *
307 * NOTE: The timings here are not obvious, maybe they have to be adjusted on very slow machines
308 *
309 * NOTE: !! The current/old SystemQueue does NOT pass this test!!
310 *
311 * That's also why we need the unhandy wait_for_checkmark function,
312 * instead a wait_until_empty function.
313 */
314TEST_F(SystemQueueTest, Threading)
315{
316 SystemQueue& sysq = SystemQueue::instance();
317 checkmark = false; // just to be sure..
318
319 // start
320 sysq.run();
321
322 // scheduleCall
323 sysq.scheduleCall(
324 boost::bind(&SystemQueueTest::LongRunner, this)
325 );
326
327 // SystemQueue must not be empty as long as the event is not finished
328 if ( sysq.isEmpty() )
329 {
330 // assert that this test is actually meaningful
331 ASSERT_FALSE( checkmark )
332 << "NOTE: This is not necessarily a bug, maybe the timing just have to adjusted. Try to increase MAX_WAIT.";
333
334 EXPECT_TRUE( ! sysq.isEmpty() || checkmark );
335 }
336
337 // wait for the event to finish
338 wait_for_checkmark(MAX_WAIT);
339
340 // stop
341 sysq.cancel();
342
343 // even the long-runner should finally finish
344 EXPECT_TRUE( checkmark ) << "Function has not finished within " << MAX_WAIT << " microseconds.";
345}
346
347/**
348 * schedule multiple calls
349 *
350 * each call must be performed, in the correct order
351 *
352 * NOTE: The nested calls are not necessarily in order with calls scheduled from the main thread,
353 * that's fine, therefore we make sure the nested calls are done, before scheduling new ones.
354 */
355TEST_F(SystemQueueTest, MultipleCalls)
356{
357 SystemQueue& sysq = SystemQueue::instance();
358
359 // start
360 sysq.run();
361
362 sysq.scheduleCall( boost::bind(&SystemQueueTest::OrderedCall, this, 1) );
363 sysq.scheduleCall( boost::bind(&SystemQueueTest::OrderedCall, this, 2) );
364 sysq.scheduleCall( boost::bind(&SystemQueueTest::NestedOrderedCall, this, 3, 5) );
365
366 // make sure all nested calls are done
367 wait_for_checkmark(MAX_WAIT); // XXX should be "wait_until_empty() ...."
368
369 checkmark = false;
370 sysq.scheduleCall( boost::bind(&SystemQueueTest::OrderedCall, this, 6) );
371
372 // XXX same here... [ wait_until_empty() ]
373 sysq.scheduleCall( boost::bind(&SystemQueueTest::Check, this) );
374 wait_for_checkmark(MAX_WAIT);
375
376 // evaluation
377 EXPECT_EQ( 6, last_ordered_call);
378
379 // stop
380 sysq.cancel();
381}
382
383
384
385/**
386 * This subclass has some special member functions suitable for timing tests
387 *
388 * NOTE: Timing tests are not always reproducable.. sorry. :-/
389 */
390class SystemQueueTimingTest :
391 public SystemQueueTest
392{
393public:
394
395/**
396 * typical delay time
397 *
398 * should be long enough for meaningful tests,
399 * but should not lengthen the test excessivly
400 */
401#define DELAY_TIME 20 // ms
402#define DELAY_MARGIN 1000 // microseconds (1/1000 ms) // NOTE: maybe a meaningful value depends
403 // on the target hardwarde and system load...
404
405
406 /// constructor
407 SystemQueueTimingTest() :
408 SystemQueueTest() /* super constructor */,
409 sysq( SystemQueue::instance() ),
410 calls(0)
411 {
412 }
413
414 virtual void SetUp()
415 {
416 // start SystemQueue
417 sysq.run();
418 }
419
420
421 virtual void TearDown()
422 {
423 // stop SystemQueue
424 sysq.cancel();
425 }
426
427
428 /**
429 * @param placed_in_queue The time (as ptime) when this event is put into the delay queue
430 * @param intended_sleep_time The time (in microseconds) the event is supposed to sleep in the queue
431 * @param margin The acceptable margin (in microseconds)
432 */
433 void TimeCheckingCall(ptime placed_in_queue, uint64_t intended_sleep_time, uint64_t margin)
434 {
435 ptime called_at = microsec_clock::local_time();
436
437 // calculate actual sleep time and difference to intended sleep time
438 boost::posix_time::time_duration actual_sleep_time = called_at - placed_in_queue;
439 uint64_t diff = actual_sleep_time.total_microseconds() - intended_sleep_time;
440
441 // info
442 cout << "### Timing difference: " << diff << " microseconds" << endl;
443
444 EXPECT_LT( abs(diff), margin );
445
446 calls++;
447 checkmark = true;
448 }
449
450 SystemQueue& sysq;
451 int calls;
452};
453
454
455/**
456 * schedules a delayed call and tests whether it is called (more or less timely..)
457 */
458TEST_F(SystemQueueTimingTest, DelayedCall)
459{
460 // scheduleCall
461 sysq.scheduleCall(
462 boost::bind(&SystemQueueTimingTest::Check, this), DELAY_TIME
463 );
464
465 // noting to do until the delay is up..
466 usleep(DELAY_TIME*1000 + DELAY_MARGIN*10);
467
468 // wait for the event to happen
469 wait_for_checkmark(MAX_WAIT);
470
471 EXPECT_TRUE( checkmark ) << "Delayed function was not called within delaytime ("
472 << DELAY_TIME << " ms) + " << (MAX_WAIT + DELAY_MARGIN) << " microseconds.";
473}
474
475
476/**
477 * tests whether the SystemQueue is considered non-empty, while an event is delayed-waiting
478 */
479TEST_F(SystemQueueTimingTest, NotEmptyWhileWaiting)
480{
481 // scheduleCall
482 sysq.scheduleCall(
483 boost::bind(&SystemQueueTimingTest::Check, this), DELAY_TIME
484 );
485
486 // SystemQueue must not be empty as long as the event is not finished (and especially while stille queued)
487 if ( sysq.isEmpty() )
488 {
489 // assert that this test is actually meaningful
490 ASSERT_FALSE( checkmark )
491 << "NOTE: This is not necessarily a bug, maybe the timing just have to adjusted. Try to increase MAX_WAIT.";
492
493 EXPECT_TRUE( ! sysq.isEmpty() || checkmark );
494 }
495}
496
497
498
499/**
500 * schedules a delayed call and tests whether it is called (more or less timely..)
501 */
502TEST_F(SystemQueueTimingTest, MultipleCalls)
503{
504 // schedule 4th call
505 sysq.scheduleCall(
506 boost::bind(&SystemQueueTest::OrderedCall, this, 4),
507 DELAY_TIME*3
508 );
509
510 // schedule 2nd call
511 sysq.scheduleCall(
512 boost::bind(&SystemQueueTest::OrderedCall, this, 2),
513 DELAY_TIME*1
514 );
515
516 // schedule 3rd call
517 sysq.scheduleCall(
518 boost::bind(&SystemQueueTest::OrderedCall, this, 3),
519 DELAY_TIME*2
520 );
521
522 // schedule 1st call (without delay)
523 sysq.scheduleCall(
524 boost::bind(&SystemQueueTest::OrderedCall, this, 1)
525 );
526
527
528 // XXX the usual bug..
529 sysq.scheduleCall(
530 boost::bind(&SystemQueueTimingTest::Check, this),
531 DELAY_TIME*4
532 );
533
534 // noting to do until the delay is up..
535 usleep(DELAY_TIME * 4000 + DELAY_MARGIN*10);
536
537 // wait for the event to happen
538 wait_for_checkmark(MAX_WAIT);
539
540 // evaluation
541 EXPECT_EQ( 4, last_ordered_call);
542}
543
544
545/**
546 * Schedules a delayed call and test whether the sleep time is acurate
547 */
548TEST_F(SystemQueueTimingTest, TimingSingleCall)
549{
550 ptime now = microsec_clock::local_time();
551
552 sysq.scheduleCall(
553 boost::bind(&SystemQueueTimingTest::TimeCheckingCall, this,
554 now, DELAY_TIME*1000, DELAY_MARGIN),
555 DELAY_TIME
556 );
557
558 // main thread is going to sleep..
559 usleep(DELAY_TIME * 1000 + DELAY_MARGIN * 2);
560 wait_for_checkmark(MAX_WAIT);
561
562 // make sure the measurement function was called at all
563 EXPECT_TRUE(checkmark) << "Measurement function was NOT RUN AT ALL!";
564}
565
566
567/**
568 * Like TimingSingleCall but tests whether the timings change when multiple events are scheduled.
569 */
570TEST_F(SystemQueueTimingTest, TimingMultipleCalls)
571{
572 ptime now = microsec_clock::local_time();
573
574 sysq.scheduleCall(
575 boost::bind(&SystemQueueTimingTest::TimeCheckingCall, this,
576 now, DELAY_TIME*1000 * 2, DELAY_MARGIN),
577 DELAY_TIME * 2
578 );
579
580 sysq.scheduleCall(
581 boost::bind(&SystemQueueTimingTest::TimeCheckingCall, this,
582 now, DELAY_TIME*1000 * 3, DELAY_MARGIN),
583 DELAY_TIME * 3
584 );
585
586 sysq.scheduleCall(
587 boost::bind(&SystemQueueTimingTest::TimeCheckingCall, this,
588 now, DELAY_TIME*1000, DELAY_MARGIN),
589 DELAY_TIME
590 );
591
592 // main thread is going to sleep..
593 usleep(DELAY_TIME * 3000 + DELAY_MARGIN * 2);
594 wait_for_checkmark(MAX_WAIT); // XXX wait_until_empty
595
596 // make sure the measurement function was called at all
597 EXPECT_EQ(3, calls) << "Not every event was performed..";
598}
599
600
601/*
602 * TODO
603 *
604 * maybe one more complicated testcase with timing and directly scheduled events
605 *
606 * but this probably only makes sense after a working SysQ implementation exists..
607 */
Note: See TracBrowser for help on using the repository browser.