blob: 4d36f535fe624381abc356e7dc84f0157ab616b8 [file] [log] [blame]
// Copyright 2020 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:file/src/backends/memory/memory_file_system.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
import 'package:device_doctor/src/android_device.dart';
import 'package:device_doctor/src/device.dart';
import 'package:device_doctor/src/health.dart';
import 'package:device_doctor/src/utils.dart';
import 'utils.dart';
void main() {
group('AndroidDeviceDiscovery', () {
late AndroidDeviceDiscovery deviceDiscovery;
late MockProcessManager processManager;
List<List<int>> output;
Process process;
setUp(() {
deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output'));
processManager = MockProcessManager();
});
test('deviceDiscovery no retries', () async {
final StringBuffer sb = StringBuffer();
sb.writeln('List of devices attached');
sb.writeln('ZY223JQNMR device');
output = <List<int>>[utf8.encode(sb.toString())];
process = FakeProcess(0, out: output);
when(processManager.start(any, workingDirectory: anyNamed('workingDirectory')))
.thenAnswer((_) => Future.value(process));
final List<Device> devices = await deviceDiscovery.discoverDevices(
retryDuration: const Duration(seconds: 0),
processManager: processManager,
);
expect(devices.length, equals(1));
expect(devices[0].deviceId, equals('ZY223JQNMR'));
});
test('deviceDiscovery fails', () async {
when(processManager.start(any, workingDirectory: anyNamed('workingDirectory')))
.thenAnswer((_) => throw TimeoutException('test'));
expect(
deviceDiscovery.discoverDevices(retryDuration: const Duration(seconds: 0), processManager: processManager),
throwsA(TypeMatcher<BuildFailedError>()),
);
});
});
group('AndroidDeviceProperties', () {
late AndroidDeviceDiscovery deviceDiscovery;
late MockProcessManager processManager;
Process property_process;
Process process;
String output;
setUp(() {
deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output'));
processManager = MockProcessManager();
});
test('returns empty when no device is attached', () async {
output = 'List of devices attached';
process = FakeProcess(0, out: <List<int>>[utf8.encode(output)]);
when(processManager.start(<Object>['adb', 'devices', '-l'], workingDirectory: anyNamed('workingDirectory')))
.thenAnswer((_) => Future.value(process));
expect(await deviceDiscovery.deviceProperties(processManager: processManager), equals(<String, String>{}));
});
test('get device properties', () async {
output = '''[ro.product.brand]: [abc]
[ro.build.id]: [def]
[ro.build.type]: [ghi]
[ro.product.model]: [jkl]
[ro.product.board]: [mno]
''';
property_process = FakeProcess(0, out: <List<int>>[utf8.encode(output)]);
when(
processManager.start(
<Object>['adb', '-s', 'ZY223JQNMR', 'shell', 'getprop'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(property_process));
final Map<String, String> deviceProperties = await deviceDiscovery
.getDeviceProperties(AndroidDevice(deviceId: 'ZY223JQNMR'), processManager: processManager);
const Map<String, String> expectedProperties = <String, String>{
'product_brand': 'abc',
'build_id': 'def',
'build_type': 'ghi',
'product_model': 'jkl',
'product_board': 'mno',
};
expect(deviceProperties, equals(expectedProperties));
});
});
group('AndroidAdbPowerServiceCheck', () {
late AndroidDeviceDiscovery deviceDiscovery;
late MockProcessManager processManager;
Process process;
setUp(() {
deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output'));
processManager = MockProcessManager();
});
test('returns success when adb power service is available', () async {
process = FakeProcess(0);
when(
processManager
.start(<Object>['adb', 'shell', 'dumpsys', 'power'], workingDirectory: anyNamed('workingDirectory')),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.adbPowerServiceCheck(processManager: processManager);
expect(healthCheckResult.succeeded, true);
expect(healthCheckResult.name, kAdbPowerServiceCheckKey);
});
test('returns failure when adb returns none 0 code', () async {
process = FakeProcess(1);
when(
processManager
.start(<Object>['adb', 'shell', 'dumpsys', 'power'], workingDirectory: anyNamed('workingDirectory')),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.adbPowerServiceCheck(processManager: processManager);
expect(healthCheckResult.succeeded, false);
expect(healthCheckResult.name, kAdbPowerServiceCheckKey);
expect(healthCheckResult.details, 'Executable adb failed with exit code 1.');
});
});
group('AndroidDevloperModeCheck', () {
late AndroidDeviceDiscovery deviceDiscovery;
late MockProcessManager processManager;
Process process;
List<List<int>> output;
setUp(() {
deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output'));
processManager = MockProcessManager();
});
test('returns success when developer mode is on', () async {
output = <List<int>>[utf8.encode('1')];
process = FakeProcess(0, out: output);
when(
processManager.start(
<Object>['adb', 'shell', 'settings', 'get', 'global', 'development_settings_enabled'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.developerModeCheck(processManager: processManager);
expect(healthCheckResult.succeeded, true);
expect(healthCheckResult.name, kDeveloperModeCheckKey);
});
test('returns failure when developer mode is off', () async {
output = <List<int>>[utf8.encode('0')];
process = FakeProcess(0, out: output);
when(
processManager.start(
<Object>['adb', 'shell', 'settings', 'get', 'global', 'development_settings_enabled'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.developerModeCheck(processManager: processManager);
expect(healthCheckResult.succeeded, false);
expect(healthCheckResult.name, kDeveloperModeCheckKey);
expect(healthCheckResult.details, 'developer mode is off');
});
test('returns success when screensaver is off', () async {
output = <List<int>>[utf8.encode('0')];
process = FakeProcess(0, out: output);
when(
processManager.start(
<Object>['adb', 'shell', 'settings', 'get', 'secure', 'screensaver_enabled'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.screenSaverCheck(processManager: processManager);
expect(healthCheckResult.succeeded, true);
expect(healthCheckResult.name, kScreenSaverCheckKey);
});
test('returns failure when screensaver is on', () async {
output = <List<int>>[utf8.encode('1')];
process = FakeProcess(0, out: output);
when(
processManager.start(
<Object>['adb', 'shell', 'settings', 'get', 'secure', 'screensaver_enabled'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.screenSaverCheck(processManager: processManager);
expect(healthCheckResult.succeeded, false);
expect(healthCheckResult.name, kScreenSaverCheckKey);
expect(healthCheckResult.details, 'Screensaver is on');
});
test('returns failure when adb return none 0 code', () async {
process = FakeProcess(1);
when(
processManager.start(
<Object>['adb', 'shell', 'settings', 'get', 'global', 'development_settings_enabled'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.developerModeCheck(processManager: processManager);
expect(healthCheckResult.succeeded, false);
expect(healthCheckResult.name, kDeveloperModeCheckKey);
expect(healthCheckResult.details, 'Executable adb failed with exit code 1.');
});
});
group('AndroidScreenOnCheck', () {
late AndroidDeviceDiscovery deviceDiscovery;
late MockProcessManager processManager;
Process process;
List<List<int>> output;
setUp(() {
deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output'));
processManager = MockProcessManager();
});
test('returns success when screen is on', () async {
const String screenMessage = '''
mHoldingDisplaySuspendBlocker=true
''';
output = <List<int>>[utf8.encode(screenMessage)];
process = FakeProcess(0, out: output);
when(
processManager.start(
<Object>['adb', 'shell', 'dumpsys', 'power', '|', 'grep', 'mHoldingDisplaySuspendBlocker'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult = await deviceDiscovery.screenOnCheck(processManager: processManager);
expect(healthCheckResult.succeeded, true);
expect(healthCheckResult.name, kScreenOnCheckKey);
});
test('returns failure when screen is off', () async {
const String screenMessage = '''
mHoldingDisplaySuspendBlocker=false
''';
output = <List<int>>[utf8.encode(screenMessage)];
process = FakeProcess(0, out: output);
when(
processManager.start(
<Object>['adb', 'shell', 'dumpsys', 'power', '|', 'grep', 'mHoldingDisplaySuspendBlocker'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult = await deviceDiscovery.screenOnCheck(processManager: processManager);
expect(healthCheckResult.succeeded, false);
expect(healthCheckResult.name, kScreenOnCheckKey);
expect(healthCheckResult.details, 'screen is off');
});
test('returns failure when adb return non 0 code', () async {
process = FakeProcess(1);
when(
processManager.start(
<Object>['adb', 'shell', 'dumpsys', 'power', '|', 'grep', 'mHoldingDisplaySuspendBlocker'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult = await deviceDiscovery.screenOnCheck(processManager: processManager);
expect(healthCheckResult.succeeded, false);
expect(healthCheckResult.name, kScreenOnCheckKey);
expect(healthCheckResult.details, 'Executable adb failed with exit code 1.');
});
});
group('AndroidScreenRotationCheck', () {
late AndroidDeviceDiscovery deviceDiscovery;
late MockProcessManager processManager;
Process process;
List<List<int>> output;
setUp(() {
deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output'));
processManager = MockProcessManager();
});
test('returns success when rotation is disabled', () async {
output = <List<int>>[utf8.encode('0')];
process = FakeProcess(0, out: output);
when(
processManager.start(
<Object>['adb', 'shell', 'settings', 'get', 'system', 'accelerometer_rotation'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.screenRotationCheck(processManager: processManager);
expect(healthCheckResult.succeeded, true);
expect(healthCheckResult.name, kScreenRotationCheckKey);
});
test('returns failure when screen rotation is enabled', () async {
output = <List<int>>[utf8.encode('1')];
process = FakeProcess(0, out: output);
when(
processManager.start(
<Object>['adb', 'shell', 'settings', 'get', 'system', 'accelerometer_rotation'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.screenRotationCheck(processManager: processManager);
expect(healthCheckResult.succeeded, false);
expect(healthCheckResult.name, kScreenRotationCheckKey);
expect(healthCheckResult.details, 'Screen rotation is enabled');
});
});
group('AndroidDeviceKillProcesses', () {
late AndroidDevice device;
late MockProcessManager processManager;
Process listProcess;
Process killProcess;
List<List<int>>? output;
setUp(() {
device = AndroidDevice(deviceId: 'abc');
processManager = MockProcessManager();
});
test('successfully killed running processes', () async {
output = <List<int>>[
utf8.encode(
'Proc #27: fg T/ /TOP LCM t: 0 0:com.google.android.apps.nexuslauncher/u0a199 (top-activity)',
),
];
listProcess = FakeProcess(0, out: output);
killProcess = FakeProcess(0);
when(
processManager.start(
<Object>['adb', 'shell', 'dumpsys', 'activity', '|', 'grep', 'top-activity'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(listProcess));
when(
processManager.start(
<Object>['adb', 'shell', 'am', 'force-stop', 'com.google.android.apps.nexuslauncher'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(killProcess));
final bool result = await device.killProcesses(processManager: processManager);
expect(result, true);
});
test('no running processes', () async {
output = <List<int>>[];
listProcess = FakeProcess(0, out: output);
killProcess = FakeProcess(0);
when(
processManager.start(
<Object>['adb', 'shell', 'dumpsys', 'activity', '|', 'grep', 'top-activity'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(listProcess));
when(
processManager.start(
<Object>['adb', 'shell', 'am', 'force-stop', 'com.google.android.apps.nexuslauncher'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(killProcess));
final bool result = await device.killProcesses(processManager: processManager);
expect(result, true);
});
test('fails to kill running processes', () async {
output = <List<int>>[
utf8.encode(
'Proc #27: fg T/ /TOP LCM t: 0 0:com.google.android.apps.nexuslauncher/u0a199 (top-activity)',
),
];
listProcess = FakeProcess(0, out: output);
killProcess = FakeProcess(1);
when(
processManager.start(
<Object>['adb', 'shell', 'dumpsys', 'activity', '|', 'grep', 'top-activity'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(listProcess));
when(
processManager.start(
<Object>['adb', 'shell', 'am', 'force-stop', 'com.google.android.apps.nexuslauncher'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(killProcess));
final bool result = await device.killProcesses(processManager: processManager);
expect(result, false);
});
});
group('KillAdbServerCheck', () {
late AndroidDeviceDiscovery deviceDiscovery;
late MockProcessManager processManager;
Process process;
setUp(() {
deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output'));
processManager = MockProcessManager();
});
test('returns success when adb power service is killed', () async {
process = FakeProcess(0);
when(processManager.start(<Object>['adb', 'kill-server'], workingDirectory: anyNamed('workingDirectory')))
.thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.killAdbServerCheck(processManager: processManager);
expect(healthCheckResult.succeeded, true);
expect(healthCheckResult.name, kKillAdbServerCheckKey);
});
test('returns failure when adb returns non 0 code', () async {
process = FakeProcess(1);
when(processManager.start(<Object>['adb', 'kill-server'], workingDirectory: anyNamed('workingDirectory')))
.thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.killAdbServerCheck(processManager: processManager);
expect(healthCheckResult.succeeded, false);
expect(healthCheckResult.name, kKillAdbServerCheckKey);
expect(healthCheckResult.details, 'Executable adb failed with exit code 1.');
});
});
group('BatteryLevelCheck', () {
late AndroidDeviceDiscovery deviceDiscovery;
late MockProcessManager processManager;
Process process;
List<List<int>> output;
setUp(() {
deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output'));
processManager = MockProcessManager();
});
test('returns success when battery level is high', () async {
const String screenMessage = '''
level: 100
mod level: -1
''';
output = <List<int>>[utf8.encode(screenMessage)];
process = FakeProcess(0, out: output);
when(
processManager.start(
<Object>['adb', 'shell', 'dumpsys', 'battery', '|', 'grep', 'level'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.batteryLevelCheck(processManager: processManager);
expect(healthCheckResult.succeeded, true);
expect(healthCheckResult.name, kBatteryLevelCheckKey);
});
test('returns failure when battery level is below threshold', () async {
const String screenMessage = '''
level: 10
mod level: -1
''';
output = <List<int>>[utf8.encode(screenMessage)];
process = FakeProcess(0, out: output);
when(
processManager.start(
<Object>['adb', 'shell', 'dumpsys', 'battery', '|', 'grep', 'level'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.batteryLevelCheck(processManager: processManager);
expect(healthCheckResult.succeeded, false);
expect(healthCheckResult.name, kBatteryLevelCheckKey);
expect(healthCheckResult.details, 'Battery level (10) is below 15');
});
});
group('BatteryTemperatureCheck', () {
late AndroidDeviceDiscovery deviceDiscovery;
late MockProcessManager processManager;
Process process;
List<List<int>> output;
setUp(() {
deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output'));
processManager = MockProcessManager();
});
test('returns success when battery temperature is low', () async {
const String screenMessage = '''
temperature: 24
''';
output = <List<int>>[utf8.encode(screenMessage)];
process = FakeProcess(0, out: output);
when(
processManager.start(
<Object>['adb', 'shell', 'dumpsys', 'battery', '|', 'grep', 'temperature'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.batteryTemperatureCheck(processManager: processManager);
expect(healthCheckResult.succeeded, true);
expect(healthCheckResult.name, kBatteryTemperatureCheckKey);
});
test('returns failure when battery temperature is above threshold', () async {
const String screenMessage = '''
temperature: 350
''';
output = <List<int>>[utf8.encode(screenMessage)];
process = FakeProcess(0, out: output);
when(
processManager.start(
<Object>['adb', 'shell', 'dumpsys', 'battery', '|', 'grep', 'temperature'],
workingDirectory: anyNamed('workingDirectory'),
),
).thenAnswer((_) => Future.value(process));
final HealthCheckResult healthCheckResult =
await deviceDiscovery.batteryTemperatureCheck(processManager: processManager);
expect(healthCheckResult.succeeded, false);
expect(healthCheckResult.name, kBatteryTemperatureCheckKey);
expect(healthCheckResult.details, 'Battery temperature (35°C) is over 34°C');
});
});
}