jobbsok/tests/helpers/golden.py

51 lines
1.9 KiB
Python

"""Golden-file comparison, with regeneration that has to be asked for.
A golden test is only worth its file if updating that file is a deliberate
act. The failure mode this module exists to prevent is the silent
self-heal: a comparator that rewrites the reference whenever it disagrees
turns every golden into a mirror and every golden test into a no-op. So the
rewrite lives behind the ``--update-golden`` flag, and the flag defaults off.
The update flag is a parameter rather than something this module reads off
the pytest config, so the rewrite path is reachable from an ordinary test
without running pytest inside pytest. `tests/conftest.py` binds the flag to
the `golden` fixture for callers who want the wiring instead.
"""
import difflib
import os
def assert_golden(path, actual, update=False):
"""Compare ``actual`` against the golden file at ``path``.
With ``update`` the file is rewritten and nothing is asserted. Without it,
a missing golden and a differing golden both raise, and the message
carries a unified diff -- "golden mismatch" on its own sends the reader
back to the file to work out what moved.
"""
if update:
with open(path, "w", encoding="utf-8") as handle:
handle.write(actual)
return
if not os.path.exists(path):
raise AssertionError(
"golden %r does not exist yet. Re-run with --update-golden to "
"create it, having first read the output you are blessing." % path
)
with open(path, "r", encoding="utf-8") as handle:
expected = handle.read()
if expected == actual:
return
diff = "".join(
difflib.unified_diff(
expected.splitlines(keepends=True),
actual.splitlines(keepends=True),
fromfile="golden: %s" % path,
tofile="actual",
)
)
raise AssertionError("golden %r does not match actual output:\n%s" % (path, diff))