Files
carbon-lang/executable_semantics/update_checks.py
T
Jon Meow 395a331cde Reorganize tests into dirs and rename numbered tests (#825)
There's a small update to update_checks.py to handle the recursive directories. Also, I'm only using one level of nesting in this PR but really no reason we can't do more. I'm just not sure what clustering is best right now.

As a pattern, I'm trying to name all failing tests `fail_*.carbon`.
2021-09-15 16:37:34 -07:00

102 lines
3.3 KiB
Python
Executable File

#!/usr/bin/env python3
"""Updates the CHECK: lines in lit tests based on the AUTOUPDATE line."""
__copyright__ = """
Part of the Carbon Language project, under the Apache License v2.0 with LLVM
Exceptions. See /LICENSE for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
"""
from concurrent import futures
import os
import subprocess
import sys
_BINDIR = "./bazel-bin/executable_semantics"
_TESTDATA = "executable_semantics/testdata"
# TODO: Right now this is a static string used. In theory maybe we should use
# the command; it's included for that flexibility.
_AUTOUPDATE_MARKER = "// AUTOUPDATE: executable_semantics %s\n"
def _get_tests():
"""Get the list of tests from the filesystem."""
tests = set()
for root, _, files in os.walk(_TESTDATA):
for f in files:
if f == "lit.cfg":
# Ignore the lit config.
continue
if os.path.splitext(f)[1] == ".carbon":
tests.add(os.path.join(root, f))
else:
sys.exit("Unrecognized file type in testdata: %s" % f)
return tests
def _update_check(test):
"""Updates the CHECK: lines for `test` by running executable_semantics."""
with open(test) as f:
orig_lines = f.readlines()
if _AUTOUPDATE_MARKER not in orig_lines:
raise ValueError("No autoupdate marker in %s" % test)
# Run executable_semantics to general output.
# (`bazel run` would serialize)
p = subprocess.run(
["%s/executable_semantics" % _BINDIR, test],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
out = p.stdout.decode("utf-8")
# `lit` uses full paths to the test file, so use a regex to ignore paths
# when used.
# TODO: Maybe revisit and see if lit can be convinced to give a
# root-relative path.
out = out.replace(test, "{{.*}}/%s" % test)
# Remove old OUT.
lines_without_check = [
x for x in orig_lines if not x.startswith("// CHECK:")
]
autoupdate_index = lines_without_check.index(_AUTOUPDATE_MARKER)
assert autoupdate_index >= 0
with open(test, "w") as f:
f.writelines(lines_without_check[: autoupdate_index + 1])
f.writelines(["// CHECK: %s\n" % x for x in out.splitlines()])
f.writelines(lines_without_check[autoupdate_index + 1 :])
print(".", end="", flush=True)
def _update_checks():
"""Runs bazel to update CHECK: lines in lit tests."""
# TODO: It may be helpful if a list of tests can be passed in args; would
# want to use argparse for this.
tests = _get_tests()
# Build all tests at once in order to allow parallel updates.
print("Building executable_semantics...")
subprocess.check_call(["bazel", "build", "//executable_semantics"])
print("Updating %d lit tests..." % len(tests))
with futures.ThreadPoolExecutor() as exec:
# list() iterates to propagate exceptions.
list(exec.map(_update_check, tests))
# Each update call indicates progress with a dot without a newline, so put a
# newline to wrap.
print("\nUpdated lit tests.")
def main():
# Go to the repository root so that paths will match bazel's view.
os.chdir(os.path.join(os.path.dirname(__file__), ".."))
_update_checks()
if __name__ == "__main__":
main()