Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
382 changes: 140 additions & 242 deletions gradle/verification-metadata.xml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion gradle/versions.properties
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ versions.kotlinx-metadata-klib=0.0.1-dev-10
versions.native-platform=0.14
versions.protobuf-relocated=2.6.1-2
versions.r8=2.2.64
versions.robolectric=4.0
versions.robolectric=4.16
versions.nodejs=25.0.0
versions.nodejs.lts=24.10.0
versions.nodejs.for.building.wasm.debug.browsers=22.0.0
Expand Down
49 changes: 47 additions & 2 deletions plugins/parcelize/parcelize-compiler/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import org.jetbrains.kotlin.build.androidsdkprovisioner.ProvisioningType
import java.util.zip.ZipFile

description = "Parcelize compiler plugin"

Expand All @@ -9,14 +10,53 @@ plugins {
id("project-tests-convention")
}

val robolectricClasspath by configurations.creating
repositories {
google()
}

@CacheableTransform
/**
* Used to unpack the `classes.jar` from `.aar` artifacts.
*
* See `androidx.test:monitor` package below.
*/
abstract class AarToJarTransform : TransformAction<TransformParameters.None> {
@get:InputArtifact
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val inputAar: Provider<FileSystemLocation>

override fun transform(outputs: TransformOutputs) {
val aarFile = inputAar.get().asFile
ZipFile(aarFile).use { zip ->
val classesJarEntry = zip.getEntry("classes.jar")
if (classesJarEntry != null) {
val outputJar = outputs.file("${aarFile.nameWithoutExtension}-classes.jar")
zip.getInputStream(classesJarEntry).use { inputStream ->
outputJar.outputStream().use { outputStream ->
inputStream.copyTo(outputStream)
}
}
}
}
}
}

val robolectricClasspath by configurations.creating {
attributes {
attribute(Attribute.of("artifactType", String::class.java), "jar")
}
}
val robolectricDependency by configurations.creating

val parcelizeRuntimeForTests by configurations.creating
val layoutLib by configurations.creating
val layoutLibApi by configurations.creating

dependencies {
registerTransform(AarToJarTransform::class.java) {
from.attribute(Attribute.of("artifactType", String::class.java), "aar")
to.attribute(Attribute.of("artifactType", String::class.java), "jar")
}
embedded(project(":plugins:parcelize:parcelize-compiler:parcelize.common")) { isTransitive = false }
embedded(project(":plugins:parcelize:parcelize-compiler:parcelize.k1")) { isTransitive = false }
embedded(project(":plugins:parcelize:parcelize-compiler:parcelize.k2")) { isTransitive = false }
Expand Down Expand Up @@ -44,9 +84,14 @@ dependencies {
testRuntimeOnly(toolsJar())
testFixturesApi(libs.junit4)

robolectricDependency("org.robolectric:android-all:5.0.2_r3-robolectric-r0")
// Must be kept in sync with ANDROID_API_VERSION in ParcelizeRuntimeClasspathProvider.
// The dependency version defined here determines the Android API version.
robolectricDependency("org.robolectric:android-all-instrumented:16-robolectric-13921718-i7")

robolectricClasspath(commonDependency("org.robolectric", "robolectric"))

// This dependency is an `.aar` file.
robolectricClasspath("androidx.test:monitor:1.8.0")
robolectricClasspath(project(":plugins:parcelize:parcelize-runtime")) { isTransitive = false }

parcelizeRuntimeForTests(project(":plugins:parcelize:parcelize-runtime")) { isTransitive = false }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB

@file:JvmName("TestKt")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// WITH_STDLIB
// IGNORE K1

@file:JvmName("TestKt")
package test
Expand Down
14 changes: 4 additions & 10 deletions plugins/parcelize/parcelize-compiler/testData/box/kt19747.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,12 @@ class J(val j: @RawValue JHelp) : Parcelable
fun box() = parcelTest { parcel ->
val test = J(JHelp("A"))

var exceptionCaught = false
try {
test.writeToParcel(parcel, 0)
} catch (e: RuntimeException) {
if (e.message!!.contains("Parcel: unable to marshal value test.JHelp")) {
exceptionCaught = true
} else {
throw e
} catch (e: IllegalArgumentException) {
val isExpectedErrorMessage = e.message!!.contains("Parcel: unknown type for value test.JHelp")
if (!isExpectedErrorMessage) {
error("Unexpected error: ${e.toString()}")
}
}

if (!exceptionCaught) {
error("Exception should be thrown")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,12 @@ class J(val j: @RawValue JHelp) : Parcelable
fun box() = parcelTest { parcel ->
val test = J(JHelp("A"))

var exceptionCaught = false
try {
test.writeToParcel(parcel, 0)
} catch (e: RuntimeException) {
if (e.message!!.contains("Parcel: unable to marshal value test.JHelp")) {
exceptionCaught = true
} else {
throw e
val isExpectedErrorMessage = e.message!!.contains("Parcel: unknown type for value test.JHelp")
if (!isExpectedErrorMessage) {
error("Unexpected error: ${e.toString()}")
}
}

if (!exceptionCaught) {
error("Exception should be thrown")
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// WITH_STDLIB
// FULL_JDK
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is my understanding correct that now we need FULL_JDK for all tests since robolectric requires JDK 21? Having these redundant directives caused the issue? Well, actually my question is the other way around: how do we know these are only tests that have redundant directives?

Copy link
Collaborator Author

@jaschdoc jaschdoc Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FULL_JDK directive is directly not redundant, but results in an error now. However, you do raise a good point that maybe there are other redundant directives in the Parcelize tests. I will investigate a bit further and see if I can remove any directives.

A bit more context:
Both the TestJdkKind and JvmEnvironmentConfigurationDirectives (I will abbreviate this as JvmEnvConfigDir) classes define a FULL_JDK directive. It seems that JvmEnvConfigDir.FULL_JDK is not used for much other than:

  1. a (negative) toggle in ClassicFrontendFacade and
  2. to convert it to TestJdkKind.FULL_JDK or throw an error if both directives are set in JvmEnvironmentConfigurator.

One conclusion from these observations is that JvmEnvConfigDir.FULL_JDK is almost redundant.

Additionally, according to the documentation, TestJdkKind.FULL_JDK is supposed to use the current JVM to run tests with, but in JvmBoxRunner, it is (mis-) used for looking up a JDK 8 in order to spawn a new JDK 8 process in which to run the tests.

I hope that provides a bit more insight. From this perspective, it seems like removing JvmEnvConfigDir.FULL_JDK is the best thing to do, but I can't at this point tell what other tests might break from doing so.


@file:JvmName("TestKt")
package test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// WITH_STDLIB
// FULL_JDK

@file:JvmName("TestKt")
package test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND_K2: ANY
// LANGUAGE: +MultiPlatformProjects
// WITH_STDLIB
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND_K1: ANY
// LANGUAGE: +MultiPlatformProjects
// WITH_STDLIB
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND_K2: ANY
// LANGUAGE: +MultiPlatformProjects
// WITH_STDLIB
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND_K1: ANY
// LANGUAGE: +MultiPlatformProjects
// WITH_STDLIB
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ package org.jetbrains.kotlin.parcelize.test.runners
import org.jetbrains.kotlin.parcelize.test.services.ParcelizeDirectives.ENABLE_PARCELIZE
import org.jetbrains.kotlin.test.runners.codegen.AbstractFirPsiAsmLikeInstructionListingTest
import org.jetbrains.kotlin.parcelize.test.services.ParcelizeEnvironmentConfigurator
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.directives.AsmLikeInstructionListingDirectives.FIR_DIFFERENCE
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.JDK_KIND


open class AbstractFirParcelizeBytecodeListingTest : AbstractFirPsiAsmLikeInstructionListingTest() {
Expand All @@ -23,6 +25,8 @@ private fun TestConfigurationBuilder.configureParcelizeSpecific() {
defaultDirectives {
+FIR_DIFFERENCE
+ENABLE_PARCELIZE
// Robolectric 4.16 (onward) with Android SDK 36 requires JDK 21
JDK_KIND with TestJdkKind.FULL_JDK_21
}
useConfigurators(::ParcelizeEnvironmentConfigurator)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ package org.jetbrains.kotlin.parcelize.test.runners
import org.jetbrains.kotlin.parcelize.test.services.ParcelizeDirectives.ENABLE_PARCELIZE
import org.jetbrains.kotlin.parcelize.test.services.ParcelizeEnvironmentConfigurator
import org.jetbrains.kotlin.test.FirParser
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.ENABLE_PLUGIN_PHASES
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.FIR_PARSER
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.JDK_KIND
import org.jetbrains.kotlin.test.frontend.fir.FirFailingTestSuppressor
import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerTest
import org.jetbrains.kotlin.test.configuration.baseFirDiagnosticTestConfiguration
Expand All @@ -26,6 +28,8 @@ abstract class AbstractFirParcelizeDiagnosticTestBase(val parser: FirParser) : A
+ENABLE_PARCELIZE
+ENABLE_PLUGIN_PHASES
FIR_PARSER with parser
// Robolectric 4.16 (onward) with Android SDK 36 requires JDK 21
JDK_KIND with TestJdkKind.FULL_JDK_21
}

useConfigurators(::ParcelizeEnvironmentConfigurator)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.parcelize.test.services.ParcelizeUtilSourcesProvider
import org.jetbrains.kotlin.test.Constructor
import org.jetbrains.kotlin.test.FirParser
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.backend.BlackBoxCodegenSuppressor
import org.jetbrains.kotlin.test.backend.handlers.IrTextDumpHandler
import org.jetbrains.kotlin.test.backend.ir.BackendCliJvmFacade
Expand All @@ -26,6 +27,7 @@ import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.REQUIRES_SEPAR
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives.REPORT_ONLY_EXPLICITLY_DEFINED_DEBUG_INFO
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.ENABLE_PLUGIN_PHASES
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.JDK_KIND
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontend2IrConverter
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendFacade
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
Expand Down Expand Up @@ -57,6 +59,8 @@ abstract class AbstractParcelizeBoxTestBase<R : ResultingArtifact.FrontendOutput
+ENABLE_PARCELIZE
+REQUIRES_SEPARATE_PROCESS
+REPORT_ONLY_EXPLICITLY_DEFINED_DEBUG_INFO
// Robolectric 4.16 (onward) with Android SDK 36 requires JDK 21
JDK_KIND with TestJdkKind.FULL_JDK_21
}

commonConfigurationForJvmTest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ package org.jetbrains.kotlin.parcelize.test.runners
import org.jetbrains.kotlin.parcelize.test.services.ParcelizeDirectives.ENABLE_PARCELIZE
import org.jetbrains.kotlin.parcelize.test.services.ParcelizeEnvironmentConfigurator
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.builders.classicFrontendHandlersStep
import org.jetbrains.kotlin.test.builders.classicFrontendStep
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives.DIAGNOSTICS
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.JDK_KIND
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.USE_PSI_CLASS_FILES_READING
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendFailingTestSuppressor
import org.jetbrains.kotlin.test.frontend.classic.handlers.ClassicDiagnosticsHandler
Expand All @@ -34,6 +36,8 @@ abstract class AbstractParcelizeDiagnosticTest : AbstractKotlinCompilerTest() {
+ENABLE_PARCELIZE
+USE_PSI_CLASS_FILES_READING
DIAGNOSTICS with "-UNUSED_PARAMETER"
// Robolectric 4.16 (onward) with Android SDK 36 requires JDK 21
JDK_KIND with TestJdkKind.FULL_JDK_21
}

enableMetaInfoHandler()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ package org.jetbrains.kotlin.parcelize.test.runners

import org.jetbrains.kotlin.parcelize.test.services.ParcelizeDirectives.ENABLE_PARCELIZE
import org.jetbrains.kotlin.parcelize.test.services.ParcelizeEnvironmentConfigurator
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.JDK_KIND
import org.jetbrains.kotlin.test.runners.codegen.AbstractIrAsmLikeInstructionListingTest

open class AbstractParcelizeIrBytecodeListingTest : AbstractIrAsmLikeInstructionListingTest() {
Expand All @@ -16,6 +18,8 @@ open class AbstractParcelizeIrBytecodeListingTest : AbstractIrAsmLikeInstruction
with(builder) {
defaultDirectives {
+ENABLE_PARCELIZE
// Robolectric 4.16 (onward) with Android SDK 36 requires JDK 21
JDK_KIND with TestJdkKind.FULL_JDK_21
}
useConfigurators(::ParcelizeEnvironmentConfigurator)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

package org.jetbrains.kotlin.parcelize.test.services

import org.hamcrest.BaseDescription
import org.jetbrains.kotlin.parcelize.test.services.ParcelizeDirectives.ENABLE_PARCELIZE
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.RuntimeClasspathProvider
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.jvm.compiledClassesManager
import org.jetbrains.kotlin.test.services.temporaryDirectoryManager
import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.org.objectweb.asm.ClassWriter
Expand All @@ -19,13 +19,19 @@ import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.junit.runner.JUnitCore
import java.io.File
import kotlin.reflect.KClass

class ParcelizeRuntimeClasspathProvider(testServices: TestServices) : RuntimeClasspathProvider(testServices) {
companion object {
val layoutlibJar: File by lazy { getLayoutLibFile("layoutLib.path") }
val layoutlibApiJar: File by lazy { getLayoutLibFile("layoutLibApi.path") }

private const val ANDROID_TOOLS_PREFIX = "studio.android.sdktools."
/**
* The android api version used by robolectric's `android-all`.
*
* See comment in gradle build file.
*/
private const val ANDROID_API_VERSION = 36

private fun getLayoutLibFile(property: String): File {
val layoutLibFile = File(System.getProperty(property))
Expand All @@ -51,7 +57,7 @@ class ParcelizeRuntimeClasspathProvider(testServices: TestServices) : RuntimeCla
}

with(visitAnnotation("Lorg/robolectric/annotation/Config;", true)) {
visit("sdk", intArrayOf(21))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, better to put a constant instead 👍🏼

visit("sdk", intArrayOf(ANDROID_API_VERSION))
visit("manifest", "--none")
visitEnd()
}
Expand Down Expand Up @@ -110,13 +116,6 @@ class ParcelizeRuntimeClasspathProvider(testServices: TestServices) : RuntimeCla
.map { File(it) }
.sortedBy { it.nameWithoutExtension }

val junitCoreResourceName = JUnitCore::class.java.name.replace('.', '/') + ".class"
val junitJar = File(
JUnitCore::class.java.classLoader.getResource(junitCoreResourceName)!!.file
.substringAfter("file:")
.substringBeforeLast('!')
)

val parcelizeRuntimeJars = System.getProperty("parcelizeRuntime.classpath")?.split(File.pathSeparator)?.map(::File)
?: error("Unable to get a valid classpath from 'parcelizeRuntime.classpath' property")

Expand All @@ -126,14 +125,30 @@ class ParcelizeRuntimeClasspathProvider(testServices: TestServices) : RuntimeCla
.resolve(JUNIT_GENERATED_TEST_CLASS_NAME)
.apply { writeBytes(JUNIT_GENERATED_TEST_CLASS_BYTES) }

val hamcrestJar = getJarFor(BaseDescription::class)
val junitJar = getJarFor(JUnitCore::class)

return buildList {
add(kotlinRuntimeJar)
add(layoutlibJar)
add(layoutlibApiJar)
addAll(robolectricJars)
add(junitJar)
add(hamcrestJar)
addAll(parcelizeRuntimeJars)
add(tempDir)
}
}

/**
* Returns jar file where `clazz` is located by using `clazz`'s classloader.
*/
private fun getJarFor(clazz: KClass<out Any>): File {
val resourceName = clazz.java.name.replace('.', '/') + ".class"
return File(
clazz.java.classLoader.getResource(resourceName)!!.file
.substringAfter("file:")
.substringBeforeLast('!')
)
}
}