code-graph-builder 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- code_graph_builder/__init__.py +82 -0
- code_graph_builder/builder.py +366 -0
- code_graph_builder/cgb_cli.py +32 -0
- code_graph_builder/cli.py +564 -0
- code_graph_builder/commands_cli.py +1288 -0
- code_graph_builder/config.py +340 -0
- code_graph_builder/constants.py +708 -0
- code_graph_builder/embeddings/__init__.py +40 -0
- code_graph_builder/embeddings/qwen3_embedder.py +573 -0
- code_graph_builder/embeddings/vector_store.py +584 -0
- code_graph_builder/examples/__init__.py +0 -0
- code_graph_builder/examples/example_configuration.py +276 -0
- code_graph_builder/examples/example_kuzu_usage.py +109 -0
- code_graph_builder/examples/example_semantic_search_full.py +347 -0
- code_graph_builder/examples/generate_wiki.py +915 -0
- code_graph_builder/examples/graph_export_example.py +100 -0
- code_graph_builder/examples/rag_example.py +206 -0
- code_graph_builder/examples/test_cli_demo.py +129 -0
- code_graph_builder/examples/test_embedding_api.py +153 -0
- code_graph_builder/examples/test_kuzu_local.py +190 -0
- code_graph_builder/examples/test_rag_redis.py +390 -0
- code_graph_builder/graph_updater.py +605 -0
- code_graph_builder/guidance/__init__.py +1 -0
- code_graph_builder/guidance/agent.py +123 -0
- code_graph_builder/guidance/prompts.py +74 -0
- code_graph_builder/guidance/toolset.py +264 -0
- code_graph_builder/language_spec.py +536 -0
- code_graph_builder/mcp/__init__.py +21 -0
- code_graph_builder/mcp/api_doc_generator.py +764 -0
- code_graph_builder/mcp/file_editor.py +207 -0
- code_graph_builder/mcp/pipeline.py +777 -0
- code_graph_builder/mcp/server.py +161 -0
- code_graph_builder/mcp/tools.py +1800 -0
- code_graph_builder/models.py +115 -0
- code_graph_builder/parser_loader.py +344 -0
- code_graph_builder/parsers/__init__.py +7 -0
- code_graph_builder/parsers/call_processor.py +306 -0
- code_graph_builder/parsers/call_resolver.py +139 -0
- code_graph_builder/parsers/definition_processor.py +796 -0
- code_graph_builder/parsers/factory.py +119 -0
- code_graph_builder/parsers/import_processor.py +293 -0
- code_graph_builder/parsers/structure_processor.py +145 -0
- code_graph_builder/parsers/type_inference.py +143 -0
- code_graph_builder/parsers/utils.py +134 -0
- code_graph_builder/rag/__init__.py +68 -0
- code_graph_builder/rag/camel_agent.py +429 -0
- code_graph_builder/rag/client.py +298 -0
- code_graph_builder/rag/config.py +239 -0
- code_graph_builder/rag/cypher_generator.py +67 -0
- code_graph_builder/rag/llm_backend.py +210 -0
- code_graph_builder/rag/markdown_generator.py +352 -0
- code_graph_builder/rag/prompt_templates.py +440 -0
- code_graph_builder/rag/rag_engine.py +640 -0
- code_graph_builder/rag/review_report.md +172 -0
- code_graph_builder/rag/tests/__init__.py +3 -0
- code_graph_builder/rag/tests/test_camel_agent.py +313 -0
- code_graph_builder/rag/tests/test_client.py +221 -0
- code_graph_builder/rag/tests/test_config.py +177 -0
- code_graph_builder/rag/tests/test_markdown_generator.py +240 -0
- code_graph_builder/rag/tests/test_prompt_templates.py +160 -0
- code_graph_builder/services/__init__.py +39 -0
- code_graph_builder/services/graph_service.py +465 -0
- code_graph_builder/services/kuzu_service.py +665 -0
- code_graph_builder/services/memory_service.py +171 -0
- code_graph_builder/settings.py +75 -0
- code_graph_builder/tests/ACCEPTANCE_CRITERIA_PHASE2.md +401 -0
- code_graph_builder/tests/__init__.py +1 -0
- code_graph_builder/tests/run_acceptance_check.py +378 -0
- code_graph_builder/tests/test_api_find.py +231 -0
- code_graph_builder/tests/test_api_find_integration.py +226 -0
- code_graph_builder/tests/test_basic.py +78 -0
- code_graph_builder/tests/test_c_api_extraction.py +388 -0
- code_graph_builder/tests/test_call_resolution_scenarios.py +504 -0
- code_graph_builder/tests/test_embedder.py +411 -0
- code_graph_builder/tests/test_integration_semantic.py +434 -0
- code_graph_builder/tests/test_mcp_protocol.py +298 -0
- code_graph_builder/tests/test_mcp_user_flow.py +190 -0
- code_graph_builder/tests/test_rag.py +404 -0
- code_graph_builder/tests/test_settings.py +135 -0
- code_graph_builder/tests/test_step1_graph_build.py +264 -0
- code_graph_builder/tests/test_step2_api_docs.py +323 -0
- code_graph_builder/tests/test_step3_embedding.py +278 -0
- code_graph_builder/tests/test_vector_store.py +552 -0
- code_graph_builder/tools/__init__.py +40 -0
- code_graph_builder/tools/graph_query.py +495 -0
- code_graph_builder/tools/semantic_search.py +387 -0
- code_graph_builder/types.py +333 -0
- code_graph_builder/utils/__init__.py +0 -0
- code_graph_builder/utils/path_utils.py +30 -0
- code_graph_builder-0.2.0.dist-info/METADATA +321 -0
- code_graph_builder-0.2.0.dist-info/RECORD +93 -0
- code_graph_builder-0.2.0.dist-info/WHEEL +4 -0
- code_graph_builder-0.2.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
"""阶段二调用关系解析测试场景.
|
|
2
|
+
|
|
3
|
+
测试场景覆盖:
|
|
4
|
+
- 场景1:简单单一文件项目
|
|
5
|
+
- 场景2:跨文件调用(import/include)
|
|
6
|
+
- 场景5:复杂调用模式
|
|
7
|
+
- 场景6:边界情况
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
from unittest.mock import MagicMock
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from code_graph_builder.builder import CodeGraphBuilder
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# =============================================================================
|
|
23
|
+
# 测试场景1:简单单一文件项目
|
|
24
|
+
# =============================================================================
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture
|
|
28
|
+
def simple_project(tmp_path: Path) -> Path:
|
|
29
|
+
"""创建简单单一文件测试项目."""
|
|
30
|
+
project_path = tmp_path / "simple_project"
|
|
31
|
+
project_path.mkdir()
|
|
32
|
+
|
|
33
|
+
with open(project_path / "main.py", "w") as f:
|
|
34
|
+
f.write('''
|
|
35
|
+
def helper():
|
|
36
|
+
"""Helper function."""
|
|
37
|
+
return "help"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Calculator:
|
|
41
|
+
"""Calculator class."""
|
|
42
|
+
|
|
43
|
+
def add(self, a: int, b: int) -> int:
|
|
44
|
+
"""Add two numbers."""
|
|
45
|
+
return a + b
|
|
46
|
+
|
|
47
|
+
def calculate(self) -> int:
|
|
48
|
+
"""Perform calculation."""
|
|
49
|
+
return self.add(1, 2)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def main() -> tuple:
|
|
53
|
+
"""Main function."""
|
|
54
|
+
calc = Calculator()
|
|
55
|
+
result = calc.calculate()
|
|
56
|
+
help_result = helper()
|
|
57
|
+
return result, help_result
|
|
58
|
+
''')
|
|
59
|
+
|
|
60
|
+
return project_path
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_simple_project_function_detection(simple_project: Path) -> None:
|
|
64
|
+
"""测试简单项目函数识别.
|
|
65
|
+
|
|
66
|
+
预期:识别 4 个函数/方法定义
|
|
67
|
+
- helper (函数)
|
|
68
|
+
- Calculator.add (方法)
|
|
69
|
+
- Calculator.calculate (方法)
|
|
70
|
+
- main (函数)
|
|
71
|
+
"""
|
|
72
|
+
from code_graph_builder.builder import CodeGraphBuilder
|
|
73
|
+
|
|
74
|
+
builder = CodeGraphBuilder(str(simple_project))
|
|
75
|
+
result = builder.build_graph(clean=True)
|
|
76
|
+
|
|
77
|
+
# 验证函数数量
|
|
78
|
+
assert result.functions_found >= 4, (
|
|
79
|
+
f"Expected at least 4 functions, found {result.functions_found}"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# 验证节点创建
|
|
83
|
+
assert result.nodes_created > 0, "No nodes were created"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_simple_project_call_relationships(simple_project: Path) -> None:
|
|
87
|
+
"""测试简单项目调用关系识别.
|
|
88
|
+
|
|
89
|
+
预期:识别 3 条调用关系
|
|
90
|
+
- main -> helper
|
|
91
|
+
- main -> Calculator.calculate
|
|
92
|
+
- Calculator.calculate -> Calculator.add
|
|
93
|
+
"""
|
|
94
|
+
from code_graph_builder.builder import CodeGraphBuilder
|
|
95
|
+
|
|
96
|
+
builder = CodeGraphBuilder(str(simple_project))
|
|
97
|
+
result = builder.build_graph(clean=True)
|
|
98
|
+
|
|
99
|
+
# 验证关系创建
|
|
100
|
+
assert result.relationships_created > 0, "No relationships were created"
|
|
101
|
+
|
|
102
|
+
# 查询调用关系
|
|
103
|
+
calls_query = """
|
|
104
|
+
MATCH (caller)-[:CALLS]->(callee)
|
|
105
|
+
RETURN caller.name as caller, callee.name as callee
|
|
106
|
+
"""
|
|
107
|
+
calls = builder.query(calls_query)
|
|
108
|
+
|
|
109
|
+
# 验证至少有一些调用关系
|
|
110
|
+
assert len(calls) >= 2, f"Expected at least 2 call relationships, found {len(calls)}"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# =============================================================================
|
|
114
|
+
# 测试场景2:跨文件调用
|
|
115
|
+
# =============================================================================
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@pytest.fixture
|
|
119
|
+
def cross_file_project(tmp_path: Path) -> Path:
|
|
120
|
+
"""创建跨文件调用测试项目."""
|
|
121
|
+
project_path = tmp_path / "cross_file_project"
|
|
122
|
+
project_path.mkdir()
|
|
123
|
+
|
|
124
|
+
# utils/helpers.py
|
|
125
|
+
utils_dir = project_path / "utils"
|
|
126
|
+
utils_dir.mkdir()
|
|
127
|
+
with open(utils_dir / "helpers.py", "w") as f:
|
|
128
|
+
f.write('''
|
|
129
|
+
def format_data(data: str) -> str:
|
|
130
|
+
"""Format data helper function."""
|
|
131
|
+
return f"formatted: {data}"
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class DataProcessor:
|
|
135
|
+
"""Data processor class."""
|
|
136
|
+
|
|
137
|
+
def process(self, data: str) -> str:
|
|
138
|
+
"""Process data method."""
|
|
139
|
+
return format_data(data)
|
|
140
|
+
''')
|
|
141
|
+
|
|
142
|
+
# utils/math_ops.py
|
|
143
|
+
with open(utils_dir / "math_ops.py", "w") as f:
|
|
144
|
+
f.write('''
|
|
145
|
+
def calculate(x: int, y: int) -> int:
|
|
146
|
+
"""Calculate function."""
|
|
147
|
+
return x + y
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def compute_complex() -> int:
|
|
151
|
+
"""Complex computation."""
|
|
152
|
+
return calculate(10, 20)
|
|
153
|
+
''')
|
|
154
|
+
|
|
155
|
+
# services/processor.py
|
|
156
|
+
services_dir = project_path / "services"
|
|
157
|
+
services_dir.mkdir()
|
|
158
|
+
with open(services_dir / "processor.py", "w") as f:
|
|
159
|
+
f.write('''
|
|
160
|
+
from utils.helpers import format_data, DataProcessor
|
|
161
|
+
from utils.math_ops import calculate
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def process_request(data: str) -> tuple:
|
|
165
|
+
"""Main processing function that calls multiple cross-file functions."""
|
|
166
|
+
# Direct function calls from different modules
|
|
167
|
+
formatted = format_data(data)
|
|
168
|
+
|
|
169
|
+
# Method calls
|
|
170
|
+
processor = DataProcessor()
|
|
171
|
+
processed = processor.process(data)
|
|
172
|
+
|
|
173
|
+
calc_result = calculate(1, 2)
|
|
174
|
+
|
|
175
|
+
return formatted, processed, calc_result
|
|
176
|
+
''')
|
|
177
|
+
|
|
178
|
+
# main.py
|
|
179
|
+
with open(project_path / "main.py", "w") as f:
|
|
180
|
+
f.write('''
|
|
181
|
+
from services.processor import process_request
|
|
182
|
+
from utils.math_ops import compute_complex
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def main() -> tuple:
|
|
186
|
+
"""Main function."""
|
|
187
|
+
result = process_request("test data")
|
|
188
|
+
complex_result = compute_complex()
|
|
189
|
+
return result, complex_result
|
|
190
|
+
''')
|
|
191
|
+
|
|
192
|
+
return project_path
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def test_cross_file_function_calls(cross_file_project: Path) -> None:
|
|
196
|
+
"""测试跨文件函数调用解析.
|
|
197
|
+
|
|
198
|
+
预期调用关系:
|
|
199
|
+
- main.main -> services.processor.process_request
|
|
200
|
+
- main.main -> utils.math_ops.compute_complex
|
|
201
|
+
- services.processor.process_request -> utils.helpers.format_data
|
|
202
|
+
- services.processor.process_request -> utils.math_ops.calculate
|
|
203
|
+
- utils.math_ops.compute_complex -> utils.math_ops.calculate
|
|
204
|
+
- utils.helpers.DataProcessor.process -> utils.helpers.format_data
|
|
205
|
+
"""
|
|
206
|
+
from code_graph_builder.builder import CodeGraphBuilder
|
|
207
|
+
|
|
208
|
+
builder = CodeGraphBuilder(str(cross_file_project))
|
|
209
|
+
result = builder.build_graph(clean=True)
|
|
210
|
+
|
|
211
|
+
# 验证函数数量
|
|
212
|
+
assert result.functions_found >= 6, (
|
|
213
|
+
f"Expected at least 6 functions, found {result.functions_found}"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# 查询调用关系
|
|
217
|
+
calls_query = """
|
|
218
|
+
MATCH (caller)-[:CALLS]->(callee)
|
|
219
|
+
RETURN caller.fqn as caller, callee.fqn as callee
|
|
220
|
+
"""
|
|
221
|
+
calls = builder.query(calls_query)
|
|
222
|
+
|
|
223
|
+
# 验证跨文件调用存在
|
|
224
|
+
assert len(calls) >= 4, f"Expected at least 4 cross-file calls, found {len(calls)}"
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
# =============================================================================
|
|
228
|
+
# 测试场景5:复杂调用模式
|
|
229
|
+
# =============================================================================
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@pytest.fixture
|
|
233
|
+
def complex_patterns_project(tmp_path: Path) -> Path:
|
|
234
|
+
"""创建复杂调用模式测试项目."""
|
|
235
|
+
project_path = tmp_path / "complex_patterns"
|
|
236
|
+
project_path.mkdir()
|
|
237
|
+
|
|
238
|
+
with open(project_path / "patterns.py", "w") as f:
|
|
239
|
+
f.write('''
|
|
240
|
+
from typing import List, Callable
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class Builder:
|
|
244
|
+
"""Builder class for chain calls."""
|
|
245
|
+
|
|
246
|
+
def __init__(self) -> None:
|
|
247
|
+
self.value = 0
|
|
248
|
+
|
|
249
|
+
def set_value(self, v: int) -> "Builder":
|
|
250
|
+
self.value = v
|
|
251
|
+
return self
|
|
252
|
+
|
|
253
|
+
def add(self, v: int) -> "Builder":
|
|
254
|
+
self.value += v
|
|
255
|
+
return self
|
|
256
|
+
|
|
257
|
+
def build(self) -> int:
|
|
258
|
+
return self.value
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def outer_func(x: int) -> int:
|
|
262
|
+
return x * 2
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def inner_func(x: int) -> int:
|
|
266
|
+
return x + 1
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def process_list(items: List[int]) -> List[int]:
|
|
270
|
+
"""Process list with map/filter."""
|
|
271
|
+
# 高阶函数调用
|
|
272
|
+
doubled = list(map(outer_func, items))
|
|
273
|
+
filtered = list(filter(lambda x: x > 0, doubled))
|
|
274
|
+
return filtered
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def chain_call_example() -> int:
|
|
278
|
+
"""Chain call example."""
|
|
279
|
+
# 链式调用
|
|
280
|
+
result = Builder().set_value(10).add(5).build()
|
|
281
|
+
return result
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def nested_call_example() -> int:
|
|
285
|
+
"""Nested call example."""
|
|
286
|
+
# 嵌套调用
|
|
287
|
+
result = outer_func(inner_func(5))
|
|
288
|
+
return result
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def conditional_call_example(flag: bool) -> int:
|
|
292
|
+
"""Conditional call example."""
|
|
293
|
+
# 条件调用
|
|
294
|
+
result = outer_func(10) if flag else inner_func(10)
|
|
295
|
+
return result
|
|
296
|
+
''')
|
|
297
|
+
|
|
298
|
+
return project_path
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def test_chain_calls(complex_patterns_project: Path) -> None:
|
|
302
|
+
"""测试链式调用识别."""
|
|
303
|
+
from code_graph_builder.builder import CodeGraphBuilder
|
|
304
|
+
|
|
305
|
+
builder = CodeGraphBuilder(str(complex_patterns_project))
|
|
306
|
+
result = builder.build_graph(clean=True)
|
|
307
|
+
|
|
308
|
+
# 链式调用应该被识别
|
|
309
|
+
assert result.functions_found >= 5, (
|
|
310
|
+
f"Expected at least 5 functions, found {result.functions_found}"
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def test_nested_calls(complex_patterns_project: Path) -> None:
|
|
315
|
+
"""测试嵌套调用识别."""
|
|
316
|
+
from code_graph_builder.builder import CodeGraphBuilder
|
|
317
|
+
|
|
318
|
+
builder = CodeGraphBuilder(str(complex_patterns_project))
|
|
319
|
+
result = builder.build_graph(clean=True)
|
|
320
|
+
|
|
321
|
+
# 嵌套调用应该被识别
|
|
322
|
+
assert result.relationships_created > 0, "No call relationships found"
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
# =============================================================================
|
|
326
|
+
# 测试场景6:边界情况
|
|
327
|
+
# =============================================================================
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
@pytest.fixture
|
|
331
|
+
def edge_cases_project(tmp_path: Path) -> Path:
|
|
332
|
+
"""创建边界情况测试项目."""
|
|
333
|
+
project_path = tmp_path / "edge_cases"
|
|
334
|
+
project_path.mkdir()
|
|
335
|
+
|
|
336
|
+
with open(project_path / "edge_cases.py", "w") as f:
|
|
337
|
+
f.write('''
|
|
338
|
+
# 短函数名
|
|
339
|
+
def a():
|
|
340
|
+
return "short"
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def b():
|
|
344
|
+
return a()
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
# 递归调用
|
|
348
|
+
def recursive(n: int) -> int:
|
|
349
|
+
if n <= 0:
|
|
350
|
+
return 0
|
|
351
|
+
return recursive(n - 1)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
# 间接递归
|
|
355
|
+
def indirect_a():
|
|
356
|
+
return indirect_b()
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def indirect_b():
|
|
360
|
+
return indirect_a()
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
# 同名函数(不同上下文)
|
|
364
|
+
class ClassA:
|
|
365
|
+
def method(self):
|
|
366
|
+
return "A"
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
class ClassB:
|
|
370
|
+
def method(self):
|
|
371
|
+
return "B"
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def main():
|
|
375
|
+
# 调用短函数名
|
|
376
|
+
result_a = a()
|
|
377
|
+
|
|
378
|
+
# 调用递归函数
|
|
379
|
+
result_rec = recursive(5)
|
|
380
|
+
|
|
381
|
+
# 调用类方法
|
|
382
|
+
obj_a = ClassA()
|
|
383
|
+
obj_b = ClassB()
|
|
384
|
+
result_a_method = obj_a.method()
|
|
385
|
+
result_b_method = obj_b.method()
|
|
386
|
+
|
|
387
|
+
return result_a, result_rec, result_a_method, result_b_method
|
|
388
|
+
''')
|
|
389
|
+
|
|
390
|
+
return project_path
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def test_short_function_names(edge_cases_project: Path) -> None:
|
|
394
|
+
"""测试短函数名识别."""
|
|
395
|
+
from code_graph_builder.builder import CodeGraphBuilder
|
|
396
|
+
|
|
397
|
+
builder = CodeGraphBuilder(str(edge_cases_project))
|
|
398
|
+
result = builder.build_graph(clean=True)
|
|
399
|
+
|
|
400
|
+
# 短函数名应该被识别
|
|
401
|
+
assert result.functions_found >= 6, (
|
|
402
|
+
f"Expected at least 6 functions, found {result.functions_found}"
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def test_recursive_calls(edge_cases_project: Path) -> None:
|
|
407
|
+
"""测试递归调用识别."""
|
|
408
|
+
from code_graph_builder.builder import CodeGraphBuilder
|
|
409
|
+
|
|
410
|
+
builder = CodeGraphBuilder(str(edge_cases_project))
|
|
411
|
+
result = builder.build_graph(clean=True)
|
|
412
|
+
|
|
413
|
+
# 递归调用应该被识别
|
|
414
|
+
assert result.relationships_created > 0, "No call relationships found"
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def test_same_name_methods(edge_cases_project: Path) -> None:
|
|
418
|
+
"""测试同名方法区分."""
|
|
419
|
+
from code_graph_builder.builder import CodeGraphBuilder
|
|
420
|
+
|
|
421
|
+
builder = CodeGraphBuilder(str(edge_cases_project))
|
|
422
|
+
result = builder.build_graph(clean=True)
|
|
423
|
+
|
|
424
|
+
# 同名方法应该被区分为不同函数
|
|
425
|
+
# 查询所有方法
|
|
426
|
+
methods_query = """
|
|
427
|
+
MATCH (m:Method)
|
|
428
|
+
RETURN m.fqn as fqn, m.name as name
|
|
429
|
+
"""
|
|
430
|
+
methods = builder.query(methods_query)
|
|
431
|
+
|
|
432
|
+
# 应该有两个名为 'method' 的方法,但 FQN 不同
|
|
433
|
+
method_names = [m["name"] for m in methods]
|
|
434
|
+
assert method_names.count("method") == 2, (
|
|
435
|
+
f"Expected 2 methods named 'method', found {method_names.count('method')}"
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
# =============================================================================
|
|
440
|
+
# 性能测试
|
|
441
|
+
# =============================================================================
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
@pytest.mark.performance
|
|
445
|
+
def test_tinycc_scale_performance() -> None:
|
|
446
|
+
"""测试 TinyCC 规模项目性能.
|
|
447
|
+
|
|
448
|
+
要求:
|
|
449
|
+
- 解析时间 ≤ 5 秒
|
|
450
|
+
- 内存占用 ≤ 2GB
|
|
451
|
+
- 函数识别率 ≥ 90%
|
|
452
|
+
"""
|
|
453
|
+
import os
|
|
454
|
+
import time
|
|
455
|
+
|
|
456
|
+
tinycc_path = os.environ.get("TINYCC_PATH", "/tmp/tinycc")
|
|
457
|
+
|
|
458
|
+
if not Path(tinycc_path).exists():
|
|
459
|
+
pytest.skip(f"TinyCC project not found at {tinycc_path}")
|
|
460
|
+
|
|
461
|
+
from code_graph_builder.builder import CodeGraphBuilder
|
|
462
|
+
|
|
463
|
+
start_time = time.time()
|
|
464
|
+
|
|
465
|
+
builder = CodeGraphBuilder(tinycc_path)
|
|
466
|
+
result = builder.build_graph(clean=True)
|
|
467
|
+
|
|
468
|
+
elapsed_time = time.time() - start_time
|
|
469
|
+
|
|
470
|
+
# 性能验证
|
|
471
|
+
assert elapsed_time <= 5.0, (
|
|
472
|
+
f"Parsing took {elapsed_time:.2f}s, expected <= 5s"
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
# 功能验证(TinyCC 大约有 1611 个函数)
|
|
476
|
+
assert result.functions_found >= 1400, (
|
|
477
|
+
f"Found {result.functions_found} functions, expected >= 1400"
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
# =============================================================================
|
|
482
|
+
# 准确率测试
|
|
483
|
+
# =============================================================================
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
@pytest.mark.accuracy
|
|
487
|
+
def test_call_detection_accuracy() -> None:
|
|
488
|
+
"""测试调用识别准确率.
|
|
489
|
+
|
|
490
|
+
使用已知调用关系的测试项目验证准确率。
|
|
491
|
+
"""
|
|
492
|
+
# 这是一个占位符测试,实际实现需要更详细的验证逻辑
|
|
493
|
+
# 可以人工创建一个包含已知数量调用的测试项目
|
|
494
|
+
pytest.skip("Requires manual verification with known test project")
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
@pytest.mark.accuracy
|
|
498
|
+
def test_call_resolution_accuracy() -> None:
|
|
499
|
+
"""测试调用解析准确率.
|
|
500
|
+
|
|
501
|
+
验证解析的调用目标是否正确。
|
|
502
|
+
"""
|
|
503
|
+
# 这是一个占位符测试,实际实现需要更详细的验证逻辑
|
|
504
|
+
pytest.skip("Requires manual verification with known test project")
|