/* * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.serialization import kotlinx.serialization.builtins.* import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.* import kotlinx.serialization.modules.* import kotlinx.serialization.test.* import kotlin.reflect.* import kotlin.test.* // Imagine this is a 3rd party interface interface IApiError { val code: Int } @Serializable(CustomSer::class) interface HasCustom object CustomSer: KSerializer { override val descriptor: SerialDescriptor get() = TODO("Not yet implemented") override fun serialize(encoder: Encoder, value: HasCustom) { TODO("Not yet implemented") } override fun deserialize(decoder: Decoder): HasCustom { TODO("Not yet implemented") } } @Suppress("UNCHECKED_CAST") class InterfaceContextualSerializerTest { @Serializable data class Box(val boxed: T) object MyApiErrorSerializer : KSerializer { override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("IApiError", PrimitiveKind.INT) override fun serialize(encoder: Encoder, value: IApiError) { encoder.encodeInt(value.code) } override fun deserialize(decoder: Decoder): IApiError { val code = decoder.decodeInt() return object : IApiError { override val code: Int = code } } } private inline fun SerializersModule.doTest(block: (KSerializer) -> Unit) { block(this.serializer()) block(this.serializer(typeOf()) as KSerializer) } // Native, WASM - can't retrieve serializer (no .isInterface) @Test fun testDefault() { if (isNative() || isWasm()) return assertEquals(PolymorphicKind.OPEN, serializer().descriptor.kind) assertEquals(PolymorphicKind.OPEN, serializer(typeOf()).descriptor.kind) } @Test fun testCustom() { assertSame(CustomSer, serializer()) assertSame(CustomSer, serializer(typeOf()) as KSerializer) } // JVM - intrinsics kick in @Test fun testContextual() { val module = serializersModuleOf(IApiError::class, MyApiErrorSerializer) assertSame(MyApiErrorSerializer, module.serializer(typeOf()) as KSerializer) shouldFail(beforeKotlin = "2.0.0", onJvm = true, onWasm = false, onNative = false, onJs = false ) { assertSame(MyApiErrorSerializer, module.serializer()) } } // JVM - intrinsics kick in @Test fun testInsideList() { val module = serializersModuleOf(IApiError::class, MyApiErrorSerializer) assertEquals(MyApiErrorSerializer.descriptor, module.serializer(typeOf>()).descriptor.elementDescriptors.first()) shouldFail(beforeKotlin = "2.0.0", onWasm = false, onNative = false, onJs = false ) { assertEquals( MyApiErrorSerializer.descriptor, module.serializer>().descriptor.elementDescriptors.first() ) } } @Test fun testInsideBox() { val module = serializersModuleOf(IApiError::class, MyApiErrorSerializer) assertEquals(MyApiErrorSerializer.descriptor, module.serializer(typeOf>()).descriptor.elementDescriptors.first()) shouldFail(beforeKotlin = "2.0.0", onWasm = false, onNative = false, onJs = false ) { assertEquals( MyApiErrorSerializer.descriptor, module.serializer>().descriptor.elementDescriptors.first() ) } } @Test fun testWithNullability() { val module = serializersModuleOf(IApiError::class, MyApiErrorSerializer) assertEquals(MyApiErrorSerializer.nullable.descriptor, module.serializer(typeOf()).descriptor) shouldFail(beforeKotlin = "2.0.0", onWasm = false, onNative = false, onJs = false ) { assertEquals(MyApiErrorSerializer.nullable.descriptor, module.serializer().descriptor) } } @Test fun testWithNullabilityInsideList() { val module = serializersModuleOf(IApiError::class, MyApiErrorSerializer) assertEquals(MyApiErrorSerializer.nullable.descriptor, module.serializer(typeOf>()).descriptor.elementDescriptors.first()) shouldFail(beforeKotlin = "2.0.0", onWasm = false, onNative = false, onJs = false ) { assertEquals( MyApiErrorSerializer.nullable.descriptor, module.serializer>().descriptor.elementDescriptors.first() ) } } class Unrelated object UnrelatedSerializer: KSerializer { override val descriptor: SerialDescriptor get() = TODO("Not yet implemented") override fun serialize(encoder: Encoder, value: Unrelated) { TODO("Not yet implemented") } override fun deserialize(decoder: Decoder): Unrelated { TODO("Not yet implemented") } } @Test fun interfaceSerializersAreCachedInsideIfModuleIsNotFilledWithInterface() = jvmOnly { // Caches are implemented on JVM val module = serializersModuleOf(Unrelated::class, UnrelatedSerializer) val p1 = module.serializer(typeOf>()) assertEquals(PolymorphicKind.OPEN, p1.descriptor.elementDescriptors.first().kind) val p2 = module.serializer(typeOf>()) assertSame(p1, p2) } @Test fun interfaceSerializersAreCachedTopLevelIfModuleIsNotFilledWithInterface() = jvmOnly { val module = serializersModuleOf(Unrelated::class, UnrelatedSerializer) val p1 = module.serializer(typeOf()) assertEquals(PolymorphicKind.OPEN, p1.descriptor.kind) val p2 = module.serializer(typeOf()) assertSame(p1, p2) } interface Parametrized { val param: List } class PSer(val tSer: KSerializer): KSerializer> { override val descriptor: SerialDescriptor get() = buildClassSerialDescriptor("PSer<${tSer.descriptor.serialName}>") override fun serialize(encoder: Encoder, value: Parametrized) { TODO("Not yet implemented") } override fun deserialize(decoder: Decoder): Parametrized { TODO("Not yet implemented") } } @Test fun testParametrizedInterface() { if (!isNative() && !isWasm()) { assertEquals(PolymorphicKind.OPEN, serializer(typeOf>()).descriptor.kind) } val md = SerializersModule { contextual(Parametrized::class) { PSer(it[0]) } } assertEquals("PSer", md.serializer(typeOf>()).descriptor.serialName) shouldFail(beforeKotlin = "2.0.0", onWasm = false, onNative = false, onJs = false ) { assertEquals("PSer", md.serializer>().descriptor.serialName) } } @Serializable sealed interface SealedI @Test fun sealedInterfacesAreNotAffected() { val module = serializersModuleOf(IApiError::class, MyApiErrorSerializer) module.doTest { assertEquals(PolymorphicKind.SEALED, it.descriptor.kind) } module.doTest> { assertEquals(PolymorphicKind.SEALED, it.descriptor.elementDescriptors.first().kind) } module.doTest> { assertEquals(PolymorphicKind.SEALED, it.descriptor.elementDescriptors.first().kind) } } object SealedSer: KSerializer { override val descriptor: SerialDescriptor get() = PrimitiveSerialDescriptor("SealedSer", PrimitiveKind.INT) override fun serialize(encoder: Encoder, value: SealedI) { TODO("Not yet implemented") } override fun deserialize(decoder: Decoder): SealedI { TODO("Not yet implemented") } } @Test fun sealedInterfacesAreNotOverriden() { val module = serializersModuleOf(SealedI::class, SealedSer) module.doTest { assertEquals(PolymorphicKind.SEALED, it.descriptor.kind) } module.doTest> { assertEquals(PolymorphicKind.SEALED, it.descriptor.elementDescriptors.first().kind) } module.doTest> { assertEquals(PolymorphicKind.SEALED, it.descriptor.elementDescriptors.first().kind) } } }