| // 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:file/memory.dart'; |
| import 'package:flutter_tools/src/android/android_device.dart'; |
| import 'package:flutter_tools/src/android/android_sdk.dart'; |
| import 'package:flutter_tools/src/android/application_package.dart'; |
| import 'package:flutter_tools/src/base/file_system.dart'; |
| import 'package:flutter_tools/src/base/logger.dart'; |
| import 'package:flutter_tools/src/base/platform.dart'; |
| import 'package:flutter_tools/src/build_info.dart'; |
| import 'package:flutter_tools/src/device.dart'; |
| import 'package:test/fake.dart'; |
| |
| import '../../src/common.dart'; |
| import '../../src/fake_process_manager.dart'; |
| |
| const FakeCommand kAdbVersionCommand = FakeCommand( |
| command: <String>['adb', 'version'], |
| stdout: 'Android Debug Bridge version 1.0.39', |
| ); |
| |
| const FakeCommand kStartServer = FakeCommand( |
| command: <String>['adb', 'start-server'], |
| ); |
| |
| const FakeCommand kShaCommand = FakeCommand( |
| command: <String>[ |
| 'adb', |
| '-s', |
| '1234', |
| 'shell', |
| 'echo', |
| '-n', |
| '', |
| '>', |
| '/data/local/tmp/sky.FlutterApp.sha1', |
| ], |
| ); |
| |
| void main() { |
| late FileSystem fileSystem; |
| late FakeProcessManager processManager; |
| late AndroidSdk androidSdk; |
| |
| setUp(() { |
| processManager = FakeProcessManager.empty(); |
| fileSystem = MemoryFileSystem.test(); |
| androidSdk = FakeAndroidSdk(); |
| }); |
| |
| for (final TargetPlatform targetPlatform in <TargetPlatform>[ |
| TargetPlatform.android_arm, |
| TargetPlatform.android_arm64, |
| TargetPlatform.android_x64, |
| ]) { |
| testWithoutContext('AndroidDevice.startApp allows release builds on $targetPlatform', () async { |
| final String arch = getNameForAndroidArch( |
| getAndroidArchForName(getNameForTargetPlatform(targetPlatform))); |
| final AndroidDevice device = AndroidDevice('1234', modelID: 'TestModel', |
| fileSystem: fileSystem, |
| processManager: processManager, |
| logger: BufferLogger.test(), |
| platform: FakePlatform(), |
| androidSdk: androidSdk, |
| ); |
| final File apkFile = fileSystem.file('app-debug.apk')..createSync(); |
| final AndroidApk apk = AndroidApk( |
| id: 'FlutterApp', |
| applicationPackage: apkFile, |
| launchActivity: 'FlutterActivity', |
| versionCode: 1, |
| ); |
| |
| processManager.addCommand(kAdbVersionCommand); |
| processManager.addCommand(kStartServer); |
| |
| // This configures the target platform of the device. |
| processManager.addCommand(FakeCommand( |
| command: const <String>['adb', '-s', '1234', 'shell', 'getprop'], |
| stdout: '[ro.product.cpu.abi]: [$arch]', |
| )); |
| processManager.addCommand(const FakeCommand( |
| command: <String>['adb', '-s', '1234', 'shell', 'am', 'force-stop', 'FlutterApp'], |
| )); |
| processManager.addCommand(const FakeCommand( |
| command: <String>['adb', '-s', '1234', 'shell', 'pm', 'list', 'packages', 'FlutterApp'], |
| )); |
| processManager.addCommand(const FakeCommand( |
| command: <String>['adb', '-s', '1234', 'install', '-t', '-r', 'app-debug.apk'], |
| )); |
| processManager.addCommand(kShaCommand); |
| processManager.addCommand(const FakeCommand( |
| command: <String>[ |
| 'adb', |
| '-s', |
| '1234', |
| 'shell', |
| 'am', |
| 'start', |
| '-a', |
| 'android.intent.action.MAIN', |
| '-c', |
| 'android.intent.category.LAUNCHER', |
| '-f', |
| '0x20000000', |
| '--ez', 'enable-dart-profiling', 'true', |
| 'FlutterActivity', |
| ], |
| )); |
| |
| final LaunchResult launchResult = await device.startApp( |
| apk, |
| prebuiltApplication: true, |
| debuggingOptions: DebuggingOptions.disabled( |
| BuildInfo.release, |
| ), |
| platformArgs: <String, dynamic>{}, |
| ); |
| |
| expect(launchResult.started, true); |
| expect(processManager, hasNoRemainingExpectations); |
| }); |
| } |
| |
| testWithoutContext('AndroidDevice.startApp does not allow release builds on x86', () async { |
| final AndroidDevice device = AndroidDevice('1234', modelID: 'TestModel', |
| fileSystem: fileSystem, |
| processManager: processManager, |
| logger: BufferLogger.test(), |
| platform: FakePlatform(), |
| androidSdk: androidSdk, |
| ); |
| final File apkFile = fileSystem.file('app-debug.apk')..createSync(); |
| final AndroidApk apk = AndroidApk( |
| id: 'FlutterApp', |
| applicationPackage: apkFile, |
| launchActivity: 'FlutterActivity', |
| versionCode: 1, |
| ); |
| |
| processManager.addCommand(kAdbVersionCommand); |
| processManager.addCommand(kStartServer); |
| |
| // This configures the target platform of the device. |
| processManager.addCommand(const FakeCommand( |
| command: <String>['adb', '-s', '1234', 'shell', 'getprop'], |
| stdout: '[ro.product.cpu.abi]: [x86]', |
| )); |
| |
| final LaunchResult launchResult = await device.startApp( |
| apk, |
| prebuiltApplication: true, |
| debuggingOptions: DebuggingOptions.disabled( |
| BuildInfo.release, |
| ), |
| platformArgs: <String, dynamic>{}, |
| ); |
| |
| expect(launchResult.started, false); |
| expect(processManager, hasNoRemainingExpectations); |
| }); |
| |
| testWithoutContext('AndroidDevice.startApp forwards all supported debugging options', () async { |
| final AndroidDevice device = AndroidDevice('1234', modelID: 'TestModel', |
| fileSystem: fileSystem, |
| processManager: processManager, |
| logger: BufferLogger.test(), |
| platform: FakePlatform(), |
| androidSdk: androidSdk, |
| ); |
| final File apkFile = fileSystem.file('app-debug.apk')..createSync(); |
| final AndroidApk apk = AndroidApk( |
| id: 'FlutterApp', |
| applicationPackage: apkFile, |
| launchActivity: 'FlutterActivity', |
| versionCode: 1, |
| ); |
| |
| // These commands are required to install and start the app |
| processManager.addCommand(kAdbVersionCommand); |
| processManager.addCommand(kStartServer); |
| processManager.addCommand(const FakeCommand( |
| command: <String>['adb', '-s', '1234', 'shell', 'getprop'], |
| )); |
| processManager.addCommand(const FakeCommand( |
| command: <String>['adb', '-s', '1234', 'shell', 'am', 'force-stop', '--user', '10', 'FlutterApp'], |
| )); |
| processManager.addCommand(const FakeCommand( |
| command: <String>['adb', '-s', '1234', 'shell', 'pm', 'list', 'packages', '--user', '10', 'FlutterApp'], |
| )); |
| processManager.addCommand(const FakeCommand( |
| command: <String>[ |
| 'adb', |
| '-s', |
| '1234', |
| 'install', |
| '-t', |
| '-r', |
| '--user', |
| '10', |
| 'app-debug.apk', |
| ], |
| stdout: '\n\nThe Dart VM service is listening on http://127.0.0.1:456\n\n', |
| )); |
| processManager.addCommand(kShaCommand); |
| processManager.addCommand(const FakeCommand( |
| command: <String>[ |
| 'adb', |
| '-s', |
| '1234', |
| 'shell', |
| '-x', |
| 'logcat', |
| '-v', |
| 'time', |
| ], |
| )); |
| |
| // This command contains all launch arguments. |
| processManager.addCommand(const FakeCommand( |
| command: <String>[ |
| 'adb', |
| '-s', |
| '1234', |
| 'shell', |
| 'am', |
| 'start', |
| '-a', |
| 'android.intent.action.MAIN', |
| '-c', |
| 'android.intent.category.LAUNCHER', |
| '-f', |
| '0x20000000', |
| // The DebuggingOptions arguments go here. |
| '--ez', 'enable-dart-profiling', 'true', |
| '--ez', 'enable-software-rendering', 'true', |
| '--ez', 'skia-deterministic-rendering', 'true', |
| '--ez', 'trace-skia', 'true', |
| '--es', 'trace-allowlist', 'bar,baz', |
| '--es', 'trace-skia-allowlist', 'skia.a,skia.b', |
| '--ez', 'trace-systrace', 'true', |
| '--ez', 'endless-trace-buffer', 'true', |
| '--ez', 'dump-skp-on-shader-compilation', 'true', |
| '--ez', 'cache-sksl', 'true', |
| '--ez', 'purge-persistent-cache', 'true', |
| '--ez', 'enable-impeller', 'true', |
| '--ez', 'enable-checked-mode', 'true', |
| '--ez', 'verify-entry-points', 'true', |
| '--ez', 'start-paused', 'true', |
| '--ez', 'disable-service-auth-codes', 'true', |
| '--es', 'dart-flags', 'foo,--null_assertions', |
| '--ez', 'use-test-fonts', 'true', |
| '--ez', 'verbose-logging', 'true', |
| '--user', '10', |
| 'FlutterActivity', |
| ], |
| )); |
| |
| final LaunchResult launchResult = await device.startApp( |
| apk, |
| prebuiltApplication: true, |
| debuggingOptions: DebuggingOptions.enabled( |
| BuildInfo.debug, |
| startPaused: true, |
| disableServiceAuthCodes: true, |
| dartFlags: 'foo', |
| enableSoftwareRendering: true, |
| skiaDeterministicRendering: true, |
| traceSkia: true, |
| traceAllowlist: 'bar,baz', |
| traceSkiaAllowlist: 'skia.a,skia.b', |
| traceSystrace: true, |
| endlessTraceBuffer: true, |
| dumpSkpOnShaderCompilation: true, |
| cacheSkSL: true, |
| purgePersistentCache: true, |
| useTestFonts: true, |
| verboseSystemLogs: true, |
| nullAssertions: true, |
| enableImpeller: true, |
| ), |
| platformArgs: <String, dynamic>{}, |
| userIdentifier: '10', |
| ); |
| |
| // This fails to start due to observatory discovery issues. |
| expect(launchResult.started, false); |
| expect(processManager, hasNoRemainingExpectations); |
| }); |
| } |
| |
| class FakeAndroidSdk extends Fake implements AndroidSdk { |
| @override |
| String get adbPath => 'adb'; |
| |
| @override |
| bool get licensesAvailable => false; |
| } |