| // Copyright (c) 2018, 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. |
| |
| // ignore_for_file: only_throw_errors, inference_failure_on_instance_creation |
| |
| import 'dart:async'; |
| |
| import 'package:test/test.dart'; |
| import 'package:timing/src/clock.dart'; |
| import 'package:timing/src/timing.dart'; |
| |
| void _noop() {} |
| |
| void main() { |
| late DateTime time; |
| final startTime = DateTime(2017); |
| DateTime fakeClock() => time; |
| |
| late TimeTracker tracker; |
| late TimeTracker nestedTracker; |
| |
| T scopedTrack<T>(T Function() f) => |
| scopeClock(fakeClock, () => tracker.track(f)); |
| |
| setUp(() { |
| time = startTime; |
| }); |
| |
| void canHandleSync([void Function() additionalExpects = _noop]) { |
| test('Can track sync code', () { |
| expect(tracker.isStarted, false); |
| expect(tracker.isTracking, false); |
| expect(tracker.isFinished, false); |
| scopedTrack(() { |
| expect(tracker.isStarted, true); |
| expect(tracker.isTracking, true); |
| expect(tracker.isFinished, false); |
| time = time.add(const Duration(seconds: 5)); |
| }); |
| expect(tracker.isStarted, true); |
| expect(tracker.isTracking, false); |
| expect(tracker.isFinished, true); |
| expect(tracker.startTime, startTime); |
| expect(tracker.stopTime, time); |
| expect(tracker.duration, const Duration(seconds: 5)); |
| additionalExpects(); |
| }); |
| |
| test('Can track handled sync exceptions', () async { |
| scopedTrack(() { |
| try { |
| time = time.add(const Duration(seconds: 4)); |
| throw 'error'; |
| } on String { |
| time = time.add(const Duration(seconds: 1)); |
| } |
| }); |
| expect(tracker.isFinished, true); |
| expect(tracker.startTime, startTime); |
| expect(tracker.stopTime, time); |
| expect(tracker.duration, const Duration(seconds: 5)); |
| additionalExpects(); |
| }); |
| |
| test('Can track in case of unhandled sync exceptions', () async { |
| expect( |
| () => scopedTrack(() { |
| time = time.add(const Duration(seconds: 5)); |
| throw 'error'; |
| }), |
| throwsA(const TypeMatcher<String>())); |
| expect(tracker.startTime, startTime); |
| expect(tracker.stopTime, time); |
| expect(tracker.duration, const Duration(seconds: 5)); |
| additionalExpects(); |
| }); |
| |
| test('Can be nested sync', () { |
| scopedTrack(() { |
| time = time.add(const Duration(seconds: 1)); |
| nestedTracker.track(() { |
| time = time.add(const Duration(seconds: 2)); |
| }); |
| time = time.add(const Duration(seconds: 4)); |
| }); |
| expect(tracker.isFinished, true); |
| expect(tracker.startTime, startTime); |
| expect(tracker.stopTime, time); |
| expect(tracker.duration, const Duration(seconds: 7)); |
| expect(nestedTracker.startTime.isAfter(startTime), true); |
| expect(nestedTracker.stopTime.isBefore(time), true); |
| expect(nestedTracker.duration, const Duration(seconds: 2)); |
| additionalExpects(); |
| }); |
| } |
| |
| void canHandleAsync([void Function() additionalExpects = _noop]) { |
| test('Can track async code', () async { |
| expect(tracker.isStarted, false); |
| expect(tracker.isTracking, false); |
| expect(tracker.isFinished, false); |
| await scopedTrack(() => Future(() { |
| expect(tracker.isStarted, true); |
| expect(tracker.isTracking, true); |
| expect(tracker.isFinished, false); |
| time = time.add(const Duration(seconds: 5)); |
| })); |
| expect(tracker.isStarted, true); |
| expect(tracker.isTracking, false); |
| expect(tracker.isFinished, true); |
| expect(tracker.startTime, startTime); |
| expect(tracker.stopTime, time); |
| expect(tracker.duration, const Duration(seconds: 5)); |
| additionalExpects(); |
| }); |
| |
| test('Can track handled async exceptions', () async { |
| await scopedTrack(() { |
| time = time.add(const Duration(seconds: 1)); |
| return Future(() { |
| time = time.add(const Duration(seconds: 2)); |
| throw 'error'; |
| }).then((_) { |
| time = time.add(const Duration(seconds: 4)); |
| }).catchError((error, stack) { |
| time = time.add(const Duration(seconds: 8)); |
| }); |
| }); |
| expect(tracker.isFinished, true); |
| expect(tracker.startTime, startTime); |
| expect(tracker.stopTime, time); |
| expect(tracker.duration, const Duration(seconds: 11)); |
| additionalExpects(); |
| }); |
| |
| test('Can track in case of unhandled async exceptions', () async { |
| final future = scopedTrack(() { |
| time = time.add(const Duration(seconds: 1)); |
| return Future(() { |
| time = time.add(const Duration(seconds: 2)); |
| throw 'error'; |
| }).then((_) { |
| time = time.add(const Duration(seconds: 4)); |
| }); |
| }); |
| await expectLater(future, throwsA(const TypeMatcher<String>())); |
| expect(tracker.isFinished, true); |
| expect(tracker.startTime, startTime); |
| expect(tracker.stopTime, time); |
| expect(tracker.duration, const Duration(seconds: 3)); |
| additionalExpects(); |
| }); |
| |
| test('Can be nested async', () async { |
| await scopedTrack(() async { |
| time = time.add(const Duration(milliseconds: 1)); |
| await Future.value(); |
| time = time.add(const Duration(milliseconds: 2)); |
| await nestedTracker.track(() async { |
| time = time.add(const Duration(milliseconds: 4)); |
| await Future.value(); |
| time = time.add(const Duration(milliseconds: 8)); |
| await Future.value(); |
| time = time.add(const Duration(milliseconds: 16)); |
| }); |
| time = time.add(const Duration(milliseconds: 32)); |
| await Future.value(); |
| time = time.add(const Duration(milliseconds: 64)); |
| }); |
| expect(tracker.isFinished, true); |
| expect(tracker.startTime, startTime); |
| expect(tracker.stopTime, time); |
| expect(tracker.duration, const Duration(milliseconds: 127)); |
| expect(nestedTracker.startTime.isAfter(startTime), true); |
| expect(nestedTracker.stopTime.isBefore(time), true); |
| expect(nestedTracker.duration, const Duration(milliseconds: 28)); |
| additionalExpects(); |
| }); |
| } |
| |
| group('SyncTimeTracker', () { |
| setUp(() { |
| tracker = SyncTimeTracker(); |
| nestedTracker = SyncTimeTracker(); |
| }); |
| |
| canHandleSync(); |
| |
| test('Can not track async code', () async { |
| await scopedTrack(() => Future(() { |
| time = time.add(const Duration(seconds: 5)); |
| })); |
| expect(tracker.isFinished, true); |
| expect(tracker.startTime, startTime); |
| expect(tracker.stopTime, startTime); |
| expect(tracker.duration, const Duration(seconds: 0)); |
| }); |
| }); |
| |
| group('AsyncTimeTracker.simple', () { |
| setUp(() { |
| tracker = SimpleAsyncTimeTracker(); |
| nestedTracker = SimpleAsyncTimeTracker(); |
| }); |
| |
| canHandleSync(); |
| |
| canHandleAsync(); |
| |
| test('Can not distinguish own async code', () async { |
| final future = scopedTrack(() => Future(() { |
| time = time.add(const Duration(seconds: 5)); |
| })); |
| time = time.add(const Duration(seconds: 10)); |
| await future; |
| expect(tracker.isFinished, true); |
| expect(tracker.startTime, startTime); |
| expect(tracker.stopTime, time); |
| expect(tracker.duration, const Duration(seconds: 15)); |
| }); |
| }); |
| |
| group('AsyncTimeTracker', () { |
| late AsyncTimeTracker asyncTracker; |
| late AsyncTimeTracker nestedAsyncTracker; |
| setUp(() { |
| tracker = asyncTracker = AsyncTimeTracker(); |
| nestedTracker = nestedAsyncTracker = AsyncTimeTracker(); |
| }); |
| |
| canHandleSync(() { |
| expect(asyncTracker.innerDuration, asyncTracker.duration); |
| expect(asyncTracker.slices.length, 1); |
| }); |
| |
| canHandleAsync(() { |
| expect(asyncTracker.innerDuration, asyncTracker.duration); |
| expect(asyncTracker.slices.length, greaterThan(1)); |
| }); |
| |
| test('Can track complex async innerDuration', () async { |
| final completer = Completer(); |
| final future = scopedTrack(() async { |
| time = time.add(const Duration(seconds: 1)); // Tracked sync |
| await Future.value(); |
| time = time.add(const Duration(seconds: 2)); // Tracked async |
| await completer.future; |
| time = time.add(const Duration(seconds: 4)); // Tracked async, delayed |
| }).then((_) { |
| time = time.add(const Duration(seconds: 8)); // Async, after tracking |
| }); |
| time = time.add(const Duration(seconds: 16)); // Sync, between slices |
| |
| await Future(() { |
| // Async, between slices |
| time = time.add(const Duration(seconds: 32)); |
| completer.complete(); |
| }); |
| await future; |
| expect(asyncTracker.isFinished, true); |
| expect(asyncTracker.startTime, startTime); |
| expect(asyncTracker.stopTime.isBefore(time), true); |
| expect(asyncTracker.duration, const Duration(seconds: 55)); |
| expect(asyncTracker.innerDuration, const Duration(seconds: 7)); |
| expect(asyncTracker.slices.length, greaterThan(1)); |
| }); |
| |
| test('Can exclude nested sync', () { |
| tracker = asyncTracker = AsyncTimeTracker(trackNested: false); |
| scopedTrack(() { |
| time = time.add(const Duration(seconds: 1)); |
| nestedAsyncTracker.track(() { |
| time = time.add(const Duration(seconds: 2)); |
| }); |
| time = time.add(const Duration(seconds: 4)); |
| }); |
| expect(asyncTracker.isFinished, true); |
| expect(asyncTracker.startTime, startTime); |
| expect(asyncTracker.stopTime, time); |
| expect(asyncTracker.duration, const Duration(seconds: 7)); |
| expect(asyncTracker.innerDuration, const Duration(seconds: 5)); |
| expect(asyncTracker.slices.length, greaterThan(1)); |
| expect(nestedAsyncTracker.startTime.isAfter(startTime), true); |
| expect(nestedAsyncTracker.stopTime.isBefore(time), true); |
| expect(nestedAsyncTracker.duration, const Duration(seconds: 2)); |
| expect(nestedAsyncTracker.innerDuration, const Duration(seconds: 2)); |
| expect(nestedAsyncTracker.slices.length, 1); |
| }); |
| |
| test('Can exclude complex nested sync', () { |
| tracker = asyncTracker = AsyncTimeTracker(trackNested: false); |
| nestedAsyncTracker = AsyncTimeTracker(trackNested: false); |
| final nestedAsyncTracker2 = AsyncTimeTracker(trackNested: false); |
| scopedTrack(() { |
| time = time.add(const Duration(seconds: 1)); |
| nestedAsyncTracker.track(() { |
| time = time.add(const Duration(seconds: 2)); |
| nestedAsyncTracker2.track(() { |
| time = time.add(const Duration(seconds: 4)); |
| }); |
| time = time.add(const Duration(seconds: 8)); |
| }); |
| time = time.add(const Duration(seconds: 16)); |
| }); |
| expect(asyncTracker.isFinished, true); |
| expect(asyncTracker.startTime, startTime); |
| expect(asyncTracker.stopTime, time); |
| expect(asyncTracker.duration, const Duration(seconds: 31)); |
| expect(asyncTracker.innerDuration, const Duration(seconds: 17)); |
| expect(asyncTracker.slices.length, greaterThan(1)); |
| expect(nestedAsyncTracker.startTime.isAfter(startTime), true); |
| expect(nestedAsyncTracker.stopTime.isBefore(time), true); |
| expect(nestedAsyncTracker.duration, const Duration(seconds: 14)); |
| expect(nestedAsyncTracker.innerDuration, const Duration(seconds: 10)); |
| expect(nestedAsyncTracker.slices.length, greaterThan(1)); |
| expect(nestedAsyncTracker2.startTime.isAfter(startTime), true); |
| expect(nestedAsyncTracker2.stopTime.isBefore(time), true); |
| expect(nestedAsyncTracker2.duration, const Duration(seconds: 4)); |
| expect(nestedAsyncTracker2.innerDuration, const Duration(seconds: 4)); |
| expect(nestedAsyncTracker2.slices.length, 1); |
| }); |
| |
| test( |
| 'Can track all on grand-parent level and ' |
| 'exclude grand-childrens from parent', () { |
| tracker = asyncTracker = AsyncTimeTracker(trackNested: true); |
| nestedAsyncTracker = AsyncTimeTracker(trackNested: false); |
| final nestedAsyncTracker2 = AsyncTimeTracker(); |
| scopedTrack(() { |
| time = time.add(const Duration(seconds: 1)); |
| nestedAsyncTracker.track(() { |
| time = time.add(const Duration(seconds: 2)); |
| nestedAsyncTracker2.track(() { |
| time = time.add(const Duration(seconds: 4)); |
| }); |
| time = time.add(const Duration(seconds: 8)); |
| }); |
| time = time.add(const Duration(seconds: 16)); |
| }); |
| expect(asyncTracker.isFinished, true); |
| expect(asyncTracker.startTime, startTime); |
| expect(asyncTracker.stopTime, time); |
| expect(asyncTracker.duration, const Duration(seconds: 31)); |
| expect(asyncTracker.innerDuration, const Duration(seconds: 31)); |
| expect(asyncTracker.slices.length, 1); |
| expect(nestedAsyncTracker.startTime.isAfter(startTime), true); |
| expect(nestedAsyncTracker.stopTime.isBefore(time), true); |
| expect(nestedAsyncTracker.duration, const Duration(seconds: 14)); |
| expect(nestedAsyncTracker.innerDuration, const Duration(seconds: 10)); |
| expect(nestedAsyncTracker.slices.length, greaterThan(1)); |
| expect(nestedAsyncTracker2.startTime.isAfter(startTime), true); |
| expect(nestedAsyncTracker2.stopTime.isBefore(time), true); |
| expect(nestedAsyncTracker2.duration, const Duration(seconds: 4)); |
| expect(nestedAsyncTracker2.innerDuration, const Duration(seconds: 4)); |
| expect(nestedAsyncTracker2.slices.length, 1); |
| }); |
| |
| test('Can exclude nested async', () async { |
| tracker = asyncTracker = AsyncTimeTracker(trackNested: false); |
| await scopedTrack(() async { |
| time = time.add(const Duration(seconds: 1)); |
| await nestedAsyncTracker.track(() async { |
| time = time.add(const Duration(seconds: 2)); |
| await Future.value(); |
| time = time.add(const Duration(seconds: 4)); |
| await Future.value(); |
| time = time.add(const Duration(seconds: 8)); |
| }); |
| time = time.add(const Duration(seconds: 16)); |
| }); |
| expect(asyncTracker.isFinished, true); |
| expect(asyncTracker.startTime, startTime); |
| expect(asyncTracker.stopTime, time); |
| expect(asyncTracker.duration, const Duration(seconds: 31)); |
| expect(asyncTracker.innerDuration, const Duration(seconds: 17)); |
| expect(asyncTracker.slices.length, greaterThan(1)); |
| expect(nestedAsyncTracker.startTime.isAfter(startTime), true); |
| expect(nestedAsyncTracker.stopTime.isBefore(time), true); |
| expect(nestedAsyncTracker.duration, const Duration(seconds: 14)); |
| expect(nestedAsyncTracker.innerDuration, const Duration(seconds: 14)); |
| expect(nestedAsyncTracker.slices.length, greaterThan(1)); |
| }); |
| |
| test('Can handle callbacks in excluded nested async', () async { |
| tracker = asyncTracker = AsyncTimeTracker(trackNested: false); |
| await scopedTrack(() async { |
| time = time.add(const Duration(seconds: 1)); |
| final completer = Completer(); |
| final future = completer.future.then((_) { |
| time = time.add(const Duration(seconds: 2)); |
| }); |
| await nestedAsyncTracker.track(() async { |
| time = time.add(const Duration(seconds: 4)); |
| await Future.value(); |
| time = time.add(const Duration(seconds: 8)); |
| completer.complete(); |
| await future; |
| time = time.add(const Duration(seconds: 16)); |
| }); |
| time = time.add(const Duration(seconds: 32)); |
| }); |
| expect(asyncTracker.isFinished, true); |
| expect(asyncTracker.startTime, startTime); |
| expect(asyncTracker.stopTime, time); |
| expect(asyncTracker.duration, const Duration(seconds: 63)); |
| expect(asyncTracker.innerDuration, const Duration(seconds: 35)); |
| expect(asyncTracker.slices.length, greaterThan(1)); |
| expect(nestedAsyncTracker.startTime.isAfter(startTime), true); |
| expect(nestedAsyncTracker.stopTime.isBefore(time), true); |
| expect(nestedAsyncTracker.duration, const Duration(seconds: 30)); |
| expect(nestedAsyncTracker.innerDuration, const Duration(seconds: 28)); |
| expect(nestedAsyncTracker.slices.length, greaterThan(1)); |
| }); |
| }); |
| } |