summaryrefslogtreecommitdiff
path: root/formats/json-tests/jvmTest/src/kotlinx/serialization/StacktraceRecoveryTest.kt
diff options
context:
space:
mode:
Diffstat (limited to 'formats/json-tests/jvmTest/src/kotlinx/serialization/StacktraceRecoveryTest.kt')
-rw-r--r--formats/json-tests/jvmTest/src/kotlinx/serialization/StacktraceRecoveryTest.kt62
1 files changed, 62 insertions, 0 deletions
diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/StacktraceRecoveryTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/StacktraceRecoveryTest.kt
new file mode 100644
index 00000000..dcf3e959
--- /dev/null
+++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/StacktraceRecoveryTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.coroutines.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+class StacktraceRecoveryTest {
+ @Serializable
+ private class Data(val s: String)
+
+ private class BadDecoder : AbstractDecoder() {
+ override val serializersModule: SerializersModule = EmptySerializersModule()
+ override fun decodeElementIndex(descriptor: SerialDescriptor): Int = 42
+ }
+
+ @Test
+ fun testJsonDecodingException() = checkRecovered("JsonDecodingException") {
+ Json.decodeFromString<String>("42")
+ }
+
+ @Test
+ fun testJsonEncodingException() = checkRecovered("JsonEncodingException") {
+ Json.encodeToString(Double.NaN)
+ }
+
+ @Test
+ // checks simple name because UFE is internal class
+ fun testUnknownFieldException() = checkRecovered("UnknownFieldException") {
+ val serializer = Data.serializer()
+ serializer.deserialize(BadDecoder())
+ }
+
+ private fun checkRecovered(exceptionClassSimpleName: String, block: () -> Unit) = runBlocking {
+ val result = runCatching {
+ callBlockWithRecovery(block)
+ }
+ assertTrue(result.isFailure, "Block should have failed")
+ val e = result.exceptionOrNull()!!
+ assertEquals(exceptionClassSimpleName, e::class.simpleName!!)
+ val cause = e.cause
+ assertNotNull(cause, "Exception should have cause: $e")
+ assertEquals(e.message, cause.message)
+ assertEquals(exceptionClassSimpleName, e::class.simpleName!!)
+ }
+
+ // KLUDGE: A separate function with state-machine to ensure coroutine DebugMetadata is generated. See KT-41789
+ private suspend fun callBlockWithRecovery(block: () -> Unit) {
+ yield()
+ // use withContext to perform switch between coroutines and thus trigger exception recovery machinery
+ withContext(NonCancellable) {
+ block()
+ }
+ }
+}