1 /*
2  * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 package sample
5 
6 import kotlinx.serialization.*
7 import kotlinx.serialization.descriptors.SerialDescriptor
8 import kotlinx.serialization.encoding.AbstractDecoder
9 import kotlinx.serialization.encoding.AbstractEncoder
10 import kotlinx.serialization.encoding.CompositeDecoder
11 import kotlinx.serialization.encoding.CompositeEncoder
12 import kotlinx.serialization.modules.*
13 import kotlin.test.Test
14 import kotlin.test.assertEquals
15 import kotlin.test.assertNotSame
16 
17 class BasicTypesSerializationTest {
18 
19     enum class Attitude { POSITIVE, NEUTRAL, NEGATIVE }
20 
21     @Serializable
22     data class Tree(val name: String, val left: Tree? = null, val right: Tree? = null)
23 
24     @Serializable
25     data class TypesUmbrella(
26         val unit: Unit,
27         val boolean: Boolean,
28         val byte: Byte,
29         val short: Short,
30         val int: Int,
31         val long: Long,
32         val float: Float,
33         val double: Double,
34         val char: Char,
35         val string: String,
36         val enum: Attitude,
37         val intData: IntData,
38         val unitN: Unit?,
39         val booleanN: Boolean?,
40         val byteN: Byte?,
41         val shortN: Short?,
42         val intN: Int?,
43         val longN: Long?,
44         val floatN: Float?,
45         val doubleN: Double?,
46         val charN: Char?,
47         val stringN: String?,
48         val enumN: Attitude?,
49         val intDataN: IntData?,
50         val listInt: List<Int>,
51         val listIntN: List<Int?>,
52         val listNInt: Set<Int>?,
53         val listNIntN: MutableSet<Int?>?,
54         val listListEnumN: List<List<Attitude?>>,
55         val listIntData: List<IntData>,
56         val listIntDataN: MutableList<IntData?>,
57         val tree: Tree,
58         val mapStringInt: Map<String, Int>,
59         val mapIntStringN: Map<Int, String?>,
60         val arrays: ArraysUmbrella
61     )
62 
63     @Serializable
64     data class ArraysUmbrella(
65         val arrByte: Array<Byte>,
66         val arrInt: Array<Int>,
67         val arrIntN: Array<Int?>,
68         val arrIntData: Array<IntData>
69     ) {
equalsnull70         override fun equals(other: Any?) = other is ArraysUmbrella &&
71                 arrByte.contentEquals(other.arrByte) &&
72                 arrInt.contentEquals(other.arrInt) &&
73                 arrIntN.contentEquals(other.arrIntN) &&
74                 arrIntData.contentEquals(other.arrIntData)
75     }
76 
77     val data = TypesUmbrella(
78         Unit, true, 10, 20, 30, 40, 50f, 60.0, 'A', "Str0", Attitude.POSITIVE, IntData(70),
79         null, null, 11, 21, 31, 41, 51f, 61.0, 'B', "Str1", Attitude.NEUTRAL, null,
80         listOf(1, 2, 3),
81         listOf(4, 5, null),
82         setOf(6, 7, 8),
83         mutableSetOf(null, 9, 10),
84         listOf(listOf(Attitude.NEGATIVE, null)),
85         listOf(IntData(1), IntData(2), IntData(3)),
86         mutableListOf(IntData(1), null, IntData(3)),
87         Tree("root", Tree("left"), Tree("right", Tree("right.left"), Tree("right.right"))),
88         mapOf("one" to 1, "two" to 2, "three" to 3),
89         mapOf(0 to null, 1 to "first", 2 to "second"),
90         ArraysUmbrella(
91             arrayOf(1, 2, 3),
92             arrayOf(100, 200, 300),
93             arrayOf(null, -1, -2),
94             arrayOf(IntData(1), IntData(2))
95         )
96     )
97 
98     @Serializable
99     class WithSecondaryConstructor(var someProperty: Int) {
100         var test: String = "Test"
101 
102         constructor() : this(42)
103 
104         override fun equals(other: Any?): Boolean {
105             if (this === other) return true
106             if (other !is WithSecondaryConstructor) return false
107 
108             if (someProperty != other.someProperty) return false
109             if (test != other.test) return false
110 
111             return true
112         }
113 
114         override fun hashCode(): Int {
115             var result = someProperty
116             result = 31 * result + test.hashCode()
117             return result
118         }
119     }
120 
121 
122     // KeyValue Input/Output
123 
124     @OptIn(ExperimentalSerializationApi::class)
125     class KeyValueOutput(val sb: StringBuilder) : AbstractEncoder() {
126 
127         override val serializersModule: SerializersModule = EmptySerializersModule()
128 
beginStructurenull129         override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
130             sb.append('{')
131             return this
132         }
133 
endStructurenull134         override fun endStructure(descriptor: SerialDescriptor) {
135             sb.append('}')
136         }
137 
encodeElementnull138         override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
139             if (index > 0) sb.append(", ")
140             sb.append(descriptor.getElementName(index))
141             sb.append(':')
142             return true
143         }
144 
encodeNullnull145         override fun encodeNull() {
146             sb.append("null")
147         }
148 
encodeValuenull149         override fun encodeValue(value: Any) {
150             sb.append(value)
151         }
152 
encodeStringnull153         override fun encodeString(value: String) {
154             sb.append('"')
155             sb.append(value)
156             sb.append('"')
157         }
158 
encodeCharnull159         override fun encodeChar(value: Char) = encodeString(value.toString())
160     }
161 
162     class KeyValueInput(val inp: Parser) : AbstractDecoder() {
163 
164         override val serializersModule: SerializersModule = EmptySerializersModule()
165 
166         override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
167             inp.expectAfterWhiteSpace('{')
168             return this
169         }
170 
171         override fun endStructure(descriptor: SerialDescriptor) = inp.expectAfterWhiteSpace('}')
172 
173         override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
174             inp.skipWhitespace(',')
175             val name = inp.nextUntil(':', '}')
176             if (name.isEmpty())
177                 return CompositeDecoder.DECODE_DONE
178             val index = descriptor.getElementIndex(name)
179             inp.expect(':')
180             return index
181         }
182 
183         private fun readToken(): String {
184             inp.skipWhitespace()
185             return inp.nextUntil(' ', ',', '}')
186         }
187 
188         override fun decodeNotNullMark(): Boolean {
189             inp.skipWhitespace()
190             if (inp.cur != 'n'.code) return true
191             return false
192         }
193 
194         override fun decodeNull(): Nothing? {
195             check(readToken() == "null") { "'null' expected" }
196             return null
197         }
198 
199         override fun decodeBoolean(): Boolean = readToken() == "true"
200         override fun decodeByte(): Byte = readToken().toByte()
201         override fun decodeShort(): Short = readToken().toShort()
202         override fun decodeInt(): Int = readToken().toInt()
203         override fun decodeLong(): Long = readToken().toLong()
204         override fun decodeFloat(): Float = readToken().toFloat()
205         override fun decodeDouble(): Double = readToken().toDouble()
206 
207         override fun decodeEnum(enumDescriptor: SerialDescriptor): Int {
208             return readToken().toInt()
209         }
210 
211         override fun decodeString(): String {
212             inp.expectAfterWhiteSpace('"')
213             val value = inp.nextUntil('"')
214             inp.expect('"')
215             return value
216         }
217 
218         override fun decodeChar(): Char = decodeString().single()
219     }
220 
221     // Very simple char-by-char parser
222     class Parser(private val inp: StringReader) {
223         var cur: Int = inp.read()
224 
nextnull225         fun next() {
226             cur = inp.read()
227         }
228 
skipWhitespacenull229         fun skipWhitespace(vararg c: Char) {
230             while (cur >= 0 && (cur.toChar().isWhitespace() || cur.toChar() in c))
231                 next()
232         }
233 
expectnull234         fun expect(c: Char) {
235             check(cur == c.code) { "Expected '$c'" }
236             next()
237         }
238 
expectAfterWhiteSpacenull239         fun expectAfterWhiteSpace(c: Char) {
240             skipWhitespace()
241             expect(c)
242         }
243 
nextUntilnull244         fun  nextUntil(vararg c: Char): String {
245             val sb = StringBuilder()
246             while (cur >= 0 && cur.toChar() !in c) {
247                 sb.append(cur.toChar())
248                 next()
249             }
250             return sb.toString()
251         }
252     }
253 
254 
255     class StringReader(val str: String) {
256         private var position: Int = 0
readnull257         fun read(): Int = when (position) {
258             str.length -> -1
259             else -> str[position++].code
260         }
261     }
262 
263     @Test
testKvSerializationnull264     fun testKvSerialization() {
265         // serialize to string
266         val sb = StringBuilder()
267         val out = KeyValueOutput(sb)
268         out.encodeSerializableValue(TypesUmbrella.serializer(), data)
269         // deserialize from string
270         val str = sb.toString()
271         val inp = KeyValueInput(Parser(StringReader(str)))
272         val other = inp.decodeSerializableValue(TypesUmbrella.serializer())
273         // assert we've got it back from string
274         assertEquals(data, other)
275         assertNotSame(data, other)
276     }
277 
278     @Test
someConstructornull279     fun someConstructor() {
280         assertStringFormAndRestored("""{"someProperty":42,"test":"Test"}""", WithSecondaryConstructor(), WithSecondaryConstructor.serializer())
281     }
282 }
283