1use crate::util::diff_paths;
8pub use crate::{DirHandle, DirHandleExt, Fs, OpenOptions, Path, Shutdown};
9pub use alloc::{
10 collections::BTreeSet,
11 format,
12 string::{String, ToString},
13 vec,
14 vec::Vec,
15};
16pub use core::future::Future;
17pub use futures::StreamExt;
18pub use mfio::backend::IoBackendExt;
19pub use mfio::traits::{IoRead, IoWrite};
20pub use once_cell::sync::Lazy;
21pub use tempdir::TempDir;
22
23#[cfg(feature = "std")]
24pub use crate::{Tcp, TcpListenerHandle, TcpStreamHandle};
25#[cfg(feature = "std")]
26pub use std::fs;
27
28const FILES: &[(&str, &str)] = &[
29 ("Cargo.toml", include_str!("../Cargo.toml")),
30 ("src/lib.rs", include_str!("lib.rs")),
31 ("src/util.rs", include_str!("util.rs")),
32 ("src/native/mod.rs", include_str!("native/mod.rs")),
33 (
34 "src/native/impls/mod.rs",
35 include_str!("native/impls/mod.rs"),
36 ),
37 (
38 "src/native/impls/thread.rs",
39 include_str!("native/impls/thread.rs"),
40 ),
41 (
42 "src/native/impls/unix_extra.rs",
43 include_str!("native/impls/unix_extra.rs"),
44 ),
45 ("p1/p2/p3/a.txt", "TEST TEST TEST"),
46];
47
48const DIRECTORIES: &[&str] = &[
49 "src/native/impls/io_uring",
50 "src/native/impls/mio",
51 "p1/p2/p3/p4/p5/p6",
52];
53
54pub static CTX: Lazy<TestCtx> = Lazy::new(TestCtx::new);
55
56#[cfg(not(miri))]
57const fn hash(mut x: u64) -> u64 {
58 x = (x ^ (x >> 30)).wrapping_mul(0xbf58476d1ce4e5b9u64);
59 x = (x ^ (x >> 27)).wrapping_mul(0x94d049bb133111ebu64);
60 x ^ (x >> 31)
61}
62
63pub struct TestCtx {
64 files: Vec<(String, Vec<u8>)>,
65 dirs: Vec<String>,
66}
67
68impl Default for TestCtx {
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74impl TestCtx {
75 pub fn new() -> Self {
76 let mut files = vec![];
77
78 for (p, c) in FILES {
79 files.push((p.to_string(), c.to_string().into_bytes()));
80 }
81
82 let mut dirs = vec![];
83
84 for d in DIRECTORIES {
85 dirs.push(d.to_string());
86 }
87
88 #[cfg(not(miri))]
91 for f in 0..4 {
92 files.push((
93 format!("large/{f}"),
94 (0..0x4000000)
95 .map(|v| hash((v as u64) << (f * 8)) as u8)
96 .collect::<Vec<_>>(),
97 ))
98 }
99
100 Self { files, dirs }
101 }
102
103 pub fn list(&self, path: &str) -> BTreeSet<String> {
104 let path = path.trim_start_matches('.').trim_start_matches('/');
105 self.files
106 .iter()
107 .map(|v| v.0.as_str())
108 .filter_map(move |v| v.strip_prefix(path))
109 .map(|v| v.trim_start_matches('/'))
110 .map(|v| v.split_once('/').map(|(a, _)| a).unwrap_or(v))
111 .chain(
112 self.dirs
113 .iter()
114 .map(|v| v.as_str())
115 .filter_map(move |v| v.strip_prefix(path))
116 .map(|v| v.trim_start_matches('/'))
117 .map(|v| v.split_once('/').map(|(a, _)| a).unwrap_or(v)),
118 )
119 .filter(|s| !s.is_empty())
120 .map(|s| s.to_string())
121 .collect()
122 }
123
124 pub fn dirs(&self) -> &[String] {
125 &self.dirs
126 }
127
128 pub fn files(&self) -> &[(String, Vec<u8>)] {
129 &self.files
130 }
131
132 pub fn all_dirs(&self) -> BTreeSet<String> {
133 let mut dirs = BTreeSet::new();
134
135 for dir in &self.dirs {
136 let mut d = ".".to_string();
137 dirs.insert(d.clone());
138 for p in dir.split('/') {
139 d.push('/');
140 d.push_str(p);
141 dirs.insert(d.clone());
142 }
143 }
144
145 for dir in self
146 .files
147 .iter()
148 .filter_map(|(a, _)| a.rsplit_once('/').map(|(a, _)| a))
149 {
150 let mut d = ".".to_string();
151 dirs.insert(d.clone());
152 for p in dir.split('/') {
153 d.push('/');
154 d.push_str(p);
155 dirs.insert(d.clone());
156 }
157 }
158
159 dirs
160 }
161
162 #[cfg(feature = "std")]
163 pub fn build_in_path(&self, path: &Path) {
164 for d in &self.dirs {
165 let _ = fs::create_dir_all(path.join(d));
166 }
167
168 for (p, data) in &self.files {
169 if let Some((d, _)) = p.rsplit_once('/') {
170 let _ = fs::create_dir_all(path.join(d));
171 }
172 fs::write(path.join(p), data).unwrap();
173 }
174 }
175
176 pub async fn build_in_fs(&self, fs: &impl Fs) {
177 let cdir = fs.current_dir();
178
179 for d in &self.dirs {
180 #[cfg(feature = "std")]
181 println!("Dir: {d:?}");
182 let _ = cdir.create_dir_all(d).await;
183 }
184
185 for (p, data) in &self.files {
186 #[cfg(feature = "std")]
187 println!("File: {p:?}");
188 if let Some((d, _)) = p.rsplit_once('/') {
189 let _ = cdir.create_dir_all(d).await;
190 }
191 let file = cdir
192 .open_file(p, OpenOptions::new().create_new(true).write(true))
193 .await
194 .unwrap();
195 file.write_all(0, &data[..]).await.unwrap();
196 }
197 }
198}
199
200#[cfg(feature = "std")]
201pub mod net {
202 use super::*;
203 use async_semaphore::Semaphore;
204 use core::pin::pin;
205
206 static TCP_SEM: Semaphore = Semaphore::new(16);
208
209 pub struct NetTestRun<'a, T> {
210 ctx: &'static TestCtx,
211 rt: &'a T,
212 }
213
214 impl<'a, T: Tcp> NetTestRun<'a, T> {
215 pub fn new(rt: &'a T) -> Self {
216 Self { ctx: &*CTX, rt }
217 }
218
219 pub async fn tcp_connect(&self) {
220 use std::net::TcpListener;
221
222 let _sem = TCP_SEM.acquire().await;
223
224 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
225 let addr = listener.local_addr().unwrap();
226
227 let jh = std::thread::spawn(move || {
228 let _ = listener.accept().unwrap();
229 });
230
231 self.rt.connect(addr).await.unwrap();
232
233 jh.join().unwrap();
234 }
235
236 pub async fn tcp_listen(&self) {
237 use std::net::TcpStream;
238
239 let _sem = TCP_SEM.acquire().await;
240
241 let listener = self.rt.bind("127.0.0.1:0").await.unwrap();
242 let addr = listener.local_addr().unwrap();
243
244 let jh = std::thread::spawn(move || {
245 let _ = TcpStream::connect(addr).unwrap();
246 });
247
248 let mut listener = pin!(listener);
249
250 let _ = listener.next().await.unwrap();
251
252 jh.join().unwrap();
253 }
254
255 pub fn tcp_receive(&self) -> impl Iterator<Item = impl Future<Output = ()> + '_> + '_ {
256 use mfio::io::NoPos;
257 use std::net::TcpListener;
258
259 self.ctx.files.iter().map(move |(name, data)| async move {
260 let _sem = TCP_SEM.acquire().await;
261
262 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
263 let addr = listener.local_addr().unwrap();
264
265 let (tx, rx) = flume::bounded(1);
266
267 let jh = std::thread::spawn(move || {
268 use std::io::Write;
269 let (mut sock, _) = listener.accept().unwrap();
270 sock.write_all(data).unwrap();
271 let _ = sock.shutdown(std::net::Shutdown::Both);
272 let _ = tx.send(());
273 });
274
275 let conn = self.rt.connect(addr).await.unwrap();
276
277 let mut out = vec![];
278 conn.read_to_end(NoPos::new(), &mut out).await.unwrap();
279 assert!(
280 &out[..] == data,
281 "{name} does not match ({} vs {})",
282 out.len(),
283 data.len()
284 );
285
286 let _ = rx.recv_async().await;
287 jh.join().unwrap();
288 })
289 }
290
291 pub fn tcp_send(&self) -> impl Iterator<Item = impl Future<Output = ()> + '_> + '_ {
292 use mfio::io::NoPos;
293 use std::net::TcpListener;
294
295 self.ctx.files.iter().map(move |(name, data)| async move {
296 let _sem = TCP_SEM.acquire().await;
297
298 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
299 let addr = listener.local_addr().unwrap();
300
301 let (tx, rx) = flume::bounded(1);
302
303 let jh = std::thread::spawn(move || {
304 use std::io::Read;
305 let (mut sock, _) = listener.accept().unwrap();
306 let mut out = vec![];
307 sock.read_to_end(&mut out).unwrap();
308 let _ = tx.send(());
309 out
310 });
311
312 {
313 let conn = self.rt.connect(addr).await.unwrap();
314 conn.write_all(NoPos::new(), &data[..]).await.unwrap();
315 core::mem::drop(conn);
316 }
317
318 let _ = rx.recv_async().await;
319
320 let ret = jh.join().unwrap();
321 assert!(
322 &ret == data,
323 "{name} does not match ({} vs {})",
324 ret.len(),
325 data.len()
326 );
327 })
328 }
329
330 pub fn tcp_echo_client(&self) -> impl Iterator<Item = impl Future<Output = ()> + '_> + '_ {
331 use mfio::io::NoPos;
332 use std::net::TcpListener;
333
334 self.ctx.files.iter().map(move |(name, data)| async move {
335 let _sem = TCP_SEM.acquire().await;
336
337 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
338 let addr = listener.local_addr().unwrap();
339
340 let (tx, rx) = flume::bounded(1);
341
342 let jh = std::thread::spawn(move || {
343 let (sock, _) = listener.accept().unwrap();
344 log::trace!("Echo STD server start");
345 std::io::copy(&mut &sock, &mut &sock).unwrap();
346 log::trace!("Echo STD server end");
347 let _ = tx.send(());
348 });
349
350 let ret = {
351 let conn = self.rt.connect(addr).await.unwrap();
352
353 let write = async {
354 conn.write_all(NoPos::new(), &data[..]).await.unwrap();
355 log::trace!("Written");
356 conn.shutdown(Shutdown::Write).unwrap();
357 };
358
359 let read = async {
360 let mut ret = vec![];
361 conn.read_to_end(NoPos::new(), &mut ret).await.unwrap();
362 ret
363 };
364
365 futures::join!(write, read).1
366 };
367
368 let _ = rx.recv_async().await;
369 jh.join().unwrap();
370
371 assert!(
372 &ret == data,
373 "{name} does not match ({} vs {})",
374 ret.len(),
375 data.len()
376 );
377 })
378 }
379
380 pub fn tcp_echo_server(&self) -> impl Iterator<Item = impl Future<Output = ()> + '_> + '_ {
381 use core::mem::MaybeUninit;
382 use flume::SendError;
383 use mfio::io::{Read, StreamIoExt, VecPacket};
384 use std::net::TcpStream;
385
386 self.ctx.files.iter().map(move |(name, data)| async move {
387 let _sem = TCP_SEM.acquire().await;
388
389 let listener = self.rt.bind("127.0.0.1:0").await.unwrap();
390 let addr = listener.local_addr().unwrap();
391
392 let (tx, rx) = flume::bounded(1);
393
394 let jh = std::thread::spawn(move || {
395 use std::io::{Read, Write};
396 let sock = TcpStream::connect(addr).unwrap();
397 let sock = std::sync::Arc::new(sock);
398
399 let jh = std::thread::spawn({
400 let sock = sock.clone();
401 move || {
402 (&mut &*sock).write_all(data).unwrap();
403 sock.shutdown(Shutdown::Write.into()).unwrap();
404 }
405 });
406
407 let mut out = vec![];
408 let _ = (&mut &*sock).read_to_end(&mut out);
409
410 jh.join().unwrap();
411 let _ = tx.send(());
412
413 out
414 });
415
416 let mut listener = pin!(listener);
417
418 let (sock, _) = listener.next().await.unwrap();
419
420 let (rtx, rrx) = flume::bounded(4);
422 let (mtx, mrx) = flume::bounded(4);
423
424 for i in 0..rtx.capacity().unwrap() {
425 let _ = rtx.send_async((i, vec![MaybeUninit::uninit(); 64])).await;
426 }
427
428 let read = {
429 let sock = &sock;
430 async move {
431 rrx.stream()
432 .then(|(i, mut v)| async move {
433 v.resize(v.len() * 2, MaybeUninit::uninit());
434 let p = VecPacket::from(v);
435 let p = sock.stream_io(p).await;
436 let len = p.simple_contiguous_slice().unwrap().len();
437 let mut v = p.take();
438 let v = unsafe {
439 v.set_len(len);
440 core::mem::transmute::<Vec<MaybeUninit<u8>>, Vec<u8>>(v)
441 };
442 if v.is_empty() {
443 log::trace!("Empty @ {i}");
444 Err(SendError((i, v)))
445 } else {
446 log::trace!("Forward {i}");
447 Ok((i, v))
448 }
449 })
450 .take_while(|v| core::future::ready(v.is_ok()))
451 .forward(mtx.sink())
452 .await
453 }
454 };
455
456 let write = async {
457 let sock = &sock;
458 let rtx = &rtx;
459 mrx.stream()
460 .for_each(|(i, v)| async move {
461 let p = VecPacket::<Read>::from(v);
462 let p = sock.stream_io(p).await;
463 let mut v = p.take();
464 let v = unsafe {
465 v.set_len(v.capacity());
466 core::mem::transmute::<Vec<u8>, Vec<MaybeUninit<u8>>>(v)
467 };
468 log::trace!("Forward back {i}");
469 let _ = rtx.send_async((i, v)).await;
472 })
473 .await
474 };
475
476 let _ = futures::join!(read, write);
477 log::trace!("Echo server");
478 core::mem::drop(sock);
479
480 let _ = rx.recv_async().await;
481 let ret = jh.join().unwrap();
482
483 assert!(
484 &ret == data,
485 "{name} does not match ({} vs {})",
486 ret.len(),
487 data.len()
488 );
489 })
490 }
491 }
492}
493
494pub struct TestRun<'a, T, D> {
495 ctx: &'a TestCtx,
496 rt: &'a T,
497 _drop_guard: D,
498}
499
500#[cfg(feature = "std")]
501impl<'a, T: Fs> TestRun<'a, T, TempDir> {
502 pub fn new(rt: &'a T, dir: TempDir) -> Self {
503 CTX.build_in_path(dir.path());
504 Self {
505 rt,
506 _drop_guard: dir,
507 ctx: &CTX,
508 }
509 }
510}
511
512impl<'a, T: Fs, D> TestRun<'a, T, D> {
513 pub async fn built_by_rt(rt: &'a T, drop_guard: D) -> TestRun<'a, T, D> {
514 CTX.build_in_fs(rt).await;
515 Self {
516 rt,
517 _drop_guard: drop_guard,
518 ctx: &CTX,
519 }
520 }
521
522 pub fn assume_built(rt: &'a T, drop_guard: D) -> TestRun<'a, T, D> {
523 Self {
524 rt,
525 _drop_guard: drop_guard,
526 ctx: &CTX,
527 }
528 }
529
530 pub fn files_equal(&self) -> impl Iterator<Item = impl Future<Output = ()> + '_> + '_ {
531 self.ctx.files.iter().map(move |(p, data)| async move {
532 let cur_dir = self.rt.current_dir().path().await.unwrap();
533 let path = &cur_dir.join(p);
534 let fh = self
535 .rt
536 .open(path, OpenOptions::new().read(true))
537 .await
538 .unwrap();
539 let mut buf = vec![];
540 fh.read_to_end(0, &mut buf).await.unwrap();
541 assert!(&buf == data, "File {p} does not match!");
542 })
543 }
544
545 pub fn files_equal_rel(&self) -> impl Iterator<Item = impl Future<Output = ()> + '_> + '_ {
546 self.ctx.files.iter().map(move |(p, data)| async move {
547 let cur_dir = self.rt.current_dir().path().await.unwrap();
548 let path = &cur_dir.join(p);
549 let dh = self
550 .rt
551 .current_dir()
552 .open_dir(path.parent().unwrap())
553 .await
554 .unwrap();
555 let fh = dh
556 .open_file(path.file_name().unwrap(), OpenOptions::new().read(true))
557 .await
558 .unwrap();
559 let mut buf = vec![];
560 fh.read_to_end(0, &mut buf).await.unwrap();
561 if &buf != data && buf.len() == data.len() {
562 for (i, (a, b)) in buf.iter().zip(data.iter()).enumerate() {
563 if a != b {
564 panic!(
565 "File {p} does not match at {i}\n{:?}\n{:?}",
566 &buf[i..(i + 20)],
567 &data[i..(i + 20)]
568 );
569 }
570 }
571 }
572 })
574 }
575
576 pub fn writes_equal<'b>(
577 &'b self,
578 tdir: &'b Path,
579 ) -> impl Iterator<Item = impl Future<Output = ()> + 'b> + 'b {
580 self.ctx.files.iter().map(move |(p, data)| async move {
581 let tdir = self.rt.current_dir().path().await.unwrap().join(tdir);
582 let path = &tdir.join(p);
583 self.rt
584 .current_dir()
585 .create_dir_all(path.parent().unwrap())
586 .await
587 .unwrap();
588
589 let fh = self
590 .rt
591 .open(path, OpenOptions::new().create(true).write(true))
592 .await
593 .unwrap();
594
595 fh.write_all(0, &data[..]).await.unwrap();
596
597 core::mem::drop(fh);
598
599 let fh = self
600 .rt
601 .open(path, OpenOptions::new().read(true))
602 .await
603 .unwrap();
604
605 let mut buf = vec![];
606 fh.read_to_end(0, &mut buf).await.unwrap();
607
608 assert_eq!(core::str::from_utf8(&buf), core::str::from_utf8(data));
609 assert!(&buf == data, "File {p} does not match!");
610 })
611 }
612
613 pub fn writes_equal_rel<'b>(
614 &'b self,
615 tdir: &'b Path,
616 ) -> impl Iterator<Item = impl Future<Output = ()> + 'b> + 'b {
617 self.ctx.files.iter().map(move |(p, data)| async move {
618 let tdir = self.rt.current_dir().path().await.unwrap().join(tdir);
619 let path = &tdir.join(p);
620 self.rt
621 .current_dir()
622 .create_dir_all(path.parent().unwrap())
623 .await
624 .unwrap();
625
626 let dh = self
627 .rt
628 .current_dir()
629 .open_dir(path.parent().unwrap())
630 .await
631 .unwrap();
632
633 let filename = path.file_name().unwrap();
634
635 let fh = dh
636 .open_file(filename, OpenOptions::new().create(true).write(true))
637 .await
638 .unwrap();
639
640 fh.write_all(0, &data[..]).await.unwrap();
641
642 core::mem::drop(fh);
643
644 let fh = dh
645 .open_file(filename, OpenOptions::new().read(true))
646 .await
647 .unwrap();
648
649 let mut buf = vec![];
650 fh.read_to_end(0, &mut buf).await.unwrap();
651
652 assert!(&buf == data, "File {p} does not match!");
653 })
654 }
655
656 pub fn dirs_equal(&self) -> impl Iterator<Item = impl Future<Output = ()> + '_> + '_ {
657 let all_dirs = self.ctx.all_dirs();
658 log::error!("{all_dirs:?}");
659 all_dirs.into_iter().map(move |d| async move {
660 let cur_path = self.rt.current_dir().path().await.unwrap();
661 log::error!("Join with {d}");
662 let path = &cur_path.join(&d);
663 log::error!("Open dir: {path:?}");
664 let dh = self
665 .rt
666 .current_dir()
667 .open_dir(path)
668 .await
669 .unwrap_or_else(|_| panic!("{path:?}"));
670 log::error!("Opened dir: {path:?}");
671 let dir = dh
672 .read_dir()
673 .await
674 .unwrap()
675 .map(|v| v.unwrap().name)
676 .collect::<BTreeSet<String>>()
677 .await;
678 assert_eq!(dir, self.ctx.list(&d), "{d}");
679 })
680 }
681
682 pub fn walk_dirs(&self) -> impl Iterator<Item = impl Future<Output = ()> + '_> + '_ {
683 let curdir = self.rt.current_dir();
684 self.ctx.all_dirs().into_iter().flat_map(move |d1| {
685 self.ctx.all_dirs().into_iter().map(move |d2| {
686 let d1 = d1.clone();
687 async move {
688 let cur_path = curdir.path().await.unwrap();
689 let path1 = &cur_path.join(&d1);
690 let path2 = &cur_path.join(&d2);
691
692 let relpath1 = diff_paths(path1, path2).unwrap();
693 let relpath2 = diff_paths(&d1, &d2).unwrap();
694 assert_eq!(&relpath1, &relpath2);
695
696 let dh1 = curdir.open_dir(path1).await.unwrap();
697 let dh2 = curdir.open_dir(path2).await.unwrap();
698
699 let relpath3 =
700 diff_paths(dh1.path().await.unwrap(), dh2.path().await.unwrap()).unwrap();
701
702 assert_eq!(&relpath1, &relpath3);
703 }
704 })
705 })
706 }
707}
708
709async fn seq(i: impl Iterator<Item = impl Future<Output = ()> + '_> + '_) {
710 for i in i {
711 i.await;
712 }
713}
714
715async fn con(i: impl Iterator<Item = impl Future<Output = ()> + '_> + '_) {
716 let unordered = i.collect::<futures::stream::FuturesUnordered<_>>();
717 unordered.count().await;
718}
719
720pub mod fs_tests {
721 use super::*;
722
723 pub async fn all_tests_seq(run: TestRun<'_, impl Fs, impl Sized>) {
724 seq(run.files_equal()).await;
725 seq(run.files_equal_rel()).await;
726 seq(run.dirs_equal()).await;
727 seq(run.walk_dirs()).await;
728 let tdir = Path::new("mfio-testsuite-writes");
729 let tdir2 = Path::new("mfio-testsuite-writes2");
730 seq(run.writes_equal(tdir)).await;
731 seq(run.writes_equal_rel(tdir2)).await;
732 }
733
734 pub async fn all_tests_con(run: TestRun<'_, impl Fs, impl Sized>) {
735 futures::join! {
736 con(run.files_equal()),
737 con(run.files_equal_rel()),
738 con(run.dirs_equal()),
739 con(run.walk_dirs()),
740 };
741 let tdir = Path::new("mfio-testsuite-writes");
744 let tdir2 = Path::new("mfio-testsuite-writes2");
745 futures::join! {
746 seq(run.writes_equal(tdir)),
747 seq(run.writes_equal_rel(tdir2)),
748 };
749 }
750
751 pub async fn files_equal(run: TestRun<'_, impl Fs, impl Sized>) {
752 seq(run.files_equal()).await;
753 }
754
755 pub async fn files_equal_rel(run: TestRun<'_, impl Fs, impl Sized>) {
756 seq(run.files_equal_rel()).await;
757 }
758
759 pub async fn dirs_equal(run: TestRun<'_, impl Fs, impl Sized>) {
760 seq(run.dirs_equal()).await;
761 }
762
763 pub async fn walk_dirs(run: TestRun<'_, impl Fs, impl Sized>) {
764 seq(run.walk_dirs()).await;
765 }
766
767 pub async fn writes_equal(run: TestRun<'_, impl Fs, impl Sized>) {
768 let tdir = Path::new("mfio-testsuite-writes");
769 seq(run.writes_equal(tdir)).await;
770 }
771
772 pub async fn writes_equal_rel(run: TestRun<'_, impl Fs, impl Sized>) {
773 let tdir = Path::new("mfio-testsuite-writes");
774 seq(run.writes_equal_rel(tdir)).await;
775 }
776}
777
778#[macro_export]
813macro_rules! test_suite_base {
814 ($test_ident:ident, $fs_builder:expr, $($(#[cfg($meta:meta)])* $test:ident),*) => {
815 #[cfg(test)]
816 #[allow(clippy::redundant_closure_call)]
817 mod $test_ident {
818 use $crate::test_suite::*;
819
820 fn staticify<T>(val: &mut T) -> &'static mut T {
821 unsafe { core::mem::transmute(val) }
822 }
823
824 macro_rules! impl_test {
825 ($name:ident) => {
826 #[test]
827 fn $name() {
828 let builder: fn(&'static str, fn(TestRun<'static, _, _>) -> _) = $fs_builder;
829 builder("mfio-testsuite", fs_tests::$name);
830 }
831 }
832 }
833
834 $(
835 $(#[cfg($meta)])*
836 impl_test!($test);
837 )*
838 }
839 };
840}
841
842#[macro_export]
873macro_rules! test_suite {
874 ($test_ident:ident, $fs_builder:expr) => {
875 $crate::test_suite_base!(
876 $test_ident,
877 $fs_builder,
878 #[cfg(not(miri))]
879 all_tests_seq,
880 #[cfg(not(miri))]
881 all_tests_con,
882 files_equal,
883 files_equal_rel,
884 dirs_equal,
885 walk_dirs,
886 writes_equal,
887 writes_equal_rel
888 );
889 };
890}
891
892#[cfg(feature = "std")]
914#[macro_export]
915macro_rules! net_test_suite {
916 ($test_ident:ident, $fs_builder:expr) => {
917 #[cfg(test)]
918 #[allow(clippy::redundant_closure_call)]
919 mod $test_ident {
920 use net::*;
921 use $crate::test_suite::*;
922
923 async fn seq(i: impl Iterator<Item = impl Future<Output = ()> + '_> + '_) {
924 for i in i {
925 i.await;
926 }
927 }
928
929 async fn con(i: impl Iterator<Item = impl Future<Output = ()> + '_> + '_) {
930 let unordered = i.collect::<futures::stream::FuturesUnordered<_>>();
931 unordered.count().await;
932 }
933
934 fn staticify<T>(val: &mut T) -> &'static mut T {
935 unsafe { core::mem::transmute(val) }
936 }
937
938 #[cfg(not(miri))]
939 #[test]
940 fn tcp_connect() {
941 $fs_builder(|rt| async move {
942 let run = NetTestRun::new(rt);
943 run.tcp_connect().await;
944 });
945 }
946
947 #[cfg(not(miri))]
948 #[test]
949 fn tcp_listen() {
950 $fs_builder(|rt| async move {
951 let run = NetTestRun::new(rt);
952 run.tcp_listen().await;
953 });
954 }
955
956 #[cfg(not(miri))]
957 #[test]
958 fn tcp_send_seq() {
959 $fs_builder(|rt| async move {
960 let run = NetTestRun::new(rt);
961 seq(run.tcp_send()).await;
962 });
963 }
964
965 #[cfg(not(miri))]
966 #[test]
967 fn tcp_send_con() {
968 $fs_builder(|rt| async move {
969 let run = NetTestRun::new(rt);
970 con(run.tcp_send()).await;
971 });
972 }
973
974 #[cfg(not(miri))]
975 #[test]
976 fn tcp_receive_seq() {
977 $fs_builder(|rt| async move {
978 let run = NetTestRun::new(rt);
979 seq(run.tcp_receive()).await;
980 });
981 }
982
983 #[cfg(not(miri))]
984 #[test]
985 fn tcp_receive_con() {
986 $fs_builder(|rt| async move {
987 let run = NetTestRun::new(rt);
988 con(run.tcp_receive()).await;
989 });
990 }
991
992 #[cfg(not(miri))]
993 #[test]
994 fn tcp_echo_client_seq() {
995 $fs_builder(|rt| async move {
996 let run = NetTestRun::new(rt);
997 seq(run.tcp_echo_client()).await;
998 });
999 }
1000
1001 #[cfg(not(miri))]
1002 #[test]
1003 fn tcp_echo_client_con() {
1004 $fs_builder(|rt| async move {
1005 let run = NetTestRun::new(rt);
1006 con(run.tcp_echo_client()).await;
1007 });
1008 }
1009
1010 #[cfg(not(miri))]
1011 #[test]
1012 fn tcp_echo_server_seq() {
1013 $fs_builder(|rt| async move {
1014 let run = NetTestRun::new(rt);
1015 seq(run.tcp_echo_server()).await;
1016 });
1017 }
1018
1019 #[cfg(not(miri))]
1020 #[test]
1021 fn tcp_echo_server_con() {
1022 $fs_builder(|rt| async move {
1023 let run = NetTestRun::new(rt);
1024 con(run.tcp_echo_server()).await;
1025 });
1026 }
1027 }
1028 };
1029}