Test harness should check for running Timers and AnimationControllers
After running a widget test, we now clear out the widget tree and check that we didn't leak any timers or animations. Also, fix several bugs that this addtional check revealed. Fixes #2481
This commit is contained in:
parent
5faf84c3fa
commit
0d7b0f9ec1
@ -22,6 +22,10 @@ void main() {
|
|||||||
tester.setLocale("es", "US");
|
tester.setLocale("es", "US");
|
||||||
tester.pump();
|
tester.pump();
|
||||||
expect(tab.widget.data, equals("MERCADO"));
|
expect(tab.widget.data, equals("MERCADO"));
|
||||||
|
|
||||||
|
// TODO(abarth): We're leaking an animation. We should track down the leak
|
||||||
|
// and plug it rather than waiting for the animation to end here.
|
||||||
|
tester.pump(const Duration(seconds: 1));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,11 @@ class _LinearProgressIndicatorState extends State<LinearProgressIndicator> {
|
|||||||
_animation = new CurvedAnimation(parent: _controller, curve: Curves.fastOutSlowIn);
|
_animation = new CurvedAnimation(parent: _controller, curve: Curves.fastOutSlowIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
_controller.stop();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildIndicator(BuildContext context, double animationValue) {
|
Widget _buildIndicator(BuildContext context, double animationValue) {
|
||||||
return new Container(
|
return new Container(
|
||||||
constraints: new BoxConstraints.tightFor(
|
constraints: new BoxConstraints.tightFor(
|
||||||
@ -209,15 +214,20 @@ final Animatable<int> _kStepTween = new StepTween(begin: 0, end: 5);
|
|||||||
final Animatable<double> _kRotationTween = new CurveTween(curve: new SawTooth(5));
|
final Animatable<double> _kRotationTween = new CurveTween(curve: new SawTooth(5));
|
||||||
|
|
||||||
class _CircularProgressIndicatorState extends State<CircularProgressIndicator> {
|
class _CircularProgressIndicatorState extends State<CircularProgressIndicator> {
|
||||||
AnimationController _animationController;
|
AnimationController _controller;
|
||||||
|
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_animationController = new AnimationController(
|
_controller = new AnimationController(
|
||||||
duration: const Duration(milliseconds: 6666)
|
duration: const Duration(milliseconds: 6666)
|
||||||
)..repeat();
|
)..repeat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
_controller.stop();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildIndicator(BuildContext context, double headValue, double tailValue, int stepValue, double rotationValue) {
|
Widget _buildIndicator(BuildContext context, double headValue, double tailValue, int stepValue, double rotationValue) {
|
||||||
return new Container(
|
return new Container(
|
||||||
constraints: new BoxConstraints(
|
constraints: new BoxConstraints(
|
||||||
@ -242,14 +252,14 @@ class _CircularProgressIndicatorState extends State<CircularProgressIndicator> {
|
|||||||
return _buildIndicator(context, 0.0, 0.0, 0, 0.0);
|
return _buildIndicator(context, 0.0, 0.0, 0, 0.0);
|
||||||
|
|
||||||
return new AnimatedBuilder(
|
return new AnimatedBuilder(
|
||||||
animation: _animationController,
|
animation: _controller,
|
||||||
builder: (BuildContext context, Widget child) {
|
builder: (BuildContext context, Widget child) {
|
||||||
return _buildIndicator(
|
return _buildIndicator(
|
||||||
context,
|
context,
|
||||||
_kStrokeHeadTween.evaluate(_animationController),
|
_kStrokeHeadTween.evaluate(_controller),
|
||||||
_kStrokeTailTween.evaluate(_animationController),
|
_kStrokeTailTween.evaluate(_controller),
|
||||||
_kStepTween.evaluate(_animationController),
|
_kStepTween.evaluate(_controller),
|
||||||
_kRotationTween.evaluate(_animationController)
|
_kRotationTween.evaluate(_controller)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -281,6 +281,7 @@ class ScaffoldState extends State<Scaffold> {
|
|||||||
_snackBarController.status == AnimationStatus.completed);
|
_snackBarController.status == AnimationStatus.completed);
|
||||||
_snackBars.first._completer.complete();
|
_snackBars.first._completer.complete();
|
||||||
_snackBarController.reverse();
|
_snackBarController.reverse();
|
||||||
|
_snackBarTimer?.cancel();
|
||||||
_snackBarTimer = null;
|
_snackBarTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,7 @@ void main() {
|
|||||||
expect(sizes, equals([const Size(10.0, 10.0), const Size(10.0, 10.0), const Size(10.0, 10.0), const Size(10.0, 10.0), const Size(10.0, 10.0), const Size(10.0, 10.0)]));
|
expect(sizes, equals([const Size(10.0, 10.0), const Size(10.0, 10.0), const Size(10.0, 10.0), const Size(10.0, 10.0), const Size(10.0, 10.0), const Size(10.0, 10.0)]));
|
||||||
expect(positions, equals([const Offset(10.0, 10.0), const Offset(10.0, 10.0), const Offset(17.0, 17.0), const Offset(24.0, 24.0), const Offset(45.0, 45.0), const Offset(80.0, 80.0)]));
|
expect(positions, equals([const Offset(10.0, 10.0), const Offset(10.0, 10.0), const Offset(17.0, 17.0), const Offset(24.0, 24.0), const Offset(45.0, 45.0), const Offset(80.0, 80.0)]));
|
||||||
|
|
||||||
|
controller.stop();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -78,7 +78,6 @@ class WidgetTester extends Instrumentation {
|
|||||||
super(binding: _SteppedWidgetFlutterBinding.ensureInitialized()) {
|
super(binding: _SteppedWidgetFlutterBinding.ensureInitialized()) {
|
||||||
timeDilation = 1.0;
|
timeDilation = 1.0;
|
||||||
ui.window.onBeginFrame = null;
|
ui.window.onBeginFrame = null;
|
||||||
runApp(new Container(key: new UniqueKey())); // flush out the last build entirely
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final FakeAsync async;
|
final FakeAsync async;
|
||||||
@ -132,6 +131,23 @@ class WidgetTester extends Instrumentation {
|
|||||||
|
|
||||||
void testWidgets(callback(WidgetTester tester)) {
|
void testWidgets(callback(WidgetTester tester)) {
|
||||||
new FakeAsync().run((FakeAsync async) {
|
new FakeAsync().run((FakeAsync async) {
|
||||||
callback(new WidgetTester._(async));
|
WidgetTester tester = new WidgetTester._(async);
|
||||||
|
runApp(new Container(key: new UniqueKey())); // Reset the tree to a known state.
|
||||||
|
callback(tester);
|
||||||
|
runApp(new Container(key: new UniqueKey())); // Unmount any remaining widgets.
|
||||||
|
async.flushMicrotasks();
|
||||||
|
assert(() {
|
||||||
|
"An animation is still running even after the widget tree was disposed.";
|
||||||
|
return Scheduler.instance.transientCallbackCount == 0;
|
||||||
|
});
|
||||||
|
assert(() {
|
||||||
|
"A Timer is still running even after the widget tree was disposed.";
|
||||||
|
return async.periodicTimerCount == 0;
|
||||||
|
});
|
||||||
|
assert(() {
|
||||||
|
"A Timer is still running even after the widget tree was disposed.";
|
||||||
|
return async.nonPeriodicTimerCount == 0;
|
||||||
|
});
|
||||||
|
assert(async.microtaskCount == 0); // Shouldn't be possible.
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user