Add tests for ParentData
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:sky' as sky;
|
||||
import 'package:sky/animation.dart';
|
||||
import 'package:sky/rendering.dart';
|
||||
import 'package:sky/src/fn3/framework.dart';
|
||||
@@ -156,8 +155,12 @@ class RenderObjectToWidgetElement<T extends RenderObject> extends RenderObjectEl
|
||||
renderObject.child = child;
|
||||
}
|
||||
|
||||
void moveChildRenderObject(RenderObject child, dynamic slot) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void removeChildRenderObject(RenderObject child) {
|
||||
assert(renderObject.child == child);
|
||||
renderObject.child = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,6 +361,8 @@ abstract class ParentDataWidget extends ProxyWidget {
|
||||
ParentDataWidget({ Key key, Widget child })
|
||||
: super(key: key, child: child);
|
||||
|
||||
ParentDataElement createElement() => new ParentDataElement(this);
|
||||
|
||||
/// Subclasses should override this function to ensure that they are placed
|
||||
/// inside widgets that expect them.
|
||||
///
|
||||
@@ -600,6 +602,11 @@ abstract class Element<T extends Widget> implements BuildContext {
|
||||
typedef Widget WidgetBuilder(BuildContext context);
|
||||
typedef void BuildScheduler(BuildableElement element);
|
||||
|
||||
class ErrorWidget extends LeafRenderObjectWidget {
|
||||
RenderBox createRenderObject() => new RenderErrorBox();
|
||||
void updateRenderObject(RenderObject renderObject, RenderObjectWidget oldWidget) { }
|
||||
}
|
||||
|
||||
/// Base class for the instantiation of StatelessComponent and StatefulComponent
|
||||
/// widgets.
|
||||
abstract class BuildableElement<T extends Widget> extends Element<T> {
|
||||
@@ -637,8 +644,17 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
|
||||
assert(built != null);
|
||||
} catch (e, stack) {
|
||||
_debugReportException('building $this', e, stack);
|
||||
built = new ErrorWidget();
|
||||
}
|
||||
|
||||
try {
|
||||
_child = updateChild(_child, built, _slot);
|
||||
assert(_child != null);
|
||||
} catch (e, stack) {
|
||||
_debugReportException('building $this', e, stack);
|
||||
built = new ErrorWidget();
|
||||
_child = updateChild(null, built, _slot);
|
||||
}
|
||||
_child = updateChild(_child, built, _slot);
|
||||
}
|
||||
|
||||
static BuildScheduler scheduleBuildFor;
|
||||
@@ -721,6 +737,23 @@ class StatefulComponentElement extends BuildableElement<StatefulComponent> {
|
||||
class ParentDataElement extends StatelessComponentElement<ParentDataWidget> {
|
||||
ParentDataElement(ParentDataWidget widget) : super(widget);
|
||||
|
||||
void mount(Element parent, dynamic slot) {
|
||||
assert(() {
|
||||
Element ancestor = parent;
|
||||
while (ancestor is! RenderObjectElement) {
|
||||
assert(ancestor != null);
|
||||
assert(() {
|
||||
'You cannot nest parent data widgets inside one another.';
|
||||
return ancestor is! ParentDataElement;
|
||||
});
|
||||
ancestor = ancestor._parent;
|
||||
}
|
||||
_widget.debugValidateAncestor(ancestor._widget);
|
||||
return true;
|
||||
});
|
||||
super.mount(parent, slot);
|
||||
}
|
||||
|
||||
void update(ParentDataWidget newWidget) {
|
||||
ParentDataWidget oldWidget = widget;
|
||||
super.update(newWidget);
|
||||
@@ -813,10 +846,6 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element
|
||||
}
|
||||
|
||||
void updateParentData(ParentDataWidget parentData) {
|
||||
assert(() {
|
||||
parentData.debugValidateAncestor(_ancestorRenderObjectElement.widget);
|
||||
return true;
|
||||
});
|
||||
parentData.applyParentData(renderObject);
|
||||
}
|
||||
|
||||
@@ -1125,12 +1154,14 @@ typedef void WidgetsExceptionHandler(String context, dynamic exception, StackTra
|
||||
/// [debugDumpApp()].
|
||||
WidgetsExceptionHandler debugWidgetsExceptionHandler;
|
||||
void _debugReportException(String context, dynamic exception, StackTrace stack) {
|
||||
print('------------------------------------------------------------------------');
|
||||
'Exception caught while $context'.split('\n').forEach(print);
|
||||
print('$exception');
|
||||
print('Stack trace:');
|
||||
'$stack'.split('\n').forEach(print);
|
||||
if (debugWidgetsExceptionHandler != null)
|
||||
if (debugWidgetsExceptionHandler != null) {
|
||||
debugWidgetsExceptionHandler(context, exception, stack);
|
||||
print('------------------------------------------------------------------------');
|
||||
} else {
|
||||
print('------------------------------------------------------------------------');
|
||||
'Exception caught while $context'.split('\n').forEach(print);
|
||||
print('$exception');
|
||||
print('Stack trace:');
|
||||
'$stack'.split('\n').forEach(print);
|
||||
print('------------------------------------------------------------------------');
|
||||
}
|
||||
}
|
||||
|
||||
287
packages/unit/test/fn3/parent_data_test.dart
Normal file
287
packages/unit/test/fn3/parent_data_test.dart
Normal file
@@ -0,0 +1,287 @@
|
||||
import 'package:sky/rendering.dart';
|
||||
import 'package:sky/src/fn3.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'test_widgets.dart';
|
||||
import 'widget_tester.dart';
|
||||
|
||||
class TestParentData {
|
||||
TestParentData({ this.top, this.right, this.bottom, this.left });
|
||||
|
||||
final double top;
|
||||
final double right;
|
||||
final double bottom;
|
||||
final double left;
|
||||
}
|
||||
|
||||
void checkTree(WidgetTester tester, List<TestParentData> expectedParentData) {
|
||||
MultiChildRenderObjectElement element =
|
||||
tester.findElement((element) => element is MultiChildRenderObjectElement);
|
||||
expect(element, isNotNull);
|
||||
expect(element.renderObject is RenderStack, isTrue);
|
||||
RenderStack renderObject = element.renderObject;
|
||||
try {
|
||||
RenderObject child = renderObject.firstChild;
|
||||
for (TestParentData expected in expectedParentData) {
|
||||
expect(child is RenderDecoratedBox, isTrue);
|
||||
RenderDecoratedBox decoratedBox = child;
|
||||
expect(decoratedBox.parentData is StackParentData, isTrue);
|
||||
StackParentData parentData = decoratedBox.parentData;
|
||||
expect(parentData.top, equals(expected.top));
|
||||
expect(parentData.right, equals(expected.right));
|
||||
expect(parentData.bottom, equals(expected.bottom));
|
||||
expect(parentData.left, equals(expected.left));
|
||||
child = decoratedBox.parentData.nextSibling;
|
||||
}
|
||||
expect(child, isNull);
|
||||
} catch (e) {
|
||||
print(renderObject.toStringDeep());
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
final TestParentData kNonPositioned = new TestParentData();
|
||||
|
||||
void main() {
|
||||
dynamic cachedException;
|
||||
|
||||
setUp(() {
|
||||
assert(cachedException == null);
|
||||
debugWidgetsExceptionHandler = (String context, dynamic exception, StackTrace stack) {
|
||||
cachedException = exception;
|
||||
};
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
cachedException = null;
|
||||
debugWidgetsExceptionHandler = null;
|
||||
});
|
||||
|
||||
test('ParentDataWidget control test', () {
|
||||
WidgetTester tester = new WidgetTester();
|
||||
|
||||
tester.pumpFrame(
|
||||
new Stack([
|
||||
new DecoratedBox(decoration: kBoxDecorationA),
|
||||
new Positioned(
|
||||
top: 10.0,
|
||||
left: 10.0,
|
||||
child: new DecoratedBox(decoration: kBoxDecorationB)
|
||||
),
|
||||
new DecoratedBox(decoration: kBoxDecorationC),
|
||||
])
|
||||
);
|
||||
|
||||
checkTree(tester, [
|
||||
kNonPositioned,
|
||||
new TestParentData(top: 10.0, left: 10.0),
|
||||
kNonPositioned,
|
||||
]);
|
||||
|
||||
tester.pumpFrame(
|
||||
new Stack([
|
||||
new Positioned(
|
||||
bottom: 5.0,
|
||||
right: 7.0,
|
||||
child: new DecoratedBox(decoration: kBoxDecorationA)
|
||||
),
|
||||
new Positioned(
|
||||
top: 10.0,
|
||||
left: 10.0,
|
||||
child: new DecoratedBox(decoration: kBoxDecorationB)
|
||||
),
|
||||
new DecoratedBox(decoration: kBoxDecorationC),
|
||||
])
|
||||
);
|
||||
|
||||
checkTree(tester, [
|
||||
new TestParentData(bottom: 5.0, right: 7.0),
|
||||
new TestParentData(top: 10.0, left: 10.0),
|
||||
kNonPositioned,
|
||||
]);
|
||||
|
||||
DecoratedBox kDecoratedBoxA = new DecoratedBox(decoration: kBoxDecorationA);
|
||||
DecoratedBox kDecoratedBoxB = new DecoratedBox(decoration: kBoxDecorationB);
|
||||
DecoratedBox kDecoratedBoxC = new DecoratedBox(decoration: kBoxDecorationC);
|
||||
|
||||
tester.pumpFrame(
|
||||
new Stack([
|
||||
new Positioned(
|
||||
bottom: 5.0,
|
||||
right: 7.0,
|
||||
child: kDecoratedBoxA
|
||||
),
|
||||
new Positioned(
|
||||
top: 10.0,
|
||||
left: 10.0,
|
||||
child: kDecoratedBoxB
|
||||
),
|
||||
kDecoratedBoxC,
|
||||
])
|
||||
);
|
||||
|
||||
checkTree(tester, [
|
||||
new TestParentData(bottom: 5.0, right: 7.0),
|
||||
new TestParentData(top: 10.0, left: 10.0),
|
||||
kNonPositioned,
|
||||
]);
|
||||
|
||||
tester.pumpFrame(
|
||||
new Stack([
|
||||
new Positioned(
|
||||
bottom: 6.0,
|
||||
right: 8.0,
|
||||
child: kDecoratedBoxA
|
||||
),
|
||||
new Positioned(
|
||||
left: 10.0,
|
||||
right: 10.0,
|
||||
child: kDecoratedBoxB
|
||||
),
|
||||
kDecoratedBoxC,
|
||||
])
|
||||
);
|
||||
|
||||
checkTree(tester, [
|
||||
new TestParentData(bottom: 6.0, right: 8.0),
|
||||
new TestParentData(left: 10.0, right: 10.0),
|
||||
kNonPositioned,
|
||||
]);
|
||||
|
||||
tester.pumpFrame(
|
||||
new Stack([
|
||||
kDecoratedBoxA,
|
||||
new Positioned(
|
||||
left: 11.0,
|
||||
right: 12.0,
|
||||
child: new Container(child: kDecoratedBoxB)
|
||||
),
|
||||
kDecoratedBoxC,
|
||||
])
|
||||
);
|
||||
|
||||
checkTree(tester, [
|
||||
kNonPositioned,
|
||||
new TestParentData(left: 11.0, right: 12.0),
|
||||
kNonPositioned,
|
||||
]);
|
||||
|
||||
tester.pumpFrame(
|
||||
new Stack([
|
||||
kDecoratedBoxA,
|
||||
new Positioned(
|
||||
right: 10.0,
|
||||
child: new Container(child: kDecoratedBoxB)
|
||||
),
|
||||
new Container(
|
||||
child: new Positioned(
|
||||
top: 8.0,
|
||||
child: kDecoratedBoxC
|
||||
)
|
||||
)
|
||||
])
|
||||
);
|
||||
|
||||
checkTree(tester, [
|
||||
kNonPositioned,
|
||||
new TestParentData(right: 10.0),
|
||||
new TestParentData(top: 8.0),
|
||||
]);
|
||||
|
||||
tester.pumpFrame(
|
||||
new Stack([
|
||||
new Positioned(
|
||||
right: 10.0,
|
||||
child: new FlipComponent(left: kDecoratedBoxA, right: kDecoratedBoxB)
|
||||
),
|
||||
])
|
||||
);
|
||||
|
||||
checkTree(tester, [
|
||||
new TestParentData(right: 10.0),
|
||||
]);
|
||||
|
||||
flipStatefulComponent(tester);
|
||||
tester.pumpFrameWithoutChange();
|
||||
|
||||
checkTree(tester, [
|
||||
new TestParentData(right: 10.0),
|
||||
]);
|
||||
|
||||
tester.pumpFrame(
|
||||
new Stack([
|
||||
new Positioned(
|
||||
top: 7.0,
|
||||
child: new FlipComponent(left: kDecoratedBoxA, right: kDecoratedBoxB)
|
||||
),
|
||||
])
|
||||
);
|
||||
|
||||
checkTree(tester, [
|
||||
new TestParentData(top: 7.0),
|
||||
]);
|
||||
|
||||
flipStatefulComponent(tester);
|
||||
tester.pumpFrameWithoutChange();
|
||||
|
||||
checkTree(tester, [
|
||||
new TestParentData(top: 7.0),
|
||||
]);
|
||||
|
||||
tester.pumpFrame(
|
||||
new Stack([])
|
||||
);
|
||||
|
||||
checkTree(tester, []);
|
||||
});
|
||||
|
||||
test('ParentDataWidget conflicting data', () {
|
||||
WidgetTester tester = new WidgetTester();
|
||||
|
||||
expect(cachedException, isNull);
|
||||
|
||||
tester.pumpFrame(
|
||||
new Stack([
|
||||
new Positioned(
|
||||
top: 5.0,
|
||||
bottom: 8.0,
|
||||
child: new Positioned(
|
||||
top: 6.0,
|
||||
left: 7.0,
|
||||
child: new DecoratedBox(decoration: kBoxDecorationB)
|
||||
)
|
||||
)
|
||||
])
|
||||
);
|
||||
|
||||
expect(cachedException, isNotNull);
|
||||
cachedException = null;
|
||||
|
||||
tester.pumpFrame(new Stack([]));
|
||||
|
||||
checkTree(tester, []);
|
||||
expect(cachedException, isNull);
|
||||
|
||||
tester.pumpFrame(
|
||||
new Container(
|
||||
child: new Flex([
|
||||
new Positioned(
|
||||
top: 6.0,
|
||||
left: 7.0,
|
||||
child: new DecoratedBox(decoration: kBoxDecorationB)
|
||||
)
|
||||
])
|
||||
)
|
||||
);
|
||||
|
||||
expect(cachedException, isNotNull);
|
||||
cachedException = null;
|
||||
|
||||
tester.pumpFrame(
|
||||
new Stack([])
|
||||
);
|
||||
|
||||
checkTree(tester, []);
|
||||
});
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user