Skip to content

Commit 56210f1

Browse files
committed
test: add unit tests for compare_size_results.py
Mirrors test_compare_test_results.py: covers human_kb formatting, load_sizes (date-keyed and flat), compare() including threshold boundaries on both growth and shrinkage, format_report, and an end-to-end main() check that the comment file is only written when something significant is reported. Wired into code-quality.yml alongside the existing compare_test_results unit tests.
1 parent ed9fcb3 commit 56210f1

3 files changed

Lines changed: 166 additions & 1 deletion

File tree

.github/workflows/code-quality.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ jobs:
226226
- name: Run Python unit tests
227227
shell: bash
228228
run: |
229-
python3 -m unittest util/test_compare_test_results.py
229+
python3 -m unittest util/test_compare_test_results.py util/test_compare_size_results.py
230230
231231
pre_commit:
232232
name: Pre-commit hooks

util/compare_size_results.py

100644100755
File mode changed.

util/test_compare_size_results.py

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#!/usr/bin/env python3
2+
# spell-checker:ignore newbin oldbin
3+
"""Unit tests for the per-binary size comparison script."""
4+
5+
import json
6+
import os
7+
import sys
8+
import tempfile
9+
import unittest
10+
11+
from util.compare_size_results import compare, format_report, human_kb, load_sizes, main
12+
13+
14+
def _write_json(data):
15+
fd, path = tempfile.mkstemp(suffix=".json")
16+
with os.fdopen(fd, "w") as f:
17+
json.dump(data, f)
18+
return path
19+
20+
21+
class TestCompareSizeResults(unittest.TestCase):
22+
def test_human_kb(self):
23+
self.assertEqual(human_kb(512), "512 KB")
24+
self.assertEqual(human_kb(1536), "1.50 MB")
25+
self.assertEqual(human_kb(1024 * 1024), "1.00 GB")
26+
self.assertEqual(human_kb(-2048), "-2.00 MB")
27+
28+
def test_load_sizes_date_keyed(self):
29+
path = _write_json(
30+
{
31+
"Mon, 01 Jan 2024 00:00:00 +0000": {
32+
"sha": "old",
33+
"sizes": {"ls": 1000},
34+
},
35+
"Tue, 02 Jan 2024 00:00:00 +0000": {
36+
"sha": "new",
37+
"sizes": {"ls": 1100},
38+
},
39+
}
40+
)
41+
try:
42+
sha, sizes = load_sizes(path)
43+
self.assertEqual(sha, "new")
44+
self.assertEqual(sizes, {"ls": 1100})
45+
finally:
46+
os.unlink(path)
47+
48+
def test_load_sizes_flat_fallback(self):
49+
path = _write_json({"ls": "1064"})
50+
try:
51+
sha, sizes = load_sizes(path)
52+
self.assertIsNone(sha)
53+
self.assertEqual(sizes, {"ls": 1064})
54+
finally:
55+
os.unlink(path)
56+
57+
def test_compare_thresholds(self):
58+
# Both thresholds met -> significant (growth and shrinkage).
59+
sig, *_ = compare({"ls": 1100}, {"ls": 1000}, 0.05, 4)
60+
self.assertEqual(len(sig), 1)
61+
self.assertEqual(sig[0]["delta"], 100)
62+
63+
sig, *_ = compare({"ls": 900}, {"ls": 1000}, 0.05, 4)
64+
self.assertEqual(sig[0]["delta"], -100)
65+
66+
# Only relative met (10% but 2 KB) -> not significant.
67+
sig, *_ = compare({"t": 22}, {"t": 20}, 0.05, 4)
68+
self.assertEqual(sig, [])
69+
70+
# Only absolute met (10 KB but 0.01%) -> not significant.
71+
sig, *_ = compare({"b": 100010}, {"b": 100000}, 0.05, 4)
72+
self.assertEqual(sig, [])
73+
74+
def test_compare_threshold_boundaries(self):
75+
# Exactly at the threshold (4 KB AND 5%) -> significant: the script
76+
# uses >= on both sides.
77+
sig, *_ = compare({"ls": 84}, {"ls": 80}, 0.05, 4)
78+
self.assertEqual(len(sig), 1)
79+
self.assertEqual(sig[0]["delta"], 4)
80+
self.assertAlmostEqual(sig[0]["rel"], 0.05)
81+
82+
# Just below absolute floor: 3 KB / 3.75% -> not significant.
83+
sig, *_ = compare({"ls": 83}, {"ls": 80}, 0.05, 4)
84+
self.assertEqual(sig, [])
85+
86+
# Absolute floor met exactly (4 KB) but relative just below (4%).
87+
sig, *_ = compare({"ls": 104}, {"ls": 100}, 0.05, 4)
88+
self.assertEqual(sig, [])
89+
90+
# Relative just below (4.99%) with comfortable absolute -> rejected.
91+
sig, *_ = compare({"ls": 10499}, {"ls": 10000}, 0.05, 4)
92+
self.assertEqual(sig, [])
93+
94+
# Symmetric shrinkage at the boundary -> still significant.
95+
sig, *_ = compare({"ls": 76}, {"ls": 80}, 0.05, 4)
96+
self.assertEqual(len(sig), 1)
97+
self.assertEqual(sig[0]["delta"], -4)
98+
99+
def test_compare_added_removed_and_totals(self):
100+
sig, added, removed, totals = compare(
101+
{"ls": 1000, "newbin": 500}, {"ls": 1000, "oldbin": 800}, 0.05, 4
102+
)
103+
self.assertEqual(sig, [])
104+
self.assertEqual(added, [("newbin", 500)])
105+
self.assertEqual(removed, [("oldbin", 800)])
106+
# Totals must only consider binaries present in both runs.
107+
self.assertEqual(totals, {"current": 1000, "reference": 1000})
108+
109+
def test_compare_sort_and_zero_reference(self):
110+
sig, *_ = compare({"a": 1100, "b": 2000}, {"a": 1000, "b": 1000}, 0.05, 4)
111+
self.assertEqual([c["name"] for c in sig], ["b", "a"])
112+
# Zero reference must not crash.
113+
sig, *_ = compare({"ls": 1000}, {"ls": 0}, 0.05, 4)
114+
self.assertEqual(len(sig), 1)
115+
116+
def test_format_report_renders_changes(self):
117+
sig = [{"name": "ls", "old": 1000, "new": 1100, "delta": 100, "rel": 0.10}]
118+
report = format_report(
119+
sig,
120+
[("new", 5)],
121+
[("old", 8)],
122+
{"current": 1100, "reference": 1000},
123+
0.05,
124+
4,
125+
)
126+
for s in ("ls", "+10.00%", "New binaries", "new", "Removed binaries", "old"):
127+
self.assertIn(s, report)
128+
129+
def _run_main(self, argv):
130+
old = sys.argv
131+
sys.argv = argv
132+
try:
133+
return main()
134+
finally:
135+
sys.argv = old
136+
137+
def test_main_writes_only_when_significant(self):
138+
cur = _write_json({"d": {"sha": "n", "sizes": {"ls": 1100}}})
139+
ref = _write_json({"d": {"sha": "o", "sizes": {"ls": 1000}}})
140+
same = _write_json({"d": {"sha": "n", "sizes": {"ls": 1000}}})
141+
out_sig = tempfile.mktemp(suffix=".txt")
142+
out_none = tempfile.mktemp(suffix=".txt")
143+
try:
144+
self.assertEqual(self._run_main(["x", cur, ref, "--output", out_sig]), 0)
145+
self.assertTrue(os.path.exists(out_sig))
146+
with open(out_sig) as f:
147+
self.assertIn("+10.00%", f.read())
148+
149+
self.assertEqual(self._run_main(["x", same, ref, "--output", out_none]), 0)
150+
self.assertFalse(os.path.exists(out_none))
151+
finally:
152+
for p in (cur, ref, same, out_sig, out_none):
153+
if os.path.exists(p):
154+
os.unlink(p)
155+
156+
def test_main_missing_reference_is_not_fatal(self):
157+
cur = _write_json({"d": {"sha": "n", "sizes": {"ls": 1000}}})
158+
try:
159+
self.assertEqual(self._run_main(["x", cur, "/nonexistent.json"]), 0)
160+
finally:
161+
os.unlink(cur)
162+
163+
164+
if __name__ == "__main__":
165+
unittest.main()

0 commit comments

Comments
 (0)