Skip to content

Commit

Permalink
Enable build cache by default in Gradle integration tests (#3716)
Browse files Browse the repository at this point in the history
* Enable build cache by default in Gradle integration tests

Enabling build cache by default will improve performance when re-running the tests locally.

Update the test assertions to check that tasks either succeed or are loaded from cache (which does not make a difference in terms of integration tests).

Additionally, use `GRADLE_RO_DEP_CACHE` to re-use the host machine's Gradle dependencies cache. This will improve test performance locally and on CI.
  • Loading branch information
adam-enko authored Aug 1, 2024
1 parent e3f9e50 commit 89c5200
Show file tree
Hide file tree
Showing 18 changed files with 179 additions and 97 deletions.
2 changes: 2 additions & 0 deletions dokka-integration-tests/gradle/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ tasks.withType<Test>().configureEach {
val enableDebug = providers.environmentVariable("ENABLE_DEBUG")
inputs.property("enableDebug", enableDebug).optional(true)
environment("ENABLE_DEBUG", enableDebug.getOrElse("false"))

systemProperty("hostGradleUserHome", gradle.gradleUserHomeDir.invariantSeparatorsPath)
}

val templateSettingsGradleKts = layout.projectDirectory.file("projects/template.settings.gradle.kts")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import java.nio.file.Path
import java.nio.file.Paths
import kotlin.io.path.copyTo
import kotlin.io.path.copyToRecursively
import kotlin.io.path.exists
import kotlin.io.path.invariantSeparatorsPathString
import kotlin.test.BeforeTest
import kotlin.time.Duration.Companion.seconds
Expand All @@ -41,6 +42,7 @@ abstract class AbstractGradleIntegrationTest : AbstractIntegrationTest() {
buildVersions: BuildVersions,
vararg arguments: String,
jvmArgs: List<String> = listOf("-Xmx2G", "-XX:MaxMetaspaceSize=1G"),
enableBuildCache: Boolean = true,
): GradleRunner {

// TODO quick hack to add `android { namespace }` on AGP 7+ (it's mandatory in 8+).
Expand All @@ -66,27 +68,43 @@ abstract class AbstractGradleIntegrationTest : AbstractIntegrationTest() {
.withJetBrainsCachedGradleVersion(buildVersions.gradleVersion)
.withTestKitDir(File("build", "gradle-test-kit").absoluteFile)
.withDebug(TestEnvironment.isEnabledDebug)
.apply {
withEnvironment(
buildMap {
// `withEnvironment()` will wipe all existing environment variables,
// which breaks things like ANDROID_HOME and PATH, so re-add them.
putAll(System.getenv())

if (hostGradleDependenciesCache.exists()) {
put("GRADLE_RO_DEP_CACHE", hostGradleDependenciesCache.invariantSeparatorsPathString)
}
}
)
}
.withArguments(
listOfNotNull(
"-Pdokka_it_dokka_version=${dokkaVersion}",
"-Pdokka_it_kotlin_version=${buildVersions.kotlinVersion}",
buildList {
add(if (enableBuildCache) "--build-cache" else "--no-build-cache")

add("-Pdokka_it_dokka_version=${dokkaVersion}")
add("-Pdokka_it_kotlin_version=${buildVersions.kotlinVersion}")

buildVersions.androidGradlePluginVersion?.let { androidVersion ->
"-Pdokka_it_android_gradle_plugin_version=$androidVersion"
},
add("-Pdokka_it_android_gradle_plugin_version=$androidVersion")
}

// property flag to use K2
if (TestEnvironment.shouldUseK2())
"-P${TestEnvironment.TRY_K2}=true"
else
null,
if (TestEnvironment.shouldUseK2()) {
add("-P${TestEnvironment.TRY_K2}=true")
}

// Decrease Gradle daemon idle timeout to prevent old agents lingering on CI.
// A lower timeout means slower tests, which is preferred over OOMs and locked processes.
"-Dorg.gradle.daemon.idletimeout=" + 10.seconds.inWholeMilliseconds, // default is 3 hours!
"-Pkotlin.daemon.options.autoshutdownIdleSeconds=10",

* arguments
)
).withJvmArguments(jvmArgs)
add("-Dorg.gradle.daemon.idletimeout=" + 10.seconds.inWholeMilliseconds) // default is 3 hours!
add("-Pkotlin.daemon.options.autoshutdownIdleSeconds=10")
addAll(arguments)
}
)
.withJvmArguments(jvmArgs)
}

fun GradleRunner.buildRelaxed(): BuildResult {
Expand All @@ -97,7 +115,6 @@ abstract class AbstractGradleIntegrationTest : AbstractIntegrationTest() {
if (gradleConnectionException != null) {
gradleConnectionException.printStackTrace()
throw IllegalStateException("Assumed Gradle connection", gradleConnectionException)

}
throw e
}
Expand All @@ -123,6 +140,27 @@ abstract class AbstractGradleIntegrationTest : AbstractIntegrationTest() {
*/
val templateSettingsGradleKts: Path by systemProperty(Paths::get)

/**
* Gradle User Home of the current machine. Defaults to `~/.gradle`, but might be different on CI.
*
* This value is provided by the Gradle Test task.
*/
private val hostGradleUserHome: Path by systemProperty(Paths::get)

/**
* Gradle dependencies cache of the current machine.
*
* Used as a read-only dependencies cache by setting `GRADLE_RO_DEP_CACHE`
*
* See https://docs.gradle.org/8.9/userguide/dependency_resolution.html#sub:cache_copy
*
* Note: Currently all Gradle versions store caches in `$GRADLE_USER_HOME/caches/modules-2`,
* but this might change. Check the docs.
*/
private val hostGradleDependenciesCache: Path by lazy {
hostGradleUserHome.resolve("caches/modules-2")
}

/** file-based Maven repositories with Dokka dependencies */
private val devMavenRepositories: List<Path> by systemProperty { repos ->
repos.split(",").map { Paths.get(it) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package org.jetbrains.dokka.it.gradle

import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.BuildTask
import org.gradle.testkit.runner.TaskOutcome
import kotlin.test.assertContains
import kotlin.test.assertNotNull


/**
* Assert that the [BuildResult] has a task in the task graph.
*
* @returns the task, if it exists.
*/
fun BuildResult.shouldHaveTask(taskPath: String): BuildTask {
val actual = task(taskPath)
assertNotNull(actual) {
"Could not find task $taskPath in BuildResult. All tasks: ${tasks.map { it.path }}"
}
return actual
}

/**
* Assert that the [outcome][TaskOutcome] of a [BuildTask] is any of [expectedOutcomes].
*/
fun BuildTask.shouldHaveOutcome(
vararg expectedOutcomes: TaskOutcome
) {
require(expectedOutcomes.isNotEmpty()) {
"Invalid assertion. Must have at least 1 expected TaskOutcome, but got none."
}
assertContains(
expectedOutcomes.toList(),
outcome,
"Expected that task $path outcome was any of $expectedOutcomes, but was actually $outcome."
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import org.jetbrains.dokka.it.TestOutputCopier
import org.jetbrains.dokka.it.copyAndApplyGitDiff
import org.jetbrains.dokka.it.gradle.AbstractGradleIntegrationTest
import org.jetbrains.dokka.it.gradle.BuildVersions
import org.jetbrains.dokka.it.gradle.OnlyDescriptors
import org.junit.jupiter.api.extension.ExtensionContext
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@

package org.jetbrains.dokka.it.gradle

import org.gradle.testkit.runner.TaskOutcome
import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE
import org.gradle.testkit.runner.TaskOutcome.SUCCESS
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ArgumentsSource
import java.io.File
import kotlin.test.BeforeTest
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

internal class AndroidTestedVersionsArgumentsProvider : TestedVersionsArgumentsProvider(TestedVersions.ANDROID)
Expand Down Expand Up @@ -43,7 +42,7 @@ class Android0GradleIntegrationTest : AbstractGradleIntegrationTest() {
@ArgumentsSource(AndroidTestedVersionsArgumentsProvider::class)
fun execute(buildVersions: BuildVersions) {
val result = createGradleRunner(buildVersions, "dokkaHtml", "-i", "-s").buildRelaxed()
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaHtml")).outcome)
result.shouldHaveTask(":dokkaHtml").shouldHaveOutcome(SUCCESS, FROM_CACHE)

val htmlOutputDir = File(projectDir, "build/dokka/html")
assertTrue(htmlOutputDir.isDirectory, "Missing html output directory")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class BasicCachingIntegrationTest : AbstractGradleCachingIntegrationTest() {
"-i",
"-s",
"-Dorg.gradle.caching.debug=true",
"--build-cache"
enableBuildCache = true,
).buildRelaxed()

assertEquals(expectedOutcome, assertNotNull(result.task(":dokkaHtml")).outcome)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ class GradleRelocatedCachingIntegrationTest : AbstractGradleCachingIntegrationTe
) {
val result = createGradleRunner(
buildVersions,
"clean", "dokkaHtml", "-i", "-s", "-Dorg.gradle.caching.debug=true", "--build-cache"
"clean",
"dokkaHtml",
"-i",
"-s",
"-Dorg.gradle.caching.debug=true",
enableBuildCache = true,
).withProjectDir(project).buildRelaxed()

assertEquals(expectedOutcome, assertNotNull(result.task(":dokkaHtml")).outcome)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,32 @@

package org.jetbrains.dokka.it.gradle

import org.gradle.testkit.runner.TaskOutcome
import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE
import org.gradle.testkit.runner.TaskOutcome.SUCCESS
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ArgumentsSource
import java.io.File
import kotlin.test.BeforeTest
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

class BasicGroovyIntegrationTest : AbstractGradleIntegrationTest() {

@ParameterizedTest(name = "{0}")
@ArgumentsSource(AllSupportedTestedVersionsArgumentsProvider::class)
fun execute(buildVersions: BuildVersions) {
val result = createGradleRunner(buildVersions, "dokkaHtml", "dokkaJavadoc", "dokkaGfm", "dokkaJekyll", "-i", "-s")
.buildRelaxed()

assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaHtml")).outcome)
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaJavadoc")).outcome)
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaGfm")).outcome)
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaJekyll")).outcome)
val result = createGradleRunner(
buildVersions,
"dokkaHtml",
"dokkaJavadoc",
"dokkaGfm",
"dokkaJekyll",
"-i",
"-s"
).buildRelaxed()

result.shouldHaveTask(":dokkaHtml").shouldHaveOutcome(SUCCESS, FROM_CACHE)
result.shouldHaveTask(":dokkaJavadoc").shouldHaveOutcome(SUCCESS, FROM_CACHE)
result.shouldHaveTask(":dokkaGfm").shouldHaveOutcome(SUCCESS, FROM_CACHE)
result.shouldHaveTask(":dokkaJekyll").shouldHaveOutcome(SUCCESS, FROM_CACHE)

File(projectDir, "build/dokka/customHtml").assertKdocOutputDir()
File(projectDir, "build/dokka/customJavadoc").assertJavadocOutputDir()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@

package org.jetbrains.dokka.it.gradle

import org.gradle.testkit.runner.TaskOutcome
import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE
import org.gradle.testkit.runner.TaskOutcome.SUCCESS
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ArgumentsSource
import java.io.File
import kotlin.test.BeforeTest
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

class Collector0IntegrationTest : AbstractGradleIntegrationTest() {
Expand All @@ -27,10 +25,10 @@ class Collector0IntegrationTest : AbstractGradleIntegrationTest() {
"-i", "-s"
).buildRelaxed()

assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:dokkaHtmlCollector")).outcome)
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:dokkaJavadocCollector")).outcome)
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:dokkaGfmCollector")).outcome)
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:dokkaJekyllCollector")).outcome)
result.shouldHaveTask(":moduleA:dokkaHtmlCollector").shouldHaveOutcome(SUCCESS, FROM_CACHE)
result.shouldHaveTask(":moduleA:dokkaJavadocCollector").shouldHaveOutcome(SUCCESS, FROM_CACHE)
result.shouldHaveTask(":moduleA:dokkaGfmCollector").shouldHaveOutcome(SUCCESS, FROM_CACHE)
result.shouldHaveTask(":moduleA:dokkaJekyllCollector").shouldHaveOutcome(SUCCESS, FROM_CACHE)

File(projectDir, "moduleA/build/dokka/htmlCollector").assertHtmlOutputDir()
File(projectDir, "moduleA/build/dokka/javadocCollector").assertJavadocOutputDir()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,9 @@

package org.jetbrains.dokka.it.gradle

import org.gradle.testkit.runner.TaskOutcome
import org.gradle.testkit.runner.TaskOutcome.FAILED
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ArgumentsSource
import java.io.File
import kotlin.test.BeforeTest
import kotlin.test.assertEquals
import kotlin.test.assertNotNull

/**
* Tests for Dokka's configuration options of the Gradle runner.
Expand Down Expand Up @@ -44,7 +40,7 @@ class ConfigurationTest : AbstractGradleIntegrationTest() {
"dokkaHtml"
).buildAndFail()

assertEquals(TaskOutcome.FAILED, assertNotNull(result.task(":dokkaHtml")).outcome)
result.shouldHaveTask(":dokkaHtml").shouldHaveOutcome(FAILED)

result.output.contains("> Task :dokkaHtml FAILED")
result.output.contains(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@

package org.jetbrains.dokka.it.gradle

import org.gradle.testkit.runner.TaskOutcome
import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE
import org.gradle.testkit.runner.TaskOutcome.SUCCESS
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ArgumentsSource
import java.io.File
import kotlin.test.BeforeTest
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

class JsIRGradleIntegrationTest : AbstractGradleIntegrationTest() {
Expand All @@ -33,8 +31,15 @@ class JsIRGradleIntegrationTest : AbstractGradleIntegrationTest() {

val reactVersion = TestedVersions.KT_REACT_WRAPPER_MAPPING[buildVersions.kotlinVersion]
?: throw IllegalStateException("Unspecified version of react for kotlin " + buildVersions.kotlinVersion)
val result = createGradleRunner(buildVersions, "-Preact_version=$reactVersion", "dokkaHtml", "-i", "-s").buildRelaxed()
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaHtml")).outcome)
val result = createGradleRunner(
buildVersions,
"-Preact_version=$reactVersion",
"dokkaHtml",
"-i",
"-s"
).buildRelaxed()

result.shouldHaveTask(":dokkaHtml").shouldHaveOutcome(SUCCESS, FROM_CACHE)

val htmlOutputDir = File(projectDir, "build/dokka/html")
assertTrue(htmlOutputDir.isDirectory, "Missing html output directory")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

package org.jetbrains.dokka.it.gradle

import org.gradle.testkit.runner.TaskOutcome
import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE
import org.gradle.testkit.runner.TaskOutcome.SUCCESS
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ArgumentsSource
import java.io.File
import kotlin.test.*
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class MultiModule0IntegrationTest : AbstractGradleIntegrationTest() {

Expand All @@ -23,16 +26,15 @@ class MultiModule0IntegrationTest : AbstractGradleIntegrationTest() {
"-i", "-s"
).buildRelaxed()

assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:dokkaHtmlMultiModule")).outcome)
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:dokkaGfmMultiModule")).outcome)
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:dokkaJekyllMultiModule")).outcome)
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:moduleB:dokkaHtmlPartial")).outcome)
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:moduleC:dokkaHtmlPartial")).outcome)
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:moduleB:dokkaGfmPartial")).outcome)
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:moduleC:dokkaGfmPartial")).outcome)
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:moduleB:dokkaJekyllPartial")).outcome)
assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:moduleC:dokkaJekyllPartial")).outcome)

result.shouldHaveTask(":moduleA:dokkaHtmlMultiModule").shouldHaveOutcome(SUCCESS, FROM_CACHE)
result.shouldHaveTask(":moduleA:dokkaGfmMultiModule").shouldHaveOutcome(SUCCESS, FROM_CACHE)
result.shouldHaveTask(":moduleA:dokkaJekyllMultiModule").shouldHaveOutcome(SUCCESS, FROM_CACHE)
result.shouldHaveTask(":moduleA:moduleB:dokkaHtmlPartial").shouldHaveOutcome(SUCCESS, FROM_CACHE)
result.shouldHaveTask(":moduleA:moduleC:dokkaHtmlPartial").shouldHaveOutcome(SUCCESS, FROM_CACHE)
result.shouldHaveTask(":moduleA:moduleB:dokkaGfmPartial").shouldHaveOutcome(SUCCESS, FROM_CACHE)
result.shouldHaveTask(":moduleA:moduleC:dokkaGfmPartial").shouldHaveOutcome(SUCCESS, FROM_CACHE)
result.shouldHaveTask(":moduleA:moduleB:dokkaJekyllPartial").shouldHaveOutcome(SUCCESS, FROM_CACHE)
result.shouldHaveTask(":moduleA:moduleC:dokkaJekyllPartial").shouldHaveOutcome(SUCCESS, FROM_CACHE)

val outputDir = File(projectDir, "moduleA/build/dokka/htmlMultiModule")
assertTrue(outputDir.isDirectory, "Missing dokka output directory")
Expand Down
Loading

0 comments on commit 89c5200

Please sign in to comment.