Coverage for tests / test_npath / test_python.py: 100%
75 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 15:04 -0800
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 15:04 -0800
1"""Tests for NPath complexity (Python)."""
3from analyzers.python import analyze_source
6def _npath(code: str) -> int:
7 """Return NPath of the first function in code."""
8 result = analyze_source(code)
9 assert result.functions, f"No functions found in:\n{code}"
10 return result.functions[0].npath
13def _npaths(code: str) -> dict[str, int]:
14 """Return {name: npath} for all functions in code."""
15 result = analyze_source(code)
16 return {f.name: f.npath for f in result.functions}
19# --- Simple ---
21def test_simple_function():
22 assert _npath("def f(): pass") == 1
25def test_empty_source():
26 result = analyze_source("")
27 assert len(result.functions) == 0
30# --- If statements ---
32def test_if_only():
33 code = """
34def f(x):
35 if x:
36 pass
37"""
38 assert _npath(code) == 2 # NP(if body)=1 + 1 (no else)
41def test_if_else():
42 code = """
43def f(x):
44 if x:
45 pass
46 else:
47 pass
48"""
49 assert _npath(code) == 2 # NP(if body)=1 + NP(else body)=1
52def test_if_elif_else():
53 code = """
54def f(x):
55 if x > 0:
56 pass
57 elif x < 0:
58 pass
59 else:
60 pass
61"""
62 assert _npath(code) == 3 # 1 + 1 + 1
65def test_if_elif_no_else():
66 code = """
67def f(x):
68 if x > 0:
69 pass
70 elif x < 0:
71 pass
72"""
73 assert _npath(code) == 3 # 1 + 1 + 1 (implicit no-else path)
76# --- Bool ops in conditions ---
78def test_bool_ops_in_if():
79 code = """
80def f(a, b):
81 if a and b:
82 pass
83"""
84 assert _npath(code) == 3 # 1 + 1 (no else) + 1 (one bool op)
87def test_two_bool_ops():
88 code = """
89def f(a, b, c):
90 if a and b or c:
91 pass
92"""
93 assert _npath(code) == 4 # 1 + 1 (no else) + 2 (two bool ops)
96# --- Loops ---
98def test_for_loop():
99 code = """
100def f(items):
101 for x in items:
102 pass
103"""
104 assert _npath(code) == 2 # NP(body)=1 + 1
107def test_while_loop():
108 code = """
109def f(x):
110 while x:
111 pass
112"""
113 assert _npath(code) == 2 # NP(body)=1 + 1
116def test_while_with_bool_cond():
117 code = """
118def f(a, b):
119 while a and b:
120 pass
121"""
122 assert _npath(code) == 3 # NP(body)=1 + 1 + 1 (bool op)
125def test_for_else():
126 code = """
127def f(items):
128 for x in items:
129 pass
130 else:
131 pass
132"""
133 assert _npath(code) == 2 # NP(body)=1 + NP(else)=1
136# --- Try/except ---
138def test_try_except():
139 code = """
140def f():
141 try:
142 pass
143 except:
144 pass
145"""
146 assert _npath(code) == 2 # NP(try body)=1 + NP(except body)=1
149# --- Ternary ---
151def test_ternary():
152 code = """
153def f(c, x, y):
154 return x if c else y
155"""
156 assert _npath(code) == 2 # NP(x)=1 + NP(y)=1
159# --- Sequential ifs (multiplicative) ---
161def test_sequential_ifs():
162 code = """
163def f(a, b):
164 if a:
165 pass
166 if b:
167 pass
168"""
169 assert _npath(code) == 4 # 2 * 2
172# --- Nested if ---
174def test_nested_if():
175 code = """
176def f(a, b):
177 if a:
178 if b:
179 pass
180"""
181 # Outer if: NP(inner if) + 1 (no else)
182 # Inner if: 1 + 1 (no else) = 2
183 # Outer: 2 + 1 = 3
184 assert _npath(code) == 3
187# --- Match ---
189def test_match_three_cases():
190 code = """
191def f(x):
192 match x:
193 case 1:
194 pass
195 case 2:
196 pass
197 case 3:
198 pass
199"""
200 assert _npath(code) == 4 # 1+1+1 + 1 (no wildcard)
203def test_match_with_wildcard():
204 code = """
205def f(x):
206 match x:
207 case 1:
208 pass
209 case _:
210 pass
211"""
212 assert _npath(code) == 2 # 1+1, has wildcard
215# --- Nested ternary ---
217def test_nested_ternary():
218 code = """
219def f(c1, c2, a, b, d):
220 return a if c1 else (b if c2 else d)
221"""
222 # Outer: NP(a) + NP(inner) = 1 + (1+1) = 3
223 assert _npath(code) == 3
226# --- Nested function ---
228def test_nested_function():
229 code = """
230def outer(x):
231 if x:
232 pass
233 def inner(y):
234 if y:
235 pass
236"""
237 npaths = _npaths(code)
238 assert npaths["outer"] == 2 # inner counts as 1
239 assert npaths["inner"] == 2
242# --- With statement ---
244def test_with_statement():
245 code = """
246def f():
247 with open("x") as fp:
248 if True:
249 pass
250"""
251 assert _npath(code) == 2 # with body contains if -> npath 2
254# --- Lambda with ternary ---
256def test_lambda_ternary():
257 code = """
258def f():
259 g = lambda x: x if x > 0 else -x
260"""
261 # The expression_statement contains a ternary inside the lambda
262 # lambda body is recursed: ternary -> 2
263 assert _npath(code) == 2