android_emulator_test.dart 7.17 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

Dan Field's avatar
Dan Field committed
5 6
import 'dart:async';

7
import 'package:flutter_tools/src/android/android_emulator.dart';
8
import 'package:flutter_tools/src/android/android_sdk.dart';
9
import 'package:flutter_tools/src/base/logger.dart';
10
import 'package:flutter_tools/src/device.dart';
11
import 'package:test/fake.dart';
12

13
import '../../src/common.dart';
Dan Field's avatar
Dan Field committed
14
import '../../src/fake_process_manager.dart';
15

16 17 18 19 20 21
const String emulatorID = 'i1234';
const String errorText = '[Android emulator test error]';
const List<String> kEmulatorLaunchCommand = <String>[
  'emulator', '-avd', emulatorID,
];

22 23
void main() {
  group('android_emulator', () {
24
    testWithoutContext('flags emulators without config', () {
25
      const String emulatorID = '1234';
26 27 28 29 30

      final AndroidEmulator emulator = AndroidEmulator(
        emulatorID,
        logger: BufferLogger.test(),
        processManager: FakeProcessManager.any(),
31
        androidSdk: FakeAndroidSdk(),
32
      );
33 34 35
      expect(emulator.id, emulatorID);
      expect(emulator.hasConfig, false);
    });
36 37

    testWithoutContext('flags emulators with config', () {
38
      const String emulatorID = '1234';
Dan Field's avatar
Dan Field committed
39 40
      final AndroidEmulator emulator = AndroidEmulator(
        emulatorID,
41 42 43
        properties: const <String, String>{'name': 'test'},
        logger: BufferLogger.test(),
        processManager: FakeProcessManager.any(),
44
        androidSdk: FakeAndroidSdk(),
Dan Field's avatar
Dan Field committed
45
      );
46

47 48 49
      expect(emulator.id, emulatorID);
      expect(emulator.hasConfig, true);
    });
50 51

    testWithoutContext('reads expected metadata', () {
52 53
      const String emulatorID = '1234';
      const String manufacturer = 'Me';
54
      const String displayName = 'The best one';
55 56
      final Map<String, String> properties = <String, String>{
        'hw.device.manufacturer': manufacturer,
57
        'avd.ini.displayname': displayName,
58
      };
59 60 61 62 63
      final AndroidEmulator emulator = AndroidEmulator(
        emulatorID,
        properties: properties,
        logger: BufferLogger.test(),
        processManager: FakeProcessManager.any(),
64
        androidSdk: FakeAndroidSdk(),
65 66
      );

67
      expect(emulator.id, emulatorID);
68
      expect(emulator.name, displayName);
69
      expect(emulator.manufacturer, manufacturer);
70
      expect(emulator.category, Category.mobile);
71
      expect(emulator.platformType, PlatformType.android);
72
    });
73 74

    testWithoutContext('prefers displayname for name', () {
75 76 77 78 79
      const String emulatorID = '1234';
      const String displayName = 'The best one';
      final Map<String, String> properties = <String, String>{
        'avd.ini.displayname': displayName,
      };
80 81 82 83 84
      final AndroidEmulator emulator = AndroidEmulator(
        emulatorID,
        properties: properties,
        logger: BufferLogger.test(),
        processManager: FakeProcessManager.any(),
85
        androidSdk: FakeAndroidSdk(),
86 87
      );

88 89
      expect(emulator.name, displayName);
    });
90 91

    testWithoutContext('uses cleaned up ID if no displayname is set', () {
92 93 94 95 96 97
      // 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',
      };
98 99 100 101 102
      final AndroidEmulator emulator = AndroidEmulator(
        emulatorID,
        properties: properties,
        logger: BufferLogger.test(),
        processManager: FakeProcessManager.any(),
103
        androidSdk: FakeAndroidSdk(),
104 105
      );

106 107
      expect(emulator.name, 'This is my ID');
    });
108 109

    testWithoutContext('parses ini files', () {
110 111 112 113 114 115 116 117
      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'));
118

119 120 121 122 123
      expect(results['hw.device.name'], 'My Test Name');
      expect(results['hw.device.manufacturer'], 'Me');
      expect(results['avd.ini.displayname'], 'dispName');
    });
  });
Dan Field's avatar
Dan Field committed
124 125

  group('Android emulator launch ', () {
126
    late FakeAndroidSdk mockSdk;
Dan Field's avatar
Dan Field committed
127 128

    setUp(() {
129 130
      mockSdk = FakeAndroidSdk();
      mockSdk.emulatorPath = 'emulator';
Dan Field's avatar
Dan Field committed
131 132
    });

133 134 135 136 137 138 139 140 141
    testWithoutContext('succeeds', () async {
      final AndroidEmulator emulator = AndroidEmulator(emulatorID,
        processManager: FakeProcessManager.list(<FakeCommand>[
          const FakeCommand(command: kEmulatorLaunchCommand),
        ]),
        androidSdk: mockSdk,
        logger: BufferLogger.test(),
      );

142
      await emulator.launch(startupDuration: Duration.zero);
Dan Field's avatar
Dan Field committed
143 144
    });

145
    testWithoutContext('succeeds with coldboot launch', () async {
146
      final List<String> kEmulatorLaunchColdBootCommand = <String>[
147
        ...kEmulatorLaunchCommand,
148
        '-no-snapshot-load',
149 150 151
      ];
      final AndroidEmulator emulator = AndroidEmulator(emulatorID,
        processManager: FakeProcessManager.list(<FakeCommand>[
152
          FakeCommand(command: kEmulatorLaunchColdBootCommand),
153 154 155 156 157 158 159 160
        ]),
        androidSdk: mockSdk,
        logger: BufferLogger.test(),
      );

      await emulator.launch(startupDuration: Duration.zero, coldBoot: true);
    });

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
    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,
      );

176
      await emulator.launch(startupDuration: Duration.zero);
Dan Field's avatar
Dan Field committed
177

178
      expect(logger.errorText, contains(errorText));
Dan Field's avatar
Dan Field committed
179 180
    });

181 182 183 184
    testWithoutContext('prints nothing on late failure with empty stderr', () async {
      final BufferLogger logger =  BufferLogger.test();
      final AndroidEmulator emulator = AndroidEmulator(emulatorID,
        processManager: FakeProcessManager.list(<FakeCommand>[
185
          FakeCommand(
186 187 188
            command: kEmulatorLaunchCommand,
            exitCode: 1,
            stdout: 'dummy text',
189
            completer: Completer<void>(),
190 191 192 193 194
          ),
        ]),
        androidSdk: mockSdk,
        logger: logger,
      );
195
      await emulator.launch(startupDuration: Duration.zero);
196 197

      expect(logger.errorText, isEmpty);
198
    });
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218

    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'),
        )),
      );
    });
Dan Field's avatar
Dan Field committed
219
  });
220
}
221 222 223

class FakeAndroidSdk extends Fake implements AndroidSdk {
  @override
224
  String? emulatorPath;
225
}