Skip to content

Commit fb4a644

Browse files
committed
set up integration test
1 parent 5374f13 commit fb4a644

File tree

4 files changed

+297
-1
lines changed

4 files changed

+297
-1
lines changed

.codespellrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[codespell]
2-
skip = .lock,*.toml
2+
skip = *.lock,*.toml,./bear/build.rs
33
count = true
44
quiet-level = 3
55

bear/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ shell-words.workspace = true
3737
tempfile.workspace = true
3838
signal-hook.workspace = true
3939

40+
[dev-dependencies]
41+
tempfile.workspace = true
42+
anyhow.workspace = true
43+
assert_cmd = "2.0"
44+
predicates = "3.1"
45+
46+
[build-dependencies]
47+
tempfile.workspace = true
48+
which = "7.0"
49+
cc = "1.2"
50+
4051
[profile.release]
4152
strip = true
4253
lto = true

bear/build.rs

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,198 @@
11
// SPDX-License-Identifier: GPL-3.0-or-later
22

3+
use std::io::Write;
4+
35
fn main() {
46
println!("cargo:rustc-env=WRAPPER_EXECUTABLE_PATH=/usr/libexec/bear/wrapper");
57
println!("cargo:rustc-env=PRELOAD_LIBRARY_PATH=/usr/libexec/bear/$LIB/libexec.so");
8+
9+
// Re-run build script if env changes
10+
println!("cargo:rerun-if-env-changed=PATH");
11+
12+
// check things for the libexec.so
13+
check_include_file("dlfcn.h", "dlfcn_h");
14+
check_symbol_exists("dlopen", "dlfcn.h");
15+
check_symbol_exists("dlsym", "dlfcn.h");
16+
check_symbol_exists("dlerror", "dlfcn.h");
17+
check_symbol_exists("dlclose", "dlfcn.h");
18+
check_symbol_exists("RTLD_NEXT", "dlfcn.h");
19+
20+
check_include_file("errno.h", "errno_h");
21+
check_symbol_exists("EACCES", "errno.h");
22+
check_symbol_exists("ENOENT", "errno.h");
23+
24+
// check things for the integration tests
25+
check_include_file("unistd.h", "unistd_h");
26+
check_symbol_exists("execve", "unistd.h");
27+
check_symbol_exists("execv", "unistd.h");
28+
check_symbol_exists("execvpe", "unistd.h");
29+
check_symbol_exists("execvp", "unistd.h");
30+
check_symbol_exists("execvP", "unistd.h");
31+
check_symbol_exists("exect", "unistd.h");
32+
check_symbol_exists("execl", "unistd.h");
33+
check_symbol_exists("execlp", "unistd.h");
34+
check_symbol_exists("execle", "unistd.h");
35+
check_symbol_exists("execveat", "unistd.h");
36+
check_symbol_exists("fexecve", "unistd.h");
37+
38+
check_include_file("spawn.h", "spawn_h");
39+
check_symbol_exists("posix_spawn", "spawn.h");
40+
check_symbol_exists("posix_spawnp", "spawn.h");
41+
42+
check_include_file("stdio.h", "stdio_h");
43+
check_symbol_exists("popen", "stdio.h");
44+
45+
check_include_file("stdlib.h", "stdlib_h");
46+
check_symbol_exists("system", "stdlib.h");
47+
48+
check_executable_exists("true");
49+
check_executable_exists("false");
50+
check_executable_exists("echo");
51+
check_executable_exists("sleep");
52+
check_one_executable_exists("shell", &["sh", "zsh", "bash"]);
53+
check_one_executable_exists("make", &["make", "gmake", "mingw32-make"]);
54+
check_one_executable_exists("compiler_c", &["cc", "gcc", "clang"]);
55+
check_one_executable_exists("compiler_cxx", &["c++", "g++", "clang++"]);
56+
check_one_executable_exists("compiler_fortran", &["gfortran", "flang"]);
57+
check_one_executable_exists("compiler_cuda", &["nvcc"]);
58+
check_executable_exists("libtool");
59+
check_executable_exists("fakeroot");
60+
check_executable_exists("valgrind");
61+
check_executable_exists("ar");
62+
}
63+
64+
fn check_include_file(header: &str, define: &str) {
65+
let result = cc::Build::new()
66+
.cargo_metadata(false)
67+
.cargo_output(false)
68+
.cargo_warnings(false)
69+
.inherit_rustflags(true)
70+
.file(
71+
tempfile::Builder::new()
72+
.prefix("check_include_")
73+
.suffix(".c")
74+
.tempfile_in(std::env::var("OUT_DIR").unwrap_or_else(|_| "target".to_string()))
75+
.expect("Failed to create temp file for include check")
76+
.keep() // Keep the file for cc to compile
77+
.expect("Failed to keep temp file")
78+
.1, // Get the PathBuf
79+
)
80+
.include(header)
81+
.try_compile(define);
82+
83+
match result {
84+
Ok(_) => {
85+
println!("cargo:rustc-cfg=has_header_{}", define);
86+
println!("cargo:rustc-check-cfg=cfg(has_header_{})", define);
87+
println!(
88+
"cargo:warning=Checking for include file: {} ... found",
89+
header
90+
);
91+
}
92+
Err(_) => {
93+
println!(
94+
"cargo:warning=Checking for include file: {} ... missing",
95+
header
96+
);
97+
}
98+
}
99+
}
100+
101+
fn check_symbol_exists(symbol: &str, header: &str) {
102+
let check_code = format!(
103+
r#"
104+
#include <stddef.h>
105+
#include <{header}>
106+
107+
// Use a function pointer to avoid unused function warnings,
108+
// and ensure the linker must find the symbol.
109+
int main() {{
110+
void *ptr = (void*){symbol};
111+
(void)ptr; // Suppress unused variable warning
112+
return 0;
113+
}}
114+
"#,
115+
symbol = symbol,
116+
header = header
117+
);
118+
119+
let (mut file, path) = tempfile::Builder::new()
120+
.prefix(&format!("check_{}", symbol))
121+
.suffix(".c")
122+
.tempfile_in(std::env::var("OUT_DIR").unwrap_or_else(|_| "target".to_string()))
123+
.expect("Failed to create temp file for symbol check")
124+
.keep()
125+
.expect("Failed to keep temp file");
126+
127+
file.write_all(check_code.as_bytes())
128+
.expect("Failed to write to temp file");
129+
file.flush().expect("Failed to flush temp file");
130+
131+
let result = cc::Build::new()
132+
.cargo_metadata(false)
133+
.cargo_output(false)
134+
.cargo_warnings(false)
135+
.inherit_rustflags(true)
136+
.file(path)
137+
.try_compile(&format!("check_{}", symbol));
138+
139+
match result {
140+
Ok(_) => {
141+
println!("cargo:rustc-cfg=has_symbol_{}", symbol);
142+
println!("cargo:rustc-check-cfg=cfg(has_symbol_{})", symbol);
143+
println!("cargo:warning=Checking for symbol: {} ... found", symbol);
144+
}
145+
Err(_) => {
146+
println!("cargo:warning=Checking for symbol: {} ... missing", symbol);
147+
}
148+
}
149+
}
150+
151+
fn check_executable_exists(executable: &str) {
152+
match which::which(executable) {
153+
Ok(path) => {
154+
println!("cargo:rustc-cfg=has_executable_{}", executable);
155+
println!("cargo:rustc-check-cfg=cfg(has_executable_{})", executable);
156+
println!(
157+
"cargo:rustc-env={}_PATH={}",
158+
executable.to_uppercase(),
159+
path.display()
160+
);
161+
println!(
162+
"cargo:warning=Checking for executable: {} ... {}",
163+
executable,
164+
path.display()
165+
);
166+
}
167+
Err(_) => {
168+
println!(
169+
"cargo:warning=Checking for executable: {} ... missing",
170+
executable
171+
);
172+
}
173+
}
174+
}
175+
176+
fn check_one_executable_exists(define: &str, executables: &[&str]) {
177+
for executable in executables {
178+
if let Ok(path) = which::which(executable) {
179+
println!("cargo:rustc-cfg=has_executable_{}", define);
180+
println!("cargo:rustc-check-cfg=cfg(has_executable_{})", define);
181+
println!(
182+
"cargo:rustc-env={}_PATH={}",
183+
define.to_uppercase(),
184+
path.display()
185+
);
186+
println!(
187+
"cargo:warning=Checking for executable: {} ... {}",
188+
define,
189+
path.display()
190+
);
191+
return;
192+
}
193+
}
194+
println!(
195+
"cargo:warning=Checking for executable: {} ... missing",
196+
define
197+
);
6198
}

bear/tests/cli.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// bear/tests/cli.rs
2+
use assert_cmd::Command;
3+
use predicates::prelude::*;
4+
use std::error::Error;
5+
use std::path::Path;
6+
use tempfile::tempdir;
7+
8+
#[test]
9+
fn test_bear_help() -> Result<(), Box<dyn Error>> {
10+
let mut cmd = Command::cargo_bin("bear")?;
11+
cmd.arg("--help");
12+
cmd.assert()
13+
.success()
14+
.stdout(predicate::str::contains("Usage: bear"));
15+
Ok(())
16+
}
17+
18+
#[test]
19+
#[cfg(target_os = "linux")]
20+
#[cfg(has_executable_echo)]
21+
fn test_wrapper_basic() -> Result<(), Box<dyn Error>> {
22+
let work_dir = tempdir()?;
23+
24+
let mut cmd = Command::cargo_bin("bear")?;
25+
cmd.args(["--", "echo", "hello"]);
26+
cmd.current_dir(work_dir.path());
27+
28+
println!("Running command: {:?}", cmd);
29+
30+
// Add assertions based on what bear/wrapper should do
31+
cmd.assert()
32+
.success()
33+
.stdout(predicate::str::contains("hello")); // Adjust assertion as needed
34+
35+
work_dir.close()?; // Clean up the temporary directory
36+
Ok(())
37+
}
38+
39+
// Add more tests for different scenarios, arguments, and interactions
40+
// between 'bear' and 'wrapper'.
41+
42+
#[test]
43+
#[cfg(target_os = "linux")]
44+
#[cfg(has_executable_make)] // Only compile this test if 'make' was found by build.rs
45+
fn test_with_make() -> Result<(), Box<dyn Error>> {
46+
let make_path = env!("MAKE_PATH"); // Get path from env var set by build.rs
47+
println!("Make found at: {}", make_path);
48+
49+
let mut cmd = Command::cargo_bin("bear")?;
50+
let work_dir = tempdir()?;
51+
52+
// Setup: Create a dummy Makefile or project structure
53+
std::fs::write(
54+
work_dir.path().join("Makefile"),
55+
"all:\n\techo \"Running make\"\n",
56+
)?;
57+
58+
cmd.current_dir(work_dir.path());
59+
// Use the detected make path if needed, or just run 'make' if it's in PATH
60+
cmd.args(["--", make_path, "all"]); // Example: run make through bear
61+
62+
cmd.assert()
63+
.success()
64+
.stdout(predicate::str::contains("Running make"));
65+
66+
// Check for compile_commands.json, etc.
67+
// assert!(work_dir.path().join("compile_commands.json").exists());
68+
69+
work_dir.close()?;
70+
Ok(())
71+
}
72+
73+
// Example test requiring 'cc'
74+
#[test]
75+
#[cfg(has_executable_compiler_c)]
76+
fn test_with_cc() -> Result<(), Box<dyn Error>> {
77+
let cc_path = env!("COMPILER_C_PATH");
78+
println!("C Compiler found at: {}", cc_path);
79+
// ... test logic using cc_path ...
80+
assert!(Path::new(cc_path).exists()); // Basic check
81+
Ok(())
82+
}
83+
84+
// Example test requiring 'c++'
85+
#[test]
86+
#[cfg(has_executable_compiler_cxx)]
87+
fn test_with_cplusplus() -> Result<(), Box<dyn Error>> {
88+
let cxx_path = env!("COMPILER_CXX_PATH");
89+
println!("C++ Compiler found at: {}", cxx_path);
90+
// ... test logic using cxx_path ...
91+
assert!(Path::new(cxx_path).exists()); // Basic check
92+
Ok(())
93+
}

0 commit comments

Comments
 (0)