blob: 1d506f79124259adf7ee76d55b0d75012529b2f5 [file] [log] [blame]
// Copyright 2015 The Chromium 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_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
import 'test_widgets.dart';
class ProbeWidget extends StatefulWidget {
@override
ProbeWidgetState createState() => new ProbeWidgetState();
}
class ProbeWidgetState extends State<ProbeWidget> {
static int buildCount = 0;
@override
void initState() {
super.initState();
setState(() {});
}
@override
void didUpdateWidget(ProbeWidget oldWidget) {
super.didUpdateWidget(oldWidget);
setState(() {});
}
@override
Widget build(BuildContext context) {
setState(() {});
buildCount++;
return new Container();
}
}
class BadWidget extends StatelessWidget {
const BadWidget(this.parentState);
final BadWidgetParentState parentState;
@override
Widget build(BuildContext context) {
parentState._markNeedsBuild();
return new Container();
}
}
class BadWidgetParent extends StatefulWidget {
@override
BadWidgetParentState createState() => new BadWidgetParentState();
}
class BadWidgetParentState extends State<BadWidgetParent> {
void _markNeedsBuild() {
setState(() {
// Our state didn't really change, but we're doing something pathological
// here to trigger an interesting scenario to test.
});
}
@override
Widget build(BuildContext context) {
return new BadWidget(this);
}
}
class BadDisposeWidget extends StatefulWidget {
@override
BadDisposeWidgetState createState() => new BadDisposeWidgetState();
}
class BadDisposeWidgetState extends State<BadDisposeWidget> {
@override
Widget build(BuildContext context) {
return new Container();
}
@override
void dispose() {
setState(() { /* This is invalid behavior. */ });
super.dispose();
}
}
class StatefulWrapper extends StatefulWidget {
const StatefulWrapper({
Key key,
this.child,
}) : super(key: key);
final Widget child;
@override
StatefulWrapperState createState() => new StatefulWrapperState();
}
class StatefulWrapperState extends State<StatefulWrapper> {
void trigger() {
setState(() { built = null; });
}
int built;
int oldBuilt;
static int buildId = 0;
@override
Widget build(BuildContext context) {
buildId += 1;
built = buildId;
return widget.child;
}
}
class Wrapper extends StatelessWidget {
const Wrapper({
Key key,
this.child,
}) : super(key: key);
final Widget child;
@override
Widget build(BuildContext context) {
return child;
}
}
void main() {
testWidgets('Legal times for setState', (WidgetTester tester) async {
final GlobalKey flipKey = new GlobalKey();
expect(ProbeWidgetState.buildCount, equals(0));
await tester.pumpWidget(new ProbeWidget());
expect(ProbeWidgetState.buildCount, equals(1));
await tester.pumpWidget(new ProbeWidget());
expect(ProbeWidgetState.buildCount, equals(2));
await tester.pumpWidget(new FlipWidget(
key: flipKey,
left: new Container(),
right: new ProbeWidget()
));
expect(ProbeWidgetState.buildCount, equals(2));
final FlipWidgetState flipState1 = flipKey.currentState;
flipState1.flip();
await tester.pump();
expect(ProbeWidgetState.buildCount, equals(3));
final FlipWidgetState flipState2 = flipKey.currentState;
flipState2.flip();
await tester.pump();
expect(ProbeWidgetState.buildCount, equals(3));
await tester.pumpWidget(new Container());
expect(ProbeWidgetState.buildCount, equals(3));
});
testWidgets('Setting parent state during build is forbidden', (WidgetTester tester) async {
await tester.pumpWidget(new BadWidgetParent());
expect(tester.takeException(), isFlutterError);
await tester.pumpWidget(new Container());
});
testWidgets('Setting state during dispose is forbidden', (WidgetTester tester) async {
await tester.pumpWidget(new BadDisposeWidget());
expect(tester.takeException(), isNull);
await tester.pumpWidget(new Container());
expect(tester.takeException(), isNotNull);
});
testWidgets('Dirty element list sort order', (WidgetTester tester) async {
final GlobalKey key1 = new GlobalKey(debugLabel: 'key1');
final GlobalKey key2 = new GlobalKey(debugLabel: 'key2');
bool didMiddle = false;
Widget middle;
final List<StateSetter> setStates = <StateSetter>[];
Widget builder(BuildContext context, StateSetter setState) {
setStates.add(setState);
final bool returnMiddle = !didMiddle;
didMiddle = true;
return new Wrapper(
child: new Wrapper(
child: new StatefulWrapper(
child: returnMiddle ? middle : new Container(),
),
),
);
}
final Widget part1 = new Wrapper(
child: new KeyedSubtree(
key: key1,
child: new StatefulBuilder(
builder: builder,
),
),
);
final Widget part2 = new Wrapper(
child: new KeyedSubtree(
key: key2,
child: new StatefulBuilder(
builder: builder,
),
),
);
middle = part2;
await tester.pumpWidget(part1);
for (StatefulWrapperState state in tester.stateList<StatefulWrapperState>(find.byType(StatefulWrapper))) {
expect(state.built, isNotNull);
state.oldBuilt = state.built;
state.trigger();
}
for (StateSetter setState in setStates)
setState(() { });
StatefulWrapperState.buildId = 0;
middle = part1;
didMiddle = false;
await tester.pumpWidget(part2);
for (StatefulWrapperState state in tester.stateList<StatefulWrapperState>(find.byType(StatefulWrapper))) {
expect(state.built, isNotNull);
expect(state.built, isNot(equals(state.oldBuilt)));
}
});
}