Skip to content

Commit

Permalink
Introduce extensions module (#309)
Browse files Browse the repository at this point in the history
* Add extensions module & introduce GrantPermissionExtension to it

* Remove deployment switch for Compose vs non-Compose

No longer needed since the minimum AGP has been bumped to 7 globally.
To work around the enforced single version per project by the Nexus Publishing plugin,
bump the Compose artifact to the same 1.4.0-SNAPSHOT line as the other instrumentation artifacts, f it
  • Loading branch information
mannodermaus authored Nov 4, 2023
1 parent b51d126 commit 86dce59
Show file tree
Hide file tree
Showing 12 changed files with 343 additions and 75 deletions.
13 changes: 10 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ defaults: &defaults
GRADLE_OPTS: -Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -Dorg.gradle.daemon=false -Dorg.gradle.caching=true -Dorg.gradle.configureondemand=true -Dkotlin.compiler.execution.strategy=in-process -Dkotlin.incremental=false

cache_key: &cache_key
key: jars-{{ checksum "plugin/build.gradle.kts" }}-{{ checksum "plugin/android-junit5/build.gradle.kts" }}-{{ checksum "plugin/gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "instrumentation/build.gradle.kts" }}-{{ checksum "instrumentation/runner/build.gradle.kts" }}-{{ checksum "instrumentation/sample/build.gradle.kts" }}-{{ checksum "instrumentation/gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "build-logic/src/main/kotlin/Environment.kt" }}-{{ checksum "build-logic/src/main/kotlin/Dependencies.kt" }}
key: jars-{{ checksum "plugin/build.gradle.kts" }}-{{ checksum "plugin/android-junit5/build.gradle.kts" }}-{{ checksum "plugin/gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "instrumentation/build.gradle.kts" }}-{{ checksum "instrumentation/core/build.gradle.kts" }}-{{ checksum "instrumentation/compose/build.gradle.kts" }}-{{ checksum "instrumentation/extensions/build.gradle.kts" }}-{{ checksum "instrumentation/runner/build.gradle.kts" }}-{{ checksum "instrumentation/sample/build.gradle.kts" }}-{{ checksum "instrumentation/gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "build-logic/src/main/kotlin/Environment.kt" }}-{{ checksum "build-logic/src/main/kotlin/Dependencies.kt" }}

commands:
construct_signing_key:
Expand Down Expand Up @@ -42,11 +42,12 @@ jobs:
./gradlew assembleRelease :core:assembleDebug \
:core:assembleDebugAndroidTest \
:compose:assembleDebugAndroidTest \
:extensions:assembleDebug \
:runner:assembleDebug \
:sample:assembleDebug --stacktrace
:sample:assembleDebug --stacktrace
- run:
name: (Instrumentation) Test
command: cd instrumentation && ./gradlew :core:check :runner:check :compose:check --stacktrace
command: cd instrumentation && ./gradlew :core:check :extensions:check :runner:check :compose:check --stacktrace

- save_cache:
<<: *cache_key
Expand Down Expand Up @@ -95,6 +96,9 @@ jobs:
- store_artifacts:
path: test-lab-results
destination: instrumentation-core/test-lab-results
- store_artifacts:
path: instrumentation/extensions/build/reports
destination: instrumentation-extensions
- store_artifacts:
path: instrumentation/runner/build/reports
destination: instrumentation-runner
Expand Down Expand Up @@ -126,6 +130,9 @@ jobs:
- store_artifacts:
path: instrumentation/core/build/publications
destination: instrumentation-core/publications/snapshots
- store_artifacts:
path: instrumentation/extensions/build/publications
destination: instrumentation-extensions/publications/snapshots
- store_artifacts:
path: instrumentation/runner/build/publications
destination: instrumentation-runner/publications/snapshots
Expand Down
21 changes: 0 additions & 21 deletions build-logic/src/main/kotlin/Deployment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,6 @@ fun Project.configureDeployment(deployConfig: Deployed) {
throw IllegalStateException("This method can not be called on the root project")
}

// Deployment of modules needs to be conditionally locked.
// If the project is set to Compose mode, only the Compose modules may be deployed.
// On the other hand, if the project is set to Default mode, only the ordinary
// instrumentation modules are deployed. This has to do with the restrictions
// of the Nexus Publishing plugin, which must use the same group and version declaration
// for all modules. It's impossible to use Version A for instrumentation modules and Version B
// for Compose modules at the same time, hence this conditional.
if (shouldSkipDeployment(deployConfig)) {
return
}

val credentials = DeployedCredentials(this)

// Configure root project (this only happens once
Expand Down Expand Up @@ -127,16 +116,6 @@ fun Project.configureDeployment(deployConfig: Deployed) {

/* Private */

private fun Project.shouldSkipDeployment(deployConfig: Deployed): Boolean {
return if (this.isComposeIncluded) {
// If Compose is included, any non-compose module should be skipped
deployConfig != Artifacts.Instrumentation.Compose
} else {
// If Compose is disabled, any compose module should be skipped
deployConfig == Artifacts.Instrumentation.Compose
}
}

private fun Project.configureRootDeployment(deployConfig: Deployed, credentials: DeployedCredentials) {
if (this != rootProject) {
throw IllegalStateException("This method can only be called on the root project")
Expand Down
88 changes: 49 additions & 39 deletions build-logic/src/main/kotlin/Environment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import Platform.Android
import Platform.Java
import org.gradle.api.Project
import java.io.File
import java.util.*
import java.util.Properties

enum class SupportedAgp(
val version: String,
val gradle: String? = null
val version: String,
val gradle: String? = null
) {
AGP_7_0("7.0.4", gradle = "7.0.2"),
AGP_7_1("7.1.3", gradle = "7.2"),
Expand Down Expand Up @@ -55,13 +55,13 @@ sealed class Platform(val name: String) {
* containing all sorts of configuration related to Maven coordinates, for instance.
*/
class Deployed internal constructor(
val platform: Platform,
val groupId: String,
val artifactId: String,
val currentVersion: String,
val latestStableVersion: String,
val description: String,
val license: String
val platform: Platform,
val groupId: String,
val artifactId: String,
val currentVersion: String,
val latestStableVersion: String,
val description: String,
val license: String
)

object Artifacts {
Expand All @@ -74,24 +74,24 @@ object Artifacts {
* Return null if none can be found
*/
fun from(project: Project) =
when (project.name) {
"core" -> Instrumentation.Core
"runner" -> Instrumentation.Runner
"android-junit5" -> Plugin
else -> null
}
when (project.name) {
"core" -> Instrumentation.Core
"runner" -> Instrumentation.Runner
"android-junit5" -> Plugin
else -> null
}

/**
* Gradle Plugin artifact
*/
val Plugin = Deployed(
platform = Java,
groupId = "de.mannodermaus.gradle.plugins",
artifactId = "android-junit5",
currentVersion = "1.10.0.0-SNAPSHOT",
latestStableVersion = "1.9.3.0",
license = license,
description = "Unit Testing with JUnit 5 for Android."
platform = Java,
groupId = "de.mannodermaus.gradle.plugins",
artifactId = "android-junit5",
currentVersion = "1.10.0.0-SNAPSHOT",
latestStableVersion = "1.9.3.0",
license = license,
description = "Unit Testing with JUnit 5 for Android."
)

/**
Expand All @@ -103,30 +103,40 @@ object Artifacts {
const val latestStableVersion = "1.3.0"

val Core = Deployed(
platform = Android(minSdk = 14),
groupId = groupId,
artifactId = "android-test-core",
currentVersion = currentVersion,
latestStableVersion = latestStableVersion,
license = license,
description = "Extensions for instrumented Android tests with JUnit 5."
platform = Android(minSdk = 14),
groupId = groupId,
artifactId = "android-test-core",
currentVersion = currentVersion,
latestStableVersion = latestStableVersion,
license = license,
description = "Extensions for instrumented Android tests with JUnit 5."
)

val Extensions = Deployed(
platform = Android(minSdk = 14),
groupId = groupId,
artifactId = "android-test-extensions",
currentVersion = currentVersion,
latestStableVersion = latestStableVersion,
license = license,
description = "Optional extensions for instrumented Android tests with JUnit 5."
)

val Runner = Deployed(
platform = Android(minSdk = 14),
groupId = groupId,
artifactId = "android-test-runner",
currentVersion = currentVersion,
latestStableVersion = latestStableVersion,
license = license,
description = "Runner for integration of instrumented Android tests with JUnit 5."
platform = Android(minSdk = 14),
groupId = groupId,
artifactId = "android-test-runner",
currentVersion = currentVersion,
latestStableVersion = latestStableVersion,
license = license,
description = "Runner for integration of instrumented Android tests with JUnit 5."
)

val Compose = Deployed(
platform = Android(minSdk = 21),
groupId = groupId,
artifactId = "android-test-compose",
currentVersion = "1.0.0-SNAPSHOT",
currentVersion = currentVersion,
latestStableVersion = "1.0.0-SNAPSHOT",
license = license,
description = "Extensions for Jetpack Compose tests with JUnit 5."
Expand Down Expand Up @@ -166,5 +176,5 @@ class DeployedCredentials(private val project: Project) {
}

private fun Properties.getOrEnvvar(key: String): String? =
getProperty(key, System.getenv(key))
getProperty(key, System.getenv(key))
}
9 changes: 0 additions & 9 deletions build-logic/src/main/kotlin/Utilities.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,6 @@ fun Project.findLocalPluginJar(): File? {
return localPluginJar
}

/**
* Returns whether or not the Compose library module is included in the project.
* This depends on the presence of the :compose module, which is configured
* in settings.gradle.
*/
val Project.isComposeIncluded: Boolean get() {
return findProject(":compose") != null
}

/* File */

/**
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions instrumentation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Change Log
- Only autoconfigure JUnit 5 for instrumentation tests when the user explicitly adds junit-jupiter-api as a dependency
- Prevent noisy logs in Logcat complaining about unresolvable annotation classes (#306)
- Add support for parallel execution of non-UI instrumentation tests (#295)
- Introduce `android-test-extensions` artifact with optional extensions, starting with a port of JUnit 4's `GrantPermissionRule` (#251)

## 1.3.0 (2021-09-17)

Expand Down
10 changes: 10 additions & 0 deletions instrumentation/extensions/api/extensions.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
public final class de/mannodermaus/junit5/extensions/GrantPermissionExtension : org/junit/jupiter/api/extension/BeforeEachCallback {
public static final field Companion Lde/mannodermaus/junit5/extensions/GrantPermissionExtension$Companion;
public fun beforeEach (Lorg/junit/jupiter/api/extension/ExtensionContext;)V
public static final fun grant ([Ljava/lang/String;)Lde/mannodermaus/junit5/extensions/GrantPermissionExtension;
}

public final class de/mannodermaus/junit5/extensions/GrantPermissionExtension$Companion {
public final fun grant ([Ljava/lang/String;)Lde/mannodermaus/junit5/extensions/GrantPermissionExtension;
}

93 changes: 93 additions & 0 deletions instrumentation/extensions/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import libs.plugins.android
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

buildscript {
repositories {
google()
mavenCentral()
sonatypeSnapshots()
}

dependencies {
val latest = Artifacts.Plugin.latestStableVersion
classpath("de.mannodermaus.gradle.plugins:android-junit5:$latest")
}
}

plugins {
id("com.android.library")
kotlin("android")
id("explicit-api-mode")
}

apply {
plugin("de.mannodermaus.android-junit5")
}

val javaVersion = JavaVersion.VERSION_1_8

android {
compileSdk = Android.compileSdkVersion

defaultConfig {
minSdk = Android.testRunnerMinSdkVersion
targetSdk = Android.targetSdkVersion
}

compileOptions {
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
}

buildFeatures {
buildConfig = false
resValues = false
}

lint {
// JUnit 4 refers to java.lang.management APIs, which are absent on Android.
warning("InvalidPackage")
}

packagingOptions {
resources.excludes.add("META-INF/LICENSE.md")
resources.excludes.add("META-INF/LICENSE-notice.md")
}

testOptions {
unitTests.isReturnDefaultValues = true
}
}

tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = javaVersion.toString()
}

tasks.withType<Test> {
failFast = true
testLogging {
events = setOf(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED)
exceptionFormat = TestExceptionFormat.FULL
}
}

configurations.all {
// The Instrumentation Test Runner uses the plugin,
// which in turn provides the Instrumentation Test Runner again -
// that's kind of deep.
// To avoid conflicts, prefer using the local classes
// and exclude the dependency from being pulled in externally.
exclude(module = Artifacts.Instrumentation.Extensions.artifactId)
}

dependencies {
implementation(libs.androidXTestRunner)
implementation(libs.junitJupiterApi)

testImplementation(project(":testutil"))
testRuntimeOnly(libs.junitJupiterEngine)
}

project.configureDeployment(Artifacts.Instrumentation.Extensions)
1 change: 1 addition & 0 deletions instrumentation/extensions/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest package="de.mannodermaus.junit5.extensions"/>
Loading

0 comments on commit 86dce59

Please sign in to comment.