Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/app/debug_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,21 @@ mod tests {
.expect("write oversized log");
}

// Cross-test interference is expected here and the retry loop below MUST
// NOT be collapsed into a single attempt. Production `log()` relies on
// PROCESS-GLOBAL statics: `CHECK_COUNTER` (AtomicU32), `LOG_FILE` (Mutex)
// and the `TEST_CACHE_HOME` redirect. `TEST_MUTEX` only serializes the two
// debug_log tests against each other; it does NOT exclude the hundreds of
// other tests running on parallel threads, many of which invoke the
// production `debug_log!`/`log()` macro. Once this test installs its
// tempdir into the process-global `TEST_CACHE_HOME`, those concurrent
// `log()` calls (a) bump the shared `CHECK_COUNTER` and (b) write to
// `log_path()`, which now points at THIS test's tempdir. So concurrent
// appends and counter bumps can perturb the careful size dance below.
// The retry loop tolerates that interference: we keep trying until one of
// our own `reset_for_test()` + `log()` cycles wins the size check.
let mut truncated = false;
for attempt in 0..20 {
for attempt in 0..50 {
reset_for_test();
log(format_args!("attempt {attempt}"));
let len = std::fs::metadata(&path).map(|m| m.len()).unwrap_or(0);
Expand All @@ -258,7 +271,7 @@ mod tests {
reset_for_test();
assert!(
truncated,
"log() never acquired mutex to truncate oversized file after 20 retries"
"log() never acquired mutex to truncate oversized file after 50 retries"
);
}
}
8 changes: 5 additions & 3 deletions src/ui/viewer/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ use std::thread;
use tempfile::NamedTempFile;

const DEFAULT_PAGE_HEIGHT: usize = 20;
const TEST_CHANNEL_TIMEOUT_SECS: u64 = 1;

fn create_test_file(content: &str) -> NamedTempFile {
let mut file = NamedTempFile::new().unwrap();
Expand Down Expand Up @@ -89,9 +88,12 @@ fn test_viewer_loader_drop_cancels_worker() {
drop(loader);

assert!(cancel.load(Ordering::Relaxed));
// Drop sets `cancel`, so the worker is guaranteed to send once it observes it.
// A blocking recv removes the arbitrary 1s deadline that made this test fail
// under heavy CPU load when the detached yield-looping worker got starved.
done_rx
.recv_timeout(std::time::Duration::from_secs(TEST_CHANNEL_TIMEOUT_SECS))
.unwrap();
.recv()
.expect("worker should send done after observing cancel");
Comment on lines 94 to +96

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using an indefinite blocking recv() in tests can cause the entire test suite to hang indefinitely in CI/CD pipelines if the worker thread panics or fails to send the cancellation signal. To prevent potential test hangs while remaining robust against CPU starvation, consider using recv_timeout with a generous timeout (e.g., 5 seconds).

Suggested change
done_rx
.recv_timeout(std::time::Duration::from_secs(TEST_CHANNEL_TIMEOUT_SECS))
.unwrap();
.recv()
.expect("worker should send done after observing cancel");
done_rx
.recv_timeout(std::time::Duration::from_secs(5))
.expect("worker should send done after observing cancel");

}

#[test]
Expand Down
Loading