Unverified Commit 9c49255f authored by Yegor's avatar Yegor Committed by GitHub

a11y: remove SemanticsSortOrder; sort locally only; semanticsOwner post-test check (#15537)

* a11y: remove SemanticsSortOrder; sort locally only; semanticsOwner post-test check

* update accessibility test framework

- default nextNodeId/previousNodeId to -1
- stop treating null as opt-out from value testing
- add `id`, `TestSemantics.root`, and `tags` to the suggested code in the TestSemantics failure message
- fix a small bug with raw string escaping
- update all tests accordingly

* fix sortKey doc

* prefer const over final
parent b494e71e
...@@ -3025,7 +3025,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3025,7 +3025,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
String decreasedValue, String decreasedValue,
String hint, String hint,
TextDirection textDirection, TextDirection textDirection,
SemanticsSortOrder sortOrder, SemanticsSortKey sortKey,
VoidCallback onTap, VoidCallback onTap,
VoidCallback onLongPress, VoidCallback onLongPress,
VoidCallback onScrollLeft, VoidCallback onScrollLeft,
...@@ -3059,7 +3059,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3059,7 +3059,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
_decreasedValue = decreasedValue, _decreasedValue = decreasedValue,
_hint = hint, _hint = hint,
_textDirection = textDirection, _textDirection = textDirection,
_sortOrder = sortOrder, _sortKey = sortKey,
_onTap = onTap, _onTap = onTap,
_onLongPress = onLongPress, _onLongPress = onLongPress,
_onScrollLeft = onScrollLeft, _onScrollLeft = onScrollLeft,
...@@ -3276,17 +3276,17 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3276,17 +3276,17 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
markNeedsSemanticsUpdate(); markNeedsSemanticsUpdate();
} }
/// Sets the [SemanticsNode.sortOrder] to the given value. /// Sets the [SemanticsNode.sortKey] to the given value.
/// ///
/// This defines how this node will be sorted with the other semantics nodes /// This defines how this node is sorted among the sibling semantics nodes
/// to determine the order in which they are traversed by the accessibility /// to determine the order in which they are traversed by the accessibility
/// services on the platform (e.g. VoiceOver on iOS and TalkBack on Android). /// services on the platform (e.g. VoiceOver on iOS and TalkBack on Android).
SemanticsSortOrder get sortOrder => _sortOrder; SemanticsSortKey get sortKey => _sortKey;
SemanticsSortOrder _sortOrder; SemanticsSortKey _sortKey;
set sortOrder(SemanticsSortOrder value) { set sortKey(SemanticsSortKey value) {
if (sortOrder == value) if (sortKey == value)
return; return;
_sortOrder = value; _sortKey = value;
markNeedsSemanticsUpdate(); markNeedsSemanticsUpdate();
} }
...@@ -3650,8 +3650,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3650,8 +3650,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
config.hint = hint; config.hint = hint;
if (textDirection != null) if (textDirection != null)
config.textDirection = textDirection; config.textDirection = textDirection;
if (sortOrder != null) if (sortKey != null)
config.sortOrder = sortOrder; config.sortKey = sortKey;
// Registering _perform* as action handlers instead of the user provided // Registering _perform* as action handlers instead of the user provided
// ones to ensure that changing a user provided handler from a non-null to // ones to ensure that changing a user provided handler from a non-null to
// another non-null value doesn't require a semantics update. // another non-null value doesn't require a semantics update.
......
...@@ -4875,13 +4875,9 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -4875,13 +4875,9 @@ class Semantics extends SingleChildRenderObjectWidget {
/// The [container] argument must not be null. To create a `const` instance /// The [container] argument must not be null. To create a `const` instance
/// of [Semantics], use the [Semantics.fromProperties] constructor. /// of [Semantics], use the [Semantics.fromProperties] constructor.
/// ///
/// Only one of [sortKey] or [sortOrder] may be specified. Specifying [sortKey]
/// is just a shorthand for specifying `new SemanticsSortOrder(key: sortKey)`
/// for the [sortOrder].
///
/// See also: /// See also:
/// ///
/// * [SemanticsSortOrder] for a class that determines accessibility traversal /// * [SemanticsSortKey] for a class that determines accessibility traversal
/// order. /// order.
Semantics({ Semantics({
Key key, Key key,
...@@ -4902,7 +4898,6 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -4902,7 +4898,6 @@ class Semantics extends SingleChildRenderObjectWidget {
String decreasedValue, String decreasedValue,
String hint, String hint,
TextDirection textDirection, TextDirection textDirection,
SemanticsSortOrder sortOrder,
SemanticsSortKey sortKey, SemanticsSortKey sortKey,
VoidCallback onTap, VoidCallback onTap,
VoidCallback onLongPress, VoidCallback onLongPress,
...@@ -4940,7 +4935,7 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -4940,7 +4935,7 @@ class Semantics extends SingleChildRenderObjectWidget {
decreasedValue: decreasedValue, decreasedValue: decreasedValue,
hint: hint, hint: hint,
textDirection: textDirection, textDirection: textDirection,
sortOrder: _effectiveSortOrder(sortKey, sortOrder), sortKey: sortKey,
onTap: onTap, onTap: onTap,
onLongPress: onLongPress, onLongPress: onLongPress,
onScrollLeft: onScrollLeft, onScrollLeft: onScrollLeft,
...@@ -4972,11 +4967,6 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -4972,11 +4967,6 @@ class Semantics extends SingleChildRenderObjectWidget {
assert(properties != null), assert(properties != null),
super(key: key, child: child); super(key: key, child: child);
static SemanticsSortOrder _effectiveSortOrder(SemanticsSortKey sortKey, SemanticsSortOrder sortOrder) {
assert(sortOrder == null || sortKey == null, 'Only one of sortOrder or sortKey may be specified.');
return sortOrder ?? (sortKey != null ? new SemanticsSortOrder(key: sortKey) : null);
}
/// Contains properties used by assistive technologies to make the application /// Contains properties used by assistive technologies to make the application
/// more accessible. /// more accessible.
final SemanticsProperties properties; final SemanticsProperties properties;
...@@ -5023,7 +5013,7 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -5023,7 +5013,7 @@ class Semantics extends SingleChildRenderObjectWidget {
decreasedValue: properties.decreasedValue, decreasedValue: properties.decreasedValue,
hint: properties.hint, hint: properties.hint,
textDirection: _getTextDirection(context), textDirection: _getTextDirection(context),
sortOrder: properties.sortOrder, sortKey: properties.sortKey,
onTap: properties.onTap, onTap: properties.onTap,
onLongPress: properties.onLongPress, onLongPress: properties.onLongPress,
onScrollLeft: properties.onScrollLeft, onScrollLeft: properties.onScrollLeft,
...@@ -5069,7 +5059,7 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -5069,7 +5059,7 @@ class Semantics extends SingleChildRenderObjectWidget {
..decreasedValue = properties.decreasedValue ..decreasedValue = properties.decreasedValue
..hint = properties.hint ..hint = properties.hint
..textDirection = _getTextDirection(context) ..textDirection = _getTextDirection(context)
..sortOrder = properties.sortOrder ..sortKey = properties.sortKey
..onTap = properties.onTap ..onTap = properties.onTap
..onLongPress = properties.onLongPress ..onLongPress = properties.onLongPress
..onScrollLeft = properties.onScrollLeft ..onScrollLeft = properties.onScrollLeft
......
...@@ -141,6 +141,8 @@ void main() { ...@@ -141,6 +141,8 @@ void main() {
); );
expect(semantics, includesNodeWith(label: 'a label')); expect(semantics, includesNodeWith(label: 'a label'));
semantics.dispose();
}); });
testWidgets('Inherited text direction rtl', (WidgetTester tester) async { testWidgets('Inherited text direction rtl', (WidgetTester tester) async {
......
...@@ -521,7 +521,6 @@ void main() { ...@@ -521,7 +521,6 @@ void main() {
actions: <SemanticsAction>[SemanticsAction.tap], actions: <SemanticsAction>[SemanticsAction.tap],
label: 'AC\nTab 1 of 3', label: 'AC\nTab 1 of 3',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: -1,
previousNodeId: 3, // Should be 2 previousNodeId: 3, // Should be 2
), ),
new TestSemantics( new TestSemantics(
......
...@@ -100,6 +100,7 @@ void main() { ...@@ -100,6 +100,7 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
nextNodeId: 3,
rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0), rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0),
transform: null, transform: null,
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[
...@@ -113,6 +114,8 @@ void main() { ...@@ -113,6 +114,8 @@ void main() {
), ),
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 3, id: 3,
previousNodeId: 1,
nextNodeId: 5,
rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0), rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0),
transform: new Matrix4.translationValues(0.0, 56.0, 0.0), transform: new Matrix4.translationValues(0.0, 56.0, 0.0),
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[
...@@ -126,6 +129,7 @@ void main() { ...@@ -126,6 +129,7 @@ void main() {
), ),
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 5, id: 5,
previousNodeId: 3,
rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0), rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0),
transform: new Matrix4.translationValues(0.0, 112.0, 0.0), transform: new Matrix4.translationValues(0.0, 112.0, 0.0),
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[
...@@ -139,6 +143,8 @@ void main() { ...@@ -139,6 +143,8 @@ void main() {
), ),
], ],
))); )));
semantics.dispose();
}); });
} }
...@@ -622,5 +622,7 @@ void _tests() { ...@@ -622,5 +622,7 @@ void _tests() {
ignoreRect: true, ignoreRect: true,
)); ));
}); });
semantics.dispose();
}); });
} }
...@@ -1363,6 +1363,7 @@ void main() { ...@@ -1363,6 +1363,7 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 3, id: 3,
nextNodeId: 4,
actions: SemanticsAction.tap.index, actions: SemanticsAction.tap.index,
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
label: 'TAB #0\nTab 1 of 2', label: 'TAB #0\nTab 1 of 2',
...@@ -1371,6 +1372,7 @@ void main() { ...@@ -1371,6 +1372,7 @@ void main() {
), ),
new TestSemantics( new TestSemantics(
id: 4, id: 4,
previousNodeId: 3,
actions: SemanticsAction.tap.index, actions: SemanticsAction.tap.index,
label: 'TAB #1\nTab 2 of 2', label: 'TAB #1\nTab 2 of 2',
rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight), rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight),
...@@ -1620,6 +1622,7 @@ void main() { ...@@ -1620,6 +1622,7 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 3, id: 3,
nextNodeId: 4,
actions: SemanticsAction.tap.index, actions: SemanticsAction.tap.index,
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
label: 'Semantics override 0\nTab 1 of 2', label: 'Semantics override 0\nTab 1 of 2',
...@@ -1628,6 +1631,7 @@ void main() { ...@@ -1628,6 +1631,7 @@ void main() {
), ),
new TestSemantics( new TestSemantics(
id: 4, id: 4,
previousNodeId: 3,
actions: SemanticsAction.tap.index, actions: SemanticsAction.tap.index,
label: 'Semantics override 1\nTab 2 of 2', label: 'Semantics override 1\nTab 2 of 2',
rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight), rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight),
......
...@@ -1675,6 +1675,8 @@ void main() { ...@@ -1675,6 +1675,8 @@ void main() {
); );
expect(semantics, includesNodeWith(flags: <SemanticsFlag>[SemanticsFlag.isTextField])); expect(semantics, includesNodeWith(flags: <SemanticsFlag>[SemanticsFlag.isTextField]));
semantics.dispose();
}); });
testWidgets('Caret works when maxLines is null', (WidgetTester tester) async { testWidgets('Caret works when maxLines is null', (WidgetTester tester) async {
...@@ -1771,7 +1773,7 @@ void main() { ...@@ -1771,7 +1773,7 @@ void main() {
expect(semantics, hasSemantics(new TestSemantics.root( expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 1,
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
actions: <SemanticsAction>[ actions: <SemanticsAction>[
SemanticsAction.tap, SemanticsAction.tap,
...@@ -1789,7 +1791,7 @@ void main() { ...@@ -1789,7 +1791,7 @@ void main() {
expect(semantics, hasSemantics(new TestSemantics.root( expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 1,
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
value: 'Guten Tag', value: 'Guten Tag',
actions: <SemanticsAction>[ actions: <SemanticsAction>[
...@@ -1808,7 +1810,7 @@ void main() { ...@@ -1808,7 +1810,7 @@ void main() {
expect(semantics, hasSemantics(new TestSemantics.root( expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 1,
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
value: 'Guten Tag', value: 'Guten Tag',
textSelection: const TextSelection.collapsed(offset: 9), textSelection: const TextSelection.collapsed(offset: 9),
...@@ -1832,7 +1834,7 @@ void main() { ...@@ -1832,7 +1834,7 @@ void main() {
expect(semantics, hasSemantics(new TestSemantics.root( expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 1,
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
textSelection: const TextSelection.collapsed(offset: 4), textSelection: const TextSelection.collapsed(offset: 4),
value: 'Guten Tag', value: 'Guten Tag',
...@@ -1858,7 +1860,7 @@ void main() { ...@@ -1858,7 +1860,7 @@ void main() {
expect(semantics, hasSemantics(new TestSemantics.root( expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 1,
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
textSelection: const TextSelection.collapsed(offset: 0), textSelection: const TextSelection.collapsed(offset: 0),
value: 'Schönen Feierabend', value: 'Schönen Feierabend',
...@@ -1897,7 +1899,7 @@ void main() { ...@@ -1897,7 +1899,7 @@ void main() {
expect(semantics, hasSemantics(new TestSemantics.root( expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 1,
value: 'Hello', value: 'Hello',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
actions: <SemanticsAction>[ actions: <SemanticsAction>[
...@@ -1917,7 +1919,7 @@ void main() { ...@@ -1917,7 +1919,7 @@ void main() {
expect(semantics, hasSemantics(new TestSemantics.root( expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 1,
value: 'Hello', value: 'Hello',
textSelection: const TextSelection.collapsed(offset: 5), textSelection: const TextSelection.collapsed(offset: 5),
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -1941,7 +1943,7 @@ void main() { ...@@ -1941,7 +1943,7 @@ void main() {
expect(semantics, hasSemantics(new TestSemantics.root( expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 1,
value: 'Hello', value: 'Hello',
textSelection: const TextSelection(baseOffset: 5, extentOffset: 3), textSelection: const TextSelection(baseOffset: 5, extentOffset: 3),
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -1985,7 +1987,7 @@ void main() { ...@@ -1985,7 +1987,7 @@ void main() {
await tester.tap(find.byKey(key)); await tester.tap(find.byKey(key));
await tester.pump(); await tester.pump();
const int inputFieldId = 2; const int inputFieldId = 1;
expect(controller.selection, const TextSelection.collapsed(offset: 5, affinity: TextAffinity.upstream)); expect(controller.selection, const TextSelection.collapsed(offset: 5, affinity: TextAffinity.upstream));
expect(semantics, hasSemantics(new TestSemantics.root( expect(semantics, hasSemantics(new TestSemantics.root(
......
...@@ -457,6 +457,8 @@ void _tests() { ...@@ -457,6 +457,8 @@ void _tests() {
action: SemanticsAction.decrease, action: SemanticsAction.decrease,
finalValue: '23', finalValue: '23',
); );
semantics.dispose();
}); });
testWidgets('can increment and decrement minutes', (WidgetTester tester) async { testWidgets('can increment and decrement minutes', (WidgetTester tester) async {
...@@ -501,6 +503,8 @@ void _tests() { ...@@ -501,6 +503,8 @@ void _tests() {
action: SemanticsAction.decrease, action: SemanticsAction.decrease,
finalValue: '58', finalValue: '58',
); );
semantics.dispose();
}); });
} }
......
...@@ -144,7 +144,7 @@ void main() { ...@@ -144,7 +144,7 @@ void main() {
}); });
test('OrdinalSortKey compares correctly', () { test('OrdinalSortKey compares correctly', () {
final List<List<SemanticsSortKey>> tests = <List<SemanticsSortKey>>[ const List<List<SemanticsSortKey>> tests = const <List<SemanticsSortKey>>[
<SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(0.0)], <SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(0.0)],
<SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(1.0)], <SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(1.0)],
<SemanticsSortKey>[const OrdinalSortKey(1.0), const OrdinalSortKey(0.0)], <SemanticsSortKey>[const OrdinalSortKey(1.0), const OrdinalSortKey(0.0)],
...@@ -163,31 +163,11 @@ void main() { ...@@ -163,31 +163,11 @@ void main() {
expect(results, orderedEquals(expectedResults)); expect(results, orderedEquals(expectedResults));
}); });
test('SemanticsSortOrder sorts correctly', () { test('SemanticsSortKey sorts correctly when assigned names', () {
final SemanticsSortOrder order1 = new SemanticsSortOrder(key: const CustomSortKey(0.0)); const SemanticsSortKey order1g1 = const CustomSortKey(0.0, name: 'group 1');
final SemanticsSortOrder order2 = new SemanticsSortOrder(key: const CustomSortKey(0.0)); const SemanticsSortKey order2g1 = const CustomSortKey(1.0, name: 'group 1');
// Equal single keys compare equal. const SemanticsSortKey order2g2 = const CustomSortKey(1.0, name: 'group 2');
expect(order1.compareTo(order2), equals(0)); const SemanticsSortKey order3g2 = const OrdinalSortKey(1.0, name: 'group 1');
// Key lists that are longer compare as after the shorter ones.
order1.keys.add(const OrdinalSortKey(1.0));
expect(order1.compareTo(order2), equals(1));
// Equal multiple key lists compare equal.
order2.keys.add(const OrdinalSortKey(1.0));
expect(order1.compareTo(order2), equals(0));
// Different types compare equal.
order1.keys.add(const OrdinalSortKey(1.0));
order2.keys.add(const CustomSortKey(1.0));
expect(order1.compareTo(order2), equals(0));
// Unequal multiple-key lists sort the shorter list first.
order1.keys.add(const CustomSortKey(2.0));
expect(order1.compareTo(order2), equals(1));
});
test('SemanticsSortOrder sorts correctly when assigned names', () {
final SemanticsSortOrder order1g1 = new SemanticsSortOrder(key: const CustomSortKey(0.0, name: 'group 1'));
final SemanticsSortOrder order2g1 = new SemanticsSortOrder(key: const CustomSortKey(1.0, name: 'group 1'));
final SemanticsSortOrder order2g2 = new SemanticsSortOrder(key: const CustomSortKey(1.0, name: 'group 2'));
final SemanticsSortOrder order3g2 = new SemanticsSortOrder(key: const OrdinalSortKey(1.0, name: 'group 1'));
// Keys in the same group compare. // Keys in the same group compare.
expect(order1g1.compareTo(order2g1), equals(-1)); expect(order1g1.compareTo(order2g1), equals(-1));
// Keys with different names compare equal. // Keys with different names compare equal.
...@@ -196,26 +176,8 @@ void main() { ...@@ -196,26 +176,8 @@ void main() {
expect(order1g1.compareTo(order3g2), equals(0)); expect(order1g1.compareTo(order3g2), equals(0));
}); });
test('SemanticsSortOrder replaces correctly in merge', () {
final SemanticsSortOrder order1 = new SemanticsSortOrder(keys: <SemanticsSortKey>[const CustomSortKey(0.0), const OrdinalSortKey(0.0)]);
final SemanticsSortOrder order2 = new SemanticsSortOrder(keys: <SemanticsSortKey>[const CustomSortKey(0.0), const OrdinalSortKey(0.0)]);
final SemanticsSortOrder order3 = new SemanticsSortOrder(keys: <SemanticsSortKey>[const CustomSortKey(1.0), const OrdinalSortKey(1.0)], discardParentOrder: true);
// Equal single keys compare equal.
expect(order1.compareTo(order2), equals(0));
// Merged orders with one that replaces merge correctly.
final SemanticsSortOrder merged = order1.merge(order3);
expect(merged.keys.length, 2);
expect(merged.keys, orderedEquals(order3.keys));
expect(merged.compareTo(order2), 1);
// Merged orders with one that doesn't replace merge correctly.
final SemanticsSortOrder merged2 = order1.merge(order2);
expect(merged2.keys.length, 4);
expect(merged2.keys, orderedEquals(<SemanticsSortKey>[]..addAll(order1.keys)..addAll(order2.keys)));
expect(merged2.compareTo(order2), 1); // (merged2 is longer, so greater than).
});
test('OrdinalSortKey compares correctly', () { test('OrdinalSortKey compares correctly', () {
final List<List<SemanticsSortKey>> tests = <List<SemanticsSortKey>>[ const List<List<SemanticsSortKey>> tests = const <List<SemanticsSortKey>>[
<SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(0.0)], <SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(0.0)],
<SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(1.0)], <SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(1.0)],
<SemanticsSortKey>[const OrdinalSortKey(1.0), const OrdinalSortKey(0.0)], <SemanticsSortKey>[const OrdinalSortKey(1.0), const OrdinalSortKey(0.0)],
...@@ -234,31 +196,11 @@ void main() { ...@@ -234,31 +196,11 @@ void main() {
expect(results, orderedEquals(expectedResults)); expect(results, orderedEquals(expectedResults));
}); });
test('SemanticsSortOrder sorts correctly', () { test('SemanticsSortKey sorts correctly when assigned names', () {
final SemanticsSortOrder order1 = new SemanticsSortOrder(key: const CustomSortKey(0.0)); const SemanticsSortKey order1g1 = const CustomSortKey(0.0, name: 'group 1');
final SemanticsSortOrder order2 = new SemanticsSortOrder(key: const CustomSortKey(0.0)); const SemanticsSortKey order2g1 = const CustomSortKey(1.0, name: 'group 1');
// Equal single keys compare equal. const SemanticsSortKey order2g2 = const CustomSortKey(1.0, name: 'group 2');
expect(order1.compareTo(order2), equals(0)); const SemanticsSortKey order3g2 = const OrdinalSortKey(1.0, name: 'group 1');
// Key lists that are longer compare as after the shorter ones.
order1.keys.add(const OrdinalSortKey(1.0));
expect(order1.compareTo(order2), equals(1));
// Equal multiple key lists compare equal.
order2.keys.add(const OrdinalSortKey(1.0));
expect(order1.compareTo(order2), equals(0));
// Different types compare equal.
order1.keys.add(const OrdinalSortKey(1.0));
order2.keys.add(const CustomSortKey(1.0));
expect(order1.compareTo(order2), equals(0));
// Unequal multiple-key lists sort the shorter list first.
order1.keys.add(const CustomSortKey(2.0));
expect(order1.compareTo(order2), equals(1));
});
test('SemanticsSortOrder sorts correctly when assigned names', () {
final SemanticsSortOrder order1g1 = new SemanticsSortOrder(key: const CustomSortKey(0.0, name: 'group 1'));
final SemanticsSortOrder order2g1 = new SemanticsSortOrder(key: const CustomSortKey(1.0, name: 'group 1'));
final SemanticsSortOrder order2g2 = new SemanticsSortOrder(key: const CustomSortKey(1.0, name: 'group 2'));
final SemanticsSortOrder order3g2 = new SemanticsSortOrder(key: const OrdinalSortKey(1.0, name: 'group 1'));
// Keys in the same group compare. // Keys in the same group compare.
expect(order1g1.compareTo(order2g1), equals(-1)); expect(order1g1.compareTo(order2g1), equals(-1));
// Keys with different names compare equal. // Keys with different names compare equal.
...@@ -267,24 +209,6 @@ void main() { ...@@ -267,24 +209,6 @@ void main() {
expect(order1g1.compareTo(order3g2), equals(0)); expect(order1g1.compareTo(order3g2), equals(0));
}); });
test('SemanticsSortOrder replaces correctly in merge', () {
final SemanticsSortOrder order1 = new SemanticsSortOrder(keys: <SemanticsSortKey>[const CustomSortKey(0.0), const OrdinalSortKey(0.0)]);
final SemanticsSortOrder order2 = new SemanticsSortOrder(keys: <SemanticsSortKey>[const CustomSortKey(0.0), const OrdinalSortKey(0.0)]);
final SemanticsSortOrder order3 = new SemanticsSortOrder(keys: <SemanticsSortKey>[const CustomSortKey(1.0), const OrdinalSortKey(1.0)], discardParentOrder: true);
// Equal single keys compare equal.
expect(order1.compareTo(order2), equals(0));
// Merged orders with one that replaces merge correctly.
final SemanticsSortOrder merged = order1.merge(order3);
expect(merged.keys.length, 2);
expect(merged.keys, orderedEquals(order3.keys));
expect(merged.compareTo(order2), 1);
// Merged orders with one that doesn't replace merge correctly.
final SemanticsSortOrder merged2 = order1.merge(order2);
expect(merged2.keys.length, 4);
expect(merged2.keys, orderedEquals(<SemanticsSortKey>[]..addAll(order1.keys)..addAll(order2.keys)));
expect(merged2.compareTo(order2), 1); // (merged2 is longer, so greater than).
});
test('toStringDeep respects childOrder parameter', () { test('toStringDeep respects childOrder parameter', () {
final SemanticsNode child1 = new SemanticsNode() final SemanticsNode child1 = new SemanticsNode()
..rect = new Rect.fromLTRB(15.0, 0.0, 20.0, 5.0); ..rect = new Rect.fromLTRB(15.0, 0.0, 20.0, 5.0);
...@@ -445,7 +369,7 @@ void main() { ...@@ -445,7 +369,7 @@ void main() {
' textDirection: null\n' ' textDirection: null\n'
' nextNodeId: null\n' ' nextNodeId: null\n'
' previousNodeId: null\n' ' previousNodeId: null\n'
' sortOrder: null\n' ' sortKey: null\n'
' scrollExtentMin: null\n' ' scrollExtentMin: null\n'
' scrollPosition: null\n' ' scrollPosition: null\n'
' scrollExtentMax: null\n' ' scrollExtentMax: null\n'
...@@ -462,7 +386,7 @@ void main() { ...@@ -462,7 +386,7 @@ void main() {
..isButton = true ..isButton = true
..label = 'Use all the properties' ..label = 'Use all the properties'
..textDirection = TextDirection.rtl ..textDirection = TextDirection.rtl
..sortOrder = new SemanticsSortOrder(keys: <SemanticsSortKey>[const OrdinalSortKey(1.0)]); ..sortKey = const OrdinalSortKey(1.0);
final SemanticsNode allProperties = new SemanticsNode() final SemanticsNode allProperties = new SemanticsNode()
..rect = new Rect.fromLTWH(50.0, 10.0, 20.0, 30.0) ..rect = new Rect.fromLTWH(50.0, 10.0, 20.0, 30.0)
..transform = new Matrix4.translation(new Vector3(10.0, 10.0, 0.0)) ..transform = new Matrix4.translation(new Vector3(10.0, 10.0, 0.0))
...@@ -479,8 +403,7 @@ void main() { ...@@ -479,8 +403,7 @@ void main() {
' flags: hasCheckedState, isSelected, isButton\n' ' flags: hasCheckedState, isSelected, isButton\n'
' label: "Use all the properties"\n' ' label: "Use all the properties"\n'
' textDirection: rtl\n' ' textDirection: rtl\n'
' sortOrder: SemanticsSortOrder#b555b(keys:\n' ' sortKey: OrdinalSortKey#19df5(order: 1.0)\n'
' [OrdinalSortKey#19df5(order: 1.0)])\n'
), ),
); );
expect( expect(
......
...@@ -150,16 +150,20 @@ void _defineTests() { ...@@ -150,16 +150,20 @@ void _defineTests() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 3, id: 3,
nextNodeId: 4,
previousNodeId: 2,
label: 'background', label: 'background',
rect: new Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), rect: new Rect.fromLTRB(1.0, 1.0, 2.0, 2.0),
), ),
new TestSemantics( new TestSemantics(
id: 2, id: 2,
nextNodeId: 3,
label: 'Hello', label: 'Hello',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 600.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 600.0),
), ),
new TestSemantics( new TestSemantics(
id: 4, id: 4,
previousNodeId: 3,
label: 'foreground', label: 'foreground',
rect: new Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), rect: new Rect.fromLTRB(1.0, 1.0, 2.0, 2.0),
), ),
...@@ -356,15 +360,11 @@ void _defineTests() { ...@@ -356,15 +360,11 @@ void _defineTests() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
previousNodeId: -1,
nextNodeId: expectedId,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: expectedId, id: expectedId,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
actions: allActions.fold(0, (int previous, SemanticsAction action) => previous | action.index), actions: allActions.fold(0, (int previous, SemanticsAction action) => previous | action.index),
previousNodeId: 1,
nextNodeId: -1,
), ),
] ]
), ),
...@@ -420,20 +420,15 @@ void _defineTests() { ...@@ -420,20 +420,15 @@ void _defineTests() {
), ),
)); ));
const int expectedId = 2;
final TestSemantics expectedSemantics = new TestSemantics.root( final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
previousNodeId: -1,
nextNodeId: expectedId,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: expectedId, id: 2,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
flags: SemanticsFlag.values.values.toList(), flags: SemanticsFlag.values.values.toList(),
previousNodeId: 1,
nextNodeId: -1,
), ),
] ]
), ),
......
...@@ -289,6 +289,8 @@ void main() { ...@@ -289,6 +289,8 @@ void main() {
await tester.pump(); await tester.pump();
expect(semantics, includesNodeWith(flags: <SemanticsFlag>[SemanticsFlag.isTextField, SemanticsFlag.isFocused])); expect(semantics, includesNodeWith(flags: <SemanticsFlag>[SemanticsFlag.isTextField, SemanticsFlag.isFocused]));
semantics.dispose();
}); });
testWidgets('EditableText includes text as value in semantics', (WidgetTester tester) async { testWidgets('EditableText includes text as value in semantics', (WidgetTester tester) async {
...@@ -327,6 +329,8 @@ void main() { ...@@ -327,6 +329,8 @@ void main() {
flags: <SemanticsFlag>[SemanticsFlag.isTextField], flags: <SemanticsFlag>[SemanticsFlag.isTextField],
value: value2, value: value2,
)); ));
semantics.dispose();
}); });
testWidgets('changing selection with keyboard does not show handles', (WidgetTester tester) async { testWidgets('changing selection with keyboard does not show handles', (WidgetTester tester) async {
...@@ -678,7 +682,7 @@ void main() { ...@@ -678,7 +682,7 @@ void main() {
await tester.pump(); await tester.pump();
final SemanticsOwner owner = tester.binding.pipelineOwner.semanticsOwner; final SemanticsOwner owner = tester.binding.pipelineOwner.semanticsOwner;
const int expectedNodeId = 3; const int expectedNodeId = 2;
expect(semantics, hasSemantics(new TestSemantics.root( expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
......
...@@ -39,6 +39,8 @@ void main() { ...@@ -39,6 +39,8 @@ void main() {
expect(callCount, 1); expect(callCount, 1);
tester.binding.pipelineOwner.semanticsOwner.performAction(detectorId, SemanticsAction.scrollDown); tester.binding.pipelineOwner.semanticsOwner.performAction(detectorId, SemanticsAction.scrollDown);
expect(callCount, 2); expect(callCount, 2);
semantics.dispose();
}); });
testWidgets('Horizontal gesture detector has up/down actions', (WidgetTester tester) async { testWidgets('Horizontal gesture detector has up/down actions', (WidgetTester tester) async {
...@@ -71,5 +73,7 @@ void main() { ...@@ -71,5 +73,7 @@ void main() {
expect(callCount, 1); expect(callCount, 1);
tester.binding.pipelineOwner.semanticsOwner.performAction(detectorId, SemanticsAction.scrollRight); tester.binding.pipelineOwner.semanticsOwner.performAction(detectorId, SemanticsAction.scrollRight);
expect(callCount, 2); expect(callCount, 2);
semantics.dispose();
}); });
} }
...@@ -142,6 +142,8 @@ void main() { ...@@ -142,6 +142,8 @@ void main() {
); );
expect(semantics, includesNodeWith(label: 'a label')); expect(semantics, includesNodeWith(label: 'a label'));
semantics.dispose();
}); });
testWidgets('Null icon with semantic label', (WidgetTester tester) async { testWidgets('Null icon with semantic label', (WidgetTester tester) async {
...@@ -160,6 +162,8 @@ void main() { ...@@ -160,6 +162,8 @@ void main() {
); );
expect(semantics, includesNodeWith(label: 'a label')); expect(semantics, includesNodeWith(label: 'a label'));
semantics.dispose();
}); });
testWidgets('Changing semantic label from null doesn\'t rebuild tree ', (WidgetTester tester) async { testWidgets('Changing semantic label from null doesn\'t rebuild tree ', (WidgetTester tester) async {
......
...@@ -79,10 +79,12 @@ void main() { ...@@ -79,10 +79,12 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 2, id: 2,
nextNodeId: 3,
label: 'Michael Goderbauer', label: 'Michael Goderbauer',
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 3,
previousNodeId: 2,
label: 'goderbauer@google.com', label: 'goderbauer@google.com',
), ),
], ],
...@@ -233,16 +235,20 @@ void main() { ...@@ -233,16 +235,20 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 6, id: 6,
nextNodeId: 7,
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
label: 'node 1', label: 'node 1',
), ),
new TestSemantics( new TestSemantics(
id: 7, id: 7,
previousNodeId: 6,
nextNodeId: 8,
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
label: 'node 2', label: 'node 2',
), ),
new TestSemantics( new TestSemantics(
id: 8, id: 8,
previousNodeId: 7,
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
label: 'node 3', label: 'node 3',
), ),
......
...@@ -16,11 +16,6 @@ void main() { ...@@ -16,11 +16,6 @@ void main() {
debugResetSemanticsIdCounter(); debugResetSemanticsIdCounter();
}); });
tearDown(() {
semantics?.dispose();
semantics = null;
});
testWidgets('scrollable exposes the correct semantic actions', (WidgetTester tester) async { testWidgets('scrollable exposes the correct semantic actions', (WidgetTester tester) async {
semantics = new SemanticsTester(tester); semantics = new SemanticsTester(tester);
...@@ -47,6 +42,8 @@ void main() { ...@@ -47,6 +42,8 @@ void main() {
await flingDown(tester); await flingDown(tester);
expect(semantics, includesNodeWith(actions: <SemanticsAction>[SemanticsAction.scrollUp, SemanticsAction.scrollDown])); expect(semantics, includesNodeWith(actions: <SemanticsAction>[SemanticsAction.scrollUp, SemanticsAction.scrollDown]));
semantics.dispose();
}); });
testWidgets('showOnScreen works in scrollable', (WidgetTester tester) async { testWidgets('showOnScreen works in scrollable', (WidgetTester tester) async {
...@@ -83,6 +80,8 @@ void main() { ...@@ -83,6 +80,8 @@ void main() {
await tester.pump(const Duration(seconds: 5)); await tester.pump(const Duration(seconds: 5));
expect(scrollController.offset, 0.0); expect(scrollController.offset, 0.0);
semantics.dispose();
}); });
testWidgets('showOnScreen works with pinned app bar and sliver list', (WidgetTester tester) async { testWidgets('showOnScreen works with pinned app bar and sliver list', (WidgetTester tester) async {
...@@ -141,6 +140,8 @@ void main() { ...@@ -141,6 +140,8 @@ void main() {
await tester.pump(); await tester.pump();
await tester.pump(const Duration(seconds: 5)); await tester.pump(const Duration(seconds: 5));
expect(tester.getTopLeft(find.byWidget(containers[1])).dy, kExpandedAppBarHeight); expect(tester.getTopLeft(find.byWidget(containers[1])).dy, kExpandedAppBarHeight);
semantics.dispose();
}); });
testWidgets('showOnScreen works with pinned app bar and individual slivers', (WidgetTester tester) async { testWidgets('showOnScreen works with pinned app bar and individual slivers', (WidgetTester tester) async {
...@@ -205,6 +206,8 @@ void main() { ...@@ -205,6 +206,8 @@ void main() {
await tester.pump(); await tester.pump();
await tester.pump(const Duration(seconds: 5)); await tester.pump(const Duration(seconds: 5));
expect(tester.getTopLeft(find.byWidget(children[1])).dy, kToolbarHeight); expect(tester.getTopLeft(find.byWidget(children[1])).dy, kToolbarHeight);
semantics.dispose();
}); });
testWidgets('correct scrollProgress', (WidgetTester tester) async { testWidgets('correct scrollProgress', (WidgetTester tester) async {
...@@ -249,6 +252,8 @@ void main() { ...@@ -249,6 +252,8 @@ void main() {
SemanticsAction.scrollDown, SemanticsAction.scrollDown,
], ],
)); ));
semantics.dispose();
}); });
testWidgets('correct scrollProgress for unbound', (WidgetTester tester) async { testWidgets('correct scrollProgress for unbound', (WidgetTester tester) async {
...@@ -296,6 +301,8 @@ void main() { ...@@ -296,6 +301,8 @@ void main() {
SemanticsAction.scrollDown, SemanticsAction.scrollDown,
], ],
)); ));
semantics.dispose();
}); });
testWidgets('Semantics tree is populated mid-scroll', (WidgetTester tester) async { testWidgets('Semantics tree is populated mid-scroll', (WidgetTester tester) async {
...@@ -321,6 +328,8 @@ void main() { ...@@ -321,6 +328,8 @@ void main() {
expect(semantics, includesNodeWith(label: 'Item 1')); expect(semantics, includesNodeWith(label: 'Item 1'));
expect(semantics, includesNodeWith(label: 'Item 2')); expect(semantics, includesNodeWith(label: 'Item 2'));
expect(semantics, includesNodeWith(label: 'Item 3')); expect(semantics, includesNodeWith(label: 'Item 3'));
semantics.dispose();
}); });
testWidgets('Can toggle semantics on, off, on without crash', (WidgetTester tester) async { testWidgets('Can toggle semantics on, off, on without crash', (WidgetTester tester) async {
...@@ -379,6 +388,8 @@ void main() { ...@@ -379,6 +388,8 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(tester.binding.pipelineOwner.semanticsOwner, isNotNull); expect(tester.binding.pipelineOwner.semanticsOwner, isNotNull);
expect(semantics, hasSemantics(expectedSemantics, ignoreId: true, ignoreRect: true, ignoreTransform: true)); expect(semantics, hasSemantics(expectedSemantics, ignoreId: true, ignoreRect: true, ignoreTransform: true));
semantics.dispose();
}); });
} }
......
...@@ -122,12 +122,14 @@ void main() { ...@@ -122,12 +122,14 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 2, id: 2,
nextNodeId: 3,
label: 'child1', label: 'child1',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 3,
previousNodeId: 2,
label: 'child2', label: 'child2',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
...@@ -218,12 +220,14 @@ void main() { ...@@ -218,12 +220,14 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 4, id: 4,
nextNodeId: 3,
label: 'child1', label: 'child1',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 3,
previousNodeId: 4,
label: 'child2', label: 'child2',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
......
...@@ -58,12 +58,14 @@ void main() { ...@@ -58,12 +58,14 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 2, id: 2,
nextNodeId: 3,
label: 'child1', label: 'child1',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 3,
previousNodeId: 2,
label: 'child2', label: 'child2',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
...@@ -154,12 +156,14 @@ void main() { ...@@ -154,12 +156,14 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 4, id: 4,
nextNodeId: 3,
label: 'child1', label: 'child1',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 3,
previousNodeId: 4,
label: 'child2', label: 'child2',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
......
...@@ -54,21 +54,25 @@ void main() { ...@@ -54,21 +54,25 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
nextNodeId: 2,
label: 'L1', label: 'L1',
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
), ),
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 2,
previousNodeId: 1,
label: 'L2', label: 'L2',
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 3, id: 3,
nextNodeId: 4,
flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index, flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
), ),
new TestSemantics( new TestSemantics(
id: 4, id: 4,
previousNodeId: 3,
flags: SemanticsFlag.hasCheckedState.index, flags: SemanticsFlag.hasCheckedState.index,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
), ),
...@@ -114,11 +118,13 @@ void main() { ...@@ -114,11 +118,13 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
nextNodeId: 2,
label: 'L1', label: 'L1',
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
), ),
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 2,
previousNodeId: 1,
label: 'L2', label: 'L2',
flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index, flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
......
...@@ -37,10 +37,12 @@ void main() { ...@@ -37,10 +37,12 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
nextNodeId: 2,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
), ),
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 2,
previousNodeId: 1,
label: 'label', label: 'label',
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
), ),
......
...@@ -56,6 +56,7 @@ void main() { ...@@ -56,6 +56,7 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
nextNodeId: 4,
flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index, flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index,
label: label, label: label,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
...@@ -63,6 +64,7 @@ void main() { ...@@ -63,6 +64,7 @@ void main() {
// IDs 2 and 3 are used up by the nodes that get merged in // IDs 2 and 3 are used up by the nodes that get merged in
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 4, id: 4,
previousNodeId: 1,
flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index, flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index,
label: label, label: label,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
...@@ -112,6 +114,7 @@ void main() { ...@@ -112,6 +114,7 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
nextNodeId: 4,
flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index, flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index,
label: label, label: label,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
...@@ -119,6 +122,7 @@ void main() { ...@@ -119,6 +122,7 @@ void main() {
// IDs 2 and 3 are used up by the nodes that get merged in // IDs 2 and 3 are used up by the nodes that get merged in
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 4, id: 4,
previousNodeId: 1,
flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index, flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index,
label: label, label: label,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
......
...@@ -45,12 +45,10 @@ void main() { ...@@ -45,12 +45,10 @@ void main() {
new TestSemantics.root( new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 1,
label: '1', label: '1',
rect: new Rect.fromLTRB(0.0, 0.0, 75.0, 14.0), rect: new Rect.fromLTRB(0.0, 0.0, 75.0, 14.0),
), ),
new TestSemantics( new TestSemantics(
id: 2,
label: '2', label: '2',
rect: new Rect.fromLTRB(0.0, 0.0, 25.0, 14.0), // clipped form original 75.0 to 25.0 rect: new Rect.fromLTRB(0.0, 0.0, 25.0, 14.0), // clipped form original 75.0 to 25.0
), ),
...@@ -58,6 +56,7 @@ void main() { ...@@ -58,6 +56,7 @@ void main() {
], ],
), ),
ignoreTransform: true, ignoreTransform: true,
ignoreId: true,
)); ));
semantics.dispose(); semantics.dispose();
...@@ -105,18 +104,17 @@ void main() { ...@@ -105,18 +104,17 @@ void main() {
new TestSemantics.root( new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 3,
label: '1', label: '1',
rect: new Rect.fromLTRB(0.0, 0.0, 75.0, 14.0), rect: new Rect.fromLTRB(0.0, 0.0, 75.0, 14.0),
), ),
new TestSemantics( new TestSemantics(
id: 4,
label: '2\n3', label: '2\n3',
rect: new Rect.fromLTRB(0.0, 0.0, 25.0, 14.0), // clipped form original 75.0 to 25.0 rect: new Rect.fromLTRB(0.0, 0.0, 25.0, 14.0), // clipped form original 75.0 to 25.0
), ),
], ],
), ),
ignoreTransform: true, ignoreTransform: true,
ignoreId: true,
)); ));
semantics.dispose(); semantics.dispose();
......
...@@ -41,8 +41,16 @@ void main() { ...@@ -41,8 +41,16 @@ void main() {
expect(semantics, hasSemantics( expect(semantics, hasSemantics(
new TestSemantics.root( new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild(id: 1, label: 'test1'), new TestSemantics.rootChild(
new TestSemantics.rootChild(id: 2, label: 'test2'), id: 1,
label: 'test1',
nextNodeId: 2,
),
new TestSemantics.rootChild(
id: 2,
label: 'test2',
previousNodeId: 1,
),
], ],
), ),
ignoreRect: true, ignoreRect: true,
...@@ -105,8 +113,8 @@ void main() { ...@@ -105,8 +113,8 @@ void main() {
expect(semantics, hasSemantics( expect(semantics, hasSemantics(
new TestSemantics.root( new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild(id: 6, label: 'test1'), new TestSemantics.rootChild(id: 6, label: 'test1', nextNodeId: 7),
new TestSemantics.rootChild(id: 7, label: 'test2'), new TestSemantics.rootChild(id: 7, label: 'test2', previousNodeId: 6),
], ],
), ),
ignoreRect: true, ignoreRect: true,
......
...@@ -43,8 +43,8 @@ class TestSemantics { ...@@ -43,8 +43,8 @@ class TestSemantics {
this.decreasedValue: '', this.decreasedValue: '',
this.hint: '', this.hint: '',
this.textDirection, this.textDirection,
this.nextNodeId, this.nextNodeId: -1,
this.previousNodeId, this.previousNodeId: -1,
this.rect, this.rect,
this.transform, this.transform,
this.textSelection, this.textSelection,
...@@ -58,6 +58,8 @@ class TestSemantics { ...@@ -58,6 +58,8 @@ class TestSemantics {
assert(decreasedValue != null), assert(decreasedValue != null),
assert(hint != null), assert(hint != null),
assert(children != null), assert(children != null),
assert(nextNodeId != null),
assert(previousNodeId != null),
tags = tags?.toSet() ?? new Set<SemanticsTag>(); tags = tags?.toSet() ?? new Set<SemanticsTag>();
/// Creates an object with some test semantics data, with the [id] and [rect] /// Creates an object with some test semantics data, with the [id] and [rect]
...@@ -71,8 +73,6 @@ class TestSemantics { ...@@ -71,8 +73,6 @@ class TestSemantics {
this.decreasedValue: '', this.decreasedValue: '',
this.hint: '', this.hint: '',
this.textDirection, this.textDirection,
this.previousNodeId,
this.nextNodeId,
this.transform, this.transform,
this.textSelection, this.textSelection,
this.children: const <TestSemantics>[], this.children: const <TestSemantics>[],
...@@ -87,6 +87,8 @@ class TestSemantics { ...@@ -87,6 +87,8 @@ class TestSemantics {
assert(hint != null), assert(hint != null),
rect = TestSemantics.rootRect, rect = TestSemantics.rootRect,
assert(children != null), assert(children != null),
nextNodeId = null,
previousNodeId = null,
tags = tags?.toSet() ?? new Set<SemanticsTag>(); tags = tags?.toSet() ?? new Set<SemanticsTag>();
/// Creates an object with some test semantics data, with the [id] and [rect] /// Creates an object with some test semantics data, with the [id] and [rect]
...@@ -108,8 +110,8 @@ class TestSemantics { ...@@ -108,8 +110,8 @@ class TestSemantics {
this.increasedValue: '', this.increasedValue: '',
this.decreasedValue: '', this.decreasedValue: '',
this.textDirection, this.textDirection,
this.nextNodeId, this.nextNodeId: -1,
this.previousNodeId, this.previousNodeId: -1,
this.rect, this.rect,
Matrix4 transform, Matrix4 transform,
this.textSelection, this.textSelection,
...@@ -124,6 +126,8 @@ class TestSemantics { ...@@ -124,6 +126,8 @@ class TestSemantics {
assert(hint != null), assert(hint != null),
transform = _applyRootChildScale(transform), transform = _applyRootChildScale(transform),
assert(children != null), assert(children != null),
assert(nextNodeId != null),
assert(previousNodeId != null),
tags = tags?.toSet() ?? new Set<SemanticsTag>(); tags = tags?.toSet() ?? new Set<SemanticsTag>();
/// The unique identifier for this node. /// The unique identifier for this node.
...@@ -265,9 +269,9 @@ class TestSemantics { ...@@ -265,9 +269,9 @@ class TestSemantics {
return fail('expected node id $id to have hint "$hint" but found hint "${nodeData.hint}".'); return fail('expected node id $id to have hint "$hint" but found hint "${nodeData.hint}".');
if (textDirection != null && textDirection != nodeData.textDirection) if (textDirection != null && textDirection != nodeData.textDirection)
return fail('expected node id $id to have textDirection "$textDirection" but found "${nodeData.textDirection}".'); return fail('expected node id $id to have textDirection "$textDirection" but found "${nodeData.textDirection}".');
if (!ignoreId && nextNodeId != null && nextNodeId != nodeData.nextNodeId) if (!ignoreId && nextNodeId != nodeData.nextNodeId)
return fail('expected node id $id to have nextNodeId "$nextNodeId" but found "${nodeData.nextNodeId}".'); return fail('expected node id $id to have nextNodeId "$nextNodeId" but found "${nodeData.nextNodeId}".');
if (!ignoreId && previousNodeId != null && previousNodeId != nodeData.previousNodeId) if (!ignoreId && previousNodeId != nodeData.previousNodeId)
return fail('expected node id $id to have previousNodeId "$previousNodeId" but found "${nodeData.previousNodeId}".'); return fail('expected node id $id to have previousNodeId "$previousNodeId" but found "${nodeData.previousNodeId}".');
if ((nodeData.label != '' || nodeData.value != '' || nodeData.hint != '' || node.increasedValue != '' || node.decreasedValue != '') && nodeData.textDirection == null) if ((nodeData.label != '' || nodeData.value != '' || nodeData.hint != '' || node.increasedValue != '' || node.decreasedValue != '') && nodeData.textDirection == null)
return fail('expected node id $id, which has a label, value, or hint, to have a textDirection, but it did not.'); return fail('expected node id $id, which has a label, value, or hint, to have a textDirection, but it did not.');
...@@ -350,6 +354,14 @@ class SemanticsTester { ...@@ -350,6 +354,14 @@ class SemanticsTester {
/// tester. /// tester.
SemanticsTester(this.tester) { SemanticsTester(this.tester) {
_semanticsHandle = tester.binding.pipelineOwner.ensureSemantics(); _semanticsHandle = tester.binding.pipelineOwner.ensureSemantics();
// This _extra_ clean-up is needed for the case when a test fails and
// therefore fails to call dispose() explicitly. The test is still required
// to call dispose() explicitly, because the semanticsOwner check is
// performed irrespective of whether the owner was created via
// SemanticsTester or directly. When the test succeeds, this tear-down
// becomes a no-op.
addTearDown(dispose);
} }
/// The widget tester that this object is testing the semantics of. /// The widget tester that this object is testing the semantics of.
...@@ -358,10 +370,12 @@ class SemanticsTester { ...@@ -358,10 +370,12 @@ class SemanticsTester {
/// Release resources held by this semantics tester. /// Release resources held by this semantics tester.
/// ///
/// Call this function at the end of any test that uses a semantics tester. /// Call this function at the end of any test that uses a semantics tester. It
/// is OK to call this function multiple times. If the resources have already
/// been released, the subsequent calls have no effect.
@mustCallSuper @mustCallSuper
void dispose() { void dispose() {
_semanticsHandle.dispose(); _semanticsHandle?.dispose();
_semanticsHandle = null; _semanticsHandle = null;
} }
...@@ -493,6 +507,10 @@ class SemanticsTester { ...@@ -493,6 +507,10 @@ class SemanticsTester {
return '<SemanticsFlag>[${list.join(', ')}]'; return '<SemanticsFlag>[${list.join(', ')}]';
} }
static String _tagsToSemanticsTagExpression(Set<SemanticsTag> tags) {
return '<SemanticsTag>[${tags.map((SemanticsTag tag) => 'const SemanticsTag(\'${tag.name}\')').join(', ')}]';
}
static String _actionsToSemanticsActionExpression(dynamic actions) { static String _actionsToSemanticsActionExpression(dynamic actions) {
Iterable<SemanticsAction> list; Iterable<SemanticsAction> list;
if (actions is int) { if (actions is int) {
...@@ -510,32 +528,37 @@ class SemanticsTester { ...@@ -510,32 +528,37 @@ class SemanticsTester {
final String indent = ' ' * indentAmount; final String indent = ' ' * indentAmount;
final StringBuffer buf = new StringBuffer(); final StringBuffer buf = new StringBuffer();
final SemanticsData nodeData = node.getSemanticsData(); final SemanticsData nodeData = node.getSemanticsData();
buf.writeln('new TestSemantics('); final bool isRoot = node.id == 0;
buf.writeln('new TestSemantics${isRoot ? '.root': ''}(');
if (!isRoot)
buf.writeln(' id: ${node.id},');
if (nodeData.tags != null)
buf.writeln(' tags: ${_tagsToSemanticsTagExpression(nodeData.tags)},');
if (nodeData.flags != 0) if (nodeData.flags != 0)
buf.writeln(' flags: ${_flagsToSemanticsFlagExpression(nodeData.flags)},'); buf.writeln(' flags: ${_flagsToSemanticsFlagExpression(nodeData.flags)},');
if (nodeData.actions != 0) if (nodeData.actions != 0)
buf.writeln(' actions: ${_actionsToSemanticsActionExpression(nodeData.actions)},'); buf.writeln(' actions: ${_actionsToSemanticsActionExpression(nodeData.actions)},');
if (node.label != null && node.label.isNotEmpty) { if (node.label != null && node.label.isNotEmpty) {
final String escapedLabel = node.label.replaceAll('\n', r'\n'); final String escapedLabel = node.label.replaceAll('\n', r'\n');
if (escapedLabel == node.label) { if (escapedLabel != node.label) {
buf.writeln(' label: r\'$escapedLabel\','); buf.writeln(' label: r\'$escapedLabel\',');
} else { } else {
buf.writeln(' label: \'$escapedLabel\','); buf.writeln(' label: \'$escapedLabel\',');
} }
} }
if (node.value != null && node.value.isNotEmpty) if (node.value != null && node.value.isNotEmpty)
buf.writeln(' value: r\'${node.value}\','); buf.writeln(' value: \'${node.value}\',');
if (node.increasedValue != null && node.increasedValue.isNotEmpty) if (node.increasedValue != null && node.increasedValue.isNotEmpty)
buf.writeln(' increasedValue: r\'${node.increasedValue}\','); buf.writeln(' increasedValue: \'${node.increasedValue}\',');
if (node.decreasedValue != null && node.decreasedValue.isNotEmpty) if (node.decreasedValue != null && node.decreasedValue.isNotEmpty)
buf.writeln(' decreasedValue: r\'${node.decreasedValue}\','); buf.writeln(' decreasedValue: \'${node.decreasedValue}\',');
if (node.hint != null && node.hint.isNotEmpty) if (node.hint != null && node.hint.isNotEmpty)
buf.writeln(' hint: r\'${node.hint}\','); buf.writeln(' hint: \'${node.hint}\',');
if (node.textDirection != null) if (node.textDirection != null)
buf.writeln(' textDirection: ${node.textDirection},'); buf.writeln(' textDirection: ${node.textDirection},');
if (node.nextNodeId != null) if (node.nextNodeId != null && node.nextNodeId != -1)
buf.writeln(' nextNodeId: ${node.nextNodeId},'); buf.writeln(' nextNodeId: ${node.nextNodeId},');
if (node.previousNodeId != null) if (node.previousNodeId != null && node.previousNodeId != -1)
buf.writeln(' previousNodeId: ${node.previousNodeId},'); buf.writeln(' previousNodeId: ${node.previousNodeId},');
if (node.hasChildren) { if (node.hasChildren) {
......
...@@ -102,32 +102,32 @@ void _tests() { ...@@ -102,32 +102,32 @@ void _tests() {
// generateTestSemanticsExpressionForCurrentSemanticsTree. Otherwise, // generateTestSemanticsExpressionForCurrentSemanticsTree. Otherwise,
// the test 'generates code', defined above, will fail. // the test 'generates code', defined above, will fail.
// vvvvvvvvvvvv // vvvvvvvvvvvv
new TestSemantics( new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
nextNodeId: 4, id: 1,
previousNodeId: -1,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
nextNodeId: 2, id: 4,
previousNodeId: 1,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
label: r'Plain text', id: 2,
tags: <SemanticsTag>[const SemanticsTag('RenderViewport.twoPane')],
label: 'Plain text',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 3, nextNodeId: 3,
previousNodeId: 4,
), ),
new TestSemantics( new TestSemantics(
id: 3,
tags: <SemanticsTag>[const SemanticsTag('RenderViewport.twoPane')],
flags: <SemanticsFlag>[SemanticsFlag.hasCheckedState, SemanticsFlag.isChecked, SemanticsFlag.isSelected], flags: <SemanticsFlag>[SemanticsFlag.hasCheckedState, SemanticsFlag.isChecked, SemanticsFlag.isSelected],
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.decrease], actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.decrease],
label: r'‪Interactive text‬', label: '‪Interactive text‬',
value: r'test-value', value: 'test-value',
increasedValue: r'test-increasedValue', increasedValue: 'test-increasedValue',
decreasedValue: r'test-decreasedValue', decreasedValue: 'test-decreasedValue',
hint: r'test-hint', hint: 'test-hint',
textDirection: TextDirection.rtl, textDirection: TextDirection.rtl,
nextNodeId: -1,
previousNodeId: 2, previousNodeId: 2,
), ),
], ],
......
...@@ -471,6 +471,19 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker ...@@ -471,6 +471,19 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
void _endOfTestVerifications() { void _endOfTestVerifications() {
verifyTickersWereDisposed('at the end of the test'); verifyTickersWereDisposed('at the end of the test');
_verifySemanticsHandlesWereDisposed();
}
void _verifySemanticsHandlesWereDisposed() {
if (binding.pipelineOwner.semanticsOwner != null) {
throw new FlutterError(
'A SemanticsHandle was active at the end of the test.\n'
'All SemanticsHandle instances must be disposed by calling dispose() on '
'the SemanticsHandle. If your test uses SemanticsTester, it is '
'sufficient to call dispose() on SemanticsTester. Otherwise, the '
'existing handle will leak into another test and alter its behavior.'
);
}
} }
/// Returns the TestTextInput singleton. /// Returns the TestTextInput singleton.
......
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