refactor [lsp]: add tests for the read line func

GitOrigin-RevId: 153cdd1c4f05c31292946809514890384a89ec53
This commit is contained in:
Ludwig Valda Vasquez
2026-02-03 01:26:50 +00:00
committed by intellij-monorepo-bot
parent b212bdd7ac
commit 96728011dc
6 changed files with 293 additions and 0 deletions
+1
View File
@@ -24,6 +24,7 @@
<module fileurl="file://$PROJECT_DIR$/fleet/ktor/network/tls/fleet.ktor.network.tls.iml" filepath="$PROJECT_DIR$/fleet/ktor/network/tls/fleet.ktor.network.tls.iml" />
<module fileurl="file://$PROJECT_DIR$/fleet/ktor/server/cio/fleet.ktor.server.cio.iml" filepath="$PROJECT_DIR$/fleet/ktor/server/cio/fleet.ktor.server.cio.iml" />
<module fileurl="file://$PROJECT_DIR$/fleet/lsp.protocol/fleet.lsp.protocol.iml" filepath="$PROJECT_DIR$/fleet/lsp.protocol/fleet.lsp.protocol.iml" />
<module fileurl="file://$PROJECT_DIR$/fleet/lsp.protocol/test/fleet.lsp.protocol.test.iml" filepath="$PROJECT_DIR$/fleet/lsp.protocol/test/fleet.lsp.protocol.test.iml" />
<module fileurl="file://$PROJECT_DIR$/fleet/modules/api/fleet.modules.api.iml" filepath="$PROJECT_DIR$/fleet/modules/api/fleet.modules.api.iml" />
<module fileurl="file://$PROJECT_DIR$/fleet/modules/jvm/fleet.modules.jvm.iml" filepath="$PROJECT_DIR$/fleet/modules/jvm/fleet.modules.jvm.iml" />
<module fileurl="file://$PROJECT_DIR$/fleet/multiplatform.shims/fleet.multiplatform.shims.iml" filepath="$PROJECT_DIR$/fleet/multiplatform.shims/fleet.multiplatform.shims.iml" />
+1
View File
@@ -240,6 +240,7 @@ fleet/ktor/client/core
fleet/ktor/network/tls
fleet/ktor/server/cio
fleet/lsp.protocol
fleet/lsp.protocol/test
fleet/modules/api
fleet/modules/jvm
fleet/multiplatform.shims
+55
View File
@@ -0,0 +1,55 @@
### auto-generated section `build fleet.lsp.protocol.test` start
load("//build:compiler-options.bzl", "create_kotlinc_options")
load("@rules_jvm//:jvm.bzl", "jvm_library")
create_kotlinc_options(
name = "custom_test",
opt_in = [
"kotlinx.coroutines.ExperimentalCoroutinesApi",
"kotlinx.serialization.ExperimentalSerializationApi",
"kotlin.concurrent.atomics.ExperimentalAtomicApi",
],
x_consistent_data_class_copy_visibility = True,
x_context_parameters = True,
x_lambdas = "class"
)
jvm_library(
name = "test",
visibility = ["//visibility:public"],
srcs = glob([], allow_empty = True, exclude = ["**/module-info.java"]),
runtime_deps = [
"@lib//:kotlin-stdlib",
"@lib//:kotlinx-coroutines-core",
"@lib//:kotlinx-io-core",
]
)
jvm_library(
name = "test_test_lib",
module_name = "fleet.lsp.protocol.test",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java", "src/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]),
kotlinc_opts = ":custom_test",
deps = [
"@lib//:kotlin-stdlib",
"@lib//:kotlinx-coroutines-core",
"@lib//:kotlinx-io-core",
"//fleet/ktor/client/cio",
"//fleet/ktor/client/core",
"//fleet/ktor/network/tls",
"//fleet/lsp.protocol",
"//fleet/test-runtime",
],
runtime_deps = [":test"]
)
### auto-generated section `build fleet.lsp.protocol.test` end
### auto-generated section `test fleet.lsp.protocol.test` start
load("@community//build:tests-options.bzl", "jps_test")
jps_test(
name = "test_test",
runtime_deps = [":test_test_lib"]
)
### auto-generated section `test fleet.lsp.protocol.test` end
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="5" platform="JVM 21" allPlatforms="JVM [21]" useProjectSettings="false">
<compilerSettings>
<option name="additionalArguments" value="-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi -Xlambdas=class -Xconsistent-data-class-copy-visibility -opt-in=kotlinx.serialization.ExperimentalSerializationApi -Xcontext-parameters -Xjvm-default=all -XXLanguage:+AllowEagerSupertypeAccessibilityChecks -opt-in=kotlin.concurrent.atomics.ExperimentalAtomicApi -progressive" />
</compilerSettings>
<compilerArguments>
<stringArguments>
<stringArg name="jvmTarget" arg="21" />
<stringArg name="apiVersion" arg="2.3" />
<stringArg name="languageVersion" arg="2.3" />
</stringArguments>
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="kotlin-stdlib" level="project" />
<orderEntry type="library" name="kotlinx-coroutines-core" level="project" />
<orderEntry type="library" name="kotlinx-io-core" level="project" />
<orderEntry type="module" module-name="fleet.ktor.client.cio" scope="TEST" />
<orderEntry type="module" module-name="fleet.ktor.client.core" scope="TEST" />
<orderEntry type="module" module-name="fleet.ktor.network.tls" scope="TEST" />
<orderEntry type="module" module-name="fleet.lsp.protocol" scope="TEST" />
<orderEntry type="module" module-name="fleet.test.runtime" scope="TEST" />
</component>
</module>
@@ -0,0 +1,72 @@
// IMPORT__MARKER_START
import fleet.buildtool.conventions.configureAtMostOneJvmTargetOrThrow
import fleet.buildtool.conventions.withJavaSourceSet
// IMPORT__MARKER_END
plugins {
alias(libs.plugins.kotlin.multiplatform)
id("fleet.project-module-conventions")
id("fleet.toolchain-conventions")
id("fleet.module-publishing-conventions")
id("fleet.sdk-repositories-publishing-conventions")
id("fleet.open-source-module-conventions")
alias(libs.plugins.dokka)
// GRADLE_PLUGINS__MARKER_START
id("fleet-module")
// GRADLE_PLUGINS__MARKER_END
}
fleetModule {
module {
name = "fleet.lsp.protocol.test"
importedFromJps {}
test {}
}
}
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
kotlin {
// KOTLIN__MARKER_START
compilerOptions.freeCompilerArgs = listOf(
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-Xlambdas=class",
"-Xconsistent-data-class-copy-visibility",
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
"-Xcontext-parameters",
"-Xjvm-default=all",
"-XXLanguage:+AllowEagerSupertypeAccessibilityChecks",
"-opt-in=kotlin.concurrent.atomics.ExperimentalAtomicApi",
"-progressive",
)
jvm {}
sourceSets.jvmTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../src")) }
configureAtMostOneJvmTargetOrThrow { compilations.named("test") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../src")) } } }
sourceSets.commonMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonMain")) }
sourceSets.commonMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonMain")) }
sourceSets.commonTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonTest")) }
sourceSets.commonTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonTest")) }
sourceSets.jvmMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmMain")) }
configureAtMostOneJvmTargetOrThrow { compilations.named("main") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } } }
sourceSets.jvmMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmMain")) }
sourceSets.jvmTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmTest")) }
configureAtMostOneJvmTargetOrThrow { compilations.named("test") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } } }
sourceSets.jvmTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmTest")) }
sourceSets.commonMain.dependencies {
implementation(jps.org.jetbrains.kotlin.kotlin.stdlib1993400674.get().let { "${it.group}:${it.name}:${it.version}" }) {
exclude(group = "org.jetbrains", module = "annotations")
}
implementation(jps.org.jetbrains.intellij.deps.kotlinx.kotlinx.coroutines.core.jvm930800474.get().let { "${it.group}:kotlinx-coroutines-core:${it.version}" }) {
isTransitive = false
}
implementation(jps.org.jetbrains.kotlinx.kotlinx.io.core.jvm479158162.get().let { "${it.group}:kotlinx-io-core:${it.version}" }) {
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
}
}
sourceSets.commonTest.dependencies {
implementation(project(":fleet.ktor.client.cio"))
implementation(project(":fleet.ktor.client.core"))
implementation(project(":fleet.ktor.network.tls"))
implementation(project(":fleet.lsp.protocol"))
implementation(project(":fleet.test.runtime"))
}
// KOTLIN__MARKER_END
}
@@ -0,0 +1,129 @@
package com.jetbrains.lsp.test
import com.jetbrains.lsp.implementation.ByteReader
import com.jetbrains.lsp.implementation.readUTF8Line
import io.ktor.utils.io.*
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.withIndex
import kotlinx.coroutines.runBlocking
import kotlinx.io.Source
import kotlin.test.Test
import kotlin.test.assertEquals
private const val LF = 0x0A.toByte()
private const val CR = 0x0D.toByte()
class ByteReaderTest {
@Test
fun `readUTF8Line reads an LF-terminated line`() {
runBlocking {
val message = "Hello, world!"
val channel = ByteChannel()
val reader = ByteChannelReader(channel)
val line = async { reader.readUTF8Line() }
channel.writeLine(message, LF)
channel.flushAndClose()
assertEquals(message, line.await())
}
}
@Test
fun `readUTF8Line reads an LF-terminated line sent byte by byte`() {
runBlocking {
val message = "Hello, world!"
val channel = ByteChannel()
val reader = ByteChannelReader(channel)
val line = async { reader.readUTF8Line() }
message.toByteArray(Charsets.UTF_8).forEach { byte ->
channel.writeByte(byte)
channel.flush()
}
channel.writeByte(LF)
channel.flushAndClose()
assertEquals(message, line.await())
}
}
@Test
fun `readUTF8Line reads a CRLF-terminated line sent byte by byte`() {
runBlocking {
val message = "Hello, world!"
val channel = ByteChannel()
val reader = ByteChannelReader(channel)
val line = async { reader.readUTF8Line() }
message.toByteArray(Charsets.UTF_8).forEach { byte ->
channel.writeByte(byte)
channel.flush()
}
channel.writeBytes(CR, LF)
channel.flushAndClose()
assertEquals(message, line.await())
}
}
@Test
fun `readUTF8Line reads several LF-terminated lines`() {
runBlocking {
val messages = listOf(
"Hello", "world", "how", "are", "you", "doing"
)
val channel = ByteChannel()
val reader = ByteChannelReader(channel)
val lines = flow {
while (!reader.isClosedForRead) {
emit(reader.readUTF8Line())
}
}
for (message in messages) {
channel.writeLine(message, LF)
channel.flush()
}
channel.close()
lines.withIndex().collect { (i, line) ->
assertEquals(messages[i], line)
}
}
}
private suspend fun ByteWriteChannel.writeBytes(vararg bytes: Byte) {
for (byte in bytes) {
writeByte(byte)
}
}
private suspend fun ByteWriteChannel.writeLine(s: String, vararg terminators: Byte = byteArrayOf(LF)) {
writeStringUtf8(s)
for (terminator in terminators) {
writeByte(terminator)
}
}
}
@OptIn(InternalAPI::class)
internal class ByteChannelReader(val underlying: ByteChannel) : ByteReader {
override val closedCause: Throwable?
get() = underlying.closedCause
override val isClosedForRead: Boolean
get() = underlying.isClosedForRead
override val readBuffer: Source
get() = underlying.readBuffer
override suspend fun awaitContent(min: Int): Boolean {
return underlying.awaitContent()
}
override fun cancel(cause: Throwable?) {
underlying.cancel(cause)
}
}