1
0
Fork 0
Code Issues Pull requests Projects Releases 2 Packages Wiki Activity Actions Pages
DocForge2/tests/test_treesitter_logic.py

197 lines
5.9 KiB
Python
Raw Normal View History

2026-07-25 22:29:15 -04:00
from __future__ import annotations
import unittest
from docforge.errors import DocForgeError
from docforge.treesitter_logic import (
TreeSitterLogicOwner,
analyze_cpp_source,
analyze_javascript_source,
)
class JavaScriptLogicTests(unittest.TestCase):
def test_comments_do_not_become_logic_actions(self) -> None:
source = """
function choose(enabled) {
// Explain the condition.
// Continue the explanation.
/* A block comment is trivia too. */
if (enabled) {
accept();
}
}
""".strip()
projection = analyze_javascript_source(
source,
source_id="source.javascript",
owners=(TreeSitterLogicOwner("js.symbol.choose", "choose", 1),),
)[0]
labels = {node.label for node in projection.nodes}
self.assertFalse(any(label.startswith(("//", "/*")) for label in labels))
self.assertIn("accept();", labels)
2026-07-25 22:29:15 -04:00
def test_branches_short_circuit_and_converge(self) -> None:
source = """
function choose(enabled, ready) {
if (enabled && ready()) {
accept();
} else {
reject();
}
return enabled;
}
""".strip()
projection = analyze_javascript_source(
source,
source_id="source.javascript",
owners=(TreeSitterLogicOwner("js.symbol.choose", "choose", 1),),
)[0]
kinds = {node.kind for node in projection.nodes}
labels = {node.label for node in projection.nodes}
relations = {edge.relation for edge in projection.edges}
self.assertIn("condition", kinds)
self.assertIn("convergence", kinds)
self.assertIn("Decision convergence", labels)
self.assertIn("when_true", relations)
self.assertIn("when_false", relations)
self.assertIn("return", relations)
def test_arrow_function_expression_is_a_returning_projection(self) -> None:
source = "const choose = (enabled) => enabled ? accept() : reject();"
projection = analyze_javascript_source(
source,
source_id="source.javascript",
owners=(TreeSitterLogicOwner("js.symbol.choose", "choose", 1),),
)[0]
labels = {node.label for node in projection.nodes}
self.assertIn("enabled", labels)
self.assertIn("return accept()", labels)
self.assertIn("return reject()", labels)
def test_loops_switch_and_exception_paths_are_preserved(self) -> None:
source = """
function process(items, mode) {
for (const item of items) {
if (!item.ready) continue;
use(item);
}
switch (mode) {
case 1:
one();
break;
default:
fallback();
}
try {
risk();
} catch (error) {
recover(error);
} finally {
clean();
}
}
""".strip()
projection = analyze_javascript_source(
source,
source_id="source.javascript",
owners=(TreeSitterLogicOwner("js.symbol.process", "process", 1),),
)[0]
kinds = {node.kind for node in projection.nodes}
labels = {node.label for node in projection.nodes}
relations = {edge.relation for edge in projection.edges}
self.assertTrue({"loop", "continue", "case", "try", "except", "finally"} <= kinds)
self.assertIn("Case convergence", labels)
self.assertIn("Exception convergence", labels)
self.assertTrue({"loop", "continue", "case", "exception"} <= relations)
class CppLogicTests(unittest.TestCase):
def test_comments_do_not_become_logic_actions(self) -> None:
source = """
int choose(bool enabled) {
// Explain the condition.
/* A block comment is trivia too. */
if (enabled) {
return 1;
}
return 0;
}
""".strip()
projection = analyze_cpp_source(
source,
source_id="source.cpp",
owners=(TreeSitterLogicOwner("cpp.symbol.choose", "choose", 1),),
)[0]
labels = {node.label for node in projection.nodes}
self.assertFalse(any(label.startswith(("//", "/*")) for label in labels))
self.assertIn("return 1", labels)
2026-07-25 22:29:15 -04:00
def test_cpp_function_branches_and_throws(self) -> None:
source = """
int choose(bool enabled) {
if (enabled) {
return 1;
}
throw Error();
}
""".strip()
projection = analyze_cpp_source(
source,
source_id="source.cpp",
owners=(TreeSitterLogicOwner("cpp.symbol.choose", "choose", 1),),
)[0]
kinds = {node.kind for node in projection.nodes}
relations = {edge.relation for edge in projection.edges}
self.assertTrue({"entry", "condition", "return", "raise", "exit"} <= kinds)
self.assertTrue({"when_true", "when_false", "return", "raise"} <= relations)
def test_cpp_qualified_method_owner_is_resolved(self) -> None:
source = """
class Worker {
public:
int run(bool ready) {
while (ready) {
ready = tick();
}
return 0;
}
};
""".strip()
projection = analyze_cpp_source(
source,
source_id="source.cpp",
owners=(TreeSitterLogicOwner("cpp.symbol.worker.run", "Worker.run", 3),),
)[0]
labels = {node.label for node in projection.nodes}
self.assertIn("Loop exit", labels)
self.assertIn("return 0", labels)
def test_invalid_source_and_missing_owner_fail_closed(self) -> None:
with self.assertRaises(DocForgeError) as invalid:
analyze_cpp_source(
"int broken( {",
source_id="source.cpp",
owners=(),
)
self.assertEqual(invalid.exception.code, "invalid_logic_source")
with self.assertRaises(DocForgeError) as missing:
analyze_javascript_source(
"function exists() {}",
source_id="source.javascript",
owners=(TreeSitterLogicOwner("js.symbol.missing", "missing", 1),),
)
self.assertEqual(missing.exception.code, "missing_logic_owner")
if __name__ == "__main__":
unittest.main()