/* * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") package kotlinx.serialization.features import kotlinx.serialization.* import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.* import kotlinx.serialization.json.internal.BATCH_SIZE import kotlinx.serialization.modules.* import kotlinx.serialization.test.* import org.junit.Test import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import kotlin.test.assertEquals import kotlin.test.assertFailsWith class JsonJvmStreamsTest { private val strLen = BATCH_SIZE * 2 + 42 @Test fun testParsesStringsLongerThanBuffer() { val str = "a".repeat(strLen) val input = """{"data":"$str"}""" assertEquals(input, Json.encodeViaStream(StringData.serializer(), StringData(str))) assertEquals(str, Json.decodeViaStream(StringData.serializer(), input).data) assertEquals(str, Json.decodeViaStream(String.serializer(), "\"$str\"")) } @Test fun testSkipsWhitespacesLongerThanBuffer() { val str = "a".repeat(strLen) val ws = " ".repeat(strLen) val input = """{"data":$ws"$str"}""" assertEquals("""{"data":"$str"}""", Json.encodeViaStream(StringData.serializer(), StringData(str))) assertEquals(str, Json.decodeViaStream(StringData.serializer(), input).data) } @Test fun testHandlesEscapesLongerThanBuffer() { val str = "\\t".repeat(strLen) val expected = "\t".repeat(strLen) val input = """{"data":"$str"}""" assertEquals(input, Json.encodeViaStream(StringData.serializer(), StringData(expected))) assertEquals(expected, Json.decodeViaStream(StringData.serializer(), input).data) } @Test fun testHandlesLongLenientStrings() { val str = "a".repeat(strLen) val input = """{"data":$str}""" val json = Json { isLenient = true } assertEquals(str, json.decodeViaStream(StringData.serializer(), input).data) assertEquals(str, json.decodeViaStream(String.serializer(), str)) } @Test fun testThrowsCorrectExceptionOnEof() { assertFailsWith { Json.decodeViaStream(StringData.serializer(), """{"data":""") } assertFailsWith { Json.decodeViaStream(StringData.serializer(), "") } assertFailsWith { Json.decodeViaStream(String.serializer(), "\"") } } @Test fun testRandomEscapeSequences() { repeat(1000) { val s = generateRandomUnicodeString(strLen) try { val serializer = String.serializer() val b = ByteArrayOutputStream() Json.encodeToStream(serializer, s, b) val restored = Json.decodeFromStream(serializer, ByteArrayInputStream(b.toByteArray())) assertEquals(s, restored) } catch (e: Throwable) { // Not assertion error to preserve cause throw IllegalStateException("Unexpectedly failed test, cause string: $s", e) } } } interface Poly @Serializable @SerialName("Impl") data class Impl(val str: String) : Poly @Test fun testPolymorphismWhenCrossingBatchSizeNonLeadingKey() { val json = Json { serializersModule = SerializersModule { polymorphic(Poly::class) { subclass(Impl::class, Impl.serializer()) } } } val longString = "a".repeat(BATCH_SIZE - 5) val string = """{"str":"$longString", "type":"Impl"}""" val golden = Impl(longString) val deserialized = json.decodeViaStream(serializer(), string) assertEquals(golden, deserialized as Impl) } @Test fun testPolymorphismWhenCrossingBatchSize() { val json = Json { serializersModule = SerializersModule { polymorphic(Poly::class) { subclass(Impl::class, Impl.serializer()) } } } val aLotOfWhiteSpaces = " ".repeat(BATCH_SIZE - 5) val string = """{$aLotOfWhiteSpaces"type":"Impl", "str":"value"}""" val golden = Impl("value") val deserialized = json.decodeViaStream(serializer(), string) assertEquals(golden, deserialized as Impl) } }