diff --git a/buildSrc/src/main/kotlin/Constants.kt b/buildSrc/src/main/kotlin/Constants.kt index a3b0fe6..e19bc1c 100644 --- a/buildSrc/src/main/kotlin/Constants.kt +++ b/buildSrc/src/main/kotlin/Constants.kt @@ -1,7 +1,7 @@ object Consts { const val releaseGroup = "com.piasy" const val releaseName = "kmp-socketio" - const val releaseVersion = "1.4.3" + const val releaseVersion = "1.4.4" val androidNS = "$releaseGroup.${releaseName.replace('-', '.')}" } diff --git a/kmp-socketio/src/commonMain/kotlin/com/piasy/kmp/socketio/socketio/Socket.kt b/kmp-socketio/src/commonMain/kotlin/com/piasy/kmp/socketio/socketio/Socket.kt index b8ccb3e..f5e3c18 100644 --- a/kmp-socketio/src/commonMain/kotlin/com/piasy/kmp/socketio/socketio/Socket.kt +++ b/kmp-socketio/src/commonMain/kotlin/com/piasy/kmp/socketio/socketio/Socket.kt @@ -17,7 +17,11 @@ class Socket( private val auth: Map, private val scope: CoroutineScope, ) : Emitter() { - private var connected = false + /** + * Whether this socket namespace is currently connected. + */ + var connected = false + private set private val subs = ArrayList() private val ack = HashMap() private var ackId = 0 diff --git a/kmp-socketio/src/commonTest/kotlin/com/piasy/kmp/socketio/socketio/ConnectionTest.kt b/kmp-socketio/src/commonTest/kotlin/com/piasy/kmp/socketio/socketio/ConnectionTest.kt index 83774ba..d5f699d 100644 --- a/kmp-socketio/src/commonTest/kotlin/com/piasy/kmp/socketio/socketio/ConnectionTest.kt +++ b/kmp-socketio/src/commonTest/kotlin/com/piasy/kmp/socketio/socketio/ConnectionTest.kt @@ -76,6 +76,31 @@ abstract class ConnectionTest { assertEquals(now.toString(), args[3]) } + @Test + fun shouldExposeConnectionState() = doTest { + val isConnectedBeforeOpen = CompletableDeferred() + val isConnectedWhenConnectedEvent = CompletableDeferred() + val isConnectedWhenDisconnectedEvent = CompletableDeferred() + + val opt = IO.Options() + opt.transports = listOf(WebSocket.NAME) + client(opt = opt) { socket -> + isConnectedBeforeOpen.complete(socket.connected) + socket.on(Socket.EVENT_CONNECT) { + isConnectedWhenConnectedEvent.complete(socket.connected) + socket.close() + }.on(Socket.EVENT_DISCONNECT) { + isConnectedWhenDisconnectedEvent.complete(socket.connected) + } + + socket.open() + } + + assertFalse(isConnectedBeforeOpen.await()) + assertTrue(isConnectedWhenConnectedEvent.await()) + assertFalse(isConnectedWhenDisconnectedEvent.await()) + } + @Test open fun shouldConnectUntrusted() = doTest { val trustAllCertsHttpClientFactory = DefaultHttpClientFactory( diff --git a/kmp-socketio/src/jvmTest/kotlin/com/piasy/kmp/socketio/socketio/ConnectionTestJvm.kt b/kmp-socketio/src/jvmTest/kotlin/com/piasy/kmp/socketio/socketio/ConnectionTestJvm.kt new file mode 100644 index 0000000..1c3b31b --- /dev/null +++ b/kmp-socketio/src/jvmTest/kotlin/com/piasy/kmp/socketio/socketio/ConnectionTestJvm.kt @@ -0,0 +1,99 @@ +package com.piasy.kmp.socketio.socketio + +import com.piasy.kmp.socketio.engineio.transports.WebSocket +import com.piasy.kmp.xlog.Logging +import java.io.BufferedReader +import java.io.InputStreamReader +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import kotlin.test.assertFalse +import kotlin.test.assertTrue +import kotlin.concurrent.thread +import kotlin.test.Test +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.withContext +import kotlin.time.Duration.Companion.seconds + +class ConnectionTestJvm { + private var server: Process? = null + private var serverOutputThread: Thread? = null + private val serverReady = CountDownLatch(1) + + @BeforeTest + fun startServer() { + Logging.info(TAG, "startServer") + val process = ProcessBuilder("node", "src/jvmTest/resources/socket-server.js", "/") + .redirectErrorStream(true) + .start() + server = process + serverOutputThread = thread(start = true, isDaemon = true, name = "socket-server-jvm-test-stdout") { + BufferedReader(InputStreamReader(process.inputStream)).use { reader -> + while (true) { + val line = reader.readLine() ?: break + Logging.info(TAG, "SERVER OUT: $line") + if (line.contains("Socket.IO server listening on port 3000")) { + serverReady.countDown() + } + } + } + } + check(serverReady.await(3, TimeUnit.SECONDS)) { "Socket.IO test server did not start in 3s" } + Logging.info(TAG, "startServer finish") + } + + @AfterTest + fun stopServer() { + Logging.info(TAG, "stopServer") + serverReady.countDown() + server?.let { process -> + process.destroy() + if (!process.waitFor(3, TimeUnit.SECONDS)) { + process.destroyForcibly() + process.waitFor(3, TimeUnit.SECONDS) + } + } + server = null + + serverOutputThread?.join(1000) + serverOutputThread = null + Logging.info(TAG, "stopServer finish") + } + + @Test + fun shouldExposeConnectionState() = runTest(timeout = 10.seconds) { + withContext(Dispatchers.Default) { + delay(1000) + } + + val isConnectedBeforeOpen = CompletableDeferred() + val isConnectedWhenConnectedEvent = CompletableDeferred() + val isConnectedWhenDisconnectedEvent = CompletableDeferred() + + val opt = IO.Options() + opt.transports = listOf(WebSocket.NAME) + IO.socket("http://localhost:3000/", opt) { socket -> + isConnectedBeforeOpen.complete(socket.connected) + socket.on(Socket.EVENT_CONNECT) { + isConnectedWhenConnectedEvent.complete(socket.connected) + socket.close() + }.on(Socket.EVENT_DISCONNECT) { + isConnectedWhenDisconnectedEvent.complete(socket.connected) + } + + socket.open() + } + + assertFalse(isConnectedBeforeOpen.await()) + assertTrue(isConnectedWhenConnectedEvent.await()) + assertFalse(isConnectedWhenDisconnectedEvent.await()) + } + + companion object { + private const val TAG = "ConnectionTestJvm" + } +}