Commit 77dd3eba authored by Adam Barth's avatar Adam Barth

Merge pull request #2521 from abarth/test_animation_leaks

Test harness should check for running Timers and AnimationControllers
parents 59bba072 0d7b0f9e
...@@ -22,6 +22,10 @@ void main() { ...@@ -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> { ...@@ -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); ...@@ -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> { ...@@ -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> { ...@@ -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() { ...@@ -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 { ...@@ -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 { ...@@ -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.
}); });
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment