Repository navigation
Description
- Repository:
pingdotgg/ts-rust - Target commit:
396bf652eed0b77453a67da247dde0d0a7130b0a - File:
scripts/goport/test-hunks.py:224
Summary
test-hunks.py uses a git() helper that runs subprocesses with check=True, and calls git grep -l -E 'cfg\((all\(.*)?test' as a prefilter for test modules. git grep exits 1 when no file matches, which is a normal “no matches” result, but check=True turns it into an uncaught CalledProcessError at test-hunks.py:314/46. A change whose only test code lives under crates/*/tests/ (no cfg(test) module declaration) therefore crashes the script instead of producing an empty match set and a report.
Affected code
scripts/goport/test-hunks.py:
216 def fn_text(self, key):
217 a, b = self.fns[key]
218 return "\n".join(self.lines[a : b + 1])
219
220 def fn_at(self, k):
221 return next((key for key, (a, b) in self.fns.items() if a <= k <= b), None)
222
223
224 def next_line(starts, off):
225 lo, hi = 0, len(starts) - 1
226 while lo < hi:
227 mid = (lo + hi + 1) // 2
228 if starts[mid] <= off:
229 lo = mid
230 else:
231 hi = mid - 1
232 return lo
233
234
235 def test_module_files(cwd, rev):
236 """Paths that a `#[cfg(test)] mod NAME;` declares at REV (path attributes too)."""
237 out = set()
238 hits = git(cwd, "grep", "-l", "-E", r"cfg\((all\(.*)?test", rev, "--", "crates/*.rs")
239 for hit in hits.split():
240 path = hit.split(":", 1)[1]
241 text = show(cwd, rev, path) or ""
242 directory = path.rsplit("/", 1)[0]
243 stem = path.rsplit("/", 1)[1][:-3]
244 # A mod.rs, lib.rs, main.rs or *_root.rs declares children in its own dir; other files in dir/stem.
245 own = stem in ("mod", "lib", "main") or stem.endswith("_root")
246 for m in MOD_DECL.finditer(mask(text)):
247 # mask() blanks the path string: read it from the text at the same offsets.
248 raw = MOD_DECL.match(text, m.start())
249 if raw and raw.group(1):
250 out.add(f"{directory}/{raw.group(1)}")
251 continue
252 base = directory if own else f"{directory}/{stem}"
253 out.add(f"{base}/{m.group(2)}.rs")
254 out.add(f"{base}/{m.group(2)}/mod.rs")
255 # A flat layout (scanner_util.rs declares scanner_test next to it).
256 out.add(f"{directory}/{m.group(2)}.rs")
257 return out
258
259
260 def main(argv):
261 if argv[:1] in (["help"], ["-h"], ["--help"]):
262 print(__doc__.strip())
263 return 0
Expected behavior
test-hunks.py must accept a valid repository revision even when no #[cfg(test)] mod declarations match, and must not abort outside its documented exit-code contract.
Actual behavior
When git grep finds no matching #[cfg(test)] mod declarations, hits = git(...) aborts via CalledProcessError instead of producing an empty set. The unhandled error prevents the tool from analyzing legal test files under tests/ and violates the documented exit-code contract.
How to reproduce
Run test-hunks.py --base HEAD~1 --head HEAD on a change whose only test code is under crates/*/tests/; the script aborts with CalledProcessError.
Root cause
test-hunks.py uses a git() helper that runs subprocesses with check=True, and calls git grep -l -E 'cfg\((all\(.*)?test' as a prefilter for test modules. git grep exits 1 when no file matches, which is a normal “no matches” result, but check=True turns it into an uncaught CalledProcessError at test-hunks.py:314/46. A change whose only test code lives under crates/*/tests/ (no cfg(test) module declaration) therefore crashes the script instead of producing an empty match set and a report.
Suggested fix
Treat git grep exit 1 as "no matches":
try:
out = git("grep", "-l", "-E", pattern, "HEAD", "--", "crates/*.rs")
except subprocess.CalledProcessError as exc:
if exc.returncode != 1: raise
out = ""Add a test whose only test code is under crates/*/tests/ and assert the script still emits a report with exit 0.
Activity