1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 * Timers are evil beasts across platforms...
13 #include <test/bootstrapfixture.hxx>
15 #include <osl/thread.hxx>
18 #include <vcl/timer.hxx>
19 #include <vcl/idle.hxx>
20 #include <vcl/svapp.hxx>
21 #include <vcl/scheduler.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
32 /// Avoid our timer tests just wedging the build if they fail.
33 class WatchDog
: public osl::Thread
37 explicit WatchDog(sal_Int32 nSeconds
) :
43 virtual void SAL_CALL
run() override
45 osl::Thread::wait( std::chrono::seconds(mnSeconds
) );
46 fprintf(stderr
, "ERROR: WatchDog timer thread expired, failing the test!\n");
48 CPPUNIT_ASSERT_MESSAGE("watchdog triggered", false);
54 static WatchDog
* aWatchDog
= new WatchDog( 120 ); // random high number in secs
56 class TimerTest
: public test::BootstrapFixture
59 TimerTest() : BootstrapFixture(true, false) {}
62 void testIdleMainloop();
67 #ifdef TEST_TIMERPRECISION
69 void testMultiAutoTimers();
71 void testAutoTimerStop();
72 void testNestedTimer();
73 void testSlowTimerCallback();
74 void testTriggerIdleFromIdle();
75 void testInvokedReStart();
77 void testRoundRobin();
79 CPPUNIT_TEST_SUITE(TimerTest
);
80 CPPUNIT_TEST(testIdle
);
81 CPPUNIT_TEST(testIdleMainloop
);
83 CPPUNIT_TEST(testWatchdog
);
85 CPPUNIT_TEST(testDurations
);
86 #ifdef TEST_TIMERPRECISION
87 CPPUNIT_TEST(testAutoTimer
);
88 CPPUNIT_TEST(testMultiAutoTimers
);
90 CPPUNIT_TEST(testAutoTimerStop
);
91 CPPUNIT_TEST(testNestedTimer
);
92 CPPUNIT_TEST(testSlowTimerCallback
);
93 CPPUNIT_TEST(testTriggerIdleFromIdle
);
94 CPPUNIT_TEST(testInvokedReStart
);
95 CPPUNIT_TEST(testPriority
);
96 CPPUNIT_TEST(testRoundRobin
);
98 CPPUNIT_TEST_SUITE_END();
102 void TimerTest::testWatchdog()
104 // out-wait the watchdog.
105 osl::Thread::wait( std::chrono::seconds(12) );
111 class IdleBool
: public Idle
115 explicit IdleBool( bool &rBool
) :
116 Idle( "IdleBool" ), mrBool( rBool
)
118 SetPriority( TaskPriority::LOWEST
);
122 virtual void Invoke() override
125 Application::EndYield();
131 void TimerTest::testIdle()
133 bool bTriggered
= false;
134 IdleBool
aTest( bTriggered
);
135 Scheduler::ProcessEventsToIdle();
136 CPPUNIT_ASSERT_MESSAGE("idle triggered", bTriggered
);
139 void TimerTest::testIdleMainloop()
141 bool bTriggered
= false;
142 IdleBool
aTest( bTriggered
);
143 // coverity[loop_top] - Application::Yield allows the timer to fire and toggle bDone
146 ImplSVData
* pSVData
= ImplGetSVData();
148 // can't test this via Application::Yield since this
149 // also processes all tasks directly via the scheduler.
150 pSVData
->maAppData
.mnDispatchLevel
++;
151 pSVData
->mpDefInst
->DoYield(true, false);
152 pSVData
->maAppData
.mnDispatchLevel
--;
154 CPPUNIT_ASSERT_MESSAGE("mainloop idle triggered", bTriggered
);
159 class TimerBool
: public Timer
163 TimerBool( sal_uLong nMS
, bool &rBool
) :
164 Timer( "TimerBool" ), mrBool( rBool
)
170 virtual void Invoke() override
173 Application::EndYield();
179 void TimerTest::testDurations()
181 static const sal_uLong aDurations
[] = { 0, 1, 500, 1000 };
182 for (size_t i
= 0; i
< SAL_N_ELEMENTS( aDurations
); i
++)
185 TimerBool
aTimer( aDurations
[i
], bDone
);
186 // coverity[loop_top] - Application::Yield allows the timer to fire and toggle bDone
189 Application::Yield();
196 class AutoTimerCount
: public AutoTimer
199 const sal_Int32 mnMaxCount
;
202 AutoTimerCount( sal_uLong nMS
, sal_Int32
&rCount
,
203 const sal_Int32 nMaxCount
= -1 )
204 : AutoTimer( "AutoTimerCount" )
206 , mnMaxCount( nMaxCount
)
213 virtual void Invoke() override
216 CPPUNIT_ASSERT( mnMaxCount
< 0 || mrCount
<= mnMaxCount
);
217 if ( mrCount
== mnMaxCount
)
224 #ifdef TEST_TIMERPRECISION
226 void TimerTest::testAutoTimer()
228 const sal_Int32 nDurationMs
= 30;
229 const sal_Int32 nEventsCount
= 5;
230 const double exp
= (nDurationMs
* nEventsCount
);
232 sal_Int32 nCount
= 0;
233 std::ostringstream msg
;
235 // Repeat when we have random latencies.
236 // This is expected on non-realtime OSes.
237 for (int i
= 0; i
< 10; ++i
)
239 const auto start
= std::chrono::high_resolution_clock::now();
241 AutoTimerCount
aCount(nDurationMs
, nCount
);
242 while (nCount
< nEventsCount
) {
243 Application::Yield();
246 const auto end
= std::chrono::high_resolution_clock::now();
247 double dur
= std::chrono::duration
<double, std::milli
>(end
- start
).count();
249 msg
<< std::setprecision(2) << std::fixed
250 << "periodic multi-timer - dur: "
251 << dur
<< " (" << exp
<< ") ms." << std::endl
;
253 // +/- 20% should be reasonable enough a margin.
254 if (dur
>= (exp
* 0.8) && dur
<= (exp
* 1.2))
261 CPPUNIT_FAIL(msg
.str().c_str());
264 void TimerTest::testMultiAutoTimers()
266 // The behavior of the timers change drastically
267 // when multiple timers are present.
268 // The worst, in my tests, is when two
269 // timers with 1ms period exist with a
270 // third of much longer period.
272 const sal_Int32 nDurationMsX
= 5;
273 const sal_Int32 nDurationMsY
= 10;
274 const sal_Int32 nDurationMs
= 40;
275 const sal_Int32 nEventsCount
= 5;
276 const double exp
= (nDurationMs
* nEventsCount
);
277 const double expX
= (exp
/ nDurationMsX
);
278 const double expY
= (exp
/ nDurationMsY
);
280 sal_Int32 nCountX
= 0;
281 sal_Int32 nCountY
= 0;
282 sal_Int32 nCount
= 0;
283 std::ostringstream msg
;
285 // Repeat when we have random latencies.
286 // This is expected on non-realtime OSes.
287 for (int i
= 0; i
< 10; ++i
)
293 const auto start
= std::chrono::high_resolution_clock::now();
294 AutoTimerCount
aCountX(nDurationMsX
, nCountX
);
295 AutoTimerCount
aCountY(nDurationMsY
, nCountY
);
297 AutoTimerCount
aCount(nDurationMs
, nCount
);
298 // coverity[loop_top] - Application::Yield allows the timer to fire and toggle nCount
299 while (nCount
< nEventsCount
) {
300 Application::Yield();
303 const auto end
= std::chrono::high_resolution_clock::now();
304 double dur
= std::chrono::duration
<double, std::milli
>(end
- start
).count();
306 msg
<< std::setprecision(2) << std::fixed
<< "periodic multi-timer - dur: "
307 << dur
<< " (" << exp
<< ") ms, nCount: " << nCount
308 << " (" << nEventsCount
<< "), nCountX: " << nCountX
309 << " (" << expX
<< "), nCountY: " << nCountY
310 << " (" << expY
<< ")." << std::endl
;
312 // +/- 20% should be reasonable enough a margin.
313 if (dur
>= (exp
* 0.8) && dur
<= (exp
* 1.2) &&
314 nCountX
>= (expX
* 0.8) && nCountX
<= (expX
* 1.2) &&
315 nCountY
>= (expY
* 0.8) && nCountY
<= (expY
* 1.2))
322 CPPUNIT_FAIL(msg
.str().c_str());
324 #endif // TEST_TIMERPRECISION
326 void TimerTest::testAutoTimerStop()
328 sal_Int32 nTimerCount
= 0;
329 const sal_Int32 nMaxCount
= 5;
330 AutoTimerCount
aAutoTimer( 0, nTimerCount
, nMaxCount
);
331 // coverity[loop_top] - Application::Yield allows the timer to fire and increment TimerCount
332 while (nMaxCount
!= nTimerCount
)
333 Application::Yield();
334 CPPUNIT_ASSERT( !aAutoTimer
.IsActive() );
335 CPPUNIT_ASSERT( !Application::Reschedule() );
340 class YieldTimer
: public Timer
343 explicit YieldTimer( sal_uLong nMS
) : Timer( "YieldTimer" )
348 virtual void Invoke() override
350 for (int i
= 0; i
< 100; i
++)
351 Application::Yield();
357 void TimerTest::testNestedTimer()
359 sal_Int32 nCount
= 0;
360 YieldTimer
aCount(5);
361 AutoTimerCount
aCountUp( 3, nCount
);
362 // coverity[loop_top] - Application::Yield allows the timer to fire and increment nCount
364 Application::Yield();
369 class SlowCallbackTimer
: public Timer
373 SlowCallbackTimer( sal_uLong nMS
, bool &bBeenSlow
) :
374 Timer( "SlowCallbackTimer" ), mbSlow( bBeenSlow
)
380 virtual void Invoke() override
382 osl::Thread::wait( std::chrono::seconds(1) );
389 void TimerTest::testSlowTimerCallback()
391 bool bBeenSlow
= false;
392 sal_Int32 nCount
= 0;
393 AutoTimerCount
aHighFreq(1, nCount
);
394 SlowCallbackTimer
aSlow(250, bBeenSlow
);
395 // coverity[loop_top] - Application::Yield allows the timer to fire and toggle bBeenSlow
397 Application::Yield();
398 // coverity[loop_top] - Application::Yield allows the timer to fire and increment nCount
400 Application::Yield();
405 class TriggerIdleFromIdle
: public Idle
408 TriggerIdleFromIdle
* mpOther
;
410 explicit TriggerIdleFromIdle( bool* pTriggered
, TriggerIdleFromIdle
* pOther
) :
411 Idle( "TriggerIdleFromIdle" ), mpTriggered(pTriggered
), mpOther(pOther
)
414 virtual void Invoke() override
419 Application::Yield();
427 void TimerTest::testTriggerIdleFromIdle()
429 bool bTriggered1
= false;
430 bool bTriggered2
= false;
431 TriggerIdleFromIdle
aTest2( &bTriggered2
, nullptr );
432 TriggerIdleFromIdle
aTest1( &bTriggered1
, &aTest2
);
434 Application::Yield();
435 CPPUNIT_ASSERT_MESSAGE("idle not triggered", bTriggered1
);
436 CPPUNIT_ASSERT_MESSAGE("idle not triggered", bTriggered2
);
441 class IdleInvokedReStart
: public Idle
445 IdleInvokedReStart( sal_Int32
&rCount
)
446 : Idle( "IdleInvokedReStart" ), mrCount( rCount
)
450 virtual void Invoke() override
460 void TimerTest::testInvokedReStart()
462 sal_Int32 nCount
= 0;
463 IdleInvokedReStart
aIdle( nCount
);
464 Scheduler::ProcessEventsToIdle();
465 CPPUNIT_ASSERT_EQUAL( sal_Int32(2), nCount
);
470 class IdleSerializer
: public Idle
472 sal_uInt32 mnPosition
;
473 sal_uInt32
&mrProcessed
;
475 IdleSerializer(const char *pDebugName
, TaskPriority ePrio
,
476 sal_uInt32 nPosition
, sal_uInt32
&rProcessed
)
478 , mnPosition( nPosition
)
479 , mrProcessed( rProcessed
)
484 virtual void Invoke() override
487 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Ignored prio", mnPosition
, mrProcessed
);
493 void TimerTest::testPriority()
495 // scope, so tasks are deleted
497 // Start: 1st Idle low, 2nd high
498 sal_uInt32 nProcessed
= 0;
499 IdleSerializer
aLowPrioIdle("IdleSerializer LowPrio",
500 TaskPriority::LOWEST
, 2, nProcessed
);
501 IdleSerializer
aHighPrioIdle("IdleSerializer HighPrio",
502 TaskPriority::HIGHEST
, 1, nProcessed
);
503 Scheduler::ProcessEventsToIdle();
504 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Not all idles processed", sal_uInt32(2), nProcessed
);
508 // Start: 1st Idle high, 2nd low
509 sal_uInt32 nProcessed
= 0;
510 IdleSerializer
aHighPrioIdle("IdleSerializer HighPrio",
511 TaskPriority::HIGHEST
, 1, nProcessed
);
512 IdleSerializer
aLowPrioIdle("IdleSerializer LowPrio",
513 TaskPriority::LOWEST
, 2, nProcessed
);
514 Scheduler::ProcessEventsToIdle();
515 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Not all idles processed", sal_uInt32(2), nProcessed
);
521 class TestAutoIdleRR
: public AutoIdle
525 DECL_LINK( IdleRRHdl
, Timer
*, void );
528 TestAutoIdleRR( sal_uInt32
&rCount
,
529 const char *pDebugName
)
530 : AutoIdle( pDebugName
)
533 CPPUNIT_ASSERT_EQUAL( sal_uInt32(0), mrCount
);
534 SetInvokeHandler( LINK( this, TestAutoIdleRR
, IdleRRHdl
) );
541 IMPL_LINK_NOARG(TestAutoIdleRR
, IdleRRHdl
, Timer
*, void)
548 void TimerTest::testRoundRobin()
550 sal_uInt32 nCount1
= 0, nCount2
= 0;
551 TestAutoIdleRR
aIdle1( nCount1
, "TestAutoIdleRR aIdle1" ),
552 aIdle2( nCount2
, "TestAutoIdleRR aIdle2" );
553 while ( Application::Reschedule() )
555 CPPUNIT_ASSERT( nCount1
== nCount2
|| nCount1
- 1 == nCount2
);
556 CPPUNIT_ASSERT( nCount1
<= 3 );
557 CPPUNIT_ASSERT( nCount2
<= 3 );
559 CPPUNIT_ASSERT_EQUAL( sal_uInt32(3), nCount1
);
560 CPPUNIT_ASSERT_EQUAL( sal_uInt32(3), nCount2
);
563 CPPUNIT_TEST_SUITE_REGISTRATION(TimerTest
);
565 CPPUNIT_PLUGIN_IMPLEMENT();
567 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */