1 use futures::join;
2 use test_programs::p3::wasi::filesystem::types::{
3     Descriptor, DescriptorFlags, ErrorCode, OpenFlags, PathFlags,
4 };
5 use test_programs::p3::{wasi, wit_stream};
6 
7 struct Component;
8 
9 test_programs::p3::export!(Component);
10 
11 impl test_programs::p3::exports::wasi::cli::run::Guest for Component {
run() -> Result<(), ()>12     async fn run() -> Result<(), ()> {
13         let preopens = wasi::filesystem::preopens::get_directories();
14         let (dir, _) = &preopens[0];
15 
16         test_file_long_write(dir, "long_write.txt").await;
17         Ok(())
18     }
19 }
20 
main()21 fn main() {
22     unreachable!()
23 }
24 
test_file_long_write(dir: &Descriptor, filename: &str)25 async fn test_file_long_write(dir: &Descriptor, filename: &str) {
26     let mut content = Vec::new();
27     // 16 byte string, 4096 times, is 64k
28     for n in 0..4096 {
29         let chunk = format!("123456789 {n:05} ");
30         assert_eq!(chunk.as_str().as_bytes().len(), 16);
31         content.extend_from_slice(chunk.as_str().as_bytes());
32     }
33 
34     // Write to the file
35     let file = dir
36         .open_at(
37             PathFlags::empty(),
38             filename.to_string(),
39             OpenFlags::CREATE,
40             DescriptorFlags::WRITE,
41         )
42         .await
43         .expect("creating a file for writing");
44     let (mut tx, rx) = wit_stream::new();
45     join! {
46         async {
47             file.write_via_stream(rx, 0).await.unwrap();
48         },
49         async {
50             let result = tx.write_all(content.clone()).await;
51             drop(tx);
52             assert!(result.is_empty());
53         },
54     };
55 
56     // The file should be of the appropriate size via `stat` now.
57     let stat = file.stat().await.unwrap();
58     assert_eq!(
59         stat.size,
60         content.len() as u64,
61         "file should be size of content",
62     );
63 
64     drop(file);
65 
66     // Make sure the file can be read at various offsets.
67     let file = dir
68         .open_at(
69             PathFlags::empty(),
70             filename.to_string(),
71             OpenFlags::empty(),
72             DescriptorFlags::READ,
73         )
74         .await
75         .expect("creating a file for reading");
76 
77     let (read_contents, result) = file.read_via_stream(0);
78     let read_contents = read_contents.collect().await;
79     result.await.unwrap();
80     assert!(read_contents == content);
81 
82     let (read_contents, result) = file.read_via_stream((content.len() as u64) - 100);
83     let read_contents = read_contents.collect().await;
84     result.await.unwrap();
85     assert!(read_contents == &content[content.len() - 100..]);
86     drop(file);
87 
88     // Writing to a read-only handle should be an error.
89     let filename = "test-zero-write-fails.txt";
90     dir.open_at(
91         PathFlags::empty(),
92         filename.to_string(),
93         OpenFlags::CREATE,
94         DescriptorFlags::WRITE,
95     )
96     .await
97     .expect("creating a file for writing");
98     let file = dir
99         .open_at(
100             PathFlags::empty(),
101             filename.to_string(),
102             OpenFlags::empty(),
103             DescriptorFlags::READ,
104         )
105         .await
106         .expect("creating a file for writing");
107 
108     let (mut tx, rx) = wit_stream::new();
109     join! {
110         async {
111             let err = file.write_via_stream(rx, 0).await.unwrap_err();
112             assert!(
113                 matches!(err, ErrorCode::Access | ErrorCode::BadDescriptor | ErrorCode::NotPermitted),
114                 "bad error {err:?}",
115             );
116         },
117         async {
118             let result = tx.write_all(b"x".to_vec()).await;
119             drop(tx);
120             assert_eq!(result.len(), 1);
121         },
122     };
123 }
124