blob: 7a51bb2b68c7da2005224f3b6a750fa40e8cab87 [file] [log] [blame]
/*
* Copyright 2018 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlinx.serialization
import org.junit.Test
// Serializable data class
@Serializable
data class Data(
val value1: String,
val value2: Int
)
// Serializable data class with explicit companion object
@Serializable
data class DataExplicit(
val value1: String,
val value2: Int
) {
companion object
}
// Regular (non-data) class with var properties
@Serializable
class Reg {
var value1: String = ""
var value2: Int = 0
}
// Specify serializable names
@Serializable
data class Names(
@SerialName("value1")
val custom1: String,
@SerialName("value2")
val custom2: Int
)
// Custom serializer
@Serializable(with= CustomSerializer::class)
data class Custom(
val _value1: String,
val _value2: Int
)
@Suppress("NAME_SHADOWING")
object CustomSerializer : KSerializer<Custom> {
override val descriptor = object : SerialDescriptor {
override val name = "kotlinx.serialization.Custom"
override val kind: SerialKind = StructureKind.CLASS
override fun getElementName(index: Int) = when(index) {
0 -> "value1"
1 -> "value2"
else -> ""
}
override fun getElementIndex(name: String) = when(name) {
"value1" -> 0
"value2" -> 1
else -> -1
}
}
override fun serialize(encoder: Encoder, obj : Custom) {
val encoder = encoder.beginStructure(descriptor)
encoder.encodeStringElement(descriptor, 0, obj._value1)
encoder.encodeIntElement(descriptor, 1, obj._value2)
encoder.endStructure(descriptor)
}
override fun deserialize(decoder: Decoder): Custom {
val decoder = decoder.beginStructure(descriptor)
if (decoder.decodeElementIndex(descriptor) != 0) throw java.lang.IllegalStateException()
val value1 = decoder.decodeStringElement(descriptor, 0)
if (decoder.decodeElementIndex(descriptor) != 1) throw java.lang.IllegalStateException()
val value2 = decoder.decodeIntElement(descriptor, 1)
if (decoder.decodeElementIndex(descriptor) != CompositeDecoder.READ_DONE) throw java.lang.IllegalStateException()
decoder.endStructure(descriptor)
return Custom(value1, value2)
}
}
// External serializer
// not Serializable !!!
data class ExternalData(
val value1: String,
val value2: Int
)
@Serializer(forClass= ExternalData::class)
object ExternalSerializer
// --------- tests and utils ---------
class SerializeFlatTest() {
@Test
fun testData() {
val out = Out("Data")
out.encode(Data::class.serializer(), Data("s1", 42))
out.done()
val inp = Inp("Data")
val data = inp.decode(Data::class.serializer())
inp.done()
assert(data.value1 == "s1" && data.value2 == 42)
}
@Test
fun testDataExplicit() {
val out = Out("DataExplicit")
out.encode(DataExplicit::class.serializer(), DataExplicit("s1", 42))
out.done()
val inp = Inp("DataExplicit")
val data = inp.decode(DataExplicit::class.serializer())
inp.done()
assert(data.value1 == "s1" && data.value2 == 42)
}
@Test
fun testReg() {
val out = Out("Reg")
val reg = Reg();
reg.value1 = "s1"
reg.value2 = 42
out.encode(Reg::class.serializer(), reg)
out.done()
val inp = Inp("Reg")
val data = inp.decode(Reg::class.serializer())
inp.done()
assert(data.value1 == "s1" && data.value2 == 42)
}
@Test
fun testNames() {
val out = Out("Names")
out.encode(Names::class.serializer(), Names("s1", 42))
out.done()
val inp = Inp("Names")
val data = inp.decode(Names::class.serializer())
inp.done()
assert(data.custom1 == "s1" && data.custom2 == 42)
}
@Test
fun testCustom() {
val out = Out("Custom")
out.encode(CustomSerializer, Custom("s1", 42))
out.done()
val inp = Inp("Custom")
val data = inp.decode(CustomSerializer)
inp.done()
assert(data._value1 == "s1" && data._value2 == 42)
}
@Test
fun testExternalData() {
val out = Out("ExternalData")
out.encode(ExternalSerializer, ExternalData("s1", 42))
out.done()
val inp = Inp("ExternalData")
val data = inp.decode(ExternalSerializer)
inp.done()
assert(data.value1 == "s1" && data.value2 == 42)
}
companion object {
fun fail(msg: String): Nothing = throw RuntimeException(msg)
fun checkDesc(name: String, desc: SerialDescriptor) {
if (desc.name != "kotlinx.serialization." + name) fail("checkDesc name $desc")
if (desc.kind != StructureKind.CLASS) fail("checkDesc kind ${desc.kind}")
if (desc.getElementName(0) != "value1") fail("checkDesc[0] $desc")
if (desc.getElementName(1) != "value2") fail("checkDesc[1] $desc")
}
}
class Out(private val name: String) : ElementValueEncoder() {
var step = 0
override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeEncoder {
checkDesc(name, desc)
if (step == 0) step++ else fail("@$step: beginStructure($desc)")
return this
}
override fun encodeElement(desc: SerialDescriptor, index: Int): Boolean {
checkDesc(name, desc)
when (step) {
1 -> if (index == 0) { step++; return true }
3 -> if (index == 1) { step++; return true }
}
fail("@$step: encodeElement($desc, $index)")
}
override fun encodeString(value: String) {
when (step) {
2 -> if (value == "s1") { step++; return }
}
fail("@$step: encodeString($value)")
}
override fun encodeInt(value: Int) {
when (step) {
4 -> if (value == 42) { step++; return }
}
fail("@$step: decodeInt($value)")
}
override fun endStructure(desc: SerialDescriptor) {
checkDesc(name, desc)
if (step == 5) step++ else fail("@$step: endStructure($desc)")
}
fun done() {
if (step != 6) fail("@$step: OUT FAIL")
}
}
class Inp(private val name: String) : ElementValueDecoder() {
var step = 0
override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
checkDesc(name, desc)
if (step == 0) step++ else fail("@$step: beginStructure($desc)")
return this
}
override fun decodeElementIndex(desc: SerialDescriptor): Int {
checkDesc(name, desc)
when (step) {
1 -> { step++; return 0 }
3 -> { step++; return 1 }
5 -> { step++; return -1 }
}
fail("@$step: decodeElementIndex($desc)")
}
override fun decodeString(): String {
when (step) {
2 -> { step++; return "s1" }
}
fail("@$step: decodeString()")
}
override fun decodeInt(): Int {
when (step) {
4 -> { step++; return 42 }
}
fail("@$step: decodeInt()")
}
override fun endStructure(desc: SerialDescriptor) {
checkDesc(name, desc)
if (step == 6) step++ else fail("@$step: endStructure($desc)")
}
fun done() {
if (step != 7) fail("@$step: INP FAIL")
}
}
}