blob: dc0d4835f2f51ab1805b5c5f10a9d1c3f5ea29fe [file] [log] [blame]
// 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:io';
import 'package:flutter_tools/src/android/android_builder.dart';
import 'package:flutter_tools/src/base/file_system.dart' as file_system;
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/project.dart';
import '../integration.shard/test_utils.dart';
import 'common.dart';
/// A fake implementation of [AndroidBuilder].
class FakeAndroidBuilder implements AndroidBuilder {
@override
Future<void> buildAar({
required FlutterProject project,
required Set<AndroidBuildInfo> androidBuildInfo,
required String target,
required Future<void> Function(FlutterProject, {required bool releaseMode}) generateTooling,
String? outputDirectoryPath,
required String buildNumber,
}) async {}
@override
Future<void> buildApk({
required FlutterProject project,
required AndroidBuildInfo androidBuildInfo,
required String target,
bool configOnly = false,
}) async {}
@override
Future<void> buildAab({
required FlutterProject project,
required AndroidBuildInfo androidBuildInfo,
required String target,
bool validateDeferredComponents = true,
bool deferredComponentsEnabled = false,
bool configOnly = false,
}) async {}
@override
Future<List<String>> getBuildVariants({required FlutterProject project}) async =>
const <String>[];
@override
Future<String> outputsAppLinkSettings(
String buildVariant, {
required FlutterProject project,
}) async => '/';
}
/// Creates a [FlutterProject] in a directory named `flutter_project`
/// within [directoryOverride].
class FakeFlutterProjectFactory extends FlutterProjectFactory {
FakeFlutterProjectFactory(this.directoryOverride)
: super(fileSystem: globals.fs, logger: globals.logger);
final file_system.Directory directoryOverride;
@override
FlutterProject fromDirectory(Directory _) {
projects.clear();
return super.fromDirectory(directoryOverride.childDirectory('flutter_project'));
}
}
// The following test outline shares a lot of similarities with the one in
// dev/devicelab/lib/framework/dependency_smoke_test_task_definition.dart
// When making changes here, consider making the corresponding changes to that
// file as well.
const gradleSettingsFileContent = r'''
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "AGP_REPLACE_ME" apply false
id "org.jetbrains.kotlin.android" version "KGP_REPLACE_ME" apply false
}
include ":app"
''';
const agpReplacementString = 'AGP_REPLACE_ME';
const kgpReplacementString = 'KGP_REPLACE_ME';
const gradleWrapperPropertiesFileContent = r'''
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-GRADLE_REPLACE_ME-all.zip
''';
const gradleReplacementString = 'GRADLE_REPLACE_ME';
const flutterCompileSdkString = 'flutter.compileSdkVersion';
class VersionTuple {
VersionTuple({
required this.agpVersion,
required this.gradleVersion,
required this.kotlinVersion,
this.compileSdkVersion,
});
String agpVersion;
String gradleVersion;
String kotlinVersion;
String? compileSdkVersion;
@override
String toString() {
return '(AGP version: $agpVersion, Gradle version: $gradleVersion, Kotlin version: $kotlinVersion'
'${(compileSdkVersion == null) ? '' : ', compileSdk version: $compileSdkVersion)'}';
}
}
/// Creates a new Flutter project with the specified AGP, Gradle, and Kotlin
/// versions and then tries to call `flutter build apk`, returning the
/// ProcessResult.
Future<ProcessResult> buildFlutterApkWithSpecifiedDependencyVersions({
required VersionTuple versions,
required Directory tempDir,
bool skipChecking = false,
}) async {
// Create a new flutter project.
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
ProcessResult result = await processManager.run(<String>[
flutterBin,
'create',
'dependency_checker_app',
'--platforms=android',
], workingDirectory: tempDir.path);
expect(result, const ProcessResultMatcher());
final app = Directory(fileSystem.path.join(tempDir.path, 'dependency_checker_app'));
if (versions.compileSdkVersion != null) {
final appGradleBuild = File(
fileSystem.path.join(app.path, 'android', 'app', 'build.gradle.kts'),
);
final String appBuildContent = appGradleBuild.readAsStringSync().replaceFirst(
flutterCompileSdkString,
versions.compileSdkVersion!,
);
appGradleBuild.writeAsStringSync(appBuildContent);
}
// Modify gradle version to passed in version.
final gradleWrapperProperties = File(
fileSystem.path.join(app.path, 'android', 'gradle', 'wrapper', 'gradle-wrapper.properties'),
);
final String propertyContent = gradleWrapperPropertiesFileContent.replaceFirst(
gradleReplacementString,
versions.gradleVersion,
);
await gradleWrapperProperties.writeAsString(propertyContent, flush: true);
final gradleSettings = File(fileSystem.path.join(app.path, 'android', 'settings.gradle'));
final String settingsContent = gradleSettingsFileContent
.replaceFirst(agpReplacementString, versions.agpVersion)
.replaceFirst(kgpReplacementString, versions.kotlinVersion);
await gradleSettings.writeAsString(settingsContent, flush: true);
// Ensure that gradle files exists from templates.
result = await processManager.run(<String>[
flutterBin,
'build',
'apk',
'--debug',
if (skipChecking) '--android-skip-build-dependency-validation',
], workingDirectory: app.path);
return result;
}