mongo/buildscripts/tests/test_bazel_plus_test_interface.py
Daniel Moody 0216fb1e68 SERVER-122044 fix clang-tidy ide setup and coverity run (#49986)
GitOrigin-RevId: 89fe63d799c851bde830c5b9d35bc34a81f25cde
2026-03-19 20:45:09 +00:00

588 lines
22 KiB
Python

import os
import sys
import tempfile
import unittest
from contextlib import redirect_stderr
from io import StringIO
sys.path.append(".")
import bazel.wrapper_hook.plus_interface as plus_interface
from bazel.wrapper_hook.plus_interface import (
BinAndSourceIncompatible,
DuplicateSourceNames,
test_runner_interface,
)
def validate_first_suggestion(stderr_output: str, expected_suggestion: str):
assert (
"Did you mean one of these?" in stderr_output
), f"Expected 'Did you mean one of these?' in stderr, got: {stderr_output}"
suggestion_section = stderr_output.split("Did you mean one of these?")[1]
first_suggestion_line = [
line.strip()
for line in suggestion_section.split("\n")
if line.strip() and line.strip().startswith("+")
][0]
assert (
first_suggestion_line == expected_suggestion
), f"Expected first suggestion to be '{expected_suggestion}', got '{first_suggestion_line}'"
class Tests(unittest.TestCase):
def test_single_source_file(self):
def buildozer_output(autocomplete_query):
return "//some:test [source1.cpp source2.cpp]"
args = ["wrapper_hook", "test", "+source1"]
result = test_runner_interface(args, False, buildozer_output)
assert result == [
"test",
"//some:test",
"--test_arg=--fileNameFilter",
"--test_arg=source1",
]
def test_double_source_file(self):
def buildozer_output(autocomplete_query):
return "//some:test [source1.cpp source2.cpp]"
args = ["wrapper_hook", "test", "+source1", "+source2"]
result = test_runner_interface(args, False, buildozer_output)
assert result == [
"test",
"//some:test",
"--test_arg=--fileNameFilter",
"--test_arg=source1|source2",
]
def test_duplicate_source_file(self):
def buildozer_output(autocomplete_query):
return "//some:test [source1.cpp source2.cpp]"
args = ["wrapper_hook", "test", "+source1", "+source1"]
result = test_runner_interface(args, False, buildozer_output)
assert result == [
"test",
"//some:test",
"--test_arg=--fileNameFilter",
"--test_arg=source1",
]
def test_no_plus_targets(self):
def buildozer_output(autocomplete_query):
return "//some:test [source1.cpp source2.cpp]"
args = ["wrapper_hook", "test", "source1", "source1"]
result = test_runner_interface(args, False, buildozer_output)
assert result == ["test", "source1", "source1"]
def test_plus_option(self):
def buildozer_output(autocomplete_query):
return "//some:test [source1.cpp source2.cpp]"
args = [
"wrapper_hook",
"test",
"+source1",
"+source2",
"//some:other_target",
"--features",
"+some_feature",
]
stderr_capture = StringIO()
with redirect_stderr(stderr_capture):
result = test_runner_interface(args, False, buildozer_output)
assert result == [
"test",
"//some:test",
"//some:other_target",
"--features",
"+some_feature",
"--test_arg=--fileNameFilter",
"--test_arg=source1|source2",
]
# Verify that the warning for +some_feature was captured
stderr_output = stderr_capture.getvalue()
assert "WARNING: Target '+some_feature' not found" in stderr_output
def test_single_bin_file(self):
def buildozer_output(autocomplete_query):
return "//some:test [source1.cpp source2.cpp]"
args = ["wrapper_hook", "test", "+test"]
result = test_runner_interface(args, False, buildozer_output)
assert result == ["test", "//some:test"]
def test_double_bin_file(self):
def buildozer_output(autocomplete_query):
return "//some:test [source1.cpp source2.cpp]\n//some:test2 [source3.cpp source4.cpp]"
args = ["wrapper_hook", "test", "+test", "+test2"]
result = test_runner_interface(args, False, buildozer_output)
assert result == ["test", "//some:test", "//some:test2"]
def test_bin_source_redundant_mix(self):
def buildozer_output(autocomplete_query):
return "//some:test [source1.cpp source2.cpp]"
args = ["wrapper_hook", "test", "+test", "+source2"]
result = test_runner_interface(args, False, buildozer_output)
assert result == ["test", "//some:test"]
def test_bin_source_mix(self):
def buildozer_output(autocomplete_query):
return "//some:test [source1.cpp source2.cpp]\n//some:test2 [source3.cpp source4.cpp]"
args = ["wrapper_hook", "test", "+test", "+source3"]
with self.assertRaises(BinAndSourceIncompatible):
test_runner_interface(args, False, buildozer_output)
def test_duplicate_source_names(self):
def buildozer_output(autocomplete_query):
return "//some:test [source1.cpp source2.cpp]\n//some:test2 [source1.cpp source4.cpp]"
args = ["wrapper_hook", "test", "+test", "+source3"]
with self.assertRaises(DuplicateSourceNames):
test_runner_interface(args, False, buildozer_output)
def test_autocomplete(self):
if "linux" not in sys.platform:
self.skipTest("Skipping because not linux")
def buildozer_output(autocomplete_query):
return "//some:test [source1.cpp source2.cpp]"
args = ["wrapper_hook", "query", "some_autocomplete_query", "+wrench", "+source1"]
result = test_runner_interface(args, True, buildozer_output)
assert result == ["query", "some_autocomplete_query", "+wrench", "+source1"]
def test_select_statement(self):
def buildozer_output(autocomplete_query):
return """//some/select:test [
"source1.cpp",
] + select({
"//some:config": [
"source2.cpp",
],
"//some:other_config": [
"source3.cpp",
],
}) + [
"source4.cpp",
"source5.cpp",
]"""
args = ["wrapper_hook", "test", "+source1", "+source2", "+source3", "+source4"]
result = test_runner_interface(args, False, buildozer_output)
assert result == [
"test",
"//some/select:test",
"--test_arg=--fileNameFilter",
"--test_arg=source1|source2|source3|source4",
]
def test_c_extensions(self):
def buildozer_output(autocomplete_query):
return "//some:test [source1.c source2.h source3.cpp source4.cc]"
args = ["wrapper_hook", "test", "+source1", "+source2", "+source3", "+source4"]
stderr_capture = StringIO()
with redirect_stderr(stderr_capture):
result = test_runner_interface(args, False, buildozer_output)
assert result == [
"test",
"//some:test",
"+source2",
"--test_arg=--fileNameFilter",
"--test_arg=source1|source3|source4",
]
# Verify that the warning for +source2 was captured
stderr_output = stderr_capture.getvalue()
assert "WARNING: Target '+source2' not found" in stderr_output
def test_prefixes(self):
def buildozer_output(autocomplete_query):
return "//some:test [source1.cpp source2.cpp source3.cpp s+ource4.cpp]"
args = ["wrapper_hook", "test", "//:+source1", ":+source2", "+source3"]
result = test_runner_interface(args, False, buildozer_output)
assert result == [
"test",
"//some:test",
"--test_arg=--fileNameFilter",
"--test_arg=source1|source2|source3",
]
def test_target_not_found_with_suggestions(self):
"""Test that unrecognized targets pass through unchanged (not a test target)."""
def buildozer_output(autocomplete_query):
return "//some:test [bson_obj_test.cpp bson_element_test.cpp other_test.cpp]"
args = ["wrapper_hook", "test", "+bsonobj_test"] # Typo: missing underscore
stderr_capture = StringIO()
with redirect_stderr(stderr_capture):
result = test_runner_interface(args, False, buildozer_output)
# Should pass through unchanged since it's not a recognized target
assert result == ["test", "+bsonobj_test"]
# Check that suggestions were printed
stderr_output = stderr_capture.getvalue()
validate_first_suggestion(stderr_output, "+bson_obj_test")
def test_target_not_found_no_close_matches(self):
"""Test that completely unrecognized targets pass through unchanged."""
def buildozer_output(autocomplete_query):
return "//some:test [bson_obj_test.cpp other_test.cpp]"
args = ["wrapper_hook", "test", "+xyz123"] # Completely different target
stderr_capture = StringIO()
with redirect_stderr(stderr_capture):
result = test_runner_interface(args, False, buildozer_output)
# Should pass through unchanged
assert result == ["test", "+xyz123"]
# Check that "no similar targets" message was printed
stderr_output = stderr_capture.getvalue()
assert (
"and no similar targets" in stderr_output
), f"Expected 'and no similar targets' in stderr output, got: {stderr_output}"
def test_target_not_found_partial_match(self):
"""Test that partial matches still pass through when not found."""
def buildozer_output(autocomplete_query):
return "//some:test [bson_obj_test.cpp bson_element_test.cpp bson_utf8_test.cpp]"
args = ["wrapper_hook", "test", "+bson_obj"] # Missing '_test' suffix
stderr_capture = StringIO()
with redirect_stderr(stderr_capture):
result = test_runner_interface(args, False, buildozer_output)
# Should pass through unchanged
assert result == ["test", "+bson_obj"]
stderr_output = stderr_capture.getvalue()
validate_first_suggestion(stderr_output, "+bson_obj_test")
def test_compiledb_target_runs_separately_and_leaves_other_targets(self):
def buildozer_output(autocomplete_query):
return ""
args = ["wrapper_hook", "build", "compiledb", "//src/mongo/base:error_codes"]
generate_calls = []
def fake_generate_compiledb(*call_args, **call_kwargs):
generate_calls.append((call_args, call_kwargs))
original_generate_compiledb = plus_interface.generate_compiledb
original_swap_default_config = plus_interface.swap_default_config
plus_interface.generate_compiledb = fake_generate_compiledb
plus_interface.swap_default_config = (
lambda args,
command,
config_mode,
compiledb_target,
clang_tidy,
user_specified_config: config_mode
)
try:
result = test_runner_interface(args, False, buildozer_output)
finally:
plus_interface.generate_compiledb = original_generate_compiledb
plus_interface.swap_default_config = original_swap_default_config
assert result == ["build", "//src/mongo/base:error_codes"]
assert len(generate_calls) == 1
assert "requested_build_flags" not in generate_calls[0][1]
def test_compiledb_only_target_skips_final_bazel_invocation(self):
def buildozer_output(autocomplete_query):
return ""
args = ["wrapper_hook", "build", "compiledb_only"]
generate_calls = []
def fake_generate_compiledb(*call_args, **call_kwargs):
generate_calls.append((call_args, call_kwargs))
original_generate_compiledb = plus_interface.generate_compiledb
original_swap_default_config = plus_interface.swap_default_config
plus_interface.generate_compiledb = fake_generate_compiledb
plus_interface.swap_default_config = (
lambda args,
command,
config_mode,
compiledb_target,
clang_tidy,
user_specified_config: config_mode
)
try:
result = test_runner_interface(args, False, buildozer_output)
finally:
plus_interface.generate_compiledb = original_generate_compiledb
plus_interface.swap_default_config = original_swap_default_config
assert result == []
assert len(generate_calls) == 1
def test_compiledb_target_preserves_define_flag_value(self):
def buildozer_output(autocomplete_query):
return ""
args = [
"wrapper_hook",
"build",
"compiledb",
"--define",
"MONGO_VERSION=1",
"--keep_going",
"//src/mongo/base:error_codes",
]
generate_calls = []
def fake_generate_compiledb(*call_args, **call_kwargs):
generate_calls.append((call_args, call_kwargs))
original_generate_compiledb = plus_interface.generate_compiledb
original_swap_default_config = plus_interface.swap_default_config
plus_interface.generate_compiledb = fake_generate_compiledb
plus_interface.swap_default_config = (
lambda args,
command,
config_mode,
compiledb_target,
clang_tidy,
user_specified_config: config_mode
)
try:
result = test_runner_interface(args, False, buildozer_output)
finally:
plus_interface.generate_compiledb = original_generate_compiledb
plus_interface.swap_default_config = original_swap_default_config
assert result == [
"build",
"--define",
"MONGO_VERSION=1",
"--keep_going",
"//src/mongo/base:error_codes",
]
assert len(generate_calls) == 1
assert "requested_build_flags" not in generate_calls[0][1]
def test_config_equals_compiledb_runs_normally(self):
def buildozer_output(autocomplete_query):
return ""
args = ["wrapper_hook", "build", "--config=compiledb", "//src/mongo/base:error_codes"]
generate_calls = []
prepare_calls = []
def fake_generate_compiledb(*call_args, **call_kwargs):
generate_calls.append((call_args, call_kwargs))
def fake_prepare_posthook(*call_args, **call_kwargs):
prepare_calls.append((call_args, call_kwargs))
return ["build", "--config=compiledb", "//src/mongo/base:error_codes"]
original_generate_compiledb = plus_interface.generate_compiledb
original_prepare_posthook = plus_interface.prepare_compiledb_posthook_args
original_wrapper_config_mode_file = plus_interface.WRAPPER_CONFIG_MODE_FILE
with tempfile.TemporaryDirectory() as tempdir:
wrapper_config_mode_file = os.path.join(tempdir, "mongo_wrapper_config_mode")
with open(wrapper_config_mode_file, "w", encoding="utf-8") as file_handle:
file_handle.write("dbg")
plus_interface.generate_compiledb = fake_generate_compiledb
plus_interface.prepare_compiledb_posthook_args = fake_prepare_posthook
plus_interface.WRAPPER_CONFIG_MODE_FILE = wrapper_config_mode_file
try:
result = test_runner_interface(args, False, buildozer_output)
finally:
plus_interface.generate_compiledb = original_generate_compiledb
plus_interface.prepare_compiledb_posthook_args = original_prepare_posthook
plus_interface.WRAPPER_CONFIG_MODE_FILE = original_wrapper_config_mode_file
assert result == ["build", "--config=compiledb", "//src/mongo/base:error_codes"]
assert len(generate_calls) == 0
assert len(prepare_calls) == 1
def test_config_separate_compiledb_runs_normally(self):
def buildozer_output(autocomplete_query):
return ""
args = ["wrapper_hook", "build", "--config", "compiledb", "//src/mongo/base:error_codes"]
generate_calls = []
prepare_calls = []
def fake_generate_compiledb(*call_args, **call_kwargs):
generate_calls.append((call_args, call_kwargs))
def fake_prepare_posthook(*call_args, **call_kwargs):
prepare_calls.append((call_args, call_kwargs))
return [
"build",
"--config",
"compiledb",
"//src/mongo/base:error_codes",
]
original_generate_compiledb = plus_interface.generate_compiledb
original_prepare_posthook = plus_interface.prepare_compiledb_posthook_args
original_wrapper_config_mode_file = plus_interface.WRAPPER_CONFIG_MODE_FILE
with tempfile.TemporaryDirectory() as tempdir:
wrapper_config_mode_file = os.path.join(tempdir, "mongo_wrapper_config_mode")
with open(wrapper_config_mode_file, "w", encoding="utf-8") as file_handle:
file_handle.write("dbg")
plus_interface.generate_compiledb = fake_generate_compiledb
plus_interface.prepare_compiledb_posthook_args = fake_prepare_posthook
plus_interface.WRAPPER_CONFIG_MODE_FILE = wrapper_config_mode_file
try:
result = test_runner_interface(args, False, buildozer_output)
finally:
plus_interface.generate_compiledb = original_generate_compiledb
plus_interface.prepare_compiledb_posthook_args = original_prepare_posthook
plus_interface.WRAPPER_CONFIG_MODE_FILE = original_wrapper_config_mode_file
assert result == [
"build",
"--config",
"compiledb",
"//src/mongo/base:error_codes",
]
assert len(generate_calls) == 0
assert len(prepare_calls) == 1
def test_config_separate_compiledb_runs_normally_with_plain_target(self):
def buildozer_output(autocomplete_query):
return ""
args = ["wrapper_hook", "build", "--config", "compiledb", "install-dist-test"]
generate_calls = []
prepare_calls = []
def fake_generate_compiledb(*call_args, **call_kwargs):
generate_calls.append((call_args, call_kwargs))
def fake_prepare_posthook(*call_args, **call_kwargs):
prepare_calls.append((call_args, call_kwargs))
return [
"build",
"--config",
"compiledb",
"install-dist-test",
]
original_generate_compiledb = plus_interface.generate_compiledb
original_prepare_posthook = plus_interface.prepare_compiledb_posthook_args
original_wrapper_config_mode_file = plus_interface.WRAPPER_CONFIG_MODE_FILE
with tempfile.TemporaryDirectory() as tempdir:
wrapper_config_mode_file = os.path.join(tempdir, "mongo_wrapper_config_mode")
with open(wrapper_config_mode_file, "w", encoding="utf-8") as file_handle:
file_handle.write("dbg")
plus_interface.generate_compiledb = fake_generate_compiledb
plus_interface.prepare_compiledb_posthook_args = fake_prepare_posthook
plus_interface.WRAPPER_CONFIG_MODE_FILE = wrapper_config_mode_file
try:
result = test_runner_interface(args, False, buildozer_output)
finally:
plus_interface.generate_compiledb = original_generate_compiledb
plus_interface.prepare_compiledb_posthook_args = original_prepare_posthook
plus_interface.WRAPPER_CONFIG_MODE_FILE = original_wrapper_config_mode_file
assert result == [
"build",
"--config",
"compiledb",
"install-dist-test",
]
assert len(generate_calls) == 0
assert len(prepare_calls) == 1
def test_config_separate_compiledb_runs_normally_with_target_before_config(self):
def buildozer_output(autocomplete_query):
return ""
args = ["wrapper_hook", "build", "install-dist-test", "--config", "compiledb"]
generate_calls = []
prepare_calls = []
def fake_generate_compiledb(*call_args, **call_kwargs):
generate_calls.append((call_args, call_kwargs))
def fake_prepare_posthook(*call_args, **call_kwargs):
prepare_calls.append((call_args, call_kwargs))
return [
"build",
"install-dist-test",
"--config",
"compiledb",
]
original_generate_compiledb = plus_interface.generate_compiledb
original_prepare_posthook = plus_interface.prepare_compiledb_posthook_args
original_wrapper_config_mode_file = plus_interface.WRAPPER_CONFIG_MODE_FILE
with tempfile.TemporaryDirectory() as tempdir:
wrapper_config_mode_file = os.path.join(tempdir, "mongo_wrapper_config_mode")
with open(wrapper_config_mode_file, "w", encoding="utf-8") as file_handle:
file_handle.write("dbg")
plus_interface.generate_compiledb = fake_generate_compiledb
plus_interface.prepare_compiledb_posthook_args = fake_prepare_posthook
plus_interface.WRAPPER_CONFIG_MODE_FILE = wrapper_config_mode_file
try:
result = test_runner_interface(args, False, buildozer_output)
finally:
plus_interface.generate_compiledb = original_generate_compiledb
plus_interface.prepare_compiledb_posthook_args = original_prepare_posthook
plus_interface.WRAPPER_CONFIG_MODE_FILE = original_wrapper_config_mode_file
assert result == [
"build",
"install-dist-test",
"--config",
"compiledb",
]
assert len(generate_calls) == 0
assert len(prepare_calls) == 1
if __name__ == "__main__":
unittest.main()