Repository navigation
Expand file tree
/
Copy pathtest_resolver.py
More file actions
148 lines (120 loc) · 5.69 KB
/
Copy pathtest_resolver.py
File metadata and controls
148 lines (120 loc) · 5.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
"""Symbol resolver: references -> exact definitions with confidence scores."""
from __future__ import annotations
from sciogen.ir import CallSite, EdgeKind, ImportRecord
from sciogen.resolve import FileRefs, SymbolTable, resolve_file
def make_table() -> SymbolTable:
table = SymbolTable()
table.files = {"src/auth/service.py", "src/auth/hashing.py", "src/app.py"}
table.add("src/auth/hashing.py:hash_password", "hash_password")
table.add("src/auth/service.py:AuthService", "AuthService")
table.add("src/auth/service.py:AuthService.login", "login")
table.add("src/auth/service.py:AuthService.check_password", "check_password")
return table
def edges_of(refs: FileRefs, kind: EdgeKind):
edges, _nodes = resolve_file(refs, make_table())
return {(e.src, e.dst): e.confidence for e in edges if e.kind == kind}
def test_local_call_full_confidence():
table = make_table()
table.add("src/app.py:main", "main")
table.add("src/app.py:setup", "setup")
refs = FileRefs(
path="src/app.py", language="python",
calls=[CallSite("src/app.py:main", "setup", 3)],
)
edges, _ = resolve_file(refs, table)
assert [(e.src, e.dst, e.confidence) for e in edges if e.kind == EdgeKind.CALLS] == [
("src/app.py:main", "src/app.py:setup", 1.0)
]
def test_imported_symbol_resolves_to_exact_definition():
refs = FileRefs(
path="src/app.py", language="python",
imports=[ImportRecord("hash_password", "src.auth.hashing.hash_password", "src.auth.hashing", 1)],
calls=[CallSite("src/app.py:main", "hash_password", 5)],
)
calls = edges_of(refs, EdgeKind.CALLS)
assert calls[("src/app.py:main", "src/auth/hashing.py:hash_password")] == 0.9
def test_self_dispatch_resolves_to_sibling_method():
refs = FileRefs(
path="src/auth/service.py", language="python",
calls=[CallSite("src/auth/service.py:AuthService.login", "self.check_password", 8,
enclosing_class="AuthService")],
)
calls = edges_of(refs, EdgeKind.CALLS)
assert calls[
("src/auth/service.py:AuthService.login", "src/auth/service.py:AuthService.check_password")
] == 1.0
def test_dynamic_receiver_unique_method_low_confidence():
refs = FileRefs(
path="src/app.py", language="python",
calls=[CallSite("src/app.py:main", "svc.login", 5)],
)
calls = edges_of(refs, EdgeKind.CALLS)
assert calls[("src/app.py:main", "src/auth/service.py:AuthService.login")] == 0.4
def test_unresolvable_reference_kept_as_external_not_dropped():
refs = FileRefs(
path="src/app.py", language="python",
imports=[ImportRecord("requests", "requests", "requests", 1, is_module=True)],
calls=[CallSite("src/app.py:main", "requests.post", 5)],
)
edges, nodes = resolve_file(refs, make_table())
call_edges = [e for e in edges if e.kind == EdgeKind.CALLS]
assert call_edges[0].dst == "external:requests.post"
assert call_edges[0].confidence == 0.4
assert any(n.id == "external:requests.post" for n in nodes)
def test_noise_method_on_unknown_receiver_elided():
refs = FileRefs(
path="src/app.py", language="python",
calls=[CallSite("src/app.py:main", "?.append", 5)],
)
assert edges_of(refs, EdgeKind.CALLS) == {}
def test_builtins_produce_no_edges():
refs = FileRefs(
path="src/app.py", language="python",
calls=[CallSite("src/app.py:main", "print", 2), CallSite("src/app.py:main", "len", 3)],
)
assert edges_of(refs, EdgeKind.CALLS) == {}
def test_imports_resolve_to_files_and_modules():
refs = FileRefs(
path="src/app.py", language="python",
imports=[
ImportRecord("AuthService", "src.auth.service.AuthService", "src.auth.service", 1),
ImportRecord("hashlib", "hashlib", "hashlib", 2, is_module=True),
],
)
imports = edges_of(refs, EdgeKind.IMPORTS)
assert imports[("src/app.py", "src/auth/service.py")] == 1.0
assert imports[("src/app.py", "module:hashlib")] == 1.0
def test_relative_import_resolution():
refs = FileRefs(
path="src/auth/service.py", language="python",
imports=[ImportRecord("hash_password", ".hashing.hash_password", ".hashing", 1)],
calls=[CallSite("src/auth/service.py:AuthService.check_password", "hash_password", 9,
enclosing_class="AuthService")],
)
calls = edges_of(refs, EdgeKind.CALLS)
assert calls[
("src/auth/service.py:AuthService.check_password", "src/auth/hashing.py:hash_password")
] == 0.9
def test_extends_resolution():
table = make_table()
table.add("src/app.py:BaseHandler", "BaseHandler")
refs = FileRefs(
path="src/app.py", language="python",
class_bases=[("src/app.py:LoginHandler", ["BaseHandler", "Protocol"], [])],
)
edges, _ = resolve_file(refs, table)
extends = {(e.src, e.dst): e.confidence for e in edges if e.kind == EdgeKind.EXTENDS}
assert extends[("src/app.py:LoginHandler", "src/app.py:BaseHandler")] == 1.0
def test_test_for_naming_convention():
table = make_table()
table.add("tests_pkg/test_hashing.py:test_hash_password", "test_hash_password")
refs = FileRefs(
path="tests_pkg/test_hashing.py", language="python", is_test_file=True,
imports=[ImportRecord("hash_password", "src.auth.hashing.hash_password", "src.auth.hashing", 1)],
calls=[CallSite("tests_pkg/test_hashing.py:test_hash_password", "hash_password", 4)],
)
edges, _ = resolve_file(refs, table)
test_for = {(e.src, e.dst): e.confidence for e in edges if e.kind == EdgeKind.TEST_FOR}
assert test_for[
("tests_pkg/test_hashing.py:test_hash_password", "src/auth/hashing.py:hash_password")
] == 1.0