diff --git a/ktor-client/ktor-client-android/api/ktor-client-android.api b/ktor-client/ktor-client-android/api/ktor-client-android.api index f8152b9a21..c9626c1ad7 100644 --- a/ktor-client/ktor-client-android/api/ktor-client-android.api +++ b/ktor-client/ktor-client-android/api/ktor-client-android.api @@ -1,6 +1,9 @@ public final class io/ktor/client/engine/android/Android : io/ktor/client/engine/HttpClientEngineFactory { public static final field INSTANCE Lio/ktor/client/engine/android/Android; public fun create (Lkotlin/jvm/functions/Function1;)Lio/ktor/client/engine/HttpClientEngine; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class io/ktor/client/engine/android/AndroidClientEngine : io/ktor/client/engine/HttpClientEngineBase { diff --git a/ktor-client/ktor-client-android/jvm/src/io/ktor/client/engine/android/Android.kt b/ktor-client/ktor-client-android/jvm/src/io/ktor/client/engine/android/Android.kt index 8138688db0..5efb76b665 100644 --- a/ktor-client/ktor-client-android/jvm/src/io/ktor/client/engine/android/Android.kt +++ b/ktor-client/ktor-client-android/jvm/src/io/ktor/client/engine/android/Android.kt @@ -26,7 +26,7 @@ import io.ktor.client.engine.* * * You can learn more about client engines from [Engines](https://ktor.io/docs/http-client-engines.html). */ -public object Android : HttpClientEngineFactory { +public data object Android : HttpClientEngineFactory { override fun create(block: AndroidEngineConfig.() -> Unit): HttpClientEngine = AndroidClientEngine(AndroidEngineConfig().apply(block)) } diff --git a/ktor-client/ktor-client-apache/api/ktor-client-apache.api b/ktor-client/ktor-client-apache/api/ktor-client-apache.api index c9622b6939..7b79b512cd 100644 --- a/ktor-client/ktor-client-apache/api/ktor-client-apache.api +++ b/ktor-client/ktor-client-apache/api/ktor-client-apache.api @@ -1,6 +1,9 @@ public final class io/ktor/client/engine/apache/Apache : io/ktor/client/engine/HttpClientEngineFactory { public static final field INSTANCE Lio/ktor/client/engine/apache/Apache; public fun create (Lkotlin/jvm/functions/Function1;)Lio/ktor/client/engine/HttpClientEngine; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class io/ktor/client/engine/apache/ApacheEngineConfig : io/ktor/client/engine/HttpClientEngineConfig { diff --git a/ktor-client/ktor-client-apache/jvm/src/io/ktor/client/engine/apache/Apache.kt b/ktor-client/ktor-client-apache/jvm/src/io/ktor/client/engine/apache/Apache.kt index ee3bd2489f..2df9167d90 100644 --- a/ktor-client/ktor-client-apache/jvm/src/io/ktor/client/engine/apache/Apache.kt +++ b/ktor-client/ktor-client-apache/jvm/src/io/ktor/client/engine/apache/Apache.kt @@ -25,7 +25,7 @@ import io.ktor.client.engine.* * * You can learn more about client engines from [Engines](https://ktor.io/docs/http-client-engines.html). */ -public object Apache : HttpClientEngineFactory { +public data object Apache : HttpClientEngineFactory { override fun create(block: ApacheEngineConfig.() -> Unit): HttpClientEngine { val config = ApacheEngineConfig().apply(block) return ApacheEngine(config) diff --git a/ktor-client/ktor-client-apache5/api/ktor-client-apache5.api b/ktor-client/ktor-client-apache5/api/ktor-client-apache5.api index 5f665603e1..9f7dd8a9e9 100644 --- a/ktor-client/ktor-client-apache5/api/ktor-client-apache5.api +++ b/ktor-client/ktor-client-apache5/api/ktor-client-apache5.api @@ -1,6 +1,9 @@ public final class io/ktor/client/engine/apache5/Apache5 : io/ktor/client/engine/HttpClientEngineFactory { public static final field INSTANCE Lio/ktor/client/engine/apache5/Apache5; public fun create (Lkotlin/jvm/functions/Function1;)Lio/ktor/client/engine/HttpClientEngine; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class io/ktor/client/engine/apache5/Apache5EngineConfig : io/ktor/client/engine/HttpClientEngineConfig { diff --git a/ktor-client/ktor-client-apache5/jvm/src/io/ktor/client/engine/apache5/Apache5.kt b/ktor-client/ktor-client-apache5/jvm/src/io/ktor/client/engine/apache5/Apache5.kt index dff6e8230b..4d52712947 100644 --- a/ktor-client/ktor-client-apache5/jvm/src/io/ktor/client/engine/apache5/Apache5.kt +++ b/ktor-client/ktor-client-apache5/jvm/src/io/ktor/client/engine/apache5/Apache5.kt @@ -25,7 +25,7 @@ import io.ktor.client.engine.* * * You can learn more about client engines from [Engines](https://ktor.io/docs/http-client-engines.html). */ -public object Apache5 : HttpClientEngineFactory { +public data object Apache5 : HttpClientEngineFactory { override fun create(block: Apache5EngineConfig.() -> Unit): HttpClientEngine { val config = Apache5EngineConfig().apply(block) return Apache5Engine(config) diff --git a/ktor-client/ktor-client-cio/api/ktor-client-cio.klib.api b/ktor-client/ktor-client-cio/api/ktor-client-cio.klib.api index 6204ea570a..4794aa06ca 100644 --- a/ktor-client/ktor-client-cio/api/ktor-client-cio.klib.api +++ b/ktor-client/ktor-client-cio/api/ktor-client-cio.klib.api @@ -1,5 +1,5 @@ // Klib ABI Dump -// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] // Rendering settings: // - Signature version: 2 // - Show manifest properties: true @@ -62,3 +62,7 @@ final object io.ktor.client.engine.cio/CIO : io.ktor.client.engine/HttpClientEng } final fun (io.ktor.client.engine.cio/CIOEngineConfig).io.ktor.client.engine.cio/endpoint(kotlin/Function1): io.ktor.client.engine.cio/EndpointConfig // io.ktor.client.engine.cio/endpoint|endpoint@io.ktor.client.engine.cio.CIOEngineConfig(kotlin.Function1){}[0] + +// Targets: [js] +final val io.ktor.client.engine.cio/initHook // io.ktor.client.engine.cio/initHook|{}initHook[0] + final fun (): dynamic // io.ktor.client.engine.cio/initHook.|(){}[0] diff --git a/ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/CIOCommon.kt b/ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/CIOCommon.kt similarity index 96% rename from ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/CIOCommon.kt rename to ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/CIOCommon.kt index ee1023e3bd..28b85a4011 100644 --- a/ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/CIOCommon.kt +++ b/ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/CIOCommon.kt @@ -26,10 +26,6 @@ import io.ktor.client.engine.* * You can learn more about client engines from [Engines](https://ktor.io/docs/http-client-engines.html). */ public data object CIO : HttpClientEngineFactory { - init { - addToLoader() - } - override fun create(block: CIOEngineConfig.() -> Unit): HttpClientEngine = CIOEngine(CIOEngineConfig().apply(block)) } diff --git a/ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/CIOEngine.kt b/ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/CIOEngine.kt similarity index 98% rename from ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/CIOEngine.kt rename to ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/CIOEngine.kt index 59fac79bfd..8103bf9fb4 100644 --- a/ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/CIOEngine.kt +++ b/ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/CIOEngine.kt @@ -61,7 +61,6 @@ internal class CIOEngine( val requestJob = requestField[Job]!! val selector = selectorManager - @OptIn(ExperimentalCoroutinesApi::class) GlobalScope.launch(parentContext, start = CoroutineStart.ATOMIC) { try { requestJob.join() diff --git a/ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/CIOEngineConfig.kt b/ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/CIOEngineConfig.kt similarity index 100% rename from ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/CIOEngineConfig.kt rename to ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/CIOEngineConfig.kt diff --git a/ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/ConnectionFactory.kt b/ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/ConnectionFactory.kt similarity index 100% rename from ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/ConnectionFactory.kt rename to ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/ConnectionFactory.kt diff --git a/ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/ConnectionPipelineCommon.kt b/ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/ConnectionPipelineCommon.kt similarity index 100% rename from ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/ConnectionPipelineCommon.kt rename to ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/ConnectionPipelineCommon.kt diff --git a/ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/Endpoint.kt b/ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/Endpoint.kt similarity index 100% rename from ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/Endpoint.kt rename to ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/Endpoint.kt diff --git a/ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/EngineTasks.kt b/ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/EngineTasks.kt similarity index 100% rename from ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/EngineTasks.kt rename to ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/EngineTasks.kt diff --git a/ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/utils.kt b/ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/utils.kt similarity index 100% rename from ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/utils.kt rename to ktor-client/ktor-client-cio/common/src/io/ktor/client/engine/cio/utils.kt diff --git a/ktor-client/ktor-client-cio/jvmAndPosix/test/CIOEngineTest.kt b/ktor-client/ktor-client-cio/common/test/CIOEngineTest.kt similarity index 94% rename from ktor-client/ktor-client-cio/jvmAndPosix/test/CIOEngineTest.kt rename to ktor-client/ktor-client-cio/common/test/CIOEngineTest.kt index 8844cac953..ccd0fd877e 100644 --- a/ktor-client/ktor-client-cio/jvmAndPosix/test/CIOEngineTest.kt +++ b/ktor-client/ktor-client-cio/common/test/CIOEngineTest.kt @@ -12,6 +12,7 @@ import io.ktor.client.tests.utils.* import io.ktor.http.* import io.ktor.network.selector.* import io.ktor.network.sockets.* +import io.ktor.test.dispatcher.* import io.ktor.utils.io.* import io.ktor.websocket.* import kotlinx.coroutines.* @@ -24,7 +25,7 @@ class CIOEngineTest { private val selectorManager = SelectorManager() @Test - fun testRequestTimeoutIgnoredWithWebSocket(): Unit = runBlocking { + fun testRequestTimeoutIgnoredWithWebSocket() = runTestWithRealTime { val client = HttpClient(CIO) { engine { requestTimeout = 10 @@ -48,7 +49,7 @@ class CIOEngineTest { } @Test - fun testRequestTimeoutIgnoredWithSSE(): Unit = runBlocking { + fun testRequestTimeoutIgnoredWithSSE() = runTestWithRealTime { val client = HttpClient(CIO) { engine { requestTimeout = 10 @@ -67,7 +68,7 @@ class CIOEngineTest { } @Test - fun testExpectHeader(): Unit = runBlocking { + fun testExpectHeader() = runTestWithRealTime { val body = "Hello World" withServerSocket { socket -> @@ -95,7 +96,7 @@ class CIOEngineTest { } @Test - fun testNoExpectHeaderIfNoBody(): Unit = runBlocking { + fun testNoExpectHeaderIfNoBody() = runTestWithRealTime { withServerSocket { socket -> val client = HttpClient(CIO) launch { @@ -116,7 +117,7 @@ class CIOEngineTest { } @Test - fun testDontWaitForContinueResponse(): Unit = runBlocking { + fun testDontWaitForContinueResponse() = runTestWithRealTime { withTimeout(30.seconds) { val body = "Hello World\n" @@ -148,7 +149,7 @@ class CIOEngineTest { } @Test - fun testRepeatRequestAfterExpectationFailed(): Unit = runBlocking { + fun testRepeatRequestAfterExpectationFailed() = runTestWithRealTime { val body = "Hello World" withServerSocket { socket -> diff --git a/ktor-client/ktor-client-cio/jvmAndPosix/test/ConnectionFactoryTest.kt b/ktor-client/ktor-client-cio/common/test/ConnectionFactoryTest.kt similarity index 92% rename from ktor-client/ktor-client-cio/jvmAndPosix/test/ConnectionFactoryTest.kt rename to ktor-client/ktor-client-cio/common/test/ConnectionFactoryTest.kt index 8c82620013..cc77fc7fe4 100644 --- a/ktor-client/ktor-client-cio/jvmAndPosix/test/ConnectionFactoryTest.kt +++ b/ktor-client/ktor-client-cio/common/test/ConnectionFactoryTest.kt @@ -6,6 +6,8 @@ package io.ktor.client.engine.cio import io.ktor.network.selector.* import io.ktor.network.sockets.* +import io.ktor.test.dispatcher.* +import io.ktor.utils.io.core.* import kotlinx.coroutines.* import kotlin.test.* @@ -24,7 +26,7 @@ class ConnectionFactoryTest { } @Test - fun testLimitSemaphore() = runBlocking { + fun testLimitSemaphore() = runTestWithRealTime { val connectionFactory = ConnectionFactory( selectorManager, connectionsLimit = 2, @@ -45,7 +47,7 @@ class ConnectionFactoryTest { } @Test - fun testAddressSemaphore() = runBlocking { + fun testAddressSemaphore() = runTestWithRealTime { val connectionFactory = ConnectionFactory( selectorManager, connectionsLimit = 2, @@ -68,7 +70,7 @@ class ConnectionFactoryTest { } @Test - fun testReleaseLimitSemaphoreWhenFailed() = runBlocking { + fun testReleaseLimitSemaphoreWhenFailed() = runTestWithRealTime { val connectionFactory = ConnectionFactory( selectorManager, connectionsLimit = 2, diff --git a/ktor-client/ktor-client-cio/gradle.properties b/ktor-client/ktor-client-cio/gradle.properties new file mode 100644 index 0000000000..d12c10bfad --- /dev/null +++ b/ktor-client/ktor-client-cio/gradle.properties @@ -0,0 +1,5 @@ +# +# Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. +# +target.js.browser=false +target.wasmJs.browser=false diff --git a/ktor-client/ktor-client-cio/js/src/io/ktor/client/engine/cio/Loader.js.kt b/ktor-client/ktor-client-cio/js/src/io/ktor/client/engine/cio/Loader.js.kt new file mode 100644 index 0000000000..95af101c02 --- /dev/null +++ b/ktor-client/ktor-client-cio/js/src/io/ktor/client/engine/cio/Loader.js.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.client.engine.cio + +import io.ktor.client.engine.* +import io.ktor.util.* +import io.ktor.utils.io.* + +@Suppress("DEPRECATION") +@OptIn(ExperimentalStdlibApi::class, ExperimentalJsExport::class, InternalAPI::class) +@Deprecated("", level = DeprecationLevel.HIDDEN) +@JsExport +@EagerInitialization +public val initHook: dynamic = run { + if (PlatformUtils.IS_NODE) engines.append(CIO) +} diff --git a/ktor-client/ktor-client-cio/jvm/src/io/ktor/client/engine/cio/LoaderJvm.kt b/ktor-client/ktor-client-cio/jvm/src/io/ktor/client/engine/cio/LoaderJvm.kt deleted file mode 100644 index 2f48c8228c..0000000000 --- a/ktor-client/ktor-client-cio/jvm/src/io/ktor/client/engine/cio/LoaderJvm.kt +++ /dev/null @@ -1,8 +0,0 @@ -/* -* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. -*/ - -package io.ktor.client.engine.cio - -internal actual fun addToLoader() { -} diff --git a/ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/Loader.kt b/ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/Loader.kt deleted file mode 100644 index 727472064e..0000000000 --- a/ktor-client/ktor-client-cio/jvmAndPosix/src/io/ktor/client/engine/cio/Loader.kt +++ /dev/null @@ -1,7 +0,0 @@ -/* -* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. -*/ - -package io.ktor.client.engine.cio - -internal expect fun addToLoader() diff --git a/ktor-client/ktor-client-cio/posix/src/io/ktor/client/engine/cio/ConnectionPipelineNative.kt b/ktor-client/ktor-client-cio/nonJvm/src/io/ktor/client/engine/cio/ConnectionPipeline.nonJvm.kt similarity index 83% rename from ktor-client/ktor-client-cio/posix/src/io/ktor/client/engine/cio/ConnectionPipelineNative.kt rename to ktor-client/ktor-client-cio/nonJvm/src/io/ktor/client/engine/cio/ConnectionPipeline.nonJvm.kt index 1c9c9baf52..a2c8a5d438 100644 --- a/ktor-client/ktor-client-cio/posix/src/io/ktor/client/engine/cio/ConnectionPipelineNative.kt +++ b/ktor-client/ktor-client-cio/nonJvm/src/io/ktor/client/engine/cio/ConnectionPipeline.nonJvm.kt @@ -21,9 +21,9 @@ internal actual class ConnectionPipeline actual constructor( actual override val coroutineContext: CoroutineContext = parentContext init { - error("Pipelining is not supported in native CIO") + error("Pipelining is not supported in native/js/wasm CIO") } actual val pipelineContext: Job - get() = error("Pipelining is not supported in native CIO") + get() = error("Pipelining is not supported in native/js/wasm CIO") } diff --git a/ktor-client/ktor-client-cio/posix/src/io/ktor/client/engine/cio/ExceptionUtilsNative.kt b/ktor-client/ktor-client-cio/nonJvm/src/io/ktor/client/engine/cio/ExceptionUtils.nonJvm.kt similarity index 100% rename from ktor-client/ktor-client-cio/posix/src/io/ktor/client/engine/cio/ExceptionUtilsNative.kt rename to ktor-client/ktor-client-cio/nonJvm/src/io/ktor/client/engine/cio/ExceptionUtils.nonJvm.kt diff --git a/ktor-client/ktor-client-cio/posix/src/io/ktor/client/engine/cio/LoaderNative.kt b/ktor-client/ktor-client-cio/posix/src/io/ktor/client/engine/cio/Loader.posix.kt similarity index 60% rename from ktor-client/ktor-client-cio/posix/src/io/ktor/client/engine/cio/LoaderNative.kt rename to ktor-client/ktor-client-cio/posix/src/io/ktor/client/engine/cio/Loader.posix.kt index 48523ab257..693972ec91 100644 --- a/ktor-client/ktor-client-cio/posix/src/io/ktor/client/engine/cio/LoaderNative.kt +++ b/ktor-client/ktor-client-cio/posix/src/io/ktor/client/engine/cio/Loader.posix.kt @@ -7,11 +7,7 @@ package io.ktor.client.engine.cio import io.ktor.client.engine.* import io.ktor.utils.io.* -@OptIn(ExperimentalStdlibApi::class) +@Suppress("DEPRECATION") +@OptIn(ExperimentalStdlibApi::class, InternalAPI::class) @EagerInitialization -private val initHook = CIO - -@OptIn(InternalAPI::class) -internal actual fun addToLoader() { - engines.append(CIO) -} +private val initHook = engines.append(CIO) diff --git a/ktor-client/ktor-client-cio/wasmJs/src/io/ktor/client/engine/cio/Loader.wasmJs.kt b/ktor-client/ktor-client-cio/wasmJs/src/io/ktor/client/engine/cio/Loader.wasmJs.kt new file mode 100644 index 0000000000..e4aae97348 --- /dev/null +++ b/ktor-client/ktor-client-cio/wasmJs/src/io/ktor/client/engine/cio/Loader.wasmJs.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.client.engine.cio + +import io.ktor.client.engine.* +import io.ktor.util.* +import io.ktor.utils.io.* + +@Suppress("DEPRECATION") +@OptIn(InternalAPI::class, ExperimentalStdlibApi::class) +@EagerInitialization +private val initHook: Unit = run { + if (PlatformUtils.IS_NODE) engines.append(CIO) +} diff --git a/ktor-client/ktor-client-core/api/ktor-client-core.klib.api b/ktor-client/ktor-client-core/api/ktor-client-core.klib.api index 854e339bc8..0ea6d0e80b 100644 --- a/ktor-client/ktor-client-core/api/ktor-client-core.klib.api +++ b/ktor-client/ktor-client-core/api/ktor-client-core.klib.api @@ -1161,6 +1161,11 @@ final object io.ktor.client.engine/ProxyBuilder { // io.ktor.client.engine/Proxy final fun socks(kotlin/String, kotlin/Int): io.ktor.client.engine/ProxyConfig // io.ktor.client.engine/ProxyBuilder.socks|socks(kotlin.String;kotlin.Int){}[0] } +final object io.ktor.client.engine/engines : kotlin.collections/Iterable> { // io.ktor.client.engine/engines|null[0] + final fun append(io.ktor.client.engine/HttpClientEngineFactory) // io.ktor.client.engine/engines.append|append(io.ktor.client.engine.HttpClientEngineFactory){}[0] + final fun iterator(): kotlin.collections/Iterator> // io.ktor.client.engine/engines.iterator|iterator(){}[0] +} + final object io.ktor.client.plugins.api/Send : io.ktor.client.plugins.api/ClientHook> { // io.ktor.client.plugins.api/Send|null[0] final fun install(io.ktor.client/HttpClient, kotlin.coroutines/SuspendFunction2) // io.ktor.client.plugins.api/Send.install|install(io.ktor.client.HttpClient;kotlin.coroutines.SuspendFunction2){}[0] @@ -1512,12 +1517,6 @@ final suspend inline fun <#A: reified kotlin/Any?> (io.ktor.client.plugins.webso final suspend inline fun <#A: reified kotlin/Any?> (io.ktor.client.plugins.websocket/DefaultClientWebSocketSession).io.ktor.client.plugins.websocket/sendSerialized(#A) // io.ktor.client.plugins.websocket/sendSerialized|sendSerialized@io.ktor.client.plugins.websocket.DefaultClientWebSocketSession(0:0){0§}[0] final suspend inline fun <#A: reified kotlin/Any?> (io.ktor.client.statement/HttpResponse).io.ktor.client.call/body(): #A // io.ktor.client.call/body|body@io.ktor.client.statement.HttpResponse(){0§}[0] -// Targets: [native] -final object io.ktor.client.engine/engines : kotlin.collections/Iterable> { // io.ktor.client.engine/engines|null[0] - final fun append(io.ktor.client.engine/HttpClientEngineFactory) // io.ktor.client.engine/engines.append|append(io.ktor.client.engine.HttpClientEngineFactory){}[0] - final fun iterator(): kotlin.collections/Iterator> // io.ktor.client.engine/engines.iterator|iterator(){}[0] -} - // Targets: [js, wasmJs] abstract interface io.ktor.client.fetch/AbortSignal : io.ktor.client.fetch/EventTarget { // io.ktor.client.fetch/AbortSignal|null[0] abstract var aborted // io.ktor.client.fetch/AbortSignal.aborted|{}aborted[0] @@ -1786,6 +1785,9 @@ open class io.ktor.client.engine.js/JsClientEngineConfig : io.ktor.client.engine // Targets: [js, wasmJs] final object io.ktor.client.engine.js/Js : io.ktor.client.engine/HttpClientEngineFactory { // io.ktor.client.engine.js/Js|null[0] final fun create(kotlin/Function1): io.ktor.client.engine/HttpClientEngine // io.ktor.client.engine.js/Js.create|create(kotlin.Function1){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // io.ktor.client.engine.js/Js.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // io.ktor.client.engine.js/Js.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // io.ktor.client.engine.js/Js.toString|toString(){}[0] } // Targets: [js, wasmJs] @@ -2179,6 +2181,10 @@ abstract interface io.ktor.client.fetch/Uint8ArrayConstructor { // io.ktor.clien abstract fun of(kotlin/Array...): io.ktor.client.fetch/Uint8Array // io.ktor.client.fetch/Uint8ArrayConstructor.of|of(kotlin.Array...){}[0] } +// Targets: [js] +final val io.ktor.client.engine.js/initHook // io.ktor.client.engine.js/initHook|{}initHook[0] + final fun (): dynamic // io.ktor.client.engine.js/initHook.|(){}[0] + // Targets: [js] final inline fun (io.ktor.client.fetch/Uint8Array).io.ktor.client.fetch/get(kotlin/Number): kotlin/Number? // io.ktor.client.fetch/get|get@io.ktor.client.fetch.Uint8Array(kotlin.Number){}[0] diff --git a/ktor-client/ktor-client-core/js/src/io/ktor/client/engine/js/Js.kt b/ktor-client/ktor-client-core/js/src/io/ktor/client/engine/js/Js.kt index e94bd74ef3..3b12c80700 100644 --- a/ktor-client/ktor-client-core/js/src/io/ktor/client/engine/js/Js.kt +++ b/ktor-client/ktor-client-core/js/src/io/ktor/client/engine/js/Js.kt @@ -1,10 +1,11 @@ /* - * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package io.ktor.client.engine.js import io.ktor.client.engine.* +import io.ktor.utils.io.* /** * A JavaScript client engine that uses the fetch API to execute requests. @@ -20,7 +21,7 @@ import io.ktor.client.engine.* * * You can learn more about client engines from [Engines](https://ktor.io/docs/http-client-engines.html). */ -public actual object Js : HttpClientEngineFactory { +public actual data object Js : HttpClientEngineFactory { override fun create(block: JsClientEngineConfig.() -> Unit): HttpClientEngine = JsClientEngine(JsClientEngineConfig().apply(block)) } @@ -45,3 +46,10 @@ public actual open class JsClientEngineConfig : HttpClientEngineConfig() { */ public var nodeOptions: dynamic = js("Object").create(null) } + +@Suppress("DEPRECATION") +@OptIn(ExperimentalStdlibApi::class, ExperimentalJsExport::class, InternalAPI::class) +@Deprecated("", level = DeprecationLevel.HIDDEN) +@JsExport +@EagerInitialization +public val initHook: dynamic = engines.append(Js) diff --git a/ktor-client/ktor-client-core/jsAndWasmShared/src/io/ktor/client/HttpClientJs.kt b/ktor-client/ktor-client-core/jsAndWasmShared/src/io/ktor/client/HttpClientJs.kt index 95c8f4c333..db69d907cd 100644 --- a/ktor-client/ktor-client-core/jsAndWasmShared/src/io/ktor/client/HttpClientJs.kt +++ b/ktor-client/ktor-client-core/jsAndWasmShared/src/io/ktor/client/HttpClientJs.kt @@ -1,9 +1,10 @@ /* - * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package io.ktor.client +import io.ktor.client.engine.* import io.ktor.client.engine.js.* import io.ktor.utils.io.* @@ -16,4 +17,9 @@ import io.ktor.utils.io.* @KtorDsl public actual fun HttpClient( block: HttpClientConfig<*>.() -> Unit -): HttpClient = HttpClient(JsClient(), block) +): HttpClient = HttpClient(FACTORY, block) + +// we need to fall back to the default (Js) engine if there are no other engines, +// but in the presence of other engines, they're preferred. +@OptIn(InternalAPI::class) +private val FACTORY = engines.firstOrNull { it != Js } ?: Js diff --git a/ktor-client/ktor-client-core/posix/src/io/ktor/client/engine/Loader.kt b/ktor-client/ktor-client-core/nonJvm/src/io/ktor/client/engine/Loader.kt similarity index 100% rename from ktor-client/ktor-client-core/posix/src/io/ktor/client/engine/Loader.kt rename to ktor-client/ktor-client-core/nonJvm/src/io/ktor/client/engine/Loader.kt diff --git a/ktor-client/ktor-client-core/posix/src/io/ktor/client/HttpClient.kt b/ktor-client/ktor-client-core/posix/src/io/ktor/client/HttpClient.kt index 58c90f0c01..0695c91c5c 100644 --- a/ktor-client/ktor-client-core/posix/src/io/ktor/client/HttpClient.kt +++ b/ktor-client/ktor-client-core/posix/src/io/ktor/client/HttpClient.kt @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package io.ktor.client @@ -13,10 +13,13 @@ import io.ktor.utils.io.* * The [HttpClientEngine] is selected from the dependencies. * https://ktor.io/docs/http-client-engines.html */ -@OptIn(InternalAPI::class) @KtorDsl public actual fun HttpClient( block: HttpClientConfig<*>.() -> Unit -): HttpClient = engines.firstOrNull()?.let { HttpClient(it, block) } ?: error( - "Failed to find HttpClientEngineContainer. Consider adding [HttpClientEngine] implementation in dependencies." +): HttpClient = HttpClient(FACTORY, block) + +@OptIn(InternalAPI::class) +private val FACTORY = engines.firstOrNull() ?: error( + "Failed to find HTTP client engine implementation: consider adding client engine dependency. " + + "See https://ktor.io/docs/http-client-engines.html" ) diff --git a/ktor-client/ktor-client-core/wasmJs/src/io/ktor/client/engine/js/Js.kt b/ktor-client/ktor-client-core/wasmJs/src/io/ktor/client/engine/js/Js.kt index 82ff4ed746..c134605f86 100644 --- a/ktor-client/ktor-client-core/wasmJs/src/io/ktor/client/engine/js/Js.kt +++ b/ktor-client/ktor-client-core/wasmJs/src/io/ktor/client/engine/js/Js.kt @@ -1,11 +1,12 @@ /* - * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package io.ktor.client.engine.js import io.ktor.client.engine.* -import io.ktor.client.utils.makeJsObject +import io.ktor.client.utils.* +import io.ktor.utils.io.* /** * A JavaScript client engine that uses the fetch API to execute requests. @@ -21,7 +22,7 @@ import io.ktor.client.utils.makeJsObject * * You can learn more about client engines from [Engines](https://ktor.io/docs/http-client-engines.html). */ -public actual object Js : HttpClientEngineFactory { +public actual data object Js : HttpClientEngineFactory { override fun create(block: JsClientEngineConfig.() -> Unit): HttpClientEngine = JsClientEngine(JsClientEngineConfig().apply(block)) } @@ -46,3 +47,8 @@ public actual open class JsClientEngineConfig : HttpClientEngineConfig() { */ public var nodeOptions: JsAny = makeJsObject() } + +@OptIn(InternalAPI::class, ExperimentalStdlibApi::class) +@Suppress("DEPRECATION") +@EagerInitialization +private val initHook: Unit = engines.append(Js) diff --git a/ktor-client/ktor-client-java/api/ktor-client-java.api b/ktor-client/ktor-client-java/api/ktor-client-java.api index 609867b39c..e8ee2f2857 100644 --- a/ktor-client/ktor-client-java/api/ktor-client-java.api +++ b/ktor-client/ktor-client-java/api/ktor-client-java.api @@ -1,6 +1,9 @@ public final class io/ktor/client/engine/java/Java : io/ktor/client/engine/HttpClientEngineFactory { public static final field INSTANCE Lio/ktor/client/engine/java/Java; public fun create (Lkotlin/jvm/functions/Function1;)Lio/ktor/client/engine/HttpClientEngine; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class io/ktor/client/engine/java/JavaHttpConfig : io/ktor/client/engine/HttpClientEngineConfig { diff --git a/ktor-client/ktor-client-java/jvm/src/io/ktor/client/engine/java/Java.kt b/ktor-client/ktor-client-java/jvm/src/io/ktor/client/engine/java/Java.kt index 1d9fc37761..753a6362e5 100644 --- a/ktor-client/ktor-client-java/jvm/src/io/ktor/client/engine/java/Java.kt +++ b/ktor-client/ktor-client-java/jvm/src/io/ktor/client/engine/java/Java.kt @@ -25,7 +25,7 @@ import io.ktor.client.engine.* * * You can learn more about client engines from [Engines](https://ktor.io/docs/http-client-engines.html). */ -public object Java : HttpClientEngineFactory { +public data object Java : HttpClientEngineFactory { override fun create(block: JavaHttpConfig.() -> Unit): HttpClientEngine = JavaHttpEngine(JavaHttpConfig().apply(block)) } diff --git a/ktor-client/ktor-client-jetty-jakarta/api/ktor-client-jetty-jakarta.api b/ktor-client/ktor-client-jetty-jakarta/api/ktor-client-jetty-jakarta.api index faef63dd75..d7c00794a7 100644 --- a/ktor-client/ktor-client-jetty-jakarta/api/ktor-client-jetty-jakarta.api +++ b/ktor-client/ktor-client-jetty-jakarta/api/ktor-client-jetty-jakarta.api @@ -1,6 +1,9 @@ public final class io/ktor/client/engine/jetty/jakarta/Jetty : io/ktor/client/engine/HttpClientEngineFactory { public static final field INSTANCE Lio/ktor/client/engine/jetty/jakarta/Jetty; public fun create (Lkotlin/jvm/functions/Function1;)Lio/ktor/client/engine/HttpClientEngine; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class io/ktor/client/engine/jetty/jakarta/JettyEngineConfig : io/ktor/client/engine/HttpClientEngineConfig { diff --git a/ktor-client/ktor-client-jetty-jakarta/jvm/src/io/ktor/client/engine/jetty/jakarta/Jetty.kt b/ktor-client/ktor-client-jetty-jakarta/jvm/src/io/ktor/client/engine/jetty/jakarta/Jetty.kt index 07df70ca0f..2b5d62be18 100644 --- a/ktor-client/ktor-client-jetty-jakarta/jvm/src/io/ktor/client/engine/jetty/jakarta/Jetty.kt +++ b/ktor-client/ktor-client-jetty-jakarta/jvm/src/io/ktor/client/engine/jetty/jakarta/Jetty.kt @@ -26,7 +26,7 @@ import io.ktor.utils.io.* * * You can learn more about client engines from [Engines](https://ktor.io/docs/http-client-engines.html). */ -public object Jetty : HttpClientEngineFactory { +public data object Jetty : HttpClientEngineFactory { override fun create(block: JettyEngineConfig.() -> Unit): HttpClientEngine = JettyHttp2Engine(JettyEngineConfig().apply(block)) } diff --git a/ktor-client/ktor-client-jetty/api/ktor-client-jetty.api b/ktor-client/ktor-client-jetty/api/ktor-client-jetty.api index 3e86808ca4..9cdc9d6be2 100644 --- a/ktor-client/ktor-client-jetty/api/ktor-client-jetty.api +++ b/ktor-client/ktor-client-jetty/api/ktor-client-jetty.api @@ -1,6 +1,9 @@ public final class io/ktor/client/engine/jetty/Jetty : io/ktor/client/engine/HttpClientEngineFactory { public static final field INSTANCE Lio/ktor/client/engine/jetty/Jetty; public fun create (Lkotlin/jvm/functions/Function1;)Lio/ktor/client/engine/HttpClientEngine; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class io/ktor/client/engine/jetty/JettyEngineConfig : io/ktor/client/engine/HttpClientEngineConfig { diff --git a/ktor-client/ktor-client-jetty/jvm/src/io/ktor/client/engine/jetty/Jetty.kt b/ktor-client/ktor-client-jetty/jvm/src/io/ktor/client/engine/jetty/Jetty.kt index 133d96eab5..d61b64a657 100644 --- a/ktor-client/ktor-client-jetty/jvm/src/io/ktor/client/engine/jetty/Jetty.kt +++ b/ktor-client/ktor-client-jetty/jvm/src/io/ktor/client/engine/jetty/Jetty.kt @@ -26,7 +26,7 @@ import io.ktor.utils.io.* * * You can learn more about client engines from [Engines](https://ktor.io/docs/http-client-engines.html). */ -public object Jetty : HttpClientEngineFactory { +public data object Jetty : HttpClientEngineFactory { override fun create(block: JettyEngineConfig.() -> Unit): HttpClientEngine = JettyHttp2Engine(JettyEngineConfig().apply(block)) } diff --git a/ktor-client/ktor-client-okhttp/api/ktor-client-okhttp.api b/ktor-client/ktor-client-okhttp/api/ktor-client-okhttp.api index 931abbd113..454b838c8e 100644 --- a/ktor-client/ktor-client-okhttp/api/ktor-client-okhttp.api +++ b/ktor-client/ktor-client-okhttp/api/ktor-client-okhttp.api @@ -1,6 +1,9 @@ public final class io/ktor/client/engine/okhttp/OkHttp : io/ktor/client/engine/HttpClientEngineFactory { public static final field INSTANCE Lio/ktor/client/engine/okhttp/OkHttp; public fun create (Lkotlin/jvm/functions/Function1;)Lio/ktor/client/engine/HttpClientEngine; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class io/ktor/client/engine/okhttp/OkHttpConfig : io/ktor/client/engine/HttpClientEngineConfig { diff --git a/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttp.kt b/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttp.kt index b8e462f370..d36221c402 100644 --- a/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttp.kt +++ b/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttp.kt @@ -26,7 +26,7 @@ import io.ktor.client.engine.* * * You can learn more about client engines from [Engines](https://ktor.io/docs/http-client-engines.html). */ -public object OkHttp : HttpClientEngineFactory { +public data object OkHttp : HttpClientEngineFactory { override fun create(block: OkHttpConfig.() -> Unit): HttpClientEngine = OkHttpEngine(OkHttpConfig().apply(block)) } diff --git a/ktor-client/ktor-client-plugins/ktor-client-auth/common/test/io/ktor/client/plugins/auth/AuthTest.kt b/ktor-client/ktor-client-plugins/ktor-client-auth/common/test/io/ktor/client/plugins/auth/AuthTest.kt index 8809eb5f08..828573ec60 100644 --- a/ktor-client/ktor-client-plugins/ktor-client-auth/common/test/io/ktor/client/plugins/auth/AuthTest.kt +++ b/ktor-client/ktor-client-plugins/ktor-client-auth/common/test/io/ktor/client/plugins/auth/AuthTest.kt @@ -23,7 +23,7 @@ import kotlin.test.assertFailsWith class AuthTest : ClientLoader() { @Test - fun testDigestAuthLegacy() = clientTests(listOf("Js", "native")) { + fun testDigestAuthLegacy() = clientTests(listOf("Js", "native:*")) { config { install(Auth) { digest { @@ -43,7 +43,7 @@ class AuthTest : ClientLoader() { } @Test - fun testDigestAuth() = clientTests(listOf("Js", "native")) { + fun testDigestAuth() = clientTests(listOf("Js", "native:*")) { config { install(Auth) { digest { @@ -60,7 +60,7 @@ class AuthTest : ClientLoader() { } @Test - fun testDigestAuthPerRealm() = clientTests(listOf("Js", "native")) { + fun testDigestAuthPerRealm() = clientTests(listOf("Js", "native:*")) { config { install(Auth) { digest { @@ -84,7 +84,7 @@ class AuthTest : ClientLoader() { } @Test - fun testDigestAuthSHA256() = clientTests(listOf("Js", "native")) { + fun testDigestAuthSHA256() = clientTests(listOf("Js", "native:*")) { config { install(Auth) { digest { diff --git a/ktor-client/ktor-client-tests/build.gradle.kts b/ktor-client/ktor-client-tests/build.gradle.kts index 2de670961a..b28e935b9c 100644 --- a/ktor-client/ktor-client-tests/build.gradle.kts +++ b/ktor-client/ktor-client-tests/build.gradle.kts @@ -2,7 +2,7 @@ * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ -import test.server.* +import test.server.TestServerPlugin description = "Common tests for client" @@ -68,9 +68,9 @@ kotlin.sourceSets { } } - jvmAndPosixTest { + commonTest { dependencies { - runtimeOnly(project(":ktor-client:ktor-client-cio")) + api(project(":ktor-client:ktor-client-cio")) } } diff --git a/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/ClientLoader.kt b/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/ClientLoader.kt index ecd84cc776..da7acf100b 100644 --- a/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/ClientLoader.kt +++ b/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/ClientLoader.kt @@ -6,11 +6,18 @@ package io.ktor.client.tests.utils import io.ktor.client.engine.* import kotlinx.coroutines.test.* +import kotlin.time.* +import kotlin.time.Duration.Companion.minutes + +internal expect val enginesToTest: Iterable> +internal expect val platformName: String +internal expect fun platformDumpCoroutines() +internal expect fun platformWaitForAllCoroutines() /** * Helper interface to test client. */ -expect abstract class ClientLoader(timeoutSeconds: Int = 60) { +abstract class ClientLoader(private val timeout: Duration = 1.minutes) { /** * Perform test against all clients from dependencies. */ @@ -19,10 +26,108 @@ expect abstract class ClientLoader(timeoutSeconds: Int = 60) { onlyWithEngine: String? = null, retries: Int = 1, block: suspend TestClientBuilder.() -> Unit - ): TestResult + ): TestResult = runTest(timeout = timeout) { + val skipPatterns = skipEngines.map { SkipEnginePattern.parse(it.lowercase()) } + + val failures: List = enginesToTest.mapNotNull { engineFactory -> + val engineName = engineFactory.toString().lowercase() + + if (shouldRun(engineName, skipPatterns, onlyWithEngine?.lowercase())) { + try { + println("Run test with engine $engineName") + // run test here + performTestWithEngine(engineFactory, this@ClientLoader, retries, block) + null // engine test passed + } catch (cause: Throwable) { + // engine test failed, save failure to report after run for every engine. + TestFailure(engineName, cause) + } + } else { + println("Skipping test with engine $engineName") + null // engine skipped + } + } + + if (failures.isNotEmpty()) { + val message = buildString { + appendLine("Test failed for engines: ${failures.map { it.engineName }}") + failures.forEach { + appendLine("Test failed for engine '$platformName:${it.engineName}' with:") + appendLine(it.cause.stackTraceToString().prependIndent(" ")) + } + } + throw AssertionError(message) + } + } + + private fun shouldRun( + engineName: String, + skipEnginePatterns: List, + onlyWithEngine: String? + ): Boolean { + if (onlyWithEngine != null && onlyWithEngine != engineName) return false + + skipEnginePatterns.forEach { + if (it.matches(engineName)) return false + } + + return true + } /** * Print coroutines in debug mode. */ - fun dumpCoroutines() + fun dumpCoroutines(): Unit = platformDumpCoroutines() + + /** + * Issues to fix before unlock: + * 1. Pinger & Ponger in ws + * 2. Nonce generator + */ + // @After + fun waitForAllCoroutines(): Unit = platformWaitForAllCoroutines() +} + +private data class SkipEnginePattern( + val skippedPlatform: String?, // null means * or empty + val skippedEngine: String?, // null means * or empty +) { + fun matches(engineName: String): Boolean { + var result = true + if (skippedEngine != null) { + result = result && engineName == skippedEngine + } + if (result && skippedPlatform != null) { + result = result && platformName.startsWith(skippedPlatform) + } + return result + } + + companion object { + fun parse(pattern: String): SkipEnginePattern { + val parts = pattern.split(":").map { it.takeIf { it != "*" } } + val platform: String? + val engine: String? + when (parts.size) { + 1 -> { + platform = null + engine = parts[0] + } + + 2 -> { + platform = parts[0] + engine = parts[1] + } + + else -> error("Skip engine pattern should consist of two parts: PLATFORM:ENGINE or ENGINE") + } + + if (platform == null && engine == null) { + error("Skip engine pattern should consist of two parts: PLATFORM:ENGINE or ENGINE") + } + return SkipEnginePattern(platform, engine) + } + } } + +private class TestFailure(val engineName: String, val cause: Throwable) diff --git a/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/CommonClientTestUtils.kt b/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/CommonClientTestUtils.kt index 737fa5612c..1036094a60 100644 --- a/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/CommonClientTestUtils.kt +++ b/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/CommonClientTestUtils.kt @@ -2,14 +2,12 @@ * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ -@file:Suppress("NO_EXPLICIT_RETURN_TYPE_IN_API_MODE_WARNING", "KDocMissingDocumentation") - package io.ktor.client.tests.utils import io.ktor.client.* import io.ktor.client.engine.* import kotlinx.coroutines.* -import kotlinx.coroutines.test.* +import kotlinx.coroutines.test.runTest import kotlin.time.Duration.Companion.milliseconds /** @@ -64,7 +62,6 @@ private fun testWithClient( /** * Perform test with selected client engine [factory]. */ -@OptIn(DelicateCoroutinesApi::class) fun testWithEngine( factory: HttpClientEngineFactory, loader: ClientLoader? = null, @@ -72,6 +69,16 @@ fun testWithEngine( retries: Int = 1, block: suspend TestClientBuilder.() -> Unit ) = runTest(timeout = timeoutMillis.milliseconds) { + performTestWithEngine(factory, loader, retries, block) +} + +@OptIn(DelicateCoroutinesApi::class) +suspend fun performTestWithEngine( + factory: HttpClientEngineFactory, + loader: ClientLoader? = null, + retries: Int = 1, + block: suspend TestClientBuilder.() -> Unit +) { val builder = TestClientBuilder().apply { block() } if (builder.dumpAfterDelay > 0 && loader != null) { diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/BodyProgressTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/BodyProgressTest.kt index 41c47af0e3..34d286b0b8 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/BodyProgressTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/BodyProgressTest.kt @@ -28,7 +28,7 @@ private val TEST_ARRAY = ByteArray(8 * 1025) { 1 } private val TEST_NAME = "123".repeat(5000) @OptIn(DelicateCoroutinesApi::class) -class BodyProgressTest : ClientLoader(timeoutSeconds = 60) { +class BodyProgressTest : ClientLoader() { @Serializable data class User(val login: String, val id: Long) diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/ContentTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/ContentTest.kt index e0a79af01a..2ea26cb165 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/ContentTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/ContentTest.kt @@ -42,7 +42,7 @@ val testArrays = testSize.map { makeArray(it) } -class ContentTest : ClientLoader(5 * 60) { +class ContentTest : ClientLoader(5.minutes) { @Test fun testGetFormData() = clientTests { diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/EventsTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/EventsTest.kt index 55195d6d08..fbf9b12ff0 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/EventsTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/EventsTest.kt @@ -74,7 +74,7 @@ class EventsTest : ClientLoader() { } @Test - fun testRedirectEvent() = clientTests(listOf("js")) { + fun testRedirectEvent() = clientTests(listOf("Js")) { test { client -> counter.value = 0 client.monitor.subscribe(HttpResponseRedirectEvent) { diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/HttpStatementTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/HttpStatementTest.kt index 0371b934c7..00b8f69fd3 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/HttpStatementTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/HttpStatementTest.kt @@ -38,7 +38,7 @@ class HttpStatementTest : ClientLoader() { } @Test - fun testGZipFromSavedResponse() = clientTests(listOf("native:CIO")) { + fun testGZipFromSavedResponse() = clientTests(listOf("native:CIO", "web:CIO")) { config { ContentEncoding { gzip() diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/HttpTimeoutTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/HttpTimeoutTest.kt index 5d2e93d892..4b15768957 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/HttpTimeoutTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/HttpTimeoutTest.kt @@ -327,7 +327,7 @@ class HttpTimeoutTest : ClientLoader() { // Fix https://youtrack.jetbrains.com/issue/KTOR-7885 @Ignore @Test - fun testRedirect() = clientTests(listOf("js"), retries = 5) { + fun testRedirect() = clientTests(listOf("Js"), retries = 5) { config { install(HttpTimeout) { requestTimeoutMillis = 10000 } } @@ -344,7 +344,7 @@ class HttpTimeoutTest : ClientLoader() { // Js can't configure test timeout in browser @Test - fun testRedirectPerRequestAttributes() = clientTests(listOf("js")) { + fun testRedirectPerRequestAttributes() = clientTests(listOf("Js")) { config { install(HttpTimeout) } @@ -429,7 +429,7 @@ class HttpTimeoutTest : ClientLoader() { } @Test - fun testConnectionRefusedException() = clientTests(listOf("Js", "native:*", "win:*")) { + fun testConnectionRefusedException() = clientTests(listOf("Js", "native:*", "jvm/win:*")) { config { install(HttpTimeout) { connectTimeoutMillis = 1000 } } @@ -445,7 +445,7 @@ class HttpTimeoutTest : ClientLoader() { } @Test - fun testSocketTimeoutRead() = clientTests(listOf("Js", "native:CIO", "Java")) { + fun testSocketTimeoutRead() = clientTests(listOf("Js", "native:CIO", "Curl", "Java")) { config { install(HttpTimeout) { socketTimeoutMillis = 1000 } } @@ -460,7 +460,9 @@ class HttpTimeoutTest : ClientLoader() { } @Test - fun testSocketTimeoutReadPerRequestAttributes() = clientTests(listOf("Js", "native:CIO", "Java", "Apache5")) { + fun testSocketTimeoutReadPerRequestAttributes() = clientTests( + listOf("Js", "native:CIO", "Curl", "Java", "Apache5") + ) { config { install(HttpTimeout) } @@ -477,7 +479,9 @@ class HttpTimeoutTest : ClientLoader() { } @Test - fun testSocketTimeoutWriteFailOnWrite() = clientTests(listOf("Js", "Android", "native:CIO", "Java")) { + fun testSocketTimeoutWriteFailOnWrite() = clientTests( + listOf("Js", "Android", "Curl", "native:CIO", "web:CIO", "Java") + ) { config { install(HttpTimeout) { socketTimeoutMillis = 500 } } @@ -491,7 +495,7 @@ class HttpTimeoutTest : ClientLoader() { @Test fun testSocketTimeoutWriteFailOnWritePerRequestAttributes() = clientTests( - listOf("Js", "Android", "Apache5", "native:CIO", "Java") + listOf("Js", "Android", "Apache5", "Curl", "native:CIO", "web:CIO", "Java") ) { config { install(HttpTimeout) diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/JsonTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/JsonTest.kt index 58b59a3ed9..1cd73017f7 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/JsonTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/JsonTest.kt @@ -22,7 +22,7 @@ class JsonTest : ClientLoader() { @OptIn(ExperimentalStdlibApi::class) @Test - fun testUserGenerics() = clientTests(listOf("js")) { + fun testUserGenerics() = clientTests(listOf("Js")) { config { install(ContentNegotiation) { json() } } diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/LoggingTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/LoggingTest.kt index 2b5eab275d..5181c1188e 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/LoggingTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/LoggingTest.kt @@ -351,7 +351,7 @@ class LoggingTest : ClientLoader() { } @Test - fun testLoggingWithCompression() = clientTests(listOf("native:CIO")) { + fun testLoggingWithCompression() = clientTests(listOf("Darwin", "native:CIO", "DarwinLegacy", "web:CIO")) { val testLogger = TestLogger( "REQUEST: http://127.0.0.1:8080/compression/deflate", "METHOD: HttpMethod(value=GET)", diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/MultiPartFormDataTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/MultiPartFormDataTest.kt index 74cc70ebca..462c2b552c 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/MultiPartFormDataTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/MultiPartFormDataTest.kt @@ -25,7 +25,7 @@ class MultiPartFormDataTest : ClientLoader() { } @Test - fun testMultiPartFormData() = clientTests(listOf("native")) { + fun testMultiPartFormData() = clientTests(listOf("native:*")) { test { client -> val result = client.preparePost("$TEST_SERVER/multipart") { setBody( diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/ProxyTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/ProxyTest.kt index 970099950b..32f28ce918 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/ProxyTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/ProxyTest.kt @@ -19,7 +19,7 @@ data class ProxyResponse(val status: String) class ProxyTest : ClientLoader() { @Test - fun testHttpProxy() = clientTests(listOf("Js")) { + fun testHttpProxy() = clientTests(listOf("Js", "web:CIO")) { config { engine { proxy = ProxyBuilder.http(TCP_SERVER) @@ -33,7 +33,7 @@ class ProxyTest : ClientLoader() { } @Test - fun testProxyWithSerialization() = clientTests(listOf("Js")) { + fun testProxyWithSerialization() = clientTests(listOf("Js", "web:CIO")) { config { engine { proxy = ProxyBuilder.http(TCP_SERVER) diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/WebSocketTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/WebSocketTest.kt index 21fe8656af..144970bddb 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/WebSocketTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/WebSocketTest.kt @@ -329,7 +329,7 @@ class WebSocketTest : ClientLoader() { @Ignore // TODO KTOR-7088 @Test fun testImmediateReceiveAfterConnect() = clientTests( - ENGINES_WITHOUT_WS + "Darwin" + "js" // TODO KTOR-7088 + ENGINES_WITHOUT_WS + "Darwin" + "Js" // TODO KTOR-7088 ) { config { install(WebSockets) diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CacheLegacyStorageTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CacheLegacyStorageTest.kt index 536c3b38e2..5e3f7f26c4 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CacheLegacyStorageTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CacheLegacyStorageTest.kt @@ -569,7 +569,7 @@ class CacheLegacyStorageTest : ClientLoader() { } @Test - fun testPublicAndPrivateCache() = clientTests(listOf("native")) { + fun testPublicAndPrivateCache() = clientTests(listOf("native:*")) { val publicStorage = HttpCacheStorage.Unlimited() val privateStorage = HttpCacheStorage.Unlimited() config { diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CookiesIntegrationTests.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CookiesIntegrationTests.kt index 33362690dd..6220897275 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CookiesIntegrationTests.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CookiesIntegrationTests.kt @@ -106,7 +106,7 @@ class CookiesIntegrationTests : ClientLoader() { } @Test - fun testPath() = clientTests(listOf("js")) { + fun testPath() = clientTests(listOf("Js")) { config { install(HttpCookies) } diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt index 5d3fcfc522..409cfa62a1 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt @@ -23,8 +23,9 @@ import kotlinx.coroutines.flow.* import kotlin.coroutines.* import kotlin.test.* import kotlin.test.assertFailsWith +import kotlin.time.Duration.Companion.minutes -class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { +class ServerSentEventsTest : ClientLoader(2.minutes) { @Test fun testExceptionIfSseIsNotInstalled() = testSuspend { diff --git a/ktor-client/ktor-client-tests/js/src/io/ktor/client/tests/utils/ClientLoaderJs.kt b/ktor-client/ktor-client-tests/js/src/io/ktor/client/tests/utils/ClientLoaderJs.kt deleted file mode 100644 index cc158c7b8c..0000000000 --- a/ktor-client/ktor-client-tests/js/src/io/ktor/client/tests/utils/ClientLoaderJs.kt +++ /dev/null @@ -1,38 +0,0 @@ -// ktlint-disable filename -/* - * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -package io.ktor.client.tests.utils - -import io.ktor.client.engine.* -import io.ktor.client.engine.js.* -import kotlinx.coroutines.* -import kotlinx.coroutines.test.* - -/** - * Helper interface to test client. - */ -actual abstract class ClientLoader actual constructor(private val timeoutSeconds: Int) { - /** - * Perform test against all clients from dependencies. - */ - @OptIn(DelicateCoroutinesApi::class) - actual fun clientTests( - skipEngines: List, - onlyWithEngine: String?, - retries: Int, - block: suspend TestClientBuilder.() -> Unit - ): TestResult { - val skipEnginesLowerCase = skipEngines.map { it.lowercase() } - if ((onlyWithEngine != null && onlyWithEngine != "js") || skipEnginesLowerCase.contains("js")) { - return runTest { } - } - - return testWithEngine(Js, retries = retries, timeoutMillis = timeoutSeconds * 1000L, block = block) - } - - actual fun dumpCoroutines() { - error("Debug probes unsupported[js]") - } -} diff --git a/ktor-client/ktor-client-tests/jsAndWasmShared/src/io/ktor/client/tests/utils/ClientLoader.jsAndWasmShared.kt b/ktor-client/ktor-client-tests/jsAndWasmShared/src/io/ktor/client/tests/utils/ClientLoader.jsAndWasmShared.kt new file mode 100644 index 0000000000..81f38bb16f --- /dev/null +++ b/ktor-client/ktor-client-tests/jsAndWasmShared/src/io/ktor/client/tests/utils/ClientLoader.jsAndWasmShared.kt @@ -0,0 +1,13 @@ +// ktlint-disable filename +/* + * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.client.tests.utils + +import io.ktor.client.engine.* +import io.ktor.utils.io.* + +@OptIn(InternalAPI::class) +internal actual val enginesToTest: Iterable> get() = engines +internal actual val platformName: String get() = "web" diff --git a/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoader.jvm.kt b/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoader.jvm.kt new file mode 100644 index 0000000000..b88241c364 --- /dev/null +++ b/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoader.jvm.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.client.tests.utils + +import io.ktor.client.* +import io.ktor.client.engine.* +import io.ktor.util.reflect.* +import io.ktor.utils.io.* +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.debug.CoroutineInfo +import kotlinx.coroutines.debug.DebugProbes +import java.util.* + +@OptIn(InternalAPI::class) +internal actual val enginesToTest: Iterable> by lazy { + loadServices().map { it.factory } +} + +internal actual val platformName: String by lazy { + val os = System.getProperty("os.name", "unknown").lowercase(Locale.getDefault()) + "jvm/" + when { + os.contains("win") -> "win" + os.contains("mac") -> "mac" + os.contains("nux") -> "unix" + else -> "unknown" + } +} + +@OptIn(ExperimentalCoroutinesApi::class) +internal actual fun platformDumpCoroutines() { + DebugProbes.dumpCoroutines() + + println("Thread Dump") + Thread.getAllStackTraces().forEach { (thread, stackTrace) -> + println("Thread: $thread") + stackTrace.forEach { + println("\t$it") + } + } +} + +@OptIn(ExperimentalCoroutinesApi::class) +internal actual fun platformWaitForAllCoroutines() { + check(DebugProbes.isInstalled) { + "Debug probes isn't installed." + } + + val info = DebugProbes.dumpCoroutinesInfo() + + if (info.isEmpty()) { + return + } + + val message = buildString { + appendLine("Test failed. There are running coroutines") + appendLine(info.dump()) + } + + error(message) +} + +@OptIn(ExperimentalCoroutinesApi::class) +private fun List.dump(): String = buildString { + this@dump.forEach { info -> + appendLine("Coroutine: $info") + info.lastObservedStackTrace().forEach { + appendLine("\t$it") + } + } +} diff --git a/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt b/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt deleted file mode 100644 index 53f655685b..0000000000 --- a/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -package io.ktor.client.tests.utils - -import io.ktor.client.* -import io.ktor.client.engine.* -import io.ktor.util.reflect.* -import io.ktor.utils.io.* -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.debug.CoroutineInfo -import kotlinx.coroutines.debug.DebugProbes -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withTimeout -import java.util.* -import kotlin.time.Duration.Companion.seconds - -/** - * Helper interface to test client. - */ -actual abstract class ClientLoader actual constructor(val timeoutSeconds: Int) { - - @OptIn(InternalAPI::class) - private val engines: List by lazy { loadServices() } - - /** - * Perform test against all clients from dependencies. - */ - @OptIn(ExperimentalCoroutinesApi::class) - actual fun clientTests( - skipEngines: List, - onlyWithEngine: String?, - retries: Int, - block: suspend TestClientBuilder.() -> Unit - ) { - DebugProbes.install() - for (engine in engines) { - if (shouldSkip(engine, skipEngines, onlyWithEngine)) { - continue - } - runBlocking { - withTimeout(timeoutSeconds.seconds.inWholeMilliseconds) { - testWithEngine(engine.factory, this@ClientLoader, timeoutSeconds * 1000L, retries, block) - } - } - } - } - - private fun shouldSkip( - engine: HttpClientEngineContainer, - skipEngines: List, - onlyWithEngine: String? - ): Boolean { - val engineName = engine.toString() - return onlyWithEngine != null && !onlyWithEngine.equals(engineName, ignoreCase = true) || - skipEngines.any { shouldSkip(engineName, it) } - } - - private fun shouldSkip(engineName: String, skipEngine: String): Boolean { - val locale = Locale.getDefault() - val skipEngineArray = skipEngine.lowercase(locale).split(":") - - val (platform, skipEngineName) = when (skipEngineArray.size) { - 2 -> skipEngineArray[0] to skipEngineArray[1] - 1 -> "*" to skipEngineArray[0] - else -> throw IllegalStateException("Wrong skip engine format, expected 'engine' or 'platform:engine'") - } - - val platformShouldBeSkipped = "*" == platform || OS_NAME == platform - val engineShouldBeSkipped = "*" == skipEngineName || engineName.lowercase(locale) == skipEngineName.lowercase( - locale - ) - - return engineShouldBeSkipped && platformShouldBeSkipped - } - - @OptIn(ExperimentalCoroutinesApi::class) - actual fun dumpCoroutines() { - DebugProbes.dumpCoroutines() - - println("Thread Dump") - Thread.getAllStackTraces().forEach { (thread, stackTrace) -> - println("Thread: $thread") - stackTrace.forEach { - println("\t$it") - } - } - } - - /** - * Issues to fix before unlock: - * 1. Pinger & Ponger in ws - * 2. Nonce generator - */ - // @After - @OptIn(ExperimentalCoroutinesApi::class) - fun waitForAllCoroutines() { - check(DebugProbes.isInstalled) { - "Debug probes isn't installed." - } - - val info = DebugProbes.dumpCoroutinesInfo() - - if (info.isEmpty()) { - return - } - - val message = buildString { - appendLine("Test failed. There are running coroutines") - appendLine(info.dump()) - } - - error(message) - } -} - -private val OS_NAME: String - get() { - val os = System.getProperty("os.name", "unknown").lowercase(Locale.getDefault()) - return when { - os.contains("win") -> "win" - os.contains("mac") -> "mac" - os.contains("nux") -> "unix" - else -> "unknown" - } - } - -@OptIn(ExperimentalCoroutinesApi::class) -private fun List.dump(): String = buildString { - this@dump.forEach { info -> - appendLine("Coroutine: $info") - info.lastObservedStackTrace().forEach { - appendLine("\t$it") - } - } -} diff --git a/ktor-client/ktor-client-tests/jvm/test/io/ktor/client/tests/LoggingTestJvm.kt b/ktor-client/ktor-client-tests/jvm/test/io/ktor/client/tests/LoggingTestJvm.kt index 8862a94c1b..6845097a06 100644 --- a/ktor-client/ktor-client-tests/jvm/test/io/ktor/client/tests/LoggingTestJvm.kt +++ b/ktor-client/ktor-client-tests/jvm/test/io/ktor/client/tests/LoggingTestJvm.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.* import kotlinx.coroutines.slf4j.* import org.slf4j.* import kotlin.test.* +import kotlin.time.Duration.Companion.seconds private class LoggerWithMdc : Logger { val logs = mutableListOf>() @@ -27,7 +28,7 @@ private class LoggerWithMdc : Logger { } } -class LoggingTestJvm : ClientLoader(timeoutSeconds = 1000000) { +class LoggingTestJvm : ClientLoader(1000000.seconds) { @OptIn(InternalAPI::class) @Test diff --git a/ktor-client/ktor-client-tests/jvm/test/io/ktor/client/tests/MultithreadedTest.kt b/ktor-client/ktor-client-tests/jvm/test/io/ktor/client/tests/MultithreadedTest.kt index e6caa8229a..9f6f199676 100644 --- a/ktor-client/ktor-client-tests/jvm/test/io/ktor/client/tests/MultithreadedTest.kt +++ b/ktor-client/ktor-client-tests/jvm/test/io/ktor/client/tests/MultithreadedTest.kt @@ -10,11 +10,12 @@ import io.ktor.client.tests.utils.* import kotlinx.coroutines.* import java.util.concurrent.* import kotlin.test.* +import kotlin.time.Duration.Companion.minutes private const val TEST_SIZE = 100_000 private const val DEFAULT_THREADS_COUNT = 32 -class MultithreadedTest : ClientLoader(timeoutSeconds = 10 * 60) { +class MultithreadedTest : ClientLoader(10.minutes) { @Test @Ignore fun numberTest() = clientTests { diff --git a/ktor-client/ktor-client-tests/jvm/test/io/ktor/client/tests/WebSocketJvmTest.kt b/ktor-client/ktor-client-tests/jvm/test/io/ktor/client/tests/WebSocketJvmTest.kt index af98b6a2af..bb7fa12434 100644 --- a/ktor-client/ktor-client-tests/jvm/test/io/ktor/client/tests/WebSocketJvmTest.kt +++ b/ktor-client/ktor-client-tests/jvm/test/io/ktor/client/tests/WebSocketJvmTest.kt @@ -8,10 +8,11 @@ import io.ktor.client.plugins.websocket.* import io.ktor.client.tests.utils.* import io.ktor.websocket.* import kotlin.test.* +import kotlin.time.Duration.Companion.seconds private const val TEST_SIZE: Int = 100 -class WebSocketJvmTest : ClientLoader(100000) { +class WebSocketJvmTest : ClientLoader(100000.seconds) { @Test fun testWebSocketDeflateBinary() = clientTests(listOf("Android", "Apache", "Apache5")) { diff --git a/ktor-client/ktor-client-tests/nonJvm/src/io/ktor/client/tests/utils/ClientLoader.nonJvm.kt b/ktor-client/ktor-client-tests/nonJvm/src/io/ktor/client/tests/utils/ClientLoader.nonJvm.kt new file mode 100644 index 0000000000..4ea4cbf9b6 --- /dev/null +++ b/ktor-client/ktor-client-tests/nonJvm/src/io/ktor/client/tests/utils/ClientLoader.nonJvm.kt @@ -0,0 +1,9 @@ +/* + * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.client.tests.utils + +// supported only on JVM +internal actual fun platformDumpCoroutines() {} +internal actual fun platformWaitForAllCoroutines() {} diff --git a/ktor-client/ktor-client-tests/posix/src/io/ktor/client/tests/utils/ClientLoader.posix.kt b/ktor-client/ktor-client-tests/posix/src/io/ktor/client/tests/utils/ClientLoader.posix.kt new file mode 100644 index 0000000000..ff21e2b486 --- /dev/null +++ b/ktor-client/ktor-client-tests/posix/src/io/ktor/client/tests/utils/ClientLoader.posix.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.client.tests.utils + +import io.ktor.client.engine.* +import io.ktor.util.* +import io.ktor.utils.io.* +import kotlin.experimental.* +import kotlin.native.runtime.* + +@OptIn(InternalAPI::class) +internal actual val enginesToTest: Iterable> get() = engines +internal actual val platformName: String get() = "native" diff --git a/ktor-client/ktor-client-tests/posix/src/io/ktor/client/tests/utils/ClientLoaderNative.kt b/ktor-client/ktor-client-tests/posix/src/io/ktor/client/tests/utils/ClientLoaderNative.kt deleted file mode 100644 index 153ee6cb6f..0000000000 --- a/ktor-client/ktor-client-tests/posix/src/io/ktor/client/tests/utils/ClientLoaderNative.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -package io.ktor.client.tests.utils - -import io.ktor.client.engine.* -import io.ktor.utils.io.* -import kotlin.experimental.* - -private class TestFailure(val name: String, val cause: Throwable) { - @OptIn(ExperimentalNativeApi::class) - override fun toString(): String = buildString { - appendLine("Test failed with engine: $name") - appendLine(cause) - for (stackline in cause.getStackTrace()) { - appendLine("\t$stackline") - } - } -} - -/** - * Helper interface to test client. - */ -actual abstract class ClientLoader actual constructor(private val timeoutSeconds: Int) { - /** - * Perform test against all clients from dependencies. - */ - @OptIn(InternalAPI::class) - actual fun clientTests( - skipEngines: List, - onlyWithEngine: String?, - retries: Int, - block: suspend TestClientBuilder.() -> Unit - ) { - if (skipEngines.any { it.startsWith("native") }) return - - val skipEnginesLowerCase = skipEngines.map { it.lowercase() }.toSet() - val filteredEngines: List> = engines.filter { - val name = it.toString().lowercase() - !skipEnginesLowerCase.contains(name) && !skipEnginesLowerCase.contains("native:$name") - } - - val failures = mutableListOf() - for (engine in filteredEngines) { - if (onlyWithEngine != null && onlyWithEngine != engine.toString()) continue - - val result = runCatching { - testWithEngine(engine, timeoutMillis = timeoutSeconds.toLong() * 1000L) { - block() - } - } - - if (result.isFailure) { - failures += TestFailure(engine.toString(), result.exceptionOrNull()!!) - } - } - - if (failures.isEmpty()) { - return - } - - error(failures.joinToString("\n")) - } - - actual fun dumpCoroutines() { - error("Debug probes unsupported native.") - } -} diff --git a/ktor-client/ktor-client-tests/wasmJs/src/io/ktor/client/tests/utils/ClientLoaderWasm.kt b/ktor-client/ktor-client-tests/wasmJs/src/io/ktor/client/tests/utils/ClientLoaderWasm.kt deleted file mode 100644 index bac76c033d..0000000000 --- a/ktor-client/ktor-client-tests/wasmJs/src/io/ktor/client/tests/utils/ClientLoaderWasm.kt +++ /dev/null @@ -1,38 +0,0 @@ -// ktlint-disable filename -/* - * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -package io.ktor.client.tests.utils - -import io.ktor.client.engine.* -import io.ktor.client.engine.js.* -import kotlinx.coroutines.* -import kotlinx.coroutines.test.* - -/** - * Helper interface to test client. - */ -actual abstract class ClientLoader actual constructor(private val timeoutSeconds: Int) { - /** - * Perform test against all clients from dependencies. - */ - @OptIn(DelicateCoroutinesApi::class) - actual fun clientTests( - skipEngines: List, - onlyWithEngine: String?, - retries: Int, - block: suspend TestClientBuilder.() -> Unit - ): TestResult { - val skipEnginesLowerCase = skipEngines.map { it.lowercase() } - return if ((onlyWithEngine != null && onlyWithEngine != "js") || skipEnginesLowerCase.contains("js")) { - runTest {} - } else { - testWithEngine(Js, retries = retries, timeoutMillis = timeoutSeconds * 1000L, block = block) - } - } - - actual fun dumpCoroutines() { - error("Debug probes unsupported[wasm]") - } -}