calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / vcl / qa / cppunit / timer.cxx
blob15493b953639aebb2e4ad4b7b02dbb1e278b2ed1
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 namespace {
32 /// Avoid our timer tests just wedging the build if they fail.
33 class WatchDog : public osl::Thread
35 sal_Int32 mnSeconds;
36 public:
37 explicit WatchDog(sal_Int32 nSeconds) :
38 Thread(),
39 mnSeconds( nSeconds )
41 create();
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");
47 fflush(stderr);
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
58 public:
59 TimerTest() : BootstrapFixture(true, false) {}
61 void testIdle();
62 void testIdleMainloop();
63 #ifdef TEST_WATCHDOG
64 void testWatchdog();
65 #endif
66 void testDurations();
67 #ifdef TEST_TIMERPRECISION
68 void testAutoTimer();
69 void testMultiAutoTimers();
70 #endif
71 void testAutoTimerStop();
72 void testNestedTimer();
73 void testSlowTimerCallback();
74 void testTriggerIdleFromIdle();
75 void testInvokedReStart();
76 void testPriority();
77 void testRoundRobin();
79 CPPUNIT_TEST_SUITE(TimerTest);
80 CPPUNIT_TEST(testIdle);
81 CPPUNIT_TEST(testIdleMainloop);
82 #ifdef TEST_WATCHDOG
83 CPPUNIT_TEST(testWatchdog);
84 #endif
85 CPPUNIT_TEST(testDurations);
86 #ifdef TEST_TIMERPRECISION
87 CPPUNIT_TEST(testAutoTimer);
88 CPPUNIT_TEST(testMultiAutoTimers);
89 #endif
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();
101 #ifdef TEST_WATCHDOG
102 void TimerTest::testWatchdog()
104 // out-wait the watchdog.
105 osl::Thread::wait( std::chrono::seconds(12) );
107 #endif
109 namespace {
111 class IdleBool : public Idle
113 bool &mrBool;
114 public:
115 explicit IdleBool( bool &rBool ) :
116 Idle( "IdleBool" ), mrBool( rBool )
118 SetPriority( TaskPriority::LOWEST );
119 Start();
120 mrBool = false;
122 virtual void Invoke() override
124 mrBool = true;
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
144 while (!bTriggered)
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);
157 namespace {
159 class TimerBool : public Timer
161 bool &mrBool;
162 public:
163 TimerBool( sal_uLong nMS, bool &rBool ) :
164 Timer( "TimerBool" ), mrBool( rBool )
166 SetTimeout( nMS );
167 Start();
168 mrBool = false;
170 virtual void Invoke() override
172 mrBool = true;
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++)
184 bool bDone = false;
185 TimerBool aTimer( aDurations[i], bDone );
186 // coverity[loop_top] - Application::Yield allows the timer to fire and toggle bDone
187 while( !bDone )
189 Application::Yield();
194 namespace {
196 class AutoTimerCount : public AutoTimer
198 sal_Int32 &mrCount;
199 const sal_Int32 mnMaxCount;
201 public:
202 AutoTimerCount( sal_uLong nMS, sal_Int32 &rCount,
203 const sal_Int32 nMaxCount = -1 )
204 : AutoTimer( "AutoTimerCount" )
205 , mrCount( rCount )
206 , mnMaxCount( nMaxCount )
208 SetTimeout( nMS );
209 Start();
210 mrCount = 0;
213 virtual void Invoke() override
215 ++mrCount;
216 CPPUNIT_ASSERT( mnMaxCount < 0 || mrCount <= mnMaxCount );
217 if ( mrCount == mnMaxCount )
218 Stop();
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();
240 nCount = 0;
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))
256 // Success.
257 return;
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)
289 nCountX = 0;
290 nCountY = 0;
291 nCount = 0;
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))
317 // Success.
318 return;
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() );
338 namespace {
340 class YieldTimer : public Timer
342 public:
343 explicit YieldTimer( sal_uLong nMS ) : Timer( "YieldTimer" )
345 SetTimeout( nMS );
346 Start();
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
363 while (nCount < 20)
364 Application::Yield();
367 namespace {
369 class SlowCallbackTimer : public Timer
371 bool &mbSlow;
372 public:
373 SlowCallbackTimer( sal_uLong nMS, bool &bBeenSlow ) :
374 Timer( "SlowCallbackTimer" ), mbSlow( bBeenSlow )
376 SetTimeout( nMS );
377 Start();
378 mbSlow = false;
380 virtual void Invoke() override
382 osl::Thread::wait( std::chrono::seconds(1) );
383 mbSlow = true;
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
396 while (!bBeenSlow)
397 Application::Yield();
398 // coverity[loop_top] - Application::Yield allows the timer to fire and increment nCount
399 while (nCount < 200)
400 Application::Yield();
403 namespace {
405 class TriggerIdleFromIdle : public Idle
407 bool* mpTriggered;
408 TriggerIdleFromIdle* mpOther;
409 public:
410 explicit TriggerIdleFromIdle( bool* pTriggered, TriggerIdleFromIdle* pOther ) :
411 Idle( "TriggerIdleFromIdle" ), mpTriggered(pTriggered), mpOther(pOther)
414 virtual void Invoke() override
416 Start();
417 if (mpOther)
418 mpOther->Start();
419 Application::Yield();
420 if (mpTriggered)
421 *mpTriggered = true;
427 void TimerTest::testTriggerIdleFromIdle()
429 bool bTriggered1 = false;
430 bool bTriggered2 = false;
431 TriggerIdleFromIdle aTest2( &bTriggered2, nullptr );
432 TriggerIdleFromIdle aTest1( &bTriggered1, &aTest2 );
433 aTest1.Start();
434 Application::Yield();
435 CPPUNIT_ASSERT_MESSAGE("idle not triggered", bTriggered1);
436 CPPUNIT_ASSERT_MESSAGE("idle not triggered", bTriggered2);
439 namespace {
441 class IdleInvokedReStart : public Idle
443 sal_Int32 &mrCount;
444 public:
445 IdleInvokedReStart( sal_Int32 &rCount )
446 : Idle( "IdleInvokedReStart" ), mrCount( rCount )
448 Start();
450 virtual void Invoke() override
452 mrCount++;
453 if ( mrCount < 2 )
454 Start();
460 void TimerTest::testInvokedReStart()
462 sal_Int32 nCount = 0;
463 IdleInvokedReStart aIdle( nCount );
464 Scheduler::ProcessEventsToIdle();
465 CPPUNIT_ASSERT_EQUAL( sal_Int32(2), nCount );
468 namespace {
470 class IdleSerializer : public Idle
472 sal_uInt32 mnPosition;
473 sal_uInt32 &mrProcessed;
474 public:
475 IdleSerializer(const char *pDebugName, TaskPriority ePrio,
476 sal_uInt32 nPosition, sal_uInt32 &rProcessed)
477 : Idle( pDebugName )
478 , mnPosition( nPosition )
479 , mrProcessed( rProcessed )
481 SetPriority(ePrio);
482 Start();
484 virtual void Invoke() override
486 ++mrProcessed;
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 );
519 namespace {
521 class TestAutoIdleRR : public AutoIdle
523 sal_uInt32 &mrCount;
525 DECL_LINK( IdleRRHdl, Timer *, void );
527 public:
528 TestAutoIdleRR( sal_uInt32 &rCount,
529 const char *pDebugName )
530 : AutoIdle( pDebugName )
531 , mrCount( rCount )
533 CPPUNIT_ASSERT_EQUAL( sal_uInt32(0), mrCount );
534 SetInvokeHandler( LINK( this, TestAutoIdleRR, IdleRRHdl ) );
535 Start();
541 IMPL_LINK_NOARG(TestAutoIdleRR, IdleRRHdl, Timer *, void)
543 ++mrCount;
544 if ( mrCount == 3 )
545 Stop();
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: */