diff options
Diffstat (limited to 'formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt')
-rw-r--r-- | formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt b/formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt new file mode 100644 index 00000000..74196b7b --- /dev/null +++ b/formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt @@ -0,0 +1,380 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.builtins.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.internal.* +import kotlinx.serialization.* +import kotlinx.serialization.modules.* +import kotlin.test.* + +@Suppress("UnsafeCastFromDynamic") +class EncodeToDynamicTest { + @Serializable + data class Data(val a: Int) + + @Serializable + open class DataWrapper(open val s: String, val d: Data? = Data(1)) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class.js != other::class.js) return false + + other as DataWrapper + + if (s != other.s) return false + if (d != other.d) return false + + return true + } + + override fun hashCode(): Int { + var result = s.hashCode() + result = 31 * result + (d?.hashCode() ?: 0) + return result + } + } + + @Serializable + data class NestedList(val a: String, val list: List<Int>) + + @Serializable + data class ListOfLists(val l: List<List<Data>>) + + @Serializable + data class MapWrapper(val m: Map<String?, Int>) + + @Serializable + data class ComplexMapWrapper(val m: Map<String, Data>) + + @Serializable + data class WithChar(val a: Char) + + @Serializable + data class WithLong(val l: Long) + + @Serializable + data class AllTypes( + val b: Byte, + val s: Short, + val i: Int, + val f: Float, + val d: Double, + val c: Char, + val B: Boolean, + val S: String + ) + + @Serializable + data class EnumWrapper(val e: Color) + + @Serializable + sealed class Sealed { + @Serializable + data class One(val string: String) : Sealed() + } + + @Serializable + class WithJsName(@JsName("b") val a: String) + + @Serializable + data class WithSerialName(@SerialName("b") val a: String) + + @Serializable + enum class Color { + RED, + GREEN, + + @SerialName("red") + WITH_SERIALNAME_red + } + + @Serializable(MyFancyClass.Companion::class) + data class MyFancyClass(val value: String) { + + companion object : KSerializer<MyFancyClass> { + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("MyFancyClass", PrimitiveKind.STRING) + override fun serialize(encoder: Encoder, value: MyFancyClass) { + encoder.encodeString("fancy ${value.value}") + } + + override fun deserialize(decoder: Decoder): MyFancyClass { + return MyFancyClass(decoder.decodeString().removePrefix("fancy ")) + } + } + } + + @Test + fun dynamicSimpleTest() { + assertDynamicForm(Data(42)) { data, serialized -> + assertEquals(data.a, serialized.a) + } + + assertDynamicForm(WithChar('c')) { data, serialized -> + assertEquals(data.a.toString(), serialized.a) + } + + assertDynamicForm(AllTypes(1, 2, 3, 4.0f, 5.0, 'c', true, "string")) + + + assertDynamicForm(WithLong(5L)) + assertDynamicForm(WithLong(MAX_SAFE_INTEGER.toLong())) + assertDynamicForm(WithLong(MAX_SAFE_INTEGER.unaryMinus().toLong())) + } + + @Test + fun wrappedObjectsTest() { + assertDynamicForm(DataWrapper("a string", Data(42))) { data, serialized -> + assertEquals(data.s, serialized.s) + assertNotNull(serialized.d) + assertEquals(data.d?.a, serialized.d.a) + } + } + + @Test + fun listTest() { + assertDynamicForm(listOf(1, 2, 3, 44), serializer = ListSerializer(Int.serializer())) { data, serialized -> + assertNotNull(serialized.length, "length property should exist") + assertEquals(data.size, serialized.length) + + for (i in data.indices) { + assertEquals(data[i], serialized[i]) + } + } + } + + @Test + fun arrayTest() { + assertDynamicForm(intArrayOf(1, 2, 3, 44), serializer = IntArraySerializer(), true) { data, serialized -> + assertNotNull(serialized.length, "length property should exist") + assertEquals(data.size, serialized.length) + + for (i in data.indices) { + assertEquals(data[i], serialized[i]) + } + } + } + + @Test + fun nestedListTest() { + assertDynamicForm(NestedList("a string", listOf(1, 2, 3, 44))) { data, serialized -> + assertEquals(data.a, serialized.a) + assertNotNull(serialized.list.length, "length property should exist") + assertEquals(data.list.size, serialized.list.length) + + for (i in data.list.indices) { + assertEquals(data.list[i], serialized.list[i]) + } + } + + } + + @Test + fun complexMapWrapperTest() { + assertDynamicForm(ComplexMapWrapper(mapOf("key1" to Data(1), "key2" to Data(2)))) + } + + @Test + fun mapWrapperTest() { + assertDynamicForm(MapWrapper(mapOf("key1" to 1, "key2" to 2))) + } + + @Test + fun listOfListsTest() { + assertDynamicForm( + ListOfLists( + listOf( + listOf(Data(11), Data(12), Data(13)), + listOf(Data(21), Data(22)) + ) + ) + ) { data, serialized -> + assertEquals(data.l.size, serialized.l.length) + assertEquals(data.l.first().size, serialized.l[0].length) + } + } + + @Serializable + data class NestedCollections(val data: Map<String, Map<String, List<Int>>>) + + @Test + fun nestedCollections() { + assertDynamicForm( + NestedCollections( + mapOf( + "one" to mapOf("oneone" to listOf(11, 12, 13), "onetwo" to listOf(1)), + "two" to mapOf("twotwo" to listOf(22, 23)) + ) + ) + , serializer = NestedCollections.serializer() + ) + } + + @Test + fun enums() { + assertDynamicForm(EnumWrapper(Color.RED)) + assertDynamicForm(Color.GREEN) + assertDynamicForm(Color.WITH_SERIALNAME_red) { _, serialized -> + assertEquals("red", serialized) + } + } + + @Test + fun singlePrimitiveValue() { + assertDynamicForm("some string") + assertDynamicForm(1.toByte()) + assertDynamicForm(1.toShort()) + assertDynamicForm(1) + assertDynamicForm(1.toFloat()) + assertDynamicForm(1.toDouble()) + assertDynamicForm('c') + assertDynamicForm(true) + assertDynamicForm(false) + assertDynamicForm(1L) + val result = Json.encodeToDynamic(String.serializer().nullable, null) + assertEquals(null, result) + } + + @Test + fun sealed() { + // test of sealed class but not polymorphic serialization + assertDynamicForm(Sealed.One("one")) + } + + @Test + fun withSerialNam() { + assertDynamicForm(WithSerialName("something")) { data, serialized -> + assertEquals(data.a, serialized.b) + } + } + + @Test + fun mapWithNullKey() { + val serialized = Json.encodeToDynamic( + MapSerializer(String.serializer().nullable, Int.serializer()), + mapOf(null to 0, "a" to 1) + ) + assertNotNull(serialized[null], "null key should be present in output") + } + + @Test + fun mapWithSimpleKey() { + + inline fun <reified T> assertSimpleMapForm(key: T, value: String) { + assertDynamicForm(mapOf(key to value), MapSerializer(serializer(), String.serializer())) + } + + assertSimpleMapForm(1, "Int 1") + assertSimpleMapForm("s", "String s") + assertSimpleMapForm('c', "char c") + assertSimpleMapForm(2.toByte(), "Byte 2") + assertSimpleMapForm(3.toShort(), "Short 3") + assertSimpleMapForm(4.toLong(), "Long 4") + assertSimpleMapForm(5.toFloat(), "Float 5") + assertSimpleMapForm(6.toDouble(), "Double 6") + + assertDynamicForm( + mapOf( + Color.RED to "RED", + Color.GREEN to "GREEN", + Color.WITH_SERIALNAME_red to "red" + ), + MapSerializer(Color.serializer(), String.serializer()) + ) { _, serialized -> + assertNotNull(serialized["red"], "WITH_SERIALNAME_red should be serialized as 'red'") + } + } + + @Test + fun mapWithIllegalKey() { + + val exception = assertFails { + Json.encodeToDynamic( + MapSerializer(Data.serializer(), String.serializer()), + mapOf( + Data(1) to "data", + Data(2) to "data", + Data(3) to "data" + ) + ) + } + assertEquals(IllegalArgumentException::class, exception::class) + assertTrue("should have a helpful error message") { + exception.message?.contains("can't be used in json as map key") == true + } + + assertFails { + @Suppress("CAST_NEVER_SUCCEEDS") + assertDynamicForm( + mapOf( + (null as? Data) to "Data null" + ), + MapSerializer(Data.serializer().nullable, String.serializer()) + ) + } + + + val doubleSerializer = MapSerializer(Double.serializer(), String.serializer()) + val value = mapOf(0.5 to "0.5") + var ex = assertFails { + assertDynamicForm(value, doubleSerializer) + } + assertTrue("should have a helpful error message") { + ex.message?.contains("can't be used in json as map key") == true + } + + ex = assertFails { + assertDynamicForm(mapOf(Double.NaN to "NaN"), doubleSerializer) + } + assertTrue("should have a helpful error message") { + ex.message?.contains("can't be used in json as map key") == true + } + + ex = assertFails { + assertDynamicForm(mapOf(Double.NEGATIVE_INFINITY to "NaN"), doubleSerializer) + } + assertTrue("should have a helpful error message") { + ex.message?.contains("can't be used in json as map key") == true + } + + assertDynamicForm(mapOf(11.0 to "11"), doubleSerializer) + + } + + @Test + fun customSerializerTest() { + assertDynamicForm(MyFancyClass("apple"), MyFancyClass.serializer()) { _, serialized -> + assertEquals("fancy apple", serialized) + } + + assertDynamicForm( + mapOf(MyFancyClass("apple") to "value"), + MapSerializer(MyFancyClass.serializer(), String.serializer()) + ) { _, serialized -> + assertNotNull(serialized["fancy apple"], "should contain key 'fancy apple'") + } + } +} + +public inline fun <reified T : Any> assertDynamicForm( + data: T, + serializer: KSerializer<T> = EmptySerializersModule().serializer(), + skipEqualsCheck: Boolean = false, + noinline assertions: ((T, dynamic) -> Unit)? = null +) { + val serialized = Json.encodeToDynamic(serializer, data) + assertions?.invoke(data, serialized) + val string = Json.encodeToString(serializer, data) + assertEquals( + string, + JSON.stringify(serialized), + "JSON.stringify representation must be the same" + ) + + if (skipEqualsCheck) return // arrays etc. + assertEquals(data, Json.decodeFromString(serializer, string)) +} |