/* * 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.encoding.CompositeDecoder.Companion.UNKNOWN_NAME import kotlinx.serialization.builtins.* import kotlinx.serialization.descriptors.* import kotlin.test.* class SerialDescriptorSpecificationTest { @Serializable class Holder(val a: Int?, @Id(42) val b: String = "", @SerialName("c") val d: Long) @Test fun testAutoGeneratedDescriptorContract() { testHolderDescriptor(Holder.serializer().descriptor) } @Test fun testManuallyConstructedDescriptor() { testHolderDescriptor(HolderDescriptor) } private object StaticHolder { val userDefinedHolderDescriptor = buildClassSerialDescriptor("kotlinx.serialization.SerialDescriptorSpecificationTest.Holder") { element("a") val annotation = Holder.serializer().descriptor.findAnnotation(1) element("b", listOf(annotation!!), isOptional = true) element("c") } } private object HolderDescriptor : SerialDescriptor by StaticHolder.userDefinedHolderDescriptor private fun testHolderDescriptor(d: SerialDescriptor) { assertEquals(StructureKind.CLASS, d.kind) assertEquals("kotlinx.serialization.SerialDescriptorSpecificationTest.Holder", d.serialName) assertEquals(3, d.elementsCount) // Indices run { assertEquals(0, d.getElementIndex("a")) assertEquals(1, d.getElementIndex("b")) assertEquals(2, d.getElementIndex("c")) assertEquals(UNKNOWN_NAME, d.getElementIndex("?")) assertEquals(UNKNOWN_NAME, d.getElementIndex("d")) assertEquals("a", d.getElementName(0)) assertEquals("b", d.getElementName(1)) assertEquals("c", d.getElementName(2)) } // Children annotations run { assertEquals(0, d.getElementAnnotations(0).size) d.assertSingleAnnotation(1) { it is Id && it.id == 42 } assertEquals(0, d.getElementAnnotations(2).size) } // Optionality run { assertFalse(d.isElementOptional(0)) assertTrue(d.isElementOptional(1)) assertFalse(d.isElementOptional(2)) } // Children descriptors run { val aDescriptor = d.getElementDescriptor(0) assertTrue(aDescriptor.isNullable) assertTrue(aDescriptor.kind is PrimitiveKind.INT) val bDescriptor = d.getElementDescriptor(1) assertFalse(bDescriptor.isNullable) assertTrue(bDescriptor.kind is PrimitiveKind.STRING) val cDescriptor = d.getElementDescriptor(2) assertFalse(cDescriptor.isNullable) assertTrue(cDescriptor.kind is PrimitiveKind.LONG) } // Failure modes assertFailsWith { d.isElementOptional(3) } assertFailsWith { d.isElementOptional(Int.MAX_VALUE) } assertFailsWith { d.getElementAnnotations(3) } assertFailsWith { d.getElementName(3) } assertFailsWith { d.getElementAnnotations(3) } } @SerialName("Named") @Serializable @Suppress("UNUSED") enum class NamedEnum { @Id(42) FIRST, @SerialName("SECOND") THIRD } @Test fun testEnumDescriptor() { fun SerialDescriptor.verifyEnumMember(name: String) { assertEquals("Named.$name", serialName) assertEquals(0, elementsCount) assertEquals(StructureKind.OBJECT, kind) } val d = NamedEnum.serializer().descriptor assertEquals(SerialKind.ENUM, d.kind) assertEquals("Named", d.serialName) assertEquals(2, d.elementsCount) assertFalse(d.isNullable) // Names assertEquals(0, d.getElementIndex("FIRST")) assertEquals(1, d.getElementIndex("SECOND")) assertEquals(UNKNOWN_NAME, d.getElementIndex("THIRD")) // Elements assertEquals("FIRST", d.getElementName(0)) assertEquals("SECOND", d.getElementName(1)) assertFailsWith { d.getElementName(2) } // Annotations d.assertSingleAnnotation(0) { it is Id && it.id == 42 } assertEquals(0, d.getElementAnnotations(1).size) // Element descriptors d.getElementDescriptor(0).verifyEnumMember("FIRST") d.getElementDescriptor(1).verifyEnumMember("SECOND") assertFailsWith { d.getElementDescriptor(2) } } @Test fun testListDescriptor() { val descriptor = serializer>().descriptor assertEquals("kotlin.collections.ArrayList", descriptor.serialName) assertFalse(descriptor.isNullable) assertEquals(1, descriptor.elementsCount) assertSame(Int.serializer().descriptor, descriptor.getElementDescriptor(0)) assertSame(Int.serializer().descriptor, descriptor.getElementDescriptor(1)) assertFalse(descriptor.isElementOptional(0)) assertFalse(descriptor.isElementOptional(1)) assertFailsWith { descriptor.isElementOptional(-1) } } @Test fun testMapDescriptor() { val descriptor = MapSerializer( Int.serializer(), Long.serializer() ).descriptor assertEquals("kotlin.collections.LinkedHashMap", descriptor.serialName) assertFalse(descriptor.isNullable) assertEquals(2, descriptor.elementsCount) assertSame(Int.serializer().descriptor, descriptor.getElementDescriptor(0)) assertSame(Long.serializer().descriptor, descriptor.getElementDescriptor(1)) assertSame(Int.serializer().descriptor, descriptor.getElementDescriptor(2)) assertSame(Long.serializer().descriptor, descriptor.getElementDescriptor(3)) assertTrue(descriptor.getElementAnnotations(0).isEmpty()) assertTrue(descriptor.getElementAnnotations(1).isEmpty()) assertFalse(descriptor.isElementOptional(0)) assertFalse(descriptor.isElementOptional(1)) assertFalse(descriptor.isElementOptional(2)) assertFalse(descriptor.isElementOptional(3)) assertFailsWith { descriptor.isElementOptional(-1) } } @Serializable @SerialName("SerializableObject") object SerializableObject {} @Test fun testObjectDescriptor() { val descriptor = SerializableObject.serializer().descriptor assertEquals(StructureKind.OBJECT, descriptor.kind) assertEquals("SerializableObject", descriptor.serialName) assertEquals(0, descriptor.elementsCount) assertEquals(UNKNOWN_NAME, descriptor.getElementIndex("?")) // Failure modes assertFailsWith { descriptor.isElementOptional(0) } assertFailsWith { descriptor.getElementAnnotations(0) } assertFailsWith { descriptor.getElementName(0) } assertFailsWith { descriptor.getElementAnnotations(0) } } @Test fun testPrimitiveDescriptors() { checkPrimitiveDescriptor("Int", Int.serializer().descriptor) checkPrimitiveDescriptor("Boolean", Boolean.serializer().descriptor) checkPrimitiveDescriptor("Byte", Byte.serializer().descriptor) checkPrimitiveDescriptor("Short", Short.serializer().descriptor) checkPrimitiveDescriptor("Long", Long.serializer().descriptor) checkPrimitiveDescriptor("Float", Float.serializer().descriptor) checkPrimitiveDescriptor("Double", Double.serializer().descriptor) checkPrimitiveDescriptor("Char", Char.serializer().descriptor) checkPrimitiveDescriptor("String", String.serializer().descriptor) } @Test fun testUnitDescriptor() { val descriptor = Unit.serializer().descriptor assertEquals(StructureKind.OBJECT, descriptor.kind) assertFalse(descriptor.isNullable) assertEquals("kotlin.Unit", descriptor.serialName) assertEquals(0, descriptor.annotations.size) assertFailsWith { descriptor.getElementName(0) } } @Test fun testCustomPrimitiveDescriptor() { assertFailsWith { PrimitiveSerialDescriptor("kotlin.Int", PrimitiveKind.INT) } assertFailsWith { PrimitiveSerialDescriptor("Int", PrimitiveKind.INT) } assertFailsWith { PrimitiveSerialDescriptor("int", PrimitiveKind.INT) } } private fun checkPrimitiveDescriptor(type: String, descriptor: SerialDescriptor) { assertEquals(0, descriptor.elementsCount) val kind = descriptor.kind.toString() assertEquals(type.uppercase(), kind) assertEquals("kotlin.${type}", descriptor.serialName) assertEquals(0, descriptor.annotations.size) assertFalse(descriptor.isNullable) assertFailsWith { descriptor.getElementName(0) } } inline fun SerialDescriptor.assertSingleAnnotation(index: Int, validator: (Annotation) -> Boolean) { val annotations = getElementAnnotations(index) assertEquals(1, annotations.size) assertTrue(validator(annotations.first())) } }