SERVER-126405 Use the same UUID for multiple golden tests within a single resmoke.py run (#53607)

GitOrigin-RevId: 36ff9d4cf4800638313ca4b8831eca9f7d2f8d72
This commit is contained in:
Philip Stoev 2026-05-18 13:46:41 +03:00 committed by MongoDB Bot
parent 864f5f4f40
commit 45a46c476a
9 changed files with 105 additions and 23 deletions

View File

@ -2,6 +2,7 @@
# Golden tests are currently incompatible with Bazel Remote Execution
unset GOLDEN_TEST_CONFIG_PATH
unset GOLDEN_TEST_OUTPUT_ROOT_PATTERN
is_ppc64le() {
local -r arch="$(uname -m)"

View File

@ -40,7 +40,10 @@ filters:
- 10gen/devprod-test-infrastructure
- "golden_test.py":
approvers:
- 10gen/query-optimization
- 10gen/query-optimization-correctness
- "util/golden_test_config.py":
approvers:
- 10gen/query-optimization-correctness
- "evergreen_gen_streams*":
approvers:
- 10gen/streams-engine

View File

@ -21,7 +21,7 @@ import click
if __name__ == "__main__" and __package__ is None:
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from buildscripts.util.fileops import read_yaml_file
from buildscripts.util.golden_test_config import GoldenTestConfig
assert sys.version_info >= (3, 7)
@ -43,25 +43,6 @@ class AppError(Exception):
pass
class GoldenTestConfig(object):
"""Represents the golden test configuration.
See: docs/golden_data_test_framework.md#appendix---config-file-reference
"""
def __init__(self, iterable=(), **kwargs):
"""Initialize the fields."""
self.__dict__.update(iterable, **kwargs)
outputRootPattern: str
diffCmd: str
@classmethod
def from_yaml_file(cls, path: str) -> "GoldenTestConfig":
"""Read the golden test configuration from the given file."""
return cls(**read_yaml_file(path))
class OutputPaths(object):
"""Represents actual and expected output paths."""
@ -250,7 +231,7 @@ class GoldenTestApp(object):
def setup_linux(self):
# Create config file
config_path = os.path.join(os.path.expanduser("~"), ".golden_test_config.yml")
config_path = GoldenTestConfig.default_config_path()
if not os.path.isfile(config_path):
print(f"Creating {config_path}")
config_contents = (

View File

@ -11,6 +11,7 @@ py_library(
visibility = ["//visibility:public"],
deps = [
"//buildscripts/ciconfig",
"//buildscripts/util",
dependency(
"requests",
group = "core",

View File

@ -46,6 +46,11 @@ from buildscripts.resmokelib.testing.suite import Suite
from buildscripts.resmokelib.utils import runtime_recorder
from buildscripts.resmokelib.utils.dictionary import get_dict_value
from buildscripts.util.download_utils import get_s3_client
from buildscripts.util.golden_test_config import (
GOLDEN_TEST_CONFIG_PATH_ENV,
GOLDEN_TEST_OUTPUT_ROOT_PATTERN_ENV,
GoldenTestConfig,
)
from buildscripts.util.teststats import HistoricTaskData
_INTERNAL_OPTIONS_TITLE = "Internal Options"
@ -90,6 +95,39 @@ class TestRunner(Subcommand):
"treating logs as incomplete"
)
def _setup_golden_test(self):
"""Pre-calculate the golden test output root pattern so that all golden tests
end up in the same directory.
Each jstest is executed in its own mongo shell subprocess. The C++
golden test framework reads ``outputRootPattern`` from the YAML config
pointed to by ``GOLDEN_TEST_CONFIG_PATH`` and replaces each ``%`` in
the basename with a random hex digit. Because that substitution happens
once per process, each test would otherwise produce its own
UUID-suffixed output directory.
"""
if GOLDEN_TEST_OUTPUT_ROOT_PATTERN_ENV in os.environ:
return
golden_test_cfg_path = os.environ.get(GOLDEN_TEST_CONFIG_PATH_ENV)
if not golden_test_cfg_path:
return
golden_test_cfg = GoldenTestConfig.from_yaml_file(golden_test_cfg_path)
if not golden_test_cfg.outputRootPattern or "%" not in golden_test_cfg.outputRootPattern:
return
# Substitute `%` placeholders only in the file name, mirroring `fs::unique_path(filename)`
# on the C++ side.
parent_dir, file_name = os.path.split(golden_test_cfg.outputRootPattern)
file_name_uuid = "".join(
random.choice("0123456789abcdef") if c == "%" else c for c in file_name
)
# Store the calculated value in the `GOLDEN_TEST_OUTPUT_ROOT_PATTERN` variable so
# that the C++ code can reference it.
os.environ[GOLDEN_TEST_OUTPUT_ROOT_PATTERN_ENV] = os.path.join(parent_dir, file_name_uuid)
def execute(self):
"""Execute the 'run' subcommand."""
@ -203,6 +241,8 @@ class TestRunner(Subcommand):
def run_tests(self):
"""Run the suite and tests specified."""
self._setup_golden_test()
# This code path should only execute when resmoke is running from a workload container.
if config.REQUIRES_WORKLOAD_CONTAINER_SETUP:
self._setup_workload_container()

View File

@ -12,6 +12,7 @@ py_library(
"expansions.py",
"fileops.py",
"generate_co_jira_map.py",
"golden_test_config.py",
"oauth.py",
"read_config.py",
"runcommand.py",

View File

@ -0,0 +1,40 @@
"""Shared helpers for the golden data test framework configuration.
Both ``buildscripts/golden_test.py`` (the user-facing diff/accept CLI) and
``buildscripts/resmokelib/run/__init__.py`` (resmoke.py's ``run`` subcommand)
need to read the YAML config pointed to by the ``GOLDEN_TEST_CONFIG_PATH``
environment variable. This module centralises that handling.
For details on the framework see ``docs/golden_data_test_framework.md``.
"""
import os
from dataclasses import dataclass
from typing import Optional
from buildscripts.util.fileops import read_yaml_file
GOLDEN_TEST_CONFIG_PATH_ENV = "GOLDEN_TEST_CONFIG_PATH"
GOLDEN_TEST_OUTPUT_ROOT_PATTERN_ENV = "GOLDEN_TEST_OUTPUT_ROOT_PATTERN"
@dataclass
class GoldenTestConfig:
"""Parsed contents of a golden test YAML config file."""
outputRootPattern: Optional[str] = None
diffCmd: Optional[str] = None
@classmethod
def from_yaml_file(cls, path: str) -> "GoldenTestConfig":
"""Read the golden test configuration from a YAML file."""
data = read_yaml_file(path) or {}
return cls(
outputRootPattern=data.get("outputRootPattern"),
diffCmd=data.get("diffCmd"),
)
@staticmethod
def default_config_path() -> str:
"""Return the default path for the golden test configuration file."""
return os.path.join(os.path.expanduser("~"), ".golden_test_config.yml")

View File

@ -231,9 +231,13 @@ GoldenTestOptions GoldenTestOptions::parseEnvironment() {
GoldenTestOptions opts;
po::options_description desc_env;
boost::optional<std::string> configPath;
boost::optional<std::string> outputRootPatternOverride;
desc_env.add_options() //
("config_path",
po::value<std::string>()->notifier([&configPath](auto v) { configPath = v; }));
po::value<std::string>()->notifier([&configPath](auto v) { configPath = v; })) //
("output_root_pattern",
po::value<std::string>()->notifier(
[&outputRootPatternOverride](auto v) { outputRootPatternOverride = v; }));
po::variables_map vm_env;
po::store(po::parse_environment(desc_env, "GOLDEN_TEST_"), vm_env);
@ -254,6 +258,14 @@ GoldenTestOptions GoldenTestOptions::parseEnvironment() {
}
}
// GOLDEN_TEST_OUTPUT_ROOT_PATTERN takes precedence over any value loaded from the YAML config.
// This allows resmoke.py to pre-resolve a single output root pattern and share it across all
// mongo shell subprocesses of one invocation, so every jstest writes its actual results under
// the same UUID-suffixed directory.
if (outputRootPatternOverride) {
opts.outputRootPattern = *outputRootPatternOverride;
}
return opts;
}

View File

@ -265,6 +265,9 @@ struct GoldenTestOptions {
* - GOLDEN_TEST_CONFIG_PATH: (optional) specifies the yaml config file.
* See config file reference:
* docs/golden_data_test_framework.md#appendix---config-file-reference
* - GOLDEN_TEST_OUTPUT_ROOT_PATTERN: (optional) overrides the `outputRootPattern` value
* loaded from the yaml config. Intended to let resmoke.py pre-resolve a single output
* root and share it across all golden tests in one invocation.
*/
static GoldenTestOptions parseEnvironment();