source: tests/SystemQueue-tests.cc@ 12764

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

New SystemQueue...

... is passing all unit tests, now. :-)

File size: 13.9 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 LongRunner()
107 {
108 usleep( MAX_WAIT / 2 );
109
110 checkmark = true;
111 }
112
113
114 /// wait for the checkmark to be set by a SystemQueue call, (but not too long!)
115 void wait_for_checkmark(int max_wait = MAX_WAIT)
116 {
117 for ( int i = 0; i < max_wait / SLEEP_TIME; i++)
118 {
119 if ( checkmark )
120 break;
121
122 cout << "### sleeping for " << SLEEP_TIME << " microseconds ..." << endl;
123 usleep(SLEEP_TIME);
124 }
125 }
126
127 /// call that checks wheather it was performed in order
128 void OrderedCall(int num)
129 {
130 // XXX
131 cout << "### OrderedCall num: " << num << endl;
132
133 // check ordering
134 EXPECT_EQ( num, last_ordered_call + 1);
135
136 last_ordered_call = num;
137 }
138
139 /// like OrderedCall, but calls itself to test nested calls
140 void NestedOrderedCall(int from, int to)
141 {
142 // check ordering
143 OrderedCall(from);
144
145 // nested call
146 if ( from < to )
147 {
148 SystemQueue::instance().scheduleCall(
149 boost::bind(&SystemQueueTest::NestedOrderedCall,
150 this,
151 from+1,
152 to)
153 );
154 }
155 else
156 {
157 /// XXX because the current/old SystemQueue does not pass the Threading test,
158 /// we have to signal, that when all nested calls are finished,
159 /// so we need to set the checkmark here..
160
161 checkmark = true;
162 }
163 }
164
165 bool checkmark;
166 int last_ordered_call;
167};
168
169
170/**
171 * Enqueues an event but then cancels the SystemQueue without running
172 */
173TEST_F(SystemQueueTest, EmptyAfterCancel)
174{
175 SystemQueue& sysq = SystemQueue::instance();
176
177 EXPECT_TRUE( sysq.isEmpty() );
178
179 // enqueue event
180 sysq.scheduleCall(
181 boost::bind(&SystemQueueTest::Check, this)
182 );
183
184 EXPECT_FALSE( sysq.isEmpty() );
185
186 // cancel
187 sysq.cancel();
188
189 EXPECT_TRUE( sysq.isEmpty() );
190}
191
192
193/**
194 * cancels the SystemQueue from inside a scheduled event
195 *
196 * --> and expects that this throws an exception
197 *
198 * NOTE: due to the SystemQueue singleton design, explicit cleanup is necessary!
199 */
200TEST_F(SystemQueueTest, CancelFromInsideEvent)
201{
202 SystemQueue& sysq = SystemQueue::instance();
203 checkmark = false; // just to be sure..
204
205 // start
206 sysq.run();
207
208 // scheduleCall
209 sysq.scheduleCall(
210 boost::bind(&SystemQueueTest::Cancel, this)
211 );
212
213 // wait for the event to happen
214 wait_for_checkmark(MAX_WAIT);
215
216 // CLEANUP
217 sysq.cancel();
218
219 EXPECT_FALSE( sysq.isRunning() );
220 EXPECT_TRUE( sysq.isEmpty() );
221}
222
223
224/**
225 * schedule a call and test whether it is actually performed by the SystemQueue
226 */
227TEST_F(SystemQueueTest, ScheduleCall)
228{
229 SystemQueue& sysq = SystemQueue::instance();
230 checkmark = false; // just to be sure..
231
232 // start
233 sysq.run();
234
235 // scheduleCall
236 sysq.scheduleCall(
237 boost::bind(&SystemQueueTest::Check, this)
238 );
239
240 // wait for the event to happen
241 wait_for_checkmark(MAX_WAIT);
242
243 // stop
244 sysq.cancel();
245
246 EXPECT_TRUE( checkmark ) << "Function was not called within " << MAX_WAIT << " microseconds.";
247}
248
249
250/**
251 * This test actually tests two things [sorry, but it's hard to test them separately!]
252 *
253 * - first: the SystemQueue should not consider itself empty, while an event is processed
254 * - second: SystemQueue events should be processed in parallel to the main thread
255 *
256 * NOTE: The timings here are not obvious, maybe they have to be adjusted on very slow machines
257 *
258 * NOTE: !! The current/old SystemQueue does NOT pass this test!!
259 *
260 * That's also why we need the unhandy wait_for_checkmark function,
261 * instead a wait_until_empty function.
262 */
263TEST_F(SystemQueueTest, Threading)
264{
265 SystemQueue& sysq = SystemQueue::instance();
266 checkmark = false; // just to be sure..
267
268 // start
269 sysq.run();
270
271 // scheduleCall
272 sysq.scheduleCall(
273 boost::bind(&SystemQueueTest::LongRunner, this)
274 );
275
276 // SystemQueue must not be empty as long as the event is not finished
277 if ( sysq.isEmpty() )
278 {
279 // assert that this test is actually meaningful
280 ASSERT_FALSE( checkmark )
281 << "NOTE: This is not necessarily a bug, maybe the timing just have to adjusted. Try to increase MAX_WAIT.";
282
283 EXPECT_TRUE( ! sysq.isEmpty() || checkmark );
284 }
285
286 // wait for the event to finish
287 wait_for_checkmark(MAX_WAIT);
288
289 // stop
290 sysq.cancel();
291
292 // even the long-runner should finally finish
293 EXPECT_TRUE( checkmark ) << "Function has not finished within " << MAX_WAIT << " microseconds.";
294}
295
296/**
297 * schedule multiple calls
298 *
299 * each call must be performed, in the correct order
300 *
301 * NOTE: The nested calls are not necessarily in order with calls scheduled from the main thread,
302 * that's fine, therefore we make sure the nested calls are done, before scheduling new ones.
303 */
304TEST_F(SystemQueueTest, MultipleCalls)
305{
306 SystemQueue& sysq = SystemQueue::instance();
307
308 // start
309 sysq.run();
310
311 sysq.scheduleCall( boost::bind(&SystemQueueTest::OrderedCall, this, 1) );
312 sysq.scheduleCall( boost::bind(&SystemQueueTest::OrderedCall, this, 2) );
313 sysq.scheduleCall( boost::bind(&SystemQueueTest::NestedOrderedCall, this, 3, 5) );
314
315 // make sure all nested calls are done
316 wait_for_checkmark(MAX_WAIT); // XXX should be "wait_until_empty() ...."
317
318 checkmark = false;
319 sysq.scheduleCall( boost::bind(&SystemQueueTest::OrderedCall, this, 6) );
320
321 // XXX same here... [ wait_until_empty() ]
322 sysq.scheduleCall( boost::bind(&SystemQueueTest::Check, this) );
323 wait_for_checkmark(MAX_WAIT);
324
325 // evaluation
326 EXPECT_EQ( 6, last_ordered_call);
327
328 // stop
329 sysq.cancel();
330}
331
332
333
334/**
335 * This subclass has some special member functions suitable for timing tests
336 *
337 * NOTE: Timing tests are not always reproducable.. sorry. :-/
338 */
339class SystemQueueTimingTest :
340 public SystemQueueTest
341{
342public:
343
344/**
345 * typical delay time
346 *
347 * should be long enough for meaningful tests,
348 * but should not lengthen the test excessivly
349 */
350#define DELAY_TIME 20 // ms
351#define DELAY_MARGIN 1000 // microseconds (1/1000 ms) // NOTE: maybe a meaningful value depends
352 // on the target hardwarde and system load...
353
354
355 /// constructor
356 SystemQueueTimingTest() :
357 SystemQueueTest() /* super constructor */,
358 sysq( SystemQueue::instance() ),
359 calls(0)
360 {
361 }
362
363 virtual void SetUp()
364 {
365 // start SystemQueue
366 sysq.run();
367 }
368
369
370 virtual void TearDown()
371 {
372 // stop SystemQueue
373 sysq.cancel();
374 }
375
376
377 /**
378 * @param placed_in_queue The time (as ptime) when this event is put into the delay queue
379 * @param intended_sleep_time The time (in microseconds) the event is supposed to sleep in the queue
380 * @param margin The acceptable margin (in microseconds)
381 */
382 void TimeCheckingCall(ptime placed_in_queue, uint64_t intended_sleep_time, uint64_t margin)
383 {
384 ptime called_at = microsec_clock::local_time();
385
386 // calculate actual sleep time and difference to intended sleep time
387 boost::posix_time::time_duration actual_sleep_time = called_at - placed_in_queue;
388 uint64_t diff = actual_sleep_time.total_microseconds() - intended_sleep_time;
389
390 // info
391 cout << "### Timing difference: " << diff << " microseconds" << endl;
392
393 EXPECT_LT( abs(diff), margin );
394
395 calls++;
396 checkmark = true;
397 }
398
399 SystemQueue& sysq;
400 int calls;
401};
402
403
404/**
405 * schedules a delayed call and tests whether it is called (more or less timely..)
406 */
407TEST_F(SystemQueueTimingTest, DelayedCall)
408{
409 // scheduleCall
410 sysq.scheduleCall(
411 boost::bind(&SystemQueueTimingTest::Check, this), DELAY_TIME
412 );
413
414 // noting to do until the delay is up..
415 usleep(DELAY_TIME*1000 + DELAY_MARGIN*10);
416
417 // wait for the event to happen
418 wait_for_checkmark(MAX_WAIT);
419
420 EXPECT_TRUE( checkmark ) << "Delayed function was not called within delaytime ("
421 << DELAY_TIME << " ms) + " << (MAX_WAIT + DELAY_MARGIN) << " microseconds.";
422}
423
424
425/**
426 * tests whether the SystemQueue is considered non-empty, while an event is delayed-waiting
427 */
428TEST_F(SystemQueueTimingTest, NotEmptyWhileWaiting)
429{
430 // scheduleCall
431 sysq.scheduleCall(
432 boost::bind(&SystemQueueTimingTest::Check, this), DELAY_TIME
433 );
434
435 // SystemQueue must not be empty as long as the event is not finished (and especially while stille queued)
436 if ( sysq.isEmpty() )
437 {
438 // assert that this test is actually meaningful
439 ASSERT_FALSE( checkmark )
440 << "NOTE: This is not necessarily a bug, maybe the timing just have to adjusted. Try to increase MAX_WAIT.";
441
442 EXPECT_TRUE( ! sysq.isEmpty() || checkmark );
443 }
444}
445
446
447
448/**
449 * schedules a delayed call and tests whether it is called (more or less timely..)
450 */
451TEST_F(SystemQueueTimingTest, MultipleCalls)
452{
453 // schedule 4th call
454 sysq.scheduleCall(
455 boost::bind(&SystemQueueTest::OrderedCall, this, 4),
456 DELAY_TIME*3
457 );
458
459 // schedule 2nd call
460 sysq.scheduleCall(
461 boost::bind(&SystemQueueTest::OrderedCall, this, 2),
462 DELAY_TIME*1
463 );
464
465 // schedule 3rd call
466 sysq.scheduleCall(
467 boost::bind(&SystemQueueTest::OrderedCall, this, 3),
468 DELAY_TIME*2
469 );
470
471 // schedule 1st call (without delay)
472 sysq.scheduleCall(
473 boost::bind(&SystemQueueTest::OrderedCall, this, 1)
474 );
475
476
477 // XXX the usual bug..
478 sysq.scheduleCall(
479 boost::bind(&SystemQueueTimingTest::Check, this),
480 DELAY_TIME*4
481 );
482
483 // noting to do until the delay is up..
484 usleep(DELAY_TIME * 4000 + DELAY_MARGIN*10);
485
486 // wait for the event to happen
487 wait_for_checkmark(MAX_WAIT);
488
489 // evaluation
490 EXPECT_EQ( 4, last_ordered_call);
491}
492
493
494/**
495 * Schedules a delayed call and test whether the sleep time is acurate
496 */
497TEST_F(SystemQueueTimingTest, TimingSingleCall)
498{
499 ptime now = microsec_clock::local_time();
500
501 sysq.scheduleCall(
502 boost::bind(&SystemQueueTimingTest::TimeCheckingCall, this,
503 now, DELAY_TIME*1000, DELAY_MARGIN),
504 DELAY_TIME
505 );
506
507 // main thread is going to sleep..
508 usleep(DELAY_TIME * 1000 + DELAY_MARGIN * 2);
509 wait_for_checkmark(MAX_WAIT);
510
511 // make sure the measurement function was called at all
512 EXPECT_TRUE(checkmark) << "Measurement function was NOT RUN AT ALL!";
513}
514
515
516/**
517 * Like TimingSingleCall but tests whether the timings change when multiple events are scheduled.
518 */
519TEST_F(SystemQueueTimingTest, TimingMultipleCalls)
520{
521 ptime now = microsec_clock::local_time();
522
523 sysq.scheduleCall(
524 boost::bind(&SystemQueueTimingTest::TimeCheckingCall, this,
525 now, DELAY_TIME*1000 * 2, DELAY_MARGIN),
526 DELAY_TIME * 2
527 );
528
529 sysq.scheduleCall(
530 boost::bind(&SystemQueueTimingTest::TimeCheckingCall, this,
531 now, DELAY_TIME*1000 * 3, DELAY_MARGIN),
532 DELAY_TIME * 3
533 );
534
535 sysq.scheduleCall(
536 boost::bind(&SystemQueueTimingTest::TimeCheckingCall, this,
537 now, DELAY_TIME*1000, DELAY_MARGIN),
538 DELAY_TIME
539 );
540
541 // main thread is going to sleep..
542 usleep(DELAY_TIME * 3000 + DELAY_MARGIN * 2);
543 wait_for_checkmark(MAX_WAIT); // XXX wait_until_empty
544
545 // make sure the measurement function was called at all
546 EXPECT_EQ(3, calls) << "Not every event was performed..";
547}
548
549
550/*
551 * TODO
552 *
553 * maybe one more complicated testcase with timing and directly scheduled events
554 *
555 * but this probably only makes sense after a working SysQ implementation exists..
556 */
Note: See TracBrowser for help on using the repository browser.