- Dart Web 브라우저 E2E 테스트 프레임워크 추가 - browser_ws_dart_io_test, browser_ws_kotlin_test 등 브라우저 통합 테스트 추가 - Dart Web 크로스테스트 케이스 추가 (go, kotlin, python, typescript) - TypeScript 브라우저 웹소켓 클라이언트 개선 - Node.js 웹소켓 서버 클라이언트 업데이트 - 테스트 실행 매트릭스 스킬 및 스크립트 업데이트 - README.md 문서 업데이트
125 lines
4.4 KiB
Kotlin
125 lines
4.4 KiB
Kotlin
@file:JvmName("KotlinDartWebKt")
|
|
|
|
package com.tokilabs.proto_socket.crosstest
|
|
|
|
import com.tokilabs.proto_socket.ParserMap
|
|
import com.tokilabs.proto_socket.WsClient
|
|
import com.tokilabs.proto_socket.WsServer
|
|
import com.tokilabs.proto_socket.addRequestListenerTyped
|
|
import com.tokilabs.proto_socket.typeNameOf
|
|
import com.tokilabs.proto_socket.packets.TestData
|
|
import kotlinx.coroutines.CompletableDeferred
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.coroutineScope
|
|
import kotlinx.coroutines.delay
|
|
import kotlinx.coroutines.launch
|
|
import kotlinx.coroutines.runBlocking
|
|
import kotlinx.coroutines.withContext
|
|
import kotlinx.coroutines.withTimeoutOrNull
|
|
import java.io.File
|
|
import java.util.concurrent.TimeUnit
|
|
import kotlin.system.exitProcess
|
|
|
|
private const val HOST = "127.0.0.1"
|
|
private const val WS_PORT = 29498
|
|
private const val WS_PATH = "/"
|
|
private const val PROCESS_TIMEOUT_MS = 60_000L
|
|
private const val SERVER_OBSERVATION_MS = 500L
|
|
private const val STREAM_JOIN_TIMEOUT_MS = 5_000L
|
|
|
|
fun main() = runBlocking {
|
|
println("INFO typeName kotlin=${typeNameOf<TestData>()}")
|
|
try {
|
|
runWebWs()
|
|
println("PASS scenario=send-push")
|
|
println("PASS scenario=request-response")
|
|
println("PASS all kotlin-server/dart-web-client crosstests passed")
|
|
} catch (error: Throwable) {
|
|
System.err.println("FAIL crosstest error=${error.message ?: error}")
|
|
exitProcess(1)
|
|
}
|
|
}
|
|
|
|
private suspend fun runWebWs() = coroutineScope {
|
|
val received = CompletableDeferred<Boolean>()
|
|
val server = WsServer(HOST, WS_PORT, WS_PATH) { conn ->
|
|
WsClient.forServer(conn, 0, 0, parserMap())
|
|
}
|
|
server.onClientConnected = { client ->
|
|
addRequestListenerTyped<TestData, TestData>(client.communicator) { req ->
|
|
println("SERVER_RECEIVED index=${req.index} message=${req.message}")
|
|
if (req.index == 101 && req.message == "fire from dart web client") {
|
|
received.complete(true)
|
|
launch {
|
|
client.send(
|
|
TestData.newBuilder()
|
|
.setIndex(200)
|
|
.setMessage("push from kotlin server")
|
|
.build(),
|
|
)
|
|
}
|
|
}
|
|
TestData.newBuilder()
|
|
.setIndex(req.index * 2)
|
|
.setMessage("echo: ${req.message}")
|
|
.build()
|
|
}
|
|
}
|
|
server.start()
|
|
delay(100)
|
|
try {
|
|
runDartBrowserTest("test/browser_ws_kotlin_test.dart")
|
|
val ok = withTimeoutOrNull(SERVER_OBSERVATION_MS) { received.await() } ?: false
|
|
check(ok) { "WS web send-push server did not receive expected data" }
|
|
} finally {
|
|
server.stop()
|
|
}
|
|
}
|
|
|
|
private fun destroyProcessTree(process: Process) {
|
|
process.toHandle().descendants().forEach { it.destroyForcibly() }
|
|
process.destroyForcibly()
|
|
}
|
|
|
|
private suspend fun runDartBrowserTest(testPath: String) = withContext(Dispatchers.IO) {
|
|
val process = ProcessBuilder("dart", "test", "-p", "chrome", testPath)
|
|
.directory(dartDir())
|
|
.redirectErrorStream(false)
|
|
.start()
|
|
|
|
val stdoutThread = Thread {
|
|
process.inputStream.bufferedReader().forEachLine { line -> println(line) }
|
|
}
|
|
val stderrThread = Thread {
|
|
process.errorStream.bufferedReader().forEachLine { line -> System.err.println(line) }
|
|
}
|
|
stdoutThread.start()
|
|
stderrThread.start()
|
|
|
|
val finished = process.waitFor(PROCESS_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
|
if (!finished) {
|
|
destroyProcessTree(process)
|
|
}
|
|
stdoutThread.join(STREAM_JOIN_TIMEOUT_MS)
|
|
stderrThread.join(STREAM_JOIN_TIMEOUT_MS)
|
|
check(!stdoutThread.isAlive && !stderrThread.isAlive) {
|
|
"dart browser test stream readers did not finish"
|
|
}
|
|
val exitCode = if (finished) process.exitValue() else -1
|
|
check(exitCode == 0) { "dart browser test failed exitCode=$exitCode" }
|
|
}
|
|
|
|
private fun parserMap(): ParserMap =
|
|
mapOf(typeNameOf<TestData>() to { TestData.parseFrom(it) })
|
|
|
|
private fun dartDir(): File {
|
|
var dir = File(System.getProperty("user.dir")).absoluteFile
|
|
while (dir.parentFile != null) {
|
|
val candidate = File(dir, "../dart").canonicalFile
|
|
if (File(candidate, "pubspec.yaml").isFile) return candidate
|
|
val direct = File(dir, "dart").canonicalFile
|
|
if (File(direct, "pubspec.yaml").isFile) return direct
|
|
dir = dir.parentFile
|
|
}
|
|
error("cannot resolve dart directory")
|
|
}
|