Skip to content

Commit

Permalink
KTOR-7552 Rerun failed tests one more time
Browse files Browse the repository at this point in the history
  • Loading branch information
osipxd committed Oct 11, 2024
1 parent 8c838dd commit 891b76b
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 13 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ subprojects {
apply(plugin = "atomicfu-conventions")

configureTargets()
if (CI) configureTestTasksOnCi()

configurations {
maybeCreate("testOutput")
Expand Down
2 changes: 2 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ dependencies {
val ktlint_version = libs.versions.ktlint.get()
implementation("org.jmailen.gradle:kotlinter-gradle:$ktlint_version")

implementation(libs.develocity)

implementation("io.ktor:ktor-server-default-headers:$ktor_version")
implementation("io.ktor:ktor-server-netty:$ktor_version")
implementation("io.ktor:ktor-server-cio:$ktor_version")
Expand Down
74 changes: 74 additions & 0 deletions buildSrc/src/main/kotlin/CI.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

import com.gradle.develocity.agent.gradle.test.*
import org.gradle.api.*
import org.gradle.api.tasks.testing.*
import org.gradle.kotlin.dsl.*
import org.jetbrains.kotlin.gradle.targets.jvm.tasks.*
import org.jetbrains.kotlin.gradle.tasks.*

val CI = System.getenv("TEAMCITY_VERSION") != null

/** Applies CI-specific configurations to test tasks. */
fun Project.configureTestTasksOnCi() {
// Don't fail build on the CI:
// 1. To distinct builds failed because of failed tests and because of compilation errors or anything else.
// TeamCity parses test results to define build status, so the build won't be green.
// 2. To run as many tests as possible while keeping fail-fast behavior locally.
tasks.withType<AbstractTestTask>().configureEach {
ignoreFailures = true
if (this is KotlinTest) ignoreRunFailures = true
}

tasks.withType<KotlinJvmTest>().configureEach {
testRetry {
maxRetries = 1
maxFailures = 10
}

applyTestRetryCompatibilityWorkaround()
}
}

/**
* Test retry plugin is incompatible with test tasks that override the `createTestExecuter` method.
* This includes the [KotlinJvmTest] task which wraps the test executor with its own wrapper.
*
* This workaround heavily relies on the internal implementation details of the test-retry plugin and KGP.
*
* The test retry plugin adds a `doFirst` action, which:
* - Retrieves the test executer using `createTestExecuter` (KGP returns wrapped test executer here)
* - Wraps it with `RetryTestExecuter`
* - Sets the executer using `setTestExecuter`
*
* In the `doLast` action, it expects that `createTestExecuter` returns the previously created `RetryTestExecuter` instance.
* However, KGP wraps every result of `createTestExecutor` with its own wrapper, resulting in the following nesting:
* KotlinJvmTarget$Executer(RetryTestExecuter(KotlinJvmTarget$Executer(DefaultTestExecuter)))
*
* KGP wraps the executer only if `targetName` is present, as it is needed to add the target name suffix to the test name.
* The workaround sets `targetName` to `null` after the first KGP wrapper is created,
* so `createTestExecuter` returns the previously created executer:
* RetryTestExecuter(KotlinJvmTarget$Executer(DefaultTestExecuter))
*
* Issue: https://github.com/gradle/test-retry-gradle-plugin/issues/116 (KT-49155)
*/
private fun KotlinJvmTest.applyTestRetryCompatibilityWorkaround() {
if (targetName == null) return

val executeTestsActionIndex = taskActions.indexOfLast { it.displayName == "Execute executeTests" }
check(executeTestsActionIndex != -1) { "Action executeTests not found" }

// Add the workaround action and then move it to the correct position right before tests execution.
doFirst("workaround for compatibility with testRetry") {
targetName = null
}
val injectedAction = taskActions.removeAt(0)
taskActions.add(executeTestsActionIndex, injectedAction)
}

// Docs: https://docs.gradle.com/develocity/gradle-plugin/current/#test_retry
private fun Test.testRetry(configure: TestRetryConfiguration.() -> Unit) {
extensions.getByName<DevelocityTestConfiguration>("develocity").testRetry(configure)
}
2 changes: 0 additions & 2 deletions buildSrc/src/main/kotlin/KtorBuildProperties.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ val IDEA_ACTIVE: Boolean = System.getProperty("idea.active") == "true"

val OS_NAME = System.getProperty("os.name").lowercase()

val CI = System.getenv("TEAMCITY_VERSION") != null

val HOST_NAME = when {
OS_NAME.startsWith("linux") -> "linux"
OS_NAME.startsWith("windows") -> "windows"
Expand Down
9 changes: 0 additions & 9 deletions buildSrc/src/main/kotlin/TargetsConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,6 @@ fun Project.configureTargets() {
}
}
}

// Don't fail build on the CI:
// 1. To distinct builds failed because of failed tests and because of compilation errors or anything else.
// TeamCity parses test results to define build status, so the build won't be green.
// 2. To run as many tests as possible while keeping fail-fast behavior locally.
if (CI) tasks.withType<AbstractTestTask>().configureEach {
ignoreFailures = true
if (this is KotlinTest) ignoreRunFailures = true
}
}

private val hierarchyTemplate = KotlinHierarchyTemplate {
Expand Down
4 changes: 2 additions & 2 deletions gradle-settings-conventions/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ repositories {
}

dependencies {
implementation("com.gradle:develocity-gradle-plugin:3.17")
implementation("com.gradle:common-custom-user-data-gradle-plugin:2.0.2")
implementation(libs.develocity)
implementation(libs.develocity.commonCustomUserData)
}
8 changes: 8 additions & 0 deletions gradle-settings-conventions/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,12 @@
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}

rootProject.name = "gradle-conventions-settings"
6 changes: 6 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ swagger-parser = "2.1.22"
puppeteer = "21.5.0"
tomlj = "1.1.1"

develocity = "3.17.6" # Should be compatible with our server: ge.jetbrains.com
develocity-commonCustomUserData = "2.0.2"

[libraries]
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
kotlin-stdlib-jdk7 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" }
Expand Down Expand Up @@ -191,3 +194,6 @@ swagger-codegen-generators = { module = "io.swagger.codegen.v3:swagger-codegen-g
swagger-parser = { module = "io.swagger.parser.v3:swagger-parser", version.ref = "swagger-parser" }

tomlj = { module = "org.tomlj:tomlj", version.ref = "tomlj" }

develocity = { module = "com.gradle:develocity-gradle-plugin", version.ref = "develocity" }
develocity-commonCustomUserData = { module = "com.gradle:common-custom-user-data-gradle-plugin", version.ref = "develocity-commonCustomUserData" }

0 comments on commit 891b76b

Please sign in to comment.