mirror of
https://github.com/pytorch/pytorch.git
synced 2025-10-20 21:14:14 +08:00
No more 311 runners, tested by generating the files for the next PRs Pull Request resolved: https://github.com/pytorch/pytorch/pull/155570 Approved by: https://github.com/zou3519 ghstack dependencies: #155473
226 lines
7.4 KiB
Python
Executable File
226 lines
7.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import argparse
|
|
import os
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
from common import (
|
|
get_testcases,
|
|
is_failure,
|
|
is_passing_skipped_test,
|
|
is_unexpected_success,
|
|
key,
|
|
open_test_results,
|
|
)
|
|
from download_reports import download_reports
|
|
|
|
|
|
"""
|
|
Usage: update_failures.py /path/to/dynamo_test_failures.py /path/to/test commit_sha
|
|
|
|
Best-effort updates the xfail and skip files under test directory
|
|
by parsing test reports.
|
|
|
|
You'll need to provide the commit_sha for the latest commit on a PR
|
|
from which we will pull CI test results.
|
|
|
|
Instructions:
|
|
- On your PR, add the "keep-going" label to ensure that all the tests are
|
|
failing (as opposed to CI stopping on the first failure). You may need to
|
|
restart your test jobs by force-pushing to your branch for CI to pick
|
|
up the "keep-going" label.
|
|
- Wait for all the tests to finish running.
|
|
- Find the full SHA of your commit and run this command.
|
|
|
|
This script requires the `gh` cli. You'll need to install it and then
|
|
authenticate with it via `gh auth login` before using this script.
|
|
https://docs.github.com/en/github-cli/github-cli/quickstart
|
|
"""
|
|
|
|
|
|
def patch_file(
|
|
filename, test_dir, unexpected_successes, new_xfails, new_skips, unexpected_skips
|
|
):
|
|
failures_directory = os.path.join(test_dir, "dynamo_expected_failures")
|
|
skips_directory = os.path.join(test_dir, "dynamo_skips")
|
|
|
|
dynamo_expected_failures = set(os.listdir(failures_directory))
|
|
dynamo_skips = set(os.listdir(skips_directory))
|
|
|
|
# These are hand written skips
|
|
extra_dynamo_skips = set()
|
|
with open(filename) as f:
|
|
start = False
|
|
for text in f.readlines():
|
|
text = text.strip()
|
|
if start:
|
|
if text == "}":
|
|
break
|
|
extra_dynamo_skips.add(text.strip(',"'))
|
|
else:
|
|
if text == "extra_dynamo_skips = {":
|
|
start = True
|
|
|
|
def format(testcase):
|
|
classname = testcase.attrib["classname"]
|
|
name = testcase.attrib["name"]
|
|
return f"{classname}.{name}"
|
|
|
|
formatted_unexpected_successes = {
|
|
f"{format(test)}" for test in unexpected_successes.values()
|
|
}
|
|
formatted_unexpected_skips = {
|
|
f"{format(test)}" for test in unexpected_skips.values()
|
|
}
|
|
formatted_new_xfails = [f"{format(test)}" for test in new_xfails.values()]
|
|
formatted_new_skips = [f"{format(test)}" for test in new_skips.values()]
|
|
|
|
def remove_file(path, name):
|
|
file = os.path.join(path, name)
|
|
cmd = ["git", "rm", file]
|
|
subprocess.run(cmd)
|
|
|
|
def add_file(path, name):
|
|
file = os.path.join(path, name)
|
|
with open(file, "w") as fp:
|
|
pass
|
|
cmd = ["git", "add", file]
|
|
subprocess.run(cmd)
|
|
|
|
covered_unexpected_successes = set()
|
|
|
|
# dynamo_expected_failures
|
|
for test in dynamo_expected_failures:
|
|
if test in formatted_unexpected_successes:
|
|
covered_unexpected_successes.add(test)
|
|
remove_file(failures_directory, test)
|
|
for test in formatted_new_xfails:
|
|
add_file(failures_directory, test)
|
|
|
|
leftover_unexpected_successes = (
|
|
formatted_unexpected_successes - covered_unexpected_successes
|
|
)
|
|
if len(leftover_unexpected_successes) > 0:
|
|
print(
|
|
"WARNING: we were unable to remove these "
|
|
f"{len(leftover_unexpected_successes)} expectedFailures:"
|
|
)
|
|
for stuff in leftover_unexpected_successes:
|
|
print(stuff)
|
|
|
|
# dynamo_skips
|
|
for test in dynamo_skips:
|
|
if test in formatted_unexpected_skips:
|
|
remove_file(skips_directory, test)
|
|
for test in extra_dynamo_skips:
|
|
if test in formatted_unexpected_skips:
|
|
print(
|
|
f"WARNING: {test} in dynamo_test_failures.py needs to be removed manually"
|
|
)
|
|
for test in formatted_new_skips:
|
|
add_file(skips_directory, test)
|
|
|
|
|
|
def get_intersection_and_outside(a_dict, b_dict):
|
|
a = set(a_dict.keys())
|
|
b = set(b_dict.keys())
|
|
intersection = a.intersection(b)
|
|
outside = (a.union(b)) - intersection
|
|
|
|
def build_dict(keys):
|
|
result = {}
|
|
for k in keys:
|
|
if k in a_dict:
|
|
result[k] = a_dict[k]
|
|
else:
|
|
result[k] = b_dict[k]
|
|
return result
|
|
|
|
return build_dict(intersection), build_dict(outside)
|
|
|
|
|
|
def update(filename, test_dir, py38_dir, py311_dir, also_remove_skips):
|
|
def read_test_results(directory):
|
|
xmls = open_test_results(directory)
|
|
testcases = get_testcases(xmls)
|
|
unexpected_successes = {
|
|
key(test): test for test in testcases if is_unexpected_success(test)
|
|
}
|
|
failures = {key(test): test for test in testcases if is_failure(test)}
|
|
passing_skipped_tests = {
|
|
key(test): test for test in testcases if is_passing_skipped_test(test)
|
|
}
|
|
return unexpected_successes, failures, passing_skipped_tests
|
|
|
|
(
|
|
py38_unexpected_successes,
|
|
py38_failures,
|
|
py38_passing_skipped_tests,
|
|
) = read_test_results(py38_dir)
|
|
(
|
|
py311_unexpected_successes,
|
|
py311_failures,
|
|
py311_passing_skipped_tests,
|
|
) = read_test_results(py311_dir)
|
|
|
|
unexpected_successes = {**py38_unexpected_successes, **py311_unexpected_successes}
|
|
_, skips = get_intersection_and_outside(
|
|
py38_unexpected_successes, py311_unexpected_successes
|
|
)
|
|
xfails, more_skips = get_intersection_and_outside(py38_failures, py311_failures)
|
|
if also_remove_skips:
|
|
unexpected_skips, _ = get_intersection_and_outside(
|
|
py38_passing_skipped_tests, py311_passing_skipped_tests
|
|
)
|
|
else:
|
|
unexpected_skips = {}
|
|
all_skips = {**skips, **more_skips}
|
|
print(
|
|
f"Discovered {len(unexpected_successes)} new unexpected successes, "
|
|
f"{len(xfails)} new xfails, {len(all_skips)} new skips, {len(unexpected_skips)} new unexpected skips"
|
|
)
|
|
return patch_file(
|
|
filename, test_dir, unexpected_successes, xfails, all_skips, unexpected_skips
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(
|
|
prog="update_dynamo_test_failures",
|
|
description="Read from logs and update the dynamo_test_failures file",
|
|
)
|
|
# dynamo_test_failures path
|
|
parser.add_argument(
|
|
"filename",
|
|
nargs="?",
|
|
default=str(
|
|
Path(__file__).absolute().parents[2]
|
|
/ "torch/testing/_internal/dynamo_test_failures.py"
|
|
),
|
|
help="Optional path to dynamo_test_failures.py",
|
|
)
|
|
# test path
|
|
parser.add_argument(
|
|
"test_dir",
|
|
nargs="?",
|
|
default=str(Path(__file__).absolute().parents[2] / "test"),
|
|
help="Optional path to test folder",
|
|
)
|
|
parser.add_argument(
|
|
"commit",
|
|
help=(
|
|
"The commit sha for the latest commit on a PR from which we will "
|
|
"pull CI test results, e.g. 7e5f597aeeba30c390c05f7d316829b3798064a5"
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--also-remove-skips",
|
|
help="Also attempt to remove skips. WARNING: does not guard against test flakiness",
|
|
action="store_true",
|
|
)
|
|
args = parser.parse_args()
|
|
assert Path(args.filename).exists(), args.filename
|
|
assert Path(args.test_dir).exists(), args.test_dir
|
|
dynamo39, dynamo313 = download_reports(args.commit, ("dynamo39", "dynamo313"))
|
|
update(args.filename, args.test_dir, dynamo39, dynamo313, args.also_remove_skips)
|