SERVER-117835 fix symbol check suggested command (#47067)

GitOrigin-RevId: 68c94d6912ada19f0b87499ac8030674b9b75be0
This commit is contained in:
Daniel Moody 2026-01-27 16:11:54 -06:00 committed by MongoDB Bot
parent ea24c3ae95
commit f7c6ecd634
4 changed files with 133 additions and 3 deletions

View File

@ -9,6 +9,20 @@ mongo_cc_binary(
srcs = ["unit_test.cpp"],
)
# Verifies evergreen symbol-check report uses the exact bazel invocation
# emitted by the Evergreen bazel compile script, and suggests *_with_debug targets.
py_test(
name = "test_generate_symbol_check_report",
srcs = ["test_generate_symbol_check_report.py"],
data = [
"//evergreen:generate_symbol_check_report.py",
],
deps = [
"//buildscripts:simple_report",
"//buildscripts/util",
],
)
# Test for verifying test_wrapper.sh timeout behavior and coredump generation.
# This test intentionally hangs to trigger the timeout mechanism in test_wrapper.sh.
# When run via the --run_under=//bazel:test_wrapper param, it should:

View File

@ -0,0 +1,78 @@
import json
import os
import pathlib
import runpy
import tempfile
import unittest
def _repo_root_from_runfiles() -> pathlib.Path:
test_srcdir = os.environ.get("TEST_SRCDIR")
test_workspace = os.environ.get("TEST_WORKSPACE")
if not test_srcdir or not test_workspace:
raise RuntimeError(
"Expected TEST_SRCDIR and TEST_WORKSPACE to be set by Bazel test environment"
)
return pathlib.Path(test_srcdir) / test_workspace
class GenerateSymbolCheckReportTest(unittest.TestCase):
def test_uses_bazel_compile_raw_invocation_and_flags(self):
repo_root = _repo_root_from_runfiles()
script_path = repo_root / "evergreen" / "generate_symbol_check_report.py"
self.assertTrue(script_path.exists(), f"Missing runfile: {script_path}")
# Simulate the Evergreen layout:
# - script runs with cwd=.../src
# - expansions.yml lives one directory above cwd (../expansions.yml)
with tempfile.TemporaryDirectory() as td:
workdir = pathlib.Path(td)
(workdir / "expansions.yml").write_text('run_for_symbol_check: "true"\n')
src = workdir / "src"
src.mkdir(parents=True)
bazel_build_flags = "--config=evg --config=symbol-checker --keep_going"
bazel_build_invocation = "bazel build --config=evg --config=symbol-checker //src/..."
(src / ".bazel_build_flags").write_text(bazel_build_flags + "\n")
(src / ".bazel_build_invocation").write_text(bazel_build_invocation + "\n")
checked_dir = src / "bazel-bin" / "buildscripts" / "bazel_testbuilds"
checked_dir.mkdir(parents=True)
checked_path = checked_dir / "unit_test_checked"
checked_payload = {
"status": "failed",
"target": "//buildscripts/bazel_testbuilds:unit_test",
"sym_file": "ignored.sym",
"missing": ["some_symbol"],
}
checked_path.write_text(json.dumps(checked_payload))
old_cwd = os.getcwd()
try:
os.chdir(src)
with self.assertRaises(SystemExit) as ctx:
runpy.run_path(str(script_path), run_name="__main__")
finally:
os.chdir(old_cwd)
# With a failing _checked file, the report script should exit non-zero.
self.assertEqual(ctx.exception.code, 1)
# The helper artifact should prefer the exact invocation written by bazel_compile.sh.
self.assertEqual(
(src / "bazel-invocation.txt").read_text().strip(), bazel_build_invocation
)
# The report content should prefer the exact flags written by bazel_compile.sh.
report = json.loads((src / "report.json").read_text())
log_raw = report["results"][0]["log_raw"]
expected_repro_target = checked_payload["target"] + "_with_debug"
self.assertIn(
f"bazel build {bazel_build_flags} {expected_repro_target}",
log_raw,
)
if __name__ == "__main__":
unittest.main()

View File

@ -25,6 +25,10 @@ py_library(
"oauthlib",
group = "testing",
),
dependency(
"pyyaml",
group = "core",
),
dependency(
"structlog",
group = "evergreen",

View File

@ -5,6 +5,23 @@ import sys
from buildscripts.simple_report import make_report, put_report, try_combine_reports
from buildscripts.util.read_config import read_config_file
def _read_optional_text_file(path: str) -> str | None:
try:
with open(path) as f:
content = f.read().strip()
except OSError:
return None
return content or None
def _with_debug_target(target: str) -> str:
"""Symbol-check aspect emits cc_library-like targets as *_with_debug."""
if target.endswith("_with_debug"):
return target
return f"{target}_with_debug"
# 1. detect if we should run symbol-check reporting
expansions = read_config_file("../expansions.yml")
symbol_check = expansions.get("run_for_symbol_check", None)
@ -14,6 +31,11 @@ if not symbol_check:
failures = []
# Prefer the exact Bazel flags/invocation emitted by the Evergreen Bazel compile script.
# This script is run from the "src" directory (see evergreen/run_python_script.sh).
bazel_build_flags = _read_optional_text_file(".bazel_build_flags")
bazel_build_invocation = _read_optional_text_file(".bazel_build_invocation")
# 2. walk bazel-bin for *_checked files emitted by the aspect
for root, _, files in os.walk("bazel-bin"):
for name in files:
@ -51,7 +73,11 @@ for root, _, files in os.walk("bazel-bin"):
repro_target = target or sym_file or checked_path
lines.append("")
lines.append("To reproduce:")
lines.append(f" bazel build --config=symbol-checker {repro_target}")
repro_target = _with_debug_target(str(repro_target))
if bazel_build_flags:
lines.append(f" bazel build {bazel_build_flags} {repro_target}")
else:
lines.append(f" bazel build --config=symbol-checker {repro_target}")
content = "\n".join(lines)
@ -62,9 +88,17 @@ for root, _, files in os.walk("bazel-bin"):
failures.append((synthetic_file, content))
# 3. write a helper invocation file
# adjust this to your actual symbol-check build config if you have one
with open("bazel-invocation.txt", "w") as f:
f.write("bazel build --config=symbol-checker //src/...")
if bazel_build_invocation:
# Keep the exact command that CI ran (targets included).
f.write(bazel_build_invocation)
elif bazel_build_flags:
# Fall back to the exact flags from CI (best effort on targets).
f.write(f"bazel build {bazel_build_flags} //src/...")
else:
# Last-resort fallback.
f.write("bazel build --config=symbol-checker //src/...")
# 4. emit reports
if failures: