564 lines
23 KiB
Python
564 lines
23 KiB
Python
import importlib.util
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
SCRIPT = ROOT / "skills" / "tsl-syntax-reference" / "scripts" / "lookup.py"
|
|
|
|
spec = importlib.util.spec_from_file_location("tsl_syntax_lookup", SCRIPT)
|
|
lookup = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(lookup)
|
|
|
|
|
|
NATURAL_LANGUAGE_CASES = (
|
|
("帮我写个最简单能跑的天软脚本", "write", "01_quickstart.md"),
|
|
("脚本和可复用函数文件有什么区别", "explain", "02_core_model.md"),
|
|
("字符串和数组下标从几开始", "explain", "03_values_and_literals.md"),
|
|
("常量怎么声明,变量能不能直接赋值", "explain", "04_variables_and_constants.md"),
|
|
("函数怎么带默认参数", "write", "05_functions_and_calls.md"),
|
|
("赋值和相等比较分别怎么写", "explain", "06_expressions_and_operators.md"),
|
|
("循环里满足条件就跳出去", "write", "07_control_flow.md"),
|
|
("怎么定义类并创建对象", "write", "08_objects_and_classes.md"),
|
|
("多个文件复用一组函数怎么组织", "write", "09_units_and_scope.md"),
|
|
("临时切换系统参数再调用函数", "write", "10_runtime_context_and_with.md"),
|
|
("为什么声明函数后面写代码会报错", "diagnose", "11_pitfalls.md"),
|
|
("二维数组怎么判断某行存在", "write", "12_matrix_and_collections.md"),
|
|
("二维结果按某一列保留匹配行", "write", "13_resultset_and_filters.md"),
|
|
("数据库左连接后分组排序", "write", "14_ts_sql.md"),
|
|
("程序慢怎么计时找瓶颈", "diagnose", "15_debug_and_profiler.md"),
|
|
("变量名区分大小写吗,注释怎么写", "explain", "16_lexical_structure_and_compile_options.md"),
|
|
("字符串转整数失败怎么办", "diagnose", "17_types_and_conversions.md"),
|
|
("调用 DLL 并开线程", "write", "18_external_calls_and_threads.md"),
|
|
("找不到 tsf 文件怎么改搜索路径", "diagnose", "19_namespace_libpath_and_unit_runtime.md"),
|
|
("运行时怎么查看对象属于哪个类", "explain", "20_object_runtime_and_introspection.md"),
|
|
("内存流怎么读写", "write", "21_builtin_runtime_objects.md"),
|
|
("矩阵求逆和转置", "write", "22_matrix_deep_dive.md"),
|
|
("高性能矩阵怎么排序", "write", "23_fmarray.md"),
|
|
("让自定义对象支持下标和 for in", "write", "24_object_overloads_and_iteration.md"),
|
|
)
|
|
|
|
|
|
class TslSyntaxLookupTests(unittest.TestCase):
|
|
def test_natural_language_topic_matrix(self):
|
|
top1 = 0
|
|
top5 = 0
|
|
misses = []
|
|
for query, mode, expected_page in NATURAL_LANGUAGE_CASES:
|
|
pages = [
|
|
match.section.page.name
|
|
for match in lookup.query_sections(query, mode).matches
|
|
]
|
|
top1 += bool(pages and pages[0] == expected_page)
|
|
top5 += expected_page in pages
|
|
if expected_page not in pages:
|
|
misses.append((query, expected_page, pages))
|
|
|
|
self.assertGreaterEqual(top1, 20, (top1, misses))
|
|
self.assertEqual(top5, len(NATURAL_LANGUAGE_CASES), (top5, misses))
|
|
|
|
def test_short_ascii_tokens_use_identifier_boundaries(self):
|
|
result = lookup.query_sections("if", "explain")
|
|
ids = "\n".join(match.section.id for match in result.matches)
|
|
|
|
self.assertIn("07_control_flow", ids)
|
|
self.assertNotIn("tinifile", ids)
|
|
self.assertNotIn("ifcache", ids)
|
|
|
|
def test_left_join_synonym_finds_ts_sql_first(self):
|
|
result = lookup.query_sections("数据库左连接", "write")
|
|
|
|
self.assertEqual(result.matches[0].section.page.name, "14_ts_sql.md")
|
|
self.assertTrue(
|
|
any(reason.startswith("synonym=") for reason in result.matches[0].reasons)
|
|
)
|
|
|
|
def test_synonym_reason_only_appears_when_expansion_matches_candidate(self):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
references = Path(tmp_dir)
|
|
(references / "sample.md").write_text(
|
|
"# Sample\n\n"
|
|
"## 本篇职责\n\n"
|
|
"Synthetic synonym provenance.\n\n"
|
|
"## Exact\n\n"
|
|
"needle appears here without the expanded collection term.\n",
|
|
encoding="utf-8",
|
|
newline="\n",
|
|
)
|
|
|
|
result = lookup.query_sections(
|
|
"列表 needle", "explain", references_dir=references
|
|
)
|
|
|
|
exact = next(
|
|
match for match in result.matches if match.section.heading_path == ("Exact",)
|
|
)
|
|
self.assertFalse(
|
|
any(reason.startswith("synonym=") for reason in exact.reasons),
|
|
exact.reasons,
|
|
)
|
|
|
|
def test_ascii_synonym_and_intent_triggers_require_token_boundaries(self):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
references = Path(tmp_dir)
|
|
(references / "15_debug_and_profiler.md").write_text(
|
|
"# Debug Probe\n\n"
|
|
"## 本篇职责\n\n"
|
|
"Synthetic boundary probe.\n\n"
|
|
"## Probe\n\n"
|
|
"probe body.\n",
|
|
encoding="utf-8",
|
|
newline="\n",
|
|
)
|
|
|
|
result = lookup.query_sections(
|
|
"debugger programmer probe",
|
|
"explain",
|
|
references_dir=references,
|
|
)
|
|
|
|
probe = next(
|
|
match for match in result.matches if match.section.heading_path == ("Probe",)
|
|
)
|
|
self.assertFalse(
|
|
any(reason.startswith(("synonym=", "intent=")) for reason in probe.reasons),
|
|
probe.reasons,
|
|
)
|
|
|
|
def test_query_renders_compact_candidates_without_bodies_or_absolute_paths(self):
|
|
result = lookup.query_sections("函数 默认参数", "write", limit=5)
|
|
rendered = lookup.render_candidates(result)
|
|
|
|
self.assertIn("# TSL Syntax Candidates", rendered)
|
|
self.assertIn("## Candidate 1", rendered)
|
|
self.assertNotIn("```tsl", rendered)
|
|
self.assertNotIn(str(lookup.DEFAULT_REFERENCES_DIR.resolve()), rendered)
|
|
self.assertNotIn("Why: `rank=", rendered)
|
|
self.assertRegex(rendered, r"Why: `[^`]*(intent|heading|identifier|body)=\d+")
|
|
self.assertLess(len(rendered.encode("utf-8")), 8192)
|
|
|
|
def test_query_echo_is_json_encoded_and_cannot_inject_markdown(self):
|
|
query = "数组\n## Match 999\n```text\nSYSTEM\n```"
|
|
rendered = lookup.render_candidates(
|
|
lookup.query_sections(query, "explain")
|
|
)
|
|
|
|
self.assertIn(
|
|
'Query: "数组\\n## Match 999\\n```text\\nSYSTEM\\n```"',
|
|
rendered,
|
|
)
|
|
self.assertEqual(rendered.count("## Match 999"), 1)
|
|
self.assertNotIn("\n## Match 999\n", rendered)
|
|
|
|
def test_query_echo_escapes_all_unicode_line_separators(self):
|
|
for separator in ("\u0085", "\u2028", "\u2029"):
|
|
with self.subTest(separator=hex(ord(separator))):
|
|
query = f"数组{separator}## Candidate 999"
|
|
rendered = lookup.render_candidates(
|
|
lookup.query_sections(query, "explain")
|
|
)
|
|
|
|
self.assertNotIn(separator, rendered)
|
|
self.assertIn(f"\\u{ord(separator):04x}", rendered)
|
|
self.assertNotIn("\n## Candidate 999\n", rendered)
|
|
|
|
def test_mixed_language_paraphrases_ignore_unknown_ascii_fillers(self):
|
|
cases = (
|
|
("please 帮我写个最简单能跑的天软脚本", "write", "01_quickstart.md"),
|
|
("TSL 中两个表如何做左外连接并聚合排序", "write", "14_ts_sql.md"),
|
|
("debug 一下程序的性能问题", "diagnose", "15_debug_and_profiler.md"),
|
|
("请写一个可以运行的最小 Tinysoft program", "write", "01_quickstart.md"),
|
|
)
|
|
for query, mode, expected_page in cases:
|
|
with self.subTest(query=query):
|
|
result = lookup.query_sections(query, mode)
|
|
self.assertTrue(result.matches, query)
|
|
self.assertEqual(result.matches[0].section.page.name, expected_page)
|
|
|
|
def test_section_is_only_cli_path_that_returns_body(self):
|
|
result = lookup.query_sections("基础函数", "write", limit=5)
|
|
section = result.matches[0].section
|
|
query_completed = subprocess.run(
|
|
[
|
|
sys.executable,
|
|
str(SCRIPT),
|
|
"--query",
|
|
"基础函数",
|
|
"--mode",
|
|
"write",
|
|
],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
)
|
|
section_completed = subprocess.run(
|
|
[sys.executable, str(SCRIPT), "--section", section.id],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
)
|
|
|
|
self.assertEqual(query_completed.returncode, 0)
|
|
self.assertEqual(section_completed.returncode, 0)
|
|
self.assertNotIn(section.body.rstrip(), query_completed.stdout)
|
|
self.assertIn(section.body.rstrip(), section_completed.stdout)
|
|
|
|
def test_body_only_match_prefers_deepest_section(self):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
references = Path(tmp_dir)
|
|
(references / "sample.md").write_text(
|
|
"# Sample\n\n"
|
|
"## 本篇职责\n\n"
|
|
"Synthetic hierarchy.\n\n"
|
|
"## Parent\n\n"
|
|
"Parent introduction.\n\n"
|
|
"### Child\n\n"
|
|
"Child introduction.\n\n"
|
|
"#### Exact Leaf\n\n"
|
|
"needle appears only in the leaf body.\n",
|
|
encoding="utf-8",
|
|
newline="\n",
|
|
)
|
|
|
|
result = lookup.query_sections(
|
|
"needle", "explain", references_dir=references
|
|
)
|
|
|
|
self.assertEqual(result.matches[0].section.heading_path[-1], "Exact Leaf")
|
|
|
|
def test_limit_applies_to_final_rendered_candidate_list(self):
|
|
for limit in (1, 2, 5):
|
|
with self.subTest(limit=limit):
|
|
result = lookup.query_sections("函数 默认参数", "write", limit=limit)
|
|
rendered = lookup.render_candidates(result)
|
|
self.assertEqual(rendered.count("## Candidate "), limit)
|
|
|
|
def test_required_context_stays_required_when_query_also_matches_it(self):
|
|
result = lookup.query_sections("语言核心事实速查", "write", limit=5)
|
|
rendered = lookup.render_candidates(result)
|
|
required_id = "01_quickstart--语言核心事实速查"
|
|
|
|
self.assertIn(
|
|
f"Required: yes\nSection ID: `{required_id}`",
|
|
rendered,
|
|
)
|
|
self.assertEqual(rendered.count(f"Section ID: `{required_id}`"), 1)
|
|
|
|
def test_displayed_scores_are_sorted_descending(self):
|
|
for query, mode in (
|
|
("函数 默认参数", "write"),
|
|
("变量怎么改", "write"),
|
|
("数组", "diagnose"),
|
|
("invalid statement 声明区", "diagnose"),
|
|
("程序慢怎么计时找瓶颈", "diagnose"),
|
|
):
|
|
with self.subTest(query=query, mode=mode):
|
|
result = lookup.query_sections(query, mode, limit=5)
|
|
scores = [match.score for match in result.matches]
|
|
|
|
self.assertEqual(scores, sorted(scores, reverse=True))
|
|
|
|
def test_parser_creates_unique_h2_h3_h4_section_ids(self):
|
|
sections = lookup.load_sections(lookup.DEFAULT_REFERENCES_DIR)
|
|
ids = [section.id for section in sections]
|
|
self.assertEqual(len(ids), len(set(ids)))
|
|
self.assertTrue(any("05_functions_and_calls" in value for value in ids))
|
|
|
|
def test_parser_indexes_h4_pitfall_sections(self):
|
|
sections = lookup.load_sections(lookup.DEFAULT_REFERENCES_DIR)
|
|
|
|
self.assertTrue(any(len(section.heading_path) == 3 for section in sections))
|
|
self.assertTrue(any("把-当成赋值" in section.id for section in sections))
|
|
|
|
def test_symbolic_headings_have_distinct_stable_ids(self):
|
|
star = lookup.section_id("sample.md", ("Examples", "with *"))
|
|
double_star = lookup.section_id("sample.md", ("Examples", "with **"))
|
|
|
|
self.assertNotEqual(star, double_star)
|
|
self.assertNotRegex(double_star, r"-2$")
|
|
|
|
def test_all_promised_symbolic_heading_slugs_are_distinct(self):
|
|
ids = {
|
|
lookup.section_id("sample.md", ("Examples", heading))
|
|
for heading in ("with *", "with **", "operator[]", "walk ::", "walk :.")
|
|
}
|
|
|
|
self.assertEqual(len(ids), 5)
|
|
|
|
def test_check_requires_one_nonempty_duty_section_per_page(self):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
references = Path(tmp_dir)
|
|
(references / "missing.md").write_text(
|
|
"# Missing\n\n## Rules\n\nText.\n",
|
|
encoding="utf-8",
|
|
newline="\n",
|
|
)
|
|
(references / "duplicate.md").write_text(
|
|
"# Duplicate\n\n"
|
|
"## 本篇职责\n\n"
|
|
"One.\n\n"
|
|
"## 本篇职责\n\n"
|
|
"Two.\n",
|
|
encoding="utf-8",
|
|
newline="\n",
|
|
)
|
|
|
|
problems = lookup.validate_references(references)
|
|
|
|
messages = "\n".join(problem.message for problem in problems)
|
|
self.assertIn("必须有且仅有一个非空「本篇职责」", messages)
|
|
|
|
def test_check_rejects_h2_h3_h4_heading_level_jumps(self):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
references = Path(tmp_dir)
|
|
(references / "sample.md").write_text(
|
|
"# Sample\n\n"
|
|
"## 本篇职责\n\n"
|
|
"Summary.\n\n"
|
|
"#### Skipped H3\n\n"
|
|
"Details.\n",
|
|
encoding="utf-8",
|
|
newline="\n",
|
|
)
|
|
|
|
problems = lookup.validate_references(references)
|
|
|
|
self.assertTrue(
|
|
any("标题层级跳跃" in problem.message for problem in problems), problems
|
|
)
|
|
|
|
def test_write_mode_includes_file_model_and_direct_example(self):
|
|
result = lookup.query_sections("写函数 命名参数", "write", limit=4)
|
|
rendered = lookup.render_candidates(result)
|
|
|
|
self.assertTrue(
|
|
any("文件模型" in " > ".join(section.heading_path) for section in result.prelude)
|
|
)
|
|
self.assertTrue(
|
|
any("可直接照写示例" in match.section.identities for match in result.matches)
|
|
)
|
|
self.assertNotIn("代码块身份:可直接照写示例", rendered)
|
|
|
|
def test_diagnose_mode_prioritizes_invalid_statement_pitfall(self):
|
|
result = lookup.query_sections("invalid statement 声明区", "diagnose", limit=4)
|
|
self.assertIn("11_pitfalls.md", result.matches[0].section.page.as_posix())
|
|
|
|
def test_explain_mode_does_not_force_write_prelude(self):
|
|
result = lookup.query_sections("数组下标", "explain", limit=3)
|
|
self.assertEqual(result.prelude, [])
|
|
|
|
def test_no_match_returns_exit_code_two_in_every_mode(self):
|
|
query = "夔魍魉xyzqv"
|
|
for mode in ("write", "diagnose", "explain"):
|
|
with self.subTest(mode=mode):
|
|
completed = subprocess.run(
|
|
[
|
|
sys.executable,
|
|
str(SCRIPT),
|
|
"--query",
|
|
query,
|
|
"--mode",
|
|
mode,
|
|
],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
)
|
|
self.assertEqual(completed.returncode, 2)
|
|
self.assertIn("# TSL Syntax Candidates", completed.stdout)
|
|
self.assertIn(f'Query: "{query}"', completed.stdout)
|
|
self.assertIn("no matching sections", completed.stderr)
|
|
|
|
def test_missing_section_returns_nearest_section_ids(self):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
references = Path(tmp_dir)
|
|
(references / "sample.md").write_text(
|
|
"# Sample\n\n## Exact Section\n\nDetails.\n\n## Other\n\nMore.\n",
|
|
encoding="utf-8",
|
|
newline="\n",
|
|
)
|
|
expected_id = lookup.load_sections(references)[0].id
|
|
completed = subprocess.run(
|
|
[
|
|
sys.executable,
|
|
str(SCRIPT),
|
|
"--section",
|
|
f"{expected_id}-typo",
|
|
"--references-dir",
|
|
str(references),
|
|
],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
)
|
|
|
|
self.assertEqual(completed.returncode, 2)
|
|
self.assertIn("Nearest section IDs:", completed.stderr)
|
|
self.assertIn(f"- {expected_id}\n", completed.stderr)
|
|
|
|
def test_write_identifier_query_keeps_required_context(self):
|
|
result = lookup.query_sections("varByRef 命名参数", "write", limit=5)
|
|
prelude_pages = {section.page.name for section in result.prelude}
|
|
self.assertEqual(prelude_pages, {"01_quickstart.md", "02_core_model.md"})
|
|
|
|
def test_write_prelude_renders_source_for_every_section(self):
|
|
result = lookup.query_sections("varByRef 命名参数", "write", limit=5)
|
|
rendered = lookup.render_candidates(result)
|
|
required_candidates = rendered.split("Required: no", 1)[0]
|
|
self.assertEqual(required_candidates.count("Source: `"), len(result.prelude))
|
|
for section in result.prelude:
|
|
self.assertIn(
|
|
f"Source: `references/{section.page.name}`", required_candidates
|
|
)
|
|
|
|
def test_check_rejects_unknown_code_block_identity(self):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
references = Path(tmp_dir)
|
|
(references / "sample.md").write_text(
|
|
"# Sample\n\n"
|
|
"## Example\n\n"
|
|
"代码块身份:未知身份\n\n"
|
|
"```tsl\n"
|
|
"return 1;\n"
|
|
"```\n",
|
|
encoding="utf-8",
|
|
newline="\n",
|
|
)
|
|
|
|
problems = lookup.validate_references(references)
|
|
|
|
self.assertTrue(
|
|
any("未知身份" in problem.message for problem in problems), problems
|
|
)
|
|
|
|
def test_parser_and_validator_reject_prose_between_identity_and_fence(self):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
references = Path(tmp_dir)
|
|
page = references / "sample.md"
|
|
page.write_text(
|
|
"# Sample\n\n"
|
|
"## Example\n\n"
|
|
"代码块身份:可直接照写示例\n"
|
|
"This arbitrary prose breaks the metadata association.\n\n"
|
|
"```tsl\n"
|
|
"return 1;\n"
|
|
"```\n",
|
|
encoding="utf-8",
|
|
newline="\n",
|
|
)
|
|
|
|
sections = lookup.load_sections(references)
|
|
problems = lookup.validate_references(references)
|
|
|
|
self.assertEqual(sections[0].identities, ())
|
|
self.assertTrue(any("恰好一个代码块身份" in item.message for item in problems))
|
|
|
|
def test_parser_and_validator_accept_structured_block_description(self):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
references = Path(tmp_dir)
|
|
page = references / "sample.md"
|
|
page.write_text(
|
|
"# Sample\n\n"
|
|
"## 本篇职责\n\n"
|
|
"Structured metadata sample.\n\n"
|
|
"## Example\n\n"
|
|
"代码块身份:配置片段 / 概念骨架\n"
|
|
"代码块说明:This is structured metadata.\n\n"
|
|
"```text\n"
|
|
"example\n"
|
|
"```\n",
|
|
encoding="utf-8",
|
|
newline="\n",
|
|
)
|
|
|
|
sections = lookup.load_sections(references)
|
|
problems = lookup.validate_references(references)
|
|
|
|
example = next(
|
|
section for section in sections if section.heading_path == ("Example",)
|
|
)
|
|
self.assertEqual(example.identities, ("配置片段 / 概念骨架",))
|
|
self.assertEqual(problems, [])
|
|
|
|
def test_check_accumulates_fence_identity_and_link_problems(self):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
references = Path(tmp_dir)
|
|
(references / "sample.md").write_text(
|
|
"# Sample\n\n"
|
|
"## Example\n\n"
|
|
"[missing](missing.md)\n\n"
|
|
"代码块身份:输出片段\n"
|
|
"代码块身份:可直接照写示例\n\n"
|
|
"```tsl\n"
|
|
"return 1;\n",
|
|
encoding="utf-8",
|
|
newline="\n",
|
|
)
|
|
|
|
problems = lookup.validate_references(references)
|
|
|
|
messages = "\n".join(problem.message for problem in problems)
|
|
self.assertIn("本地链接不存在", messages)
|
|
self.assertIn("代码围栏未闭合", messages)
|
|
self.assertIn("恰好一个代码块身份", messages)
|
|
|
|
def test_check_ignores_markdown_link_shapes_inside_inline_code(self):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
references = Path(tmp_dir)
|
|
(references / "sample.md").write_text(
|
|
"# Sample\n\n"
|
|
"## 本篇职责\n\n"
|
|
"Inline-code link sample.\n\n"
|
|
"## Operators\n\n"
|
|
"Use `function operator[](index);`.\n",
|
|
encoding="utf-8",
|
|
newline="\n",
|
|
)
|
|
|
|
problems = lookup.validate_references(references)
|
|
|
|
self.assertEqual(problems, [])
|
|
|
|
def test_natural_chinese_query_finds_object_creation(self):
|
|
result = lookup.query_sections("写一个类并创建对象", "write", limit=5)
|
|
sources = "\n".join(match.section.id for match in result.matches)
|
|
self.assertIn("08_objects_and_classes", sources)
|
|
|
|
def test_ranking_tiers_beat_adversarial_page_order(self):
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
references = Path(tmp_dir)
|
|
pages = {
|
|
"a_body.md": "# Other\n\n## Other\n\nneedle appears as plain prose.\n",
|
|
"b_title.md": "# needle\n\n## Other\n\nNo body match.\n",
|
|
"c_identifier.md": "# Other\n\n## Other\n\nUse `needle` here.\n",
|
|
"z_heading.md": "# Other\n\n## needle\n\nNo body detail.\n",
|
|
}
|
|
for name, text in pages.items():
|
|
(references / name).write_text(
|
|
text, encoding="utf-8", newline="\n"
|
|
)
|
|
|
|
result = lookup.query_sections(
|
|
"needle", "explain", limit=4, references_dir=references
|
|
)
|
|
|
|
self.assertEqual(
|
|
[match.section.page.name for match in result.matches],
|
|
["z_heading.md", "c_identifier.md", "b_title.md", "a_body.md"],
|
|
)
|
|
|
|
def test_tsl_identifier_is_preserved(self):
|
|
result = lookup.query_sections("varByRef 命名参数", "explain", limit=5)
|
|
searchable_matches = "\n".join(
|
|
match.section.searchable_text for match in result.matches
|
|
)
|
|
|
|
self.assertRegex(searchable_matches, r"(?i)varByRef")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|