Trigger display_cutout_rotation flutter driver test in ci. (#162641)
Fixes https://github.com/flutter/flutter/issues/162615 Can test 2 different ways. On a mac (or linux machine) with adb on the path (or android sdk set in ANDROID_HOME) and an emulator running (or physical device attached) that is api 30 or higher. ``` cd dev/devicelab dart bin/test_runner.dart test -t android_display_cutout ``` OR ``` dev/integration_tests/display_cutout_rotation flutter drive integration_test/display_cutout_test.dart ``` Proof the test ran successfully ``` [2025-02-12 08:08:22.069817] [STDOUT] Removing Synthetic notch... [2025-02-12 08:08:22.071147] [STDOUT] Executing "/b/s/w/ir/cache/android/sdk/platform-tools/adb -s emulator-5554 shell cmd overlay disable com.android.internal.display.cutout.emulation.tall" in "/b/s/w/ir/x/w/rc/tmpk3k3yhhp/flutter sdk/dev/integration_tests/display_cutout_rotation/" with environment {BOT: true, LANG: en_US.UTF-8} [2025-02-12 08:08:22.862219] [STDOUT] Checking for reboot [android_defines_test] Process terminated with exit code 0. Task result: { "success": true, "data": null, "detailFiles": [], "benchmarkScoreKeys": [], "reason": "success" } ``` https://logs.chromium.org/logs/flutter/buildbucket/cr-buildbucket/8723125792202374961/+/u/run_android_defines_test/stdout All checks passed https://github.com/flutter/flutter/pull/162641/checks?check_run_id=36991537539 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
This commit is contained in:
parent
c3fc69d18d
commit
0f5d669acb
10
.ci.yaml
10
.ci.yaml
@ -1128,6 +1128,16 @@ targets:
|
|||||||
["devicelab", "hostonly", "linux"]
|
["devicelab", "hostonly", "linux"]
|
||||||
task_name: linux_desktop_impeller
|
task_name: linux_desktop_impeller
|
||||||
|
|
||||||
|
- name: Linux_android_emu android_display_cutout
|
||||||
|
recipe: devicelab/devicelab_drone
|
||||||
|
timeout: 60
|
||||||
|
bringup: true
|
||||||
|
properties:
|
||||||
|
tags: >
|
||||||
|
["devicelab", "linux"]
|
||||||
|
task_name: android_display_cutout
|
||||||
|
presubmit_max_attempts: "2"
|
||||||
|
|
||||||
- name: Linux android_release_builds_exclude_dev_dependencies_test
|
- name: Linux android_release_builds_exclude_dev_dependencies_test
|
||||||
recipe: devicelab/devicelab_drone
|
recipe: devicelab/devicelab_drone
|
||||||
timeout: 60
|
timeout: 60
|
||||||
|
@ -301,6 +301,7 @@
|
|||||||
/dev/devicelab/bin/tasks/windows_desktop_impeller.dart @jonahwilliams @flutter/engine
|
/dev/devicelab/bin/tasks/windows_desktop_impeller.dart @jonahwilliams @flutter/engine
|
||||||
/dev/devicelab/bin/tasks/mac_desktop_impeller.dart @jonahwilliams @flutter/engine
|
/dev/devicelab/bin/tasks/mac_desktop_impeller.dart @jonahwilliams @flutter/engine
|
||||||
/dev/devicelab/bin/tasks/linux_desktop_impeller.dart @jonahwilliams @flutter/engine
|
/dev/devicelab/bin/tasks/linux_desktop_impeller.dart @jonahwilliams @flutter/engine
|
||||||
|
/dev/devicelab/bin/tasks/android_display_cutout.dart @reidbaker @flutter/android
|
||||||
|
|
||||||
## Host only framework tests
|
## Host only framework tests
|
||||||
# Linux docs_deploy_beta
|
# Linux docs_deploy_beta
|
||||||
|
12
dev/devicelab/bin/tasks/android_display_cutout.dart
Normal file
12
dev/devicelab/bin/tasks/android_display_cutout.dart
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter_devicelab/framework/devices.dart';
|
||||||
|
import 'package:flutter_devicelab/framework/framework.dart';
|
||||||
|
import 'package:flutter_devicelab/tasks/integration_tests.dart';
|
||||||
|
|
||||||
|
Future<void> main() async {
|
||||||
|
deviceOperatingSystem = DeviceOperatingSystem.android;
|
||||||
|
await task(createDisplayCutoutTest());
|
||||||
|
}
|
@ -139,6 +139,44 @@ TaskFunction createSolidColorTest({required bool enableImpeller}) {
|
|||||||
).call;
|
).call;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Can run on emulator or physical android device.
|
||||||
|
// Device must have developer settings enabled.
|
||||||
|
// Device must be android api 30 or higher.
|
||||||
|
TaskFunction createDisplayCutoutTest() {
|
||||||
|
return IntegrationTest(
|
||||||
|
'${flutterDirectory.path}/dev/integration_tests/display_cutout_rotation/',
|
||||||
|
'integration_test/display_cutout_test.dart',
|
||||||
|
setup: (Device device) async {
|
||||||
|
if (device is! AndroidDevice) {
|
||||||
|
// Only android devices support this cutoutTest.
|
||||||
|
throw TaskResult.failure('This test should only target android');
|
||||||
|
}
|
||||||
|
// Test requires developer settings added in 28 and behavior added in 30.
|
||||||
|
final String sdkResult = await device.shellEval('getprop', <String>['ro.build.version.sdk']);
|
||||||
|
if (sdkResult.startsWith('2') || sdkResult.startsWith('1') || sdkResult.length == 1) {
|
||||||
|
throw TaskResult.failure('This test should only target android 30+.');
|
||||||
|
}
|
||||||
|
print('Adding Synthetic notch...');
|
||||||
|
// This command will cause any running android activity to be recreated.
|
||||||
|
await device.shellExec('cmd', <String>[
|
||||||
|
'overlay',
|
||||||
|
'enable',
|
||||||
|
'com.android.internal.display.cutout.emulation.tall',
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
tearDown: (Device device) async {
|
||||||
|
if (device is AndroidDevice) {
|
||||||
|
print('Removing Synthetic notch...');
|
||||||
|
await device.shellExec('cmd', <String>[
|
||||||
|
'overlay',
|
||||||
|
'disable',
|
||||||
|
'com.android.internal.display.cutout.emulation.tall',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
).call;
|
||||||
|
}
|
||||||
|
|
||||||
TaskFunction dartDefinesTask() {
|
TaskFunction dartDefinesTask() {
|
||||||
return DriverTest(
|
return DriverTest(
|
||||||
'${flutterDirectory.path}/dev/integration_tests/ui',
|
'${flutterDirectory.path}/dev/integration_tests/ui',
|
||||||
@ -217,7 +255,6 @@ class DriverTest {
|
|||||||
...extraOptions,
|
...extraOptions,
|
||||||
];
|
];
|
||||||
await flutter('drive', options: options, environment: environment);
|
await flutter('drive', options: options, environment: environment);
|
||||||
|
|
||||||
return TaskResult.success(null);
|
return TaskResult.success(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -231,6 +268,8 @@ class IntegrationTest {
|
|||||||
this.createPlatforms = const <String>[],
|
this.createPlatforms = const <String>[],
|
||||||
this.withTalkBack = false,
|
this.withTalkBack = false,
|
||||||
this.environment,
|
this.environment,
|
||||||
|
this.setup,
|
||||||
|
this.tearDown,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String testDirectory;
|
final String testDirectory;
|
||||||
@ -240,12 +279,19 @@ class IntegrationTest {
|
|||||||
final bool withTalkBack;
|
final bool withTalkBack;
|
||||||
final Map<String, String>? environment;
|
final Map<String, String>? environment;
|
||||||
|
|
||||||
|
/// Run before flutter drive with the result from devices.workingDevice.
|
||||||
|
final Future<void> Function(Device device)? setup;
|
||||||
|
|
||||||
|
/// Run after flutter drive with the result from devices.workingDevice.
|
||||||
|
final Future<void> Function(Device device)? tearDown;
|
||||||
|
|
||||||
Future<TaskResult> call() {
|
Future<TaskResult> call() {
|
||||||
return inDirectory<TaskResult>(testDirectory, () async {
|
return inDirectory<TaskResult>(testDirectory, () async {
|
||||||
final Device device = await devices.workingDevice;
|
final Device device = await devices.workingDevice;
|
||||||
await device.unlock();
|
await device.unlock();
|
||||||
final String deviceId = device.deviceId;
|
final String deviceId = device.deviceId;
|
||||||
await flutter('packages', options: <String>['get']);
|
await flutter('packages', options: <String>['get']);
|
||||||
|
await setup?.call(await devices.workingDevice);
|
||||||
|
|
||||||
if (createPlatforms.isNotEmpty) {
|
if (createPlatforms.isNotEmpty) {
|
||||||
await flutter(
|
await flutter(
|
||||||
@ -265,6 +311,7 @@ class IntegrationTest {
|
|||||||
|
|
||||||
final List<String> options = <String>['-v', '-d', deviceId, testTarget, ...extraOptions];
|
final List<String> options = <String>['-v', '-d', deviceId, testTarget, ...extraOptions];
|
||||||
await flutter('test', options: options, environment: environment);
|
await flutter('test', options: options, environment: environment);
|
||||||
|
await tearDown?.call(await devices.workingDevice);
|
||||||
|
|
||||||
if (withTalkBack) {
|
if (withTalkBack) {
|
||||||
await disableTalkBack();
|
await disableTalkBack();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# display_cutout_rotation
|
# display_cutout_rotation
|
||||||
|
|
||||||
To run test locally use `flutter drive integration_test/display_cutout_test.dart` from this folder.
|
To run test locally use `flutter test integration_test/display_cutout_test.dart` from this folder.
|
||||||
|
|
||||||
OR from `flutter/dev/devicelab` run `dart bin/test_runner.dart test -t android_display_cutout`.
|
OR from `flutter/dev/devicelab` run `dart bin/test_runner.dart test -t android_display_cutout`.
|
||||||
|
@ -14,7 +14,7 @@ void main() {
|
|||||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
group('end-to-end test', () {
|
group('end-to-end test', () {
|
||||||
// Test assumes a custom driver that enables
|
// Test assumes that the device already has enabled
|
||||||
// "com.android.internal.display.cutout.emulation.tall".
|
// "com.android.internal.display.cutout.emulation.tall".
|
||||||
testWidgets('cutout should be on top in portrait mode', (WidgetTester tester) async {
|
testWidgets('cutout should be on top in portrait mode', (WidgetTester tester) async {
|
||||||
// Force rotation
|
// Force rotation
|
||||||
|
@ -1,104 +0,0 @@
|
|||||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter_driver/flutter_driver.dart';
|
|
||||||
|
|
||||||
// display_cutout needs a custom driver becuase cutout manipulations needs to be
|
|
||||||
// done to a device/emulator in order for the tests to pass.
|
|
||||||
Future<void> main() async {
|
|
||||||
if (!(Platform.isLinux || Platform.isMacOS)) {
|
|
||||||
// Not a fundemental limitation, developer shortcut.
|
|
||||||
print('This test must be run on a POSIX host. Skipping...');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final bool adbExists = Process.runSync('which', <String>['adb']).exitCode == 0;
|
|
||||||
if (!adbExists) {
|
|
||||||
print(r'This test needs ADB to exist on the $PATH.');
|
|
||||||
exitCode = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Test requires developer settings added in 28 and behavior added in 30
|
|
||||||
final ProcessResult checkApiLevel = Process.runSync('adb', <String>[
|
|
||||||
'shell',
|
|
||||||
'getprop',
|
|
||||||
'ro.build.version.sdk',
|
|
||||||
]);
|
|
||||||
final String apiStdout = checkApiLevel.stdout.toString();
|
|
||||||
// Api level 30 or higher.
|
|
||||||
if (apiStdout.startsWith('2') || apiStdout.startsWith('1') || apiStdout.length == 1) {
|
|
||||||
print('This test must be run on api 30 or higher. Skipping...');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Developer settings are required on target device for cutout manipulation.
|
|
||||||
bool shouldResetDevSettings = false;
|
|
||||||
final ProcessResult checkDevSettingsResult = Process.runSync('adb', <String>[
|
|
||||||
'shell',
|
|
||||||
'settings',
|
|
||||||
'get',
|
|
||||||
'global',
|
|
||||||
'development_settings_enabled',
|
|
||||||
]);
|
|
||||||
if (checkDevSettingsResult.stdout.toString().startsWith('0')) {
|
|
||||||
print('Enabling developer settings...');
|
|
||||||
// Developer settings not enabled, enable them and mark that the origional
|
|
||||||
// state should be restored after.
|
|
||||||
shouldResetDevSettings = true;
|
|
||||||
Process.runSync('adb', <String>[
|
|
||||||
'shell',
|
|
||||||
'settings',
|
|
||||||
'put',
|
|
||||||
'global',
|
|
||||||
'development_settings_enabled',
|
|
||||||
'1',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
// Assumption of diplay_cutout_test.dart is that there is a "tall" notch.
|
|
||||||
print('Adding Synthetic notch...');
|
|
||||||
Process.runSync('adb', <String>[
|
|
||||||
'shell',
|
|
||||||
'cmd',
|
|
||||||
'overlay',
|
|
||||||
'enable',
|
|
||||||
'com.android.internal.display.cutout.emulation.tall',
|
|
||||||
]);
|
|
||||||
print('Starting test.');
|
|
||||||
try {
|
|
||||||
final FlutterDriver driver = await FlutterDriver.connect();
|
|
||||||
final String data = await driver.requestData(null, timeout: const Duration(minutes: 1));
|
|
||||||
await driver.close();
|
|
||||||
final Map<String, dynamic> result = jsonDecode(data) as Map<String, dynamic>;
|
|
||||||
print('Test finished!');
|
|
||||||
print(result);
|
|
||||||
exitCode = result['result'] == 'true' ? 0 : 1;
|
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
|
||||||
exitCode = 1;
|
|
||||||
} finally {
|
|
||||||
print('Removing Synthetic notch...');
|
|
||||||
Process.runSync('adb', <String>[
|
|
||||||
'shell',
|
|
||||||
'cmd',
|
|
||||||
'overlay',
|
|
||||||
'disable',
|
|
||||||
'com.android.internal.display.cutout.emulation.tall',
|
|
||||||
]);
|
|
||||||
print('Reverting Adb changes...');
|
|
||||||
if (shouldResetDevSettings) {
|
|
||||||
print('Disabling developer settings...');
|
|
||||||
Process.runSync('adb', <String>[
|
|
||||||
'shell',
|
|
||||||
'settings',
|
|
||||||
'put',
|
|
||||||
'global',
|
|
||||||
'development_settings_enabled',
|
|
||||||
'0',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user