Skip to content

Commit bb18d62

Browse files
authored
Tests for serializable sealed interfaces (#1754)
1 parent 6a5ebd5 commit bb18d62

File tree

4 files changed

+158
-5
lines changed

4 files changed

+158
-5
lines changed

buildSrc/build.gradle.kts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
/*
2+
* Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
15
import java.util.*
26
import java.io.FileInputStream
37

@@ -7,12 +11,21 @@ plugins {
711

812
repositories {
913
mavenCentral()
14+
mavenLocal()
1015
}
1116

12-
val kotlinVersion = FileInputStream(file("../gradle.properties")).use { propFile ->
13-
val ver = Properties().apply { load(propFile) }["kotlin.version"]
14-
require(ver is String) { "kotlin.version must be string in ../gradle.properties, got $ver instead" }
15-
ver
17+
val kotlinVersion = run {
18+
if (project.hasProperty("build_snapshot_train")) {
19+
val ver = project.properties["kotlin_snapshot_version"] as? String
20+
require(!ver.isNullOrBlank()) {"kotlin_snapshot_version must be present if build_snapshot_train is used" }
21+
return@run ver
22+
}
23+
val targetProp = if (project.hasProperty("bootstrap")) "kotlin.version.snapshot" else "kotlin.version"
24+
FileInputStream(file("../gradle.properties")).use { propFile ->
25+
val ver = Properties().apply { load(propFile) }[targetProp]
26+
require(ver is String) { "$targetProp must be string in ../gradle.properties, got $ver instead" }
27+
ver
28+
}
1629
}
1730

1831
dependencies {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
@file:Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
6+
7+
package kotlinx.serialization.features
8+
9+
import kotlinx.serialization.*
10+
import kotlinx.serialization.descriptors.*
11+
import kotlinx.serialization.encoding.Decoder
12+
import kotlinx.serialization.encoding.Encoder
13+
import kotlinx.serialization.test.*
14+
import kotlin.test.Test
15+
import kotlin.test.assertEquals
16+
17+
18+
class SealedInterfacesSerializationTest {
19+
interface A
20+
21+
sealed interface B
22+
23+
@Serializable
24+
sealed interface C
25+
26+
@Serializable(DummySerializer::class)
27+
sealed interface D
28+
29+
@Serializable(DummySerializer::class)
30+
interface E
31+
32+
@Serializable
33+
@Polymorphic
34+
sealed interface F
35+
36+
@Serializable
37+
class ImplA : A, B, C, D, E, F
38+
39+
@Serializable
40+
class ImplB : A, B, C, D, E, F
41+
42+
@Serializable
43+
class Holder(
44+
val a: A,
45+
val b: B,
46+
val c: C,
47+
val d: D,
48+
val e: E,
49+
@Polymorphic val polyC: C,
50+
val f: F
51+
)
52+
53+
class DummySerializer : KSerializer<Any> {
54+
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Dummy")
55+
56+
override fun serialize(encoder: Encoder, value: Any) {
57+
error("serialize")
58+
}
59+
60+
override fun deserialize(decoder: Decoder): Any {
61+
error("deserialize")
62+
}
63+
}
64+
65+
private fun SerialDescriptor.haveSealedSubclasses() {
66+
assertEquals(PolymorphicKind.SEALED, kind)
67+
val subclasses = getElementDescriptor(1).elementDescriptors.map { it.serialName.substringAfterLast('.') }
68+
assertEquals(listOf("ImplA", "ImplB"), subclasses)
69+
}
70+
71+
private fun SerialDescriptor.isDummy() = serialName == "Dummy"
72+
73+
private fun SerialDescriptor.isPolymorphic() = kind == PolymorphicKind.OPEN
74+
75+
operator fun SerialDescriptor.get(i: Int) = getElementDescriptor(i)
76+
77+
@Test
78+
fun testInHolder() {
79+
val desc = Holder.serializer().descriptor
80+
desc[0].isPolymorphic()
81+
desc[1].isPolymorphic()
82+
desc[2].haveSealedSubclasses()
83+
desc[3].isDummy()
84+
desc[4].isDummy()
85+
desc[5].isPolymorphic()
86+
desc[6].isPolymorphic()
87+
}
88+
89+
@Test
90+
fun testGenerated() {
91+
C.serializer().descriptor.haveSealedSubclasses()
92+
}
93+
94+
@Test
95+
fun testResolved() = noJsLegacy {
96+
serializer<C>().descriptor.haveSealedSubclasses()
97+
}
98+
99+
100+
}

core/jvmMain/src/kotlinx/serialization/internal/Platform.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
package kotlinx.serialization.internal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.serialization.features.sealed
6+
7+
import kotlinx.serialization.*
8+
import kotlinx.serialization.json.*
9+
import kotlinx.serialization.test.*
10+
import kotlin.test.*
11+
12+
class SealedInterfacesJsonSerializationTest : JsonTestBase() {
13+
@Serializable
14+
sealed interface I
15+
16+
@Serializable
17+
sealed class Response: I {
18+
@Serializable
19+
@SerialName("ResponseInt")
20+
data class ResponseInt(val i: Int): Response()
21+
22+
@Serializable
23+
@SerialName("ResponseString")
24+
data class ResponseString(val s: String): Response()
25+
}
26+
27+
@Serializable
28+
@SerialName("NoResponse")
29+
object NoResponse: I
30+
31+
@Test
32+
fun testSealedInterfaceJson() = noLegacyJs {
33+
val messages = listOf(Response.ResponseInt(10), NoResponse, Response.ResponseString("foo"))
34+
assertJsonFormAndRestored(
35+
serializer(),
36+
messages,
37+
"""[{"type":"ResponseInt","i":10},{"type":"NoResponse"},{"type":"ResponseString","s":"foo"}]"""
38+
)
39+
}
40+
}

0 commit comments

Comments
 (0)