diff options
author | Colin Cross <ccross@android.com> | 2023-08-04 01:34:39 +0000 |
---|---|---|
committer | Colin Cross <ccross@android.com> | 2023-08-04 19:01:49 +0000 |
commit | 901b58e236e3382985d30ac03b9912fd6fee4927 (patch) | |
tree | f0b6d402c7830108016238a214ef5a036e77acd8 /integration-testing | |
parent | 8fa7fa92f3dce21ab73b36b67c3c96c6314d84de (diff) | |
download | kotlinx.coroutines-901b58e236e3382985d30ac03b9912fd6fee4927.tar.gz |
Reland^3: kotlinx.coroutines to 1.7.2
This reverts commit 8fa7fa92f3dce21ab73b36b67c3c96c6314d84de.
Bug: 290933559
Test: atest SystemUITests
Change-Id: Iee27834b03229a0e10f408c34ba0a1e7d31f6c03
Diffstat (limited to 'integration-testing')
-rw-r--r-- | integration-testing/README.md | 12 | ||||
-rw-r--r-- | integration-testing/build.gradle | 64 | ||||
-rw-r--r-- | integration-testing/gradle.properties | 6 | ||||
-rw-r--r-- | integration-testing/settings.gradle | 9 | ||||
-rw-r--r-- | integration-testing/smokeTest/build.gradle | 9 | ||||
-rw-r--r-- | integration-testing/src/debugAgentTest/kotlin/PrecompiledDebugProbesTest.kt | 21 | ||||
-rw-r--r-- | integration-testing/src/jvmCoreTest/kotlin/Jdk8InCoreIntegration.kt | 21 | ||||
-rw-r--r-- | integration-testing/src/jvmCoreTest/kotlin/ListAllCoroutineThrowableSubclassesTest.kt (renamed from integration-testing/src/withGuavaTest/kotlin/ListAllCoroutineThrowableSubclassesTest.kt) | 15 | ||||
-rw-r--r-- | integration-testing/src/mavenTest/kotlin/MavenPublicationAtomicfuValidator.kt | 41 | ||||
-rw-r--r-- | integration-testing/src/mavenTest/kotlin/MavenPublicationMetaInfValidator.kt | 70 | ||||
-rw-r--r-- | integration-testing/src/mavenTest/kotlin/MavenPublicationVersionValidator.kt | 1 |
11 files changed, 230 insertions, 39 deletions
diff --git a/integration-testing/README.md b/integration-testing/README.md index 0ede9b25..0218b23c 100644 --- a/integration-testing/README.md +++ b/integration-testing/README.md @@ -3,11 +3,13 @@ This is a supplementary project that provides integration tests. The tests are the following: -* `MavenPublicationValidator` depends on the published artifacts and tests artifacts binary content and absence of atomicfu in the classpath. -* `CoreAgentTest` checks that `kotlinx-coroutines-core` can be run as a Java agent. -* `DebugAgentTest` checks that the coroutine debugger can be run as a Java agent. -* `smokeTest` builds the test project that depends on coroutines. +* `mavenTest` depends on the published artifacts and tests artifacts binary content for absence of atomicfu in the classpath. +* `jvmCoreTest` miscellaneous tests that check the behaviour of `kotlinx-coroutines-core` dependency in a smoke manner. +* `coreAgentTest` checks that `kotlinx-coroutines-core` can be run as a Java agent. +* `debugAgentTest` checks that the coroutine debugger can be run as a Java agent. +* `debugDynamicAgentTest` checks that `kotlinx-coroutines-debug` agent can self-attach dynamically to JVM as a standalone dependency. +* `smokeTest` builds the multiplatform test project that depends on coroutines. The `integration-testing` project is expected to be in a subdirectory of the main `kotlinx.coroutines` project. -To run all the available tests: `cd integration-testing` + `./gradlew check`. +To run all the available tests: `./gradlew publishToMavenLocal` + `cd integration-testing` + `./gradlew check`. diff --git a/integration-testing/build.gradle b/integration-testing/build.gradle index 985a40ed..26ee9d99 100644 --- a/integration-testing/build.gradle +++ b/integration-testing/build.gradle @@ -2,13 +2,54 @@ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType + +buildscript { + + /* + * These property group is used to build kotlinx.coroutines against Kotlin compiler snapshot. + * How does it work: + * When build_snapshot_train is set to true, kotlin_version property is overridden with kotlin_snapshot_version, + * atomicfu_version is overwritten by TeamCity environment (AFU is built with snapshot and published to mavenLocal + * as previous step or the snapshot build). + * Additionally, mavenLocal and Sonatype snapshots are added to repository list and stress tests are disabled. + * DO NOT change the name of these properties without adapting kotlinx.train build chain. + */ + def prop = rootProject.properties['build_snapshot_train'] + ext.build_snapshot_train = prop != null && prop != "" + if (build_snapshot_train) { + ext.kotlin_version = rootProject.properties['kotlin_snapshot_version'] + if (kotlin_version == null) { + throw new IllegalArgumentException("'kotlin_snapshot_version' should be defined when building with snapshot compiler") + } + } + ext.native_targets_enabled = rootProject.properties['disable_native_targets'] == null + + // Determine if any project dependency is using a snapshot version + ext.using_snapshot_version = build_snapshot_train + rootProject.properties.each { key, value -> + if (key.endsWith("_version") && value instanceof String && value.endsWith("-SNAPSHOT")) { + println("NOTE: USING SNAPSHOT VERSION: $key=$value") + ext.using_snapshot_version = true + } + } + + if (using_snapshot_version) { + repositories { + mavenLocal() + maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" } + } + } + +} plugins { - id "org.jetbrains.kotlin.jvm" + id "org.jetbrains.kotlin.jvm" version "$kotlin_version" } repositories { + if (build_snapshot_train) { + maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" } + } mavenLocal() mavenCentral() } @@ -20,11 +61,13 @@ java { dependencies { testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + testImplementation "org.ow2.asm:asm:$asm_version" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" } sourceSets { - // Test that relies on Guava to reflectively check all Throwable subclasses in coroutines - withGuavaTest { + // An assortment of tests for behavior of the core coroutines module on JVM + jvmCoreTest { kotlin compileClasspath += sourceSets.test.runtimeClasspath runtimeClasspath += sourceSets.test.runtimeClasspath @@ -57,7 +100,7 @@ sourceSets { } } - // Checks that kotlinx-coroutines-debug agent can self-attach dynamically to JVM as standalone dependency + // Checks that kotlinx-coroutines-debug agent can self-attach dynamically to JVM as a standalone dependency debugDynamicAgentTest { kotlin compileClasspath += sourceSets.test.runtimeClasspath @@ -87,9 +130,9 @@ compileDebugAgentTestKotlin { } } -task withGuavaTest(type: Test) { +task jvmCoreTest(type: Test) { environment "version", coroutines_version - def sourceSet = sourceSets.withGuavaTest + def sourceSet = sourceSets.jvmCoreTest testClassesDirs = sourceSet.output.classesDirs classpath = sourceSet.runtimeClasspath } @@ -129,5 +172,10 @@ compileTestKotlin { } check { - dependsOn([withGuavaTest, debugDynamicAgentTest, mavenTest, debugAgentTest, coreAgentTest, 'smokeTest:build']) + dependsOn([jvmCoreTest, debugDynamicAgentTest, mavenTest, debugAgentTest, coreAgentTest, 'smokeTest:build']) +} +compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + } } diff --git a/integration-testing/gradle.properties b/integration-testing/gradle.properties index 1038d817..30b2b5ed 100644 --- a/integration-testing/gradle.properties +++ b/integration-testing/gradle.properties @@ -1,4 +1,6 @@ -kotlin_version=1.6.21 -coroutines_version=1.6.4-SNAPSHOT +kotlin_version=1.8.20 +coroutines_version=1.7.2-SNAPSHOT +asm_version=9.3 kotlin.code.style=official +kotlin.mpp.stability.nowarn=true diff --git a/integration-testing/settings.gradle b/integration-testing/settings.gradle index 67336c98..8584c05a 100644 --- a/integration-testing/settings.gradle +++ b/integration-testing/settings.gradle @@ -1,15 +1,8 @@ pluginManagement { - resolutionStrategy { - eachPlugin { - if (requested.id.id == "org.jetbrains.kotlin.multiplatform" || requested.id.id == "org.jetbrains.kotlin.jvm") { - useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") - } - } - } - repositories { mavenCentral() maven { url "https://plugins.gradle.org/m2/" } + maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" } mavenLocal() } } diff --git a/integration-testing/smokeTest/build.gradle b/integration-testing/smokeTest/build.gradle index b200bb2f..26cd02b6 100644 --- a/integration-testing/smokeTest/build.gradle +++ b/integration-testing/smokeTest/build.gradle @@ -6,6 +6,7 @@ repositories { // Coroutines from the outer project are published by previous CI buils step mavenLocal() mavenCentral() + maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" } } kotlin { @@ -40,4 +41,12 @@ kotlin { } } } + targets { + configure([]) { + tasks.getByName(compilations.main.compileKotlinTaskName).kotlinOptions { + jvmTarget = "1.8" + } + } + } } + diff --git a/integration-testing/src/debugAgentTest/kotlin/PrecompiledDebugProbesTest.kt b/integration-testing/src/debugAgentTest/kotlin/PrecompiledDebugProbesTest.kt index 84886a18..ab207e09 100644 --- a/integration-testing/src/debugAgentTest/kotlin/PrecompiledDebugProbesTest.kt +++ b/integration-testing/src/debugAgentTest/kotlin/PrecompiledDebugProbesTest.kt @@ -16,10 +16,9 @@ class PrecompiledDebugProbesTest { @Test fun testClassFileContent() { val clz = Class.forName("kotlin.coroutines.jvm.internal.DebugProbesKt") - val className: String = clz.getName() - val classFileResourcePath = className.replace(".", "/") + ".class" - val stream = clz.classLoader.getResourceAsStream(classFileResourcePath)!! - val array = stream.readBytes() + val classFileResourcePath = clz.name.replace(".", "/") + ".class" + val array = clz.classLoader.getResourceAsStream(classFileResourcePath).use { it.readBytes() } + assertJava8Compliance(array) // we expect the integration testing project to be in a subdirectory of the main kotlinx.coroutines project val base = File("").absoluteFile.parentFile val probes = File(base, "kotlinx-coroutines-core/jvm/resources/DebugProbesKt.bin") @@ -31,8 +30,20 @@ class PrecompiledDebugProbesTest { assertTrue( array.contentEquals(binContent), "Compiled DebugProbesKt.class does not match the file shipped as a resource in kotlinx-coroutines-core. " + - "Typically it happens because of the Kotlin version update (-> binary metadata). In that case, run the same test with -Poverwrite.probes=true." + "Typically it happens because of the Kotlin version update (-> binary metadata). " + + "In that case, run the same test with -Poverwrite.probes=true." ) } } + + private fun assertJava8Compliance(classBytes: ByteArray) { + DataInputStream(classBytes.inputStream()).use { + val magic: Int = it.readInt() + if (magic != -0x35014542) throw IllegalArgumentException("Not a valid class!") + val minor: Int = it.readUnsignedShort() + val major: Int = it.readUnsignedShort() + assertEquals(52, major) + assertEquals(0, minor) + } + } } diff --git a/integration-testing/src/jvmCoreTest/kotlin/Jdk8InCoreIntegration.kt b/integration-testing/src/jvmCoreTest/kotlin/Jdk8InCoreIntegration.kt new file mode 100644 index 00000000..91eef7e2 --- /dev/null +++ b/integration-testing/src/jvmCoreTest/kotlin/Jdk8InCoreIntegration.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +package kotlinx.coroutines + +import kotlinx.coroutines.future.* +import org.junit.Test +import kotlin.test.* + +/* + * Integration test that ensures signatures from both the jdk8 and the core source sets of the kotlinx-coroutines-core subproject are used. + */ +class Jdk8InCoreIntegration { + + @Test + fun testFuture() = runBlocking<Unit> { + val future = future { yield(); 42 } + future.whenComplete { r, _ -> assertEquals(42, r) } + assertEquals(42, future.await()) + } +} diff --git a/integration-testing/src/withGuavaTest/kotlin/ListAllCoroutineThrowableSubclassesTest.kt b/integration-testing/src/jvmCoreTest/kotlin/ListAllCoroutineThrowableSubclassesTest.kt index fefcc005..7253658e 100644 --- a/integration-testing/src/withGuavaTest/kotlin/ListAllCoroutineThrowableSubclassesTest.kt +++ b/integration-testing/src/jvmCoreTest/kotlin/ListAllCoroutineThrowableSubclassesTest.kt @@ -7,6 +7,8 @@ package kotlinx.coroutines import com.google.common.reflect.* import kotlinx.coroutines.* import org.junit.Test +import java.io.Serializable +import java.lang.reflect.Modifier import kotlin.test.* class ListAllCoroutineThrowableSubclassesTest { @@ -25,19 +27,28 @@ class ListAllCoroutineThrowableSubclassesTest { "kotlinx.coroutines.JobCancellationException", "kotlinx.coroutines.internal.UndeliveredElementException", "kotlinx.coroutines.CompletionHandlerException", - "kotlinx.coroutines.DiagnosticCoroutineContextException", + "kotlinx.coroutines.internal.DiagnosticCoroutineContextException", + "kotlinx.coroutines.internal.ExceptionSuccessfullyProcessed", "kotlinx.coroutines.CoroutinesInternalError", "kotlinx.coroutines.channels.ClosedSendChannelException", "kotlinx.coroutines.channels.ClosedReceiveChannelException", "kotlinx.coroutines.flow.internal.ChildCancelledException", "kotlinx.coroutines.flow.internal.AbortFlowException", - ) + ) @Test fun testThrowableSubclassesAreSerializable() { val classes = ClassPath.from(this.javaClass.classLoader) .getTopLevelClassesRecursive("kotlinx.coroutines"); val throwables = classes.filter { Throwable::class.java.isAssignableFrom(it.load()) }.map { it.toString() } + for (throwable in throwables) { + for (field in throwable.javaClass.declaredFields) { + if (Modifier.isStatic(field.modifiers)) continue + val type = field.type + assertTrue(type.isPrimitive || Serializable::class.java.isAssignableFrom(type), + "Throwable $throwable has non-serializable field $field") + } + } assertEquals(knownThrowables.sorted(), throwables.sorted()) } } diff --git a/integration-testing/src/mavenTest/kotlin/MavenPublicationAtomicfuValidator.kt b/integration-testing/src/mavenTest/kotlin/MavenPublicationAtomicfuValidator.kt index dbb1921d..13bac015 100644 --- a/integration-testing/src/mavenTest/kotlin/MavenPublicationAtomicfuValidator.kt +++ b/integration-testing/src/mavenTest/kotlin/MavenPublicationAtomicfuValidator.kt @@ -4,12 +4,17 @@ package kotlinx.coroutines.validator -import org.junit.* -import org.junit.Assert.assertTrue +import org.junit.Test +import org.objectweb.asm.* +import org.objectweb.asm.ClassReader.* +import org.objectweb.asm.ClassWriter.* +import org.objectweb.asm.Opcodes.* import java.util.jar.* +import kotlin.test.* class MavenPublicationAtomicfuValidator { private val ATOMIC_FU_REF = "Lkotlinx/atomicfu/".toByteArray() + private val KOTLIN_METADATA_DESC = "Lkotlin/Metadata;" @Test fun testNoAtomicfuInClasspath() { @@ -34,19 +39,39 @@ class MavenPublicationAtomicfuValidator { for (e in entries()) { if (!e.name.endsWith(".class")) continue val bytes = getInputStream(e).use { it.readBytes() } - loop@for (i in 0 until bytes.size - ATOMIC_FU_REF.size) { - for (j in 0 until ATOMIC_FU_REF.size) { - if (bytes[i + j] != ATOMIC_FU_REF[j]) continue@loop - } + // The atomicfu compiler plugin does not remove atomic properties from metadata, + // so for now we check that there are no ATOMIC_FU_REF left in the class bytecode excluding metadata. + // This may be reverted after the fix in the compiler plugin transformer (for Kotlin 1.8.0). + val outBytes = bytes.eraseMetadata() + if (outBytes.checkBytes()) { foundClasses += e.name // report error at the end with all class names - break@loop } } if (foundClasses.isNotEmpty()) { error("Found references to atomicfu in jar file $name in the following class files: ${ - foundClasses.joinToString("") { "\n\t\t" + it } + foundClasses.joinToString("") { "\n\t\t" + it } }") } close() } + + private fun ByteArray.checkBytes(): Boolean { + loop@for (i in 0 until this.size - ATOMIC_FU_REF.size) { + for (j in 0 until ATOMIC_FU_REF.size) { + if (this[i + j] != ATOMIC_FU_REF[j]) continue@loop + } + return true + } + return false + } + + private fun ByteArray.eraseMetadata(): ByteArray { + val cw = ClassWriter(COMPUTE_MAXS or COMPUTE_FRAMES) + ClassReader(this).accept(object : ClassVisitor(ASM9, cw) { + override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? { + return if (descriptor == KOTLIN_METADATA_DESC) null else super.visitAnnotation(descriptor, visible) + } + }, SKIP_FRAMES) + return cw.toByteArray() + } } diff --git a/integration-testing/src/mavenTest/kotlin/MavenPublicationMetaInfValidator.kt b/integration-testing/src/mavenTest/kotlin/MavenPublicationMetaInfValidator.kt new file mode 100644 index 00000000..8ed2b823 --- /dev/null +++ b/integration-testing/src/mavenTest/kotlin/MavenPublicationMetaInfValidator.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.validator + +import org.junit.Test +import org.objectweb.asm.* +import org.objectweb.asm.ClassReader.* +import org.objectweb.asm.ClassWriter.* +import org.objectweb.asm.Opcodes.* +import java.util.jar.* +import kotlin.test.* + +class MavenPublicationMetaInfValidator { + + @Test + fun testMetaInfCoreStructure() { + val clazz = Class.forName("kotlinx.coroutines.Job") + JarFile(clazz.protectionDomain.codeSource.location.file).checkMetaInfStructure( + setOf( + "MANIFEST.MF", + "kotlinx-coroutines-core.kotlin_module", + "com.android.tools/proguard/coroutines.pro", + "com.android.tools/r8/coroutines.pro", + "proguard/coroutines.pro", + "versions/9/module-info.class", + "kotlinx_coroutines_core.version" + ) + ) + } + + @Test + fun testMetaInfAndroidStructure() { + val clazz = Class.forName("kotlinx.coroutines.android.HandlerDispatcher") + JarFile(clazz.protectionDomain.codeSource.location.file).checkMetaInfStructure( + setOf( + "MANIFEST.MF", + "kotlinx-coroutines-android.kotlin_module", + "services/kotlinx.coroutines.CoroutineExceptionHandler", + "services/kotlinx.coroutines.internal.MainDispatcherFactory", + "com.android.tools/r8-from-1.6.0/coroutines.pro", + "com.android.tools/r8-upto-3.0.0/coroutines.pro", + "com.android.tools/proguard/coroutines.pro", + "proguard/coroutines.pro", + "versions/9/module-info.class", + "kotlinx_coroutines_android.version" + ) + ) + } + + private fun JarFile.checkMetaInfStructure(expected: Set<String>) { + val actual = HashSet<String>() + for (e in entries()) { + if (e.isDirectory() || !e.realName.contains("META-INF")) { + continue + } + val partialName = e.realName.substringAfter("META-INF/") + actual.add(partialName) + } + + if (actual != expected) { + val intersection = actual.intersect(expected) + val mismatch = actual.subtract(intersection) + expected.subtract(intersection) + fail("Mismatched files: " + mismatch) + } + + close() + } +} diff --git a/integration-testing/src/mavenTest/kotlin/MavenPublicationVersionValidator.kt b/integration-testing/src/mavenTest/kotlin/MavenPublicationVersionValidator.kt index da87d4cc..11529d2d 100644 --- a/integration-testing/src/mavenTest/kotlin/MavenPublicationVersionValidator.kt +++ b/integration-testing/src/mavenTest/kotlin/MavenPublicationVersionValidator.kt @@ -4,7 +4,6 @@ package kotlinx.coroutines.validator -import org.junit.* import org.junit.Test import java.util.jar.* import kotlin.test.* |