|
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 |