Skip to content

Commit 63fb804

Browse files
rizsottoclaude
andcommitted
test: read the dispatch verdict, not the log line's substrings
The ambiguous-name probe test scanned bear's debug log with two plain `contains` checks: a line holding both "gcc" and "Recognized(" meant the probe had been bypassed. Both halves match text that says nothing about dispatch. "NotRecognized(" ends in "Recognized(", and every verdict line dumps the intercepted execution together with its environment, so a build environment that merely mentions GCC satisfies the first half. A Gentoo package build sets CC=x86_64-pc-linux-gnu-gcc and CFLAGS=-frecord-gcc-switches, so the `coreutils_to_ignore` interpreter's NotRecognized line tripped the assertion and failed the test on a run where the probe had correctly dispatched to Clang (#716). Bear itself was right; only the test's reading of the log was wrong. Parse the OutputLogger line instead: take the interpreter id and the verdict from their own positions, and compare the id exactly. The test exports CFLAGS the way the Gentoo build does, so the false positive is reproduced rather than left to the host, and a unit test pins the parsing against a line from the reported log. CFLAGS rather than CC because a program variable gets resolved on PATH in wrapper mode, and a Gentoo triplet name would warn on every host that lacks that compiler. Requirements: recognition-ambiguous-name-probe Closes #716 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent a53148e commit 63fb804

1 file changed

Lines changed: 66 additions & 15 deletions

File tree

tests/integration/tests/cases/compilation_output.rs

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -971,14 +971,25 @@ fn probe_dispatches_cc_to_clang_when_version_advertises_clang() -> Result<()> {
971971

972972
// Use command_bear() directly so we can force RUST_LOG=debug without
973973
// touching the test process env (which would race with parallel tests).
974+
//
975+
// `CFLAGS` carries a flag that names GCC, to reproduce #716: the debug
976+
// log dumps the intercepted execution's environment, so a build
977+
// environment that merely mentions GCC (as a Gentoo package build does)
978+
// must not read as "the GCC interpreter recognized this command".
979+
//
980+
// It has to be a flags variable rather than `CC`, which is how the
981+
// reported build spelled it. `CC` is a program variable: in wrapper mode
982+
// the runner resolves its value on PATH, so a Gentoo triplet name would
983+
// log a resolution warning on every other distro. `CFLAGS` reaches the
984+
// log through the same env filter (`intercept::environment`) without
985+
// ever being resolved or folded into the recorded arguments, so the
986+
// reproduction costs nothing on hosts that have no such compiler.
974987
let mut cmd = env.command_bear();
975-
cmd.current_dir(env.test_dir()).env("RUST_LOG", "debug").env("RUST_BACKTRACE", "1").args([
976-
"--output",
977-
"compile_commands.json",
978-
"--",
979-
SHELL_PATH,
980-
script.to_str().unwrap(),
981-
]);
988+
cmd.current_dir(env.test_dir())
989+
.env("RUST_LOG", "debug")
990+
.env("RUST_BACKTRACE", "1")
991+
.env("CFLAGS", "-frecord-gcc-switches")
992+
.args(["--output", "compile_commands.json", "--", SHELL_PATH, script.to_str().unwrap()]);
982993
let output = cmd.output()?;
983994
let stderr = String::from_utf8_lossy(&output.stderr);
984995
assert!(output.status.success(), "bear failed:\n{}", stderr);
@@ -995,27 +1006,67 @@ fn probe_dispatches_cc_to_clang_when_version_advertises_clang() -> Result<()> {
9951006
// parses the command, and `gcc : Recognized(...)` if the
9961007
// GCC flag table did. We assert the former is present and the latter is
9971008
// absent for this run.
998-
//
999-
// Note: we look for "Recognized(" specifically to ignore any unrelated
1000-
// log lines that might mention the id in passing (e.g. the probe's
1001-
// "clang version" reading of `cc --version`).
1002-
let saw_clang_recognized = stderr.lines().any(|l| l.contains("clang") && l.contains("Recognized("));
1003-
let saw_gcc_recognized = stderr.lines().any(|l| l.contains("gcc") && l.contains("Recognized("));
1009+
let recognized = interpreters_that_recognized(&stderr);
10041010

10051011
assert!(
1006-
saw_clang_recognized,
1012+
recognized.contains(&"clang"),
10071013
"expected a `clang ... Recognized(` log line proving probe dispatched to Clang.\nstderr:\n{}",
10081014
stderr
10091015
);
10101016
assert!(
1011-
!saw_gcc_recognized,
1017+
!recognized.contains(&"gcc"),
10121018
"did not expect a `gcc ... Recognized(` log line; that would mean the probe was bypassed and the regex fell back to GCC.\nstderr:\n{}",
10131019
stderr
10141020
);
10151021

10161022
Ok(())
10171023
}
10181024

1025+
/// Collect the interpreter ids that reported a `Recognized` verdict in a
1026+
/// debug log, as emitted by the `OutputLogger` combinator:
1027+
///
1028+
/// ```text
1029+
/// [... semantic::interpreters::combinators] clang : Recognized(Command { ... })
1030+
/// ```
1031+
///
1032+
/// The id and the verdict are matched positionally, not by substring: both
1033+
/// the `Execution` dump of a `NotRecognized(...)` verdict and the build
1034+
/// environment it carries can mention a compiler id in passing (a Gentoo
1035+
/// build sets `CC=x86_64-pc-linux-gnu-gcc` and `CFLAGS=-frecord-gcc-switches`,
1036+
/// for instance), and `NotRecognized(` itself ends in `Recognized(`.
1037+
fn interpreters_that_recognized(stderr: &str) -> Vec<&str> {
1038+
stderr
1039+
.lines()
1040+
.filter_map(|line| line.split_once("combinators] "))
1041+
.filter_map(|(_, entry)| entry.split_once(": "))
1042+
.filter(|(_, verdict)| verdict.starts_with("Recognized("))
1043+
.map(|(id, _)| id.trim_end())
1044+
.collect()
1045+
}
1046+
1047+
/// Regression guard for #716: the probe test above read the debug log with
1048+
/// plain `contains` checks, so a `NotRecognized` line whose environment dump
1049+
/// mentioned GCC (the Gentoo package build sets `CC=x86_64-pc-linux-gnu-gcc`)
1050+
/// read as "the probe fell back to GCC" and failed the test on a run where
1051+
/// the probe had in fact dispatched to Clang. The lines below are taken from
1052+
/// that build log.
1053+
// Requirements: recognition-ambiguous-name-probe
1054+
#[test]
1055+
fn recognized_ids_come_from_the_verdict_not_the_execution_dump() {
1056+
let log = concat!(
1057+
"[21:17:10.137 DEBUG bear[2649] semantic::interpreters::combinators] coreutils_to_ignore : ",
1058+
"NotRecognized(Execution { executable: \"/tmp/fake-cc/cc\", arguments: [\"cc\", \"-c\", ",
1059+
"\"hello.c\"], environment: {\"CC\": \"x86_64-pc-linux-gnu-gcc\", \"CFLAGS\": ",
1060+
"\"-O2 -ggdb3 -frecord-gcc-switches\"} })\n",
1061+
"[21:17:10.142 DEBUG bear[2649] semantic::interpreters::combinators] clang : ",
1062+
"Recognized(Command { executable: \"/tmp/fake-cc/cc\", source_mode: PerSourceStripped })\n",
1063+
);
1064+
1065+
let sut = interpreters_that_recognized(log);
1066+
1067+
assert_eq!(sut, vec!["clang"], "only the Recognized verdict's own id counts, got {sut:?}");
1068+
}
1069+
10191070
/// Regression guard for #532: --append was unusable on large projects in the
10201071
/// 3.x C++ implementation. The Rust rewrite (output-append.md) made it linear
10211072
/// but no test enforces the scaling property -- the requirement file cites

0 commit comments

Comments
 (0)