OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 Google Inc. All Rights Reserved. |
| 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at |
| 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. |
| 14 |
| 15 library quiver.testing.async.fake_async_test; |
| 16 |
| 17 import 'dart:async'; |
| 18 |
| 19 import 'package:quiver/testing/async.dart'; |
| 20 import 'package:test/test.dart'; |
| 21 |
| 22 main() { |
| 23 group('FakeAsync', () { |
| 24 var initialTime = new DateTime(2000); |
| 25 var elapseBy = const Duration(days: 1); |
| 26 |
| 27 test('should set initial time', () { |
| 28 expect(new FakeAsync().getClock(initialTime).now(), initialTime); |
| 29 }); |
| 30 |
| 31 group('elapseBlocking', () { |
| 32 test('should elapse time without calling timers', () { |
| 33 var timerCalled = false; |
| 34 var timer = new Timer(elapseBy ~/ 2, () => timerCalled = true); |
| 35 new FakeAsync().elapseBlocking(elapseBy); |
| 36 expect(timerCalled, isFalse); |
| 37 timer.cancel(); |
| 38 }); |
| 39 |
| 40 test('should elapse time by the specified amount', () { |
| 41 var it = new FakeAsync(); |
| 42 it.elapseBlocking(elapseBy); |
| 43 expect(it.getClock(initialTime).now(), initialTime.add(elapseBy)); |
| 44 }); |
| 45 |
| 46 test('should throw when called with a negative duration', () { |
| 47 expect(() { |
| 48 new FakeAsync().elapseBlocking(const Duration(days: -1)); |
| 49 }, throwsA(new isInstanceOf<ArgumentError>())); |
| 50 }); |
| 51 }); |
| 52 |
| 53 group('elapse', () { |
| 54 test('should elapse time by the specified amount', () { |
| 55 new FakeAsync().run((async) { |
| 56 async.elapse(elapseBy); |
| 57 expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); |
| 58 }); |
| 59 }); |
| 60 |
| 61 test('should throw ArgumentError when called with a negative duration', |
| 62 () { |
| 63 expect(() => new FakeAsync().elapse(const Duration(days: -1)), |
| 64 throwsA(new isInstanceOf<ArgumentError>())); |
| 65 }); |
| 66 |
| 67 test('should throw when called before previous call is complete', () { |
| 68 new FakeAsync().run((async) { |
| 69 var error; |
| 70 new Timer(elapseBy ~/ 2, () { |
| 71 try { |
| 72 async.elapse(elapseBy); |
| 73 } catch (e) { |
| 74 error = e; |
| 75 } |
| 76 }); |
| 77 async.elapse(elapseBy); |
| 78 expect(error, new isInstanceOf<StateError>()); |
| 79 }); |
| 80 }); |
| 81 |
| 82 group('when creating timers', () { |
| 83 test('should call timers expiring before or at end time', () { |
| 84 new FakeAsync().run((async) { |
| 85 var beforeCallCount = 0; |
| 86 var atCallCount = 0; |
| 87 new Timer(elapseBy ~/ 2, () { |
| 88 beforeCallCount++; |
| 89 }); |
| 90 new Timer(elapseBy, () { |
| 91 atCallCount++; |
| 92 }); |
| 93 async.elapse(elapseBy); |
| 94 expect(beforeCallCount, 1); |
| 95 expect(atCallCount, 1); |
| 96 }); |
| 97 }); |
| 98 |
| 99 test('should call timers expiring due to elapseBlocking', () { |
| 100 new FakeAsync().run((async) { |
| 101 bool secondaryCalled = false; |
| 102 new Timer(elapseBy, () { |
| 103 async.elapseBlocking(elapseBy); |
| 104 }); |
| 105 new Timer(elapseBy * 2, () { |
| 106 secondaryCalled = true; |
| 107 }); |
| 108 async.elapse(elapseBy); |
| 109 expect(secondaryCalled, isTrue); |
| 110 expect(async.getClock(initialTime).now(), |
| 111 initialTime.add(elapseBy * 2)); |
| 112 }); |
| 113 }); |
| 114 |
| 115 test('should call timers at their scheduled time', () { |
| 116 new FakeAsync().run((async) { |
| 117 DateTime calledAt; |
| 118 var periodicCalledAt = <DateTime>[]; |
| 119 new Timer(elapseBy ~/ 2, () { |
| 120 calledAt = async.getClock(initialTime).now(); |
| 121 }); |
| 122 new Timer.periodic(elapseBy ~/ 2, (_) { |
| 123 periodicCalledAt.add(async.getClock(initialTime).now()); |
| 124 }); |
| 125 async.elapse(elapseBy); |
| 126 expect(calledAt, initialTime.add(elapseBy ~/ 2)); |
| 127 expect(periodicCalledAt, |
| 128 [elapseBy ~/ 2, elapseBy].map(initialTime.add)); |
| 129 }); |
| 130 }); |
| 131 |
| 132 test('should not call timers expiring after end time', () { |
| 133 new FakeAsync().run((async) { |
| 134 var timerCallCount = 0; |
| 135 new Timer(elapseBy * 2, () { |
| 136 timerCallCount++; |
| 137 }); |
| 138 async.elapse(elapseBy); |
| 139 expect(timerCallCount, 0); |
| 140 }); |
| 141 }); |
| 142 |
| 143 test('should not call canceled timers', () { |
| 144 new FakeAsync().run((async) { |
| 145 int timerCallCount = 0; |
| 146 var timer = new Timer(elapseBy ~/ 2, () { |
| 147 timerCallCount++; |
| 148 }); |
| 149 timer.cancel(); |
| 150 async.elapse(elapseBy); |
| 151 expect(timerCallCount, 0); |
| 152 }); |
| 153 }); |
| 154 |
| 155 test('should call periodic timers each time the duration elapses', () { |
| 156 new FakeAsync().run((async) { |
| 157 var periodicCallCount = 0; |
| 158 new Timer.periodic(elapseBy ~/ 10, (_) { |
| 159 periodicCallCount++; |
| 160 }); |
| 161 async.elapse(elapseBy); |
| 162 expect(periodicCallCount, 10); |
| 163 }); |
| 164 }); |
| 165 |
| 166 test('should process microtasks surrounding each timer', () { |
| 167 new FakeAsync().run((async) { |
| 168 var microtaskCalls = 0; |
| 169 var timerCalls = 0; |
| 170 scheduleMicrotasks() { |
| 171 for (int i = 0; i < 5; i++) { |
| 172 scheduleMicrotask(() => microtaskCalls++); |
| 173 } |
| 174 } |
| 175 scheduleMicrotasks(); |
| 176 new Timer.periodic(elapseBy ~/ 5, (_) { |
| 177 timerCalls++; |
| 178 expect(microtaskCalls, 5 * timerCalls); |
| 179 scheduleMicrotasks(); |
| 180 }); |
| 181 async.elapse(elapseBy); |
| 182 expect(timerCalls, 5); |
| 183 expect(microtaskCalls, 5 * (timerCalls + 1)); |
| 184 }); |
| 185 }); |
| 186 |
| 187 test('should pass the periodic timer itself to callbacks', () { |
| 188 new FakeAsync().run((async) { |
| 189 Timer passedTimer; |
| 190 Timer periodic = new Timer.periodic(elapseBy, (timer) { |
| 191 passedTimer = timer; |
| 192 }); |
| 193 async.elapse(elapseBy); |
| 194 expect(periodic, same(passedTimer)); |
| 195 }); |
| 196 }); |
| 197 |
| 198 test('should call microtasks before advancing time', () { |
| 199 new FakeAsync().run((async) { |
| 200 DateTime calledAt; |
| 201 scheduleMicrotask(() { |
| 202 calledAt = async.getClock(initialTime).now(); |
| 203 }); |
| 204 async.elapse(const Duration(minutes: 1)); |
| 205 expect(calledAt, initialTime); |
| 206 }); |
| 207 }); |
| 208 |
| 209 test('should add event before advancing time', () { |
| 210 return new Future(() => new FakeAsync().run((async) { |
| 211 var controller = new StreamController(); |
| 212 var ret = controller.stream.first.then((_) { |
| 213 expect(async.getClock(initialTime).now(), initialTime); |
| 214 }); |
| 215 controller.add(null); |
| 216 async.elapse(const Duration(minutes: 1)); |
| 217 return ret; |
| 218 })); |
| 219 }); |
| 220 |
| 221 test('should increase negative duration timers to zero duration', () { |
| 222 new FakeAsync().run((async) { |
| 223 var negativeDuration = const Duration(days: -1); |
| 224 DateTime calledAt; |
| 225 new Timer(negativeDuration, () { |
| 226 calledAt = async.getClock(initialTime).now(); |
| 227 }); |
| 228 async.elapse(const Duration(minutes: 1)); |
| 229 expect(calledAt, initialTime); |
| 230 }); |
| 231 }); |
| 232 |
| 233 test('should not be additive with elapseBlocking', () { |
| 234 new FakeAsync().run((async) { |
| 235 new Timer(Duration.ZERO, () => async.elapseBlocking(elapseBy * 5)); |
| 236 async.elapse(elapseBy); |
| 237 expect(async.getClock(initialTime).now(), |
| 238 initialTime.add(elapseBy * 5)); |
| 239 }); |
| 240 }); |
| 241 |
| 242 group('isActive', () { |
| 243 test('should be false after timer is run', () { |
| 244 new FakeAsync().run((async) { |
| 245 var timer = new Timer(elapseBy ~/ 2, () {}); |
| 246 async.elapse(elapseBy); |
| 247 expect(timer.isActive, isFalse); |
| 248 }); |
| 249 }); |
| 250 |
| 251 test('should be true after periodic timer is run', () { |
| 252 new FakeAsync().run((async) { |
| 253 var timer = new Timer.periodic(elapseBy ~/ 2, (_) {}); |
| 254 async.elapse(elapseBy); |
| 255 expect(timer.isActive, isTrue); |
| 256 }); |
| 257 }); |
| 258 |
| 259 test('should be false after timer is canceled', () { |
| 260 new FakeAsync().run((async) { |
| 261 var timer = new Timer(elapseBy ~/ 2, () {}); |
| 262 timer.cancel(); |
| 263 expect(timer.isActive, isFalse); |
| 264 }); |
| 265 }); |
| 266 }); |
| 267 |
| 268 test('should work with new Future()', () { |
| 269 new FakeAsync().run((async) { |
| 270 var callCount = 0; |
| 271 new Future(() => callCount++); |
| 272 async.elapse(Duration.ZERO); |
| 273 expect(callCount, 1); |
| 274 }); |
| 275 }); |
| 276 |
| 277 test('should work with Future.delayed', () { |
| 278 new FakeAsync().run((async) { |
| 279 int result; |
| 280 new Future.delayed(elapseBy, () => result = 5); |
| 281 async.elapse(elapseBy); |
| 282 expect(result, 5); |
| 283 }); |
| 284 }); |
| 285 |
| 286 test('should work with Future.timeout', () { |
| 287 new FakeAsync().run((async) { |
| 288 var completer = new Completer(); |
| 289 var timed = completer.future.timeout(elapseBy ~/ 2); |
| 290 expect(timed, throwsA(new isInstanceOf<TimeoutException>())); |
| 291 async.elapse(elapseBy); |
| 292 completer.complete(); |
| 293 }); |
| 294 }); |
| 295 |
| 296 // TODO: Pausing and resuming the timeout Stream doesn't work since |
| 297 // it uses `new Stopwatch()`. |
| 298 // |
| 299 // See https://code.google.com/p/dart/issues/detail?id=18149 |
| 300 test('should work with Stream.periodic', () { |
| 301 new FakeAsync().run((async) { |
| 302 var events = <int>[]; |
| 303 StreamSubscription subscription; |
| 304 var periodic = |
| 305 new Stream.periodic(const Duration(minutes: 1), (i) => i); |
| 306 subscription = periodic.listen(events.add, cancelOnError: true); |
| 307 async.elapse(const Duration(minutes: 3)); |
| 308 subscription.cancel(); |
| 309 expect(events, [0, 1, 2]); |
| 310 }); |
| 311 }); |
| 312 |
| 313 test('should work with Stream.timeout', () { |
| 314 new FakeAsync().run((async) { |
| 315 var events = <int>[]; |
| 316 var errors = []; |
| 317 var controller = new StreamController(); |
| 318 var timed = controller.stream.timeout(const Duration(minutes: 2)); |
| 319 var subscription = timed.listen(events.add, |
| 320 onError: errors.add, cancelOnError: true); |
| 321 controller.add(0); |
| 322 async.elapse(const Duration(minutes: 1)); |
| 323 expect(events, [0]); |
| 324 async.elapse(const Duration(minutes: 1)); |
| 325 subscription.cancel(); |
| 326 expect(errors, hasLength(1)); |
| 327 expect(errors.first, new isInstanceOf<TimeoutException>()); |
| 328 return controller.close(); |
| 329 }); |
| 330 }); |
| 331 }); |
| 332 }); |
| 333 |
| 334 group('flushMicrotasks', () { |
| 335 test('should flush a microtask', () { |
| 336 new FakeAsync().run((async) { |
| 337 bool microtaskRan = false; |
| 338 new Future.microtask(() { |
| 339 microtaskRan = true; |
| 340 }); |
| 341 expect(microtaskRan, isFalse, |
| 342 reason: 'should not flush until asked to'); |
| 343 async.flushMicrotasks(); |
| 344 expect(microtaskRan, isTrue); |
| 345 }); |
| 346 }); |
| 347 test('should flush microtasks scheduled by microtasks in order', () { |
| 348 new FakeAsync().run((async) { |
| 349 final log = []; |
| 350 new Future.microtask(() { |
| 351 log.add(1); |
| 352 new Future.microtask(() { |
| 353 log.add(3); |
| 354 }); |
| 355 }); |
| 356 new Future.microtask(() { |
| 357 log.add(2); |
| 358 }); |
| 359 expect(log, hasLength(0), reason: 'should not flush until asked to'); |
| 360 async.flushMicrotasks(); |
| 361 expect(log, [1, 2, 3]); |
| 362 }); |
| 363 }); |
| 364 test('should not run timers', () { |
| 365 new FakeAsync().run((async) { |
| 366 final log = []; |
| 367 new Future.microtask(() { |
| 368 log.add(1); |
| 369 }); |
| 370 new Future(() { |
| 371 log.add(2); |
| 372 }); |
| 373 new Timer.periodic(new Duration(seconds: 1), (_) { |
| 374 log.add(2); |
| 375 }); |
| 376 async.flushMicrotasks(); |
| 377 expect(log, [1]); |
| 378 }); |
| 379 }); |
| 380 }); |
| 381 |
| 382 group('flushTimers', () { |
| 383 test('should flush timers', () { |
| 384 new FakeAsync().run((async) { |
| 385 final log = []; |
| 386 new Future(() { |
| 387 log.add(2); |
| 388 new Future.delayed(elapseBy, () { |
| 389 log.add(3); |
| 390 }); |
| 391 }); |
| 392 new Future(() { |
| 393 log.add(1); |
| 394 }); |
| 395 expect(log, hasLength(0), reason: 'should not flush until asked to'); |
| 396 async.flushTimers(timeout: elapseBy * 2, flushPeriodicTimers: false); |
| 397 expect(log, [1, 2, 3]); |
| 398 expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); |
| 399 }); |
| 400 }); |
| 401 |
| 402 test('should run collateral periodic timers', () { |
| 403 new FakeAsync().run((async) { |
| 404 final log = []; |
| 405 new Future.delayed(new Duration(seconds: 2), () { |
| 406 log.add('delayed'); |
| 407 }); |
| 408 new Timer.periodic(new Duration(seconds: 1), (_) { |
| 409 log.add('periodic'); |
| 410 }); |
| 411 expect(log, hasLength(0), reason: 'should not flush until asked to'); |
| 412 async.flushTimers(flushPeriodicTimers: false); |
| 413 expect(log, ['periodic', 'periodic', 'delayed']); |
| 414 }); |
| 415 }); |
| 416 |
| 417 test('should timeout', () { |
| 418 new FakeAsync().run((async) { |
| 419 int count = 0; |
| 420 // Schedule 3 timers. All but the last one should fire. |
| 421 for (int delay in [30, 60, 90]) { |
| 422 new Future.delayed(new Duration(minutes: delay), () { |
| 423 count++; |
| 424 }); |
| 425 } |
| 426 expect(() => async.flushTimers(flushPeriodicTimers: false), |
| 427 throwsStateError); |
| 428 expect(count, 2); |
| 429 }); |
| 430 }); |
| 431 |
| 432 test('should timeout a chain of timers', () { |
| 433 new FakeAsync().run((async) { |
| 434 int count = 0; |
| 435 createTimer() { |
| 436 new Future.delayed(new Duration(minutes: 30), () { |
| 437 count++; |
| 438 createTimer(); |
| 439 }); |
| 440 } |
| 441 createTimer(); |
| 442 expect(() => async.flushTimers( |
| 443 timeout: new Duration(hours: 2), flushPeriodicTimers: false), |
| 444 throwsStateError); |
| 445 expect(count, 4); |
| 446 }); |
| 447 }); |
| 448 |
| 449 test('should timeout periodic timers', () { |
| 450 new FakeAsync().run((async) { |
| 451 int count = 0; |
| 452 new Timer.periodic(new Duration(minutes: 30), (Timer timer) { |
| 453 count++; |
| 454 }); |
| 455 expect(() => async.flushTimers(timeout: new Duration(hours: 1)), |
| 456 throwsStateError); |
| 457 expect(count, 2); |
| 458 }); |
| 459 }); |
| 460 |
| 461 test('should flush periodic timers', () { |
| 462 new FakeAsync().run((async) { |
| 463 int count = 0; |
| 464 new Timer.periodic(new Duration(minutes: 30), (Timer timer) { |
| 465 if (count == 3) { |
| 466 timer.cancel(); |
| 467 } |
| 468 count++; |
| 469 }); |
| 470 async.flushTimers(timeout: new Duration(hours: 20)); |
| 471 expect(count, 4); |
| 472 }); |
| 473 }); |
| 474 |
| 475 test('should compute absolute timeout as elapsed + timeout', () { |
| 476 new FakeAsync().run((async) { |
| 477 final log = []; |
| 478 int count = 0; |
| 479 createTimer() { |
| 480 new Future.delayed(new Duration(minutes: 30), () { |
| 481 log.add(count); |
| 482 count++; |
| 483 if (count < 4) { |
| 484 createTimer(); |
| 485 } |
| 486 }); |
| 487 } |
| 488 createTimer(); |
| 489 async.elapse(new Duration(hours: 1)); |
| 490 async.flushTimers(timeout: new Duration(hours: 1)); |
| 491 expect(count, 4); |
| 492 }); |
| 493 }); |
| 494 }); |
| 495 |
| 496 group('stats', () { |
| 497 test('should report the number of pending microtasks', () { |
| 498 new FakeAsync().run((async) { |
| 499 expect(async.microtaskCount, 0); |
| 500 scheduleMicrotask(() => null); |
| 501 expect(async.microtaskCount, 1); |
| 502 scheduleMicrotask(() => null); |
| 503 expect(async.microtaskCount, 2); |
| 504 async.flushMicrotasks(); |
| 505 expect(async.microtaskCount, 0); |
| 506 }); |
| 507 }); |
| 508 |
| 509 test('it should report the number of pending periodic timers', () { |
| 510 new FakeAsync().run((async) { |
| 511 expect(async.periodicTimerCount, 0); |
| 512 Timer timer = new Timer.periodic(new Duration(minutes: 30), |
| 513 (Timer timer) { }); |
| 514 expect(async.periodicTimerCount, 1); |
| 515 new Timer.periodic(new Duration(minutes: 20), (Timer timer) { }); |
| 516 expect(async.periodicTimerCount, 2); |
| 517 async.elapse(new Duration(minutes: 20)); |
| 518 expect(async.periodicTimerCount, 2); |
| 519 timer.cancel(); |
| 520 expect(async.periodicTimerCount, 1); |
| 521 }); |
| 522 }); |
| 523 |
| 524 test('it should report the number of pending non periodic timers', () { |
| 525 new FakeAsync().run((async) { |
| 526 expect(async.nonPeriodicTimerCount, 0); |
| 527 Timer timer = new Timer(new Duration(minutes: 30), () { }); |
| 528 expect(async.nonPeriodicTimerCount, 1); |
| 529 new Timer(new Duration(minutes: 20), () { }); |
| 530 expect(async.nonPeriodicTimerCount, 2); |
| 531 async.elapse(new Duration(minutes: 25)); |
| 532 expect(async.nonPeriodicTimerCount, 1); |
| 533 timer.cancel(); |
| 534 expect(async.nonPeriodicTimerCount, 0); |
| 535 }); |
| 536 }); |
| 537 }); |
| 538 }); |
| 539 } |
OLD | NEW |