Navegador

navegador / tests / test_new_language_parsers.py
Source Blame History 1033 lines
fa82b95… lmata 1 """
fa82b95… lmata 2 Tests for the 7 new language parsers:
fa82b95… lmata 3 KotlinParser, CSharpParser, PHPParser, RubyParser, SwiftParser, CParser, CppParser
fa82b95… lmata 4
fa82b95… lmata 5 All tree-sitter grammar imports are mocked so no grammars need to be installed.
fa82b95… lmata 6 """
fa82b95… lmata 7
fa82b95… lmata 8 import tempfile
fa82b95… lmata 9 from pathlib import Path
fa82b95… lmata 10 from unittest.mock import MagicMock, patch
fa82b95… lmata 11
fa82b95… lmata 12 import pytest
fa82b95… lmata 13
fa82b95… lmata 14 from navegador.graph.schema import NodeLabel
fa82b95… lmata 15 from navegador.ingestion.parser import LANGUAGE_MAP, RepoIngester
fa82b95… lmata 16
fa82b95… lmata 17
fa82b95… lmata 18 # ── Shared helpers ────────────────────────────────────────────────────────────
fa82b95… lmata 19
fa82b95… lmata 20
fa82b95… lmata 21 class MockNode:
fa82b95… lmata 22 _id_counter = 0
fa82b95… lmata 23
fa82b95… lmata 24 def __init__(
fa82b95… lmata 25 self,
fa82b95… lmata 26 type_: str,
fa82b95… lmata 27 text: bytes = b"",
fa82b95… lmata 28 children: list = None,
fa82b95… lmata 29 start_byte: int = 0,
fa82b95… lmata 30 end_byte: int = 0,
fa82b95… lmata 31 start_point: tuple = (0, 0),
fa82b95… lmata 32 end_point: tuple = (0, 0),
fa82b95… lmata 33 parent=None,
fa82b95… lmata 34 ):
fa82b95… lmata 35 MockNode._id_counter += 1
fa82b95… lmata 36 self.id = MockNode._id_counter
fa82b95… lmata 37 self.type = type_
fa82b95… lmata 38 self.children = children or []
fa82b95… lmata 39 self.start_byte = start_byte
fa82b95… lmata 40 self.end_byte = end_byte
fa82b95… lmata 41 self.start_point = start_point
fa82b95… lmata 42 self.end_point = end_point
fa82b95… lmata 43 self.parent = parent
fa82b95… lmata 44 self._fields: dict = {}
fa82b95… lmata 45 for child in self.children:
fa82b95… lmata 46 child.parent = self
fa82b95… lmata 47
fa82b95… lmata 48 def child_by_field_name(self, name: str):
fa82b95… lmata 49 return self._fields.get(name)
fa82b95… lmata 50
fa82b95… lmata 51 def set_field(self, name: str, node):
fa82b95… lmata 52 self._fields[name] = node
fa82b95… lmata 53 node.parent = self
fa82b95… lmata 54 return self
fa82b95… lmata 55
fa82b95… lmata 56
fa82b95… lmata 57 def _text_node(text: bytes, type_: str = "identifier") -> MockNode:
fa82b95… lmata 58 return MockNode(type_, text, start_byte=0, end_byte=len(text))
fa82b95… lmata 59
fa82b95… lmata 60
fa82b95… lmata 61 def _make_store():
fa82b95… lmata 62 store = MagicMock()
fa82b95… lmata 63 store.query.return_value = MagicMock(result_set=[])
fa82b95… lmata 64 return store
fa82b95… lmata 65
fa82b95… lmata 66
fa82b95… lmata 67 def _make_mock_tree(root_node: MockNode):
fa82b95… lmata 68 tree = MagicMock()
fa82b95… lmata 69 tree.root_node = root_node
fa82b95… lmata 70 return tree
fa82b95… lmata 71
fa82b95… lmata 72
fa82b95… lmata 73 def _mock_ts_modules(lang_module_name: str):
fa82b95… lmata 74 """Return a patch.dict context that mocks tree_sitter and the given grammar module."""
fa82b95… lmata 75 mock_lang_module = MagicMock()
fa82b95… lmata 76 mock_ts = MagicMock()
fa82b95… lmata 77 return patch.dict("sys.modules", {lang_module_name: mock_lang_module, "tree_sitter": mock_ts})
fa82b95… lmata 78
fa82b95… lmata 79
fa82b95… lmata 80 # ── LANGUAGE_MAP coverage ─────────────────────────────────────────────────────
fa82b95… lmata 81
fa82b95… lmata 82
fa82b95… lmata 83 class TestLanguageMapExtensions:
fa82b95… lmata 84 def test_kotlin_kt(self):
fa82b95… lmata 85 assert LANGUAGE_MAP[".kt"] == "kotlin"
fa82b95… lmata 86
fa82b95… lmata 87 def test_kotlin_kts(self):
fa82b95… lmata 88 assert LANGUAGE_MAP[".kts"] == "kotlin"
fa82b95… lmata 89
fa82b95… lmata 90 def test_csharp_cs(self):
fa82b95… lmata 91 assert LANGUAGE_MAP[".cs"] == "csharp"
fa82b95… lmata 92
fa82b95… lmata 93 def test_php(self):
fa82b95… lmata 94 assert LANGUAGE_MAP[".php"] == "php"
fa82b95… lmata 95
fa82b95… lmata 96 def test_ruby_rb(self):
fa82b95… lmata 97 assert LANGUAGE_MAP[".rb"] == "ruby"
fa82b95… lmata 98
fa82b95… lmata 99 def test_swift(self):
fa82b95… lmata 100 assert LANGUAGE_MAP[".swift"] == "swift"
fa82b95… lmata 101
fa82b95… lmata 102 def test_c_c(self):
fa82b95… lmata 103 assert LANGUAGE_MAP[".c"] == "c"
fa82b95… lmata 104
fa82b95… lmata 105 def test_c_h(self):
fa82b95… lmata 106 assert LANGUAGE_MAP[".h"] == "c"
fa82b95… lmata 107
fa82b95… lmata 108 def test_cpp_cpp(self):
fa82b95… lmata 109 assert LANGUAGE_MAP[".cpp"] == "cpp"
fa82b95… lmata 110
fa82b95… lmata 111 def test_cpp_hpp(self):
fa82b95… lmata 112 assert LANGUAGE_MAP[".hpp"] == "cpp"
fa82b95… lmata 113
fa82b95… lmata 114 def test_cpp_cc(self):
fa82b95… lmata 115 assert LANGUAGE_MAP[".cc"] == "cpp"
fa82b95… lmata 116
fa82b95… lmata 117 def test_cpp_cxx(self):
fa82b95… lmata 118 assert LANGUAGE_MAP[".cxx"] == "cpp"
fa82b95… lmata 119
fa82b95… lmata 120
fa82b95… lmata 121 # ── _get_parser dispatch ──────────────────────────────────────────────────────
fa82b95… lmata 122
fa82b95… lmata 123
fa82b95… lmata 124 class TestGetParserDispatch:
fa82b95… lmata 125 def _make_ingester(self):
fa82b95… lmata 126 store = _make_store()
fa82b95… lmata 127 ingester = RepoIngester.__new__(RepoIngester)
fa82b95… lmata 128 ingester.store = store
fa82b95… lmata 129 ingester.redact = False
fa82b95… lmata 130 ingester._detector = None
fa82b95… lmata 131 ingester._parsers = {}
fa82b95… lmata 132 return ingester
fa82b95… lmata 133
fa82b95… lmata 134 def _test_parser_type(self, language: str, grammar_module: str, parser_cls_name: str):
fa82b95… lmata 135 ingester = self._make_ingester()
fa82b95… lmata 136 with _mock_ts_modules(grammar_module):
fa82b95… lmata 137 # Also need to force re-import of the parser module
fa82b95… lmata 138 import sys
fa82b95… lmata 139 mod_name = f"navegador.ingestion.{language}"
fa82b95… lmata 140 if mod_name in sys.modules:
fa82b95… lmata 141 del sys.modules[mod_name]
fa82b95… lmata 142 parser = ingester._get_parser(language)
fa82b95… lmata 143 assert type(parser).__name__ == parser_cls_name
fa82b95… lmata 144
fa82b95… lmata 145 def test_kotlin_parser(self):
fa82b95… lmata 146 self._test_parser_type("kotlin", "tree_sitter_kotlin", "KotlinParser")
fa82b95… lmata 147
fa82b95… lmata 148 def test_csharp_parser(self):
fa82b95… lmata 149 self._test_parser_type("csharp", "tree_sitter_c_sharp", "CSharpParser")
fa82b95… lmata 150
fa82b95… lmata 151 def test_php_parser(self):
fa82b95… lmata 152 self._test_parser_type("php", "tree_sitter_php", "PHPParser")
fa82b95… lmata 153
fa82b95… lmata 154 def test_ruby_parser(self):
fa82b95… lmata 155 self._test_parser_type("ruby", "tree_sitter_ruby", "RubyParser")
fa82b95… lmata 156
fa82b95… lmata 157 def test_swift_parser(self):
fa82b95… lmata 158 self._test_parser_type("swift", "tree_sitter_swift", "SwiftParser")
fa82b95… lmata 159
fa82b95… lmata 160 def test_c_parser(self):
fa82b95… lmata 161 self._test_parser_type("c", "tree_sitter_c", "CParser")
fa82b95… lmata 162
fa82b95… lmata 163 def test_cpp_parser(self):
fa82b95… lmata 164 self._test_parser_type("cpp", "tree_sitter_cpp", "CppParser")
fa82b95… lmata 165
fa82b95… lmata 166
fa82b95… lmata 167 # ── _get_*_language ImportError ────────────────────────────────────────────────
fa82b95… lmata 168
fa82b95… lmata 169
fa82b95… lmata 170 class TestMissingGrammars:
fa82b95… lmata 171 def _assert_import_error(self, module_path: str, fn_name: str, grammar_pkg: str, grammar_module: str):
fa82b95… lmata 172 import importlib
fa82b95… lmata 173 import sys
fa82b95… lmata 174 # Remove cached module if present
fa82b95… lmata 175 if module_path in sys.modules:
fa82b95… lmata 176 del sys.modules[module_path]
fa82b95… lmata 177 with patch.dict("sys.modules", {grammar_module: None, "tree_sitter": None}):
fa82b95… lmata 178 mod = importlib.import_module(module_path)
fa82b95… lmata 179 fn = getattr(mod, fn_name)
fa82b95… lmata 180 with pytest.raises(ImportError, match=grammar_pkg):
fa82b95… lmata 181 fn()
fa82b95… lmata 182
fa82b95… lmata 183 def test_kotlin_missing(self):
fa82b95… lmata 184 self._assert_import_error(
fa82b95… lmata 185 "navegador.ingestion.kotlin", "_get_kotlin_language",
fa82b95… lmata 186 "tree-sitter-kotlin", "tree_sitter_kotlin",
fa82b95… lmata 187 )
fa82b95… lmata 188
fa82b95… lmata 189 def test_csharp_missing(self):
fa82b95… lmata 190 self._assert_import_error(
fa82b95… lmata 191 "navegador.ingestion.csharp", "_get_csharp_language",
fa82b95… lmata 192 "tree-sitter-c-sharp", "tree_sitter_c_sharp",
fa82b95… lmata 193 )
fa82b95… lmata 194
fa82b95… lmata 195 def test_php_missing(self):
fa82b95… lmata 196 self._assert_import_error(
fa82b95… lmata 197 "navegador.ingestion.php", "_get_php_language",
fa82b95… lmata 198 "tree-sitter-php", "tree_sitter_php",
fa82b95… lmata 199 )
fa82b95… lmata 200
fa82b95… lmata 201 def test_ruby_missing(self):
fa82b95… lmata 202 self._assert_import_error(
fa82b95… lmata 203 "navegador.ingestion.ruby", "_get_ruby_language",
fa82b95… lmata 204 "tree-sitter-ruby", "tree_sitter_ruby",
fa82b95… lmata 205 )
fa82b95… lmata 206
fa82b95… lmata 207 def test_swift_missing(self):
fa82b95… lmata 208 self._assert_import_error(
fa82b95… lmata 209 "navegador.ingestion.swift", "_get_swift_language",
fa82b95… lmata 210 "tree-sitter-swift", "tree_sitter_swift",
fa82b95… lmata 211 )
fa82b95… lmata 212
fa82b95… lmata 213 def test_c_missing(self):
fa82b95… lmata 214 self._assert_import_error(
fa82b95… lmata 215 "navegador.ingestion.c", "_get_c_language",
fa82b95… lmata 216 "tree-sitter-c", "tree_sitter_c",
fa82b95… lmata 217 )
fa82b95… lmata 218
fa82b95… lmata 219 def test_cpp_missing(self):
fa82b95… lmata 220 self._assert_import_error(
fa82b95… lmata 221 "navegador.ingestion.cpp", "_get_cpp_language",
fa82b95… lmata 222 "tree-sitter-cpp", "tree_sitter_cpp",
fa82b95… lmata 223 )
fa82b95… lmata 224
fa82b95… lmata 225
fa82b95… lmata 226 # ── KotlinParser ──────────────────────────────────────────────────────────────
fa82b95… lmata 227
fa82b95… lmata 228
fa82b95… lmata 229 def _make_kotlin_parser():
fa82b95… lmata 230 from navegador.ingestion.kotlin import KotlinParser
fa82b95… lmata 231 p = KotlinParser.__new__(KotlinParser)
fa82b95… lmata 232 p._parser = MagicMock()
fa82b95… lmata 233 return p
fa82b95… lmata 234
fa82b95… lmata 235
fa82b95… lmata 236 class TestKotlinParserFileNode:
fa82b95… lmata 237 def test_parse_file_creates_file_node(self):
fa82b95… lmata 238 parser = _make_kotlin_parser()
fa82b95… lmata 239 store = _make_store()
fa82b95… lmata 240 root = MockNode("source_file")
fa82b95… lmata 241 parser._parser.parse.return_value = _make_mock_tree(root)
fa82b95… lmata 242 with tempfile.NamedTemporaryFile(suffix=".kt", delete=False) as f:
fa82b95… lmata 243 f.write(b"fun main() {}\n")
fa82b95… lmata 244 fpath = Path(f.name)
fa82b95… lmata 245 try:
fa82b95… lmata 246 parser.parse_file(fpath, fpath.parent, store)
fa82b95… lmata 247 assert store.create_node.call_args[0][0] == NodeLabel.File
fa82b95… lmata 248 assert store.create_node.call_args[0][1]["language"] == "kotlin"
fa82b95… lmata 249 finally:
fa82b95… lmata 250 fpath.unlink()
fa82b95… lmata 251
fa82b95… lmata 252
fa82b95… lmata 253 class TestKotlinHandleClass:
fa82b95… lmata 254 def test_creates_class_node(self):
fa82b95… lmata 255 parser = _make_kotlin_parser()
fa82b95… lmata 256 store = _make_store()
fa82b95… lmata 257 source = b"class Foo {}"
fa82b95… lmata 258 name_node = _text_node(b"Foo", "simple_identifier")
fa82b95… lmata 259 body = MockNode("class_body")
fa82b95… lmata 260 node = MockNode("class_declaration", start_point=(0, 0), end_point=(0, 11))
fa82b95… lmata 261 node.set_field("name", name_node)
fa82b95… lmata 262 node.set_field("body", body)
fa82b95… lmata 263 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 264 parser._handle_class(node, source, "Foo.kt", store, stats)
fa82b95… lmata 265 assert stats["classes"] == 1
fa82b95… lmata 266 assert store.create_node.call_args[0][0] == NodeLabel.Class
fa82b95… lmata 267
fa82b95… lmata 268 def test_skips_if_no_name(self):
fa82b95… lmata 269 parser = _make_kotlin_parser()
fa82b95… lmata 270 store = _make_store()
fa82b95… lmata 271 node = MockNode("class_declaration")
fa82b95… lmata 272 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 273 parser._handle_class(node, b"", "Foo.kt", store, stats)
fa82b95… lmata 274 assert stats["classes"] == 0
fa82b95… lmata 275
fa82b95… lmata 276 def test_walks_member_functions(self):
fa82b95… lmata 277 parser = _make_kotlin_parser()
fa82b95… lmata 278 store = _make_store()
fa82b95… lmata 279 source = b"class Foo { fun bar() {} }"
fa82b95… lmata 280 class_name = _text_node(b"Foo", "simple_identifier")
fa82b95… lmata 281 fn_name = _text_node(b"bar", "simple_identifier")
fa82b95… lmata 282 fn_node = MockNode("function_declaration", start_point=(0, 12), end_point=(0, 24))
fa82b95… lmata 283 fn_node.set_field("name", fn_name)
fa82b95… lmata 284 body = MockNode("class_body", children=[fn_node])
fa82b95… lmata 285 node = MockNode("class_declaration", start_point=(0, 0), end_point=(0, 25))
fa82b95… lmata 286 node.set_field("name", class_name)
fa82b95… lmata 287 node.set_field("body", body)
fa82b95… lmata 288 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 289 parser._handle_class(node, source, "Foo.kt", store, stats)
fa82b95… lmata 290 assert stats["classes"] == 1
fa82b95… lmata 291 assert stats["functions"] == 1
fa82b95… lmata 292
fa82b95… lmata 293
fa82b95… lmata 294 class TestKotlinHandleFunction:
fa82b95… lmata 295 def test_creates_function_node(self):
fa82b95… lmata 296 parser = _make_kotlin_parser()
fa82b95… lmata 297 store = _make_store()
fa82b95… lmata 298 source = b"fun greet() {}"
fa82b95… lmata 299 name_node = _text_node(b"greet", "simple_identifier")
fa82b95… lmata 300 node = MockNode("function_declaration", start_point=(0, 0), end_point=(0, 13))
fa82b95… lmata 301 node.set_field("name", name_node)
fa82b95… lmata 302 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 303 parser._handle_function(node, source, "Foo.kt", store, stats, class_name=None)
fa82b95… lmata 304 assert stats["functions"] == 1
fa82b95… lmata 305 assert store.create_node.call_args[0][0] == NodeLabel.Function
fa82b95… lmata 306
fa82b95… lmata 307 def test_creates_method_node_in_class(self):
fa82b95… lmata 308 parser = _make_kotlin_parser()
fa82b95… lmata 309 store = _make_store()
fa82b95… lmata 310 source = b"fun run() {}"
fa82b95… lmata 311 name_node = _text_node(b"run", "simple_identifier")
fa82b95… lmata 312 node = MockNode("function_declaration", start_point=(0, 0), end_point=(0, 11))
fa82b95… lmata 313 node.set_field("name", name_node)
fa82b95… lmata 314 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 315 parser._handle_function(node, source, "Foo.kt", store, stats, class_name="Foo")
fa82b95… lmata 316 assert store.create_node.call_args[0][0] == NodeLabel.Method
fa82b95… lmata 317
fa82b95… lmata 318 def test_skips_if_no_name(self):
fa82b95… lmata 319 parser = _make_kotlin_parser()
fa82b95… lmata 320 store = _make_store()
fa82b95… lmata 321 node = MockNode("function_declaration")
fa82b95… lmata 322 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 323 parser._handle_function(node, b"", "Foo.kt", store, stats, class_name=None)
fa82b95… lmata 324 assert stats["functions"] == 0
fa82b95… lmata 325
fa82b95… lmata 326
fa82b95… lmata 327 class TestKotlinHandleImport:
fa82b95… lmata 328 def test_creates_import_node(self):
fa82b95… lmata 329 parser = _make_kotlin_parser()
fa82b95… lmata 330 store = _make_store()
fa82b95… lmata 331 source = b"import kotlin.collections.List"
fa82b95… lmata 332 node = MockNode("import_header", start_byte=0, end_byte=len(source), start_point=(0, 0))
fa82b95… lmata 333 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 334 parser._handle_import(node, source, "Foo.kt", store, stats)
fa82b95… lmata 335 assert stats["edges"] == 1
fa82b95… lmata 336 props = store.create_node.call_args[0][1]
fa82b95… lmata 337 assert "kotlin.collections.List" in props["name"]
fa82b95… lmata 338
fa82b95… lmata 339
fa82b95… lmata 340 # ── CSharpParser ───────────────────────────────────────────────────────────────
fa82b95… lmata 341
fa82b95… lmata 342
fa82b95… lmata 343 def _make_csharp_parser():
fa82b95… lmata 344 from navegador.ingestion.csharp import CSharpParser
fa82b95… lmata 345 p = CSharpParser.__new__(CSharpParser)
fa82b95… lmata 346 p._parser = MagicMock()
fa82b95… lmata 347 return p
fa82b95… lmata 348
fa82b95… lmata 349
fa82b95… lmata 350 class TestCSharpParserFileNode:
fa82b95… lmata 351 def test_parse_file_creates_file_node(self):
fa82b95… lmata 352 parser = _make_csharp_parser()
fa82b95… lmata 353 store = _make_store()
fa82b95… lmata 354 root = MockNode("compilation_unit")
fa82b95… lmata 355 parser._parser.parse.return_value = _make_mock_tree(root)
fa82b95… lmata 356 with tempfile.NamedTemporaryFile(suffix=".cs", delete=False) as f:
fa82b95… lmata 357 f.write(b"class Foo {}\n")
fa82b95… lmata 358 fpath = Path(f.name)
fa82b95… lmata 359 try:
fa82b95… lmata 360 parser.parse_file(fpath, fpath.parent, store)
fa82b95… lmata 361 assert store.create_node.call_args[0][0] == NodeLabel.File
fa82b95… lmata 362 assert store.create_node.call_args[0][1]["language"] == "csharp"
fa82b95… lmata 363 finally:
fa82b95… lmata 364 fpath.unlink()
fa82b95… lmata 365
fa82b95… lmata 366
fa82b95… lmata 367 class TestCSharpHandleClass:
fa82b95… lmata 368 def test_creates_class_node(self):
fa82b95… lmata 369 parser = _make_csharp_parser()
fa82b95… lmata 370 store = _make_store()
fa82b95… lmata 371 source = b"class Foo {}"
fa82b95… lmata 372 name_node = _text_node(b"Foo")
fa82b95… lmata 373 body = MockNode("declaration_list")
fa82b95… lmata 374 node = MockNode("class_declaration", start_point=(0, 0), end_point=(0, 11))
fa82b95… lmata 375 node.set_field("name", name_node)
fa82b95… lmata 376 node.set_field("body", body)
fa82b95… lmata 377 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 378 parser._handle_class(node, source, "Foo.cs", store, stats)
fa82b95… lmata 379 assert stats["classes"] == 1
fa82b95… lmata 380 assert store.create_node.call_args[0][0] == NodeLabel.Class
fa82b95… lmata 381
fa82b95… lmata 382 def test_skips_if_no_name(self):
fa82b95… lmata 383 parser = _make_csharp_parser()
fa82b95… lmata 384 store = _make_store()
fa82b95… lmata 385 node = MockNode("class_declaration")
fa82b95… lmata 386 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 387 parser._handle_class(node, b"", "Foo.cs", store, stats)
fa82b95… lmata 388 assert stats["classes"] == 0
fa82b95… lmata 389
fa82b95… lmata 390 def test_creates_inherits_edge(self):
fa82b95… lmata 391 parser = _make_csharp_parser()
fa82b95… lmata 392 store = _make_store()
fa82b95… lmata 393 source = b"class Child : Parent {}"
fa82b95… lmata 394 name_node = _text_node(b"Child")
fa82b95… lmata 395 parent_id = _text_node(b"Parent")
fa82b95… lmata 396 bases = MockNode("base_list", children=[parent_id])
fa82b95… lmata 397 body = MockNode("declaration_list")
fa82b95… lmata 398 node = MockNode("class_declaration", start_point=(0, 0), end_point=(0, 22))
fa82b95… lmata 399 node.set_field("name", name_node)
fa82b95… lmata 400 node.set_field("bases", bases)
fa82b95… lmata 401 node.set_field("body", body)
fa82b95… lmata 402 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 403 parser._handle_class(node, source, "Child.cs", store, stats)
fa82b95… lmata 404 assert stats["edges"] == 2 # CONTAINS + INHERITS
fa82b95… lmata 405
fa82b95… lmata 406 def test_walks_methods(self):
fa82b95… lmata 407 parser = _make_csharp_parser()
fa82b95… lmata 408 store = _make_store()
fa82b95… lmata 409 source = b"class Foo { void Save() {} }"
fa82b95… lmata 410 class_name_node = _text_node(b"Foo")
fa82b95… lmata 411 method_name_node = _text_node(b"Save")
fa82b95… lmata 412 method = MockNode("method_declaration", start_point=(0, 12), end_point=(0, 25))
fa82b95… lmata 413 method.set_field("name", method_name_node)
fa82b95… lmata 414 body = MockNode("declaration_list", children=[method])
fa82b95… lmata 415 node = MockNode("class_declaration", start_point=(0, 0), end_point=(0, 27))
fa82b95… lmata 416 node.set_field("name", class_name_node)
fa82b95… lmata 417 node.set_field("body", body)
fa82b95… lmata 418 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 419 parser._handle_class(node, source, "Foo.cs", store, stats)
fa82b95… lmata 420 assert stats["functions"] == 1
fa82b95… lmata 421
fa82b95… lmata 422
fa82b95… lmata 423 class TestCSharpHandleUsing:
fa82b95… lmata 424 def test_creates_import_node(self):
fa82b95… lmata 425 parser = _make_csharp_parser()
fa82b95… lmata 426 store = _make_store()
fa82b95… lmata 427 source = b"using System.Collections.Generic;"
fa82b95… lmata 428 node = MockNode("using_directive", start_byte=0, end_byte=len(source), start_point=(0, 0))
fa82b95… lmata 429 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 430 parser._handle_using(node, source, "Foo.cs", store, stats)
fa82b95… lmata 431 assert stats["edges"] == 1
fa82b95… lmata 432 props = store.create_node.call_args[0][1]
fa82b95… lmata 433 assert "System.Collections.Generic" in props["name"]
fa82b95… lmata 434
fa82b95… lmata 435
fa82b95… lmata 436 # ── PHPParser ─────────────────────────────────────────────────────────────────
fa82b95… lmata 437
fa82b95… lmata 438
fa82b95… lmata 439 def _make_php_parser():
fa82b95… lmata 440 from navegador.ingestion.php import PHPParser
fa82b95… lmata 441 p = PHPParser.__new__(PHPParser)
fa82b95… lmata 442 p._parser = MagicMock()
fa82b95… lmata 443 return p
fa82b95… lmata 444
fa82b95… lmata 445
fa82b95… lmata 446 class TestPHPParserFileNode:
fa82b95… lmata 447 def test_parse_file_creates_file_node(self):
fa82b95… lmata 448 parser = _make_php_parser()
fa82b95… lmata 449 store = _make_store()
fa82b95… lmata 450 root = MockNode("program")
fa82b95… lmata 451 parser._parser.parse.return_value = _make_mock_tree(root)
fa82b95… lmata 452 with tempfile.NamedTemporaryFile(suffix=".php", delete=False) as f:
fa82b95… lmata 453 f.write(b"<?php class Foo {} ?>\n")
fa82b95… lmata 454 fpath = Path(f.name)
fa82b95… lmata 455 try:
fa82b95… lmata 456 parser.parse_file(fpath, fpath.parent, store)
fa82b95… lmata 457 assert store.create_node.call_args[0][0] == NodeLabel.File
fa82b95… lmata 458 assert store.create_node.call_args[0][1]["language"] == "php"
fa82b95… lmata 459 finally:
fa82b95… lmata 460 fpath.unlink()
fa82b95… lmata 461
fa82b95… lmata 462
fa82b95… lmata 463 class TestPHPHandleClass:
fa82b95… lmata 464 def test_creates_class_node(self):
fa82b95… lmata 465 parser = _make_php_parser()
fa82b95… lmata 466 store = _make_store()
fa82b95… lmata 467 source = b"class Foo {}"
fa82b95… lmata 468 name_node = _text_node(b"Foo", "name")
fa82b95… lmata 469 body = MockNode("declaration_list")
fa82b95… lmata 470 node = MockNode("class_declaration", start_point=(0, 0), end_point=(0, 11))
fa82b95… lmata 471 node.set_field("name", name_node)
fa82b95… lmata 472 node.set_field("body", body)
fa82b95… lmata 473 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 474 parser._handle_class(node, source, "Foo.php", store, stats)
fa82b95… lmata 475 assert stats["classes"] == 1
fa82b95… lmata 476
fa82b95… lmata 477 def test_skips_if_no_name(self):
fa82b95… lmata 478 parser = _make_php_parser()
fa82b95… lmata 479 store = _make_store()
fa82b95… lmata 480 node = MockNode("class_declaration")
fa82b95… lmata 481 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 482 parser._handle_class(node, b"", "Foo.php", store, stats)
fa82b95… lmata 483 assert stats["classes"] == 0
fa82b95… lmata 484
fa82b95… lmata 485 def test_creates_inherits_edge(self):
fa82b95… lmata 486 parser = _make_php_parser()
fa82b95… lmata 487 store = _make_store()
fa82b95… lmata 488 source = b"class Child extends Parent {}"
fa82b95… lmata 489 name_node = _text_node(b"Child", "name")
fa82b95… lmata 490 parent_name_node = _text_node(b"Parent", "qualified_name")
fa82b95… lmata 491 base_clause = MockNode("base_clause", children=[parent_name_node])
fa82b95… lmata 492 body = MockNode("declaration_list")
fa82b95… lmata 493 node = MockNode("class_declaration", start_point=(0, 0), end_point=(0, 28))
fa82b95… lmata 494 node.set_field("name", name_node)
fa82b95… lmata 495 node.set_field("base_clause", base_clause)
fa82b95… lmata 496 node.set_field("body", body)
fa82b95… lmata 497 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 498 parser._handle_class(node, source, "Child.php", store, stats)
fa82b95… lmata 499 assert stats["edges"] == 2 # CONTAINS + INHERITS
fa82b95… lmata 500
fa82b95… lmata 501
fa82b95… lmata 502 class TestPHPHandleFunction:
fa82b95… lmata 503 def test_creates_function_node(self):
fa82b95… lmata 504 parser = _make_php_parser()
fa82b95… lmata 505 store = _make_store()
fa82b95… lmata 506 source = b"function save() {}"
fa82b95… lmata 507 name_node = _text_node(b"save", "name")
fa82b95… lmata 508 node = MockNode("function_definition", start_point=(0, 0), end_point=(0, 17))
fa82b95… lmata 509 node.set_field("name", name_node)
fa82b95… lmata 510 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 511 parser._handle_function(node, source, "Foo.php", store, stats, class_name=None)
fa82b95… lmata 512 assert stats["functions"] == 1
fa82b95… lmata 513 assert store.create_node.call_args[0][0] == NodeLabel.Function
fa82b95… lmata 514
fa82b95… lmata 515 def test_skips_if_no_name(self):
fa82b95… lmata 516 parser = _make_php_parser()
fa82b95… lmata 517 store = _make_store()
fa82b95… lmata 518 node = MockNode("function_definition")
fa82b95… lmata 519 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 520 parser._handle_function(node, b"", "Foo.php", store, stats, class_name=None)
fa82b95… lmata 521 assert stats["functions"] == 0
fa82b95… lmata 522
fa82b95… lmata 523
fa82b95… lmata 524 class TestPHPHandleUse:
fa82b95… lmata 525 def test_creates_import_node(self):
fa82b95… lmata 526 parser = _make_php_parser()
fa82b95… lmata 527 store = _make_store()
fa82b95… lmata 528 source = b"use App\\Http\\Controllers\\Controller;"
fa82b95… lmata 529 node = MockNode("use_declaration", start_byte=0, end_byte=len(source), start_point=(0, 0))
fa82b95… lmata 530 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 531 parser._handle_use(node, source, "Foo.php", store, stats)
fa82b95… lmata 532 assert stats["edges"] == 1
fa82b95… lmata 533 props = store.create_node.call_args[0][1]
fa82b95… lmata 534 assert "Controller" in props["name"]
fa82b95… lmata 535
fa82b95… lmata 536
fa82b95… lmata 537 # ── RubyParser ────────────────────────────────────────────────────────────────
fa82b95… lmata 538
fa82b95… lmata 539
fa82b95… lmata 540 def _make_ruby_parser():
fa82b95… lmata 541 from navegador.ingestion.ruby import RubyParser
fa82b95… lmata 542 p = RubyParser.__new__(RubyParser)
fa82b95… lmata 543 p._parser = MagicMock()
fa82b95… lmata 544 return p
fa82b95… lmata 545
fa82b95… lmata 546
fa82b95… lmata 547 class TestRubyParserFileNode:
fa82b95… lmata 548 def test_parse_file_creates_file_node(self):
fa82b95… lmata 549 parser = _make_ruby_parser()
fa82b95… lmata 550 store = _make_store()
fa82b95… lmata 551 root = MockNode("program")
fa82b95… lmata 552 parser._parser.parse.return_value = _make_mock_tree(root)
fa82b95… lmata 553 with tempfile.NamedTemporaryFile(suffix=".rb", delete=False) as f:
fa82b95… lmata 554 f.write(b"class Foo; end\n")
fa82b95… lmata 555 fpath = Path(f.name)
fa82b95… lmata 556 try:
fa82b95… lmata 557 parser.parse_file(fpath, fpath.parent, store)
fa82b95… lmata 558 assert store.create_node.call_args[0][0] == NodeLabel.File
fa82b95… lmata 559 assert store.create_node.call_args[0][1]["language"] == "ruby"
fa82b95… lmata 560 finally:
fa82b95… lmata 561 fpath.unlink()
fa82b95… lmata 562
fa82b95… lmata 563
fa82b95… lmata 564 class TestRubyHandleClass:
fa82b95… lmata 565 def test_creates_class_node(self):
fa82b95… lmata 566 parser = _make_ruby_parser()
fa82b95… lmata 567 store = _make_store()
fa82b95… lmata 568 source = b"class Foo; end"
fa82b95… lmata 569 name_node = _text_node(b"Foo", "constant")
fa82b95… lmata 570 body = MockNode("body_statement")
fa82b95… lmata 571 node = MockNode("class", start_point=(0, 0), end_point=(0, 13))
fa82b95… lmata 572 node.set_field("name", name_node)
fa82b95… lmata 573 node.set_field("body", body)
fa82b95… lmata 574 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 575 parser._handle_class(node, source, "foo.rb", store, stats)
fa82b95… lmata 576 assert stats["classes"] == 1
fa82b95… lmata 577
fa82b95… lmata 578 def test_skips_if_no_name(self):
fa82b95… lmata 579 parser = _make_ruby_parser()
fa82b95… lmata 580 store = _make_store()
fa82b95… lmata 581 node = MockNode("class")
fa82b95… lmata 582 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 583 parser._handle_class(node, b"", "foo.rb", store, stats)
fa82b95… lmata 584 assert stats["classes"] == 0
fa82b95… lmata 585
fa82b95… lmata 586 def test_creates_inherits_edge(self):
fa82b95… lmata 587 parser = _make_ruby_parser()
fa82b95… lmata 588 store = _make_store()
fa82b95… lmata 589 source = b"class Child < Parent; end"
fa82b95… lmata 590 name_node = _text_node(b"Child", "constant")
fa82b95… lmata 591 superclass_node = _text_node(b"Parent", "constant")
fa82b95… lmata 592 body = MockNode("body_statement")
fa82b95… lmata 593 node = MockNode("class", start_point=(0, 0), end_point=(0, 24))
fa82b95… lmata 594 node.set_field("name", name_node)
fa82b95… lmata 595 node.set_field("superclass", superclass_node)
fa82b95… lmata 596 node.set_field("body", body)
fa82b95… lmata 597 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 598 parser._handle_class(node, source, "child.rb", store, stats)
fa82b95… lmata 599 assert stats["edges"] >= 2 # CONTAINS + INHERITS
fa82b95… lmata 600
fa82b95… lmata 601 def test_walks_body_methods(self):
fa82b95… lmata 602 parser = _make_ruby_parser()
fa82b95… lmata 603 store = _make_store()
fa82b95… lmata 604 source = b"class Foo; def run; end; end"
fa82b95… lmata 605 class_name_node = _text_node(b"Foo", "constant")
fa82b95… lmata 606 method_name_node = _text_node(b"run")
fa82b95… lmata 607 method_node = MockNode("method", start_point=(0, 11), end_point=(0, 22))
fa82b95… lmata 608 method_node.set_field("name", method_name_node)
fa82b95… lmata 609 body = MockNode("body_statement", children=[method_node])
fa82b95… lmata 610 node = MockNode("class", start_point=(0, 0), end_point=(0, 26))
fa82b95… lmata 611 node.set_field("name", class_name_node)
fa82b95… lmata 612 node.set_field("body", body)
fa82b95… lmata 613 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 614 parser._handle_class(node, source, "foo.rb", store, stats)
fa82b95… lmata 615 assert stats["functions"] == 1
fa82b95… lmata 616
fa82b95… lmata 617
fa82b95… lmata 618 class TestRubyHandleMethod:
fa82b95… lmata 619 def test_creates_function_node(self):
fa82b95… lmata 620 parser = _make_ruby_parser()
fa82b95… lmata 621 store = _make_store()
fa82b95… lmata 622 source = b"def run; end"
fa82b95… lmata 623 name_node = _text_node(b"run")
fa82b95… lmata 624 node = MockNode("method", start_point=(0, 0), end_point=(0, 11))
fa82b95… lmata 625 node.set_field("name", name_node)
fa82b95… lmata 626 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 627 parser._handle_method(node, source, "foo.rb", store, stats, class_name=None)
fa82b95… lmata 628 assert stats["functions"] == 1
fa82b95… lmata 629 assert store.create_node.call_args[0][0] == NodeLabel.Function
fa82b95… lmata 630
fa82b95… lmata 631 def test_creates_method_node_in_class(self):
fa82b95… lmata 632 parser = _make_ruby_parser()
fa82b95… lmata 633 store = _make_store()
fa82b95… lmata 634 source = b"def run; end"
fa82b95… lmata 635 name_node = _text_node(b"run")
fa82b95… lmata 636 node = MockNode("method", start_point=(0, 0), end_point=(0, 11))
fa82b95… lmata 637 node.set_field("name", name_node)
fa82b95… lmata 638 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 639 parser._handle_method(node, source, "foo.rb", store, stats, class_name="Foo")
fa82b95… lmata 640 assert store.create_node.call_args[0][0] == NodeLabel.Method
fa82b95… lmata 641
fa82b95… lmata 642 def test_skips_if_no_name(self):
fa82b95… lmata 643 parser = _make_ruby_parser()
fa82b95… lmata 644 store = _make_store()
fa82b95… lmata 645 node = MockNode("method")
fa82b95… lmata 646 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 647 parser._handle_method(node, b"", "foo.rb", store, stats, class_name=None)
fa82b95… lmata 648 assert stats["functions"] == 0
fa82b95… lmata 649
fa82b95… lmata 650
fa82b95… lmata 651 class TestRubyHandleModule:
fa82b95… lmata 652 def test_creates_module_node(self):
fa82b95… lmata 653 parser = _make_ruby_parser()
fa82b95… lmata 654 store = _make_store()
fa82b95… lmata 655 source = b"module Concerns; end"
fa82b95… lmata 656 name_node = _text_node(b"Concerns", "constant")
fa82b95… lmata 657 body = MockNode("body_statement")
fa82b95… lmata 658 node = MockNode("module", start_point=(0, 0), end_point=(0, 19))
fa82b95… lmata 659 node.set_field("name", name_node)
fa82b95… lmata 660 node.set_field("body", body)
fa82b95… lmata 661 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 662 parser._handle_module(node, source, "concerns.rb", store, stats)
fa82b95… lmata 663 assert stats["classes"] == 1
fa82b95… lmata 664 props = store.create_node.call_args[0][1]
fa82b95… lmata 665 assert props.get("docstring") == "module"
fa82b95… lmata 666
fa82b95… lmata 667
fa82b95… lmata 668 # ── SwiftParser ───────────────────────────────────────────────────────────────
fa82b95… lmata 669
fa82b95… lmata 670
fa82b95… lmata 671 def _make_swift_parser():
fa82b95… lmata 672 from navegador.ingestion.swift import SwiftParser
fa82b95… lmata 673 p = SwiftParser.__new__(SwiftParser)
fa82b95… lmata 674 p._parser = MagicMock()
fa82b95… lmata 675 return p
fa82b95… lmata 676
fa82b95… lmata 677
fa82b95… lmata 678 class TestSwiftParserFileNode:
fa82b95… lmata 679 def test_parse_file_creates_file_node(self):
fa82b95… lmata 680 parser = _make_swift_parser()
fa82b95… lmata 681 store = _make_store()
fa82b95… lmata 682 root = MockNode("source_file")
fa82b95… lmata 683 parser._parser.parse.return_value = _make_mock_tree(root)
fa82b95… lmata 684 with tempfile.NamedTemporaryFile(suffix=".swift", delete=False) as f:
fa82b95… lmata 685 f.write(b"class Foo {}\n")
fa82b95… lmata 686 fpath = Path(f.name)
fa82b95… lmata 687 try:
fa82b95… lmata 688 parser.parse_file(fpath, fpath.parent, store)
fa82b95… lmata 689 assert store.create_node.call_args[0][0] == NodeLabel.File
fa82b95… lmata 690 assert store.create_node.call_args[0][1]["language"] == "swift"
fa82b95… lmata 691 finally:
fa82b95… lmata 692 fpath.unlink()
fa82b95… lmata 693
fa82b95… lmata 694
fa82b95… lmata 695 class TestSwiftHandleClass:
fa82b95… lmata 696 def test_creates_class_node(self):
fa82b95… lmata 697 parser = _make_swift_parser()
fa82b95… lmata 698 store = _make_store()
fa82b95… lmata 699 source = b"class Foo {}"
fa82b95… lmata 700 name_node = _text_node(b"Foo", "type_identifier")
fa82b95… lmata 701 body = MockNode("class_body")
fa82b95… lmata 702 node = MockNode("class_declaration", start_point=(0, 0), end_point=(0, 11))
fa82b95… lmata 703 node.set_field("name", name_node)
fa82b95… lmata 704 node.set_field("body", body)
fa82b95… lmata 705 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 706 parser._handle_class(node, source, "Foo.swift", store, stats)
fa82b95… lmata 707 assert stats["classes"] == 1
fa82b95… lmata 708
fa82b95… lmata 709 def test_skips_if_no_name(self):
fa82b95… lmata 710 parser = _make_swift_parser()
fa82b95… lmata 711 store = _make_store()
fa82b95… lmata 712 node = MockNode("class_declaration")
fa82b95… lmata 713 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 714 parser._handle_class(node, b"", "Foo.swift", store, stats)
fa82b95… lmata 715 assert stats["classes"] == 0
fa82b95… lmata 716
fa82b95… lmata 717 def test_creates_inherits_edge(self):
fa82b95… lmata 718 parser = _make_swift_parser()
fa82b95… lmata 719 store = _make_store()
fa82b95… lmata 720 source = b"class Child: Parent {}"
fa82b95… lmata 721 name_node = _text_node(b"Child", "type_identifier")
fa82b95… lmata 722 parent_id = _text_node(b"Parent", "type_identifier")
fa82b95… lmata 723 inheritance = MockNode("type_inheritance_clause", children=[parent_id])
fa82b95… lmata 724 body = MockNode("class_body")
fa82b95… lmata 725 node = MockNode("class_declaration", start_point=(0, 0), end_point=(0, 21))
fa82b95… lmata 726 node.set_field("name", name_node)
fa82b95… lmata 727 node.set_field("type_inheritance_clause", inheritance)
fa82b95… lmata 728 node.set_field("body", body)
fa82b95… lmata 729 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 730 parser._handle_class(node, source, "Child.swift", store, stats)
fa82b95… lmata 731 assert stats["edges"] == 2 # CONTAINS + INHERITS
fa82b95… lmata 732
fa82b95… lmata 733 def test_walks_body_functions(self):
fa82b95… lmata 734 parser = _make_swift_parser()
fa82b95… lmata 735 store = _make_store()
fa82b95… lmata 736 source = b"class Foo { func run() {} }"
fa82b95… lmata 737 class_name = _text_node(b"Foo", "type_identifier")
fa82b95… lmata 738 fn_name = _text_node(b"run", "simple_identifier")
fa82b95… lmata 739 fn_node = MockNode("function_declaration", start_point=(0, 12), end_point=(0, 24))
fa82b95… lmata 740 fn_node.set_field("name", fn_name)
fa82b95… lmata 741 body = MockNode("class_body", children=[fn_node])
fa82b95… lmata 742 node = MockNode("class_declaration", start_point=(0, 0), end_point=(0, 26))
fa82b95… lmata 743 node.set_field("name", class_name)
fa82b95… lmata 744 node.set_field("body", body)
fa82b95… lmata 745 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 746 parser._handle_class(node, source, "Foo.swift", store, stats)
fa82b95… lmata 747 assert stats["functions"] == 1
fa82b95… lmata 748
fa82b95… lmata 749
fa82b95… lmata 750 class TestSwiftHandleImport:
fa82b95… lmata 751 def test_creates_import_node(self):
fa82b95… lmata 752 parser = _make_swift_parser()
fa82b95… lmata 753 store = _make_store()
fa82b95… lmata 754 source = b"import Foundation"
fa82b95… lmata 755 node = MockNode("import_declaration", start_byte=0, end_byte=len(source), start_point=(0, 0))
fa82b95… lmata 756 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 757 parser._handle_import(node, source, "Foo.swift", store, stats)
fa82b95… lmata 758 assert stats["edges"] == 1
fa82b95… lmata 759 props = store.create_node.call_args[0][1]
fa82b95… lmata 760 assert "Foundation" in props["name"]
fa82b95… lmata 761
fa82b95… lmata 762
fa82b95… lmata 763 # ── CParser ───────────────────────────────────────────────────────────────────
fa82b95… lmata 764
fa82b95… lmata 765
fa82b95… lmata 766 def _make_c_parser():
fa82b95… lmata 767 from navegador.ingestion.c import CParser
fa82b95… lmata 768 p = CParser.__new__(CParser)
fa82b95… lmata 769 p._parser = MagicMock()
fa82b95… lmata 770 return p
fa82b95… lmata 771
fa82b95… lmata 772
fa82b95… lmata 773 class TestCParserFileNode:
fa82b95… lmata 774 def test_parse_file_creates_file_node(self):
fa82b95… lmata 775 parser = _make_c_parser()
fa82b95… lmata 776 store = _make_store()
fa82b95… lmata 777 root = MockNode("translation_unit")
fa82b95… lmata 778 parser._parser.parse.return_value = _make_mock_tree(root)
fa82b95… lmata 779 with tempfile.NamedTemporaryFile(suffix=".c", delete=False) as f:
fa82b95… lmata 780 f.write(b"void foo() {}\n")
fa82b95… lmata 781 fpath = Path(f.name)
fa82b95… lmata 782 try:
fa82b95… lmata 783 parser.parse_file(fpath, fpath.parent, store)
fa82b95… lmata 784 assert store.create_node.call_args[0][0] == NodeLabel.File
fa82b95… lmata 785 assert store.create_node.call_args[0][1]["language"] == "c"
fa82b95… lmata 786 finally:
fa82b95… lmata 787 fpath.unlink()
fa82b95… lmata 788
fa82b95… lmata 789
fa82b95… lmata 790 class TestCHandleFunction:
fa82b95… lmata 791 def _make_function_node(self, fn_name: bytes) -> tuple[MockNode, bytes]:
fa82b95… lmata 792 source = fn_name + b"(void) {}"
fa82b95… lmata 793 fn_id = _text_node(fn_name)
fa82b95… lmata 794 fn_decl = MockNode("function_declarator")
fa82b95… lmata 795 fn_decl.set_field("declarator", fn_id)
fa82b95… lmata 796 body = MockNode("compound_statement")
fa82b95… lmata 797 node = MockNode("function_definition", start_point=(0, 0), end_point=(0, len(source)))
fa82b95… lmata 798 node.set_field("declarator", fn_decl)
fa82b95… lmata 799 node.set_field("body", body)
fa82b95… lmata 800 return node, source
fa82b95… lmata 801
fa82b95… lmata 802 def test_creates_function_node(self):
fa82b95… lmata 803 parser = _make_c_parser()
fa82b95… lmata 804 store = _make_store()
fa82b95… lmata 805 node, source = self._make_function_node(b"main")
fa82b95… lmata 806 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 807 parser._handle_function(node, source, "main.c", store, stats)
fa82b95… lmata 808 assert stats["functions"] == 1
fa82b95… lmata 809 assert store.create_node.call_args[0][0] == NodeLabel.Function
fa82b95… lmata 810
fa82b95… lmata 811 def test_skips_if_no_name(self):
fa82b95… lmata 812 parser = _make_c_parser()
fa82b95… lmata 813 store = _make_store()
fa82b95… lmata 814 node = MockNode("function_definition")
fa82b95… lmata 815 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 816 parser._handle_function(node, b"", "main.c", store, stats)
fa82b95… lmata 817 assert stats["functions"] == 0
fa82b95… lmata 818
fa82b95… lmata 819
fa82b95… lmata 820 class TestCHandleStruct:
fa82b95… lmata 821 def test_creates_struct_node(self):
fa82b95… lmata 822 parser = _make_c_parser()
fa82b95… lmata 823 store = _make_store()
fa82b95… lmata 824 source = b"struct Point { int x; int y; };"
fa82b95… lmata 825 name_node = _text_node(b"Point", "type_identifier")
fa82b95… lmata 826 node = MockNode("struct_specifier", start_point=(0, 0), end_point=(0, 30))
fa82b95… lmata 827 node.set_field("name", name_node)
fa82b95… lmata 828 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 829 parser._handle_struct(node, source, "point.c", store, stats)
fa82b95… lmata 830 assert stats["classes"] == 1
fa82b95… lmata 831 props = store.create_node.call_args[0][1]
fa82b95… lmata 832 assert props["docstring"] == "struct"
fa82b95… lmata 833
fa82b95… lmata 834 def test_skips_if_no_name(self):
fa82b95… lmata 835 parser = _make_c_parser()
fa82b95… lmata 836 store = _make_store()
fa82b95… lmata 837 node = MockNode("struct_specifier")
fa82b95… lmata 838 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 839 parser._handle_struct(node, b"", "point.c", store, stats)
fa82b95… lmata 840 assert stats["classes"] == 0
fa82b95… lmata 841
fa82b95… lmata 842
fa82b95… lmata 843 class TestCHandleInclude:
fa82b95… lmata 844 def test_creates_import_node_angle_bracket(self):
fa82b95… lmata 845 parser = _make_c_parser()
fa82b95… lmata 846 store = _make_store()
fa82b95… lmata 847 source = b"#include <stdio.h>"
fa82b95… lmata 848 path_node = MockNode("system_lib_string", start_byte=9, end_byte=18)
fa82b95… lmata 849 node = MockNode("preproc_include", start_byte=0, end_byte=18, start_point=(0, 0))
fa82b95… lmata 850 node.set_field("path", path_node)
fa82b95… lmata 851 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 852 parser._handle_include(node, source, "main.c", store, stats)
fa82b95… lmata 853 assert stats["edges"] == 1
fa82b95… lmata 854 props = store.create_node.call_args[0][1]
fa82b95… lmata 855 assert "stdio.h" in props["name"]
fa82b95… lmata 856
fa82b95… lmata 857 def test_creates_import_node_quoted(self):
fa82b95… lmata 858 parser = _make_c_parser()
fa82b95… lmata 859 store = _make_store()
fa82b95… lmata 860 source = b'#include "utils.h"'
fa82b95… lmata 861 path_node = MockNode("string_literal", start_byte=9, end_byte=18)
fa82b95… lmata 862 node = MockNode("preproc_include", start_byte=0, end_byte=18, start_point=(0, 0))
fa82b95… lmata 863 node.set_field("path", path_node)
fa82b95… lmata 864 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 865 parser._handle_include(node, source, "main.c", store, stats)
fa82b95… lmata 866 assert stats["edges"] == 1
fa82b95… lmata 867 props = store.create_node.call_args[0][1]
fa82b95… lmata 868 assert "utils.h" in props["name"]
fa82b95… lmata 869
fa82b95… lmata 870
fa82b95… lmata 871 # ── CppParser ─────────────────────────────────────────────────────────────────
fa82b95… lmata 872
fa82b95… lmata 873
fa82b95… lmata 874 def _make_cpp_parser():
fa82b95… lmata 875 from navegador.ingestion.cpp import CppParser
fa82b95… lmata 876 p = CppParser.__new__(CppParser)
fa82b95… lmata 877 p._parser = MagicMock()
fa82b95… lmata 878 return p
fa82b95… lmata 879
fa82b95… lmata 880
fa82b95… lmata 881 class TestCppParserFileNode:
fa82b95… lmata 882 def test_parse_file_creates_file_node(self):
fa82b95… lmata 883 parser = _make_cpp_parser()
fa82b95… lmata 884 store = _make_store()
fa82b95… lmata 885 root = MockNode("translation_unit")
fa82b95… lmata 886 parser._parser.parse.return_value = _make_mock_tree(root)
fa82b95… lmata 887 with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as f:
fa82b95… lmata 888 f.write(b"class Foo {};\n")
fa82b95… lmata 889 fpath = Path(f.name)
fa82b95… lmata 890 try:
fa82b95… lmata 891 parser.parse_file(fpath, fpath.parent, store)
fa82b95… lmata 892 assert store.create_node.call_args[0][0] == NodeLabel.File
fa82b95… lmata 893 assert store.create_node.call_args[0][1]["language"] == "cpp"
fa82b95… lmata 894 finally:
fa82b95… lmata 895 fpath.unlink()
fa82b95… lmata 896
fa82b95… lmata 897
fa82b95… lmata 898 class TestCppHandleClass:
fa82b95… lmata 899 def test_creates_class_node(self):
fa82b95… lmata 900 parser = _make_cpp_parser()
fa82b95… lmata 901 store = _make_store()
fa82b95… lmata 902 source = b"class Foo {};"
fa82b95… lmata 903 name_node = _text_node(b"Foo", "type_identifier")
fa82b95… lmata 904 body = MockNode("field_declaration_list")
fa82b95… lmata 905 node = MockNode("class_specifier", start_point=(0, 0), end_point=(0, 12))
fa82b95… lmata 906 node.set_field("name", name_node)
fa82b95… lmata 907 node.set_field("body", body)
fa82b95… lmata 908 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 909 parser._handle_class(node, source, "Foo.cpp", store, stats)
fa82b95… lmata 910 assert stats["classes"] == 1
fa82b95… lmata 911
fa82b95… lmata 912 def test_skips_if_no_name(self):
fa82b95… lmata 913 parser = _make_cpp_parser()
fa82b95… lmata 914 store = _make_store()
fa82b95… lmata 915 node = MockNode("class_specifier")
fa82b95… lmata 916 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 917 parser._handle_class(node, b"", "Foo.cpp", store, stats)
fa82b95… lmata 918 assert stats["classes"] == 0
fa82b95… lmata 919
fa82b95… lmata 920 def test_creates_inherits_edge(self):
fa82b95… lmata 921 parser = _make_cpp_parser()
fa82b95… lmata 922 store = _make_store()
fa82b95… lmata 923 source = b"class Child : public Parent {};"
fa82b95… lmata 924 name_node = _text_node(b"Child", "type_identifier")
fa82b95… lmata 925 parent_id = _text_node(b"Parent", "type_identifier")
fa82b95… lmata 926 base_clause = MockNode("base_class_clause", children=[parent_id])
fa82b95… lmata 927 body = MockNode("field_declaration_list")
fa82b95… lmata 928 node = MockNode("class_specifier", start_point=(0, 0), end_point=(0, 30))
fa82b95… lmata 929 node.set_field("name", name_node)
fa82b95… lmata 930 node.set_field("base_clause", base_clause)
fa82b95… lmata 931 node.set_field("body", body)
fa82b95… lmata 932 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 933 parser._handle_class(node, source, "Child.cpp", store, stats)
fa82b95… lmata 934 assert stats["edges"] == 2 # CONTAINS + INHERITS
fa82b95… lmata 935
fa82b95… lmata 936 def test_walks_member_functions(self):
fa82b95… lmata 937 parser = _make_cpp_parser()
fa82b95… lmata 938 store = _make_store()
fa82b95… lmata 939 source = b"class Foo { void run() {} };"
fa82b95… lmata 940 class_name = _text_node(b"Foo", "type_identifier")
fa82b95… lmata 941 fn_id = _text_node(b"run")
fa82b95… lmata 942 fn_decl = MockNode("function_declarator")
fa82b95… lmata 943 fn_decl.set_field("declarator", fn_id)
fa82b95… lmata 944 fn_body = MockNode("compound_statement")
fa82b95… lmata 945 fn_node = MockNode("function_definition", start_point=(0, 12), end_point=(0, 24))
fa82b95… lmata 946 fn_node.set_field("declarator", fn_decl)
fa82b95… lmata 947 fn_node.set_field("body", fn_body)
fa82b95… lmata 948 body = MockNode("field_declaration_list", children=[fn_node])
fa82b95… lmata 949 node = MockNode("class_specifier", start_point=(0, 0), end_point=(0, 27))
fa82b95… lmata 950 node.set_field("name", class_name)
fa82b95… lmata 951 node.set_field("body", body)
fa82b95… lmata 952 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 953 parser._handle_class(node, source, "Foo.cpp", store, stats)
fa82b95… lmata 954 assert stats["functions"] == 1
fa82b95… lmata 955
fa82b95… lmata 956
fa82b95… lmata 957 class TestCppHandleFunction:
fa82b95… lmata 958 def test_creates_function_node(self):
fa82b95… lmata 959 parser = _make_cpp_parser()
fa82b95… lmata 960 store = _make_store()
fa82b95… lmata 961 source = b"void main() {}"
fa82b95… lmata 962 fn_id = _text_node(b"main")
fa82b95… lmata 963 fn_decl = MockNode("function_declarator")
fa82b95… lmata 964 fn_decl.set_field("declarator", fn_id)
fa82b95… lmata 965 body = MockNode("compound_statement")
fa82b95… lmata 966 node = MockNode("function_definition", start_point=(0, 0), end_point=(0, 13))
fa82b95… lmata 967 node.set_field("declarator", fn_decl)
fa82b95… lmata 968 node.set_field("body", body)
fa82b95… lmata 969 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 970 parser._handle_function(node, source, "main.cpp", store, stats, class_name=None)
fa82b95… lmata 971 assert stats["functions"] == 1
fa82b95… lmata 972 assert store.create_node.call_args[0][0] == NodeLabel.Function
fa82b95… lmata 973
fa82b95… lmata 974 def test_creates_method_node_in_class(self):
fa82b95… lmata 975 parser = _make_cpp_parser()
fa82b95… lmata 976 store = _make_store()
fa82b95… lmata 977 source = b"void run() {}"
fa82b95… lmata 978 fn_id = _text_node(b"run")
fa82b95… lmata 979 fn_decl = MockNode("function_declarator")
fa82b95… lmata 980 fn_decl.set_field("declarator", fn_id)
fa82b95… lmata 981 body = MockNode("compound_statement")
fa82b95… lmata 982 node = MockNode("function_definition", start_point=(0, 0), end_point=(0, 12))
fa82b95… lmata 983 node.set_field("declarator", fn_decl)
fa82b95… lmata 984 node.set_field("body", body)
fa82b95… lmata 985 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 986 parser._handle_function(node, source, "Foo.cpp", store, stats, class_name="Foo")
fa82b95… lmata 987 assert store.create_node.call_args[0][0] == NodeLabel.Method
fa82b95… lmata 988
fa82b95… lmata 989 def test_skips_if_no_name(self):
fa82b95… lmata 990 parser = _make_cpp_parser()
fa82b95… lmata 991 store = _make_store()
fa82b95… lmata 992 node = MockNode("function_definition")
fa82b95… lmata 993 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 994 parser._handle_function(node, b"", "main.cpp", store, stats, class_name=None)
fa82b95… lmata 995 assert stats["functions"] == 0
fa82b95… lmata 996
fa82b95… lmata 997
fa82b95… lmata 998 class TestCppHandleInclude:
fa82b95… lmata 999 def test_creates_import_node(self):
fa82b95… lmata 1000 parser = _make_cpp_parser()
fa82b95… lmata 1001 store = _make_store()
fa82b95… lmata 1002 source = b"#include <vector>"
fa82b95… lmata 1003 path_node = MockNode("system_lib_string", start_byte=9, end_byte=17)
fa82b95… lmata 1004 node = MockNode("preproc_include", start_byte=0, end_byte=17, start_point=(0, 0))
fa82b95… lmata 1005 node.set_field("path", path_node)
fa82b95… lmata 1006 stats = {"functions": 0, "classes": 0, "edges": 0}
fa82b95… lmata 1007 parser._handle_include(node, source, "Foo.cpp", store, stats)
fa82b95… lmata 1008 assert stats["edges"] == 1
fa82b95… lmata 1009 props = store.create_node.call_args[0][1]
fa82b95… lmata 1010 assert "vector" in props["name"]
fa82b95… lmata 1011
fa82b95… lmata 1012
fa82b95… lmata 1013 class TestCppExtractFunctionName:
fa82b95… lmata 1014 def test_simple_identifier(self):
fa82b95… lmata 1015 from navegador.ingestion.cpp import CppParser
fa82b95… lmata 1016 parser = CppParser.__new__(CppParser)
fa82b95… lmata 1017 source = b"foo"
fa82b95… lmata 1018 node = _text_node(b"foo")
fa82b95… lmata 1019 assert parser._extract_function_name(node, source) == "foo"
fa82b95… lmata 1020
fa82b95… lmata 1021 def test_function_declarator(self):
fa82b95… lmata 1022 from navegador.ingestion.cpp import CppParser
fa82b95… lmata 1023 parser = CppParser.__new__(CppParser)
fa82b95… lmata 1024 source = b"foo"
fa82b95… lmata 1025 fn_id = _text_node(b"foo")
fa82b95… lmata 1026 fn_decl = MockNode("function_declarator")
fa82b95… lmata 1027 fn_decl.set_field("declarator", fn_id)
fa82b95… lmata 1028 assert parser._extract_function_name(fn_decl, source) == "foo"
fa82b95… lmata 1029
fa82b95… lmata 1030 def test_none_input(self):
fa82b95… lmata 1031 from navegador.ingestion.cpp import CppParser
fa82b95… lmata 1032 parser = CppParser.__new__(CppParser)
fa82b95… lmata 1033 assert parser._extract_function_name(None, b"") is None

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button