test(client): unify fake and real backend testing via BackendPeer
Introduce a BackendPeer abstraction to transparently switch between fake and real IRTT backends. This simplifies test logic by removing manual skip checks and providing a consistent interface for server lifecycle management.
This commit is contained in:
1 parent
3d49f725ae
commit
a4e2f3aef4
4 files changed
+186
-76
No files matched your search
@@ -9,10 +9,9 @@ use irtt_client::{
|
|||||||
use irtt_proto::{echo_packet_len, ProtoError, TimestampFields};
|
use irtt_proto::{echo_packet_len, ProtoError, TimestampFields};
|
||||||
|
|
||||||
use support::{
|
use support::{
|
||||||
config_for_params, default_params, params_for_modes, require_real_backend,
|
config_for_params, default_params, params_for_modes, run_one_probe_with_config,
|
||||||
run_one_probe_with_config, standard_timestamps, start_bad_hmac_echo_reply_server,
|
standard_timestamps, start_bad_hmac_echo_reply_server, start_hmac_close_server,
|
||||||
start_hmac_close_server, start_hmac_required_open_drop_server, RealIrtServer,
|
start_hmac_required_open_drop_server, BackendPeer, ServerObservation, TOKEN,
|
||||||
ServerObservation, TOKEN,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -297,14 +296,11 @@ fn assert_no_duplicate_late_or_warning(events: &[ClientEvent]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn real_hmac_correct_key_succeeds() {
|
fn backend_hmac_correct_key_succeeds() {
|
||||||
if !require_real_backend() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let key = b"compat-secret".to_vec();
|
let key = b"compat-secret".to_vec();
|
||||||
let server = RealIrtServer::start(Some(&key)).unwrap();
|
|
||||||
let params = default_params();
|
let params = default_params();
|
||||||
let mut config = config_for_params(server.addr(), ¶ms);
|
let peer = BackendPeer::start_open_echo(params.clone(), Some(key.clone()));
|
||||||
|
let mut config = config_for_params(peer.addr(), ¶ms);
|
||||||
config.hmac_key = Some(key);
|
config.hmac_key = Some(key);
|
||||||
|
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
@@ -322,13 +318,10 @@ fn real_hmac_correct_key_succeeds() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn real_hmac_wrong_key_fails() {
|
fn backend_hmac_wrong_key_fails() {
|
||||||
if !require_real_backend() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let server_key = b"compat-secret".to_vec();
|
let server_key = b"compat-secret".to_vec();
|
||||||
let server = RealIrtServer::start(Some(&server_key)).unwrap();
|
let peer = BackendPeer::start_hmac_required(server_key);
|
||||||
let mut config = config_for_params(server.addr(), &default_params());
|
let mut config = config_for_params(peer.addr(), &default_params());
|
||||||
config.open_timeouts = vec![Duration::from_millis(200)];
|
config.open_timeouts = vec![Duration::from_millis(200)];
|
||||||
config.hmac_key = Some(b"wrong-secret".to_vec());
|
config.hmac_key = Some(b"wrong-secret".to_vec());
|
||||||
|
|
||||||
@@ -340,13 +333,10 @@ fn real_hmac_wrong_key_fails() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn real_hmac_missing_key_fails() {
|
fn backend_hmac_missing_key_fails() {
|
||||||
if !require_real_backend() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let server_key = b"compat-secret".to_vec();
|
let server_key = b"compat-secret".to_vec();
|
||||||
let server = RealIrtServer::start(Some(&server_key)).unwrap();
|
let peer = BackendPeer::start_hmac_required(server_key);
|
||||||
let mut config = config_for_params(server.addr(), &default_params());
|
let mut config = config_for_params(peer.addr(), &default_params());
|
||||||
config.open_timeouts = vec![Duration::from_millis(200)];
|
config.open_timeouts = vec![Duration::from_millis(200)];
|
||||||
config.hmac_key = None;
|
config.hmac_key = None;
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ use irtt_client::{
|
|||||||
use irtt_proto::{echo_packet_len, Clock, Params, ReceivedStats, StampAt, TimestampFields};
|
use irtt_proto::{echo_packet_len, Clock, Params, ReceivedStats, StampAt, TimestampFields};
|
||||||
|
|
||||||
use support::{
|
use support::{
|
||||||
config_for_params, default_params, params_for_modes, require_real_backend, run_one_probe,
|
config_for_params, default_params, params_for_modes, run_one_probe, run_one_probe_with_config,
|
||||||
run_one_probe_with_config, server_fill, standard_timestamps, start_open_server, OneProbeRun,
|
server_fill, standard_timestamps, start_open_server, BackendPeer, OneProbeRun,
|
||||||
RealIrtServer, ServerObservation, RECV_COUNT, RECV_WINDOW, TOKEN,
|
ServerObservation, RECV_COUNT, RECV_WINDOW, TOKEN,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ReplyView<'a> {
|
struct ReplyView<'a> {
|
||||||
@@ -374,13 +374,10 @@ fn assert_one_way_presence(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn real_basic_open_echo_close() {
|
fn backend_basic_open_echo_close() {
|
||||||
if !require_real_backend() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let server = RealIrtServer::start(None).unwrap();
|
|
||||||
let params = default_params();
|
let params = default_params();
|
||||||
let mut client = Client::connect(config_for_params(server.addr(), ¶ms)).unwrap();
|
let peer = BackendPeer::start_open_echo(params.clone(), None);
|
||||||
|
let mut client = Client::connect(config_for_params(peer.addr(), ¶ms)).unwrap();
|
||||||
|
|
||||||
let outcome = client.open(ClientTimestamp::now()).unwrap();
|
let outcome = client.open(ClientTimestamp::now()).unwrap();
|
||||||
assert!(matches!(outcome, irtt_client::OpenOutcome::Started { .. }));
|
assert!(matches!(outcome, irtt_client::OpenOutcome::Started { .. }));
|
||||||
@@ -396,19 +393,16 @@ fn real_basic_open_echo_close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn real_received_stats_smoke() {
|
fn backend_received_stats_smoke() {
|
||||||
if !require_real_backend() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for mode in [
|
for mode in [
|
||||||
ReceivedStats::None,
|
ReceivedStats::None,
|
||||||
ReceivedStats::Count,
|
ReceivedStats::Count,
|
||||||
ReceivedStats::Window,
|
ReceivedStats::Window,
|
||||||
ReceivedStats::Both,
|
ReceivedStats::Both,
|
||||||
] {
|
] {
|
||||||
let server = RealIrtServer::start(None).unwrap();
|
|
||||||
let params = params_for_modes(mode, StampAt::None, Clock::Both);
|
let params = params_for_modes(mode, StampAt::None, Clock::Both);
|
||||||
let mut client = Client::connect(config_for_params(server.addr(), ¶ms)).unwrap();
|
let peer = BackendPeer::start_open_echo(params.clone(), None);
|
||||||
|
let mut client = Client::connect(config_for_params(peer.addr(), ¶ms)).unwrap();
|
||||||
|
|
||||||
client.open(ClientTimestamp::now()).unwrap();
|
client.open(ClientTimestamp::now()).unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
@@ -421,10 +415,7 @@ fn real_received_stats_smoke() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn real_timestamp_smoke() {
|
fn backend_timestamp_smoke() {
|
||||||
if !require_real_backend() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for mode in [
|
for mode in [
|
||||||
StampAt::None,
|
StampAt::None,
|
||||||
StampAt::Send,
|
StampAt::Send,
|
||||||
@@ -432,9 +423,9 @@ fn real_timestamp_smoke() {
|
|||||||
StampAt::Both,
|
StampAt::Both,
|
||||||
StampAt::Midpoint,
|
StampAt::Midpoint,
|
||||||
] {
|
] {
|
||||||
let server = RealIrtServer::start(None).unwrap();
|
|
||||||
let params = params_for_modes(ReceivedStats::None, mode, Clock::Both);
|
let params = params_for_modes(ReceivedStats::None, mode, Clock::Both);
|
||||||
let mut client = Client::connect(config_for_params(server.addr(), ¶ms)).unwrap();
|
let peer = BackendPeer::start_open_echo(params.clone(), None);
|
||||||
|
let mut client = Client::connect(config_for_params(peer.addr(), ¶ms)).unwrap();
|
||||||
|
|
||||||
client.open(ClientTimestamp::now()).unwrap();
|
client.open(ClientTimestamp::now()).unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
@@ -477,14 +468,11 @@ fn real_timestamp_smoke() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn real_clock_smoke() {
|
fn backend_clock_smoke() {
|
||||||
if !require_real_backend() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for clock in [Clock::Wall, Clock::Monotonic, Clock::Both] {
|
for clock in [Clock::Wall, Clock::Monotonic, Clock::Both] {
|
||||||
let server = RealIrtServer::start(None).unwrap();
|
|
||||||
let params = params_for_modes(ReceivedStats::None, StampAt::Both, clock);
|
let params = params_for_modes(ReceivedStats::None, StampAt::Both, clock);
|
||||||
let mut client = Client::connect(config_for_params(server.addr(), ¶ms)).unwrap();
|
let peer = BackendPeer::start_open_echo(params.clone(), None);
|
||||||
|
let mut client = Client::connect(config_for_params(peer.addr(), ¶ms)).unwrap();
|
||||||
|
|
||||||
client.open(ClientTimestamp::now()).unwrap();
|
client.open(ClientTimestamp::now()).unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
|
|||||||
@@ -25,20 +25,62 @@ pub const TOKEN: u64 = 0x1234_5678_90ab_cdef;
|
|||||||
pub const RECV_COUNT: u32 = 42;
|
pub const RECV_COUNT: u32 = 42;
|
||||||
pub const RECV_WINDOW: u64 = 0x07;
|
pub const RECV_WINDOW: u64 = 0x07;
|
||||||
|
|
||||||
pub fn require_real_backend() -> bool {
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum TestBackendKind {
|
||||||
|
Fake,
|
||||||
|
Real,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selected_backend() -> TestBackendKind {
|
||||||
match std::env::var("IRTT_TEST_BACKEND").as_deref() {
|
match std::env::var("IRTT_TEST_BACKEND").as_deref() {
|
||||||
Ok("real") => {
|
Ok("real") => {
|
||||||
let irtt_bin = std::env::var("IRTT_BIN").unwrap_or_else(|_| "irtt".to_string());
|
let irtt_bin = std::env::var("IRTT_BIN").unwrap_or_else(|_| "irtt".to_string());
|
||||||
match Command::new(&irtt_bin).arg("version").output() {
|
match Command::new(&irtt_bin).arg("version").output() {
|
||||||
Ok(output) if output.status.success() => true,
|
Ok(output) if output.status.success() => TestBackendKind::Real,
|
||||||
_ => panic!("IRTT_TEST_BACKEND=real but irtt binary not found at '{irtt_bin}'"),
|
_ => panic!("IRTT_TEST_BACKEND=real but irtt binary not found at '{irtt_bin}'"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok("fake") | Err(_) => false,
|
Ok("fake") | Err(_) => TestBackendKind::Fake,
|
||||||
Ok(other) => panic!("unknown IRTT_TEST_BACKEND value: {other}"),
|
Ok(other) => panic!("unknown IRTT_TEST_BACKEND value: {other}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum BackendPeer {
|
||||||
|
Fake(FakeServer),
|
||||||
|
Real(RealIrtServer),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackendPeer {
|
||||||
|
pub fn addr(&self) -> SocketAddr {
|
||||||
|
match self {
|
||||||
|
BackendPeer::Fake(s) => s.addr,
|
||||||
|
BackendPeer::Real(s) => s.addr(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_open_echo(params: Params, hmac_key: Option<Vec<u8>>) -> Self {
|
||||||
|
match selected_backend() {
|
||||||
|
TestBackendKind::Fake => {
|
||||||
|
let timestamps = standard_timestamps();
|
||||||
|
BackendPeer::Fake(start_one_probe_server(params, timestamps, hmac_key))
|
||||||
|
}
|
||||||
|
TestBackendKind::Real => {
|
||||||
|
BackendPeer::Real(RealIrtServer::start(hmac_key.as_deref()).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_hmac_required(key: Vec<u8>) -> Self {
|
||||||
|
match selected_backend() {
|
||||||
|
TestBackendKind::Fake => BackendPeer::Fake(start_hmac_required_open_drop_server(
|
||||||
|
key,
|
||||||
|
Duration::from_millis(250),
|
||||||
|
)),
|
||||||
|
TestBackendKind::Real => BackendPeer::Real(RealIrtServer::start(Some(&key)).unwrap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct FakeServer {
|
pub struct FakeServer {
|
||||||
pub addr: SocketAddr,
|
pub addr: SocketAddr,
|
||||||
rx: mpsc::Receiver<ServerObservation>,
|
rx: mpsc::Receiver<ServerObservation>,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use std::time::{Duration, Instant};
|
|||||||
pub struct RealIrtServer {
|
pub struct RealIrtServer {
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
child: Child,
|
child: Child,
|
||||||
|
keepalive_ms: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RealIrtServer {
|
impl RealIrtServer {
|
||||||
@@ -20,6 +21,12 @@ impl RealIrtServer {
|
|||||||
|
|
||||||
let irtt_bin = std::env::var("IRTT_BIN").unwrap_or_else(|_| "irtt".to_string());
|
let irtt_bin = std::env::var("IRTT_BIN").unwrap_or_else(|_| "irtt".to_string());
|
||||||
|
|
||||||
|
let keepalive_ms = std::env::var("IRTT_TEST_KEEP_SERVER_MS")
|
||||||
|
.ok()
|
||||||
|
.and_then(|v| v.parse::<u64>().ok());
|
||||||
|
|
||||||
|
debug_eprintln!("[real_irtt] backend=real irtt_bin={irtt_bin} bind={bind}");
|
||||||
|
|
||||||
let mut cmd = Command::new(&irtt_bin);
|
let mut cmd = Command::new(&irtt_bin);
|
||||||
cmd.arg("server")
|
cmd.arg("server")
|
||||||
.arg("-b")
|
.arg("-b")
|
||||||
@@ -33,18 +40,42 @@ impl RealIrtServer {
|
|||||||
cmd.arg(format!("--hmac=0x{}", hex_key));
|
cmd.arg(format!("--hmac=0x{}", hex_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let full_cmd = format!("{cmd:?}");
|
||||||
|
debug_eprintln!("[real_irtt] spawning: {full_cmd}");
|
||||||
|
|
||||||
let mut child = cmd
|
let mut child = cmd
|
||||||
.spawn()
|
.spawn()
|
||||||
.map_err(|e| format!("failed to spawn irtt server: {}", e))?;
|
.map_err(|e| format!("failed to spawn irtt server: {}", e))?;
|
||||||
|
|
||||||
|
let pid = child.id();
|
||||||
|
debug_eprintln!("[real_irtt] child pid={pid}");
|
||||||
|
|
||||||
let stdout = child.stdout.take().ok_or("failed to capture stdout")?;
|
let stdout = child.stdout.take().ok_or("failed to capture stdout")?;
|
||||||
let (tx, rx) = mpsc::channel();
|
let stderr = child.stderr.take().ok_or("failed to capture stderr")?;
|
||||||
|
|
||||||
|
let (out_tx, out_rx) = mpsc::channel();
|
||||||
|
let (err_tx, err_rx) = mpsc::channel();
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let reader = BufReader::new(stdout);
|
let reader = BufReader::new(stdout);
|
||||||
for line in reader.lines() {
|
for line in reader.lines() {
|
||||||
match line {
|
match line {
|
||||||
Ok(l) => {
|
Ok(l) => {
|
||||||
if tx.send(l).is_err() {
|
if out_tx.send(("stdout", l)).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
let reader = BufReader::new(stderr);
|
||||||
|
for line in reader.lines() {
|
||||||
|
match line {
|
||||||
|
Ok(l) => {
|
||||||
|
if err_tx.send(("stderr", l)).is_err() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,40 +86,59 @@ impl RealIrtServer {
|
|||||||
|
|
||||||
let timeout = Duration::from_secs(5);
|
let timeout = Duration::from_secs(5);
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let mut stdout_lines = Vec::new();
|
let mut captured_lines = Vec::new();
|
||||||
loop {
|
|
||||||
|
let ready = loop {
|
||||||
let remaining = timeout
|
let remaining = timeout
|
||||||
.checked_sub(start.elapsed())
|
.checked_sub(start.elapsed())
|
||||||
.unwrap_or(Duration::ZERO);
|
.unwrap_or(Duration::ZERO);
|
||||||
match rx.recv_timeout(remaining) {
|
|
||||||
Ok(line) => {
|
let line = select_line(&out_rx, &err_rx, remaining);
|
||||||
stdout_lines.push(line.clone());
|
match line {
|
||||||
if line.contains("[ListenerStart]") {
|
Some((stream, text)) => {
|
||||||
break;
|
debug_eprintln!("[real_irtt] {stream}: {text}");
|
||||||
|
captured_lines.push(format!("{stream}: {text}"));
|
||||||
|
if text.contains("[ListenerStart]") {
|
||||||
|
break true;
|
||||||
|
}
|
||||||
|
if (text.contains("[ListenerStop]") || text.contains("[ServerStop]"))
|
||||||
|
&& (text.contains("error") || text.contains("Error"))
|
||||||
|
{
|
||||||
|
break false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(mpsc::RecvTimeoutError::Timeout) => {
|
None => {
|
||||||
|
break false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !ready {
|
||||||
|
let exit_status = child.try_wait().ok().flatten();
|
||||||
|
debug_eprintln!("[real_irtt] startup failed, exit_status={exit_status:?}");
|
||||||
child.kill().ok();
|
child.kill().ok();
|
||||||
child.wait().ok();
|
child.wait().ok();
|
||||||
|
|
||||||
|
let output = captured_lines.join("\n");
|
||||||
|
let extra = match exit_status {
|
||||||
|
Some(status) => format!("\nexit status: {status}"),
|
||||||
|
None => String::new(),
|
||||||
|
};
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"timeout waiting for irtt server to start\nstdout:\n{}",
|
"irtt server failed to start\noutput:\n{output}{extra}"
|
||||||
stdout_lines.join("\n")
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Err(mpsc::RecvTimeoutError::Disconnected) => {
|
|
||||||
child.kill().ok();
|
|
||||||
child.wait().ok();
|
|
||||||
return Err(format!(
|
|
||||||
"irtt server exited unexpectedly\nstdout:\n{}",
|
|
||||||
stdout_lines.join("\n")
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drop(rx);
|
debug_eprintln!("[real_irtt] ready on {addr}");
|
||||||
|
|
||||||
Ok(Self { addr, child })
|
drop(out_rx);
|
||||||
|
drop(err_rx);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
addr,
|
||||||
|
child,
|
||||||
|
keepalive_ms,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addr(&self) -> SocketAddr {
|
pub fn addr(&self) -> SocketAddr {
|
||||||
@@ -109,7 +159,47 @@ impl RealIrtServer {
|
|||||||
|
|
||||||
impl Drop for RealIrtServer {
|
impl Drop for RealIrtServer {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
if let Some(ms) = self.keepalive_ms {
|
||||||
|
debug_eprintln!(
|
||||||
|
"[real_irtt] keepalive: sleeping {ms}ms before kill (pid={})",
|
||||||
|
self.child.id()
|
||||||
|
);
|
||||||
|
thread::sleep(Duration::from_millis(ms));
|
||||||
|
}
|
||||||
|
debug_eprintln!("[real_irtt] killing child pid={}", self.child.id());
|
||||||
self.child.kill().ok();
|
self.child.kill().ok();
|
||||||
self.child.wait().ok();
|
self.child.wait().ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn select_line(
|
||||||
|
out_rx: &mpsc::Receiver<(&str, String)>,
|
||||||
|
err_rx: &mpsc::Receiver<(&str, String)>,
|
||||||
|
timeout: Duration,
|
||||||
|
) -> Option<(&'static str, String)> {
|
||||||
|
if let Ok((_, line)) = out_rx.recv_timeout(timeout) {
|
||||||
|
return Some(("stdout", line));
|
||||||
|
}
|
||||||
|
if let Ok((_, line)) = err_rx.recv_timeout(Duration::ZERO) {
|
||||||
|
return Some(("stderr", line));
|
||||||
|
}
|
||||||
|
if timeout > Duration::ZERO {
|
||||||
|
if let Ok((_, line)) = out_rx.recv_timeout(Duration::from_millis(100)) {
|
||||||
|
return Some(("stdout", line));
|
||||||
|
}
|
||||||
|
if let Ok((_, line)) = err_rx.recv_timeout(Duration::from_millis(100)) {
|
||||||
|
return Some(("stderr", line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! debug_eprintln {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
if std::env::var("IRTT_TEST_BACKEND_DEBUG").as_deref() == Ok("1") {
|
||||||
|
eprintln!($($arg)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
use debug_eprintln;
|
||||||
Reference in new issue
Block a user