Unverified Commit 8a5ace25 authored by Jia Hao's avatar Jia Hao Committed by GitHub

Improve performance of Widget Tests (#70730)

parent 492ec23b
...@@ -1341,6 +1341,10 @@ class _IntrinsicDimensionsCacheEntry { ...@@ -1341,6 +1341,10 @@ class _IntrinsicDimensionsCacheEntry {
/// [computeMinIntrinsicWidth], [computeMaxIntrinsicWidth], /// [computeMinIntrinsicWidth], [computeMaxIntrinsicWidth],
/// [computeMinIntrinsicHeight], [computeMaxIntrinsicHeight]. /// [computeMinIntrinsicHeight], [computeMaxIntrinsicHeight].
/// ///
/// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if you
/// do override any of these methods, which will add additional checks to
/// help validate your implementation.
///
/// In addition, if the box has any children, it must implement /// In addition, if the box has any children, it must implement
/// [computeDistanceToActualBaseline]. [RenderProxyBox] provides a simple /// [computeDistanceToActualBaseline]. [RenderProxyBox] provides a simple
/// implementation that forwards to the child; [RenderShiftedBox] provides an /// implementation that forwards to the child; [RenderShiftedBox] provides an
...@@ -1445,6 +1449,10 @@ abstract class RenderBox extends RenderObject { ...@@ -1445,6 +1449,10 @@ abstract class RenderBox extends RenderObject {
/// ///
/// This function should never return a negative or infinite value. /// This function should never return a negative or infinite value.
/// ///
/// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if
/// you do override this method, which will add additional checks to help
/// validate your implementation.
///
/// ## Examples /// ## Examples
/// ///
/// ### Text /// ### Text
...@@ -1597,6 +1605,10 @@ abstract class RenderBox extends RenderObject { ...@@ -1597,6 +1605,10 @@ abstract class RenderBox extends RenderObject {
/// ///
/// This function should never return a negative or infinite value. /// This function should never return a negative or infinite value.
/// ///
/// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if
/// you do override this method, which will add additional checks to help
/// validate your implementation.
///
/// See also: /// See also:
/// ///
/// * [computeMinIntrinsicWidth], which has usage examples. /// * [computeMinIntrinsicWidth], which has usage examples.
...@@ -1675,6 +1687,10 @@ abstract class RenderBox extends RenderObject { ...@@ -1675,6 +1687,10 @@ abstract class RenderBox extends RenderObject {
/// ///
/// This function should never return a negative or infinite value. /// This function should never return a negative or infinite value.
/// ///
/// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if
/// you do override this method, which will add additional checks to help
/// validate your implementation.
///
/// See also: /// See also:
/// ///
/// * [computeMinIntrinsicWidth], which has usage examples. /// * [computeMinIntrinsicWidth], which has usage examples.
...@@ -1760,6 +1776,10 @@ abstract class RenderBox extends RenderObject { ...@@ -1760,6 +1776,10 @@ abstract class RenderBox extends RenderObject {
/// ///
/// This function should never return a negative or infinite value. /// This function should never return a negative or infinite value.
/// ///
/// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if
/// you do override this method, which will add additional checks to help
/// validate your implementation.
///
/// See also: /// See also:
/// ///
/// * [computeMinIntrinsicWidth], which has usage examples. /// * [computeMinIntrinsicWidth], which has usage examples.
......
...@@ -132,8 +132,9 @@ bool debugPrintLayouts = false; ...@@ -132,8 +132,9 @@ bool debugPrintLayouts = false;
/// Check the intrinsic sizes of each [RenderBox] during layout. /// Check the intrinsic sizes of each [RenderBox] during layout.
/// ///
/// By default this is turned off since these checks are expensive, but it is /// By default this is turned off since these checks are expensive. If you are
/// enabled by the test framework. /// implementing your own children of [RenderBox] with custom intrinsics, turn
/// this on in your unit tests for additional validations.
bool debugCheckIntrinsicSizes = false; bool debugCheckIntrinsicSizes = false;
/// Adds [dart:developer.Timeline] events for every [RenderObject] layout. /// Adds [dart:developer.Timeline] events for every [RenderObject] layout.
......
...@@ -2,5 +2,17 @@ ...@@ -2,5 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
export '_goldens_io.dart' import 'dart:async';
if (dart.library.html) '_goldens_web.dart' show testExecutable;
import 'package:flutter/rendering.dart';
import '_goldens_io.dart'
if (dart.library.html) '_goldens_web.dart' as flutter_goldens;
Future<void> testExecutable(FutureOr<void> testMain()) {
// Enable checks because there are many implementations of [RenderBox] in this
// package can benefit from the additional validations.
debugCheckIntrinsicSizes = true;
return flutter_goldens.testExecutable(testMain);
}
...@@ -60,6 +60,21 @@ class RenderIntrinsicSize extends RenderProxyBox { ...@@ -60,6 +60,21 @@ class RenderIntrinsicSize extends RenderProxyBox {
} }
} }
class RenderInvalidIntrinsics extends RenderBox {
@override
bool get sizedByParent => true;
@override
double computeMinIntrinsicWidth(double height) => -1;
@override
double computeMaxIntrinsicWidth(double height) => -1;
@override
double computeMinIntrinsicHeight(double width) => -1;
@override
double computeMaxIntrinsicHeight(double width) => -1;
@override
Size computeDryLayout(BoxConstraints constraints) => const Size(0, 0);
}
void main() { void main() {
test('Whether using intrinsics means you get hooked into layout', () { test('Whether using intrinsics means you get hooked into layout', () {
RenderBox root; RenderBox root;
...@@ -84,36 +99,49 @@ void main() { ...@@ -84,36 +99,49 @@ void main() {
expect(root.size, equals(inner.size)); expect(root.size, equals(inner.size));
}); });
test('When RenderObject.debugCheckingIntrinsics is true, parent returns correct intrinsics', () { test('Parent returns correct intrinsics', () {
RenderObject.debugCheckingIntrinsics = true; RenderParentSize parent;
RenderFixedSize inner;
try { layout(
RenderParentSize parent; RenderIntrinsicSize(
RenderFixedSize inner; child: parent = RenderParentSize(
child: inner = RenderFixedSize()
)
),
constraints: const BoxConstraints(
minWidth: 0.0,
minHeight: 0.0,
maxWidth: 1000.0,
maxHeight: 1000.0,
),
);
_expectIntrinsicDimensions(parent, 100);
layout( inner.grow();
RenderIntrinsicSize( pumpFrame();
child: parent = RenderParentSize(
child: inner = RenderFixedSize() _expectIntrinsicDimensions(parent, 200);
) });
),
test('Intrinsic checks are turned on', () async {
final List<FlutterErrorDetails> errorDetails = <FlutterErrorDetails>[];
layout(RenderInvalidIntrinsics(),
constraints: const BoxConstraints( constraints: const BoxConstraints(
minWidth: 0.0, minWidth: 0.0,
minHeight: 0.0, minHeight: 0.0,
maxWidth: 1000.0, maxWidth: 1000.0,
maxHeight: 1000.0, maxHeight: 1000.0,
), ), onErrors: () {
); errorDetails.addAll(renderer.takeAllFlutterErrorDetails());
});
_expectIntrinsicDimensions(parent, 100);
expect(errorDetails, isNotEmpty);
inner.grow(); expect(
pumpFrame(); errorDetails.map((FlutterErrorDetails details) => details.toString()),
everyElement(contains('violate the intrinsic protocol')),
_expectIntrinsicDimensions(parent, 200); );
} finally {
RenderObject.debugCheckingIntrinsics = false;
}
}); });
} }
......
...@@ -181,7 +181,6 @@ abstract class TestWidgetsFlutterBinding extends BindingBase ...@@ -181,7 +181,6 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
TestWidgetsFlutterBinding() : _window = TestWindow(window: ui.window) { TestWidgetsFlutterBinding() : _window = TestWindow(window: ui.window) {
debugPrint = debugPrintOverride; debugPrint = debugPrintOverride;
debugDisableShadows = disableShadows; debugDisableShadows = disableShadows;
debugCheckIntrinsicSizes = checkIntrinsicSizes;
} }
@override @override
...@@ -287,14 +286,6 @@ abstract class TestWidgetsFlutterBinding extends BindingBase ...@@ -287,14 +286,6 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
/// equivalent as [Future.delayed]. /// equivalent as [Future.delayed].
Future<void> delayed(Duration duration); Future<void> delayed(Duration duration);
/// The value to set [debugCheckIntrinsicSizes] to while tests are running.
///
/// This can be used to enable additional checks. For example,
/// [AutomatedTestWidgetsFlutterBinding] sets this to true, so that all tests
/// always run with aggressive intrinsic sizing tests enabled.
@protected
bool get checkIntrinsicSizes => false;
/// Creates and initializes the binding. This function is /// Creates and initializes the binding. This function is
/// idempotent; calling it a second time will just return the /// idempotent; calling it a second time will just return the
/// previously-created instance. /// previously-created instance.
...@@ -782,6 +773,8 @@ abstract class TestWidgetsFlutterBinding extends BindingBase ...@@ -782,6 +773,8 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
Future<void> _runTestBody(Future<void> testBody(), VoidCallback invariantTester) async { Future<void> _runTestBody(Future<void> testBody(), VoidCallback invariantTester) async {
assert(inTest); assert(inTest);
// So that we can assert that it remains the same after the test finishes.
_beforeTestCheckIntrinsicSizes = debugCheckIntrinsicSizes;
runApp(Container(key: UniqueKey(), child: _preTestMessage)); // Reset the tree to a known state. runApp(Container(key: UniqueKey(), child: _preTestMessage)); // Reset the tree to a known state.
await pump(); await pump();
...@@ -814,6 +807,8 @@ abstract class TestWidgetsFlutterBinding extends BindingBase ...@@ -814,6 +807,8 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
asyncBarrier(); // When using AutomatedTestWidgetsFlutterBinding, this flushes the microtasks. asyncBarrier(); // When using AutomatedTestWidgetsFlutterBinding, this flushes the microtasks.
} }
late bool _beforeTestCheckIntrinsicSizes;
void _verifyInvariants() { void _verifyInvariants() {
assert(debugAssertNoTransientCallbacks( assert(debugAssertNoTransientCallbacks(
'An animation is still running even after the widget tree was disposed.' 'An animation is still running even after the widget tree was disposed.'
...@@ -831,7 +826,7 @@ abstract class TestWidgetsFlutterBinding extends BindingBase ...@@ -831,7 +826,7 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
)); ));
assert(debugAssertAllRenderVarsUnset( assert(debugAssertAllRenderVarsUnset(
'The value of a rendering debug variable was changed by the test.', 'The value of a rendering debug variable was changed by the test.',
debugCheckIntrinsicSizesOverride: checkIntrinsicSizes, debugCheckIntrinsicSizesOverride: _beforeTestCheckIntrinsicSizes,
)); ));
assert(debugAssertAllWidgetVarsUnset( assert(debugAssertAllWidgetVarsUnset(
'The value of a widget debug variable was changed by the test.', 'The value of a widget debug variable was changed by the test.',
...@@ -944,9 +939,6 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding { ...@@ -944,9 +939,6 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
@override @override
bool get disableShadows => true; bool get disableShadows => true;
@override
bool get checkIntrinsicSizes => true;
/// The value of [defaultTestTimeout] can be set to `None` to enable debugging flutter tests where /// The value of [defaultTestTimeout] can be set to `None` to enable debugging flutter tests where
/// we would not want to timeout the test. This is expected to be used by test tooling which /// we would not want to timeout the test. This is expected to be used by test tooling which
/// can detect debug mode. /// can detect debug mode.
......
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