Unverified Commit 574fb1f0 authored by pdblasi-google's avatar pdblasi-google Committed by GitHub

110598: expect() in semantic test producing unhelpful output (#110613)

parent ab77e435
......@@ -2272,10 +2272,10 @@ class _MatchesSemanticsData extends Matcher {
.toList();
if (expectedActions.isNotEmpty) {
description.add(' with actions: ').addDescriptionOf(expectedActions);
description.add(' with actions: ${_createEnumsSummary(expectedActions)} ');
}
if (notExpectedActions.isNotEmpty) {
description.add(' without actions: ').addDescriptionOf(notExpectedActions);
description.add(' without actions: ${_createEnumsSummary(notExpectedActions)} ');
}
}
if (flags.isNotEmpty) {
......@@ -2289,10 +2289,10 @@ class _MatchesSemanticsData extends Matcher {
.toList();
if (expectedFlags.isNotEmpty) {
description.add(' with flags: ').addDescriptionOf(expectedFlags);
description.add(' with flags: ${_createEnumsSummary(expectedFlags)} ');
}
if (notExpectedFlags.isNotEmpty) {
description.add(' without flags: ').addDescriptionOf(notExpectedFlags);
description.add(' without flags: ${_createEnumsSummary(notExpectedFlags)} ');
}
}
if (textDirection != null) {
......@@ -2434,18 +2434,24 @@ class _MatchesSemanticsData extends Matcher {
return failWithDescription(matchState, 'maxValueLength was: ${data.maxValueLength}');
}
if (actions.isNotEmpty) {
final List<SemanticsAction> unexpectedActions = <SemanticsAction>[];
final List<SemanticsAction> missingActions = <SemanticsAction>[];
for (final MapEntry<ui.SemanticsAction, bool> actionEntry in actions.entries) {
final ui.SemanticsAction action = actionEntry.key;
final bool actionExpected = actionEntry.value;
final bool actionPresent = (action.index & data.actions) == action.index;
if (actionPresent != actionExpected) {
final List<String> actionSummary = <String>[
for (final int action in SemanticsAction.values.keys)
if ((data.actions & action) != 0) describeEnum(action),
];
return failWithDescription(matchState, 'actions were: $actionSummary');
if(actionExpected) {
missingActions.add(action);
} else {
unexpectedActions.add(action);
}
}
}
if (unexpectedActions.isNotEmpty || missingActions.isNotEmpty) {
return failWithDescription(matchState, 'missing actions: ${_createEnumsSummary(missingActions)} unexpected actions: ${_createEnumsSummary(unexpectedActions)}');
}
}
if (customActions != null || hintOverrides != null) {
final List<CustomSemanticsAction> providedCustomActions = data.customSemanticsActionIds?.map<CustomSemanticsAction>((int id) {
......@@ -2473,18 +2479,24 @@ class _MatchesSemanticsData extends Matcher {
}
}
if (flags.isNotEmpty) {
final List<SemanticsFlag> unexpectedFlags = <SemanticsFlag>[];
final List<SemanticsFlag> missingFlags = <SemanticsFlag>[];
for (final MapEntry<ui.SemanticsFlag, bool> flagEntry in flags.entries) {
final ui.SemanticsFlag flag = flagEntry.key;
final bool flagExpected = flagEntry.value;
final bool flagPresent = flag.index & data.flags == flag.index;
if (flagPresent != flagExpected) {
final List<String> flagSummary = <String>[
for (final int flag in SemanticsFlag.values.keys)
if ((data.flags & flag) != 0) describeEnum(flag),
];
return failWithDescription(matchState, 'flags were: $flagSummary');
if(flagExpected) {
missingFlags.add(flag);
} else {
unexpectedFlags.add(flag);
}
}
}
if (unexpectedFlags.isNotEmpty || missingFlags.isNotEmpty) {
return failWithDescription(matchState, 'missing flags: ${_createEnumsSummary(missingFlags)} unexpected flags: ${_createEnumsSummary(unexpectedFlags)}');
}
}
bool allMatched = true;
if (children != null) {
......@@ -2512,6 +2524,11 @@ class _MatchesSemanticsData extends Matcher {
) {
return mismatchDescription.add(matchState['failure'] as String);
}
static String _createEnumsSummary<T extends Object>(List<T> enums) {
assert(T == SemanticsAction || T == SemanticsFlag, 'This method is only intended for lists of SemanticsActions or SemanticsFlags.');
return '[${enums.map(describeEnum).join(', ')}]';
}
}
class _MatchesAccessibilityGuideline extends AsyncMatcher {
......
......@@ -733,6 +733,62 @@ void main() {
));
handle.dispose();
});
testWidgets('failure does not throw unexpected errors', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
addTearDown(() => handle.dispose());
const Key key = Key('semantics');
await tester.pumpWidget(Semantics(
key: key,
namesRoute: true,
header: true,
button: true,
link: true,
onTap: () { },
onLongPress: () { },
label: 'foo',
hint: 'bar',
value: 'baz',
increasedValue: 'a',
decreasedValue: 'b',
textDirection: TextDirection.rtl,
onTapHint: 'scan',
onLongPressHint: 'fill',
customSemanticsActions: <CustomSemanticsAction, VoidCallback>{
const CustomSemanticsAction(label: 'foo'): () { },
const CustomSemanticsAction(label: 'bar'): () { },
},
));
// This should fail due to the mis-match between the `namesRoute` value.
void failedExpectation() => expect(tester.getSemantics(find.byKey(key)),
matchesSemantics(
// Adding the explicit `false` for test readability
// ignore: avoid_redundant_argument_values
namesRoute: false,
label: 'foo',
hint: 'bar',
value: 'baz',
increasedValue: 'a',
decreasedValue: 'b',
textDirection: TextDirection.rtl,
hasTapAction: true,
hasLongPressAction: true,
isButton: true,
isLink: true,
isHeader: true,
onTapHint: 'scan',
onLongPressHint: 'fill',
customActions: <CustomSemanticsAction>[
const CustomSemanticsAction(label: 'foo'),
const CustomSemanticsAction(label: 'bar'),
],
),
);
expect(failedExpectation, throwsA(isA<TestFailure>()));
});
});
group('containsSemantics', () {
......@@ -1173,6 +1229,60 @@ void main() {
expect(node, containsSemantics(customActions: <CustomSemanticsAction>[action]));
});
testWidgets('failure does not throw unexpected errors', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
addTearDown(() => handle.dispose());
const Key key = Key('semantics');
await tester.pumpWidget(Semantics(
key: key,
namesRoute: true,
header: true,
button: true,
link: true,
onTap: () { },
onLongPress: () { },
label: 'foo',
hint: 'bar',
value: 'baz',
increasedValue: 'a',
decreasedValue: 'b',
textDirection: TextDirection.rtl,
onTapHint: 'scan',
onLongPressHint: 'fill',
customSemanticsActions: <CustomSemanticsAction, VoidCallback>{
const CustomSemanticsAction(label: 'foo'): () { },
const CustomSemanticsAction(label: 'bar'): () { },
},
));
// This should fail due to the mis-match between the `namesRoute` value.
void failedExpectation() => expect(tester.getSemantics(find.byKey(key)),
containsSemantics(
label: 'foo',
hint: 'bar',
value: 'baz',
increasedValue: 'a',
decreasedValue: 'b',
textDirection: TextDirection.rtl,
hasTapAction: true,
hasLongPressAction: true,
isButton: true,
isLink: true,
isHeader: true,
namesRoute: false,
onTapHint: 'scan',
onLongPressHint: 'fill',
customActions: <CustomSemanticsAction>[
const CustomSemanticsAction(label: 'foo'),
const CustomSemanticsAction(label: 'bar'),
],
),
);
expect(failedExpectation, throwsA(isA<TestFailure>()));
});
});
group('findsAtLeastNWidgets', () {
......
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