Commit fa119c38 authored by Adam Barth's avatar Adam Barth

Add tests for ParentData

parent 82e8e3f0
......@@ -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;
}
}
\ No newline at end of file
}
......@@ -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('------------------------------------------------------------------------');
}
}
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, []);
});
}
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