Port internal self-tests out of libHalide into test/correctness - #9276
Port internal self-tests out of libHalide into test/correctness#9276alexreinking wants to merge 6 commits into
Conversation
Fidelity check: did the test bodies actually move unchanged?Since each ported test's body still exists as pure deletions in the still-present Result: 94.7% of the ported content (1864 / 1968 lines) is identical, modulo whitespace.
I inspected the residual (non-matching) lines for every file by hand. They fall into exactly two buckets: 1. Purely mechanical. The old 2. Deliberate, disclosed changes (each has an explanatory comment in the new file):
Everything else — Script and commands used to produce this (click to expand)Run from the root of a checkout that has both commits in its history: python3 compare_ports.py # summary table (shown above)
python3 compare_ports.py --full # + full unified diff per file, for spot-checking
#!/usr/bin/env python3
"""Correlate deleted src/*.cpp test bodies with their new test/correctness/*.cpp
homes, and report how much of the diff is "real" content change vs mechanical
renaming/whitespace, by diffing the two blocks of text with whitespace ignored.
Run from the root of a Halide checkout that has both OLD_REF and NEW_REF in
its history (e.g. after `git fetch origin pull/9276/head`):
python3 compare_ports.py # summary table
python3 compare_ports.py --full # + full unified diff per file
"""
import difflib
import re
import subprocess
import sys
OLD_REF = "734f678e1" # parent of the porting commit
NEW_REF = "0cb496c08" # the porting commit itself
PAIRS = [
("src/IRPrinter.cpp", "test/correctness/ir_printer.cpp"),
("src/CodeGen_C.cpp", "test/correctness/codegen_c.cpp"),
("src/IREquality.cpp", "test/correctness/ir_equality.cpp"),
("src/Bounds.cpp", "test/correctness/bounds_internal.cpp"),
("src/IRMatch.cpp", "test/correctness/expr_match.cpp"),
("src/Deinterleave.cpp", "test/correctness/deinterleave_vector.cpp"),
("src/ModulusRemainder.cpp", "test/correctness/modulus_remainder.cpp"),
("src/CSE.cpp", "test/correctness/cse.cpp"),
("src/CPlusPlusMangle.cpp", "test/correctness/cplusplus_mangle.cpp"),
("src/Monotonic.cpp", "test/correctness/is_monotonic.cpp"),
("src/Reduction.cpp", "test/correctness/split_predicate.cpp"),
("src/Associativity.cpp", "test/correctness/associativity.cpp"),
("src/Generator.cpp", "test/correctness/generator_internal.cpp"),
("src/AutoScheduleUtils.cpp", "test/correctness/propagate_estimate.cpp"),
("src/UniquifyVariableNames.cpp", "test/correctness/uniquify_variable_names.cpp"),
("src/SpirvIR.cpp", "test/correctness/spirv_ir.cpp"),
]
def git_diff_lines(path, sign):
"""Return the lines added ('+') or removed ('-') for `path` between
OLD_REF and NEW_REF, stripped of the diff marker, in original order."""
out = subprocess.run(
["git", "diff", "-U0", "--no-color", OLD_REF, NEW_REF, "--", path],
capture_output=True, text=True, check=True
).stdout
lines = []
for line in out.splitlines():
if line.startswith("@@") or line.startswith("diff ") or line.startswith("index "):
continue
if line.startswith("--- ") or line.startswith("+++ "):
continue
if sign == "-" and line.startswith("-"):
lines.append(line[1:])
elif sign == "+" and line.startswith("+"):
lines.append(line[1:])
return lines
# Lines that are pure "porting scaffolding" and shouldn't count against fidelity.
BOILERPLATE = re.compile(
r'^\s*('
r'#include "Halide\.h"|'
r'using namespace Halide;|'
r'using namespace Halide::Internal;|'
r'namespace Halide \{|'
r'namespace Internal \{|'
r'\}\s*//\s*namespace Internal|'
r'\}\s*//\s*namespace Halide|'
r'int main\(.*\)\s*\{|'
r'printf\("Success!\\n"\);|'
r'return 0;|'
r'\}'
r')\s*$'
)
def strip_boilerplate(lines):
return [l for l in lines if not BOILERPLATE.match(l.strip())]
grand_old = grand_new = grand_equal = 0
for old_file, new_file in PAIRS:
old_lines = strip_boilerplate(git_diff_lines(old_file, "-"))
new_lines = strip_boilerplate(git_diff_lines(new_file, "+"))
old_norm = [l.strip() for l in old_lines if l.strip()]
new_norm = [l.strip() for l in new_lines if l.strip()]
sm = difflib.SequenceMatcher(a=old_norm, b=new_norm, autojunk=False)
equal = sum(block.size for block in sm.get_matching_blocks())
total = max(len(old_norm), len(new_norm))
pct = 100.0 * equal / total if total else 100.0
grand_old += len(old_norm)
grand_new += len(new_norm)
grand_equal += equal
print(f"{old_file:32s} -> {new_file:48s} "
f"old={len(old_norm):4d} new={len(new_norm):4d} equal={equal:4d} ({pct:5.1f}%)")
if "--full" in sys.argv:
diff = difflib.unified_diff(old_norm, new_norm,
fromfile=old_file, tofile=new_file, lineterm="")
for line in diff:
print(" ", line)
total = max(grand_old, grand_new)
pct = 100.0 * grand_equal / total if total else 100.0
print(f"\nTOTAL: old={grand_old} new={grand_new} equal={grand_equal} ({pct:.1f}% content-identical)") |
Halide has historically embedded self-test functions (e.g. Foo::test(), foo_test()) directly in src/*.cpp, invoked from a single test/internal.cpp binary. This moves every one of those test bodies (and their exclusive helper functions) out of libHalide entirely, into standalone test/correctness/*.cpp executables that exercise the same internal APIs via Halide::Internal::, following the same conventions as the rest of the correctness suite. test/internal.cpp and the _test_internal test target are removed; a trivial test/pch_helper.cpp takes over _test_internal's role as the precompiled-header donor for the other test targets. Two internal APIs needed small additions to support this: - CodeGen_C.h gained three accessor functions (codegen_c_test_*) since the binary2cpp-generated blobs its test compares against aren't exported from the shared library. - spirv_ir.cpp gets its own CMake wiring for the internal-only SpirvIR.h header and vendored SPIR-V headers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
_test_internal no longer runs through add_halide_test(), so it stopped getting the same warning flags as the test targets that reuse its precompiled header via REUSE_FROM. MSVC treats a warning-level mismatch between a PCH and its consumer as an error under /WX (C4652). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
0357687 to
5da585e
Compare
# Conflicts: # test/internal.cpp
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9276 +/- ##
==========================================
- Coverage 70.26% 69.91% -0.35%
==========================================
Files 257 257
Lines 79106 77251 -1855
Branches 18954 18784 -170
==========================================
- Hits 55583 54011 -1572
+ Misses 17885 17676 -209
+ Partials 5638 5564 -74 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…ernal-tests-to-correctness # Conflicts: # src/Bounds.cpp # src/CSE.cpp # src/CodeGen_C.cpp # src/Deinterleave.cpp # src/IRPrinter.cpp
PR #9247 (merged into main) removed the default is_streaming argument from Load::make/Store::make and added short-form overloads instead. The tests recently ported out of src/*.cpp into test/correctness still used the old call forms, which no longer compiled after merging main.
Halide has historically embedded self-test functions (e.g. Foo::test(), foo_test()) directly in
src/*.cpp, invoked from a single test/internal.cpp binary. This moves every one of those test bodies (and their exclusive helper functions) out of libHalide entirely, into standalonetest/correctness/*.cppexecutables that exercise the same internal APIs via Halide::Internal::, following the same conventions as the rest of the correctness suite.test/internal.cpp and the
_test_internaltest target are removed; a trivialtest/pch_helper.cpptakes over_test_internal's role as the precompiled-header donor for the other test targets.Two internal APIs needed small additions to support this:
Breaking changes
None
Checklist