"Fossies" - the Fresh Open Source Software Archive 
Member "gradle-8.1.1/build-logic/binary-compatibility/src/test/kotlin/gradlebuild/binarycompatibility/AbstractBinaryCompatibilityTest.kt" (20 Apr 2023, 13217 Bytes) of package /linux/misc/gradle-8.1.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Kotlin source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
See also the last
Fossies "Diffs" side-by-side code changes report for "AbstractBinaryCompatibilityTest.kt":
7.6.0_vs_8.0.0.
1 /*
2 * Copyright 2020 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package gradlebuild.binarycompatibility
18
19 import org.gradle.kotlin.dsl.*
20 import org.gradle.testkit.runner.BuildResult
21 import org.gradle.testkit.runner.GradleRunner
22 import org.gradle.testkit.runner.UnexpectedBuildFailure
23 import org.hamcrest.CoreMatchers
24 import org.hamcrest.MatcherAssert.assertThat
25 import org.junit.Assert.assertFalse
26 import org.junit.Assert.assertTrue
27 import org.junit.Rule
28 import org.junit.rules.TemporaryFolder
29 import java.io.File
30 import java.nio.file.Files
31
32
33 abstract class AbstractBinaryCompatibilityTest {
34
35 @get:Rule
36 val tmpDir = TemporaryFolder()
37
38 private
39 val rootDir: File
40 get() = tmpDir.root
41
42 internal
43 fun checkBinaryCompatibleKotlin(v1: String = "", v2: String, block: CheckResult.() -> Unit = {}): CheckResult =
44 runKotlinBinaryCompatibilityCheck(v1, v2) {
45 assertBinaryCompatible()
46 block()
47 }
48
49 internal
50 fun checkNotBinaryCompatibleKotlin(v1: String = "", v2: String, block: CheckResult.() -> Unit = {}): CheckResult =
51 runKotlinBinaryCompatibilityCheck(v1, v2) {
52 assertNotBinaryCompatible()
53 block()
54 }
55
56 internal
57 fun checkBinaryCompatibleJava(v1: String = "", v2: String, block: CheckResult.() -> Unit = {}): CheckResult =
58 runJavaBinaryCompatibilityCheck(v1, v2) {
59 assertBinaryCompatible()
60 block()
61 }
62
63 internal
64 fun checkNotBinaryCompatibleJava(v1: String = "", v2: String, block: CheckResult.() -> Unit = {}): CheckResult =
65 runJavaBinaryCompatibilityCheck(v1, v2) {
66 assertNotBinaryCompatible()
67 block()
68 }
69
70 internal
71 fun checkBinaryCompatible(v1: File.() -> Unit = {}, v2: File.() -> Unit = {}, block: CheckResult.() -> Unit = {}): CheckResult =
72 runBinaryCompatibilityCheck(v1, v2) {
73 assertBinaryCompatible()
74 block()
75 }
76
77 internal
78 fun checkNotBinaryCompatible(v1: File.() -> Unit = {}, v2: File.() -> Unit = {}, block: CheckResult.() -> Unit = {}): CheckResult =
79 runBinaryCompatibilityCheck(v1, v2) {
80 assertNotBinaryCompatible()
81 block()
82 }
83
84 private
85 fun CheckResult.assertBinaryCompatible() {
86 assertTrue(richReport.toAssertionMessage("Expected to be compatible but the check failed"), isBinaryCompatible)
87 }
88
89 private
90 fun CheckResult.assertNotBinaryCompatible() {
91 assertFalse(richReport.toAssertionMessage("Expected to be breaking but the check passed"), isBinaryCompatible)
92 }
93
94 private
95 fun RichReport.toAssertionMessage(message: String) =
96 if (isEmpty) "$message with an empty report"
97 else "$message\n${toText().prependIndent(" ")}"
98
99 private
100 fun runKotlinBinaryCompatibilityCheck(v1: String, v2: String, block: CheckResult.() -> Unit = {}): CheckResult =
101 runBinaryCompatibilityCheck(
102 v1 = {
103 withFile(
104 "kotlin/com/example/Source.kt",
105 """
106 package com.example
107
108 import org.gradle.api.Incubating
109 import javax.annotation.Nullable
110
111 $v1
112 """
113 )
114 },
115 v2 = {
116 withFile(
117 "kotlin/com/example/Source.kt",
118 """
119 package com.example
120
121 import org.gradle.api.Incubating
122 import javax.annotation.Nullable
123
124 $v2
125 """
126 )
127 },
128 block = block
129 )
130
131 private
132 fun runJavaBinaryCompatibilityCheck(v1: String, v2: String, block: CheckResult.() -> Unit = {}): CheckResult =
133 runBinaryCompatibilityCheck(
134 v1 = {
135 withFile(
136 "java/com/example/Source.java",
137 """
138 package com.example;
139
140 import org.gradle.api.Incubating;
141 import javax.annotation.Nullable;
142
143 $v1
144 """
145 )
146 },
147 v2 = {
148 withFile(
149 "java/com/example/Source.java",
150 """
151 package com.example;
152
153 import org.gradle.api.Incubating;
154 import javax.annotation.Nullable;
155
156 $v2
157 """
158 )
159 },
160 block = block
161 )
162
163 /**
164 * Runs the binary compatibility check against two source trees.
165 *
166 * The fixture build supports both Java and Kotlin sources.
167 *
168 * @param v1 sources producer for V1, receiver is the `src/main` directory
169 * @param v2 sources producer for V2, receiver is the `src/main` directory
170 * @param block convenience block invoked on the result
171 * @return the check result
172 */
173 private
174 fun runBinaryCompatibilityCheck(v1: File.() -> Unit, v2: File.() -> Unit, block: CheckResult.() -> Unit = {}): CheckResult {
175 rootDir.withFile("version.txt", "1.0")
176
177 val inputBuildDir = rootDir.withUniqueDirectory("input-build").apply {
178
179 withSettings("""include("v1", "v2", "binary-compatibility")""")
180 withBuildScript(
181 """
182 import gradlebuild.identity.extension.ModuleIdentityExtension
183
184 plugins {
185 base
186 kotlin("jvm") version "$embeddedKotlinVersion" apply false
187 }
188 subprojects {
189 apply(plugin = "gradlebuild.module-identity")
190 apply(plugin = "kotlin")
191 the<ModuleIdentityExtension>().baseName.set("api-module")
192 repositories {
193 mavenCentral()
194 }
195 dependencies {
196 "implementation"(gradleApi())
197 "implementation"(kotlin("stdlib"))
198 }
199 }
200 project(":v1") {
201 version = "1.0"
202 }
203 project(":v2") {
204 version = "2.0"
205 }
206 """
207 )
208 withDirectory("v1/src/main").v1()
209 withDirectory("v2/src/main").v2()
210 withDirectory("binary-compatibility").apply {
211 withBuildScript(
212 """
213 import japicmp.model.JApiChangeStatus
214 import gradlebuild.binarycompatibility.*
215 import gradlebuild.binarycompatibility.filters.*
216
217 tasks.register<JapicmpTask>("checkBinaryCompatibility") {
218
219 dependsOn(":v1:jar", ":v2:jar")
220
221 val v1 = rootProject.project(":v1")
222 val v1Jar = v1.tasks.named("jar")
223 val v2 = rootProject.project(":v2")
224 val v2Jar = v2.tasks.named("jar")
225
226 oldArchives.from(v1Jar)
227 oldClasspath.from(v1.configurations.named("runtimeClasspath"), v1Jar)
228
229 newArchives.from(v2Jar)
230 newClasspath.from(v2.configurations.named("runtimeClasspath"), v2Jar)
231
232 onlyModified.set(false)
233 failOnModification.set(false) // we rely on the rich report to fail
234
235 txtOutputFile.set(file("build/japi-report.txt"))
236
237 richReport {
238
239 title.set("Gradle Binary Compatibility Check")
240 destinationDir.set(file("build/japi"))
241 reportName.set("japi.html")
242
243 includedClasses.set(listOf(".*"))
244 excludedClasses.set(emptyList())
245
246 }
247
248 BinaryCompatibilityHelper.setupJApiCmpRichReportRules(
249 this,
250 AcceptedApiChanges.parse("{acceptedApiChanges:[]}"),
251 rootProject.files("v2/src/main/kotlin"),
252 "2.0",
253 file("test-api-changes.json"),
254 rootProject.layout.projectDirectory
255 )
256 }
257 """
258 )
259 }
260 }
261
262 val runner = GradleRunner.create()
263 .withProjectDir(inputBuildDir)
264 .withPluginClasspath()
265 .withArguments(":binary-compatibility:checkBinaryCompatibility", "-s")
266
267 val (buildResult, failure) = try {
268 runner.build()!! to null
269 } catch (ex: UnexpectedBuildFailure) {
270 ex.buildResult!! to ex
271 }
272
273 println(buildResult.output)
274
275 val richReportFile = inputBuildDir.resolve("binary-compatibility/build/japi/japi.html").apply {
276 assertTrue("Rich report file exists", isFile)
277 }
278
279 return CheckResult(failure, scrapeRichReport(richReportFile), buildResult).apply {
280 println(richReport.toText())
281 block()
282 }
283 }
284
285 internal
286 data class CheckResult(
287 val checkFailure: UnexpectedBuildFailure?,
288 val richReport: RichReport,
289 val buildResult: BuildResult
290 ) {
291
292 val isBinaryCompatible = checkFailure == null
293
294 fun assertEmptyReport() {
295 assertHasNoError()
296 assertHasNoWarning()
297 assertHasNoInformation()
298 }
299
300 fun assertHasNoError() {
301 assertTrue("Has no error (${richReport.errors})", richReport.errors.isEmpty())
302 }
303
304 fun assertHasNoWarning() {
305 assertTrue("Has no warning (${richReport.warnings})", richReport.warnings.isEmpty())
306 }
307
308 fun assertHasNoInformation() {
309 assertTrue("Has no information (${richReport.information})", richReport.information.isEmpty())
310 }
311
312 fun assertHasErrors(vararg errors: String) {
313 assertThat("Has errors", richReport.errors.map { it.message }, CoreMatchers.equalTo(errors.toList()))
314 }
315
316 fun assertHasWarnings(vararg warnings: String) {
317 assertThat("Has warnings", richReport.warnings.map { it.message }, CoreMatchers.equalTo(warnings.toList()))
318 }
319
320 fun assertHasInformation(vararg information: String) {
321 assertThat("Has information", richReport.information.map { it.message }, CoreMatchers.equalTo(information.toList()))
322 }
323
324 fun assertHasErrors(vararg errors: List<String>) {
325 assertHasErrors(*errors.toList().flatten().toTypedArray())
326 }
327
328 fun assertHasErrors(vararg errorWithDetail: Pair<String, List<String>>) {
329 assertThat("Has errors", richReport.errors, CoreMatchers.equalTo(errorWithDetail.map { ReportMessage(it.first, it.second) }))
330 }
331
332 fun newApi(thing: String, desc: String): String =
333 "$thing ${describe(thing, desc)}: New public API in 2.0 (@Incubating)"
334
335 fun added(thing: String, desc: String): List<String> =
336 listOf(
337 "$thing ${describe(thing, desc)}: Is not annotated with @Incubating.",
338 "$thing ${describe(thing, desc)}: Is not annotated with @since 2.0."
339 )
340
341 fun removed(thing: String, desc: String): Pair<String, List<String>> =
342 "$thing ${describe(thing, desc)}: Is not binary compatible." to listOf("$thing has been removed")
343
344 private
345 fun describe(thing: String, desc: String) =
346 if (thing == "Field") desc else "com.example.$desc"
347 }
348
349 protected
350 fun File.withFile(path: String, text: String = ""): File =
351 resolve(path).apply {
352 parentFile.mkdirs()
353 writeText(text.trimIndent())
354 }
355
356 private
357 fun File.withUniqueDirectory(prefixPath: String): File =
358 Files.createTempDirectory(
359 withDirectory(prefixPath.substringBeforeLast("/")).toPath(),
360 prefixPath.substringAfterLast("/")
361 ).toFile()
362
363 private
364 fun File.withDirectory(path: String): File =
365 resolve(path).apply {
366 mkdirs()
367 }
368
369 private
370 fun File.withSettings(text: String = ""): File =
371 withFile("settings.gradle.kts", text)
372
373 private
374 fun File.withBuildScript(text: String = ""): File =
375 withFile("build.gradle.kts", text)
376 }