1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/animation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('awaiting animation controllers - using direct future', (WidgetTester tester) async {
final AnimationController controller1 = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
);
final AnimationController controller2 = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: const TestVSync(),
);
final AnimationController controller3 = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: const TestVSync(),
);
final List<String> log = <String>[];
Future<void> runTest() async {
log.add('a'); // t=0
await controller1.forward(); // starts at t=0 again
log.add('b'); // wants to end at t=100 but missed frames until t=150
await controller2.forward(); // starts at t=150
log.add('c'); // wants to end at t=750 but missed frames until t=799
await controller3.forward(); // starts at t=799
log.add('d'); // wants to end at t=1099 but missed frames until t=1200
}
log.add('start');
runTest().then((void value) {
log.add('end');
});
await tester.pump(); // t=0
expect(log, <String>['start', 'a']);
await tester.pump(); // t=0 again
expect(log, <String>['start', 'a']);
await tester.pump(const Duration(milliseconds: 50)); // t=50
expect(log, <String>['start', 'a']);
await tester.pump(const Duration(milliseconds: 100)); // t=150
expect(log, <String>['start', 'a', 'b']);
await tester.pump(const Duration(milliseconds: 50)); // t=200
expect(log, <String>['start', 'a', 'b']);
await tester.pump(const Duration(milliseconds: 400)); // t=600
expect(log, <String>['start', 'a', 'b']);
await tester.pump(const Duration(milliseconds: 199)); // t=799
expect(log, <String>['start', 'a', 'b', 'c']);
await tester.pump(const Duration(milliseconds: 51)); // t=850
expect(log, <String>['start', 'a', 'b', 'c']);
await tester.pump(const Duration(milliseconds: 400)); // t=1200
expect(log, <String>['start', 'a', 'b', 'c', 'd', 'end']);
await tester.pump(const Duration(milliseconds: 400)); // t=1600
expect(log, <String>['start', 'a', 'b', 'c', 'd', 'end']);
});
testWidgets('awaiting animation controllers - using orCancel', (WidgetTester tester) async {
final AnimationController controller1 = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
);
final AnimationController controller2 = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: const TestVSync(),
);
final AnimationController controller3 = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: const TestVSync(),
);
final List<String> log = <String>[];
Future<void> runTest() async {
log.add('a'); // t=0
await controller1.forward().orCancel; // starts at t=0 again
log.add('b'); // wants to end at t=100 but missed frames until t=150
await controller2.forward().orCancel; // starts at t=150
log.add('c'); // wants to end at t=750 but missed frames until t=799
await controller3.forward().orCancel; // starts at t=799
log.add('d'); // wants to end at t=1099 but missed frames until t=1200
}
log.add('start');
runTest().then((void value) {
log.add('end');
});
await tester.pump(); // t=0
expect(log, <String>['start', 'a']);
await tester.pump(); // t=0 again
expect(log, <String>['start', 'a']);
await tester.pump(const Duration(milliseconds: 50)); // t=50
expect(log, <String>['start', 'a']);
await tester.pump(const Duration(milliseconds: 100)); // t=150
expect(log, <String>['start', 'a', 'b']);
await tester.pump(const Duration(milliseconds: 50)); // t=200
expect(log, <String>['start', 'a', 'b']);
await tester.pump(const Duration(milliseconds: 400)); // t=600
expect(log, <String>['start', 'a', 'b']);
await tester.pump(const Duration(milliseconds: 199)); // t=799
expect(log, <String>['start', 'a', 'b', 'c']);
await tester.pump(const Duration(milliseconds: 51)); // t=850
expect(log, <String>['start', 'a', 'b', 'c']);
await tester.pump(const Duration(milliseconds: 400)); // t=1200
expect(log, <String>['start', 'a', 'b', 'c', 'd', 'end']);
await tester.pump(const Duration(milliseconds: 400)); // t=1600
expect(log, <String>['start', 'a', 'b', 'c', 'd', 'end']);
});
testWidgets('awaiting animation controllers and failing', (WidgetTester tester) async {
final AnimationController controller1 = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
);
final List<String> log = <String>[];
Future<void> runTest() async {
try {
log.add('start');
await controller1.forward().orCancel;
log.add('fail');
} on TickerCanceled {
log.add('caught');
}
}
runTest().then((void value) {
log.add('end');
});
await tester.pump(); // start ticker
expect(log, <String>['start']);
await tester.pump(const Duration(milliseconds: 50));
expect(log, <String>['start']);
controller1.dispose();
expect(log, <String>['start']);
await tester.idle();
expect(log, <String>['start', 'caught', 'end']);
});
testWidgets('creating orCancel future later', (WidgetTester tester) async {
final AnimationController controller1 = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
);
final TickerFuture f = controller1.forward();
await tester.pump(); // start ticker
await tester.pump(const Duration(milliseconds: 200)); // end ticker
await f; // should be a no-op
await f.orCancel; // should create a resolved future
expect(true, isTrue); // should reach here
});
testWidgets('creating orCancel future later', (WidgetTester tester) async {
final AnimationController controller1 = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
);
final TickerFuture f = controller1.forward();
await tester.pump(); // start ticker
controller1.stop(); // cancel ticker
bool ok = false;
try {
await f.orCancel; // should create a resolved future
} on TickerCanceled {
ok = true;
}
expect(ok, isTrue); // should reach here
});
testWidgets('TickerFuture is a Future', (WidgetTester tester) async {
final AnimationController controller1 = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
);
final TickerFuture f = controller1.forward();
await tester.pump(); // start ticker
await tester.pump(const Duration(milliseconds: 200)); // end ticker
expect(f.asStream().single, isA<Future<void>>());
await f.catchError((dynamic e) { throw 'do not reach'; });
expect(await f.then<bool>((_) => true), isTrue);
expect(f.whenComplete(() => false), isA<Future<void>>());
expect(f.timeout(const Duration(seconds: 5)), isA<Future<void>>());
});
}