Reland "Allow for gradle downloading missing SDK assets" (#28097) (#28355)
* Allow for gradle downloading missing SDK assets if SDK licenses are present.
* Improvements for windows testing
diff --git a/packages/flutter_tools/test/android/android_device_test.dart b/packages/flutter_tools/test/android/android_device_test.dart
index b6988f5..d7945e5 100644
--- a/packages/flutter_tools/test/android/android_device_test.dart
+++ b/packages/flutter_tools/test/android/android_device_test.dart
@@ -27,16 +27,23 @@
});
group('getAdbDevices', () {
+ final MockProcessManager mockProcessManager = MockProcessManager();
testUsingContext('throws on missing adb path', () {
final Directory sdkDir = MockAndroidSdk.createSdkDirectory();
Config.instance.setValue('android-sdk', sdkDir.path);
final File adbExe = fs.file(getAdbPath(androidSdk));
- adbExe.deleteSync();
+ when(mockProcessManager.runSync(
+ <String>[adbExe.path, 'devices', '-l'],
+ ))
+ .thenAnswer(
+ (_) => throw ArgumentError(adbExe.path),
+ );
expect(() => getAdbDevices(), throwsToolExit(message: RegExp('Unable to run "adb".*${adbExe.path}')));
}, overrides: <Type, Generator>{
AndroidSdk: () => MockAndroidSdk(),
FileSystem: () => MemoryFileSystem(),
+ ProcessManager: () => mockProcessManager,
});
testUsingContext('physical devices', () {
diff --git a/packages/flutter_tools/test/android/android_sdk_test.dart b/packages/flutter_tools/test/android/android_sdk_test.dart
index 9ef1711..0a7578b 100644
--- a/packages/flutter_tools/test/android/android_sdk_test.dart
+++ b/packages/flutter_tools/test/android/android_sdk_test.dart
@@ -225,12 +225,13 @@
bool withSdkManager = true,
}) {
final Directory dir = fs.systemTempDirectory.createTempSync('flutter_mock_android_sdk.');
+ final String exe = platform.isWindows ? '.exe' : '';
+ _createSdkFile(dir, 'licenses/dummy');
+ _createSdkFile(dir, 'platform-tools/adb$exe');
- _createSdkFile(dir, 'platform-tools/adb');
-
- _createSdkFile(dir, 'build-tools/sda/aapt');
- _createSdkFile(dir, 'build-tools/af/aapt');
- _createSdkFile(dir, 'build-tools/ljkasd/aapt');
+ _createSdkFile(dir, 'build-tools/sda/aapt$exe');
+ _createSdkFile(dir, 'build-tools/af/aapt$exe');
+ _createSdkFile(dir, 'build-tools/ljkasd/aapt$exe');
_createSdkFile(dir, 'platforms/android-22/android.jar');
_createSdkFile(dir, 'platforms/android-23/android.jar');
diff --git a/packages/flutter_tools/test/android/android_workflow_test.dart b/packages/flutter_tools/test/android/android_workflow_test.dart
index 4764b1b..c988474 100644
--- a/packages/flutter_tools/test/android/android_workflow_test.dart
+++ b/packages/flutter_tools/test/android/android_workflow_test.dart
@@ -42,11 +42,12 @@
return (List<String> command) => MockProcess(stdout: stdoutStream);
}
- testUsingContext('licensesAccepted throws if cannot run sdkmanager', () async {
+ testUsingContext('licensesAccepted returns LicensesAccepted.unknown if cannot run sdkmanager', () async {
processManager.succeed = false;
when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator();
- expect(licenseValidator.licensesAccepted, throwsToolExit());
+ final LicensesAccepted licenseStatus = await licenseValidator.licensesAccepted;
+ expect(licenseStatus, LicensesAccepted.unknown);
}, overrides: <Type, Generator>{
AndroidSdk: () => sdk,
FileSystem: () => fs,
@@ -178,8 +179,27 @@
Stdio: () => stdio,
});
+ 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),
+ );
+ }, overrides: <Type, Generator>{
+ AndroidSdk: () => sdk,
+ FileSystem: () => fs,
+ Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
+ ProcessManager: () => processManager,
+ Stdio: () => stdio,
+ });
+
testUsingContext('detects minium required SDK and buildtools', () async {
final AndroidSdkVersion mockSdkVersion = MockAndroidSdkVersion();
+ when(sdk.licensesAvailable).thenReturn(true);
+ when(sdk.platformToolsAvailable).thenReturn(true);
// Test with invalid SDK and build tools
when(mockSdkVersion.sdkLevel).thenReturn(26);
diff --git a/packages/flutter_tools/test/application_package_test.dart b/packages/flutter_tools/test/application_package_test.dart
index 48a1396..099e8a2 100644
--- a/packages/flutter_tools/test/application_package_test.dart
+++ b/packages/flutter_tools/test/application_package_test.dart
@@ -3,18 +3,24 @@
// found in the LICENSE file.
import 'dart:convert';
+import 'dart:io' show ProcessResult;
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/platform.dart';
+import 'package:flutter_tools/src/build_info.dart';
+import 'package:flutter_tools/src/cache.dart';
+import 'package:flutter_tools/src/project.dart';
import 'package:mockito/mockito.dart';
import 'package:flutter_tools/src/application_package.dart';
+import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/ios/ios_workflow.dart';
+import 'package:process/process.dart';
import 'src/common.dart';
import 'src/context.dart';
@@ -24,7 +30,78 @@
Platform: _kNoColorTerminalPlatform,
};
+class MockitoProcessManager extends Mock implements ProcessManager {}
+class MockitoAndroidSdk extends Mock implements AndroidSdk {}
+class MockitoAndroidSdkVersion extends Mock implements AndroidSdkVersion {}
+
void main() {
+ group('Apk with partial Android SDK works', () {
+ AndroidSdk sdk;
+ ProcessManager mockProcessManager;
+ MemoryFileSystem fs;
+ File gradle;
+ final Map<Type, Generator> overrides = <Type, Generator>{
+ AndroidSdk: () => sdk,
+ ProcessManager: () => mockProcessManager,
+ FileSystem: () => fs,
+ };
+
+ setUp(() async {
+ sdk = MockitoAndroidSdk();
+ mockProcessManager = MockitoProcessManager();
+ fs = MemoryFileSystem();
+ Cache.flutterRoot = '../..';
+ when(sdk.licensesAvailable).thenReturn(true);
+ when(mockProcessManager.canRun(any)).thenReturn(true);
+ when(mockProcessManager.run(
+ any,
+ workingDirectory: anyNamed('workingDirectory'),
+ environment: anyNamed('environment'),
+ )).thenAnswer((_) async => ProcessResult(1, 0, 'stdout', 'stderr'));
+ when(mockProcessManager.runSync(any)).thenReturn(ProcessResult(1, 0, 'stdout', 'stderr'));
+ final FlutterProject project = await FlutterProject.current();
+ gradle = fs.file(project.android.hostAppGradleRoot.childFile(
+ platform.isWindows ? 'gradlew.bat' : 'gradlew',
+ ).path)..createSync(recursive: true);
+ });
+
+ testUsingContext('Licenses available, build tools not, apk exists', () async {
+ when(sdk.latestVersion).thenReturn(null);
+ final FlutterProject project = await FlutterProject.current();
+ final File gradle = project.android.hostAppGradleRoot.childFile(
+ platform.isWindows ? 'gradlew.bat' : 'gradlew',
+ )..createSync(recursive: true);
+
+ await ApplicationPackageFactory.instance.getPackageForPlatform(
+ TargetPlatform.android_arm,
+ applicationBinary: fs.file('app.apk'),
+ );
+ verify(
+ mockProcessManager.run(
+ argThat(equals(<String>[gradle.path, 'dependencies'])),
+ workingDirectory: anyNamed('workingDirectory'),
+ environment: anyNamed('environment'),
+ ),
+ ).called(1);
+ }, overrides: overrides);
+
+ testUsingContext('Licenses available, build tools available, does not call gradle dependencies', () async {
+ final AndroidSdkVersion sdkVersion = MockitoAndroidSdkVersion();
+ when(sdk.latestVersion).thenReturn(sdkVersion);
+
+ await ApplicationPackageFactory.instance.getPackageForPlatform(
+ TargetPlatform.android_arm,
+ );
+ verifyNever(
+ mockProcessManager.run(
+ argThat(equals(<String>[gradle.path, 'dependencies'])),
+ workingDirectory: anyNamed('workingDirectory'),
+ environment: anyNamed('environment'),
+ ),
+ );
+ }, overrides: overrides);
+ });
+
group('ApkManifestData', () {
test('Parses manifest with an Activity that has enabled set to true, action set to android.intent.action.MAIN and category set to android.intent.category.LAUNCHER', () {
final ApkManifestData data = ApkManifestData.parseFromXmlDump(_aaptDataWithExplicitEnabledAndMainLauncherActivity);
diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart
index 015b96e..0150d4c 100644
--- a/packages/flutter_tools/test/src/mocks.dart
+++ b/packages/flutter_tools/test/src/mocks.dart
@@ -11,6 +11,7 @@
import 'package:flutter_tools/src/application_package.dart';
import 'package:flutter_tools/src/base/file_system.dart' hide IOSink;
import 'package:flutter_tools/src/base/io.dart';
+import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/compile.dart';
import 'package:flutter_tools/src/devfs.dart';
@@ -44,16 +45,26 @@
int ndkVersion = 16,
bool withNdkSysroot = false,
bool withSdkManager = true,
+ bool withPlatformTools = true,
+ bool withBuildTools = true,
}) {
final Directory dir = fs.systemTempDirectory.createTempSync('flutter_mock_android_sdk.');
+ final String exe = platform.isWindows ? '.exe' : '';
+ final String bat = platform.isWindows ? '.bat' : '';
- _createSdkFile(dir, 'platform-tools/adb');
+ _createDir(dir, 'licenses');
- _createSdkFile(dir, 'build-tools/19.1.0/aapt');
- _createSdkFile(dir, 'build-tools/22.0.1/aapt');
- _createSdkFile(dir, 'build-tools/23.0.2/aapt');
- if (withAndroidN)
- _createSdkFile(dir, 'build-tools/24.0.0-preview/aapt');
+ if (withPlatformTools) {
+ _createSdkFile(dir, 'platform-tools/adb$exe');
+ }
+
+ if (withBuildTools) {
+ _createSdkFile(dir, 'build-tools/19.1.0/aapt$exe');
+ _createSdkFile(dir, 'build-tools/22.0.1/aapt$exe');
+ _createSdkFile(dir, 'build-tools/23.0.2/aapt$exe');
+ if (withAndroidN)
+ _createSdkFile(dir, 'build-tools/24.0.0-preview/aapt$exe');
+ }
_createSdkFile(dir, 'platforms/android-22/android.jar');
_createSdkFile(dir, 'platforms/android-23/android.jar');
@@ -63,7 +74,7 @@
}
if (withSdkManager)
- _createSdkFile(dir, 'tools/bin/sdkmanager');
+ _createSdkFile(dir, 'tools/bin/sdkmanager$bat');
if (withNdkDir != null) {
final String ndkToolchainBin = fs.path.join(