lyng/lynglib/src/commonTest/kotlin/EvalSessionTest.kt

161 lines
4.1 KiB
Kotlin

/*
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.EvalSession
import net.sergeych.lyng.Scope
import net.sergeych.lyng.obj.ObjBool
import net.sergeych.lyng.obj.ObjFlow
import net.sergeych.lyng.obj.ObjInt
import net.sergeych.lyng.obj.ObjList
import kotlin.test.*
class EvalSessionTest {
@Test
fun sessionCreatesAndReusesScope() = runTest {
val session = EvalSession()
assertEquals(null, session.scope)
assertEquals(10L, (session.eval("var x = 10; x") as ObjInt).value)
assertNotNull(session.scope)
assertEquals(session.scope, session.getScope())
assertEquals(15L, (session.eval("x += 5; x") as ObjInt).value)
}
@Test
fun sessionCancelStopsLaunchedCoroutines() = runTest {
val scope = Scope()
val session = EvalSession(scope)
session.eval(
"""
var touched = false
launch {
delay(100)
touched = true
}
"""
.trimIndent()
)
session.cancel()
session.join()
advanceTimeBy(150)
assertFalse((scope.eval("touched") as ObjBool).value)
}
@Test
fun joinObservesWorkStartedByLaterEval() = runTest {
val session = EvalSession(Scope())
session.eval("launch { delay(100) }")
var joined = false
val waiter = launch {
session.join()
joined = true
}
advanceTimeBy(50)
assertFalse(joined)
session.eval("launch { delay(100) }")
advanceTimeBy(60)
assertFalse(joined)
advanceTimeBy(50)
waiter.join()
assertTrue(joined)
}
@Test
fun concurrentEvalCallsAreSerialized() = runTest {
val session = EvalSession(Scope())
session.eval("var counter = 0")
val first = async {
session.eval(
"""
delay(100)
counter += 1
counter
"""
.trimIndent()
) as ObjInt
}
val second = async {
session.eval(
"""
counter += 10
counter
"""
.trimIndent()
) as ObjInt
}
advanceTimeBy(150)
assertEquals(1L, first.await().value)
assertEquals(11L, second.await().value)
assertEquals(11L, (session.eval("counter") as ObjInt).value)
}
@Test
fun joinWaitsForActiveFlowProducer() = runTest {
val scope = Scope()
val session = EvalSession(scope)
val flow = session.eval(
"""
flow {
delay(100)
emit(1)
delay(100)
emit(2)
}
"""
.trimIndent()
) as ObjFlow
var joined = false
var collected: ObjList? = null
val collector = launch {
collected = flow.callMethod(scope, "toList")
}
val waiter = launch {
session.join()
joined = true
}
advanceTimeBy(150)
assertFalse(joined)
advanceTimeBy(100)
collector.join()
waiter.join()
assertTrue(joined)
assertEquals(listOf(1L, 2L), collected!!.list.map { (it as ObjInt).value })
}
}