Skip to content

Commit e0245cc

Browse files
committed
Test against multiple versions.
1 parent 7218fcd commit e0245cc

File tree

14 files changed

+771
-22
lines changed

14 files changed

+771
-22
lines changed

.github/workflows/check.yml

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name: CI
2+
3+
# Controls when the action will run. Triggers the workflow on push or pull request
4+
# events but only for the master branch
5+
on:
6+
push:
7+
branches: [ master, citest ]
8+
pull_request:
9+
branches: [ master ]
10+
11+
jobs:
12+
generate_versions:
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
17+
- uses: actions/checkout@v1
18+
19+
- name: Gradle test
20+
run: |
21+
./gradlew -p plugin generateTestTasksJson
22+
23+
- id: setup-matrix
24+
run: echo "::set-output name=matrix::$(cat plugin/build/build-resources/androidTestTasks.json)"
25+
26+
- name: debug
27+
run: echo ${{ steps.setup-matrix.outputs.matrix }}
28+
29+
outputs:
30+
matrix: ${{ steps.setup-matrix.outputs.matrix }}
31+
32+
android_version_tests:
33+
needs: [generate_versions] # , sanity_check]
34+
35+
# The type of runner that the job will run on
36+
runs-on: ubuntu-latest
37+
38+
strategy:
39+
fail-fast: false
40+
matrix:
41+
androidTestTask: ${{ fromJson(needs.generate_versions.outputs.matrix) }}
42+
43+
# Steps represent a sequence of tasks that will be executed as part of the job
44+
steps:
45+
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
46+
- uses: actions/checkout@v1
47+
48+
# # Sets up the NDK required by AGP 3.6.x
49+
# - name: Setup NDK
50+
# run: sudo $ANDROID_HOME/tools/bin/sdkmanager 'ndk;20.0.5594570'
51+
52+
# - name: Install Rustup
53+
# run: |
54+
# curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain none
55+
# echo "$HOME/.cargo/bin" >> $GITHUB_PATH
56+
57+
- name: Setup Rust
58+
run: |
59+
rustup toolchain install stable
60+
rustup target add x86_64-linux-android
61+
rustup target add x86_64-unknown-linux-gnu
62+
63+
# Use Java 8
64+
- name: Setup Java 8
65+
uses: actions/setup-java@v1
66+
with:
67+
java-version: 8
68+
69+
- name: Gradle setup
70+
run: |
71+
./gradlew -p plugin tasks --warning-mode all
72+
73+
- name: Gradle test
74+
run: |
75+
./gradlew -p plugin ${{ matrix.androidTestTask }} --tests CargoBuildTest --info --warning-mode all
76+
77+
# # Gradle build
78+
# - uses: eskatos/gradle-command-action@v1
79+
# with:
80+
# arguments: ${{ matrix.androidTestTask }} -I gradle/buildScanInit.gradle
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists

plugin/build.gradle

+116-19
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1+
import groovy.json.JsonBuilder
2+
import org.gradle.util.VersionNumber
3+
14
plugins {
25
id 'com.gradle.plugin-publish' version '0.14.0'
6+
id "org.gradle.test-retry" version "1.2.0"
37
}
48

59
apply plugin: "java-gradle-plugin"
610
apply plugin: "maven-publish"
11+
apply plugin: "groovy"
712
apply plugin: "kotlin"
813

914
gradlePlugin {
1015
plugins {
11-
simplePlugin {
16+
rustAndroidGradlePlugin {
1217
id = 'org.mozilla.rust-android-gradle'
1318
implementationClass = 'com.nishtahir.RustAndroidPlugin'
1419
displayName = 'Plugin for building Rust with Cargo in Android projects'
@@ -20,26 +25,40 @@ gradlePlugin {
2025
group 'org.mozilla.rust-android-gradle'
2126
version "$plugin_version"
2227

28+
def isCI = (System.getenv('CI') ?: 'false').toBoolean()
29+
30+
// Maps supported Android plugin versions to the versions of Gradle that support it
31+
def supportedVersions = [
32+
"7.0.0": ["7.1.1"],
33+
"4.2.2": ["6.8.3", "7.1.1"],
34+
"4.1.3": ["6.5.1", "6.8.3"],
35+
"4.0.2": ["6.1.1", "6.8.3"],
36+
"3.6.4": ["5.6.4", "6.8.3"],
37+
"3.5.4": ["5.4.1", "5.6.4", "6.8.3"],
38+
"3.1.2": ["4.10.2"]
39+
]
40+
41+
// A local repo we publish our library to for testing in order to workaround limitations
42+
// in the TestKit plugin classpath.
43+
def localRepo = file("$buildDir/local-repo")
2344
publishing {
2445
repositories {
2546
maven {
26-
url "../samples/maven-repo"
27-
}
28-
}
29-
publications {
30-
maven(MavenPublication) {
31-
groupId 'org.mozilla.rust-android-gradle'
32-
artifactId 'rust-android'
33-
34-
from components.java
47+
url = localRepo.toURI()
3548
}
3649
}
3750
}
3851

3952
dependencies {
40-
compileOnly gradleApi()
41-
implementation "com.android.tools.build:gradle:$agp_version"
42-
// implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
53+
implementation gradleApi()
54+
compileOnly "com.android.tools.build:gradle:${agp_version}"
55+
56+
testImplementation gradleTestKit()
57+
testImplementation "com.android.tools.build:gradle:${agp_version}"
58+
testImplementation platform("org.spockframework:spock-bom:2.0-M5-groovy-3.0")
59+
testImplementation("org.spockframework:spock-core") { exclude group: 'org.codehaus.groovy' }
60+
testImplementation("org.spockframework:spock-junit4") { exclude group: 'org.codehaus.groovy' }
61+
testImplementation "org.junit.jupiter:junit-jupiter-api"
4362
}
4463

4564
compileKotlin {
@@ -54,12 +73,90 @@ pluginBundle {
5473
vcsUrl = 'https://github.com/mozilla/rust-android-gradle.git'
5574
tags = ['rust', 'cargo', 'android']
5675

57-
plugins {
58-
rustAndroidPlugin {
59-
id = 'org.mozilla.rust-android-gradle.rust-android'
60-
displayName = 'Plugin for building Rust with Cargo in Android projects'
61-
description = 'A plugin that helps build Rust JNI libraries with Cargo for use in Android projects.'
62-
tags = ['rust', 'cargo', 'android']
76+
mavenCoordinates {
77+
groupId = "gradle.plugin.org.mozilla.rust-android-gradle"
78+
}
79+
}
80+
81+
82+
// Generate a json file that contains the matrix of Gradle and AGP versions to test against.
83+
def generatedResources = "$buildDir/generated-resources/main"
84+
tasks.register('generateVersions') {
85+
def outputFile = file("$generatedResources/versions.json")
86+
inputs.property "version", version
87+
inputs.property "supportedVersions", supportedVersions
88+
outputs.dir generatedResources
89+
doLast {
90+
outputFile.text = new JsonBuilder([
91+
version: version,
92+
supportedVersions: supportedVersions
93+
]).toPrettyString()
94+
}
95+
}
96+
97+
sourceSets {
98+
main {
99+
output.dir(generatedResources, builtBy: tasks.named('generateVersions'))
100+
}
101+
}
102+
103+
// This is used by github actions to split out jobs by Android version test task
104+
def generatedBuildResources = "$buildDir/build-resources"
105+
tasks.register('generateTestTasksJson') {
106+
def outputFile = file("${generatedBuildResources}/androidTestTasks.json")
107+
inputs.property "supportedVersions", supportedVersions
108+
outputs.dir generatedBuildResources
109+
doLast {
110+
outputFile.text = new JsonBuilder(
111+
// Fails in CI with issues invoking Java 11. The single test that
112+
// requires Java 11 succeeds. To be investigated in the future.
113+
// ['test'] +
114+
(supportedVersions.keySet().collect {androidVersion -> androidTestTaskName(androidVersion) })
115+
).toString()
116+
}
117+
}
118+
119+
// Configuration common to all test tasks
120+
tasks.withType(Test).configureEach {
121+
dependsOn publish
122+
systemProperty "local.repo", localRepo.toURI()
123+
useJUnitPlatform()
124+
retry {
125+
maxRetries = isCI ? 1 : 0
126+
maxFailures = 20
127+
}
128+
}
129+
130+
// Generate a test task for each Android version and run the tests annotated with the MultiVersionTest category
131+
supportedVersions.keySet().each { androidVersion ->
132+
def testTaskName = androidTestTaskName(androidVersion)
133+
def jdkVersion = jdkVersionFor(androidVersion)
134+
def versionSpecificTest = tasks.register(testTaskName, Test) {
135+
description = "Runs the multi-version tests for AGP ${androidVersion}"
136+
group = "verification"
137+
138+
javaLauncher = javaToolchains.launcherFor {
139+
languageVersion = jdkVersion
63140
}
141+
142+
systemProperty 'org.gradle.android.testVersion', androidVersion
143+
}
144+
145+
tasks.named('check').configure {
146+
dependsOn versionSpecificTest
64147
}
65148
}
149+
150+
static def androidTestTaskName(String androidVersion) {
151+
return "testAndroid${normalizeVersion(androidVersion)}"
152+
}
153+
154+
static def normalizeVersion(String version) {
155+
return version.replaceAll('[.\\-]', '_')
156+
}
157+
158+
static def jdkVersionFor(String version) {
159+
def jdkVersion = VersionNumber.parse(version) > VersionNumber.parse("7.0.0-alpha01") ? 11 : 8
160+
161+
return JavaLanguageVersion.of(jdkVersion)
162+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.nishtahir
2+
3+
import com.google.common.collect.ImmutableMultimap
4+
import com.google.common.collect.ImmutableSortedSet
5+
import com.google.common.collect.Multimap
6+
import groovy.json.JsonSlurper
7+
import groovy.transform.CompileStatic
8+
import groovy.transform.TypeCheckingMode
9+
import org.gradle.util.GradleVersion
10+
import org.gradle.util.VersionNumber
11+
12+
@CompileStatic(TypeCheckingMode.SKIP)
13+
class Versions {
14+
static final VersionNumber PLUGIN_VERSION;
15+
static final Set<GradleVersion> SUPPORTED_GRADLE_VERSIONS
16+
static final Set<VersionNumber> SUPPORTED_ANDROID_VERSIONS
17+
static final Multimap<VersionNumber, GradleVersion> SUPPORTED_VERSIONS_MATRIX
18+
19+
static {
20+
def versions = new JsonSlurper().parse(Versions.classLoader.getResource("versions.json"))
21+
PLUGIN_VERSION = VersionNumber.parse(versions.version)
22+
23+
def builder = ImmutableMultimap.<VersionNumber, GradleVersion>builder()
24+
versions.supportedVersions.each { String androidVersion, List<String> gradleVersions ->
25+
builder.putAll(android(androidVersion), gradleVersions.collect { gradle(it) })
26+
}
27+
def matrix = builder.build()
28+
29+
SUPPORTED_VERSIONS_MATRIX = matrix
30+
SUPPORTED_ANDROID_VERSIONS = ImmutableSortedSet.copyOf(matrix.keySet())
31+
SUPPORTED_GRADLE_VERSIONS = ImmutableSortedSet.copyOf(matrix.values())
32+
}
33+
34+
static VersionNumber android(String version) {
35+
VersionNumber.parse(version)
36+
}
37+
38+
static GradleVersion gradle(String version) {
39+
GradleVersion.version(version)
40+
}
41+
42+
static VersionNumber earliestMaybeSupportedAndroidVersion() {
43+
VersionNumber earliestSupported = SUPPORTED_ANDROID_VERSIONS.min()
44+
// "alpha" is lower than null
45+
return new VersionNumber(earliestSupported.major, earliestSupported.minor, 0, "alpha")
46+
}
47+
48+
static VersionNumber latestAndroidVersion() {
49+
return SUPPORTED_ANDROID_VERSIONS.max()
50+
}
51+
}

plugin/src/main/kotlin/com/nishtahir/RustAndroidPlugin.kt

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import org.gradle.api.DefaultTask
55
import org.gradle.api.GradleException
66
import org.gradle.api.Plugin
77
import org.gradle.api.Project
8+
import org.gradle.api.file.DuplicatesStrategy
89
import java.io.File
910
import java.util.Properties
1011

@@ -254,6 +255,7 @@ open class RustAndroidPlugin : Plugin<Project> {
254255
}
255256
fileMode = 493 // 0755 in decimal; Kotlin doesn't have octal literals (!).
256257
includeEmptyDirs = false
258+
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
257259
}
258260

259261
val buildTask = tasks.maybeCreate("cargoBuild",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.nishtahir
2+
3+
import org.gradle.testkit.runner.GradleRunner
4+
import org.junit.Rule
5+
import org.junit.rules.TemporaryFolder
6+
import spock.lang.Specification
7+
8+
class AbstractTest extends Specification {
9+
@Rule TemporaryFolder temporaryFolder
10+
File cacheDir
11+
12+
def setup() {
13+
cacheDir = temporaryFolder.newFolder()
14+
}
15+
16+
def withGradleVersion(String gradleVersion) {
17+
GradleRunner.create()
18+
.withGradleVersion(gradleVersion)
19+
.forwardOutput()
20+
.withDebug(false)
21+
}
22+
23+
File file(String path) {
24+
return new File(temporaryFolder.root, path)
25+
}
26+
}

0 commit comments

Comments
 (0)