fix in site search and more tests on evalSession cancellation
This commit is contained in:
parent
fc01016a74
commit
a051280e0c
@ -21,6 +21,7 @@ import kotlinx.coroutines.test.advanceTimeBy
|
|||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import net.sergeych.lyng.EvalSession
|
import net.sergeych.lyng.EvalSession
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.Script
|
||||||
import net.sergeych.lyng.obj.ObjBool
|
import net.sergeych.lyng.obj.ObjBool
|
||||||
import net.sergeych.lyng.obj.ObjFlow
|
import net.sergeych.lyng.obj.ObjFlow
|
||||||
import net.sergeych.lyng.obj.ObjInt
|
import net.sergeych.lyng.obj.ObjInt
|
||||||
@ -41,27 +42,51 @@ class EvalSessionTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun sessionCancelStopsLaunchedCoroutines() = runTest {
|
fun sessionCancelStopsLaunchedCoroutines() = runTest {
|
||||||
val scope = Scope()
|
val scope = Script.newScope()
|
||||||
|
scope.eval("var exportedVal=0")
|
||||||
val session = EvalSession(scope)
|
val session = EvalSession(scope)
|
||||||
|
|
||||||
session.eval(
|
session.eval(
|
||||||
"""
|
"""
|
||||||
var touched = false
|
var touched = false
|
||||||
launch {
|
launch {
|
||||||
|
while(true) {
|
||||||
delay(100)
|
delay(100)
|
||||||
touched = true
|
touched = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
.trimIndent()
|
.trimIndent()
|
||||||
)
|
)
|
||||||
|
|
||||||
session.cancel()
|
session.cancelAndJoin()
|
||||||
session.join()
|
|
||||||
advanceTimeBy(150)
|
advanceTimeBy(150)
|
||||||
|
|
||||||
assertFalse((scope.eval("touched") as ObjBool).value)
|
assertFalse((scope.eval("touched") as ObjBool).value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cancelAndJoinStopsLaunchedCoroutinesForUserProvidedScriptScope() = runTest {
|
||||||
|
val scope = Script.newScope()
|
||||||
|
val session = EvalSession(scope)
|
||||||
|
|
||||||
|
session.eval(
|
||||||
|
"""
|
||||||
|
var counter = 0
|
||||||
|
launch {
|
||||||
|
delay(10)
|
||||||
|
counter += 1
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
session.cancelAndJoin()
|
||||||
|
advanceTimeBy(20)
|
||||||
|
|
||||||
|
assertEquals(0L, (scope.eval("counter") as ObjInt).value)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun joinObservesWorkStartedByLaterEval() = runTest {
|
fun joinObservesWorkStartedByLaterEval() = runTest {
|
||||||
val session = EvalSession(Scope())
|
val session = EvalSession(Scope())
|
||||||
|
|||||||
@ -512,8 +512,9 @@ internal fun plainFromMarkdown(md: String): String {
|
|||||||
// Use non-greedy dot-all equivalents ("[\n\r\s\S]") instead of character classes with ']' where possible.
|
// Use non-greedy dot-all equivalents ("[\n\r\s\S]") instead of character classes with ']' where possible.
|
||||||
try {
|
try {
|
||||||
// Safer patterns (avoid unescaped ']' inside character classes):
|
// Safer patterns (avoid unescaped ']' inside character classes):
|
||||||
val reCodeBlocks = Regex("```[\\s\\S]*?```")
|
val reBacktickCodeBlocks = Regex("```[\\s\\S]*?```")
|
||||||
val reInlineCode = Regex("`[^`]*`")
|
val reTildeCodeBlocks = Regex("~~~[\\s\\S]*?~~~")
|
||||||
|
val reInlineCode = Regex("`([^`]*)`")
|
||||||
val reBlockquote = Regex("^> +", setOf(RegexOption.MULTILINE))
|
val reBlockquote = Regex("^> +", setOf(RegexOption.MULTILINE))
|
||||||
val reHeadings = Regex("^#+ +", setOf(RegexOption.MULTILINE))
|
val reHeadings = Regex("^#+ +", setOf(RegexOption.MULTILINE))
|
||||||
// Images:  — capture alt lazily with [\s\S]*? to avoid character class pitfalls
|
// Images:  — capture alt lazily with [\s\S]*? to avoid character class pitfalls
|
||||||
@ -523,9 +524,10 @@ internal fun plainFromMarkdown(md: String): String {
|
|||||||
|
|
||||||
var t = md
|
var t = md
|
||||||
// Triple-backtick code blocks across lines
|
// Triple-backtick code blocks across lines
|
||||||
t = t.replace(reCodeBlocks, " ")
|
t = t.replace(reBacktickCodeBlocks, " ")
|
||||||
// Inline code
|
t = t.replace(reTildeCodeBlocks, " ")
|
||||||
t = t.replace(reInlineCode, " ")
|
// Keep inline code content so API names in prose and headings remain searchable
|
||||||
|
t = t.replace(reInlineCode, "\$1")
|
||||||
// Strip blockquotes and headings markers
|
// Strip blockquotes and headings markers
|
||||||
t = t.replace(reBlockquote, "")
|
t = t.replace(reBlockquote, "")
|
||||||
t = t.replace(reHeadings, "")
|
t = t.replace(reHeadings, "")
|
||||||
@ -539,7 +541,8 @@ internal fun plainFromMarkdown(md: String): String {
|
|||||||
// Minimal safe fallback: strip code blocks and inline code, then normalize
|
// Minimal safe fallback: strip code blocks and inline code, then normalize
|
||||||
var t = md
|
var t = md
|
||||||
t = t.replace(Regex("```[\\s\\S]*?```"), " ")
|
t = t.replace(Regex("```[\\s\\S]*?```"), " ")
|
||||||
t = t.replace(Regex("`[^`]*`"), " ")
|
t = t.replace(Regex("~~~[\\s\\S]*?~~~"), " ")
|
||||||
|
t = t.replace(Regex("`([^`]*)`"), "\$1")
|
||||||
return norm(t)
|
return norm(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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 kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class SearchScoringTest {
|
class SearchScoringTest {
|
||||||
@ -26,4 +44,31 @@ class SearchScoringTest {
|
|||||||
val sFar = scoreQueryAdvanced(listOf("alp", "bet"), far)
|
val sFar = scoreQueryAdvanced(listOf("alp", "bet"), far)
|
||||||
assertTrue(sNear > sFar, "closer terms should have higher score")
|
assertTrue(sNear > sFar, "closer terms should have higher score")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun preservesInlineCodeTermsInHeadingsAndText() {
|
||||||
|
val plain = plainFromMarkdown(
|
||||||
|
"""
|
||||||
|
### Preferred runtime: `EvalSession`
|
||||||
|
|
||||||
|
For host applications, prefer `EvalSession` as the main way to run scripts.
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
assertTrue(plain.contains("evalsession"), "inline code terms should remain in searchable text")
|
||||||
|
assertTrue(scoreQueryAdvanced(listOf("evalsession"), rec(plain)) > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun stripsTildeCodeFencesLikeBacktickFences() {
|
||||||
|
val plain = plainFromMarkdown(
|
||||||
|
"""
|
||||||
|
~~~kotlin
|
||||||
|
val session = EvalSession()
|
||||||
|
~~~
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
assertFalse(plain.contains("evalsession"), "fenced code should not leak into the search corpus")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user