android_workflow_test.dart 13.5 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.

5 6
import 'dart:async';

7 8 9 10 11
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/android/android_workflow.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:flutter_tools/src/globals.dart' as globals;

17 18 19 20
import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';

21 22 23
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/mocks.dart' show MockAndroidSdk, MockProcess, MockProcessManager, MockStdio;
24

25 26
class MockAndroidSdkVersion extends Mock implements AndroidSdkVersion {}

27 28 29 30 31 32 33
void main() {
  AndroidSdk sdk;
  MemoryFileSystem fs;
  MockProcessManager processManager;
  MockStdio stdio;

  setUp(() {
34 35
    sdk = MockAndroidSdk();
    fs = MemoryFileSystem();
36
    fs.directory('/home/me').createSync(recursive: true);
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
  testUsingContext('licensesAccepted returns LicensesAccepted.unknown if cannot find sdkmanager', () async {
    processManager.canRunSucceeds = false;
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator();
    final LicensesAccepted licenseStatus = await licenseValidator.licensesAccepted;
    expect(licenseStatus, LicensesAccepted.unknown);
53
  }, overrides: Map<Type, Generator>.unmodifiable(<Type, Generator>{
54 55 56
    AndroidSdk: () => sdk,
    FileSystem: () => fs,
    ProcessManager: () => processManager,
57
    Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
58
    Stdio: () => stdio,
59
  }));
60

61
  testUsingContext('licensesAccepted returns LicensesAccepted.unknown if cannot run sdkmanager', () async {
62
    processManager.runSucceeds = false;
63
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
64
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator();
65 66
    final LicensesAccepted licenseStatus = await licenseValidator.licensesAccepted;
    expect(licenseStatus, LicensesAccepted.unknown);
67
  }, overrides: Map<Type, Generator>.unmodifiable(<Type, Generator>{
68 69 70
    AndroidSdk: () => sdk,
    FileSystem: () => fs,
    ProcessManager: () => processManager,
71
    Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
72
    Stdio: () => stdio,
73
  }));
74

75 76
  testUsingContext('licensesAccepted handles garbage/no output', () async {
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
77 78
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator();
    final LicensesAccepted result = await licenseValidator.licensesAccepted;
79 80 81
    expect(result, equals(LicensesAccepted.unknown));
    expect(processManager.commands.first, equals('/foo/bar/sdkmanager'));
    expect(processManager.commands.last, equals('--licenses'));
82
  }, overrides: Map<Type, Generator>.unmodifiable(<Type, Generator>{
83 84 85
    AndroidSdk: () => sdk,
    FileSystem: () => fs,
    ProcessManager: () => processManager,
86
    Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
87
    Stdio: () => stdio,
88
  }));
89 90 91 92

  testUsingContext('licensesAccepted works for all licenses accepted', () async {
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
    processManager.processFactory = processMetaFactory(<String>[
93 94
      '[=======================================] 100% Computing updates...             ',
      'All SDK package licenses accepted.',
95 96
    ]);

97 98
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator();
    final LicensesAccepted result = await licenseValidator.licensesAccepted;
99
    expect(result, equals(LicensesAccepted.all));
100
  }, overrides: Map<Type, Generator>.unmodifiable(<Type, Generator>{
101 102 103
    AndroidSdk: () => sdk,
    FileSystem: () => fs,
    ProcessManager: () => processManager,
104
    Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
105
    Stdio: () => stdio,
106
  }));
107 108 109 110 111 112 113 114 115

  testUsingContext('licensesAccepted works for some licenses accepted', () async {
    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)?',
    ]);

116 117
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator();
    final LicensesAccepted result = await licenseValidator.licensesAccepted;
118
    expect(result, equals(LicensesAccepted.some));
119
  }, overrides: Map<Type, Generator>.unmodifiable(<Type, Generator>{
120 121 122
    AndroidSdk: () => sdk,
    FileSystem: () => fs,
    ProcessManager: () => processManager,
123
    Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
124
    Stdio: () => stdio,
125
  }));
126 127 128 129 130 131 132 133 134

  testUsingContext('licensesAccepted works for no licenses accepted', () async {
    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)?',
    ]);

135 136
    final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator();
    final LicensesAccepted result = await licenseValidator.licensesAccepted;
137
    expect(result, equals(LicensesAccepted.none));
138
  }, overrides: Map<Type, Generator>.unmodifiable(<Type, Generator>{
139 140 141
    AndroidSdk: () => sdk,
    FileSystem: () => fs,
    ProcessManager: () => processManager,
142
    Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
143
    Stdio: () => stdio,
144
  }));
145

146 147 148 149
  testUsingContext('runLicenseManager succeeds for version >= 26', () async {
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
    when(sdk.sdkManagerVersion).thenReturn('26.0.0');

150
    expect(await AndroidLicenseValidator.runLicenseManager(), isTrue);
151
  }, overrides: Map<Type, Generator>.unmodifiable(<Type, Generator>{
152 153 154
    AndroidSdk: () => sdk,
    FileSystem: () => fs,
    ProcessManager: () => processManager,
155
    Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
156
    Stdio: () => stdio,
157
  }));
158 159 160 161 162

  testUsingContext('runLicenseManager errors for version < 26', () async {
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
    when(sdk.sdkManagerVersion).thenReturn('25.0.0');

163
    expect(AndroidLicenseValidator.runLicenseManager(), throwsToolExit(message: 'To update, run'));
164
  }, overrides: Map<Type, Generator>.unmodifiable(<Type, Generator>{
165 166 167
    AndroidSdk: () => sdk,
    FileSystem: () => fs,
    ProcessManager: () => processManager,
168
    Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
169
    Stdio: () => stdio,
170
  }));
171 172 173 174 175

  testUsingContext('runLicenseManager errors correctly for null version', () async {
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
    when(sdk.sdkManagerVersion).thenReturn(null);

176
    expect(AndroidLicenseValidator.runLicenseManager(), throwsToolExit(message: 'To update, run'));
177
  }, overrides: Map<Type, Generator>.unmodifiable(<Type, Generator>{
178 179 180
    AndroidSdk: () => sdk,
    FileSystem: () => fs,
    ProcessManager: () => processManager,
181
    Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
182
    Stdio: () => stdio,
183
  }));
184 185 186

  testUsingContext('runLicenseManager errors when sdkmanager is not found', () async {
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
187 188 189
    processManager.canRunSucceeds = false;

    expect(AndroidLicenseValidator.runLicenseManager(), throwsToolExit());
190
  }, overrides: Map<Type, Generator>.unmodifiable(<Type, Generator>{
191 192 193
    AndroidSdk: () => sdk,
    FileSystem: () => fs,
    ProcessManager: () => processManager,
194
    Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
195
    Stdio: () => stdio,
196
  }));
197 198 199 200

  testUsingContext('runLicenseManager errors when sdkmanager fails to run', () async {
    when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
    processManager.runSucceeds = false;
201

202
    expect(AndroidLicenseValidator.runLicenseManager(), throwsToolExit());
203
  }, overrides: Map<Type, Generator>.unmodifiable(<Type, Generator>{
204 205 206
    AndroidSdk: () => sdk,
    FileSystem: () => fs,
    ProcessManager: () => processManager,
207
    Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
208
    Stdio: () => stdio,
209
  }));
210

211 212 213 214 215 216 217 218 219
  testUsingContext('detects license-only SDK installation', () async {
    when(sdk.licensesAvailable).thenReturn(true);
    when(sdk.platformToolsAvailable).thenReturn(false);
    final ValidationResult validationResult = await AndroidValidator().validate();
    expect(validationResult.type, ValidationType.partial);
    expect(
      validationResult.messages.last.message,
      userMessages.androidSdkLicenseOnly(kAndroidHome),
    );
220
  }, overrides: Map<Type, Generator>.unmodifiable(<Type, Generator>{
221 222 223
    AndroidSdk: () => sdk,
    FileSystem: () => fs,
    ProcessManager: () => processManager,
224
    Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
225
    Stdio: () => stdio,
226
  }));
227

228 229
  testUsingContext('detects minium required SDK and buildtools', () async {
    final AndroidSdkVersion mockSdkVersion = MockAndroidSdkVersion();
230 231
    when(sdk.licensesAvailable).thenReturn(true);
    when(sdk.platformToolsAvailable).thenReturn(true);
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273

    // Test with invalid SDK and build tools
    when(mockSdkVersion.sdkLevel).thenReturn(26);
    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>[]);
    final String errorMessage = userMessages.androidSdkBuildToolsOutdated(
      sdk.sdkManagerPath,
      kAndroidSdkMinVersion,
      kAndroidSdkBuildToolsMinVersion.toString(),
    );

    ValidationResult validationResult = await AndroidValidator().validate();
    expect(validationResult.type, ValidationType.missing);
    expect(
      validationResult.messages.last.message,
      errorMessage,
    );

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

    validationResult = await AndroidValidator().validate();
    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);

    validationResult = await AndroidValidator().validate();
    expect(validationResult.type, ValidationType.partial); // No Java binary
    expect(
      validationResult.messages.any((ValidationMessage message) => message.message == errorMessage),
      isFalse,
    );
274
  }, overrides: Map<Type, Generator>.unmodifiable(<Type, Generator>{
275 276 277
    AndroidSdk: () => sdk,
    FileSystem: () => fs,
    ProcessManager: () => processManager,
278
    Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
279
    Stdio: () => stdio,
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
  }));

  testUsingContext('detects minimum required java version', () async {
    final AndroidSdkVersion mockSdkVersion = MockAndroidSdkVersion();

    // Mock a pass through scenario to reach _checkJavaVersion()
    when(sdk.licensesAvailable).thenReturn(true);
    when(sdk.platformToolsAvailable).thenReturn(true);
    when(mockSdkVersion.sdkLevel).thenReturn(28);
    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"';
296
    when(globals.processManager.run(argThat(contains('-version')))).thenAnswer((_) =>
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
      Future<ProcessResult>.value(ProcessResult(0, 0, null, javaVersionText)));
    final String errorMessage = userMessages.androidJavaMinimumVersion(javaVersionText);

    final ValidationResult validationResult = await AndroidValidator().validate();
    expect(validationResult.type, ValidationType.partial);
    expect(
      validationResult.messages.last.message,
      errorMessage,
    );
  }, overrides: Map<Type, Generator>.unmodifiable(<Type, Generator>{
    AndroidSdk: () => sdk,
    FileSystem: () => fs,
    Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me', 'JAVA_HOME': 'home/java'},
    ProcessManager: () => processManager,
    Stdio: () => stdio,
  }));
313

314
}