test: harden ci flake diagnostics (#365)
This commit is contained in:
parent
bdc189e32e
commit
9586ee9c3f
|
|
@ -1230,7 +1230,7 @@ mod tests {
|
|||
let repo = create_committed_repo("api-worktree-open-source-repo");
|
||||
let event_hub = crate::api::EventHub::default();
|
||||
let mut app = test_app_with_event_hub(event_hub.clone());
|
||||
app.state.default_shell = "/bin/true".into();
|
||||
app.state.default_shell = "/usr/bin/true".into();
|
||||
|
||||
let response = app.handle_api_request(Request {
|
||||
id: "req".into(),
|
||||
|
|
@ -1242,7 +1242,9 @@ mod tests {
|
|||
}),
|
||||
});
|
||||
|
||||
let success: SuccessResponse = serde_json::from_str(&response).unwrap();
|
||||
let success: SuccessResponse = serde_json::from_str(&response).unwrap_or_else(|err| {
|
||||
panic!("expected success response, got {response}: {err}");
|
||||
});
|
||||
let ResponseResult::WorktreeOpened {
|
||||
workspace,
|
||||
already_open,
|
||||
|
|
|
|||
|
|
@ -755,8 +755,7 @@ fn live_handoff_accepts_old_pane_id_from_child_env() {
|
|||
"params": {"pane_id": pane_id, "text": format!("printf '%s' \"$HERDR_PANE_ID\" > {}", pane_id_marker.display()), "keys": ["Enter"]}
|
||||
}),
|
||||
));
|
||||
support::wait_for_file(&pane_id_marker, Duration::from_secs(5));
|
||||
let old_pane_id = fs::read_to_string(&pane_id_marker).unwrap();
|
||||
let old_pane_id = wait_for_file_contains(&pane_id_marker, "p_", Duration::from_secs(5));
|
||||
assert!(
|
||||
old_pane_id.starts_with("p_"),
|
||||
"unexpected pane id from env: {old_pane_id:?}"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
mod support;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fs;
|
||||
use std::io::{self, BufRead, BufReader, Read, Write};
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
|
@ -190,6 +191,20 @@ fn count_log_occurrences(path: &Path, needle: &str) -> usize {
|
|||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn log_tail(path: &Path, lines: usize) -> String {
|
||||
let Ok(text) = fs::read_to_string(path) else {
|
||||
return format!("could not read {}", path.display());
|
||||
};
|
||||
let mut tail = VecDeque::with_capacity(lines);
|
||||
for line in text.lines() {
|
||||
if tail.len() == lines {
|
||||
tail.pop_front();
|
||||
}
|
||||
tail.push_back(line.to_string());
|
||||
}
|
||||
tail.into_iter().collect::<Vec<_>>().join("\n")
|
||||
}
|
||||
|
||||
fn wait_for_log_occurrence_count(
|
||||
path: &Path,
|
||||
needle: &str,
|
||||
|
|
@ -673,12 +688,13 @@ fn wait_for_frame(stream: &mut UnixStream, timeout: Duration) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
fn wait_for_frame_matching(
|
||||
fn wait_for_frame_matching_with_snapshots(
|
||||
stream: &mut UnixStream,
|
||||
timeout: Duration,
|
||||
predicate: impl Fn(&FrameWire) -> bool,
|
||||
) -> io::Result<bool> {
|
||||
) -> io::Result<(bool, Vec<String>)> {
|
||||
let deadline = Instant::now() + timeout;
|
||||
let mut snapshots = VecDeque::with_capacity(5);
|
||||
while Instant::now() < deadline {
|
||||
let slice = deadline
|
||||
.saturating_duration_since(Instant::now())
|
||||
|
|
@ -686,8 +702,12 @@ fn wait_for_frame_matching(
|
|||
match read_server_message_payload(stream, slice) {
|
||||
Ok((1, frame_payload)) => {
|
||||
let frame = decode_frame_payload(&frame_payload)?;
|
||||
if snapshots.len() == 5 {
|
||||
snapshots.pop_front();
|
||||
}
|
||||
snapshots.push_back(frame_text(&frame));
|
||||
if predicate(&frame) {
|
||||
return Ok(true);
|
||||
return Ok((true, snapshots.into_iter().collect()));
|
||||
}
|
||||
}
|
||||
Ok((_variant, _payload)) => {}
|
||||
|
|
@ -696,12 +716,12 @@ fn wait_for_frame_matching(
|
|||
}
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
Ok((false, snapshots.into_iter().collect()))
|
||||
}
|
||||
|
||||
fn frame_contains_text(frame: &FrameWire, needle: &str) -> bool {
|
||||
fn frame_text(frame: &FrameWire) -> String {
|
||||
if frame.cells.is_empty() {
|
||||
return false;
|
||||
return String::new();
|
||||
}
|
||||
|
||||
let row_width = frame.width.max(1) as usize;
|
||||
|
|
@ -720,7 +740,11 @@ fn frame_contains_text(frame: &FrameWire, needle: &str) -> bool {
|
|||
let _ = (cursor.x, cursor.y, cursor.visible, cursor.shape);
|
||||
}
|
||||
|
||||
full_text.contains(needle)
|
||||
full_text
|
||||
}
|
||||
|
||||
fn frame_contains_text(frame: &FrameWire, needle: &str) -> bool {
|
||||
frame_text(frame).contains(needle)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -791,7 +815,7 @@ fn multi_client_effective_size_shrinks_when_smaller_client_joins() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn multi_client_broadcasts_frame_updates_to_all_clients_within_500ms() {
|
||||
fn multi_client_broadcasts_frame_updates_to_all_clients() {
|
||||
let _lock = test_lock();
|
||||
let base = unique_test_dir();
|
||||
let config_home = base.join("config");
|
||||
|
|
@ -822,25 +846,26 @@ fn multi_client_broadcasts_frame_updates_to_all_clients_within_500ms() {
|
|||
.unwrap_or(0)
|
||||
);
|
||||
|
||||
let start = Instant::now();
|
||||
send_client_input(&mut client_a, marker.as_bytes());
|
||||
let received = wait_for_frame_matching(&mut client_b, Duration::from_millis(500), |frame| {
|
||||
frame_contains_text(frame, &marker)
|
||||
})
|
||||
.expect("frame decoding should succeed");
|
||||
send_client_input(&mut client_a, format!("echo {marker}\n").as_bytes());
|
||||
if !pane_read_recent_contains(&api_socket, &pane_id, &marker, Duration::from_secs(5)) {
|
||||
panic!(
|
||||
"pane output should include client A marker so broadcast reflects a real state change. pane output:\n{}\nserver log tail:\n{}",
|
||||
pane_read_recent(&api_socket, &pane_id, 200),
|
||||
log_tail(&server_log_path(&config_home), 80)
|
||||
);
|
||||
}
|
||||
let (received, client_b_frames) =
|
||||
wait_for_frame_matching_with_snapshots(&mut client_b, Duration::from_secs(10), |frame| {
|
||||
frame_contains_text(frame, &marker)
|
||||
})
|
||||
.expect("frame decoding should succeed");
|
||||
|
||||
assert!(
|
||||
received,
|
||||
"client B should receive a broadcast frame containing client A marker within 500ms"
|
||||
);
|
||||
assert!(
|
||||
start.elapsed() <= Duration::from_millis(500),
|
||||
"broadcast frame containing marker should arrive within 500ms, got {:?}",
|
||||
start.elapsed()
|
||||
);
|
||||
assert!(
|
||||
pane_read_recent_contains(&api_socket, &pane_id, &marker, Duration::from_secs(5)),
|
||||
"pane output should include client A marker so broadcast reflects a real state change"
|
||||
"client B should receive a broadcast frame containing client A marker. pane output:\n{}\nclient B frame snapshots:\n{}\nserver log tail:\n{}",
|
||||
pane_read_recent(&api_socket, &pane_id, 200),
|
||||
client_b_frames.join("\n--- frame ---\n"),
|
||||
log_tail(&server_log_path(&config_home), 80)
|
||||
);
|
||||
|
||||
cleanup_spawned_herdr(server, base);
|
||||
|
|
|
|||
Loading…
Reference in New Issue