-
Notifications
You must be signed in to change notification settings - Fork 392
/
AbstractGradleIntegrationTest.kt
224 lines (199 loc) · 8.94 KB
/
AbstractGradleIntegrationTest.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*
* 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.GradleRunner
import org.gradle.tooling.GradleConnectionException
import org.gradle.util.GradleVersion
import org.jetbrains.dokka.it.AbstractIntegrationTest
import org.jetbrains.dokka.it.systemProperty
import java.io.File
import java.net.URI
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.io.path.copyTo
import kotlin.io.path.copyToRecursively
import kotlin.io.path.invariantSeparatorsPathString
import kotlin.test.BeforeTest
import kotlin.time.Duration.Companion.seconds
abstract class AbstractGradleIntegrationTest : AbstractIntegrationTest() {
@BeforeTest
open fun beforeEachTest() {
prepareProjectFiles()
}
fun prepareProjectFiles(
templateProjectDir: Path = AbstractGradleIntegrationTest.templateProjectDir,
destination: File = projectDir,
) {
templateProjectDir.copyToRecursively(destination.toPath(), followLinks = false, overwrite = true)
templateSettingsGradleKts.copyTo(destination.resolve("template.settings.gradle.kts").toPath(), overwrite = true)
destination.updateProjectLocalMavenDir()
}
fun createGradleRunner(
buildVersions: BuildVersions,
vararg arguments: String,
jvmArgs: List<String> = listOf("-Xmx2G", "-XX:MaxMetaspaceSize=1G")
): GradleRunner {
// TODO quick hack to add `android { namespace }` on AGP 7+ (it's mandatory in 8+).
// This hack could be made prettier, or only test AGP 7+
val androidMajorVersion = buildVersions.androidGradlePluginVersion
?.substringBefore(".")
?.toIntOrNull() ?: 0
if (androidMajorVersion >= 7) {
projectDir.resolve("build.gradle.kts").appendText(
"""
|
|android {
| namespace = "org.jetbrains.dokka.it.android"
|}
|
""".trimMargin()
)
}
return GradleRunner.create()
.withProjectDir(projectDir)
.forwardOutput()
.withJetBrainsCachedGradleVersion(buildVersions.gradleVersion)
.withTestKitDir(File("build", "gradle-test-kit").absoluteFile)
.withDebug(TestEnvironment.isEnabledDebug)
.withArguments(
listOfNotNull(
"-Pdokka_it_dokka_version=${dokkaVersion}",
"-Pdokka_it_kotlin_version=${buildVersions.kotlinVersion}",
buildVersions.androidGradlePluginVersion?.let { androidVersion ->
"-Pdokka_it_android_gradle_plugin_version=$androidVersion"
},
// property flag to use K2
if (TestEnvironment.shouldUseK2())
"-P${TestEnvironment.TRY_K2}=true"
else
null,
// 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)
}
fun GradleRunner.buildRelaxed(): BuildResult {
return try {
build()
} catch (e: Throwable) {
val gradleConnectionException = e.withAllCauses().find { it is GradleConnectionException }
if (gradleConnectionException != null) {
gradleConnectionException.printStackTrace()
throw IllegalStateException("Assumed Gradle connection", gradleConnectionException)
}
throw e
}
}
companion object {
private val dokkaVersionOverride: String? = System.getenv("DOKKA_VERSION_OVERRIDE")
private val dokkaVersion: String = dokkaVersionOverride ?: System.getenv("DOKKA_VERSION")
/**
* Location of the template project that will be copied into [AbstractIntegrationTest.projectDir].
*
* The contents of this directory _must not_ be modified.
*
* The value is provided by the Gradle Test task.
*/
val templateProjectDir: Path by systemProperty(Paths::get)
/**
* Location of the `template.settings.gradle.kts` file used to provide common Gradle Settings configuration for template projects.
*
* This value is provided by the Gradle Test task.
*/
val templateSettingsGradleKts: Path by systemProperty(Paths::get)
/** file-based Maven repositories with Dokka dependencies */
private val devMavenRepositories: List<Path> by systemProperty { repos ->
repos.split(",").map { Paths.get(it) }
}
private val mavenRepositories: String by lazy {
val reposSpecs = if (dokkaVersionOverride != null) {
// if `DOKKA_VERSION_OVERRIDE` environment variable is provided,
// we allow running tests on a custom Dokka version from specific repositories
when {
// release version like `2.0.0`
!dokkaVersion.contains("-") -> "mavenCentral()"
// locally published version for testing some bug like `2.0.0-local-reproducing-bug`
dokkaVersion.contains("-local-") -> "mavenLocal()"
// dev version like `2.0.0-dev-329`
dokkaVersion.contains("-dev-") -> "maven(\"https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev\")"
// test version like `2.0.0-test-49477c44dfc58e2702f4c145ff41190b39d117fb`
dokkaVersion.contains("-test-") -> "maven(\"https://maven.pkg.jetbrains.space/kotlin/p/dokka/test\")"
else -> error(
"""
Provided Dokka version override is not supported: $dokkaVersion.
Supported versions are:
- release versions like '2.0.0'
- dev versions like `2.0.0-dev-329`
- test versions like `2.0.0-test-49477c44dfc58e2702f4c145ff41190b39d117fb`
- locally published (to mavenLocal) versions with `-local-` suffix like `2.0.0-local-reproducing-bug`
""".trimIndent()
)
}.also { repository ->
println("Dokka version overridden with ${dokkaVersionOverride}. Using $repository for resolving Dokka")
}
} else {
// otherwise - use locally published versions via `devMavenPublish`
devMavenRepositories.withIndex().joinToString(",\n") { (i, repoPath) ->
// Exclusive repository containing local Dokka artifacts.
// Must be compatible with both Groovy and Kotlin DSL.
/* language=kts */
"""
|maven {
| setUrl("${repoPath.invariantSeparatorsPathString}")
| name = "DokkaDevMavenRepo${i}"
|}
""".trimMargin()
}
}
/* language=kts */
"""
|exclusiveContent {
| forRepositories(
| $reposSpecs
| )
| filter {
| includeGroup("org.jetbrains.dokka")
| }
|}
|
""".trimMargin()
}
fun File.updateProjectLocalMavenDir() {
val dokkaMavenRepoMarker = "/* %{PROJECT_LOCAL_MAVEN_DIR}% */"
// Exclusive repository containing local Dokka artifacts.
// Must be compatible with both Groovy and Kotlin DSL.
walk().filter { it.isFile }.forEach { file ->
val fileText = file.readText()
if (dokkaMavenRepoMarker in fileText) {
file.writeText(
fileText.replace(dokkaMavenRepoMarker, mavenRepositories)
)
}
}
}
}
}
private fun GradleRunner.withJetBrainsCachedGradleVersion(version: GradleVersion): GradleRunner {
return withGradleDistribution(
URI.create(
"https://cache-redirector.jetbrains.com/" +
"services.gradle.org/distributions/" +
"gradle-${version.version}-bin.zip"
)
)
}
private fun Throwable.withAllCauses(): Sequence<Throwable> {
val root = this
return sequence {
yield(root)
val cause = root.cause
if (cause != null && cause != root) {
yieldAll(cause.withAllCauses())
}
}
}