From 4d0d1992aa33e839fa1b3d500b314723b74d2ede Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 13 Dec 2024 09:53:21 +0100 Subject: [PATCH] WIP Implement more tests for FDA check --- talpid-core/Cargo.toml | 4 ++ talpid-core/src/split_tunnel/macos/process.rs | 72 +++++++++++++++++-- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index 7ec3fd20f288..5f28c91cd04b 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -103,3 +103,7 @@ features = [ [build-dependencies] tonic-build = { workspace = true, default-features = false, features = ["transport", "prost"] } + + +[dev-dependencies] +tokio = { workspace = true, features = [ "io-util" ] } diff --git a/talpid-core/src/split_tunnel/macos/process.rs b/talpid-core/src/split_tunnel/macos/process.rs index d5d00451449b..2f50dedb8e52 100644 --- a/talpid-core/src/split_tunnel/macos/process.rs +++ b/talpid-core/src/split_tunnel/macos/process.rs @@ -564,12 +564,55 @@ fn check_os_version_support_inner(version: MacosVersion) -> Result<(), Error> { #[cfg(test)] mod test { - use super::{ - check_os_version_support_inner, parse_logger_status, NeedFda, EARLY_FAIL_TIMEOUT, - MIN_OS_VERSION, - }; - use std::{process::ExitStatus, time::Duration}; + use super::*; + + use std::{pin::Pin, process::ExitStatus, time::Duration}; + use talpid_platform_metadata::MacosVersion; + use tokio::io::{AsyncWriteExt, ReadHalf, SimplexStream, WriteHalf}; + // use tokio::io::SimplexStream; + + /// A mock-version of stdout. [SimplexStream] implements [AsyncRead], so it can be used to test + /// [parse_logger_status]. + struct MockStdout { + read_half: ReadHalf, + lag: Pin>, + } + + impl MockStdout { + // "print" to "stdout" after `duration`. + async fn delay(until: Duration) -> Self { + let (stdout, mut write_half) = tokio::io::simplex(64); + write_half + .write_all(b"this will arrive.. Eventually") + .await + .unwrap(); + + let lag = Box::pin(tokio::time::sleep(until)); + + Self { + read_half: stdout, + lag, + } + } + } + + impl AsyncRead for MockStdout { + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> std::task::Poll> { + use std::task::Poll; + match self.lag.as_mut().poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(_) => { + let stdout = std::pin::pin!(&mut self.read_half); + stdout.poll_read(cx, buf) + } + } + } + } #[test] fn test_min_os_version() { @@ -643,4 +686,23 @@ mod test { "expected 'NeedFda::No' on immediate exit", ); } + + /// Check that [parse_logger_status] doesn't get stuck because nothing is ever output + /// to stdout. + #[tokio::test] + async fn test_parse_logger_status_hogged() { + let stdout = MockStdout::delay(Duration::from_secs(999)).await; + let need_fda = parse_logger_status( + async { Ok(ExitStatus::default()) }, + stdout, + b"ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED\n".as_slice(), + ) + .await; + + assert_eq!( + need_fda, + NeedFda::Yes, + "expected 'NeedFda::Yes' when ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED was present" + ); + } }