build_scope_test.dart 5.77 KB
Newer Older
Hixie's avatar
Hixie committed
1 2 3 4
// 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.

Adam Barth's avatar
Adam Barth committed
5
import 'package:flutter_test/flutter_test.dart';
6
import 'package:flutter/widgets.dart';
7

Adam Barth's avatar
Adam Barth committed
8
import 'test_widgets.dart';
9

10
class ProbeWidget extends StatefulWidget {
11
  @override
12 13 14 15 16 17
  ProbeWidgetState createState() => new ProbeWidgetState();
}

class ProbeWidgetState extends State<ProbeWidget> {
  static int buildCount = 0;

18
  @override
19 20
  void initState() {
    super.initState();
21 22 23
    setState(() {});
  }

24
  @override
25
  void didUpdateWidget(ProbeWidget oldWidget) {
26
    super.didUpdateWidget(oldWidget);
27 28 29
    setState(() {});
  }

30
  @override
31 32 33 34 35 36 37
  Widget build(BuildContext context) {
    setState(() {});
    buildCount++;
    return new Container();
  }
}

38
class BadWidget extends StatelessWidget {
39
  const BadWidget(this.parentState);
40

41
  final BadWidgetParentState parentState;
42

43
  @override
44
  Widget build(BuildContext context) {
45
    parentState._markNeedsBuild();
46 47 48 49
    return new Container();
  }
}

50
class BadWidgetParent extends StatefulWidget {
51
  @override
52 53 54 55
  BadWidgetParentState createState() => new BadWidgetParentState();
}

class BadWidgetParentState extends State<BadWidgetParent> {
56 57 58 59 60 61 62
  void _markNeedsBuild() {
    setState(() {
      // Our state didn't really change, but we're doing something pathological
      // here to trigger an interesting scenario to test.
    });
  }

63
  @override
64 65 66 67 68
  Widget build(BuildContext context) {
    return new BadWidget(this);
  }
}

69
class BadDisposeWidget extends StatefulWidget {
70
  @override
71 72 73 74
  BadDisposeWidgetState createState() => new BadDisposeWidgetState();
}

class BadDisposeWidgetState extends State<BadDisposeWidget> {
75
  @override
76 77 78 79
  Widget build(BuildContext context) {
    return new Container();
  }

80
  @override
81
  void dispose() {
82
    setState(() { /* This is invalid behavior. */ });
83 84 85 86
    super.dispose();
  }
}

87
class StatefulWrapper extends StatefulWidget {
88
  const StatefulWrapper({
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
    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;
114
    return widget.child;
115 116 117 118
  }
}

class Wrapper extends StatelessWidget {
119
  const Wrapper({
120 121 122 123 124 125 126 127 128 129 130 131
    Key key,
    this.child,
  }) : super(key: key);

  final Widget child;

  @override
  Widget build(BuildContext context) {
    return child;
  }
}

132
void main() {
133
  testWidgets('Legal times for setState', (WidgetTester tester) async {
134
    final GlobalKey flipKey = new GlobalKey();
135
    expect(ProbeWidgetState.buildCount, equals(0));
136
    await tester.pumpWidget(new ProbeWidget());
137
    expect(ProbeWidgetState.buildCount, equals(1));
138
    await tester.pumpWidget(new ProbeWidget());
139
    expect(ProbeWidgetState.buildCount, equals(2));
140
    await tester.pumpWidget(new FlipWidget(
141 142 143 144 145
      key: flipKey,
      left: new Container(),
      right: new ProbeWidget()
    ));
    expect(ProbeWidgetState.buildCount, equals(2));
146
    final FlipWidgetState flipState1 = flipKey.currentState;
147
    flipState1.flip();
148
    await tester.pump();
149
    expect(ProbeWidgetState.buildCount, equals(3));
150
    final FlipWidgetState flipState2 = flipKey.currentState;
151
    flipState2.flip();
152
    await tester.pump();
153
    expect(ProbeWidgetState.buildCount, equals(3));
154
    await tester.pumpWidget(new Container());
155
    expect(ProbeWidgetState.buildCount, equals(3));
156 157
  });

158 159
  testWidgets('Setting parent state during build is forbidden', (WidgetTester tester) async {
    await tester.pumpWidget(new BadWidgetParent());
160
    expect(tester.takeException(), isFlutterError);
161
    await tester.pumpWidget(new Container());
162 163
  });

164 165
  testWidgets('Setting state during dispose is forbidden', (WidgetTester tester) async {
    await tester.pumpWidget(new BadDisposeWidget());
166
    expect(tester.takeException(), isNull);
167
    await tester.pumpWidget(new Container());
168
    expect(tester.takeException(), isNotNull);
169
  });
170 171

  testWidgets('Dirty element list sort order', (WidgetTester tester) async {
172 173
    final GlobalKey key1 = new GlobalKey(debugLabel: 'key1');
    final GlobalKey key2 = new GlobalKey(debugLabel: 'key2');
174 175 176

    bool didMiddle = false;
    Widget middle;
177
    final List<StateSetter> setStates = <StateSetter>[];
178 179
    Widget builder(BuildContext context, StateSetter setState) {
      setStates.add(setState);
180
      final bool returnMiddle = !didMiddle;
181 182 183 184 185 186 187 188 189
      didMiddle = true;
      return new Wrapper(
        child: new Wrapper(
          child: new StatefulWrapper(
            child: returnMiddle ? middle : new Container(),
          ),
        ),
      );
    }
190
    final Widget part1 = new Wrapper(
191 192 193 194 195 196 197
      child: new KeyedSubtree(
        key: key1,
        child: new StatefulBuilder(
          builder: builder,
        ),
      ),
    );
198
    final Widget part2 = new Wrapper(
199 200 201 202 203 204 205 206 207 208 209
      child: new KeyedSubtree(
        key: key2,
        child: new StatefulBuilder(
          builder: builder,
        ),
      ),
    );

    middle = part2;
    await tester.pumpWidget(part1);

210
    for (StatefulWrapperState state in tester.stateList<StatefulWrapperState>(find.byType(StatefulWrapper))) {
211 212 213 214 215 216 217 218 219 220 221 222
      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);

223
    for (StatefulWrapperState state in tester.stateList<StatefulWrapperState>(find.byType(StatefulWrapper))) {
224 225 226 227 228
      expect(state.built, isNotNull);
      expect(state.built, isNot(equals(state.oldBuilt)));
    }

  });
229
}