reattach_test.dart 7.12 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/rendering.dart';
6
import 'package:flutter_test/flutter_test.dart';
7 8 9 10 11

import 'rendering_tester.dart';

class TestTree {
  TestTree() {
12
    // incoming constraints are tight 800x600
13 14
    root = RenderPositionedBox(
      child: RenderConstrainedBox(
15 16 17 18 19
        additionalConstraints: const BoxConstraints.tightFor(width: 800.0),
        // Place the child to be evaluated within both a repaint boundary and a
        // layout-root element (in this case a tightly constrained box). Otherwise
        // the act of transplanting the root into a new container will cause the
        // relayout/repaint of the new parent node to satisfy the test.
20 21
        child: RenderRepaintBoundary(
          child: RenderConstrainedBox(
22
            additionalConstraints: const BoxConstraints.tightFor(height: 20.0, width: 20.0),
23 24 25
            child: RenderRepaintBoundary(
              child: RenderCustomPaint(
                painter: TestCallbackPainter(
26 27
                  onPaint: () { painted = true; },
                ),
28 29
                child: RenderPositionedBox(
                  child: child = RenderConstrainedBox(
30
                    additionalConstraints: const BoxConstraints.tightFor(height: 20.0, width: 20.0),
31 32 33 34
                    child: RenderSemanticsAnnotations(
                      textDirection: TextDirection.ltr,
                      properties: const SemanticsProperties(label: 'Hello there foo'),
                    ),
35 36
                  ),
                ),
37
              ),
38 39 40 41
            ),
          ),
        ),
      ),
42 43
    );
  }
44 45
  late RenderBox root;
  late RenderConstrainedBox child;
46 47 48 49
  bool painted = false;
}

class MutableCompositor extends RenderProxyBox {
50
  MutableCompositor({ required RenderBox child }) : super(child);
51 52 53 54 55 56 57
  bool _alwaysComposite = false;
  @override
  bool get alwaysNeedsCompositing => _alwaysComposite;
}

class TestCompositingBitsTree {
  TestCompositingBitsTree() {
58
    // incoming constraints are tight 800x600
59 60
    root = RenderPositionedBox(
      child: RenderConstrainedBox(
61 62 63 64
        additionalConstraints: const BoxConstraints.tightFor(width: 800.0),
        // Place the child to be evaluated within a repaint boundary. Otherwise
        // the act of transplanting the root into a new container will cause the
        // repaint of the new parent node to satisfy the test.
65 66 67 68
        child: RenderRepaintBoundary(
          child: compositor = MutableCompositor(
            child: RenderCustomPaint(
              painter: TestCallbackPainter(
69 70
                onPaint: () { painted = true; },
              ),
71
              child: child = RenderConstrainedBox(
72
                additionalConstraints: const BoxConstraints.tightFor(height: 20.0, width: 20.0),
73
              ),
74
            ),
75 76 77
          ),
        ),
      ),
78 79
    );
  }
80 81 82
  late RenderBox root;
  late MutableCompositor compositor;
  late RenderConstrainedBox child;
83 84 85 86
  bool painted = false;
}

void main() {
87 88
  TestRenderingFlutterBinding.ensureInitialized();

89
  test('objects can be detached and re-attached: layout', () {
90
    final TestTree testTree = TestTree();
91
    // Lay out
92
    layout(testTree.root);
93 94
    expect(testTree.child.size, equals(const Size(20.0, 20.0)));
    // Remove testTree from the custom render view
95
    TestRenderingFlutterBinding.instance.renderView.child = null;
96 97 98
    expect(testTree.child.owner, isNull);
    // Dirty one of the elements
    testTree.child.additionalConstraints =
99
      const BoxConstraints.tightFor(height: 5.0, width: 5.0);
100
    // Lay out again
101
    layout(testTree.root);
102 103 104
    expect(testTree.child.size, equals(const Size(5.0, 5.0)));
  });
  test('objects can be detached and re-attached: compositingBits', () {
105
    final TestCompositingBitsTree testTree = TestCompositingBitsTree();
106 107 108 109
    // Lay out, composite, and paint
    layout(testTree.root, phase: EnginePhase.paint);
    expect(testTree.painted, isTrue);
    // Remove testTree from the custom render view
110
    TestRenderingFlutterBinding.instance.renderView.child = null;
111 112 113 114 115 116 117 118 119 120
    expect(testTree.child.owner, isNull);
    // Dirty one of the elements
    testTree.compositor._alwaysComposite = true;
    testTree.child.markNeedsCompositingBitsUpdate();
    testTree.painted = false;
    // Lay out, composite, and paint again
    layout(testTree.root, phase: EnginePhase.paint);
    expect(testTree.painted, isTrue);
  });
  test('objects can be detached and re-attached: paint', () {
121
    final TestTree testTree = TestTree();
122 123 124 125
    // Lay out, composite, and paint
    layout(testTree.root, phase: EnginePhase.paint);
    expect(testTree.painted, isTrue);
    // Remove testTree from the custom render view
126
    TestRenderingFlutterBinding.instance.renderView.child = null;
127 128 129 130 131 132 133 134
    expect(testTree.child.owner, isNull);
    // Dirty one of the elements
    testTree.child.markNeedsPaint();
    testTree.painted = false;
    // Lay out, composite, and paint again
    layout(testTree.root, phase: EnginePhase.paint);
    expect(testTree.painted, isTrue);
  });
135
  test('objects can be detached and re-attached: semantics (no change)', () {
136
    final TestTree testTree = TestTree();
137
    int semanticsUpdateCount = 0;
138
    final SemanticsHandle semanticsHandle = TestRenderingFlutterBinding.instance.pipelineOwner.ensureSemantics(
139 140
      listener: () {
        ++semanticsUpdateCount;
141
      },
142
    );
143
    // Lay out, composite, paint, and update semantics
144
    layout(testTree.root, phase: EnginePhase.flushSemantics);
145
    expect(semanticsUpdateCount, 1);
146
    // Remove testTree from the custom render view
147
    TestRenderingFlutterBinding.instance.renderView.child = null;
148 149
    expect(testTree.child.owner, isNull);
    // Dirty one of the elements
150
    semanticsUpdateCount = 0;
151
    testTree.child.markNeedsSemanticsUpdate();
152
    expect(semanticsUpdateCount, 0);
153
    // Lay out, composite, paint, and update semantics again
154
    layout(testTree.root, phase: EnginePhase.flushSemantics);
155
    expect(semanticsUpdateCount, 0); // no semantics have changed.
156 157 158
    semanticsHandle.dispose();
  });
  test('objects can be detached and re-attached: semantics (with change)', () {
159
    final TestTree testTree = TestTree();
160
    int semanticsUpdateCount = 0;
161
    final SemanticsHandle semanticsHandle = TestRenderingFlutterBinding.instance.pipelineOwner.ensureSemantics(
162 163 164
      listener: () {
        ++semanticsUpdateCount;
      },
165 166 167
    );
    // Lay out, composite, paint, and update semantics
    layout(testTree.root, phase: EnginePhase.flushSemantics);
168
    expect(semanticsUpdateCount, 1);
169
    // Remove testTree from the custom render view
170
    TestRenderingFlutterBinding.instance.renderView.child = null;
171 172 173 174 175 176 177 178
    expect(testTree.child.owner, isNull);
    // Dirty one of the elements
    semanticsUpdateCount = 0;
    testTree.child.additionalConstraints = const BoxConstraints.tightFor(height: 20.0, width: 30.0);
    testTree.child.markNeedsSemanticsUpdate();
    expect(semanticsUpdateCount, 0);
    // Lay out, composite, paint, and update semantics again
    layout(testTree.root, phase: EnginePhase.flushSemantics);
179
    expect(semanticsUpdateCount, 1); // semantics have changed.
180
    semanticsHandle.dispose();
181 182
  });
}