From 0d1a8a7798133bbb363bad53ecd0fc282aa2e92f Mon Sep 17 00:00:00 2001 From: Michael Goderbauer Date: Mon, 7 Jun 2021 13:53:08 -0700 Subject: [PATCH] Migrate android_semantics_testing to null safety (#84136) --- .../android_semantics_testing/lib/main.dart | 12 +-- .../lib/src/common.dart | 44 +++++----- .../lib/src/constants.dart | 4 +- .../lib/src/matcher.dart | 86 ++++++++++--------- .../lib/src/tests/controls_page.dart | 10 +-- .../lib/src/tests/headings_page.dart | 2 +- .../lib/src/tests/popup_page.dart | 6 +- .../lib/src/tests/text_field_page.dart | 2 +- .../android_semantics_testing/pubspec.yaml | 2 +- .../test_driver/main_test.dart | 6 +- 10 files changed, 91 insertions(+), 83 deletions(-) diff --git a/dev/integration_tests/android_semantics_testing/lib/main.dart b/dev/integration_tests/android_semantics_testing/lib/main.dart index e721e1ba52..0238f47388 100644 --- a/dev/integration_tests/android_semantics_testing/lib/main.dart +++ b/dev/integration_tests/android_semantics_testing/lib/main.dart @@ -23,18 +23,18 @@ void main() { const MethodChannel kSemanticsChannel = MethodChannel('semantics'); -Future dataHandler(String message) async { - if (message.contains('getSemanticsNode')) { +Future dataHandler(String? message) async { + if (message!.contains('getSemanticsNode')) { final Completer completer = Completer(); final int id = int.tryParse(message.split('#')[1]) ?? 0; - Future completeSemantics([Object _]) async { + Future completeSemantics([Object? _]) async { final dynamic result = await kSemanticsChannel.invokeMethod('getSemanticsNode', { 'id': id, }); completer.complete(json.encode(result)); } - if (SchedulerBinding.instance.hasScheduledFrame) - SchedulerBinding.instance.addPostFrameCallback(completeSemantics); + if (SchedulerBinding.instance!.hasScheduledFrame) + SchedulerBinding.instance!.addPostFrameCallback(completeSemantics); else completeSemantics(); return completer.future; @@ -50,7 +50,7 @@ Map routes = { }; class TestApp extends StatelessWidget { - const TestApp({Key key}) : super(key: key); + const TestApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/dev/integration_tests/android_semantics_testing/lib/src/common.dart b/dev/integration_tests/android_semantics_testing/lib/src/common.dart index 8879850835..69e4eedc9f 100644 --- a/dev/integration_tests/android_semantics_testing/lib/src/common.dart +++ b/dev/integration_tests/android_semantics_testing/lib/src/common.dart @@ -53,19 +53,19 @@ class AndroidSemanticsNode { /// ] /// } factory AndroidSemanticsNode.deserialize(String value) { - return AndroidSemanticsNode._(json.decode(value) as Map); + return AndroidSemanticsNode._(json.decode(value) as Map); } - final Map _values; + final Map _values; final List _children = []; - Map get _flags => _values['flags'] as Map; + Map get _flags => _values['flags']! as Map; /// The text value of the semantics node. /// /// This is produced by combining the value, label, and hint fields from /// the Flutter [SemanticsNode]. - String get text => _values['text'] as String; + String get text => _values['text']! as String; /// The contentDescription of the semantics node. /// @@ -75,7 +75,7 @@ class AndroidSemanticsNode { /// /// This is produced by combining the value, label, and hint fields from /// the Flutter [SemanticsNode]. - String get contentDescription => _values['contentDescription'] as String; + String get contentDescription => _values['contentDescription']! as String; /// The className of the semantics node. /// @@ -84,10 +84,10 @@ class AndroidSemanticsNode { /// /// If a more specific value isn't provided, it defaults to /// "android.view.View". - String get className => _values['className'] as String; + String get className => _values['className']! as String; /// The identifier for this semantics node. - int get id => _values['id'] as int; + int get id => _values['id']! as int; /// The children of this semantics node. List get children => _children; @@ -95,50 +95,50 @@ class AndroidSemanticsNode { /// Whether the node is currently in a checked state. /// /// Equivalent to [SemanticsFlag.isChecked]. - bool get isChecked => _flags['isChecked'] as bool; + bool get isChecked => _flags['isChecked']! as bool; /// Whether the node can be in a checked state. /// /// Equivalent to [SemanticsFlag.hasCheckedState] - bool get isCheckable => _flags['isCheckable'] as bool; + bool get isCheckable => _flags['isCheckable']! as bool; /// Whether the node is editable. /// /// This is usually only applied to text fields, which map /// to "android.widget.EditText". - bool get isEditable => _flags['isEditable'] as bool; + bool get isEditable => _flags['isEditable']! as bool; /// Whether the node is enabled. - bool get isEnabled => _flags['isEnabled'] as bool; + bool get isEnabled => _flags['isEnabled']! as bool; /// Whether the node is focusable. - bool get isFocusable => _flags['isFocusable'] as bool; + bool get isFocusable => _flags['isFocusable']! as bool; /// Whether the node is focused. - bool get isFocused => _flags['isFocused'] as bool; + bool get isFocused => _flags['isFocused']! as bool; /// Whether the node is considered a heading. - bool get isHeading => _flags['isHeading'] as bool; + bool get isHeading => _flags['isHeading']! as bool; /// Whether the node represents a password field. /// /// Equivalent to [SemanticsFlag.isObscured]. - bool get isPassword => _flags['isPassword'] as bool; + bool get isPassword => _flags['isPassword']! as bool; /// Whether the node is long clickable. /// /// Equivalent to having [SemanticsAction.longPress]. - bool get isLongClickable => _flags['isLongClickable'] as bool; + bool get isLongClickable => _flags['isLongClickable']! as bool; /// Gets a [Rect] which defines the position and size of the semantics node. Rect getRect() { - final Map rawRect = _values['rect'] as Map; + final Map rawRect = _values['rect']! as Map; final Map rect = rawRect.cast(); return Rect.fromLTRB( - rect['left'].toDouble(), - rect['top'].toDouble(), - rect['right'].toDouble(), - rect['bottom'].toDouble(), + rect['left']!.toDouble(), + rect['top']!.toDouble(), + rect['right']!.toDouble(), + rect['bottom']!.toDouble(), ); } @@ -150,7 +150,7 @@ class AndroidSemanticsNode { /// Gets a list of [AndroidSemanticsActions] which are defined for the node. List getActions() => [ - for (final int id in (_values['actions'] as List).cast()) AndroidSemanticsAction.deserialize(id), + for (final int id in (_values['actions']! as List).cast()) AndroidSemanticsAction.deserialize(id)!, ]; @override diff --git a/dev/integration_tests/android_semantics_testing/lib/src/constants.dart b/dev/integration_tests/android_semantics_testing/lib/src/constants.dart index 66ebff9b89..da772796ab 100644 --- a/dev/integration_tests/android_semantics_testing/lib/src/constants.dart +++ b/dev/integration_tests/android_semantics_testing/lib/src/constants.dart @@ -168,7 +168,7 @@ class AndroidSemanticsAction { case _kSetText: return 'AndroidSemanticsAction.setText'; default: - return null; + return 'INVALID'; } } @@ -210,7 +210,7 @@ class AndroidSemanticsAction { /// Creates a new [AndroidSemanticsAction] from an integer `value`. /// /// Returns `null` if the id is not a known Android accessibility action. - static AndroidSemanticsAction deserialize(int value) { + static AndroidSemanticsAction? deserialize(int value) { return _kActionById[value]; } } diff --git a/dev/integration_tests/android_semantics_testing/lib/src/matcher.dart b/dev/integration_tests/android_semantics_testing/lib/src/matcher.dart index d3f7f27555..4daa83d6f4 100644 --- a/dev/integration_tests/android_semantics_testing/lib/src/matcher.dart +++ b/dev/integration_tests/android_semantics_testing/lib/src/matcher.dart @@ -18,23 +18,23 @@ import 'constants.dart'; /// the Android accessibility bridge, and not the semantics object created by /// the Flutter framework. Matcher hasAndroidSemantics({ - String text, - String contentDescription, - String className, - int id, - Rect rect, - Size size, - List actions, - List children, - bool isChecked, - bool isCheckable, - bool isEditable, - bool isEnabled, - bool isFocusable, - bool isFocused, - bool isHeading, - bool isPassword, - bool isLongClickable, + String? text, + String? contentDescription, + String? className, + int? id, + Rect? rect, + Size? size, + List? actions, + List? children, + bool? isChecked, + bool? isCheckable, + bool? isEditable, + bool? isEnabled, + bool? isFocusable, + bool? isFocused, + bool? isHeading, + bool? isPassword, + bool? isLongClickable, }) { return _AndroidSemanticsMatcher( text: text, @@ -76,22 +76,22 @@ class _AndroidSemanticsMatcher extends Matcher { this.isLongClickable, }); - final String text; - final String className; - final String contentDescription; - final int id; - final List actions; - final Rect rect; - final Size size; - final bool isChecked; - final bool isCheckable; - final bool isEditable; - final bool isEnabled; - final bool isFocusable; - final bool isFocused; - final bool isHeading; - final bool isPassword; - final bool isLongClickable; + final String? text; + final String? className; + final String? contentDescription; + final int? id; + final List? actions; + final Rect? rect; + final Size? size; + final bool? isChecked; + final bool? isCheckable; + final bool? isEditable; + final bool? isEnabled; + final bool? isFocusable; + final bool? isFocused; + final bool? isHeading; + final bool? isPassword; + final bool? isLongClickable; @override Description describe(Description description) { @@ -130,7 +130,10 @@ class _AndroidSemanticsMatcher extends Matcher { } @override - bool matches(covariant AndroidSemanticsNode item, Map matchState) { + bool matches(covariant AndroidSemanticsNode? item, Map matchState) { + if (item == null) { + return false; + } if (text != null && text != item.text) return _failWithMessage('Expected text: $text', matchState); if (contentDescription != null && contentDescription != item.contentDescription) @@ -145,8 +148,8 @@ class _AndroidSemanticsMatcher extends Matcher { return _failWithMessage('Expected size: $size', matchState); if (actions != null) { final List itemActions = item.getActions(); - if (!unorderedEquals(actions).matches(itemActions, matchState)) { - final List actionsString = actions.map((AndroidSemanticsAction action) => action.toString()).toList()..sort(); + if (!unorderedEquals(actions!).matches(itemActions, matchState)) { + final List actionsString = actions!.map((AndroidSemanticsAction action) => action.toString()).toList()..sort(); final List itemActionsString = itemActions.map((AndroidSemanticsAction action) => action.toString()).toList()..sort(); final Set unexpected = itemActionsString.toSet().difference(actionsString.toSet()); final Set missing = actionsString.toSet().difference(itemActionsString.toSet()); @@ -176,9 +179,14 @@ class _AndroidSemanticsMatcher extends Matcher { } @override - Description describeMismatch(Object item, Description mismatchDescription, - Map matchState, bool verbose) { - return mismatchDescription.add(matchState['failure'] as String); + Description describeMismatch( + Object? item, + Description mismatchDescription, + Map matchState, + bool verbose, + ) { + print(matchState); + return mismatchDescription.add(matchState['failure']! as String); } bool _failWithMessage(String value, Map matchState) { diff --git a/dev/integration_tests/android_semantics_testing/lib/src/tests/controls_page.dart b/dev/integration_tests/android_semantics_testing/lib/src/tests/controls_page.dart index 1df9e298f4..a71dbafa83 100644 --- a/dev/integration_tests/android_semantics_testing/lib/src/tests/controls_page.dart +++ b/dev/integration_tests/android_semantics_testing/lib/src/tests/controls_page.dart @@ -9,7 +9,7 @@ export 'controls_constants.dart'; /// A test page with a checkbox, three radio buttons, and a switch. class SelectionControlsPage extends StatefulWidget { - const SelectionControlsPage({Key key}) : super(key: key); + const SelectionControlsPage({Key? key}) : super(key: key); @override State createState() => _SelectionControlsPageState(); @@ -28,15 +28,15 @@ class _SelectionControlsPageState extends State { bool _isLabeledOn = false; int _radio = 0; - void _updateCheckbox(bool newValue) { + void _updateCheckbox(bool? newValue) { setState(() { - _isChecked = newValue; + _isChecked = newValue!; }); } - void _updateRadio(int newValue) { + void _updateRadio(int? newValue) { setState(() { - _radio = newValue; + _radio = newValue!; }); } diff --git a/dev/integration_tests/android_semantics_testing/lib/src/tests/headings_page.dart b/dev/integration_tests/android_semantics_testing/lib/src/tests/headings_page.dart index b08ebdbbe4..108582373d 100644 --- a/dev/integration_tests/android_semantics_testing/lib/src/tests/headings_page.dart +++ b/dev/integration_tests/android_semantics_testing/lib/src/tests/headings_page.dart @@ -9,7 +9,7 @@ export 'headings_constants.dart'; /// A test page with an app bar and some body text for testing heading flags. class HeadingsPage extends StatelessWidget { - const HeadingsPage({Key key}) : super(key: key); + const HeadingsPage({Key? key}) : super(key: key); static const ValueKey _appBarTitleKey = ValueKey(appBarTitleKeyValue); static const ValueKey _bodyTextKey = ValueKey(bodyTextKeyValue); diff --git a/dev/integration_tests/android_semantics_testing/lib/src/tests/popup_page.dart b/dev/integration_tests/android_semantics_testing/lib/src/tests/popup_page.dart index 6f0b064fc0..039e210c85 100644 --- a/dev/integration_tests/android_semantics_testing/lib/src/tests/popup_page.dart +++ b/dev/integration_tests/android_semantics_testing/lib/src/tests/popup_page.dart @@ -10,7 +10,7 @@ export 'popup_constants.dart'; /// A page with a popup menu, a dropdown menu, and a modal alert. class PopupControlsPage extends StatefulWidget { - const PopupControlsPage({Key key}) : super(key: key); + const PopupControlsPage({Key? key}) : super(key: key); @override State createState() => _PopupControlsPageState(); @@ -22,7 +22,7 @@ class _PopupControlsPageState extends State { final Key alertKey = const ValueKey(alertKeyValue); String popupValue = popupItems.first; - String dropdownValue = popupItems.first; + String? dropdownValue = popupItems.first; @override Widget build(BuildContext context) { @@ -60,7 +60,7 @@ class _PopupControlsPageState extends State { child: Text(item), ); }).toList(), - onChanged: (String value) { + onChanged: (String? value) { setState(() { dropdownValue = value; }); diff --git a/dev/integration_tests/android_semantics_testing/lib/src/tests/text_field_page.dart b/dev/integration_tests/android_semantics_testing/lib/src/tests/text_field_page.dart index 71c061f377..2c7624b29a 100644 --- a/dev/integration_tests/android_semantics_testing/lib/src/tests/text_field_page.dart +++ b/dev/integration_tests/android_semantics_testing/lib/src/tests/text_field_page.dart @@ -10,7 +10,7 @@ export 'text_field_constants.dart'; /// A page with a normal text field and a password field. class TextFieldPage extends StatefulWidget { - const TextFieldPage({Key key}) : super(key: key); + const TextFieldPage({Key? key}) : super(key: key); @override State createState() => _TextFieldPageState(); diff --git a/dev/integration_tests/android_semantics_testing/pubspec.yaml b/dev/integration_tests/android_semantics_testing/pubspec.yaml index 77128d5836..ed6153f5f0 100644 --- a/dev/integration_tests/android_semantics_testing/pubspec.yaml +++ b/dev/integration_tests/android_semantics_testing/pubspec.yaml @@ -1,7 +1,7 @@ name: android_semantics_testing description: Integration testing library for Android semantics environment: - sdk: '>=2.9.0 <3.0.0' + sdk: '>=2.12.0 <3.0.0' dependencies: flutter: diff --git a/dev/integration_tests/android_semantics_testing/test_driver/main_test.dart b/dev/integration_tests/android_semantics_testing/test_driver/main_test.dart index 7c59114509..0bb0715ddb 100644 --- a/dev/integration_tests/android_semantics_testing/test_driver/main_test.dart +++ b/dev/integration_tests/android_semantics_testing/test_driver/main_test.dart @@ -11,7 +11,7 @@ import 'package:path/path.dart' as path; import 'package:test/test.dart' hide isInstanceOf; 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) { return 'adb'; } else { @@ -21,7 +21,7 @@ String adbPath() { void main() { group('AccessibilityBridge', () { - FlutterDriver driver; + late FlutterDriver driver; Future getSemantics(SerializableFinder finder) async { final int id = await driver.getSemanticsId(finder); final String data = await driver.requestData('getSemanticsNode#$id'); @@ -53,7 +53,7 @@ void main() { 'null', ]); await run.exitCode; - driver?.close(); + driver.close(); }); group('TextField', () {