Refactor SemanticsData and SemanticsNode to use Diagnosticable. (#11957)

* Refactor SemanticsData and SemanticsNode to use Diagnosticable.

* Switch toStringDeep to take named parameters.
This commit is contained in:
Jacob Richman 2017-09-11 09:25:58 -07:00 committed by GitHub
parent ff90ba5b3a
commit 0229711ba1
10 changed files with 247 additions and 98 deletions

View file

@ -679,7 +679,7 @@ abstract class DiagnosticsNode {
String toString({ TextTreeConfiguration parentConfiguration }) {
assert(style != null);
if (style == DiagnosticsTreeStyle.singleLine)
return toStringDeep('', '', parentConfiguration);
return toStringDeep(parentConfiguration: parentConfiguration);
final String description = toDescription(parentConfiguration: parentConfiguration);
@ -736,7 +736,11 @@ abstract class DiagnosticsNode {
/// * [toString], for a brief description of the [value] but not its children.
/// * [toStringShallow], for a detailed description of the [value] but not its
/// children.
String toStringDeep([String prefixLineOne = '', String prefixOtherLines, TextTreeConfiguration parentConfiguration]) {
String toStringDeep({
String prefixLineOne: '',
String prefixOtherLines,
TextTreeConfiguration parentConfiguration,
}) {
prefixOtherLines ??= prefixLineOne;
final List<DiagnosticsNode> children = getChildren();
@ -796,9 +800,9 @@ abstract class DiagnosticsNode {
if (property.style != DiagnosticsTreeStyle.singleLine) {
final TextTreeConfiguration propertyStyle = property.textTreeConfiguration;
builder.writeRaw(property.toStringDeep(
'${builder.prefixOtherLines}${propertyStyle.prefixLineOne}',
'${builder.prefixOtherLines}${propertyStyle.linkCharacter}${propertyStyle.prefixOtherLines}',
config,
prefixLineOne: '${builder.prefixOtherLines}${propertyStyle.prefixLineOne}',
prefixOtherLines: '${builder.prefixOtherLines}${propertyStyle.linkCharacter}${propertyStyle.prefixOtherLines}',
parentConfiguration: config,
));
continue;
}
@ -850,9 +854,9 @@ abstract class DiagnosticsNode {
if (i == children.length - 1) {
final String lastChildPrefixLineOne = '$prefixChildren${childConfig.prefixLastChildLineOne}';
builder.writeRawLine(child.toStringDeep(
lastChildPrefixLineOne,
'$prefixChildren${childConfig.childLinkSpace}${childConfig.prefixOtherLines}',
config,
prefixLineOne: lastChildPrefixLineOne,
prefixOtherLines: '$prefixChildren${childConfig.childLinkSpace}${childConfig.prefixOtherLines}',
parentConfiguration: config,
));
if (childConfig.footer.isNotEmpty)
builder.writeRaw('$prefixChildren${childConfig.childLinkSpace}${childConfig.footer}');
@ -860,7 +864,7 @@ abstract class DiagnosticsNode {
final TextTreeConfiguration nextChildStyle = _childTextConfiguration(children[i + 1], config);
final String childPrefixLineOne = '$prefixChildren${childConfig.prefixLineOne}';
final String childPrefixOtherLines ='$prefixChildren${nextChildStyle.linkCharacter}${childConfig.prefixOtherLines}';
builder.writeRawLine(child.toStringDeep(childPrefixLineOne, childPrefixOtherLines));
builder.writeRawLine(child.toStringDeep(prefixLineOne: childPrefixLineOne, prefixOtherLines: childPrefixOtherLines));
if (childConfig.footer.isNotEmpty)
builder.writeRaw('$prefixChildren${nextChildStyle.linkCharacter}${childConfig.footer}');
}
@ -1249,16 +1253,18 @@ class IterableProperty<T> extends DiagnosticsProperty<Iterable<T>> {
/// value with 0 elements would, confusingly, be displayed as the empty
/// string. It defaults to the string `[]`.
///
/// The [style] and [hidden] arguments must also not be null.
/// The [style], [hidden], and [showName] arguments must also not be null.
IterableProperty(String name, Iterable<T> value, {
Object defaultValue: kNoDefaultValue,
String ifNull,
String ifEmpty: '[]',
DiagnosticsTreeStyle style: DiagnosticsTreeStyle.singleLine,
bool hidden: false,
bool showName: true,
}) : assert(ifEmpty != null),
assert(style != null),
assert(hidden != null),
assert(showName != null),
super(
name,
value,
@ -1267,6 +1273,7 @@ class IterableProperty<T> extends DiagnosticsProperty<Iterable<T>> {
ifEmpty: ifEmpty,
style: style,
hidden: hidden,
showName: showName,
);
@override
@ -1634,14 +1641,19 @@ class DiagnosticsProperty<T> extends DiagnosticsNode {
List<DiagnosticsNode> getChildren() => <DiagnosticsNode>[];
}
/// [DiagnosticsNode] for an instance of [Diagnosticable].
class _DiagnosticableNode<T extends Diagnosticable> extends DiagnosticsNode {
_DiagnosticableNode({
/// [DiagnosticsNode] that lazily calls the associated [Diagnosticable] [value]
/// to implement [getChildren] and [getProperties].
class DiagnosticableNode<T extends Diagnosticable> extends DiagnosticsNode {
/// Create a diagnostics describing a [Diagnosticable] value.
///
/// The [value] argument must not be null.
DiagnosticableNode({
String name,
@required this.value,
@required DiagnosticsTreeStyle style,
String emptyBodyDescription,
}) : super(
}) : assert(value != null),
super(
name: name,
style: style,
);
@ -1683,7 +1695,7 @@ class _DiagnosticableNode<T extends Diagnosticable> extends DiagnosticsNode {
}
/// [DiagnosticsNode] for an instance of [DiagnosticableTree].
class _DiagnosticableTreeNode extends _DiagnosticableNode<DiagnosticableTree> {
class _DiagnosticableTreeNode extends DiagnosticableNode<DiagnosticableTree> {
_DiagnosticableTreeNode({
String name,
@required DiagnosticableTree value,
@ -1841,7 +1853,7 @@ abstract class Diagnosticable {
/// relationship between the parent and the node. For example, pass
/// [DiagnosticsTreeStyle.offstage] to indicate that a node is offstage.
DiagnosticsNode toDiagnosticsNode({ String name, DiagnosticsTreeStyle style }) {
return new _DiagnosticableNode<Diagnosticable>(
return new DiagnosticableNode<Diagnosticable>(
name: name,
value: this,
style: style,
@ -2110,8 +2122,8 @@ abstract class DiagnosticableTree extends Diagnosticable {
/// * [toString], for a brief description of the object but not its children.
/// * [toStringShallow], for a detailed description of the object but not its
/// children.
String toStringDeep([String prefixLineOne = '', String prefixOtherLines]) {
return toDiagnosticsNode().toStringDeep(prefixLineOne, prefixOtherLines);
String toStringDeep({ String prefixLineOne: '', String prefixOtherLines }) {
return toDiagnosticsNode().toStringDeep(prefixLineOne: prefixLineOne, prefixOtherLines: prefixOtherLines);
}
@override
@ -2176,8 +2188,8 @@ abstract class DiagnosticableTreeMixin implements DiagnosticableTree {
}
@override
String toStringDeep([String prefixLineOne = '', String prefixOtherLines]) {
return toDiagnosticsNode().toStringDeep(prefixLineOne, prefixOtherLines);
String toStringDeep({ String prefixLineOne: '', String prefixOtherLines }) {
return toDiagnosticsNode().toStringDeep(prefixLineOne: prefixLineOne, prefixOtherLines: prefixOtherLines);
}
@override

View file

@ -271,7 +271,7 @@ class TextSpan extends DiagnosticableTree {
'TextSpan contains a null child.\n'
'A TextSpan object with a non-null child list should not have any nulls in its child list.\n'
'The full text in question was:\n'
'${toStringDeep(' ')}'
'${toStringDeep(prefixLineOne: ' ')}'
);
}
return true;

View file

@ -329,7 +329,7 @@ void debugDumpLayerTree() {
/// The order in which the children of a [SemanticsNode] will be printed is
/// controlled by the [childOrder] parameter.
void debugDumpSemanticsTree(DebugSemanticsDumpOrder childOrder) {
debugPrint(RendererBinding.instance?.renderView?.debugSemantics?.toStringDeep(childOrder) ?? 'Semantics not collected.');
debugPrint(RendererBinding.instance?.renderView?.debugSemantics?.toStringDeep(childOrder: childOrder) ?? 'Semantics not collected.');
}
/// A concrete binding for applications that use the Rendering framework

View file

@ -145,11 +145,15 @@ List<String> debugDescribeTransform(Matrix4 transform) {
/// Property which handles [Matrix4] that represent transforms.
class TransformProperty extends DiagnosticsProperty<Matrix4> {
/// Create a diagnostics property for [Matrix4] objects.
///
/// The [showName] argument must not be null.
TransformProperty(String name, Matrix4 value, {
bool showName: true,
Object defaultValue: kNoDefaultValue,
}) : super(
}) : assert(showName != null), super(
name,
value,
showName: showName,
defaultValue: defaultValue,
);

View file

@ -2839,11 +2839,11 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
/// If the prefix argument is provided, then every line in the output
/// will be prefixed by that string.
@override
String toStringDeep([String prefixLineOne = '', String prefixOtherLines = '']) {
String toStringDeep({ String prefixLineOne: '', String prefixOtherLines: '' }) {
final RenderObject debugPreviousActiveLayout = _debugActiveLayout;
_debugActiveLayout = null;
final String result = super.toStringDeep(prefixLineOne, prefixOtherLines);
final String result = super.toStringDeep(prefixLineOne: prefixLineOne, prefixOtherLines: prefixOtherLines);
_debugActiveLayout = debugPreviousActiveLayout;
return result;

View file

@ -91,7 +91,7 @@ class SemanticsTag {
///
/// Typically obtained from [SemanticsNode.getSemanticsData].
@immutable
class SemanticsData {
class SemanticsData extends Diagnosticable {
/// Creates a semantics data object.
///
/// The [flags], [actions], [label], and [Rect] arguments must not be null.
@ -146,25 +146,28 @@ class SemanticsData {
bool hasAction(SemanticsAction action) => (actions & action.index) != 0;
@override
String toString() {
final StringBuffer buffer = new StringBuffer();
buffer.write('$runtimeType($rect');
if (transform != null)
buffer.write('; $transform');
String toStringShort() => '$runtimeType';
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<Rect>('rect', rect, showName: false));
properties.add(new TransformProperty('transform', transform, showName: false, defaultValue: null));
final List<String> actionSummary = <String>[];
for (SemanticsAction action in SemanticsAction.values.values) {
if ((actions & action.index) != 0)
buffer.write('; $action');
actionSummary.add(describeEnum(action));
}
properties.add(new IterableProperty<String>('actions', actionSummary, hidden: actionSummary.isEmpty));
final List<String> flagSummary = <String>[];
for (SemanticsFlags flag in SemanticsFlags.values.values) {
if ((flags & flag.index) != 0)
buffer.write('; $flag');
flagSummary.add(describeEnum(flag));
}
if (label.isNotEmpty)
buffer.write('; "$label"');
if (textDirection != null)
buffer.write('; $textDirection');
buffer.write(')');
return buffer.toString();
properties.add(new IterableProperty<String>('flags', flagSummary, hidden: flagSummary.isEmpty));
properties.add(new StringProperty('label', label, defaultValue: ''));
properties.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
}
@override
@ -185,13 +188,36 @@ class SemanticsData {
int get hashCode => hashValues(flags, actions, label, textDirection, rect, tags, transform);
}
class _SemanticsDiagnosticableNode extends DiagnosticableNode<SemanticsNode> {
_SemanticsDiagnosticableNode({
String name,
@required SemanticsNode value,
@required DiagnosticsTreeStyle style,
@required this.childOrder,
}) : super(
name: name,
value: value,
style: style,
);
final DebugSemanticsDumpOrder childOrder;
@override
List<DiagnosticsNode> getChildren() {
if (value != null)
return value.debugDescribeChildren(childOrder: childOrder);
return const <DiagnosticsNode>[];
}
}
/// A node that represents some semantic data.
///
/// The semantics tree is maintained during the semantics phase of the pipeline
/// (i.e., during [PipelineOwner.flushSemantics]), which happens after
/// compositing. The semantics tree is then uploaded into the engine for use
/// by assistive technology.
class SemanticsNode extends AbstractNode {
class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
/// Creates a semantic node.
///
/// Each semantic node has a unique identifier that is assigned when the node
@ -211,7 +237,7 @@ class SemanticsNode extends AbstractNode {
VoidCallback showOnScreen,
SemanticsOwner owner,
}) : id = 0,
_showOnScreen = showOnScreen,
_showOnScreen = showOnScreen,
_actionHandler = handler {
attach(owner);
}
@ -722,77 +748,87 @@ class SemanticsNode extends AbstractNode {
}
@override
String toString() {
final StringBuffer buffer = new StringBuffer();
buffer.write('$runtimeType($id');
if (_dirty)
buffer.write(' (${ owner != null && owner._dirtyNodes.contains(this) ? "dirty" : "STALE; owner=$owner" })');
if (_shouldMergeAllDescendantsIntoThisNode)
buffer.write(' (leaf merge)');
String toStringShort() => '$runtimeType#$id';
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
bool hideOwner = true;
if (_dirty) {
final bool inDirtyNodes = owner != null && owner._dirtyNodes.contains(this);
properties.add(new FlagProperty('inDirtyNodes', value: inDirtyNodes, ifTrue: 'dirty', ifFalse: 'STALE'));
hideOwner = inDirtyNodes;
}
properties.add(new DiagnosticsProperty<SemanticsOwner>('owner', owner, hidden: hideOwner));
properties.add(new FlagProperty('shouldMergeAllDescendantsIntoThisNode', value: _shouldMergeAllDescendantsIntoThisNode, ifTrue: 'leaf merge'));
final Offset offset = transform != null ? MatrixUtils.getAsTranslation(transform) : null;
if (offset != null) {
buffer.write('; ${rect.shift(offset)}');
properties.add(new DiagnosticsProperty<Rect>('rect', rect.shift(offset), showName: false));
} else {
final double scale = transform != null ? MatrixUtils.getAsScale(transform) : null;
String description;
if (scale != null) {
buffer.write('; $rect scaled by ${scale.toStringAsFixed(1)}x');
description = '$rect scaled by ${scale.toStringAsFixed(1)}x';
} else if (transform != null && !MatrixUtils.isIdentity(transform)) {
final String matrix = transform.toString().split('\n').take(4).map((String line) => line.substring(4)).join('; ');
buffer.write('; $rect with transform [$matrix]');
} else {
buffer.write('; $rect');
description = '$rect with transform [$matrix]';
}
properties.add(new DiagnosticsProperty<Rect>('rect', rect, description: description, showName: false));
}
if (wasAffectedByClip)
buffer.write(' (clipped)');
properties.add(new FlagProperty('wasAffectedByClip', value: wasAffectedByClip, ifTrue: 'clipped'));
final List<String> actions = <String>[];
for (SemanticsAction action in SemanticsAction.values.values) {
if ((_actions & action.index) != 0)
buffer.write('; $action');
actions.add(describeEnum(action));
}
for (SemanticsTag tag in _tags)
buffer.write('; $tag');
if (hasCheckedState) {
if (isChecked)
buffer.write('; checked');
else
buffer.write('; unchecked');
}
if (isSelected)
buffer.write('; selected');
if (label.isNotEmpty)
buffer.write('; "$label"');
if (textDirection != null)
buffer.write('; $textDirection');
buffer.write(')');
return buffer.toString();
properties.add(new IterableProperty<String>('actions', actions, hidden: actions.isEmpty));
properties.add(new IterableProperty<SemanticsTag>('tags', _tags, hidden: _tags.isEmpty));
if (hasCheckedState)
properties.add(new FlagProperty('isChecked', value: isChecked, ifTrue: 'checked', ifFalse: 'unchecked'));
properties.add(new FlagProperty('isSelected', value: isSelected, ifTrue: 'selected'));
properties.add(new StringProperty('label', label, defaultValue: ''));
properties.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
}
/// Returns a string representation of this node and its descendants.
///
/// The order in which the children of the [SemanticsNode] will be printed is
/// controlled by the [childOrder] parameter.
String toStringDeep(DebugSemanticsDumpOrder childOrder, [
String prefixLineOne = '',
String prefixOtherLines = ''
]) {
@override
String toStringDeep({
String prefixLineOne: '',
String prefixOtherLines,
DebugSemanticsDumpOrder childOrder: DebugSemanticsDumpOrder.traversal,
}) {
assert(childOrder != null);
final StringBuffer result = new StringBuffer()
..write(prefixLineOne)
..write(this)
..write('\n');
if (_children != null && _children.isNotEmpty) {
final List<SemanticsNode> childrenInOrder = _getChildrenInOrder(childOrder);
for (int index = 0; index < childrenInOrder.length - 1; index += 1) {
final SemanticsNode child = childrenInOrder[index];
result.write(child.toStringDeep(childOrder, "$prefixOtherLines \u251C", "$prefixOtherLines \u2502"));
}
result.write(childrenInOrder.last.toStringDeep(childOrder, "$prefixOtherLines \u2514", "$prefixOtherLines "));
}
return result.toString();
return toDiagnosticsNode(childOrder: childOrder).toStringDeep(prefixLineOne: prefixLineOne, prefixOtherLines: prefixOtherLines);
}
@override
DiagnosticsNode toDiagnosticsNode({
String name,
DiagnosticsTreeStyle style: DiagnosticsTreeStyle.dense,
DebugSemanticsDumpOrder childOrder: DebugSemanticsDumpOrder.traversal,
}) {
return new _SemanticsDiagnosticableNode(
name: name,
value: this,
style: style,
childOrder: childOrder,
);
}
@override
List<DiagnosticsNode> debugDescribeChildren({ DebugSemanticsDumpOrder childOrder: DebugSemanticsDumpOrder.inverseHitTest }) {
return _getChildrenInOrder(childOrder)
.map<DiagnosticsNode>((SemanticsNode node) => node.toDiagnosticsNode(childOrder: childOrder))
.toList();
}
Iterable<SemanticsNode> _getChildrenInOrder(DebugSemanticsDumpOrder childOrder) {
assert(childOrder != null);
if (_children == null)
return const <SemanticsNode>[];
switch(childOrder) {
case DebugSemanticsDumpOrder.traversal:
return new List<SemanticsNode>.from(_children)..sort(_geometryComparator);

View file

@ -456,6 +456,6 @@ class FocusManager {
final String indent = ' ';
return '${describeIdentity(this)}$status\n'
'${indent}currentFocus: $_currentFocus\n'
'${rootScope.toStringDeep(indent, indent)}';
'${rootScope.toStringDeep(prefixLineOne: indent, prefixOtherLines: indent)}';
}
}

View file

@ -4,6 +4,7 @@
import 'package:flutter/rendering.dart';
import 'package:test/test.dart';
import 'package:vector_math/vector_math_64.dart';
import 'rendering_tester.dart';
@ -94,8 +95,8 @@ void main() {
});
test('toStringDeep() does not throw with transform == null', () {
final SemanticsNode child2 = new SemanticsNode();
final SemanticsNode child1 = new SemanticsNode();
final SemanticsNode child2 = new SemanticsNode();
final SemanticsNode root = new SemanticsNode();
root.addChildren(<SemanticsNode>[child1, child2]);
root.finalizeChildren();
@ -104,10 +105,106 @@ void main() {
expect(child1.transform, isNull);
expect(child2.transform, isNull);
expect(root.toStringDeep(DebugSemanticsDumpOrder.traversal),
'SemanticsNode(8 (STALE; owner=null); Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n'
' ├SemanticsNode(7; Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n'
' └SemanticsNode(6; Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n'
expect(
root.toStringDeep(childOrder: DebugSemanticsDumpOrder.traversal),
'SemanticsNode#8(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n'
'├SemanticsNode#6(Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n'
'└SemanticsNode#7(Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n',
);
});
test('toStringDeep respects childOrder parameter', () {
final SemanticsNode child1 = new SemanticsNode()
..rect = new Rect.fromLTRB(20.0, 20.0, 20.0, 20.0);
final SemanticsNode child2 = new SemanticsNode()
..rect = new Rect.fromLTRB(10.0, 10.0, 10.0, 10.0);
final SemanticsNode root = new SemanticsNode();
root.addChildren(<SemanticsNode>[child1, child2]);
root.finalizeChildren();
expect(
root.toStringDeep(childOrder: DebugSemanticsDumpOrder.traversal),
'SemanticsNode#11(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n'
'├SemanticsNode#10(STALE, owner: null, Rect.fromLTRB(10.0, 10.0, 10.0, 10.0))\n'
'└SemanticsNode#9(STALE, owner: null, Rect.fromLTRB(20.0, 20.0, 20.0, 20.0))\n',
);
expect(
root.toStringDeep(childOrder: DebugSemanticsDumpOrder.inverseHitTest),
'SemanticsNode#11(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n'
'├SemanticsNode#9(STALE, owner: null, Rect.fromLTRB(20.0, 20.0, 20.0, 20.0))\n'
'└SemanticsNode#10(STALE, owner: null, Rect.fromLTRB(10.0, 10.0, 10.0, 10.0))\n',
);
final SemanticsNode child3 = new SemanticsNode()
..rect = new Rect.fromLTRB(0.0, 0.0, 0.0, 0.0);
child3.addChildren(<SemanticsNode>[
new SemanticsNode()..rect = new Rect.fromLTRB(20.0, 0.0, 20.0, 0.0),
new SemanticsNode(),
]);
child3.finalizeChildren();
final SemanticsNode rootComplex = new SemanticsNode();
rootComplex.addChildren(<SemanticsNode>[child1, child2, child3]);
rootComplex.finalizeChildren();
expect(
rootComplex.toStringDeep(childOrder: DebugSemanticsDumpOrder.traversal),
'SemanticsNode#15(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n'
'├SemanticsNode#12(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n'
'│├SemanticsNode#14(Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n'
'│└SemanticsNode#13(STALE, owner: null, Rect.fromLTRB(20.0, 0.0, 20.0, 0.0))\n'
'├SemanticsNode#10(STALE, owner: null, Rect.fromLTRB(10.0, 10.0, 10.0, 10.0))\n'
'└SemanticsNode#9(STALE, owner: null, Rect.fromLTRB(20.0, 20.0, 20.0, 20.0))\n',
);
expect(
rootComplex.toStringDeep(childOrder: DebugSemanticsDumpOrder.inverseHitTest),
'SemanticsNode#15(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n'
'├SemanticsNode#9(STALE, owner: null, Rect.fromLTRB(20.0, 20.0, 20.0, 20.0))\n'
'├SemanticsNode#10(STALE, owner: null, Rect.fromLTRB(10.0, 10.0, 10.0, 10.0))\n'
'└SemanticsNode#12(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n'
' ├SemanticsNode#13(STALE, owner: null, Rect.fromLTRB(20.0, 0.0, 20.0, 0.0))\n'
' └SemanticsNode#14(Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n',
);
});
test('debug properties', () {
expect(
new SemanticsNode().toStringDeep(),
'SemanticsNode#16(Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n',
);
final SemanticsNode allProperties = new SemanticsNode()
..rect = new Rect.fromLTWH(50.0, 10.0, 20.0, 30.0)
..mergeAllDescendantsIntoThisNode = true
..transform = new Matrix4.translation(new Vector3(10.0, 10.0, 0.0))
..wasAffectedByClip = true
..addAction(SemanticsAction.scrollUp)
..addAction(SemanticsAction.longPress)
..addAction(SemanticsAction.showOnScreen)
..isChecked = false
..isSelected = true
..label = "Use all the properties"
..textDirection = TextDirection.rtl;
expect(
allProperties.toStringDeep(),
'SemanticsNode#17(STALE, owner: null, leaf merge, Rect.fromLTRB(60.0, 20.0, 80.0, 50.0), clipped, actions: [longPress, scrollUp, showOnScreen], selected, label: "Use all the properties", textDirection: rtl)\n',
);
expect(
allProperties.getSemanticsData().toString(),
'SemanticsData(Rect.fromLTRB(50.0, 10.0, 70.0, 40.0), [1.0,0.0,0.0,10.0; 0.0,1.0,0.0,10.0; 0.0,0.0,1.0,0.0; 0.0,0.0,0.0,1.0], actions: [longPress, scrollUp, showOnScreen], flags: [isSelected], label: "Use all the properties", textDirection: rtl)',
);
final SemanticsNode scaled = new SemanticsNode()
..rect = new Rect.fromLTWH(50.0, 10.0, 20.0, 30.0)
..transform = new Matrix4.diagonal3(new Vector3(10.0, 10.0, 1.0));
expect(
scaled.toStringDeep(),
'SemanticsNode#18(STALE, owner: null, Rect.fromLTRB(50.0, 10.0, 70.0, 40.0) scaled by 10.0x)\n',
);
expect(
scaled.getSemanticsData().toString(),
'SemanticsData(Rect.fromLTRB(50.0, 10.0, 70.0, 40.0), [10.0,0.0,0.0,0.0; 0.0,10.0,0.0,0.0; 0.0,0.0,1.0,0.0; 0.0,0.0,0.0,1.0])',
);
});

View file

@ -507,7 +507,7 @@ class _HasGoodToStringDeep extends Matcher {
final String prefixOtherLines = 'PREFIX_OTHER_LINES_';
final List<String> prefixIssues = <String>[];
String descriptionWithPrefixes =
object.toStringDeep(prefixLineOne, prefixOtherLines);
object.toStringDeep(prefixLineOne: prefixLineOne, prefixOtherLines: prefixOtherLines);
if (descriptionWithPrefixes.endsWith('\n')) {
// Trim off trailing \n as the remaining calculations assume
// the description does not end with a trailing \n.
@ -532,7 +532,7 @@ class _HasGoodToStringDeep extends Matcher {
if (prefixIssues.isNotEmpty) {
errorDescription.writeln(
'Bad toStringDeep("$prefixLineOne", "$prefixOtherLines"):');
'Bad toStringDeep(prefixLineOne: "$prefixLineOne", prefixOtherLines: "$prefixOtherLines"):');
errorDescription.writeln(descriptionWithPrefixes);
errorDescription.writeAll(prefixIssues, '\n');
}

View file

@ -26,7 +26,7 @@ class _MockToStringDeep {
/// line break.
List<String> _lines;
String toStringDeep([String prefixLineOne="", String prefixOtherLines=""]) {
String toStringDeep({ String prefixLineOne: "", String prefixOtherLines: "" }) {
final StringBuffer sb = new StringBuffer();
if (_lines.isNotEmpty)
sb.write('$prefixLineOne${_lines.first}');