Skip to content

Commit 4f4432b

Browse files
committed
Extract shared logic for matching on stdout line
Avoid repetition by introducing `wait_for_stdout_match` which reads from stdout until a match is found against the given pattern. Note: it's important that `child_stdout` lives long enough, until `sigint` is called on the child process. Otherwise the interrupt handler would try to `println!` to a dropped stdout and panic.
1 parent 9ebf027 commit 4f4432b

File tree

1 file changed

+54
-77
lines changed

1 file changed

+54
-77
lines changed

payjoin-cli/tests/e2e.rs

+54-77
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ mod e2e {
146146
use std::path::PathBuf;
147147

148148
use payjoin_test_utils::{init_tracing, TestServices};
149-
use tokio::process::Child;
149+
use tokio::process::{Child, ChildStdout};
150150

151151
type Result<T> = std::result::Result<T, BoxError>;
152152

@@ -266,117 +266,94 @@ mod e2e {
266266
}
267267

268268
async fn get_bip21_from_receiver(mut cli_receiver: Child) -> String {
269-
let stdout =
270-
cli_receiver.stdout.take().expect("Failed to take stdout of child process");
271-
let reader = BufReader::new(stdout);
272-
let mut stdout = tokio::io::stdout();
273-
let mut bip21 = String::new();
274-
275-
let mut lines = reader.lines();
276-
277-
while let Some(line) = lines.next_line().await.expect("Failed to read line from stdout")
278-
{
279-
// Write to stdout regardless
280-
stdout
281-
.write_all(format!("{}\n", line).as_bytes())
282-
.await
283-
.expect("Failed to write to stdout");
284-
285-
if line.to_ascii_uppercase().starts_with("BITCOIN") {
286-
bip21 = line;
287-
break;
288-
}
289-
}
269+
let mut stdout =
270+
cli_receiver.stdout.take().expect("failed to take stdout of child process");
271+
let bip21 = wait_for_stdout_match(&mut stdout, |line| {
272+
line.to_ascii_uppercase().starts_with("BITCOIN")
273+
})
274+
.await
275+
.expect("payjoin-cli receiver should output a bitcoin URI");
290276
log::debug!("Got bip21 {}", &bip21);
291277

292278
sigint(cli_receiver).await.expect("Failed to kill payjoin-cli");
293279
bip21
294280
}
295281

296282
async fn send_until_request_timeout(mut cli_sender: Child) -> Result<()> {
297-
let stdout = cli_sender.stdout.take().expect("Failed to take stdout of child process");
298-
let reader = BufReader::new(stdout);
299-
let mut stdout = tokio::io::stdout();
300-
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
301-
302-
let mut lines = reader.lines();
303-
while let Some(line) = lines.next_line().await.expect("Failed to read line from stdout")
304-
{
305-
stdout
306-
.write_all(format!("{}\n", line).as_bytes())
307-
.await
308-
.expect("Failed to write to stdout");
309-
if line.contains("No response yet.") {
310-
let _ = tx.send(true).await;
311-
break;
312-
}
313-
}
314-
283+
let mut stdout =
284+
cli_sender.stdout.take().expect("failed to take stdout of child process");
315285
let timeout = tokio::time::Duration::from_secs(35);
316-
let fallback_sent = tokio::time::timeout(timeout, rx.recv()).await?;
286+
let res = tokio::time::timeout(
287+
timeout,
288+
wait_for_stdout_match(&mut stdout, |line| line.contains("No response yet.")),
289+
)
290+
.await?;
317291

318292
sigint(cli_sender).await.expect("Failed to kill payjoin-cli initial sender");
319-
320-
assert!(fallback_sent.unwrap_or(false), "Fallback send was not detected");
293+
assert!(res.is_some(), "Fallback send was not detected");
321294
Ok(())
322295
}
323296

324297
async fn respond_with_payjoin(mut cli_receive_resumer: Child) -> Result<()> {
325-
let stdout =
298+
let mut stdout =
326299
cli_receive_resumer.stdout.take().expect("Failed to take stdout of child process");
327-
let reader = BufReader::new(stdout);
328-
let mut stdout = tokio::io::stdout();
329-
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
330-
331-
let mut lines = reader.lines();
332-
while let Some(line) = lines.next_line().await.expect("Failed to read line from stdout")
333-
{
334-
stdout
335-
.write_all(format!("{}\n", line).as_bytes())
336-
.await
337-
.expect("Failed to write to stdout");
338-
if line.contains("Response successful") {
339-
let _ = tx.send(true).await;
340-
break;
341-
}
342-
}
343-
344300
let timeout = tokio::time::Duration::from_secs(10);
345-
let response_successful = tokio::time::timeout(timeout, rx.recv()).await?;
301+
let res = tokio::time::timeout(
302+
timeout,
303+
wait_for_stdout_match(&mut stdout, |line| line.contains("Response successful")),
304+
)
305+
.await?;
346306

347307
sigint(cli_receive_resumer).await.expect("Failed to kill payjoin-cli");
348-
349-
assert!(response_successful.unwrap_or(false), "Did not respond with Payjoin PSBT");
308+
assert!(res.is_some(), "Did not respond with Payjoin PSBT");
350309
Ok(())
351310
}
352311

353312
async fn check_payjoin_sent(mut cli_send_resumer: Child) -> Result<()> {
354-
let stdout =
313+
let mut stdout =
355314
cli_send_resumer.stdout.take().expect("Failed to take stdout of child process");
356-
let reader = BufReader::new(stdout);
357-
let mut stdout = tokio::io::stdout();
358-
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
315+
let timeout = tokio::time::Duration::from_secs(10);
316+
let res = tokio::time::timeout(
317+
timeout,
318+
wait_for_stdout_match(&mut stdout, |line| line.contains("Payjoin sent")),
319+
)
320+
.await?;
321+
322+
sigint(cli_send_resumer).await.expect("Failed to kill payjoin-cli");
323+
assert!(res.is_some(), "Payjoin send was not detected");
324+
Ok(())
325+
}
359326

327+
/// Read lines from `child_stdout` until `match_pattern` is found and the corresponding
328+
/// line is returned.
329+
/// Also writes every read line to tokio::io::stdout();
330+
async fn wait_for_stdout_match<F>(
331+
child_stdout: &mut ChildStdout,
332+
match_pattern: F,
333+
) -> Option<String>
334+
where
335+
F: Fn(&str) -> bool,
336+
{
337+
let reader = BufReader::new(child_stdout);
360338
let mut lines = reader.lines();
339+
let mut res = None;
340+
341+
let mut stdout = tokio::io::stdout();
361342
while let Some(line) = lines.next_line().await.expect("Failed to read line from stdout")
362343
{
344+
// Write all output to tests stdout
363345
stdout
364346
.write_all(format!("{}\n", line).as_bytes())
365347
.await
366348
.expect("Failed to write to stdout");
367-
if line.contains("Payjoin sent") {
368-
let _ = tx.send(true).await;
349+
350+
if match_pattern(&line) {
351+
res = Some(line);
369352
break;
370353
}
371354
}
372355

373-
let timeout = tokio::time::Duration::from_secs(10);
374-
let payjoin_sent = tokio::time::timeout(timeout, rx.recv()).await?;
375-
376-
sigint(cli_send_resumer).await.expect("Failed to kill payjoin-cli");
377-
378-
assert!(payjoin_sent.unwrap_or(false), "Payjoin send was not detected");
379-
Ok(())
356+
res
380357
}
381358

382359
Ok(())

0 commit comments

Comments
 (0)