Unverified Commit 166f1d76 authored by chunhtai's avatar chunhtai Committed by GitHub

Fix android semantics integration test flakiness (#94875)

parent 77402ce5
...@@ -27,7 +27,6 @@ import io.flutter.plugin.common.MethodChannel; ...@@ -27,7 +27,6 @@ import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugins.GeneratedPluginRegistrant; import io.flutter.plugins.GeneratedPluginRegistrant;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo;
...@@ -67,25 +66,6 @@ public class MainActivity extends FlutterActivity { ...@@ -67,25 +66,6 @@ public class MainActivity extends FlutterActivity {
result.success(convertSemantics(node, id)); result.success(convertSemantics(node, id));
return; 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(); result.notImplemented();
} }
......
...@@ -39,21 +39,6 @@ Future<String> dataHandler(String message) async { ...@@ -39,21 +39,6 @@ Future<String> dataHandler(String message) async {
completeSemantics(); completeSemantics();
return completer.future; 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(); throw UnimplementedError();
} }
......
...@@ -25,6 +25,7 @@ Matcher hasAndroidSemantics({ ...@@ -25,6 +25,7 @@ Matcher hasAndroidSemantics({
Rect rect, Rect rect,
Size size, Size size,
List<AndroidSemanticsAction> actions, List<AndroidSemanticsAction> actions,
List<AndroidSemanticsAction> ignoredActions,
List<AndroidSemanticsNode> children, List<AndroidSemanticsNode> children,
bool isChecked, bool isChecked,
bool isCheckable, bool isCheckable,
...@@ -44,6 +45,7 @@ Matcher hasAndroidSemantics({ ...@@ -44,6 +45,7 @@ Matcher hasAndroidSemantics({
size: size, size: size,
id: id, id: id,
actions: actions, actions: actions,
ignoredActions: ignoredActions,
isChecked: isChecked, isChecked: isChecked,
isCheckable: isCheckable, isCheckable: isCheckable,
isEditable: isEditable, isEditable: isEditable,
...@@ -63,6 +65,7 @@ class _AndroidSemanticsMatcher extends Matcher { ...@@ -63,6 +65,7 @@ class _AndroidSemanticsMatcher extends Matcher {
this.className, this.className,
this.id, this.id,
this.actions, this.actions,
this.ignoredActions,
this.rect, this.rect,
this.size, this.size,
this.isChecked, this.isChecked,
...@@ -81,6 +84,7 @@ class _AndroidSemanticsMatcher extends Matcher { ...@@ -81,6 +84,7 @@ class _AndroidSemanticsMatcher extends Matcher {
final String contentDescription; final String contentDescription;
final int id; final int id;
final List<AndroidSemanticsAction> actions; final List<AndroidSemanticsAction> actions;
final List<AndroidSemanticsAction> ignoredActions;
final Rect rect; final Rect rect;
final Size size; final Size size;
final bool isChecked; final bool isChecked;
...@@ -148,9 +152,13 @@ class _AndroidSemanticsMatcher extends Matcher { ...@@ -148,9 +152,13 @@ class _AndroidSemanticsMatcher extends Matcher {
if (!unorderedEquals(actions).matches(itemActions, matchState)) { if (!unorderedEquals(actions).matches(itemActions, matchState)) {
final List<String> actionsString = actions.map<String>((AndroidSemanticsAction action) => action.toString()).toList()..sort(); 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 List<String> itemActionsString = itemActions.map<String>((AndroidSemanticsAction action) => action.toString()).toList()..sort();
final Set<String> unexpected = itemActionsString.toSet().difference(actionsString.toSet()); final Set<AndroidSemanticsAction> unexpected = itemActions.toSet().difference(actions.toSet());
final Set<String> missing = actionsString.toSet().difference(itemActionsString.toSet()); final Set<String> unexpectedInString = itemActionsString.toSet().difference(actionsString.toSet());
return _failWithMessage('Expected actions: $actionsString\nActual actions: $itemActionsString\nUnexpected: $unexpected\nMissing: $missing', matchState); 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) if (isChecked != null && isChecked != item.isChecked)
......
...@@ -11,6 +11,14 @@ import 'package:path/path.dart' as path; ...@@ -11,6 +11,14 @@ import 'package:path/path.dart' as path;
import 'package:pub_semver/pub_semver.dart'; import 'package:pub_semver/pub_semver.dart';
import 'package:test/test.dart' hide isInstanceOf; 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() { String adbPath() {
final String androidHome = io.Platform.environment['ANDROID_HOME'] ?? io.Platform.environment['ANDROID_SDK_ROOT']; final String androidHome = io.Platform.environment['ANDROID_HOME'] ?? io.Platform.environment['ANDROID_SDK_ROOT'];
if (androidHome == null) { if (androidHome == null) {
...@@ -29,11 +37,6 @@ void main() { ...@@ -29,11 +37,6 @@ void main() {
return AndroidSemanticsNode.deserialize(data); 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. // The version of TalkBack running on the device.
Version talkbackVersion; Version talkbackVersion;
...@@ -153,10 +156,6 @@ void main() { ...@@ -153,10 +156,6 @@ void main() {
matching: find.byType('Semantics'), matching: find.byType('Semantics'),
firstMatchOnly: true, 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( expect(
await getSemantics(normalTextField), await getSemantics(normalTextField),
hasAndroidSemantics( hasAndroidSemantics(
...@@ -166,9 +165,10 @@ void main() { ...@@ -166,9 +165,10 @@ void main() {
isFocused: false, isFocused: false,
isPassword: false, isPassword: false,
actions: <AndroidSemanticsAction>[ actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.accessibilityFocus,
AndroidSemanticsAction.click, AndroidSemanticsAction.click,
], ],
// We can't predict the a11y focus when the screen changes.
ignoredActions: ignoredAccessibilityFocusActions
), ),
); );
......
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