forked from firka/flutter
Fix android semantics integration test flakiness (#94875)
This commit is contained in:
@@ -27,7 +27,6 @@ import io.flutter.plugin.common.MethodChannel;
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityNodeProvider;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
@@ -67,25 +66,6 @@ public class MainActivity extends FlutterActivity {
|
||||
result.success(convertSemantics(node, id));
|
||||
return;
|
||||
}
|
||||
if (methodCall.method.equals("sendSemanticsFocus")) {
|
||||
Map<String, Object> data = methodCall.arguments();
|
||||
@SuppressWarnings("unchecked")
|
||||
Integer id = (Integer) data.get("id");
|
||||
if (id == null) {
|
||||
result.error("No ID provided", "", null);
|
||||
return;
|
||||
}
|
||||
if (provider == null) {
|
||||
result.error("Semantics not enabled", "", null);
|
||||
return;
|
||||
}
|
||||
AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED);
|
||||
event.setPackageName(flutterView.getContext().getPackageName());
|
||||
event.setSource(flutterView, id);
|
||||
flutterView.getParent().requestSendAccessibilityEvent(flutterView, event);
|
||||
result.success(null);
|
||||
return;
|
||||
}
|
||||
result.notImplemented();
|
||||
}
|
||||
|
||||
|
||||
@@ -39,21 +39,6 @@ Future<String> dataHandler(String message) async {
|
||||
completeSemantics();
|
||||
return completer.future;
|
||||
}
|
||||
if (message.contains('sendSemanticsFocus')) {
|
||||
final Completer<String> completer = Completer<String>();
|
||||
final int id = int.tryParse(message.split('#')[1]) ?? 0;
|
||||
Future<void> completeSemantics([Object _]) async {
|
||||
final dynamic result = await kSemanticsChannel.invokeMethod<dynamic>('sendSemanticsFocus', <String, dynamic>{
|
||||
'id': id,
|
||||
});
|
||||
completer.complete(json.encode(result));
|
||||
}
|
||||
if (SchedulerBinding.instance.hasScheduledFrame)
|
||||
SchedulerBinding.instance.addPostFrameCallback(completeSemantics);
|
||||
else
|
||||
completeSemantics();
|
||||
return completer.future;
|
||||
}
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ Matcher hasAndroidSemantics({
|
||||
Rect rect,
|
||||
Size size,
|
||||
List<AndroidSemanticsAction> actions,
|
||||
List<AndroidSemanticsAction> ignoredActions,
|
||||
List<AndroidSemanticsNode> children,
|
||||
bool isChecked,
|
||||
bool isCheckable,
|
||||
@@ -44,6 +45,7 @@ Matcher hasAndroidSemantics({
|
||||
size: size,
|
||||
id: id,
|
||||
actions: actions,
|
||||
ignoredActions: ignoredActions,
|
||||
isChecked: isChecked,
|
||||
isCheckable: isCheckable,
|
||||
isEditable: isEditable,
|
||||
@@ -63,6 +65,7 @@ class _AndroidSemanticsMatcher extends Matcher {
|
||||
this.className,
|
||||
this.id,
|
||||
this.actions,
|
||||
this.ignoredActions,
|
||||
this.rect,
|
||||
this.size,
|
||||
this.isChecked,
|
||||
@@ -81,6 +84,7 @@ class _AndroidSemanticsMatcher extends Matcher {
|
||||
final String contentDescription;
|
||||
final int id;
|
||||
final List<AndroidSemanticsAction> actions;
|
||||
final List<AndroidSemanticsAction> ignoredActions;
|
||||
final Rect rect;
|
||||
final Size size;
|
||||
final bool isChecked;
|
||||
@@ -148,9 +152,13 @@ class _AndroidSemanticsMatcher extends Matcher {
|
||||
if (!unorderedEquals(actions).matches(itemActions, matchState)) {
|
||||
final List<String> actionsString = actions.map<String>((AndroidSemanticsAction action) => action.toString()).toList()..sort();
|
||||
final List<String> itemActionsString = itemActions.map<String>((AndroidSemanticsAction action) => action.toString()).toList()..sort();
|
||||
final Set<String> unexpected = itemActionsString.toSet().difference(actionsString.toSet());
|
||||
final Set<String> missing = actionsString.toSet().difference(itemActionsString.toSet());
|
||||
return _failWithMessage('Expected actions: $actionsString\nActual actions: $itemActionsString\nUnexpected: $unexpected\nMissing: $missing', matchState);
|
||||
final Set<AndroidSemanticsAction> unexpected = itemActions.toSet().difference(actions.toSet());
|
||||
final Set<String> unexpectedInString = itemActionsString.toSet().difference(actionsString.toSet());
|
||||
final Set<String> missingInString = actionsString.toSet().difference(itemActionsString.toSet());
|
||||
if (missingInString.isEmpty && ignoredActions != null && unexpected.every(ignoredActions.contains)) {
|
||||
return true;
|
||||
}
|
||||
return _failWithMessage('Expected actions: $actionsString\nActual actions: $itemActionsString\nUnexpected: $unexpectedInString\nMissing: $missingInString', matchState);
|
||||
}
|
||||
}
|
||||
if (isChecked != null && isChecked != item.isChecked)
|
||||
|
||||
@@ -11,6 +11,14 @@ import 'package:path/path.dart' as path;
|
||||
import 'package:pub_semver/pub_semver.dart';
|
||||
import 'package:test/test.dart' hide isInstanceOf;
|
||||
|
||||
// The accessibility focus actions are added when a semantics node receives or
|
||||
// lose accessibility focus. This test ignores these actions since it is hard to
|
||||
// predict which node has the accessibility focus after a screen changes.
|
||||
const List<AndroidSemanticsAction> ignoredAccessibilityFocusActions = <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.accessibilityFocus,
|
||||
AndroidSemanticsAction.clearAccessibilityFocus,
|
||||
];
|
||||
|
||||
String adbPath() {
|
||||
final String androidHome = io.Platform.environment['ANDROID_HOME'] ?? io.Platform.environment['ANDROID_SDK_ROOT'];
|
||||
if (androidHome == null) {
|
||||
@@ -29,11 +37,6 @@ void main() {
|
||||
return AndroidSemanticsNode.deserialize(data);
|
||||
}
|
||||
|
||||
Future<void> sendSemanticsFocus(SerializableFinder finder) async {
|
||||
final int id = await driver.getSemanticsId(finder);
|
||||
await driver.requestData('sendSemanticsFocus#$id');
|
||||
}
|
||||
|
||||
// The version of TalkBack running on the device.
|
||||
Version talkbackVersion;
|
||||
|
||||
@@ -153,10 +156,6 @@ void main() {
|
||||
matching: find.byType('Semantics'),
|
||||
firstMatchOnly: true,
|
||||
);
|
||||
// Make sure the focus is on the back button.
|
||||
await sendSemanticsFocus(find.byValueKey(backButtonKeyValue));
|
||||
await Future<void>.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
expect(
|
||||
await getSemantics(normalTextField),
|
||||
hasAndroidSemantics(
|
||||
@@ -166,9 +165,10 @@ void main() {
|
||||
isFocused: false,
|
||||
isPassword: false,
|
||||
actions: <AndroidSemanticsAction>[
|
||||
AndroidSemanticsAction.accessibilityFocus,
|
||||
AndroidSemanticsAction.click,
|
||||
],
|
||||
// We can't predict the a11y focus when the screen changes.
|
||||
ignoredActions: ignoredAccessibilityFocusActions
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user