Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(695)

Unified Diff: tests/lib/async/zone_timer_task_test.dart

Issue 2119243002: Reapply zone tasks. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Fix test and update status files for IE. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tests/lib/async/zone_task_test.dart ('k') | tests/lib/lib.status » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tests/lib/async/zone_timer_task_test.dart
diff --git a/tests/lib/async/zone_timer_task_test.dart b/tests/lib/async/zone_timer_task_test.dart
new file mode 100644
index 0000000000000000000000000000000000000000..310f7ca510a5b6fa5ecf0ac24247e712a11240aa
--- /dev/null
+++ b/tests/lib/async/zone_timer_task_test.dart
@@ -0,0 +1,515 @@
+// Copyright (c) 2016, 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.
+
+// Tests timer tasks.
+
+import 'package:expect/expect.dart';
+import 'package:async_helper/async_helper.dart';
+import 'dart:async';
+import 'dart:collection';
+
+class MyTimerSpecification implements SingleShotTimerTaskSpecification {
+ final Function callback;
+ final Duration duration;
+
+ MyTimerSpecification(this.callback, this.duration);
+
+ bool get isOneShot => true;
+ String get name => "test.timer-override";
+}
+
+class MyPeriodicTimerSpecification implements PeriodicTimerTaskSpecification {
+ final Function callback;
+ final Duration duration;
+
+ MyPeriodicTimerSpecification(this.callback, this.duration);
+
+ bool get isOneShot => true;
+ String get name => "test.periodic-timer-override";
+}
+
+/// Makes sure things are working in a simple setting.
+/// No interceptions, changes, ...
+Future testTimerTask() {
+ List log = [];
+
+ var testCompleter = new Completer();
+ asyncStart();
+
+ int taskIdCounter = 0;
+
+ Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
+ TaskCreate create, TaskSpecification specification) {
+ var taskMap = self['taskMap'];
+ var taskIdMap = self['taskIdMap'];
+ if (specification is SingleShotTimerTaskSpecification) {
+ log.add("create enter "
+ "zone: ${self['name']} "
+ "spec-duration: ${specification.duration} "
+ "spec-oneshot?: ${specification.isOneShot}");
+ var result = parent.createTask(zone, create, specification);
+ taskMap[result] = specification;
+ taskIdMap[specification] = taskIdCounter++;
+ log.add("create leave");
+ return result;
+ } else if (specification is PeriodicTimerTaskSpecification) {
+ log.add("create enter "
+ "zone: ${self['name']} "
+ "spec-duration: ${specification.duration} "
+ "spec-oneshot?: ${specification.isOneShot}");
+ var result = parent.createTask(zone, create, specification);
+ taskMap[result] = specification;
+ taskIdMap[specification] = taskIdCounter++;
+ log.add("create leave");
+ return result;
+ }
+ return parent.createTask(zone, create, specification);
+ }
+
+ void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, TaskRun run,
+ Object task, Object arg) {
+ var taskMap = self['taskMap'];
+ var taskIdMap = self['taskIdMap'];
+ if (taskMap.containsKey(task)) {
+ var spec = taskMap[task];
+ log.add("run enter "
+ "zone: ${self['name']} "
+ "task-id: ${taskIdMap[spec]} "
+ "arg: $arg");
+ parent.runTask(zone, run, task, arg);
+ log.add("run leave");
+ return;
+ }
+ parent.runTask(zone, run, task, arg);
+ }
+
+ runZoned(() async {
+ var completer0 = new Completer();
+ Timer.run(() {
+ completer0.complete("done");
+ });
+ await completer0.future;
+
+ Expect.listEquals([
+ 'create enter zone: custom zone spec-duration: 0:00:00.000000 '
+ 'spec-oneshot?: true',
+ 'create leave',
+ 'run enter zone: custom zone task-id: 0 arg: null',
+ 'run leave'
+ ], log);
+ log.clear();
+
+ var completer1 = new Completer();
+ var counter1 = 0;
+ new Timer.periodic(const Duration(milliseconds: 5), (Timer timer) {
+ if (counter1++ > 1) {
+ timer.cancel();
+ completer1.complete("done");
+ }
+ });
+ await completer1.future;
+
+ Expect.listEquals([
+ 'create enter zone: custom zone spec-duration: 0:00:00.005000 '
+ 'spec-oneshot?: false',
+ 'create leave',
+ 'run enter zone: custom zone task-id: 1 arg: null',
+ 'run leave',
+ 'run enter zone: custom zone task-id: 1 arg: null',
+ 'run leave',
+ 'run enter zone: custom zone task-id: 1 arg: null',
+ 'run leave'
+ ], log);
+ log.clear();
+
+ testCompleter.complete("done");
+ asyncEnd();
+ },
+ zoneValues: {'name': 'custom zone', 'taskMap': {}, 'taskIdMap': {}},
+ zoneSpecification: new ZoneSpecification(
+ createTask: createTaskHandler,
+ runTask: runTaskHandler));
+
+ return testCompleter.future;
+}
+
+/// More complicated zone, that intercepts...
+Future testTimerTask2() {
+ List log = [];
+
+ var testCompleter = new Completer();
+ asyncStart();
+
+ int taskIdCounter = 0;
+
+ Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
+ TaskCreate create, TaskSpecification specification) {
+ var taskMap = self['taskMap'];
+ var taskIdMap = self['taskIdMap'];
+ if (specification is SingleShotTimerTaskSpecification) {
+ log.add("create enter "
+ "zone: ${self['name']} "
+ "spec-duration: ${specification.duration} "
+ "spec-oneshot?: ${specification.isOneShot}");
+ var mySpec = new MyTimerSpecification(specification.callback,
+ specification.duration + const Duration(milliseconds: 2));
+ var result = parent.createTask(zone, create, mySpec);
+ taskMap[result] = specification;
+ taskIdMap[specification] = taskIdCounter++;
+ log.add("create leave");
+ return result;
+ } else if (specification is PeriodicTimerTaskSpecification) {
+ log.add("create enter "
+ "zone: ${self['name']} "
+ "spec-duration: ${specification.duration} "
+ "spec-oneshot?: ${specification.isOneShot}");
+ var mySpec = new MyPeriodicTimerSpecification(specification.callback,
+ specification.duration + const Duration(milliseconds: 2));
+ var result = parent.createTask(zone, create, specification);
+ taskMap[result] = specification;
+ taskIdMap[specification] = taskIdCounter++;
+ log.add("create leave");
+ return result;
+ }
+ return parent.createTask(zone, create, specification);
+ }
+
+ void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, TaskRun run,
+ Object task, Object arg) {
+ var taskMap = self['taskMap'];
+ var taskIdMap = self['taskIdMap'];
+ if (taskMap.containsKey(task)) {
+ var spec = taskMap[task];
+ log.add("run enter "
+ "zone: ${self['name']} "
+ "task-id: ${taskIdMap[spec]} "
+ "arg: $arg");
+ parent.runTask(zone, run, task, arg);
+ log.add("run leave");
+ return;
+ }
+ parent.runTask(zone, run, task, arg);
+ }
+
+ runZoned(() async {
+ var completer0 = new Completer();
+ Timer.run(() {
+ completer0.complete("done");
+ });
+ await completer0.future;
+
+ // No visible change (except for the zone name) in the log, compared to the
+ // simple invocations.
+ Expect.listEquals([
+ 'create enter zone: outer-zone spec-duration: 0:00:00.000000 '
+ 'spec-oneshot?: true',
+ 'create leave',
+ 'run enter zone: outer-zone task-id: 0 arg: null',
+ 'run leave'
+ ], log);
+ log.clear();
+
+ var completer1 = new Completer();
+ var counter1 = 0;
+ new Timer.periodic(const Duration(milliseconds: 5), (Timer timer) {
+ if (counter1++ > 1) {
+ timer.cancel();
+ completer1.complete("done");
+ }
+ });
+ await completer1.future;
+
+ // No visible change (except for the zone nome) in the log, compared to the
+ // simple invocations.
+ Expect.listEquals([
+ 'create enter zone: outer-zone spec-duration: 0:00:00.005000 '
+ 'spec-oneshot?: false',
+ 'create leave',
+ 'run enter zone: outer-zone task-id: 1 arg: null',
+ 'run leave',
+ 'run enter zone: outer-zone task-id: 1 arg: null',
+ 'run leave',
+ 'run enter zone: outer-zone task-id: 1 arg: null',
+ 'run leave'
+ ], log);
+ log.clear();
+
+ var nestedCompleter = new Completer();
+
+ runZoned(() async {
+ var completer0 = new Completer();
+ Timer.run(() {
+ completer0.complete("done");
+ });
+ await completer0.future;
+
+ // The outer zone sees the duration change of the inner zone.
+ Expect.listEquals([
+ 'create enter zone: inner-zone spec-duration: 0:00:00.000000 '
+ 'spec-oneshot?: true',
+ 'create enter zone: outer-zone spec-duration: 0:00:00.002000 '
+ 'spec-oneshot?: true',
+ 'create leave',
+ 'create leave',
+ 'run enter zone: inner-zone task-id: 3 arg: null',
+ 'run enter zone: outer-zone task-id: 2 arg: null',
+ 'run leave',
+ 'run leave'
+ ], log);
+ log.clear();
+
+ var completer1 = new Completer();
+ var counter1 = 0;
+ new Timer.periodic(const Duration(milliseconds: 5), (Timer timer) {
+ if (counter1++ > 1) {
+ timer.cancel();
+ completer1.complete("done");
+ }
+ });
+ await completer1.future;
+
+ // The outer zone sees the duration change of the inner zone.
+ Expect.listEquals([
+ 'create enter zone: inner-zone spec-duration: 0:00:00.005000 '
+ 'spec-oneshot?: false',
+ 'create enter zone: outer-zone spec-duration: 0:00:00.005000 '
+ 'spec-oneshot?: false',
+ 'create leave',
+ 'create leave',
+ 'run enter zone: inner-zone task-id: 5 arg: null',
+ 'run enter zone: outer-zone task-id: 4 arg: null',
+ 'run leave',
+ 'run leave',
+ 'run enter zone: inner-zone task-id: 5 arg: null',
+ 'run enter zone: outer-zone task-id: 4 arg: null',
+ 'run leave',
+ 'run leave',
+ 'run enter zone: inner-zone task-id: 5 arg: null',
+ 'run enter zone: outer-zone task-id: 4 arg: null',
+ 'run leave',
+ 'run leave'
+ ], log);
+ log.clear();
+
+ nestedCompleter.complete("done");
+ },
+ zoneValues: {'name': 'inner-zone', 'taskMap': {}, 'taskIdMap': {}},
+ zoneSpecification: new ZoneSpecification(
+ createTask: createTaskHandler,
+ runTask: runTaskHandler));
+
+ await nestedCompleter.future;
+ testCompleter.complete("done");
+ asyncEnd();
+ },
+ zoneValues: {'name': 'outer-zone', 'taskMap': {}, 'taskIdMap': {}},
+ zoneSpecification: new ZoneSpecification(
+ createTask: createTaskHandler,
+ runTask: runTaskHandler));
+
+ return testCompleter.future;
+}
+
+class TimerEntry {
+ final int time;
+ final SimulatedTimer timer;
+
+ TimerEntry(this.time, this.timer);
+}
+
+class SimulatedTimer implements Timer {
+ static int _idCounter = 0;
+
+ Zone _zone;
+ final int _id = _idCounter++;
+ final Duration _duration;
+ final Function _callback;
+ final bool _isPeriodic;
+ bool _isActive = true;
+
+ SimulatedTimer(this._zone, this._duration, this._callback, this._isPeriodic);
+
+ bool get isActive => _isActive;
+
+ void cancel() {
+ _isActive = false;
+ }
+
+ void _run() {
+ if (!isActive) return;
+ _zone.runTask(_runTimer, this, null);
+ }
+
+ static void _runTimer(SimulatedTimer timer, _) {
+ if (timer._isPeriodic) {
+ timer._callback(timer);
+ } else {
+ timer._callback();
+ }
+ }
+}
+
+testSimulatedTimer() {
+ List log = [];
+
+ var currentTime = 0;
+ // Using a simple list as queue. Not very efficient, but the test has only
+ // very few timers running at the same time.
+ var queue = new DoubleLinkedQueue<TimerEntry>();
+
+ // Schedules the given callback at now + duration.
+ void schedule(int scheduledTime, SimulatedTimer timer) {
+ log.add("scheduling timer ${timer._id} for $scheduledTime");
+ if (queue.isEmpty) {
+ queue.add(new TimerEntry(scheduledTime, timer));
+ } else {
+ DoubleLinkedQueueEntry current = queue.firstEntry();
+ while (current != null) {
+ if (current.element.time <= scheduledTime) {
+ current = current.nextEntry();
+ } else {
+ current.prepend(new TimerEntry(scheduledTime, timer));
+ break;
+ }
+ }
+ if (current == null) {
+ queue.add(new TimerEntry(scheduledTime, timer));
+ }
+ }
+ }
+
+ void runQueue() {
+ while (queue.isNotEmpty) {
+ var item = queue.removeFirst();
+ // If multiple callbacks were scheduled at the same time, increment the
+ // current time instead of staying at the same time.
+ currentTime = item.time > currentTime ? item.time : currentTime + 1;
+ SimulatedTimer timer = item.timer;
+ log.add("running timer ${timer._id} at $currentTime "
+ "(active?: ${timer.isActive})");
+ if (!timer.isActive) continue;
+ if (timer._isPeriodic) {
+ schedule(currentTime + timer._duration.inMilliseconds, timer);
+ }
+ item.timer._run();
+ }
+ }
+
+ SimulatedTimer createSimulatedOneShotTimer(
+ SingleShotTimerTaskSpecification spec, Zone zone) {
+ var timer = new SimulatedTimer(zone, spec.duration, spec.callback, false);
+ schedule(currentTime + spec.duration.inMilliseconds, timer);
+ return timer;
+ }
+
+ SimulatedTimer createSimulatedPeriodicTimer(
+ PeriodicTimerTaskSpecification spec, Zone zone) {
+ var timer = new SimulatedTimer(zone, spec.duration, spec.callback, true);
+ schedule(currentTime + spec.duration.inMilliseconds, timer);
+ return timer;
+ }
+
+ Object createSimulatedTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
+ TaskCreate create, TaskSpecification specification) {
+ var taskMap = self['taskMap'];
+ var taskIdMap = self['taskIdMap'];
+ if (specification is SingleShotTimerTaskSpecification) {
+ log.add("create enter "
+ "zone: ${self['name']} "
+ "spec-duration: ${specification.duration} "
+ "spec-oneshot?: ${specification.isOneShot}");
+ var result =
+ parent.createTask(zone, createSimulatedOneShotTimer, specification);
+ log.add("create leave");
+ return result;
+ }
+ if (specification is PeriodicTimerTaskSpecification) {
+ log.add("create enter "
+ "zone: ${self['name']} "
+ "spec-duration: ${specification.duration} "
+ "spec-oneshot?: ${specification.isOneShot}");
+ var result =
+ parent.createTask(zone, createSimulatedPeriodicTimer, specification);
+ log.add("create leave");
+ return result;
+ }
+ return parent.createTask(zone, create, specification);
+ }
+
+ runZoned(() {
+ Timer.run(() {
+ log.add("running Timer.run");
+ });
+
+ var timer0;
+
+ new Timer(const Duration(milliseconds: 10), () {
+ log.add("running Timer(10)");
+ timer0.cancel();
+ log.add("canceled timer0");
+ });
+
+ timer0 = new Timer(const Duration(milliseconds: 15), () {
+ log.add("running Timer(15)");
+ });
+
+ var counter1 = 0;
+ new Timer.periodic(const Duration(milliseconds: 5), (Timer timer) {
+ log.add("running periodic timer $counter1");
+ if (counter1++ > 1) {
+ timer.cancel();
+ }
+ });
+ },
+ zoneSpecification:
+ new ZoneSpecification(createTask: createSimulatedTaskHandler));
+
+ runQueue();
+
+ Expect.listEquals([
+ 'create enter zone: null spec-duration: 0:00:00.000000 spec-oneshot?: true',
+ 'scheduling timer 0 for 0',
+ 'create leave',
+ 'create enter zone: null spec-duration: 0:00:00.010000 spec-oneshot?: true',
+ 'scheduling timer 1 for 10',
+ 'create leave',
+ 'create enter zone: null spec-duration: 0:00:00.015000 spec-oneshot?: true',
+ 'scheduling timer 2 for 15',
+ 'create leave',
+ 'create enter zone: null spec-duration: 0:00:00.005000 '
+ 'spec-oneshot?: false',
+ 'scheduling timer 3 for 5',
+ 'create leave',
+ 'running timer 0 at 1 (active?: true)',
+ 'running Timer.run',
+ 'running timer 3 at 5 (active?: true)',
+ 'scheduling timer 3 for 10',
+ 'running periodic timer 0',
+ 'running timer 1 at 10 (active?: true)',
+ 'running Timer(10)',
+ 'canceled timer0',
+ 'running timer 3 at 11 (active?: true)',
+ 'scheduling timer 3 for 16',
+ 'running periodic timer 1',
+ 'running timer 2 at 15 (active?: false)',
+ 'running timer 3 at 16 (active?: true)',
+ 'scheduling timer 3 for 21',
+ 'running periodic timer 2',
+ 'running timer 3 at 21 (active?: false)'
+ ], log);
+ log.clear();
+}
+
+runTests() async {
+ await testTimerTask();
+ await testTimerTask2();
+ testSimulatedTimer();
+}
+
+main() {
+ asyncStart();
+ runTests().then((_) {
+ asyncEnd();
+ });
+}
« no previous file with comments | « tests/lib/async/zone_task_test.dart ('k') | tests/lib/lib.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698