@file:OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class, ExperimentalUnsignedTypes::class) import kotlinx.coroutines.* import kotlinx.coroutines.test.* import net.sergeych.crypto2.initCrypto import net.sergeych.kiloparsec.* import kotlin.test.* import kotlin.time.Duration.Companion.seconds class TransportAdmissionTest { @Test fun readyCallsDoNotAddPollingDelay() = runTest(timeout = 10.seconds) { val ping by command() val (a, b) = createTestDevice() val local = LocalInterface().apply { on(ping) { it } } val client = Transport(a, local, Unit) val server = Transport(b, local, Unit) val jobs = listOf(launch { client.run() }, launch { server.run() }) try { val start = currentTime repeat(20) { assertEquals(it, client.call(ping, it)) } assertEquals(0L, currentTime - start, "Ready transports must not impose an artificial per-call delay") } finally { jobs.forEach { it.cancelAndJoin() } } } @Test fun successfulPlainPushReturnsNormally() = runTest(timeout = 10.seconds) { val notify by command() val received = CompletableDeferred() val (a, b) = createTestDevice() val client = Transport(a, LocalInterface(), Unit) val server = Transport(b, LocalInterface().apply { on(notify) { received.complete(it); Unit } }, Unit) val jobs = listOf(launch { client.run() }, launch { server.run() }) try { client.push(notify, 7) assertEquals(7, received.await()) } finally { jobs.forEach { it.cancelAndJoin() } } } @Test fun encryptedPushDoesNotWaitForRemoteHandler() = runTest(timeout = 10.seconds) { initCrypto() val notify by command() val ping by command() val entered = CompletableDeferred() val release = CompletableDeferred() val ready = CompletableDeferred() val (a, b) = createTestDevice() val server = KiloServerConnection(KiloInterface().apply { on(notify) { entered.complete(Unit); release.await() } on(ping) { it } }, b, Unit) val client = KiloClientConnection(KiloInterface(), a, Unit) val jobs = listOf(launch { server.run() }, launch { client.run { if (it) ready.complete(Unit) } }) try { ready.await() val notification = async { client.push(notify, 7) } entered.await() runCurrent() assertTrue(notification.isCompleted, "Push must return before the remote handler finishes") notification.await() assertEquals(9, client.call(ping, 9), "A pending notification must not block ordinary RPC") } finally { release.complete(Unit) jobs.forEach { it.cancelAndJoin() } } } }