android_workflow_test.dart 15.1 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7
// 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_sdk.dart';
import 'package:flutter_tools/src/android/android_workflow.dart';
8
import 'package:flutter_tools/src/base/io.dart';
9
import 'package:flutter_tools/src/base/logger.dart';
10 11
import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/base/platform.dart';
12 13 14
import 'package:flutter_tools/src/base/user_messages.dart';
import 'package:flutter_tools/src/base/version.dart';
import 'package:flutter_tools/src/doctor.dart';
15 16
import 'package:mockito/mockito.dart';

17 18 19
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/mocks.dart' show MockAndroidSdk, MockProcess, MockProcessManager, MockStdio;
20
import '../../src/testbed.dart';
21

22
class MockAndroidSdkVersion extends Mock implements AndroidSdkVersion {}
23
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {}
24

25 26
void main() {
  AndroidSdk sdk;
27
  Logger logger;
28
  MemoryFileSystem fileSystem;
29 30 31 32
  MockProcessManager processManager;
  MockStdio stdio;

  setUp(() {
33
    sdk = MockAndroidSdk();
34 35 36
    fileSystem = MemoryFileSystem.test();
    fileSystem.directory('/home/me').createSync(recursive: true);
    logger = BufferLogger.test();
37 38
    processManager = MockProcessManager();
    stdio = MockStdio();
39 40
  });

41
  MockProcess Function(List<String>) processMetaFactory(List<String> stdout) {
42
    final Stream<List<int>> stdoutStream = Stream<List<int>>.fromIterable(
43
        stdout.map<List<int>>((String s) => s.codeUnits));
44
    return (List<String> command) => MockProcess(stdout: stdoutStream);
45 46
  }

47 48 49 50 51 52 53 54 55 56 57
  testWithoutContext('AndroidWorkflow handles a null AndroidSDK', () {
    final AndroidWorkflow androidWorkflow = AndroidWorkflow(
      featureFlags: TestFeatureFlags(),
      androidSdk: null,
    );

    expect(androidWorkflow.canLaunchDevices, false);
    expect(androidWorkflow.canListDevices, false);
    expect(androidWorkflow.canListEmulators, false);
  });

58 59 60 61 62 63 64 65 66 67 68 69 70 71
  testWithoutContext('AndroidWorkflow handles a null adb', () {
    final MockAndroidSdk androidSdk = MockAndroidSdk();
    when(androidSdk.adbPath).thenReturn(null);
    final AndroidWorkflow androidWorkflow = AndroidWorkflow(
      featureFlags: TestFeatureFlags(),
      androidSdk: androidSdk,
    );

    expect(androidWorkflow.canLaunchDevices, false);
    expect(androidWorkflow.canListDevices, false);
    expect(androidWorkflow.canListEmulators, false);
  });


72
  testWithoutContext('licensesAccepted returns LicensesAccepted.unknown if cannot find sdkmanager', () async {
73 74
    processManager.canRunSucceeds = false;
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
75 76 77 78 79 80 81 82 83 84 85
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
      androidSdk: sdk,
      fileSystem: fileSystem,
      processManager: processManager,
      platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
      stdio: stdio,
      logger: BufferLogger.test(),
      userMessages: UserMessages(),
      androidStudio: null,
      operatingSystemUtils: FakeOperatingSystemUtils(),
    );
86
    final LicensesAccepted licenseStatus = await licenseValidator.licensesAccepted;
87

88
    expect(licenseStatus, LicensesAccepted.unknown);
89 90 91
  });

  testWithoutContext('licensesAccepted returns LicensesAccepted.unknown if cannot run sdkmanager', () async {
92
    processManager.runSucceeds = false;
93
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
94 95 96 97 98 99 100 101 102 103 104
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
      androidSdk: sdk,
      fileSystem: fileSystem,
      processManager: processManager,
      platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
      stdio: stdio,
      logger: BufferLogger.test(),
      userMessages: UserMessages(),
      androidStudio: null,
      operatingSystemUtils: FakeOperatingSystemUtils(),
    );
105
    final LicensesAccepted licenseStatus = await licenseValidator.licensesAccepted;
106

107
    expect(licenseStatus, LicensesAccepted.unknown);
108 109 110
  });

  testWithoutContext('licensesAccepted handles garbage/no output', () async {
111
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
112 113 114 115 116 117 118 119 120 121 122
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
      androidSdk: sdk,
      fileSystem: fileSystem,
      processManager: processManager,
      platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
      stdio: stdio,
      logger: BufferLogger.test(),
      userMessages: UserMessages(),
      androidStudio: null,
      operatingSystemUtils: FakeOperatingSystemUtils(),
    );
123
    final LicensesAccepted result = await licenseValidator.licensesAccepted;
124

125 126 127
    expect(result, equals(LicensesAccepted.unknown));
    expect(processManager.commands.first, equals('/foo/bar/sdkmanager'));
    expect(processManager.commands.last, equals('--licenses'));
128 129 130
  });

  testWithoutContext('licensesAccepted works for all licenses accepted', () async {
131 132
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
    processManager.processFactory = processMetaFactory(<String>[
133 134
      '[=======================================] 100% Computing updates...             ',
      'All SDK package licenses accepted.',
135 136
    ]);

137 138 139 140 141 142 143 144 145 146 147
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
      androidSdk: sdk,
      fileSystem: fileSystem,
      processManager: processManager,
      platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
      stdio: stdio,
      logger: BufferLogger.test(),
      userMessages: UserMessages(),
      androidStudio: null,
      operatingSystemUtils: FakeOperatingSystemUtils(),
    );
148
    final LicensesAccepted result = await licenseValidator.licensesAccepted;
149

150
    expect(result, equals(LicensesAccepted.all));
151 152 153
  });

  testWithoutContext('licensesAccepted works for some licenses accepted', () async {
154 155 156 157 158 159 160
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
    processManager.processFactory = processMetaFactory(<String>[
      '[=======================================] 100% Computing updates...             ',
      '2 of 5 SDK package licenses not accepted.',
      'Review licenses that have not been accepted (y/N)?',
    ]);

161 162 163 164 165 166 167 168 169 170 171
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
      androidSdk: sdk,
      fileSystem: fileSystem,
      processManager: processManager,
      platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
      stdio: stdio,
      logger: BufferLogger.test(),
      userMessages: UserMessages(),
      androidStudio: null,
      operatingSystemUtils: FakeOperatingSystemUtils(),
    );
172
    final LicensesAccepted result = await licenseValidator.licensesAccepted;
173

174
    expect(result, equals(LicensesAccepted.some));
175 176 177
  });

  testWithoutContext('licensesAccepted works for no licenses accepted', () async {
178 179 180 181 182 183 184
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
    processManager.processFactory = processMetaFactory(<String>[
      '[=======================================] 100% Computing updates...             ',
      '5 of 5 SDK package licenses not accepted.',
      'Review licenses that have not been accepted (y/N)?',
    ]);

185 186 187 188 189 190 191 192 193 194 195
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
      androidSdk: sdk,
      fileSystem: fileSystem,
      processManager: processManager,
      platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
      stdio: stdio,
      logger: BufferLogger.test(),
      userMessages: UserMessages(),
      androidStudio: null,
      operatingSystemUtils: FakeOperatingSystemUtils(),
    );
196
    final LicensesAccepted result = await licenseValidator.licensesAccepted;
197

198
    expect(result, equals(LicensesAccepted.none));
199 200 201
  });

  testWithoutContext('runLicenseManager succeeds for version >= 26', () async {
202 203 204
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
    when(sdk.sdkManagerVersion).thenReturn('26.0.0');

205 206 207 208 209 210 211 212 213 214 215
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
      androidSdk: sdk,
      fileSystem: fileSystem,
      processManager: processManager,
      platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
      stdio: stdio,
      logger: BufferLogger.test(),
      userMessages: UserMessages(),
      androidStudio: null,
      operatingSystemUtils: FakeOperatingSystemUtils(),
    );
216

217 218 219 220
    expect(await licenseValidator.runLicenseManager(), isTrue);
  });

  testWithoutContext('runLicenseManager errors when sdkmanager is not found', () async {
221
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
222 223
    processManager.canRunSucceeds = false;

224 225 226 227 228 229 230 231 232 233 234 235 236 237
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
      androidSdk: sdk,
      fileSystem: fileSystem,
      processManager: processManager,
      platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
      stdio: stdio,
      logger: BufferLogger.test(),
      userMessages: UserMessages(),
      androidStudio: null,
      operatingSystemUtils: FakeOperatingSystemUtils(),
    );

    expect(licenseValidator.runLicenseManager(), throwsToolExit());
  });
238

239
  testWithoutContext('runLicenseManager errors when sdkmanager fails to run', () async {
240 241
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
    processManager.runSucceeds = false;
242

243 244 245 246 247 248 249 250 251 252 253 254 255 256
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
      androidSdk: sdk,
      fileSystem: fileSystem,
      processManager: processManager,
      platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
      stdio: stdio,
      logger: BufferLogger.test(),
      userMessages: UserMessages(),
      androidStudio: null,
      operatingSystemUtils: FakeOperatingSystemUtils(),
    );

    expect(licenseValidator.runLicenseManager(), throwsToolExit());
  });
257

258
  testWithoutContext('detects license-only SDK installation', () async {
259 260
    when(sdk.licensesAvailable).thenReturn(true);
    when(sdk.platformToolsAvailable).thenReturn(false);
261 262 263
    final ValidationResult validationResult = await AndroidValidator(
      androidStudio: null,
      androidSdk: sdk,
264
      fileSystem: fileSystem,
265 266 267
      logger: logger,
      processManager: processManager,
      platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
268
      userMessages: UserMessages(),
269
    ).validate();
270 271 272
    expect(validationResult.type, ValidationType.partial);
    expect(
      validationResult.messages.last.message,
273
      UserMessages().androidSdkLicenseOnly(kAndroidHome),
274
    );
275
  });
276

277
  testWithoutContext('detects minimum required SDK and buildtools', () async {
278
    final AndroidSdkVersion mockSdkVersion = MockAndroidSdkVersion();
279 280
    when(sdk.licensesAvailable).thenReturn(true);
    when(sdk.platformToolsAvailable).thenReturn(true);
281 282

    // Test with invalid SDK and build tools
283
    when(mockSdkVersion.sdkLevel).thenReturn(28);
284 285 286 287
    when(mockSdkVersion.buildToolsVersion).thenReturn(Version(26, 0, 3));
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
    when(sdk.latestVersion).thenReturn(mockSdkVersion);
    when(sdk.validateSdkWellFormed()).thenReturn(<String>[]);
288
    when(processManager.runSync(<String>['which', 'java'])).thenReturn(ProcessResult(123, 1, '', ''));
289
    final String errorMessage = UserMessages().androidSdkBuildToolsOutdated(
290 291 292
      sdk.sdkManagerPath,
      kAndroidSdkMinVersion,
      kAndroidSdkBuildToolsMinVersion.toString(),
293
      FakePlatform(),
294 295
    );

296 297 298
    final AndroidValidator androidValidator = AndroidValidator(
      androidStudio: null,
      androidSdk: sdk,
299
      fileSystem: fileSystem,
300 301 302
      logger: logger,
      processManager: processManager,
      platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
303
      userMessages: UserMessages(),
304 305 306
    );

    ValidationResult validationResult = await androidValidator.validate();
307 308 309 310 311 312 313
    expect(validationResult.type, ValidationType.missing);
    expect(
      validationResult.messages.last.message,
      errorMessage,
    );

    // Test with valid SDK but invalid build tools
314
    when(mockSdkVersion.sdkLevel).thenReturn(29);
315 316
    when(mockSdkVersion.buildToolsVersion).thenReturn(Version(28, 0, 2));

317
    validationResult = await androidValidator.validate();
318 319 320 321 322 323 324 325 326 327 328
    expect(validationResult.type, ValidationType.missing);
    expect(
      validationResult.messages.last.message,
      errorMessage,
    );

    // Test with valid SDK and valid build tools
    // Will still be partial because AnroidSdk.findJavaBinary is static :(
    when(mockSdkVersion.sdkLevel).thenReturn(kAndroidSdkMinVersion);
    when(mockSdkVersion.buildToolsVersion).thenReturn(kAndroidSdkBuildToolsMinVersion);

329
    validationResult = await androidValidator.validate();
330 331 332 333 334
    expect(validationResult.type, ValidationType.partial); // No Java binary
    expect(
      validationResult.messages.any((ValidationMessage message) => message.message == errorMessage),
      isFalse,
    );
335
  });
336

337
  testWithoutContext('detects minimum required java version', () async {
338 339 340 341 342
    final AndroidSdkVersion mockSdkVersion = MockAndroidSdkVersion();

    // Mock a pass through scenario to reach _checkJavaVersion()
    when(sdk.licensesAvailable).thenReturn(true);
    when(sdk.platformToolsAvailable).thenReturn(true);
343
    when(mockSdkVersion.sdkLevel).thenReturn(29);
344 345 346 347 348 349 350
    when(mockSdkVersion.buildToolsVersion).thenReturn(Version(28, 0, 3));
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
    when(sdk.latestVersion).thenReturn(mockSdkVersion);
    when(sdk.validateSdkWellFormed()).thenReturn(<String>[]);

    //Test with older version of JDK
    const String javaVersionText = 'openjdk version "1.7.0_212"';
351
    when(processManager.run(argThat(contains('-version')))).thenAnswer((_) =>
352
      Future<ProcessResult>.value(ProcessResult(0, 0, null, javaVersionText)));
353
    final String errorMessage = UserMessages().androidJavaMinimumVersion(javaVersionText);
354

355 356 357
    final ValidationResult validationResult = await AndroidValidator(
      androidSdk: sdk,
      androidStudio: null,
358
      fileSystem: fileSystem,
359 360 361
      logger: logger,
      platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me', 'JAVA_HOME': 'home/java'},
      processManager: processManager,
362
      userMessages: UserMessages(),
363
    ).validate();
364 365 366 367 368
    expect(validationResult.type, ValidationType.partial);
    expect(
      validationResult.messages.last.message,
      errorMessage,
    );
369 370 371 372 373 374
    expect(
      validationResult.messages.any(
        (ValidationMessage message) => message.message.contains('Unable to locate Android SDK')
      ),
      false,
    );
375
  });
376

377
  testWithoutContext('Mentions `flutter config --android-sdk if user has no AndroidSdk`', () async {
378 379 380
    final ValidationResult validationResult = await AndroidValidator(
      androidSdk: null,
      androidStudio: null,
381
      fileSystem: fileSystem,
382 383 384
      logger: logger,
      platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me', 'JAVA_HOME': 'home/java'},
      processManager: processManager,
385
      userMessages: UserMessages(),
386
    ).validate();
387

388 389
    expect(
      validationResult.messages.any(
390
        (ValidationMessage message) => message.message.contains('flutter config --android-sdk')
391 392 393 394
      ),
      true,
    );
  });
395
}