Files
mongo/buildscripts/gather_failed_tests.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

124 lines
4.4 KiB
Python
Raw Normal View History

import json
import os
import shutil
import subprocess
from pathlib import Path
from typing import List
import typer
def process_bep(bep_path):
failed_tests = []
successful_tests = []
with open(bep_path, "rt") as f:
# Each line in the BEP JSON file is a separate JSON object representing an event
for line in f:
event = json.loads(line)
if "testSummary" in event.get("id", {}):
target_label = event["id"]["testSummary"]["label"]
if "testSummary" not in event:
continue
overall_status = event["testSummary"]["overallStatus"]
if overall_status != "PASSED":
failed_tests += [target_label]
else:
successful_tests += [target_label]
return failed_tests, successful_tests
def _relink_binaries_with_symbols(failed_test_labels: List[str]):
print("Rebuilding tests with --remote_download_outputs=toplevel...")
bazel_build_flags = ""
if os.path.isfile(".bazel_build_flags"):
with open(".bazel_build_flags", "r", encoding="utf-8") as f:
bazel_build_flags = f.read().strip()
bazel_build_flags += " --remote_download_outputs=toplevel"
relink_command = [
arg for arg in ["bazel", "build", *bazel_build_flags.split(" "), *failed_test_labels] if arg
]
print(f"Running command: {' '.join(relink_command)}")
subprocess.run(
relink_command,
check=True,
)
repro_test_command = " ".join(["test" if arg == "build" else arg for arg in relink_command])
with open(".failed_unittest_repro.txt", "w", encoding="utf-8") as f:
f.write(repro_test_command)
print(f"Repro command written to .failed_unittest_repro.txt: {repro_test_command}")
def _copy_bins_to_upload(upload_bin_dir: str, upload_lib_dir: str):
libs = []
bins = []
dsyms = []
bazel_bin_dir = Path("./bazel-bin/src")
for dirpath, _, filenames in os.walk(bazel_bin_dir):
if dirpath.endswith(".dSYM"):
dsyms.append(Path(dirpath))
for f in filenames:
file = Path(f)
if file.stem.endswith(("_with_debug", "_ci_wrapper")):
continue
if file.suffix in [".so", ".so.debug", ".dylib"]:
libs.append(Path(os.path.join(dirpath, file)))
elif file.suffix in [".debug", ".dwp", ".pdb", ".exe", ""]:
bins.append(Path(os.path.join(dirpath, file)))
for binary_file in bins:
new_binary_file = upload_bin_dir / binary_file.name
if not os.path.exists(new_binary_file):
try:
shutil.copy(binary_file, new_binary_file)
except FileNotFoundError:
continue # It is likely a broken symlink.
for lib_file in libs:
new_lib_file = upload_lib_dir / lib_file.name
if not os.path.exists(new_lib_file):
try:
shutil.copy(lib_file, new_lib_file)
except FileNotFoundError:
continue # It is likely a broken symlink.
for dsym_dir in dsyms:
print(f"Copying dsym {dsym_dir} to {upload_bin_dir}")
try:
shutil.copytree(dsym_dir, upload_bin_dir / dsym_dir.name, dirs_exist_ok=True)
except FileNotFoundError:
continue # It is likely a broken symlink.
def main(build_events: str = "build_events.json"):
"""Gather binaries and debug symbols of failed tests based off of a Build Event Protocol (BEP) json file."""
os.chdir(os.environ.get("BUILD_WORKSPACE_DIRECTORY", "."))
upload_bin_dir = Path("dist-tests/bin")
upload_lib_dir = Path("dist-tests/lib")
upload_bin_dir.mkdir(parents=True, exist_ok=True)
upload_lib_dir.mkdir(parents=True, exist_ok=True)
failed_tests, successful_tests = process_bep(build_events)
if len(failed_tests) == 0 and len(successful_tests) == 0:
print("Test results not found, aborting. Please check above for any build errors.")
exit(1)
if not failed_tests:
print("No failed tests found.")
exit(0)
print(f"Found {len(failed_tests)} failed tests. Gathering binaries and debug symbols.")
_relink_binaries_with_symbols(failed_tests)
print("Copying binaries and debug symbols to upload directories.")
_copy_bins_to_upload(upload_bin_dir, upload_lib_dir)
if __name__ == "__main__":
typer.run(main)