Skip to content

Commit 5137e46

Browse files
committed
Add tests for cli.py
1 parent c19fc84 commit 5137e46

2 files changed

Lines changed: 233 additions & 2 deletions

File tree

tests/test_cli.py

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
import json
2+
import importlib
3+
import sys
4+
from pathlib import Path
5+
6+
import pytest
7+
8+
9+
REPO_ROOT = Path(__file__).resolve().parents[1]
10+
sys.path.insert(0, str(REPO_ROOT))
11+
sys.modules.pop("oracletrace.cli", None)
12+
sys.modules.pop("oracletrace", None)
13+
cli = importlib.import_module("oracletrace.cli")
14+
assert str(REPO_ROOT / "oracletrace") in str(Path(cli.__file__).resolve())
15+
16+
17+
@pytest.fixture
18+
def trace_data():
19+
return {
20+
"functions": [
21+
{
22+
"name": "foo",
23+
"total_time": 1.5,
24+
"call_count": 3,
25+
"avg_time": 0.5,
26+
},
27+
{
28+
"name": "bar",
29+
"total_time": 2.0,
30+
"call_count": 2,
31+
"avg_time": 1.0,
32+
},
33+
]
34+
}
35+
36+
37+
class FakeTracer:
38+
def __init__(self, root, ignore_patterns, data):
39+
self.root = root
40+
self.ignore_patterns = ignore_patterns
41+
self.data = data
42+
self.started = False
43+
self.stopped = False
44+
self.show_results_calls = []
45+
46+
def start(self):
47+
self.started = True
48+
49+
def stop(self):
50+
self.stopped = True
51+
52+
def get_trace_data(self):
53+
return self.data
54+
55+
def show_results(self, top):
56+
self.show_results_calls.append(top)
57+
58+
59+
def _run_cli(monkeypatch, argv):
60+
monkeypatch.setattr(sys, "argv", argv)
61+
return cli.main()
62+
63+
64+
def test_main_returns_1_when_target_not_found(monkeypatch, capsys):
65+
exit_code = _run_cli(monkeypatch, ["oracletrace", "missing_script.py"])
66+
67+
captured = capsys.readouterr()
68+
assert exit_code == 1
69+
assert "Target not found: missing_script.py" in captured.out
70+
71+
72+
def test_main_returns_1_when_ignore_regex_is_invalid(monkeypatch, tmp_path, capsys):
73+
target = tmp_path / "target.py"
74+
target.write_text("print('ok')\n", encoding="utf-8")
75+
76+
exit_code = _run_cli(monkeypatch, ["oracletrace", str(target), "--ignore", "["])
77+
78+
captured = capsys.readouterr()
79+
assert exit_code == 1
80+
assert "Regex error: [" in captured.out
81+
82+
83+
def test_main_runs_trace_and_exports_json_and_csv(monkeypatch, tmp_path, trace_data):
84+
target = tmp_path / "target.py"
85+
target.write_text("print('hello')\n", encoding="utf-8")
86+
json_output = tmp_path / "trace.json"
87+
csv_output = tmp_path / "trace.csv"
88+
89+
fake_tracer_holder = {}
90+
91+
def tracer_factory(root, ignore_patterns):
92+
fake = FakeTracer(root, ignore_patterns, trace_data)
93+
fake_tracer_holder["instance"] = fake
94+
return fake
95+
96+
run_path_calls = []
97+
98+
def fake_run_path(path, run_name):
99+
run_path_calls.append((path, run_name))
100+
101+
monkeypatch.setattr(cli, "Tracer", tracer_factory)
102+
monkeypatch.setattr(cli.runpy, "run_path", fake_run_path)
103+
monkeypatch.setattr(sys, "path", sys.path.copy())
104+
105+
exit_code = _run_cli(
106+
monkeypatch,
107+
[
108+
"oracletrace",
109+
str(target),
110+
"--json",
111+
str(json_output),
112+
"--csv",
113+
str(csv_output),
114+
"--ignore",
115+
r".*target.py:foo",
116+
],
117+
)
118+
119+
fake_tracer = fake_tracer_holder["instance"]
120+
121+
assert exit_code == 0
122+
assert fake_tracer.started is True
123+
assert fake_tracer.stopped is True
124+
assert fake_tracer.show_results_calls == [None]
125+
assert run_path_calls == [(str(target.resolve()), "__main__")]
126+
127+
loaded_json = json.loads(json_output.read_text(encoding="utf-8"))
128+
assert loaded_json == trace_data
129+
130+
csv_text = csv_output.read_text(encoding="utf-8")
131+
assert "function,total_time,calls,avg_time" in csv_text
132+
assert "foo,1.5,3,0.5" in csv_text
133+
assert "bar,2.0,2,1.0" in csv_text
134+
135+
assert sys.path[0] == str(target.parent.resolve())
136+
137+
138+
def test_main_passes_top_value_to_show_results(monkeypatch, tmp_path, trace_data):
139+
target = tmp_path / "target.py"
140+
target.write_text("print('hello')\n", encoding="utf-8")
141+
142+
fake_tracer_holder = {}
143+
144+
def tracer_factory(root, ignore_patterns):
145+
fake = FakeTracer(root, ignore_patterns, trace_data)
146+
fake_tracer_holder["instance"] = fake
147+
return fake
148+
149+
monkeypatch.setattr(cli, "Tracer", tracer_factory)
150+
monkeypatch.setattr(cli.runpy, "run_path", lambda *args, **kwargs: None)
151+
152+
exit_code = _run_cli(monkeypatch, ["oracletrace", str(target), "--top", "5"])
153+
154+
fake_tracer = fake_tracer_holder["instance"]
155+
assert exit_code == 0
156+
assert fake_tracer.show_results_calls == [5]
157+
158+
159+
def test_main_returns_1_when_compare_file_not_found(monkeypatch, tmp_path, trace_data, capsys):
160+
target = tmp_path / "target.py"
161+
target.write_text("print('hello')\n", encoding="utf-8")
162+
163+
monkeypatch.setattr(cli, "Tracer", lambda root, ignore_patterns: FakeTracer(root, ignore_patterns, trace_data))
164+
monkeypatch.setattr(cli.runpy, "run_path", lambda *args, **kwargs: None)
165+
166+
exit_code = _run_cli(
167+
monkeypatch,
168+
["oracletrace", str(target), "--compare", str(tmp_path / "missing_compare.json")],
169+
)
170+
171+
captured = capsys.readouterr()
172+
assert exit_code == 1
173+
assert "Compare file not found:" in captured.out
174+
175+
176+
def test_main_fails_with_exit_2_on_regression(monkeypatch, tmp_path, trace_data, capsys):
177+
target = tmp_path / "target.py"
178+
target.write_text("print('hello')\n", encoding="utf-8")
179+
compare_file = tmp_path / "baseline.json"
180+
compare_file.write_text(json.dumps({"functions": []}), encoding="utf-8")
181+
182+
monkeypatch.setattr(cli, "Tracer", lambda root, ignore_patterns: FakeTracer(root, ignore_patterns, trace_data))
183+
monkeypatch.setattr(cli.runpy, "run_path", lambda *args, **kwargs: None)
184+
185+
compare_calls = []
186+
187+
def fake_compare_traces(old_data, new_data, threshold):
188+
compare_calls.append((old_data, new_data, threshold))
189+
return {"has_regression": True}
190+
191+
monkeypatch.setattr(cli, "compare_traces", fake_compare_traces)
192+
193+
exit_code = _run_cli(
194+
monkeypatch,
195+
[
196+
"oracletrace",
197+
str(target),
198+
"--compare",
199+
str(compare_file),
200+
"--fail-on-regression",
201+
"--threshold",
202+
"7.5",
203+
],
204+
)
205+
206+
captured = capsys.readouterr()
207+
assert exit_code == 2
208+
assert "Build failed: performance regression above 7.50% detected." in captured.out
209+
assert compare_calls[0][2] == 7.5
210+
211+
212+
def test_main_returns_0_when_no_regression(monkeypatch, tmp_path, trace_data):
213+
target = tmp_path / "target.py"
214+
target.write_text("print('hello')\n", encoding="utf-8")
215+
compare_file = tmp_path / "baseline.json"
216+
compare_file.write_text(json.dumps({"functions": []}), encoding="utf-8")
217+
218+
monkeypatch.setattr(cli, "Tracer", lambda root, ignore_patterns: FakeTracer(root, ignore_patterns, trace_data))
219+
monkeypatch.setattr(cli.runpy, "run_path", lambda *args, **kwargs: None)
220+
monkeypatch.setattr(cli, "compare_traces", lambda old_data, new_data, threshold: {"has_regression": False})
221+
222+
exit_code = _run_cli(
223+
monkeypatch,
224+
[
225+
"oracletrace",
226+
str(target),
227+
"--compare",
228+
str(compare_file),
229+
"--fail-on-regression",
230+
],
231+
)
232+
233+
assert exit_code == 0

tests/test_placeholder.py

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)