Skip to content

Commit a1f0360

Browse files
authored
Set up tests that verify we can build a fresh counter app across our Gradle/AGP/Kotlin support range (#151568)
Sets up tests that verify we can build a fresh counter app across our Gradle/AGP/Kotlin support range. Post submit only, because the suite takes ~30 minutes to run, and I expect it to be _somewhat_ rare that we break only one of these versions (and therefore it doesn't get caught by existing presubmits).
1 parent 118a015 commit a1f0360

File tree

7 files changed

+382
-99
lines changed

7 files changed

+382
-99
lines changed

.ci.yaml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,42 @@ targets:
12151215
- bin/**
12161216
- .ci.yaml
12171217

1218+
- name: Linux android_java11_dependency_smoke_tests
1219+
recipe: devicelab/devicelab_drone
1220+
presubmit: false
1221+
bringup: true
1222+
timeout: 60
1223+
properties:
1224+
add_recipes_cq: "true"
1225+
dependencies: >-
1226+
[
1227+
{"dependency": "android_sdk", "version": "version:34v3"},
1228+
{"dependency": "chrome_and_driver", "version": "version:125.0.6422.141"},
1229+
{"dependency": "open_jdk", "version": "version:11"}
1230+
]
1231+
task_name: android_java11_dependency_smoke_tests
1232+
tags: >
1233+
["devicelab", "hostonly", "linux"]
1234+
test_timeout_secs: "2700"
1235+
1236+
- name: Linux android_java17_dependency_smoke_tests
1237+
recipe: devicelab/devicelab_drone
1238+
presubmit: false
1239+
bringup: true
1240+
timeout: 60
1241+
properties:
1242+
add_recipes_cq: "true"
1243+
dependencies: >-
1244+
[
1245+
{"dependency": "android_sdk", "version": "version:34v3"},
1246+
{"dependency": "chrome_and_driver", "version": "version:125.0.6422.141"},
1247+
{"dependency": "open_jdk", "version": "version:17"}
1248+
]
1249+
task_name: android_java17_dependency_smoke_tests
1250+
tags: >
1251+
["devicelab", "hostonly", "linux"]
1252+
test_timeout_secs: "2700"
1253+
12181254
- name: Linux tool_tests_commands
12191255
recipe: flutter/flutter_drone
12201256
timeout: 60

TESTOWNERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
/dev/devicelab/bin/tasks/analyzer_benchmark.dart @andrewkolos @flutter/tool
1313
/dev/devicelab/bin/tasks/android_choreographer_do_frame_test.dart @reidbaker @flutter/engine
1414
/dev/devicelab/bin/tasks/android_defines_test.dart @andrewkolos @flutter/tool
15+
/dev/devicelab/bin/tasks/android_java11_dependency_smoke_tests.dart @gmackall @flutter/android
16+
/dev/devicelab/bin/tasks/android_java17_dependency_smoke_tests.dart @gmackall @flutter/android
1517
/dev/devicelab/bin/tasks/android_lifecycles_test.dart @reidbaker @flutter/engine
1618
/dev/devicelab/bin/tasks/android_obfuscate_test.dart @andrewkolos @flutter/tool
1719
/dev/devicelab/bin/tasks/android_picture_cache_complexity_scoring_perf__timeline_summary.dart @flar @flutter/engine
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:io';
6+
7+
import 'package:file/local.dart';
8+
import 'package:flutter_devicelab/framework/dependency_smoke_test_task_definition.dart';
9+
import 'package:flutter_devicelab/framework/framework.dart';
10+
11+
// Methodology:
12+
// - AGP: all versions within our support range (*).
13+
// - Gradle: The version that AGP lists as the default Gradle version for that
14+
// AGP version under the release notes, e.g.
15+
// https://developer.android.com/build/releases/past-releases/agp-8-4-0-release-notes.
16+
// - Kotlin: No methodology as of yet.
17+
// (*) - support range defined in packages/flutter_tools/gradle/src/main/kotlin/dependency_version_checker.gradle.kts.
18+
List<VersionTuple> versionTuples = <VersionTuple>[
19+
VersionTuple(agpVersion: '7.0.1', gradleVersion: '7.0.2', kotlinVersion: '1.7.10'),
20+
VersionTuple(agpVersion: '7.1.0', gradleVersion: '7.2', kotlinVersion: '1.7.10'),
21+
VersionTuple(agpVersion: '7.2.0', gradleVersion: '7.3.3', kotlinVersion: '1.7.10'),
22+
VersionTuple(agpVersion: '7.3.0', gradleVersion: '7.4', kotlinVersion: '1.7.10'),
23+
VersionTuple(agpVersion: '7.4.0', gradleVersion: '7.5', kotlinVersion: '1.8.10'),
24+
];
25+
26+
// This test requires a Java version less than 17 due to the intentionally low
27+
// version of Gradle. We choose 11 because this was the primary version used in
28+
// CI before 17, and hence it is also hosted on CIPD.
29+
// https://docs.gradle.org/current/userguide/compatibility.html
30+
Future<void> main() async {
31+
/// The [FileSystem] for the integration test environment.
32+
const LocalFileSystem fileSystem = LocalFileSystem();
33+
34+
final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_android_dependency_version_tests');
35+
await task(() {
36+
return buildFlutterApkWithSpecifiedDependencyVersions(versionTuples: versionTuples, tempDir: tempDir, localFileSystem: fileSystem);
37+
});
38+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:io';
6+
7+
import 'package:file/local.dart';
8+
import 'package:flutter_devicelab/framework/dependency_smoke_test_task_definition.dart';
9+
import 'package:flutter_devicelab/framework/framework.dart';
10+
11+
// Methodology:
12+
// - AGP: all versions within our support range (*).
13+
// - Gradle: The version that AGP lists as the default Gradle version for that
14+
// AGP version under the release notes, e.g.
15+
// https://developer.android.com/build/releases/past-releases/agp-8-4-0-release-notes.
16+
// - Kotlin: No methodology as of yet.
17+
// (*) - support range defined in packages/flutter_tools/gradle/src/main/kotlin/dependency_version_checker.gradle.kts.
18+
List<VersionTuple> versionTuples = <VersionTuple>[
19+
VersionTuple(agpVersion: '8.0.0', gradleVersion: '8.0', kotlinVersion: '1.8.22'),
20+
VersionTuple(agpVersion: '8.1.0', gradleVersion: '8.0', kotlinVersion: '1.8.22'),
21+
VersionTuple(agpVersion: '8.2.0', gradleVersion: '8.2', kotlinVersion: '1.8.22'),
22+
VersionTuple(agpVersion: '8.3.0', gradleVersion: '8.4', kotlinVersion: '1.8.22'),
23+
VersionTuple(agpVersion: '8.4.0', gradleVersion: '8.6', kotlinVersion: '1.8.22'),
24+
VersionTuple(agpVersion: '8.5.0', gradleVersion: '8.7', kotlinVersion: '1.8.22'),
25+
];
26+
27+
Future<void> main() async {
28+
/// The [FileSystem] for the integration test environment.
29+
const LocalFileSystem fileSystem = LocalFileSystem();
30+
31+
final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_android_dependency_version_tests');
32+
await task(() {
33+
return buildFlutterApkWithSpecifiedDependencyVersions(versionTuples: versionTuples, tempDir: tempDir, localFileSystem: fileSystem);
34+
});
35+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:io';
6+
7+
import 'package:file/local.dart';
8+
import 'task_result.dart';
9+
import 'utils.dart';
10+
11+
// The following test outline shares a lot of similarities with
12+
// the one in packages/flutter_tools/test/src/android_common.dart. When making
13+
// changes here, consider making the corresponding changes to that file as well.
14+
15+
/// The template settings.gradle content, with AGP and Kotlin versions replaced
16+
/// by an easily find/replaceable string.
17+
const String gradleSettingsFileContent = r'''
18+
pluginManagement {
19+
def flutterSdkPath = {
20+
def properties = new Properties()
21+
file("local.properties").withInputStream { properties.load(it) }
22+
def flutterSdkPath = properties.getProperty("flutter.sdk")
23+
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
24+
return flutterSdkPath
25+
}()
26+
27+
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
28+
29+
repositories {
30+
google()
31+
mavenCentral()
32+
gradlePluginPortal()
33+
}
34+
}
35+
36+
plugins {
37+
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
38+
id "com.android.application" version "AGP_REPLACE_ME" apply false
39+
id "org.jetbrains.kotlin.android" version "KGP_REPLACE_ME" apply false
40+
}
41+
42+
include ":app"
43+
44+
''';
45+
46+
const String agpReplacementString = 'AGP_REPLACE_ME';
47+
const String kgpReplacementString = 'KGP_REPLACE_ME';
48+
49+
/// The template gradle-wrapper.properties content, with the Gradle version replaced
50+
/// by an easily find/replaceable string.
51+
const String gradleWrapperPropertiesFileContent = r'''
52+
distributionBase=GRADLE_USER_HOME
53+
distributionPath=wrapper/dists
54+
zipStoreBase=GRADLE_USER_HOME
55+
zipStorePath=wrapper/dists
56+
distributionUrl=https\://services.gradle.org/distributions/gradle-GRADLE_REPLACE_ME-all.zip
57+
58+
''';
59+
60+
const String gradleReplacementString = 'GRADLE_REPLACE_ME';
61+
62+
/// A simple class containing a Kotlin, Gradle, and AGP version.
63+
class VersionTuple {
64+
65+
VersionTuple({
66+
required this.agpVersion,
67+
required this.gradleVersion,
68+
required this.kotlinVersion
69+
});
70+
71+
String agpVersion;
72+
String gradleVersion;
73+
String kotlinVersion;
74+
75+
@override
76+
String toString() {
77+
return '(AGP version: $agpVersion, Gradle version: $gradleVersion, Kotlin version: $kotlinVersion)';
78+
}
79+
}
80+
81+
/// For each [VersionTuple] in versionTuples:
82+
/// 1. Calls `flutter create`
83+
/// 2. Replaces the template AGP, Gradle, and Kotlin versions with those in the
84+
/// tuple.
85+
/// 3. Calls `flutter build apk`.
86+
/// Returns a failed task result if any of the `create` or `build apk` calls
87+
/// fails, returns a successful result otherwise. Cleans up in either case.
88+
Future<TaskResult> buildFlutterApkWithSpecifiedDependencyVersions({
89+
required List<VersionTuple> versionTuples,
90+
required Directory tempDir,
91+
required LocalFileSystem localFileSystem,}) async {
92+
for (final VersionTuple versions in versionTuples) {
93+
final Directory innerTempDir = tempDir.createTempSync(versions.gradleVersion);
94+
try {
95+
// Create a new flutter project.
96+
section('Create new app with Gradle ${versions.gradleVersion}, AGP ${versions.agpVersion}, and Kotlin ${versions.kotlinVersion}');
97+
await flutter(
98+
'create',
99+
options: <String>[
100+
'dependency_checker_app',
101+
'--platforms=android',
102+
],
103+
workingDirectory: innerTempDir.path,
104+
);
105+
106+
final String appPath = '${innerTempDir.absolute.path}/dependency_checker_app';
107+
108+
// Modify gradle version to passed in version.
109+
final File gradleWrapperProperties = localFileSystem.file(localFileSystem.path.join(
110+
appPath, 'android', 'gradle', 'wrapper', 'gradle-wrapper.properties'));
111+
final String propertyContent = gradleWrapperPropertiesFileContent.replaceFirst(
112+
gradleReplacementString,
113+
versions.gradleVersion,
114+
);
115+
await gradleWrapperProperties.writeAsString(propertyContent, flush: true);
116+
117+
final File gradleSettings = localFileSystem.file(localFileSystem.path.join(
118+
appPath, 'android', 'settings.gradle'));
119+
final String settingsContent = gradleSettingsFileContent
120+
.replaceFirst(agpReplacementString, versions.agpVersion)
121+
.replaceFirst(kgpReplacementString, versions.kotlinVersion);
122+
await gradleSettings.writeAsString(settingsContent, flush: true);
123+
124+
125+
// Ensure that gradle files exists from templates.
126+
section("Ensure 'flutter build apk' succeeds with Gradle ${versions.gradleVersion}, AGP ${versions.agpVersion}, and Kotlin ${versions.kotlinVersion}");
127+
await flutter(
128+
'build',
129+
options: <String>[
130+
'apk',
131+
'--debug',
132+
],
133+
workingDirectory: appPath,
134+
);
135+
} catch (e) {
136+
tempDir.deleteSync(recursive: true);
137+
return TaskResult.failure('Failed to build app with Gradle ${versions.gradleVersion}, AGP ${versions.agpVersion}, and Kotlin ${versions.kotlinVersion}, error was:\n$e');
138+
}
139+
}
140+
tempDir.deleteSync(recursive: true);
141+
return TaskResult.success(null);
142+
}

0 commit comments

Comments
 (0)