Add repeated local import regression coverage

This commit is contained in:
Sergey Chernov 2026-04-12 11:11:32 +03:00
parent 47eb2d7d61
commit ab39110834
3 changed files with 317 additions and 20 deletions

View File

@ -1,40 +1,92 @@
package net.sergeych
import net.sergeych.lyng.EvalSession
import net.sergeych.lyng.Source
import net.sergeych.lyng.obj.ObjString
import kotlinx.coroutines.runBlocking
import java.nio.file.Files
import kotlin.io.path.writeText
import kotlin.test.Test
import kotlin.test.assertEquals
class CliLocalModuleImportRegressionJvmTest {
private fun writeTransitiveImportTree(root: java.nio.file.Path) {
val packageDir = Files.createDirectories(root.resolve("package1"))
val nestedDir = Files.createDirectories(packageDir.resolve("nested"))
packageDir.resolve("alpha.lyng").writeText(
"""
package package1.alpha
import lyng.stdlib
import lyng.io.net
class Alpha {
val headers = Map<String, String>()
fun makeTask(port: Int, host: String): Deferred = launch {
host + ":" + port
}
fun netModule() = Net
}
fun alphaValue() = "alpha"
""".trimIndent()
)
packageDir.resolve("beta.lyng").writeText(
"""
package package1.beta
import lyng.stdlib
import package1.alpha
fun betaValue() = alphaValue() + "|beta"
""".trimIndent()
)
nestedDir.resolve("gamma.lyng").writeText(
"""
package package1.nested.gamma
import lyng.io.net
import package1.alpha
import package1.beta
val String.gammaTag get() = this + "|gamma"
fun gammaValue() = betaValue().gammaTag
fun netModule() = Net
""".trimIndent()
)
packageDir.resolve("entry.lyng").writeText(
"""
package package1.entry
import lyng.stdlib
import lyng.io.net
import package1.alpha
import package1.beta
import package1.nested.gamma
fun report() = gammaValue() + "|entry"
""".trimIndent()
)
}
@Test
fun localModuleUsingLaunchAndNetImportsWithoutStdlibRedefinition() = runBlocking {
val root = Files.createTempDirectory("lyng-cli-import-regression")
try {
val packageDir = Files.createDirectories(root.resolve("package1"))
val mainFile = root.resolve("main.lyng")
val alphaFile = packageDir.resolve("alpha.lyng")
writeTransitiveImportTree(root)
mainFile.writeText(
"""
import package1.alpha
import package1.entry
import package1.beta
import package1.nested.gamma
println("ok")
""".trimIndent()
)
alphaFile.writeText(
"""
import lyng.io.net
class Alpha {
val headers = Map<String, String>()
fn startListen(port, host) {
launch {
println(port, host)
}
}
}
println(report())
""".trimIndent()
)
@ -43,4 +95,43 @@ class CliLocalModuleImportRegressionJvmTest {
root.toFile().deleteRecursively()
}
}
@Test
fun localModuleImportsAreNoOpsWhenEvaldRepeatedlyOnSameCliContext() = runBlocking {
val root = Files.createTempDirectory("lyng-cli-import-regression-repeat")
try {
val mainFile = root.resolve("main.lyng")
writeTransitiveImportTree(root)
mainFile.writeText("println(\"bootstrap\")")
val session = EvalSession(newCliScope(emptyList(), mainFile.toString()))
try {
repeat(5) { index ->
val result = evalOnCliDispatcher(
session,
Source(
"<repeat-local-import-$index>",
"""
import package1.entry
import package1.nested.gamma
import package1.beta
import package1.alpha
report()
""".trimIndent()
)
) as ObjString
assertEquals(
"alpha|beta|gamma|entry",
result.value
)
}
} finally {
session.cancelAndJoin()
}
} finally {
root.toFile().deleteRecursively()
}
}
}

View File

@ -64,6 +64,73 @@ class CliLocalImportsJvmTest {
return CliResult(outBuf.toString("UTF-8"), errBuf.toString("UTF-8"), exitCode)
}
private fun writeTransitiveImportTree(root: java.nio.file.Path) {
val packageDir = Files.createDirectories(root.resolve("package1"))
val nestedDir = Files.createDirectories(packageDir.resolve("nested"))
Files.writeString(
packageDir.resolve("alpha.lyng"),
"""
package package1.alpha
import lyng.stdlib
import lyng.io.net
class Alpha {
val headers = Map<String, String>()
fun makeTask(port: Int, host: String): Deferred = launch {
host + ":" + port
}
fun netModule() = Net
}
fun alphaValue() = "alpha"
""".trimIndent()
)
Files.writeString(
packageDir.resolve("beta.lyng"),
"""
package package1.beta
import lyng.stdlib
import package1.alpha
fun betaValue() = alphaValue() + "|beta"
""".trimIndent()
)
Files.writeString(
nestedDir.resolve("gamma.lyng"),
"""
package package1.nested.gamma
import lyng.io.net
import package1.alpha
import package1.beta
val String.gammaTag get() = this + "|gamma"
fun gammaValue() = betaValue().gammaTag
fun netModule() = Net
""".trimIndent()
)
Files.writeString(
packageDir.resolve("entry.lyng"),
"""
package package1.entry
import lyng.stdlib
import lyng.io.net
import package1.alpha
import package1.beta
import package1.nested.gamma
fun report() = gammaValue() + "|entry"
""".trimIndent()
)
}
@Test
fun cliDiscoversSiblingAndNestedLocalImportsFromEntryRoot() {
val dir = Files.createTempDirectory("lyng_cli_local_imports_")
@ -134,4 +201,37 @@ class CliLocalImportsJvmTest {
dir.toFile().deleteRecursively()
}
}
@Test
fun cliHandlesOverlappingDirectoryImportsWithTransitiveStdlibAndNetSymbols() {
val dir = Files.createTempDirectory("lyng_cli_local_imports_transitive_")
try {
val mainFile = dir.resolve("main.lyng")
writeTransitiveImportTree(dir)
Files.writeString(
mainFile,
"""
import package1.entry
import package1.beta
import package1.nested.gamma
println(report())
println(gammaValue())
""".trimIndent()
)
val result = runCli(mainFile.toString())
assertTrue(result.err, result.err.isBlank())
assertTrue(
result.out,
result.out.contains("alpha|beta|gamma|entry")
)
assertTrue(
result.out,
result.out.contains("alpha|beta|gamma")
)
} finally {
dir.toFile().deleteRecursively()
}
}
}

View File

@ -16,17 +16,60 @@
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.Compiler
import net.sergeych.lyng.EvalSession
import net.sergeych.lyng.Script
import net.sergeych.lyng.Source
import net.sergeych.lyng.obj.ObjString
import net.sergeych.lyng.obj.toInt
import net.sergeych.lyng.pacman.ImportManager
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertSame
class ScriptImportPreparationTest {
private fun nestedImportSources(prefix: String): Array<String> =
arrayOf(
"""
package $prefix.alpha
class Alpha {
val headers = Map<String, String>()
fun tagged(port: Int, host: String): String {
val task: Deferred = launch {
host + ":" + port + ":" + headers.size
}
return task.await()
}
}
fun alphaValue() = Alpha().tagged(7, "alpha")
""".trimIndent(),
"""
package $prefix.beta
import $prefix.alpha
fun betaValue() = alphaValue() + "|" + Alpha().tagged(8, "beta")
""".trimIndent(),
"""
package $prefix.gamma
import $prefix.alpha
import $prefix.beta
val String.gammaTag get() = this + ":gamma"
fun gammaValue() = betaValue() + "|" + "done".gammaTag
""".trimIndent()
)
private fun nestedImportManager(prefix: String = "tree"): ImportManager =
Script.defaultImportManager.copy().apply {
addTextPackages(*nestedImportSources(prefix))
}
@Test
fun scriptImportIntoExplicitlyPreparesExistingScope() = runTest {
val manager = ImportManager()
@ -84,4 +127,67 @@ class ScriptImportPreparationTest {
val record = assertNotNull(module["answer"])
assertEquals(42, module.resolve(record, "answer").toInt())
}
@Test
fun repeatedImportIntoOnSameScopeIsIdempotentForNestedPackageGraph() = runTest {
val manager = nestedImportManager()
val script = Compiler.compile(
Source(
"<repeat-import-into>",
"""
import tree.gamma
import tree.beta
import tree.alpha
gammaValue()
""".trimIndent()
),
manager
)
val scope = manager.newStdScope()
script.importInto(scope)
val importedGammaValue = assertNotNull(scope["gammaValue"])
val importedAlpha = assertNotNull(scope["Alpha"])
repeat(5) {
script.importInto(scope)
}
assertSame(importedGammaValue, scope["gammaValue"])
assertSame(importedAlpha, scope["Alpha"])
assertEquals(
"alpha:7:0|beta:8:0|done:gamma",
(script.execute(scope) as ObjString).value
)
}
@Test
fun repeatedEvalOnSameSessionCanReimportNestedPackageGraph() = runTest {
val prefix = "repeattree"
val manager = nestedImportManager(prefix)
val scope = manager.newModule()
val session = EvalSession(scope)
try {
repeat(5) { index ->
val result = session.eval(
Source(
"<repeat-eval-$index>",
"""
import $prefix.gamma
import $prefix.beta
import $prefix.alpha
gammaValue()
""".trimIndent()
)
) as ObjString
assertEquals("alpha:7:0|beta:8:0|done:gamma", result.value)
}
} finally {
session.cancelAndJoin()
}
}
}