1 /* 2 * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5 package kotlinx.serialization 6 7 import kotlinx.coroutines.* 8 import kotlinx.serialization.descriptors.* 9 import kotlinx.serialization.encoding.* 10 import kotlinx.serialization.json.* 11 import kotlinx.serialization.json.internal.* 12 import kotlinx.serialization.modules.* 13 import kotlin.test.* 14 15 class StacktraceRecoveryTest { 16 @Serializable 17 private class Data(val s: String) 18 19 private class BadDecoder : AbstractDecoder() { 20 override val serializersModule: SerializersModule = EmptySerializersModule() decodeElementIndexnull21 override fun decodeElementIndex(descriptor: SerialDescriptor): Int = 42 22 } 23 24 @Test 25 fun testJsonDecodingException() = checkRecovered("JsonDecodingException") { 26 Json.decodeFromString<String>("42") 27 } 28 29 @Test <lambda>null30 fun testJsonEncodingException() = checkRecovered("JsonEncodingException") { 31 Json.encodeToString(Double.NaN) 32 } 33 34 @Test 35 // checks simple name because UFE is internal class testUnknownFieldExceptionnull36 fun testUnknownFieldException() = checkRecovered("UnknownFieldException") { 37 val serializer = Data.serializer() 38 serializer.deserialize(BadDecoder()) 39 } 40 <lambda>null41 private fun checkRecovered(exceptionClassSimpleName: String, block: () -> Unit) = runBlocking { 42 val result = runCatching { 43 callBlockWithRecovery(block) 44 } 45 assertTrue(result.isFailure, "Block should have failed") 46 val e = result.exceptionOrNull()!! 47 assertEquals(exceptionClassSimpleName, e::class.simpleName!!) 48 val cause = e.cause 49 assertNotNull(cause, "Exception should have cause: $e") 50 assertEquals(e.message, cause.message) 51 assertEquals(exceptionClassSimpleName, e::class.simpleName!!) 52 } 53 54 // KLUDGE: A separate function with state-machine to ensure coroutine DebugMetadata is generated. See KT-41789 callBlockWithRecoverynull55 private suspend fun callBlockWithRecovery(block: () -> Unit) { 56 yield() 57 // use withContext to perform switch between coroutines and thus trigger exception recovery machinery 58 withContext(NonCancellable) { 59 block() 60 } 61 } 62 } 63