test: switch from windows-only debugging to a general solution

This commit is contained in:
zhom
2025-08-02 18:44:36 +04:00
parent 93010dcc64
commit d3558a28c9
2 changed files with 241 additions and 150 deletions
+94 -38
View File
@@ -495,7 +495,11 @@ impl BundlerTestHelper {
/// Get the path to the banderole binary
pub fn get_bundler_path() -> Result<PathBuf> {
let target_dir = std::env::current_dir()?.join("target");
let bundler_path = target_dir.join("debug/banderole");
let bundler_path = if cfg!(windows) {
target_dir.join("debug/banderole.exe")
} else {
target_dir.join("debug/banderole")
};
if !bundler_path.exists() {
// Build the bundler if it doesn't exist
@@ -553,6 +557,18 @@ impl BundlerTestHelper {
);
}
// Debug: Print bundler output
println!(
"Bundler stdout: {}",
String::from_utf8_lossy(&bundle_output.stdout)
);
if !bundle_output.stderr.is_empty() {
println!(
"Bundler stderr: {}",
String::from_utf8_lossy(&bundle_output.stderr)
);
}
// Find the created executable
let executable_name = custom_name.unwrap_or("test-project");
let executable_path = output_dir.join(if cfg!(windows) {
@@ -575,9 +591,31 @@ impl BundlerTestHelper {
}
if !executable_path.exists() {
// List directory contents for debugging
let dir_contents = fs::read_dir(output_dir)
.map(|entries| {
entries
.filter_map(|e| e.ok())
.map(|entry| {
let path = entry.path();
let metadata = fs::metadata(&path).ok();
format!(
"{} (size: {}, is_file: {})",
entry.file_name().to_string_lossy(),
metadata.as_ref().map(|m| m.len()).unwrap_or(0),
metadata.as_ref().map(|m| m.is_file()).unwrap_or(false)
)
})
.collect::<Vec<_>>()
})
.unwrap_or_else(|e| vec![format!("Error reading directory: {}", e)]);
anyhow::bail!(
"Executable was not created at {}",
executable_path.display()
"Executable was not created at {}\nExpected name: {}\nOutput directory: {}\nOutput directory contents: {:?}",
executable_path.display(),
executable_name,
output_dir.display(),
dir_contents
);
}
@@ -590,39 +628,40 @@ impl BundlerTestHelper {
args: &[&str],
env_vars: &[(&str, &str)],
) -> Result<std::process::Output> {
// Windows-specific debugging for "program not found" errors
#[cfg(windows)]
{
if !executable_path.exists() {
anyhow::bail!(
"Windows: Executable does not exist at path: {}\nParent directory exists: {}\nParent directory contents: {:?}",
executable_path.display(),
executable_path.parent().map(|p| p.exists()).unwrap_or(false),
executable_path.parent()
.and_then(|p| fs::read_dir(p).ok())
.map(|entries| entries.filter_map(|e| e.ok()).map(|e| e.file_name().to_string_lossy().to_string()).collect::<Vec<_>>())
.unwrap_or_else(|| vec!["Could not read directory".to_string()])
);
}
// Verify executable exists and is accessible
if !executable_path.exists() {
let parent_contents = executable_path
.parent()
.and_then(|p| fs::read_dir(p).ok())
.map(|entries| {
entries
.filter_map(|e| e.ok())
.map(|e| e.file_name().to_string_lossy().to_string())
.collect::<Vec<_>>()
})
.unwrap_or_else(|| vec!["Could not read directory".to_string()]);
if let Ok(metadata) = fs::metadata(executable_path) {
if !metadata.is_file() {
anyhow::bail!(
"Windows: Path exists but is not a file: {} (is_dir: {})",
executable_path.display(),
metadata.is_dir()
);
}
println!(
"Windows debug: Executable found, size: {} bytes",
metadata.len()
);
} else {
anyhow::bail!(
"Executable does not exist at path: {}\nParent directory exists: {}\nParent directory contents: {:?}",
executable_path.display(),
executable_path.parent().map(|p| p.exists()).unwrap_or(false),
parent_contents
);
}
if let Ok(metadata) = fs::metadata(executable_path) {
if !metadata.is_file() {
anyhow::bail!(
"Windows: Cannot read metadata for executable: {}",
executable_path.display()
"Path exists but is not a file: {} (is_dir: {})",
executable_path.display(),
metadata.is_dir()
);
}
} else {
anyhow::bail!(
"Cannot read metadata for executable: {}",
executable_path.display()
);
}
// Make executable on Unix
@@ -635,13 +674,33 @@ impl BundlerTestHelper {
fs::set_permissions(executable_path, perms)?;
}
let mut cmd = Command::new(executable_path);
// On Windows, ensure we're using the full path and handle potential issues
let mut cmd = if cfg!(windows) {
// Use the full canonical path on Windows to avoid "program not found" issues
let canonical_path = executable_path.canonicalize().with_context(|| {
format!("Failed to canonicalize path: {}", executable_path.display())
})?;
println!(
"Windows: Using canonical path: {}",
canonical_path.display()
);
Command::new(canonical_path)
} else {
Command::new(executable_path)
};
cmd.args(args);
for (key, value) in env_vars {
cmd.env(key, value);
}
println!(
"Executing: {} with args: {:?}",
executable_path.display(),
args
);
let output = cmd.output().with_context(|| {
format!(
"Failed to execute command: {}\nArgs: {:?}\nEnv vars: {:?}\nWorking directory: {:?}",
@@ -674,14 +733,11 @@ impl BundlerTestHelper {
Ok(result) => result.map_err(|e| anyhow::anyhow!("Command execution failed: {}", e)),
Err(_) => {
// Timeout occurred, kill the process
#[cfg(unix)]
{
if cfg!(unix) {
let _ = std::process::Command::new("kill")
.args(["-9", &child_id.to_string()])
.output();
}
#[cfg(windows)]
{
} else if cfg!(windows) {
let _ = std::process::Command::new("taskkill")
.args(["/F", "/PID", &child_id.to_string()])
.output();
+147 -112
View File
@@ -28,41 +28,40 @@ async fn test_concurrent_first_launch() -> Result<()> {
false, // No compression for faster testing
)?;
// Windows-specific debugging for executable creation
#[cfg(windows)]
{
eprintln!(
"Windows debug: Executable path created: {}",
executable_path.display()
);
eprintln!(
"Windows debug: Executable exists: {}",
executable_path.exists()
);
if executable_path.exists() {
if let Ok(metadata) = std::fs::metadata(&executable_path) {
eprintln!("Windows debug: Executable size: {} bytes", metadata.len());
eprintln!("Windows debug: Executable is file: {}", metadata.is_file());
} else {
eprintln!("Windows debug: Cannot read executable metadata");
}
// Debug executable creation
eprintln!(
"Debug: Executable path created: {}",
executable_path.display()
);
eprintln!("Debug: Executable exists: {}", executable_path.exists());
if executable_path.exists() {
if let Ok(metadata) = std::fs::metadata(&executable_path) {
eprintln!("Debug: Executable size: {} bytes", metadata.len());
eprintln!("Debug: Executable is file: {}", metadata.is_file());
} else {
eprintln!("Windows debug: Executable does not exist!");
if let Some(parent) = executable_path.parent() {
eprintln!("Windows debug: Parent directory: {}", parent.display());
eprintln!("Windows debug: Parent exists: {}", parent.exists());
if let Ok(entries) = std::fs::read_dir(parent) {
eprintln!("Windows debug: Parent directory contents:");
for entry in entries.flatten() {
eprintln!(" - {}", entry.file_name().to_string_lossy());
}
} else {
eprintln!("Windows debug: Cannot read parent directory");
eprintln!("Debug: Cannot read executable metadata");
}
} else {
eprintln!("Debug: Executable does not exist!");
if let Some(parent) = executable_path.parent() {
eprintln!("Debug: Parent directory: {}", parent.display());
eprintln!("Debug: Parent exists: {}", parent.exists());
if let Ok(entries) = std::fs::read_dir(parent) {
eprintln!("Debug: Parent directory contents:");
for entry in entries.flatten() {
eprintln!(" - {}", entry.file_name().to_string_lossy());
}
} else {
eprintln!("Debug: Cannot read parent directory");
}
}
}
// Give the filesystem a moment to settle on Windows
if cfg!(windows) {
std::thread::sleep(std::time::Duration::from_millis(100));
}
// Clear any existing cache to ensure we test first launch
TestCacheManager::clear_application_cache()?;
@@ -85,33 +84,54 @@ async fn test_concurrent_first_launch() -> Result<()> {
// Wait for all threads to be ready
barrier.wait();
// Add a small staggered delay to reduce race conditions on Windows
std::thread::sleep(std::time::Duration::from_millis(i as u64 * 10));
let thread_start = Instant::now();
// Execute the binary using the test helper
let output = BundlerTestHelper::run_executable(
executable_path.as_ref(),
&[&format!("--thread-id={i}")],
&[("TEST_VAR", &format!("thread_{i}"))],
)
.map_err(|e| {
#[cfg(windows)]
{
eprintln!(
"Windows debug - Thread {}: Failed to execute binary at {}",
i,
executable_path.as_ref().display()
);
eprintln!("Windows debug - Thread {i}: Error details: {e:?}");
eprintln!("Windows debug - Thread {i}: Error chain:");
let mut source = e.source();
let mut level = 0;
while let Some(err) = source {
eprintln!("Windows debug - Thread {i}: Level {level}: {err}");
source = err.source();
level += 1;
// Execute the binary using the test helper with retry logic for Windows
let mut last_error = None;
let mut output = None;
for attempt in 1..=3 {
match BundlerTestHelper::run_executable(
executable_path.as_ref(),
&[&format!("--thread-id={i}")],
&[("TEST_VAR", &format!("thread_{i}"))],
) {
Ok(result) => {
output = Some(result);
break;
}
Err(e) => {
eprintln!("Thread {i}: Attempt {attempt} failed: {e}");
last_error = Some(e);
if attempt < 3 {
std::thread::sleep(std::time::Duration::from_millis(
100 * attempt as u64,
));
}
}
}
anyhow::anyhow!("Failed to execute binary: {e}")
}
let output = output.ok_or_else(|| {
let e = last_error.unwrap();
eprintln!(
"Thread {}: Failed to execute binary at {} after 3 attempts",
i,
executable_path.as_ref().display()
);
eprintln!("Thread {i}: Final error details: {e:?}");
eprintln!("Thread {i}: Error chain:");
let mut source = e.source();
let mut level = 0;
while let Some(err) = source {
eprintln!("Thread {i}: Level {level}: {err}");
source = err.source();
level += 1;
}
anyhow::anyhow!("Failed to execute binary after retries: {e}")
})?;
let duration = thread_start.elapsed();
@@ -218,50 +238,43 @@ async fn test_cached_concurrent_execution() -> Result<()> {
false,
)?;
// Windows-specific debugging for executable creation
#[cfg(windows)]
{
eprintln!(
"Windows debug (cached): Executable path created: {}",
executable_path.display()
);
eprintln!(
"Windows debug (cached): Executable exists: {}",
executable_path.exists()
);
if executable_path.exists() {
if let Ok(metadata) = std::fs::metadata(&executable_path) {
eprintln!(
"Windows debug (cached): Executable size: {} bytes",
metadata.len()
);
eprintln!(
"Windows debug (cached): Executable is file: {}",
metadata.is_file()
);
} else {
eprintln!("Windows debug (cached): Cannot read executable metadata");
}
// Debug executable creation
eprintln!(
"Debug (cached): Executable path created: {}",
executable_path.display()
);
eprintln!(
"Debug (cached): Executable exists: {}",
executable_path.exists()
);
if executable_path.exists() {
if let Ok(metadata) = std::fs::metadata(&executable_path) {
eprintln!("Debug (cached): Executable size: {} bytes", metadata.len());
eprintln!("Debug (cached): Executable is file: {}", metadata.is_file());
} else {
eprintln!("Windows debug (cached): Executable does not exist!");
if let Some(parent) = executable_path.parent() {
eprintln!(
"Windows debug (cached): Parent directory: {}",
parent.display()
);
eprintln!("Windows debug (cached): Parent exists: {}", parent.exists());
if let Ok(entries) = std::fs::read_dir(parent) {
eprintln!("Windows debug (cached): Parent directory contents:");
for entry in entries.flatten() {
eprintln!(" - {}", entry.file_name().to_string_lossy());
}
} else {
eprintln!("Windows debug (cached): Cannot read parent directory");
eprintln!("Debug (cached): Cannot read executable metadata");
}
} else {
eprintln!("Debug (cached): Executable does not exist!");
if let Some(parent) = executable_path.parent() {
eprintln!("Debug (cached): Parent directory: {}", parent.display());
eprintln!("Debug (cached): Parent exists: {}", parent.exists());
if let Ok(entries) = std::fs::read_dir(parent) {
eprintln!("Debug (cached): Parent directory contents:");
for entry in entries.flatten() {
eprintln!(" - {}", entry.file_name().to_string_lossy());
}
} else {
eprintln!("Debug (cached): Cannot read parent directory");
}
}
}
// Give the filesystem a moment to settle on Windows
if cfg!(windows) {
std::thread::sleep(std::time::Duration::from_millis(100));
}
// Clear cache and run once to populate it
TestCacheManager::clear_application_cache()?;
@@ -290,32 +303,54 @@ async fn test_cached_concurrent_execution() -> Result<()> {
let handle = thread::spawn(move || -> Result<(usize, Duration)> {
barrier.wait();
// Add a small staggered delay to reduce race conditions on Windows
std::thread::sleep(std::time::Duration::from_millis(i as u64 * 10));
let thread_start = Instant::now();
let output = BundlerTestHelper::run_executable(
executable_path.as_ref(),
&[],
&[("TEST_VAR", &format!("cached_{i}"))],
)
.map_err(|e| {
#[cfg(windows)]
{
eprintln!(
"Windows debug - Cached thread {}: Failed to execute binary at {}",
i,
executable_path.as_ref().display()
);
eprintln!("Windows debug - Cached thread {e}: Error details: {e:?}");
eprintln!("Windows debug - Cached thread {i}: Error chain:");
let mut source = e.source();
let mut level = 0;
while let Some(err) = source {
eprintln!("Windows debug - Cached thread {i}: Level {level}: {err}");
source = err.source();
level += 1;
// Execute the binary using the test helper with retry logic for Windows
let mut last_error = None;
let mut output = None;
for attempt in 1..=3 {
match BundlerTestHelper::run_executable(
executable_path.as_ref(),
&[],
&[("TEST_VAR", &format!("cached_{i}"))],
) {
Ok(result) => {
output = Some(result);
break;
}
Err(e) => {
eprintln!("Cached thread {i}: Attempt {attempt} failed: {e}");
last_error = Some(e);
if attempt < 3 {
std::thread::sleep(std::time::Duration::from_millis(
100 * attempt as u64,
));
}
}
}
anyhow::anyhow!("Failed to execute binary: {}", e)
}
let output = output.ok_or_else(|| {
let e = last_error.unwrap();
eprintln!(
"Cached thread {}: Failed to execute binary at {} after 3 attempts",
i,
executable_path.as_ref().display()
);
eprintln!("Cached thread {i}: Final error details: {e:?}");
eprintln!("Cached thread {i}: Error chain:");
let mut source = e.source();
let mut level = 0;
while let Some(err) = source {
eprintln!("Cached thread {i}: Level {level}: {err}");
source = err.source();
level += 1;
}
anyhow::anyhow!("Failed to execute binary after retries: {}", e)
})?;
let duration = thread_start.elapsed();