1use crate::internal::{CapturedOutput, FailureCause, TestResult};
2use crate::stats::Summary;
3use desert_rust::BinaryCodec;
4use interprocess::local_socket::{
5 GenericFilePath, GenericNamespaced, Name, NameType, ToFsName, ToNsName,
6};
7use std::io::{self, Read, Write};
8use std::time::Duration;
9
10pub const FRAME_LEN_BYTES: usize = 4;
14
15pub fn write_frame<W: Write>(writer: &mut W, payload: &[u8]) -> io::Result<()> {
18 let len = u32::try_from(payload.len()).map_err(|_| {
19 io::Error::new(
20 io::ErrorKind::InvalidData,
21 "IPC payload size exceeds u32::MAX",
22 )
23 })?;
24 writer.write_all(&len.to_le_bytes())?;
25 writer.write_all(payload)
26}
27
28pub fn read_frame<R: Read>(reader: &mut R) -> io::Result<Vec<u8>> {
30 let mut len_bytes = [0u8; FRAME_LEN_BYTES];
31 reader.read_exact(&mut len_bytes)?;
32 let len = u32::from_le_bytes(len_bytes) as usize;
33 let mut payload = vec![0; len];
34 reader.read_exact(&mut payload)?;
35 Ok(payload)
36}
37
38#[cfg(feature = "tokio")]
39pub async fn write_frame_async<W>(writer: &mut W, payload: &[u8]) -> io::Result<()>
40where
41 W: tokio::io::AsyncWriteExt + Unpin,
42{
43 let len = u32::try_from(payload.len()).map_err(|_| {
44 io::Error::new(
45 io::ErrorKind::InvalidData,
46 "IPC payload size exceeds u32::MAX",
47 )
48 })?;
49 writer.write_all(&len.to_le_bytes()).await?;
50 writer.write_all(payload).await
51}
52
53#[cfg(feature = "tokio")]
54pub async fn read_frame_async<R>(reader: &mut R) -> io::Result<Vec<u8>>
55where
56 R: tokio::io::AsyncReadExt + Unpin,
57{
58 let mut len_bytes = [0u8; FRAME_LEN_BYTES];
59 reader.read_exact(&mut len_bytes).await?;
60 let len = u32::from_le_bytes(len_bytes) as usize;
61 let mut payload = vec![0; len];
62 reader.read_exact(&mut payload).await?;
63 Ok(payload)
64}
65
66#[derive(Debug, BinaryCodec)]
68pub enum IpcCommand {
69 RunTest {
70 name: String,
71 crate_name: String,
72 module_path: String,
73 },
74 ProvideCloneable { dep_id: String, wire_bytes: Vec<u8> },
81 ProvideHostedDescriptor { dep_id: String, wire_bytes: Vec<u8> },
87 HostedRpcReply {
94 request_id: u64,
95 body: HostedRpcReplyBody,
96 },
97 Shutdown,
102}
103
104pub(crate) fn test_loop_should_exit(
114 is_ipc_worker: bool,
115 execution_done: bool,
116 owned_worker_exhausted: bool,
117) -> bool {
118 owned_worker_exhausted || (!is_ipc_worker && execution_done)
119}
120
121#[derive(Debug, BinaryCodec)]
125pub enum HostedRpcReplyBody {
126 Ok { result_bytes: Vec<u8> },
127 Err { message: String },
128}
129
130#[derive(Debug, BinaryCodec)]
131pub enum SerializableTestResult {
132 Passed {
133 exec_time: Duration,
134 },
135 Benchmarked {
136 exec_time: Duration,
137 ns_iter_summ: Summary,
138 mb_s: usize,
139 },
140 Failed {
141 exec_time: Duration,
142 rendered_failure_cause: String,
143 },
144 Ignored,
145}
146
147impl SerializableTestResult {
148 pub fn into_test_result(
149 self,
150 stdout: Vec<CapturedOutput>,
151 stderr: Vec<CapturedOutput>,
152 ) -> TestResult {
153 let mut captured = [stdout, stderr].concat();
154 captured.sort();
155
156 let mut result: TestResult = self.into();
157 result.set_captured_output(captured);
158 result
159 }
160}
161
162impl From<&TestResult> for SerializableTestResult {
163 fn from(result: &TestResult) -> Self {
164 match &result {
165 TestResult::Passed { exec_time, .. } => SerializableTestResult::Passed {
166 exec_time: *exec_time,
167 },
168 TestResult::Benchmarked {
169 exec_time,
170 ns_iter_summ,
171 mb_s,
172 ..
173 } => SerializableTestResult::Benchmarked {
174 exec_time: *exec_time,
175 ns_iter_summ: *ns_iter_summ,
176 mb_s: *mb_s,
177 },
178 TestResult::Failed {
179 exec_time, cause, ..
180 } => SerializableTestResult::Failed {
181 exec_time: *exec_time,
182 rendered_failure_cause: cause.render(),
183 },
184 TestResult::Ignored { .. } => SerializableTestResult::Ignored,
185 }
186 }
187}
188
189impl From<SerializableTestResult> for TestResult {
190 fn from(result: SerializableTestResult) -> Self {
191 match result {
192 SerializableTestResult::Passed { exec_time } => TestResult::passed(exec_time),
193 SerializableTestResult::Failed {
194 exec_time,
195 rendered_failure_cause,
196 } => TestResult::failed(
197 exec_time,
198 FailureCause::HarnessError(rendered_failure_cause),
199 ),
200 SerializableTestResult::Ignored => TestResult::ignored(),
201 SerializableTestResult::Benchmarked {
202 exec_time,
203 ns_iter_summ,
204 mb_s,
205 } => TestResult::benchmarked(exec_time, ns_iter_summ, mb_s),
206 }
207 }
208}
209
210#[derive(Debug, BinaryCodec)]
213pub enum IpcResponse {
214 TestFinished {
215 result: SerializableTestResult,
216 finish_marker: String,
217 worker_exhausted: bool,
222 },
223 CloneableAccepted { dep_id: String },
226 HostedDescriptorAccepted { dep_id: String },
229 HostedRpcCall {
237 request_id: u64,
238 dep_id: String,
239 method_idx: u32,
240 args_bytes: Vec<u8>,
241 },
242}
243
244pub fn ipc_name<'s>(name: String) -> Name<'s> {
245 if GenericNamespaced::is_supported() {
246 name.to_ns_name::<GenericNamespaced>()
247 .expect("Invalid local socket name")
248 } else {
249 name.to_fs_name::<GenericFilePath>()
250 .expect("Invalid local socket name")
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257 use std::io::Cursor;
258
259 #[test]
260 fn write_then_read_round_trip_empty() {
261 let mut buf: Vec<u8> = Vec::new();
262 write_frame(&mut buf, &[]).expect("write");
263 let mut cursor = Cursor::new(&buf);
264 let payload = read_frame(&mut cursor).expect("read");
265 assert!(payload.is_empty());
266 }
267
268 #[test]
269 fn write_then_read_round_trip_small() {
270 let mut buf: Vec<u8> = Vec::new();
271 let data = b"hello, world";
272 write_frame(&mut buf, data).expect("write");
273 let mut cursor = Cursor::new(&buf);
274 let payload = read_frame(&mut cursor).expect("read");
275 assert_eq!(payload, data);
276 }
277
278 #[test]
279 fn write_then_read_round_trip_large_payload_exceeds_u16() {
280 let mut data = vec![0u8; 200 * 1024];
282 for (i, b) in data.iter_mut().enumerate() {
283 *b = (i % 251) as u8;
284 }
285 let mut buf: Vec<u8> = Vec::new();
286 write_frame(&mut buf, &data).expect("write");
287 assert_eq!(buf.len(), FRAME_LEN_BYTES + data.len());
288 let mut cursor = Cursor::new(&buf);
289 let payload = read_frame(&mut cursor).expect("read");
290 assert_eq!(payload.len(), data.len());
291 assert_eq!(payload, data);
292 }
293
294 #[test]
295 fn read_frame_propagates_eof() {
296 let buf: Vec<u8> = Vec::new();
297 let mut cursor = Cursor::new(&buf);
298 let err = read_frame(&mut cursor).expect_err("must fail on empty");
299 assert_eq!(err.kind(), std::io::ErrorKind::UnexpectedEof);
300 }
301
302 #[test]
303 fn worker_lifetime_and_parent_scheduler_capacity_are_distinct() {
304 assert!(test_loop_should_exit(false, true, false));
305 assert!(!test_loop_should_exit(true, true, false));
306 assert!(!test_loop_should_exit(true, false, false));
307 assert!(test_loop_should_exit(false, false, true));
308 }
309
310 #[cfg(feature = "tokio")]
311 #[test]
312 fn async_round_trip_large_payload_exceeds_u16() {
313 let runtime = tokio::runtime::Runtime::new().unwrap();
314 runtime.block_on(async {
315 let mut data = vec![0u8; 200 * 1024];
316 for (i, b) in data.iter_mut().enumerate() {
317 *b = (i % 251) as u8;
318 }
319 let mut buf: Vec<u8> = Vec::new();
320 write_frame_async(&mut buf, &data).await.expect("write");
321 assert_eq!(buf.len(), FRAME_LEN_BYTES + data.len());
322 let mut slice: &[u8] = &buf;
323 let payload = read_frame_async(&mut slice).await.expect("read");
324 assert_eq!(payload, data);
325 });
326 }
327}