Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / qa / cppunit / timer.cxx
blob90705d26ec193b646c234081196a564ddd4394d7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9 /*
10 * Timers are evil beasts across platforms...
13 #include <test/bootstrapfixture.hxx>
15 #include <osl/thread.hxx>
16 #include <chrono>
18 #include <vcl/timer.hxx>
19 #include <vcl/idle.hxx>
20 #include <vcl/svapp.hxx>
21 #include <vcl/scheduler.hxx>
22 #include <svdata.hxx>
23 #include <salinst.hxx>
25 // #define TEST_WATCHDOG
27 // Enables timer tests that appear to provoke windows under load unduly.
28 //#define TEST_TIMERPRECISION
30 /// Avoid our timer tests just wedging the build if they fail.
31 class WatchDog : public osl::Thread
33 sal_Int32 const mnSeconds;
34 public:
35 explicit WatchDog(sal_Int32 nSeconds) :
36 Thread(),
37 mnSeconds( nSeconds )
39 create();
41 virtual void SAL_CALL run() override
43 osl::Thread::wait( std::chrono::seconds(mnSeconds) );
44 fprintf(stderr, "ERROR: WatchDog timer thread expired, failing the test!\n");
45 fflush(stderr);
46 CPPUNIT_ASSERT_MESSAGE("watchdog triggered", false);
50 static WatchDog * aWatchDog = new WatchDog( 120 ); // random high number in secs
52 class TimerTest : public test::BootstrapFixture
54 public:
55 TimerTest() : BootstrapFixture(true, false) {}
57 void testIdle();
58 void testIdleMainloop();
59 #ifdef TEST_WATCHDOG
60 void testWatchdog();
61 #endif
62 void testDurations();
63 #ifdef TEST_TIMERPRECISION
64 void testAutoTimer();
65 void testMultiAutoTimers();
66 #endif
67 void testAutoTimerStop();
68 void testNestedTimer();
69 void testSlowTimerCallback();
70 void testTriggerIdleFromIdle();
71 void testInvokedReStart();
72 void testPriority();
73 void testRoundRobin();
75 CPPUNIT_TEST_SUITE(TimerTest);
76 CPPUNIT_TEST(testIdle);
77 CPPUNIT_TEST(testIdleMainloop);
78 #ifdef TEST_WATCHDOG
79 CPPUNIT_TEST(testWatchdog);
80 #endif
81 CPPUNIT_TEST(testDurations);
82 #ifdef TEST_TIMERPRECISION
83 CPPUNIT_TEST(testAutoTimer);
84 CPPUNIT_TEST(testMultiAutoTimers);
85 #endif
86 CPPUNIT_TEST(testAutoTimerStop);
87 CPPUNIT_TEST(testNestedTimer);
88 CPPUNIT_TEST(testSlowTimerCallback);
89 CPPUNIT_TEST(testTriggerIdleFromIdle);
90 CPPUNIT_TEST(testInvokedReStart);
91 CPPUNIT_TEST(testPriority);
92 CPPUNIT_TEST(testRoundRobin);
94 CPPUNIT_TEST_SUITE_END();
97 #ifdef TEST_WATCHDOG
98 void TimerTest::testWatchdog()
100 // out-wait the watchdog.
101 osl::Thread::wait( std::chrono::seconds(12) );
103 #endif
106 class IdleBool : public Idle
108 bool &mrBool;
109 public:
110 explicit IdleBool( bool &rBool ) :
111 Idle( "IdleBool" ), mrBool( rBool )
113 SetPriority( TaskPriority::LOWEST );
114 Start();
115 mrBool = false;
117 virtual void Invoke() override
119 mrBool = true;
120 Application::EndYield();
124 void TimerTest::testIdle()
126 bool bTriggered = false;
127 IdleBool aTest( bTriggered );
128 Scheduler::ProcessEventsToIdle();
129 CPPUNIT_ASSERT_MESSAGE("idle triggered", bTriggered);
132 void TimerTest::testIdleMainloop()
134 bool bTriggered = false;
135 IdleBool aTest( bTriggered );
136 // coverity[loop_top] - Application::Yield allows the timer to fire and toggle bDone
137 while (!bTriggered)
139 ImplSVData* pSVData = ImplGetSVData();
141 // can't test this via Application::Yield since this
142 // also processes all tasks directly via the scheduler.
143 pSVData->maAppData.mnDispatchLevel++;
144 pSVData->mpDefInst->DoYield(true, false);
145 pSVData->maAppData.mnDispatchLevel--;
147 CPPUNIT_ASSERT_MESSAGE("mainloop idle triggered", bTriggered);
150 class TimerBool : public Timer
152 bool &mrBool;
153 public:
154 TimerBool( sal_uLong nMS, bool &rBool ) :
155 Timer( "TimerBool" ), mrBool( rBool )
157 SetTimeout( nMS );
158 Start();
159 mrBool = false;
161 virtual void Invoke() override
163 mrBool = true;
164 Application::EndYield();
168 void TimerTest::testDurations()
170 static const sal_uLong aDurations[] = { 0, 1, 500, 1000 };
171 for (size_t i = 0; i < SAL_N_ELEMENTS( aDurations ); i++)
173 bool bDone = false;
174 TimerBool aTimer( aDurations[i], bDone );
175 // coverity[loop_top] - Application::Yield allows the timer to fire and toggle bDone
176 while( !bDone )
178 Application::Yield();
184 class AutoTimerCount : public AutoTimer
186 sal_Int32 &mrCount;
187 const sal_Int32 mnMaxCount;
189 public:
190 AutoTimerCount( sal_uLong nMS, sal_Int32 &rCount,
191 const sal_Int32 nMaxCount = -1 )
192 : AutoTimer( "AutoTimerCount" )
193 , mrCount( rCount )
194 , mnMaxCount( nMaxCount )
196 SetTimeout( nMS );
197 Start();
198 mrCount = 0;
201 virtual void Invoke() override
203 ++mrCount;
204 CPPUNIT_ASSERT( mnMaxCount < 0 || mrCount <= mnMaxCount );
205 if ( mrCount == mnMaxCount )
206 Stop();
210 #ifdef TEST_TIMERPRECISION
212 void TimerTest::testAutoTimer()
214 const sal_Int32 nDurationMs = 30;
215 const sal_Int32 nEventsCount = 5;
216 const double exp = (nDurationMs * nEventsCount);
218 sal_Int32 nCount = 0;
219 std::ostringstream msg;
221 // Repeat when we have random latencies.
222 // This is expected on non-realtime OSes.
223 for (int i = 0; i < 10; ++i)
225 const auto start = std::chrono::high_resolution_clock::now();
226 nCount = 0;
227 AutoTimerCount aCount(nDurationMs, nCount);
228 while (nCount < nEventsCount) {
229 Application::Yield();
232 const auto end = std::chrono::high_resolution_clock::now();
233 double dur = std::chrono::duration<double, std::milli>(end - start).count();
235 msg << std::setprecision(2) << std::fixed
236 << "periodic multi-timer - dur: "
237 << dur << " (" << exp << ") ms." << std::endl;
239 // +/- 20% should be reasonable enough a margin.
240 if (dur >= (exp * 0.8) && dur <= (exp * 1.2))
242 // Success.
243 return;
247 CPPUNIT_FAIL(msg.str().c_str());
250 void TimerTest::testMultiAutoTimers()
252 // The behavior of the timers change drastically
253 // when multiple timers are present.
254 // The worst, in my tests, is when two
255 // timers with 1ms period exist with a
256 // third of much longer period.
258 const sal_Int32 nDurationMsX = 5;
259 const sal_Int32 nDurationMsY = 10;
260 const sal_Int32 nDurationMs = 40;
261 const sal_Int32 nEventsCount = 5;
262 const double exp = (nDurationMs * nEventsCount);
263 const double expX = (exp / nDurationMsX);
264 const double expY = (exp / nDurationMsY);
266 sal_Int32 nCountX = 0;
267 sal_Int32 nCountY = 0;
268 sal_Int32 nCount = 0;
269 std::ostringstream msg;
271 // Repeat when we have random latencies.
272 // This is expected on non-realtime OSes.
273 for (int i = 0; i < 10; ++i)
275 nCountX = 0;
276 nCountY = 0;
277 nCount = 0;
279 const auto start = std::chrono::high_resolution_clock::now();
280 AutoTimerCount aCountX(nDurationMsX, nCountX);
281 AutoTimerCount aCountY(nDurationMsY, nCountY);
283 AutoTimerCount aCount(nDurationMs, nCount);
284 // coverity[loop_top] - Application::Yield allows the timer to fire and toggle nCount
285 while (nCount < nEventsCount) {
286 Application::Yield();
289 const auto end = std::chrono::high_resolution_clock::now();
290 double dur = std::chrono::duration<double, std::milli>(end - start).count();
292 msg << std::setprecision(2) << std::fixed << "periodic multi-timer - dur: "
293 << dur << " (" << exp << ") ms, nCount: " << nCount
294 << " (" << nEventsCount << "), nCountX: " << nCountX
295 << " (" << expX << "), nCountY: " << nCountY
296 << " (" << expY << ")." << std::endl;
298 // +/- 20% should be reasonable enough a margin.
299 if (dur >= (exp * 0.8) && dur <= (exp * 1.2) &&
300 nCountX >= (expX * 0.8) && nCountX <= (expX * 1.2) &&
301 nCountY >= (expY * 0.8) && nCountY <= (expY * 1.2))
303 // Success.
304 return;
308 CPPUNIT_FAIL(msg.str().c_str());
310 #endif // TEST_TIMERPRECISION
312 void TimerTest::testAutoTimerStop()
314 sal_Int32 nTimerCount = 0;
315 const sal_Int32 nMaxCount = 5;
316 AutoTimerCount aAutoTimer( 0, nTimerCount, nMaxCount );
317 // coverity[loop_top] - Application::Yield allows the timer to fire and increment TimerCount
318 while (nMaxCount != nTimerCount)
319 Application::Yield();
320 CPPUNIT_ASSERT( !aAutoTimer.IsActive() );
321 CPPUNIT_ASSERT( !Application::Reschedule() );
325 class YieldTimer : public Timer
327 public:
328 explicit YieldTimer( sal_uLong nMS ) : Timer( "YieldTimer" )
330 SetTimeout( nMS );
331 Start();
333 virtual void Invoke() override
335 for (int i = 0; i < 100; i++)
336 Application::Yield();
340 void TimerTest::testNestedTimer()
342 sal_Int32 nCount = 0;
343 YieldTimer aCount(5);
344 AutoTimerCount aCountUp( 3, nCount );
345 // coverity[loop_top] - Application::Yield allows the timer to fire and increment nCount
346 while (nCount < 20)
347 Application::Yield();
351 class SlowCallbackTimer : public Timer
353 bool &mbSlow;
354 public:
355 SlowCallbackTimer( sal_uLong nMS, bool &bBeenSlow ) :
356 Timer( "SlowCallbackTimer" ), mbSlow( bBeenSlow )
358 SetTimeout( nMS );
359 Start();
360 mbSlow = false;
362 virtual void Invoke() override
364 osl::Thread::wait( std::chrono::seconds(1) );
365 mbSlow = true;
369 void TimerTest::testSlowTimerCallback()
371 bool bBeenSlow = false;
372 sal_Int32 nCount = 0;
373 AutoTimerCount aHighFreq(1, nCount);
374 SlowCallbackTimer aSlow(250, bBeenSlow);
375 // coverity[loop_top] - Application::Yield allows the timer to fire and toggle bBeenSlow
376 while (!bBeenSlow)
377 Application::Yield();
378 // coverity[loop_top] - Application::Yield allows the timer to fire and increment nCount
379 while (nCount < 200)
380 Application::Yield();
384 class TriggerIdleFromIdle : public Idle
386 bool* mpTriggered;
387 TriggerIdleFromIdle* mpOther;
388 public:
389 explicit TriggerIdleFromIdle( bool* pTriggered, TriggerIdleFromIdle* pOther ) :
390 Idle( "TriggerIdleFromIdle" ), mpTriggered(pTriggered), mpOther(pOther)
393 virtual void Invoke() override
395 Start();
396 if (mpOther)
397 mpOther->Start();
398 Application::Yield();
399 if (mpTriggered)
400 *mpTriggered = true;
404 void TimerTest::testTriggerIdleFromIdle()
406 bool bTriggered1 = false;
407 bool bTriggered2 = false;
408 TriggerIdleFromIdle aTest2( &bTriggered2, nullptr );
409 TriggerIdleFromIdle aTest1( &bTriggered1, &aTest2 );
410 aTest1.Start();
411 Application::Yield();
412 CPPUNIT_ASSERT_MESSAGE("idle not triggered", bTriggered1);
413 CPPUNIT_ASSERT_MESSAGE("idle not triggered", bTriggered2);
417 class IdleInvokedReStart : public Idle
419 sal_Int32 &mrCount;
420 public:
421 IdleInvokedReStart( sal_Int32 &rCount )
422 : Idle( "IdleInvokedReStart" ), mrCount( rCount )
424 Start();
426 virtual void Invoke() override
428 mrCount++;
429 if ( mrCount < 2 )
430 Start();
434 void TimerTest::testInvokedReStart()
436 sal_Int32 nCount = 0;
437 IdleInvokedReStart aIdle( nCount );
438 Scheduler::ProcessEventsToIdle();
439 CPPUNIT_ASSERT_EQUAL( sal_Int32(2), nCount );
443 class IdleSerializer : public Idle
445 sal_uInt32 const mnPosition;
446 sal_uInt32 &mrProcesed;
447 public:
448 IdleSerializer(const sal_Char *pDebugName, TaskPriority ePrio,
449 sal_uInt32 nPosition, sal_uInt32 &rProcesed)
450 : Idle( pDebugName )
451 , mnPosition( nPosition )
452 , mrProcesed( rProcesed )
454 SetPriority(ePrio);
455 Start();
457 virtual void Invoke() override
459 ++mrProcesed;
460 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Ignored prio", mnPosition, mrProcesed );
464 void TimerTest::testPriority()
466 // scope, so tasks are deleted
468 // Start: 1st Idle low, 2nd high
469 sal_uInt32 nProcessed = 0;
470 IdleSerializer aLowPrioIdle("IdleSerializer LowPrio",
471 TaskPriority::LOWEST, 2, nProcessed);
472 IdleSerializer aHighPrioIdle("IdleSerializer HighPrio",
473 TaskPriority::HIGHEST, 1, nProcessed);
474 Scheduler::ProcessEventsToIdle();
475 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Not all idles processed", sal_uInt32(2), nProcessed );
479 // Start: 1st Idle high, 2nd low
480 sal_uInt32 nProcessed = 0;
481 IdleSerializer aHighPrioIdle("IdleSerializer HighPrio",
482 TaskPriority::HIGHEST, 1, nProcessed);
483 IdleSerializer aLowPrioIdle("IdleSerializer LowPrio",
484 TaskPriority::LOWEST, 2, nProcessed);
485 Scheduler::ProcessEventsToIdle();
486 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Not all idles processed", sal_uInt32(2), nProcessed );
491 class TestAutoIdleRR : public AutoIdle
493 sal_uInt32 &mrCount;
495 DECL_LINK( IdleRRHdl, Timer *, void );
497 public:
498 TestAutoIdleRR( sal_uInt32 &rCount,
499 const sal_Char *pDebugName )
500 : AutoIdle( pDebugName )
501 , mrCount( rCount )
503 CPPUNIT_ASSERT_EQUAL( sal_uInt32(0), mrCount );
504 SetInvokeHandler( LINK( this, TestAutoIdleRR, IdleRRHdl ) );
505 Start();
509 IMPL_LINK_NOARG(TestAutoIdleRR, IdleRRHdl, Timer *, void)
511 ++mrCount;
512 if ( mrCount == 3 )
513 Stop();
516 void TimerTest::testRoundRobin()
518 sal_uInt32 nCount1 = 0, nCount2 = 0;
519 TestAutoIdleRR aIdle1( nCount1, "TestAutoIdleRR aIdle1" ),
520 aIdle2( nCount2, "TestAutoIdleRR aIdle2" );
521 while ( Application::Reschedule() )
523 CPPUNIT_ASSERT( nCount1 == nCount2 || nCount1 - 1 == nCount2 );
524 CPPUNIT_ASSERT( nCount1 <= 3 );
525 CPPUNIT_ASSERT( nCount2 <= 3 );
527 CPPUNIT_ASSERT( 3 == nCount1 && 3 == nCount2 );
530 CPPUNIT_TEST_SUITE_REGISTRATION(TimerTest);
532 CPPUNIT_PLUGIN_IMPLEMENT();
534 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */