summaryrefslogtreecommitdiff
path: root/formats/json-tests/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt
diff options
context:
space:
mode:
Diffstat (limited to 'formats/json-tests/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt')
-rw-r--r--formats/json-tests/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt332
1 files changed, 332 insertions, 0 deletions
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt
new file mode 100644
index 00000000..4b4aebfd
--- /dev/null
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2017-2021 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.features.sealed.SealedChild
+import kotlinx.serialization.features.sealed.SealedParent
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.reflect.*
+import kotlin.test.*
+import kotlin.time.Duration
+
+@Suppress("RemoveExplicitTypeArguments") // This is exactly what's being tested
+class SerializersLookupTest : JsonTestBase() {
+
+ @Test
+ fun testPrimitive() {
+ val token = typeOf<Int>()
+ val serial = serializer(token)
+ assertSame(Int.serializer() as KSerializer<*>, serial)
+ assertSerializedWithType("42", 42)
+ }
+
+ @Test
+ fun testPlainClass() {
+ val b = StringData("some string")
+ assertSerializedWithType("""{"data":"some string"}""", b)
+ }
+
+ @Test
+ fun testListWithT() {
+ val source = """[{"intV":42}]"""
+ val serial = serializer<List<IntData>>()
+ assertEquals(listOf(IntData(42)), Json.decodeFromString(serial, source))
+ }
+
+ @Test
+ fun testPrimitiveList() {
+ val myArr = listOf("a", "b", "c")
+ assertSerializedWithType("""["a","b","c"]""", myArr)
+ }
+
+ @Test
+ fun testListAsCollection() {
+ val myArr: Collection<String> = listOf("a", "b", "c")
+ assertSerializedWithType("""["a","b","c"]""", myArr)
+ }
+
+ @Test
+ fun testUnsigned() {
+ assertSame(UByte.serializer(), serializer<UByte>())
+ assertSame(UShort.serializer(), serializer<UShort>())
+ assertSame(UInt.serializer(), serializer<UInt>())
+ assertSame(ULong.serializer(), serializer<ULong>())
+ }
+
+ @Test
+ @OptIn(ExperimentalUnsignedTypes::class)
+ fun testUnsignedArrays() {
+ assertSame(UByteArraySerializer(), serializer<UByteArray>())
+ assertSame(UShortArraySerializer(), serializer<UShortArray>())
+ assertSame(UIntArraySerializer(), serializer<UIntArray>())
+ assertSame(ULongArraySerializer(), serializer<ULongArray>())
+ }
+
+ @Test
+ fun testPrimitiveSet() {
+ val mySet = setOf("a", "b", "c", "c")
+ assertSerializedWithType("""["a","b","c"]""", mySet)
+ }
+
+ @Test
+ fun testMapWithT() {
+ val myMap = mapOf("string" to StringData("foo"), "string2" to StringData("bar"))
+ assertSerializedWithType("""{"string":{"data":"foo"},"string2":{"data":"bar"}}""", myMap)
+ }
+
+ @Test
+ fun testNestedLists() {
+ val myList = listOf(listOf(listOf(1, 2, 3)), listOf())
+ assertSerializedWithType("[[[1,2,3]],[]]", myList)
+ }
+
+ @Test
+ fun testListSubtype() {
+ val myList = arrayListOf(1, 2, 3)
+ assertSerializedWithType<ArrayList<Int>>("[1,2,3]", myList)
+ assertSerializedWithType<List<Int>>("[1,2,3]", myList)
+ }
+
+ @Test
+ fun testListProjection() {
+ val myList = arrayListOf(1, 2, 3)
+ assertSerializedWithType<List<Int>>("[1,2,3]", myList)
+ assertSerializedWithType<MutableList<out Int>>("[1,2,3]", myList)
+ assertSerializedWithType<ArrayList<in Int>>("[1,2,3]", myList)
+ }
+
+ @Test
+ fun testStarProjectionsAreProhibited() {
+ val expectedMessage = "Star projections in type arguments are not allowed"
+ assertFailsWithMessage<IllegalArgumentException>(expectedMessage) {
+ serializer<Box<*>>()
+ }
+ assertFailsWithMessage<IllegalArgumentException>(expectedMessage) {
+ serializer(typeOf<Box<*>>())
+ }
+ assertFailsWithMessage<IllegalArgumentException>(expectedMessage) {
+ serializerOrNull(typeOf<Box<*>>())
+ }
+ }
+
+ @Test
+ fun testNullableTypes() {
+ val myList: List<Int?> = listOf(1, null, 3)
+ assertSerializedWithType("[1,null,3]", myList)
+ assertSerializedWithType<List<Int?>?>("[1,null,3]", myList)
+ }
+
+ @Test
+ fun testPair() {
+ val myPair = "42" to 42
+ assertSerializedWithType("""{"first":"42","second":42}""", myPair)
+ }
+
+ @Test
+ fun testTriple() {
+ val myTriple = Triple("1", 2, Box(42))
+ assertSerializedWithType("""{"first":"1","second":2,"third":{"boxed":42}}""", myTriple)
+ }
+
+ @Test
+ fun testLookupDuration() {
+ assertNotNull(serializerOrNull(typeOf<Duration>()))
+ assertSame(Duration.serializer(), serializer<Duration>())
+ }
+
+ @Test
+ fun testCustomGeneric() {
+ val intBox = Box(42)
+ val intBoxSerializer = serializer<Box<Int>>()
+ assertEquals(Box.serializer(Int.serializer()).descriptor, intBoxSerializer.descriptor)
+ assertSerializedWithType("""{"boxed":42}""", intBox)
+ val dataBox = Box(StringData("foo"))
+ assertSerializedWithType("""{"boxed":{"data":"foo"}}""", dataBox)
+ }
+
+ @Test
+ fun testRecursiveGeneric() {
+ val boxBox = Box(Box(Box(IntData(42))))
+ assertSerializedWithType("""{"boxed":{"boxed":{"boxed":{"intV":42}}}}""", boxBox)
+ }
+
+ @Test
+ fun testMixedGeneric() {
+ val listOfBoxes = listOf(Box("foo"), Box("bar"))
+ assertSerializedWithType("""[{"boxed":"foo"},{"boxed":"bar"}]""", listOfBoxes)
+ val boxedList = Box(listOf("foo", "bar"))
+ assertSerializedWithType("""{"boxed":["foo","bar"]}""", boxedList)
+ }
+
+ @Test
+ fun testReferenceArrays() {
+ assertSerializedWithType("[1,2,3]", Array<Int>(3) { it + 1 }, default)
+ assertSerializedWithType("""["1","2","3"]""", Array<String>(3) { (it + 1).toString() }, default)
+ assertSerializedWithType("[[0],[1],[2]]", Array<Array<Int>>(3) { cnt -> Array(1) { cnt } }, default)
+ assertSerializedWithType("""[{"boxed":"foo"}]""", Array(1) { Box("foo") }, default)
+ assertSerializedWithType("""[[{"boxed":"foo"}]]""", Array(1) { Array(1) { Box("foo") } }, default)
+ }
+
+ @Test
+ fun testPrimitiveArrays() {
+ assertSerializedWithType("[1,2,3]", intArrayOf(1, 2, 3), default)
+ assertSerializedWithType("[1,2,3]", longArrayOf(1, 2, 3), default)
+ assertSerializedWithType("[1,2,3]", byteArrayOf(1, 2, 3), default)
+ assertSerializedWithType("[1,2,3]", shortArrayOf(1, 2, 3), default)
+ assertSerializedWithType("[true,false]", booleanArrayOf(true, false), default)
+ assertSerializedWithType("""["a","b","c"]""", charArrayOf('a', 'b', 'c'), default)
+ }
+
+ @Test
+ fun testSerializableObject() {
+ assertSerializedWithType("{}", SampleObject)
+ }
+
+ class IntBox(val i: Int)
+
+ class CustomIntSerializer(isNullable: Boolean) : KSerializer<IntBox?> {
+ override val descriptor: SerialDescriptor
+
+ init {
+ val d = PrimitiveSerialDescriptor("CIS", PrimitiveKind.INT)
+ descriptor = if (isNullable) d.nullable else d
+ }
+
+ override fun serialize(encoder: Encoder, value: IntBox?) {
+ if (value == null) encoder.encodeInt(41)
+ else encoder.encodeInt(42)
+ }
+
+ override fun deserialize(decoder: Decoder): IntBox? {
+ TODO()
+ }
+ }
+
+ class GenericHolder<T>(value: T)
+
+ class GenericSerializer<T>(typeSerializer: KSerializer<T>) : KSerializer<GenericHolder<T>> {
+ override val descriptor: SerialDescriptor =
+ PrimitiveSerialDescriptor(
+ "Generic Serializer parametrized by ${typeSerializer.descriptor}",
+ PrimitiveKind.STRING
+ )
+
+ override fun deserialize(decoder: Decoder): GenericHolder<T> {
+ TODO()
+ }
+
+ override fun serialize(encoder: Encoder, value: GenericHolder<T>) {
+ TODO()
+ }
+ }
+
+ @Test
+ fun testContextualLookup() {
+ val module = SerializersModule { contextual(CustomIntSerializer(false).cast<IntBox>()) }
+ val json = Json { serializersModule = module }
+ val data = listOf(listOf(IntBox(1)))
+ assertEquals("[[42]]", json.encodeToString(data))
+ }
+
+ @Test
+ fun testGenericOfContextual() {
+ val module = SerializersModule {
+ contextual(CustomIntSerializer(false).cast<IntBox>())
+ contextual(GenericHolder::class) { args -> GenericSerializer(args[0]) }
+ }
+
+ val listSerializer = module.serializerOrNull(typeOf<List<IntBox>>())
+ assertNotNull(listSerializer)
+ assertEquals("kotlin.collections.ArrayList(PrimitiveDescriptor(CIS))", listSerializer.descriptor.toString())
+
+ val genericSerializer = module.serializerOrNull(typeOf<GenericHolder<IntBox>>())
+ assertNotNull(genericSerializer)
+ assertEquals(
+ "PrimitiveDescriptor(Generic Serializer parametrized by PrimitiveDescriptor(CIS))",
+ genericSerializer.descriptor.toString()
+ )
+ }
+
+ @Test
+ fun testContextualLookupNullable() {
+ val module = SerializersModule { contextual(CustomIntSerializer(true).cast<IntBox>()) }
+ val serializer = module.serializer<List<List<IntBox?>>>()
+ assertEquals("[[41]]", Json.encodeToString(serializer, listOf(listOf<IntBox?>(null))))
+ }
+
+ @Test
+ fun testContextualLookupNonNullable() {
+ val module = SerializersModule { contextual(CustomIntSerializer(false).cast<IntBox>()) }
+ val serializer = module.serializer<List<List<IntBox?>>>()
+ assertEquals("[[null]]", Json.encodeToString(serializer, listOf(listOf<IntBox?>(null))))
+ }
+
+ @Test
+ fun testCompiledWinsOverContextual() {
+ val contextual = object : KSerializer<Int> {
+ override val descriptor: SerialDescriptor = Int.serializer().descriptor
+
+ override fun serialize(encoder: Encoder, value: Int) {
+ fail()
+ }
+
+ override fun deserialize(decoder: Decoder): Int {
+ fail()
+ }
+ }
+ val json = Json { serializersModule = SerializersModule { contextual(contextual) } }
+ assertEquals("[[1]]", json.encodeToString(listOf(listOf<Int>(1))))
+ assertEquals("42", json.encodeToString(42))
+ }
+
+ class NonSerializable
+
+ class NonSerializableBox<T>(val boxed: T)
+
+ @Test
+ fun testSealedFromOtherFileLookup() {
+ assertNotNull(serializerOrNull(typeOf<SealedParent>()))
+ assertNotNull(serializerOrNull(typeOf<SealedChild>()))
+ }
+
+ @Test
+ fun testLookupFail() {
+ assertNull(serializerOrNull(typeOf<NonSerializable>()))
+ assertNull(serializerOrNull(typeOf<NonSerializableBox<String>>()))
+ assertNull(serializerOrNull(typeOf<Box<NonSerializable>>()))
+
+ assertFailsWithMessage<SerializationException>("for class 'NonSerializable'") {
+ serializer(typeOf<NonSerializable>())
+ }
+
+ assertFailsWithMessage<SerializationException>("for class 'NonSerializableBox'") {
+ serializer(typeOf<NonSerializableBox<String>>())
+ }
+
+ assertFailsWithMessage<SerializationException>("for class 'NonSerializable'") {
+ serializer(typeOf<Box<NonSerializable>>())
+ }
+ }
+
+ private inline fun <reified T> assertSerializedWithType(
+ expected: String,
+ value: T,
+ json: StringFormat = default
+ ) {
+ val serial = serializer<T>()
+ assertEquals(expected, json.encodeToString(serial, value))
+ val serial2 = requireNotNull(serializerOrNull(typeOf<T>())) { "Expected serializer to be found" }
+ assertEquals(expected, json.encodeToString(serial2, value))
+ }
+
+ @Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
+ inline fun <T> KSerializer<*>.cast(): KSerializer<T> = this as KSerializer<T>
+
+}