Skip to content

Commit

Permalink
Fixes #2471. Fix timer constructor tests. Add check for microtasks (#…
Browse files Browse the repository at this point in the history
…2472)

Fix timer constructor tests. Add check for microtasks
  • Loading branch information
sgrekhov authored Jan 9, 2024
1 parent 155e055 commit 720cc61
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 55 deletions.
56 changes: 56 additions & 0 deletions LibTest/async/Timer/Timer.periodic_A01_t02.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

/// @assertion
/// factory Timer.periodic(Duration duration, void callback(Timer timer))
///
/// Creates a new repeating timer.
///
/// The callback is invoked repeatedly with duration intervals until canceled
/// with the cancel function.
///
/// The exact timing depends on the underlying timer implementation. No more
/// than n callbacks will be made in duration * n time, but the time between two
/// consecutive callbacks can be shorter and longer than duration.
///
/// In particular, an implementation may schedule the next callback, e.g., a
/// duration after either when the previous callback ended, when the previous
/// callback started, or when the previous callback was scheduled for - even if
/// the actual callback was delayed.
///
/// @description Checks that no more than `n` callbacks are made in
/// `duration * n` time
/// @author [email protected]
import "dart:async";
import "../../../Utils/expect.dart";

void check(int durationMs, int n) {
int callbacksCount = 0;
Duration duration = Duration(milliseconds: durationMs);
Stopwatch sw = Stopwatch();

sw.start();
Timer.periodic(duration, (Timer timer) {
callbacksCount++;
if (sw.elapsedMilliseconds <= n * durationMs) {
Expect.isTrue(
callbacksCount <= n,
"Expected no more than $n callbacks for ${n * durationMs}ms, but " +
"actually there were $callbacksCount for " +
"${sw.elapsedMilliseconds}ms");
} else {
timer.cancel();
asyncEnd();
}
});
}

void main() {
asyncStart(4);
check(1, 5);
check(10, 5);
check(50, 5);
check(100, 5);
}
54 changes: 27 additions & 27 deletions LibTest/async/Timer/Timer.periodic_A02_t01.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,46 @@

/// @assertion
/// factory Timer.periodic(Duration duration, void callback(Timer timer))
/// ...
/// A negative duration is treated the same as [Duration.zero]
///
/// A negative duration is treated the same as a duration of 0
///
/// @description Checks that negative duration is accepted and treated like a 0.
/// @description Checks that negative duration is accepted and treated as
/// [Duration.zero].
/// @author kaigorodov
import "dart:async";
import "../../../Utils/expect.dart";

// Most browsers can trigger timers too early. Test data shows instances where
// timers fire even 15ms early. We add a safety margin to prevent flakiness
// when running this test on affected platforms.
Duration safetyMargin = const bool.fromEnvironment('dart.library.js')
? Duration(milliseconds: 40)
: Duration.zero;
check(int durationInMs) {
bool earlierZeroTimerTriggered = false;
bool laterZeroTimerTriggered = false;

// Timer is not started immediately but waits for microtask queue. So the
// timer can be started with some unpredictable delay. Therefore let's check
// that it triggers not earlier that previously-scheduled zero-duration timer
// and not earlier that later-scheduled zero-duration timer
Timer.periodic(Duration.zero, (timer) {
earlierZeroTimerTriggered = true;
timer.cancel();
});

check(int delay) {
final maxCount = 5;
int count = 0;
Stopwatch sw = new Stopwatch();
sw.start();
Timer.periodic(Duration(milliseconds: durationInMs), (Timer timer) {
Expect.isTrue(earlierZeroTimerTriggered);
Expect.isFalse(laterZeroTimerTriggered);
timer.cancel();
});

asyncStart();
new Timer.periodic(durationInMilliseconds(delay), (Timer timer) {
count++;
Duration expected = durationInMilliseconds(40); // Expect actual time near 0
Duration actual = sw.elapsed;
Expect.isTrue(actual <= expected + safetyMargin,
"expected=${expected + safetyMargin}, actual=$actual");
if (count == maxCount) {
timer.cancel();
asyncEnd();
}
Timer.periodic(Duration.zero, (timer) {
laterZeroTimerTriggered = true;
timer.cancel();
asyncEnd();
});
}

main() {
asyncStart();
asyncStart(4);
check(0);
check(-10);
check(-100);
check(-1000);
asyncEnd();
}
53 changes: 53 additions & 0 deletions LibTest/async/Timer/Timer.periodic_A02_t02.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

/// @assertion
/// factory Timer.periodic(Duration duration, void callback(Timer timer))
/// ...
/// A negative duration is treated the same as [Duration.zero]
///
/// @description Checks that negative duration is accepted and treated as
/// [Duration.zero]. Test that timer waits for microtasks queue before
/// triggering
/// @author [email protected]
import "dart:async";
import "../../../Utils/expect.dart";

const maxMicrotasks = 10;
int microtasksCount = 0;

void microtask() {
if (++microtasksCount < maxMicrotasks) {
scheduleMicrotask(microtask);
}
}

Future<void> check(int durationInMs, int n) {
Completer<void> completer = Completer();
int timerCount = 0;

scheduleMicrotask(microtask);

Timer.periodic(Duration(milliseconds: durationInMs), (Timer timer) {
Expect.equals(maxMicrotasks, microtasksCount);
microtasksCount = 0;
if (++timerCount == n) {
timer.cancel();
completer.complete();
asyncEnd();
} else {
scheduleMicrotask(microtask);
}
});
return completer.future;
}

main() async {
asyncStart(4);
await check(0, 5);
await check(-10, 5);
await check(-100, 5);
await check(-1000, 3);
}
8 changes: 1 addition & 7 deletions LibTest/async/Timer/Timer_A01_t01.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ check(int delayms) {
Duration delay = durationInMilliseconds(delayms);
Stopwatch sw = new Stopwatch();
sw.start();

asyncStart();
new Timer(delay, () {
Duration actual = sw.elapsed;
Expect.isTrue(delay <= actual + safetyMargin,
Expand All @@ -35,16 +33,12 @@ check(int delayms) {
}

main() {
asyncStart();
asyncStart(7);
check(150);
check(100);
check(50);
check(25);
check(10);
check(2);
check(1);
check(0);
check(-5);
check(-50);
asyncEnd();
}
43 changes: 22 additions & 21 deletions LibTest/async/Timer/Timer_A02_t01.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,42 @@

/// @assertion factory Timer(Duration duration, void callback())
///
/// A negative duration is treated the same as a duration of 0.
/// A negative duration is treated the same as [Duration.zero]
///
/// @description Checks that negative duration is accepted.
/// @description Checks that negative duration is accepted and treated as
/// [Duration.zero].
/// @author kaigorodov
import "dart:async";
import "../../../Utils/expect.dart";

// Most browsers can trigger timers too early. Test data shows instances where
// timers fire even 15ms early. We add a safety margin to prevent flakiness
// when running this test on affected platforms.
Duration safetyMargin = const bool.fromEnvironment('dart.library.js')
? Duration(milliseconds: 40)
: Duration.zero;
check(int durationInMs) {
bool earlierZeroTimerTriggered = false;
bool laterZeroTimerTriggered = false;

check(int delayms) {
Stopwatch sw = new Stopwatch();
sw.start();
// Timer is not started immediately but waits for microtask queue. So the
// timer can be started with some unpredictable delay. Therefore let's check
// that it triggers not earlier that previously-scheduled zero-duration timer
// and not earlier that later-scheduled zero-duration timer
Timer(Duration.zero, () {
earlierZeroTimerTriggered = true;
});

Timer(Duration(milliseconds: durationInMs), () {
Expect.isTrue(earlierZeroTimerTriggered);
Expect.isFalse(laterZeroTimerTriggered);
});

asyncStart();
new Timer(durationInMilliseconds(delayms), () {
Duration expected = durationInMilliseconds(40); // Expect actual time near 0
Duration actual = sw.elapsed;
Expect.isTrue(actual <= expected + safetyMargin,
"expected=${expected + safetyMargin}, actual=$actual");
Timer(Duration.zero, () {
laterZeroTimerTriggered = true;
asyncEnd();
});
}

main() {
asyncStart();
asyncStart(4);
check(0);
check(-1);
check(-10);
check(-100);
check(-1000);
asyncEnd();
check(-500);
}
45 changes: 45 additions & 0 deletions LibTest/async/Timer/Timer_A02_t02.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

/// @assertion factory Timer(Duration duration, void callback())
///
/// A negative duration is treated the same as [Duration.zero]
///
/// @description Checks that negative duration is accepted and treated as
/// [Duration.zero]. Test that timer waits for microtask queue before the start
/// @author [email protected]
import "dart:async";
import "../../../Utils/expect.dart";

const maxMicrotasks = 10;
int microtasksCount = 0;

void microtask() {
if (++microtasksCount < maxMicrotasks) {
scheduleMicrotask(microtask);
}
}

Future<void> check(int durationInMs, int n) {
Completer<void> completer = Completer();

scheduleMicrotask(microtask);

Timer(Duration(milliseconds: durationInMs), () {
Expect.equals(maxMicrotasks, microtasksCount);
microtasksCount = 0;
completer.complete();
asyncEnd();
});
return completer.future;
}

main() async {
asyncStart(4);
await check(0, 5);
await check(-10, 5);
await check(-100, 5);
await check(-500, 3);
}

0 comments on commit 720cc61

Please sign in to comment.