Unverified Commit 406ca0bc authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[devicelab] reboot attached devices after 30 test runs (#69447)

parent 059cd318
...@@ -131,6 +131,9 @@ abstract class Device { ...@@ -131,6 +131,9 @@ abstract class Device {
/// Assumes the device doesn't have a secure unlock pattern. /// Assumes the device doesn't have a secure unlock pattern.
Future<void> unlock(); Future<void> unlock();
/// Attempt to reboot the phone.
Future<void> reboot();
/// Emulate a tap on the touch screen. /// Emulate a tap on the touch screen.
Future<void> tap(int x, int y); Future<void> tap(int x, int y);
...@@ -575,6 +578,11 @@ class AndroidDevice extends Device { ...@@ -575,6 +578,11 @@ class AndroidDevice extends Device {
String toString() { String toString() {
return '$deviceId $deviceInfo'; return '$deviceId $deviceInfo';
} }
@override
Future<void> reboot() {
return adb(<String>['reboot']);
}
} }
class IosDeviceDiscovery implements DeviceDiscovery { class IosDeviceDiscovery implements DeviceDiscovery {
...@@ -740,6 +748,11 @@ class IosDevice extends Device { ...@@ -740,6 +748,11 @@ class IosDevice extends Device {
@override @override
Future<void> stop(String packageName) async {} Future<void> stop(String packageName) async {}
@override
Future<void> reboot() {
return Process.run('idevicesyslog', <String>['reboot', '-u', deviceId]);
}
} }
/// Fuchsia device. /// Fuchsia device.
...@@ -783,6 +796,11 @@ class FuchsiaDevice extends Device { ...@@ -783,6 +796,11 @@ class FuchsiaDevice extends Device {
Stream<String> get logcat { Stream<String> get logcat {
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future<void> reboot() async {
// Unsupported.
}
} }
/// Path to the `adb` executable. /// Path to the `adb` executable.
...@@ -846,6 +864,11 @@ class FakeDevice extends Device { ...@@ -846,6 +864,11 @@ class FakeDevice extends Device {
@override @override
Future<void> stop(String packageName) async {} Future<void> stop(String packageName) async {}
@override
Future<void> reboot() async {
// Unsupported.
}
} }
class FakeDeviceDiscovery implements DeviceDiscovery { class FakeDeviceDiscovery implements DeviceDiscovery {
......
...@@ -12,10 +12,20 @@ import 'package:path/path.dart' as path; ...@@ -12,10 +12,20 @@ import 'package:path/path.dart' as path;
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:stack_trace/stack_trace.dart'; import 'package:stack_trace/stack_trace.dart';
import 'running_processes.dart'; import 'adb.dart';
import 'task_result.dart'; import 'task_result.dart';
import 'utils.dart'; import 'utils.dart';
/// Identifiers for devices that should never be rebooted.
final Set<String> noRebootForbidList = <String>{
'822ef7958bba573829d85eef4df6cbdd86593730', // 32bit iPhone requires manual intervention on reboot.
};
/// The maximum number of test runs before a device must be rebooted.
///
/// This number was chosen arbitrarily.
const int maxiumRuns = 30;
/// Represents a unit of work performed in the CI environment that can /// Represents a unit of work performed in the CI environment that can
/// succeed, fail and be retried independently of others. /// succeed, fail and be retried independently of others.
typedef TaskFunction = Future<TaskResult> Function(); typedef TaskFunction = Future<TaskResult> Function();
...@@ -80,27 +90,15 @@ class _TaskRunner { ...@@ -80,27 +90,15 @@ class _TaskRunner {
try { try {
_taskStarted = true; _taskStarted = true;
print('Running task with a timeout of $taskTimeout.'); print('Running task with a timeout of $taskTimeout.');
final String exe = Platform.isWindows ? '.exe' : '';
section('Checking running Dart$exe processes');
final Set<RunningProcessInfo> beforeRunningDartInstances = await getRunningProcesses(
processName: 'dart$exe',
).toSet();
final Set<RunningProcessInfo> allProcesses = await getRunningProcesses().toSet();
beforeRunningDartInstances.forEach(print);
for (final RunningProcessInfo info in allProcesses) {
if (info.commandLine.contains('iproxy')) {
print('[LEAK]: ${info.commandLine} ${info.creationDate} ${info.pid} ');
}
}
print('enabling configs for macOS, Linux, Windows, and Web...'); print('enabling configs for macOS, Linux, Windows, and Web...');
final int configResult = await exec(path.join(flutterDirectory.path, 'bin', 'flutter'), <String>[ final int configResult = await exec(path.join(flutterDirectory.path, 'bin', 'flutter'), <String>[
'config', 'config',
'-v',
'--enable-macos-desktop', '--enable-macos-desktop',
'--enable-windows-desktop', '--enable-windows-desktop',
'--enable-linux-desktop', '--enable-linux-desktop',
'--enable-web' '--enable-web'
]); ], canFail: true);
if (configResult != 0) { if (configResult != 0) {
print('Failed to enable configuration, tasks may not run.'); print('Failed to enable configuration, tasks may not run.');
} }
...@@ -109,34 +107,7 @@ class _TaskRunner { ...@@ -109,34 +107,7 @@ class _TaskRunner {
if (taskTimeout != null) if (taskTimeout != null)
futureResult = futureResult.timeout(taskTimeout); futureResult = futureResult.timeout(taskTimeout);
TaskResult result = await futureResult; final TaskResult result = await futureResult;
section('Checking running Dart$exe processes after task...');
final List<RunningProcessInfo> afterRunningDartInstances = await getRunningProcesses(
processName: 'dart$exe',
).toList();
for (final RunningProcessInfo info in afterRunningDartInstances) {
if (!beforeRunningDartInstances.contains(info)) {
print('$info was leaked by this test.');
if (result is TaskResultCheckProcesses) {
result = TaskResult.failure('This test leaked dart processes');
}
final bool killed = await killProcess(info.pid);
if (!killed) {
print('Failed to kill process ${info.pid}.');
} else {
print('Killed process id ${info.pid}.');
}
}
}
final Set<RunningProcessInfo> allEndProcesses = await getRunningProcesses().toSet();
for (final RunningProcessInfo info in allEndProcesses) {
if (allProcesses.contains(info)) {
continue;
}
print('[LEAK]: ${info.commandLine} ${info.creationDate} ${info.pid} ');
}
_completer.complete(result); _completer.complete(result);
return result; return result;
} on TimeoutException catch (err, stackTrace) { } on TimeoutException catch (err, stackTrace) {
...@@ -147,10 +118,40 @@ class _TaskRunner { ...@@ -147,10 +118,40 @@ class _TaskRunner {
} finally { } finally {
print('Cleaning up after task...'); print('Cleaning up after task...');
await forceQuitRunningProcesses(); await forceQuitRunningProcesses();
await checkForRebootRequired();
_closeKeepAlivePort(); _closeKeepAlivePort();
} }
} }
Future<void> checkForRebootRequired() async {
try {
final Device device = await devices.workingDevice.timeout(const Duration(seconds: 15));
if (noRebootForbidList.contains(device.deviceId)) {
return;
}
final File rebootFile = _rebootFile();
int runCount;
if (rebootFile.existsSync()) {
runCount = int.tryParse(rebootFile.readAsStringSync().trim());
} else {
runCount = 0;
}
if (runCount < maxiumRuns) {
rebootFile
..createSync()
..writeAsStringSync((runCount + 1).toString());
return;
}
rebootFile.deleteSync();
print('Rebooting ${device.deviceId}');
await device.reboot();
} on TimeoutException {
// Could not find device in order to reboot.
} on DeviceException {
// No attached device needed to reboot.
}
}
/// Causes the Dart VM to stay alive until a request to run the task is /// Causes the Dart VM to stay alive until a request to run the task is
/// received via the VM service protocol. /// received via the VM service protocol.
void keepVmAliveUntilTaskRunRequested() { void keepVmAliveUntilTaskRunRequested() {
...@@ -199,3 +200,13 @@ class _TaskRunner { ...@@ -199,3 +200,13 @@ class _TaskRunner {
return completer.future; return completer.future;
} }
} }
File _rebootFile() {
if (Platform.isLinux || Platform.isMacOS) {
return File(path.join(Platform.environment['HOME'], '.reboot-count'));
}
if (!Platform.isWindows) {
throw StateError('Unexpected platform ${Platform.operatingSystem}');
}
return File(path.join(Platform.environment['USERPROFILE'], '.reboot-count'));
}
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