blob: b5836d9d2b17c75f972ee38ad47a0800d92de9ba [file] [log] [blame]
// 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));
});
});
}