| // 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 'package:flutter_tools/src/android/android_emulator.dart'; |
| import 'package:flutter_tools/src/android/android_sdk.dart'; |
| import 'package:flutter_tools/src/base/logger.dart'; |
| import 'package:flutter_tools/src/device.dart'; |
| import 'package:test/fake.dart'; |
| |
| import '../../src/common.dart'; |
| import '../../src/fake_process_manager.dart'; |
| |
| const String emulatorID = 'i1234'; |
| const String errorText = '[Android emulator test error]'; |
| const List<String> kEmulatorLaunchCommand = <String>[ |
| 'emulator', '-avd', emulatorID, |
| ]; |
| |
| void main() { |
| group('android_emulator', () { |
| testWithoutContext('flags emulators without config', () { |
| const String emulatorID = '1234'; |
| |
| final AndroidEmulator emulator = AndroidEmulator( |
| emulatorID, |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.any(), |
| androidSdk: FakeAndroidSdk(), |
| ); |
| expect(emulator.id, emulatorID); |
| expect(emulator.hasConfig, false); |
| }); |
| |
| testWithoutContext('flags emulators with config', () { |
| const String emulatorID = '1234'; |
| final AndroidEmulator emulator = AndroidEmulator( |
| emulatorID, |
| properties: const <String, String>{'name': 'test'}, |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.any(), |
| androidSdk: FakeAndroidSdk(), |
| ); |
| |
| expect(emulator.id, emulatorID); |
| expect(emulator.hasConfig, true); |
| }); |
| |
| testWithoutContext('reads expected metadata', () { |
| const String emulatorID = '1234'; |
| const String manufacturer = 'Me'; |
| const String displayName = 'The best one'; |
| final Map<String, String> properties = <String, String>{ |
| 'hw.device.manufacturer': manufacturer, |
| 'avd.ini.displayname': displayName, |
| }; |
| final AndroidEmulator emulator = AndroidEmulator( |
| emulatorID, |
| properties: properties, |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.any(), |
| androidSdk: FakeAndroidSdk(), |
| ); |
| |
| expect(emulator.id, emulatorID); |
| expect(emulator.name, displayName); |
| expect(emulator.manufacturer, manufacturer); |
| expect(emulator.category, Category.mobile); |
| expect(emulator.platformType, PlatformType.android); |
| }); |
| |
| testWithoutContext('prefers displayname for name', () { |
| const String emulatorID = '1234'; |
| const String displayName = 'The best one'; |
| final Map<String, String> properties = <String, String>{ |
| 'avd.ini.displayname': displayName, |
| }; |
| final AndroidEmulator emulator = AndroidEmulator( |
| emulatorID, |
| properties: properties, |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.any(), |
| androidSdk: FakeAndroidSdk(), |
| ); |
| |
| expect(emulator.name, displayName); |
| }); |
| |
| testWithoutContext('uses cleaned up ID if no displayname is set', () { |
| // Android Studio uses the ID with underscores replaced with spaces |
| // for the name if displayname is not set so we do the same. |
| const String emulatorID = 'This_is_my_ID'; |
| final Map<String, String> properties = <String, String>{ |
| 'avd.ini.notadisplayname': 'this is not a display name', |
| }; |
| final AndroidEmulator emulator = AndroidEmulator( |
| emulatorID, |
| properties: properties, |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.any(), |
| androidSdk: FakeAndroidSdk(), |
| ); |
| |
| expect(emulator.name, 'This is my ID'); |
| }); |
| |
| testWithoutContext('parses ini files', () { |
| const String iniFile = ''' |
| hw.device.name=My Test Name |
| #hw.device.name=Bad Name |
| |
| hw.device.manufacturer=Me |
| avd.ini.displayname = dispName |
| '''; |
| final Map<String, String> results = parseIniLines(iniFile.split('\n')); |
| |
| expect(results['hw.device.name'], 'My Test Name'); |
| expect(results['hw.device.manufacturer'], 'Me'); |
| expect(results['avd.ini.displayname'], 'dispName'); |
| }); |
| }); |
| |
| group('Android emulator launch ', () { |
| late FakeAndroidSdk mockSdk; |
| |
| setUp(() { |
| mockSdk = FakeAndroidSdk(); |
| mockSdk.emulatorPath = 'emulator'; |
| }); |
| |
| testWithoutContext('succeeds', () async { |
| final AndroidEmulator emulator = AndroidEmulator(emulatorID, |
| processManager: FakeProcessManager.list(<FakeCommand>[ |
| const FakeCommand(command: kEmulatorLaunchCommand), |
| ]), |
| androidSdk: mockSdk, |
| logger: BufferLogger.test(), |
| ); |
| |
| await emulator.launch(startupDuration: Duration.zero); |
| }); |
| |
| testWithoutContext('succeeds with coldboot launch', () async { |
| final List<String> kEmulatorLaunchColdBootCommand = <String>[ |
| ...kEmulatorLaunchCommand, |
| '-no-snapshot-load', |
| ]; |
| final AndroidEmulator emulator = AndroidEmulator(emulatorID, |
| processManager: FakeProcessManager.list(<FakeCommand>[ |
| FakeCommand(command: kEmulatorLaunchColdBootCommand), |
| ]), |
| androidSdk: mockSdk, |
| logger: BufferLogger.test(), |
| ); |
| |
| await emulator.launch(startupDuration: Duration.zero, coldBoot: true); |
| }); |
| |
| testWithoutContext('prints error on failure', () async { |
| final BufferLogger logger = BufferLogger.test(); |
| final AndroidEmulator emulator = AndroidEmulator(emulatorID, |
| processManager: FakeProcessManager.list(<FakeCommand>[ |
| const FakeCommand( |
| command: kEmulatorLaunchCommand, |
| exitCode: 1, |
| stderr: errorText, |
| stdout: 'dummy text', |
| ), |
| ]), |
| androidSdk: mockSdk, |
| logger: logger, |
| ); |
| |
| await emulator.launch(startupDuration: Duration.zero); |
| |
| expect(logger.errorText, contains(errorText)); |
| }); |
| |
| testWithoutContext('prints nothing on late failure with empty stderr', () async { |
| final BufferLogger logger = BufferLogger.test(); |
| final AndroidEmulator emulator = AndroidEmulator(emulatorID, |
| processManager: FakeProcessManager.list(<FakeCommand>[ |
| FakeCommand( |
| command: kEmulatorLaunchCommand, |
| exitCode: 1, |
| stdout: 'dummy text', |
| completer: Completer<void>(), |
| ), |
| ]), |
| androidSdk: mockSdk, |
| logger: logger, |
| ); |
| await emulator.launch(startupDuration: Duration.zero); |
| |
| expect(logger.errorText, isEmpty); |
| }); |
| |
| testWithoutContext('throws if emulator not found', () async { |
| mockSdk.emulatorPath = null; |
| |
| final AndroidEmulator emulator = AndroidEmulator( |
| emulatorID, |
| processManager: FakeProcessManager.empty(), |
| androidSdk: mockSdk, |
| logger: BufferLogger.test(), |
| ); |
| |
| await expectLater( |
| () => emulator.launch(startupDuration: Duration.zero), |
| throwsA(isException.having( |
| (Exception exception) => exception.toString(), |
| 'description', |
| contains('Emulator is missing from the Android SDK'), |
| )), |
| ); |
| }); |
| }); |
| } |
| |
| class FakeAndroidSdk extends Fake implements AndroidSdk { |
| @override |
| String? emulatorPath; |
| } |