| // 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'); |
| }); |
| }); |
| } |