|  | 
|  | 1 | +use futures::join; | 
|  | 2 | +use std::process; | 
|  | 3 | +extern crate wit_bindgen; | 
|  | 4 | + | 
|  | 5 | +wit_bindgen::generate!({ | 
|  | 6 | +    inline: r" | 
|  | 7 | +  package test:test; | 
|  | 8 | +
 | 
|  | 9 | +  world test { | 
|  | 10 | +      include wasi:filesystem/[email protected]; | 
|  | 11 | +      include wasi:cli/[email protected]; | 
|  | 12 | +  } | 
|  | 13 | +", | 
|  | 14 | +    additional_derives: [PartialEq, Eq, Hash, Clone], | 
|  | 15 | +    // Work around https://github.com/bytecodealliance/wasm-tools/issues/2285. | 
|  | 16 | +    features:["clocks-timezone"], | 
|  | 17 | +    generate_all | 
|  | 18 | +}); | 
|  | 19 | + | 
|  | 20 | +use wasi::filesystem::types::Descriptor; | 
|  | 21 | +use wasi::filesystem::types::{DescriptorFlags, ErrorCode, OpenFlags, PathFlags}; | 
|  | 22 | +use wit_bindgen::StreamResult; | 
|  | 23 | + | 
|  | 24 | +async fn pread(fd: &Descriptor, size: usize, offset: u64) -> Result<Vec<u8>, ErrorCode> { | 
|  | 25 | +    let (mut rx, future) = fd.read_via_stream(offset); | 
|  | 26 | +    let data = Vec::<u8>::with_capacity(size); | 
|  | 27 | +    let mut bytes_read = 0; | 
|  | 28 | +    let (mut result, mut data) = rx.read(data).await; | 
|  | 29 | +    loop { | 
|  | 30 | +        match result { | 
|  | 31 | +            StreamResult::Complete(n) => { | 
|  | 32 | +                assert!(n <= size - bytes_read); | 
|  | 33 | +                bytes_read += n; | 
|  | 34 | +                assert_eq!(data.len(), bytes_read); | 
|  | 35 | +                if bytes_read == size { | 
|  | 36 | +                    break; | 
|  | 37 | +                } | 
|  | 38 | +                (result, data) = rx.read(data).await; | 
|  | 39 | +            } | 
|  | 40 | +            StreamResult::Dropped => { | 
|  | 41 | +                assert_eq!(data.len(), bytes_read); | 
|  | 42 | +                break; | 
|  | 43 | +            } | 
|  | 44 | +            StreamResult::Cancelled => { | 
|  | 45 | +                panic!("who cancelled the stream?"); | 
|  | 46 | +            } | 
|  | 47 | +        } | 
|  | 48 | +    }; | 
|  | 49 | +    drop(rx); | 
|  | 50 | +    match future.await { | 
|  | 51 | +        Ok(()) => Ok(data), | 
|  | 52 | +        Err(err) => Err(err), | 
|  | 53 | +    } | 
|  | 54 | +} | 
|  | 55 | + | 
|  | 56 | +async fn pwrite(fd: &Descriptor, offset: u64, data: &[u8]) -> Result<usize, ErrorCode> { | 
|  | 57 | +    let (mut tx, rx) = wit_stream::new(); | 
|  | 58 | +    let future = fd.write_via_stream(rx, offset); | 
|  | 59 | +    let len = data.len(); | 
|  | 60 | +    let mut written: usize = 0; | 
|  | 61 | +    let mut result: Result<(), ErrorCode> = Ok(()); | 
|  | 62 | +    join!{ | 
|  | 63 | +        async { | 
|  | 64 | +            let (mut result, mut buf) = tx.write(data.to_vec()).await; | 
|  | 65 | +            loop { | 
|  | 66 | +                match result { | 
|  | 67 | +                    StreamResult::Complete(n) => { | 
|  | 68 | +                        assert!(n <= len - written); | 
|  | 69 | +                        written += n; | 
|  | 70 | +                        assert_eq!(buf.remaining(), len - written); | 
|  | 71 | +                        if buf.remaining() != 0 { | 
|  | 72 | +                            (result, buf) = tx.write_buf(buf).await; | 
|  | 73 | +                        } else { | 
|  | 74 | +                            break; | 
|  | 75 | +                        } | 
|  | 76 | +                    } | 
|  | 77 | +                    StreamResult::Dropped => { | 
|  | 78 | +                        panic!("receiver dropped the stream?"); | 
|  | 79 | +                    } | 
|  | 80 | +                    StreamResult::Cancelled => { | 
|  | 81 | +                        break; | 
|  | 82 | +                    } | 
|  | 83 | +                } | 
|  | 84 | +            } | 
|  | 85 | +            assert_eq!(buf.remaining(), len - written); | 
|  | 86 | +            drop(tx); | 
|  | 87 | +        }, | 
|  | 88 | +        async { result = future.await; } | 
|  | 89 | +    }; | 
|  | 90 | +    match result { | 
|  | 91 | +        Ok(()) => Ok(written), | 
|  | 92 | +        Err(err) => Err(err), | 
|  | 93 | +    } | 
|  | 94 | +} | 
|  | 95 | + | 
|  | 96 | +async fn pappend(fd: &Descriptor, data: &[u8]) -> Result<usize, ErrorCode> { | 
|  | 97 | +    let (mut tx, rx) = wit_stream::new(); | 
|  | 98 | +    let future = fd.append_via_stream(rx); | 
|  | 99 | +    let initial_size = fd.stat().await.unwrap().size as usize; | 
|  | 100 | +    let len = data.len(); | 
|  | 101 | +    let mut written: usize = 0; | 
|  | 102 | +    let mut result: Result<(), ErrorCode> = Ok(()); | 
|  | 103 | +    join!{ | 
|  | 104 | +        async { | 
|  | 105 | +            let (mut result, mut buf) = tx.write(data.to_vec()).await; | 
|  | 106 | +            loop { | 
|  | 107 | +                match result { | 
|  | 108 | +                    StreamResult::Complete(n) => { | 
|  | 109 | +                        assert!(n <= len - written); | 
|  | 110 | +                        written += n; | 
|  | 111 | +                        assert_eq!(buf.remaining(), len - written); | 
|  | 112 | +                        assert_eq!(fd.stat().await.unwrap().size as usize, | 
|  | 113 | +                                   initial_size + written); | 
|  | 114 | +                        if buf.remaining() != 0 { | 
|  | 115 | +                            (result, buf) = tx.write_buf(buf).await; | 
|  | 116 | +                        } else { | 
|  | 117 | +                            break; | 
|  | 118 | +                        } | 
|  | 119 | +                    } | 
|  | 120 | +                    StreamResult::Dropped => { | 
|  | 121 | +                        panic!("receiver dropped the stream?"); | 
|  | 122 | +                    } | 
|  | 123 | +                    StreamResult::Cancelled => { | 
|  | 124 | +                        break; | 
|  | 125 | +                    } | 
|  | 126 | +                } | 
|  | 127 | +            } | 
|  | 128 | +            assert_eq!(buf.remaining(), len - written); | 
|  | 129 | +            drop(tx); | 
|  | 130 | +        }, | 
|  | 131 | +        async { result = future.await; } | 
|  | 132 | +    }; | 
|  | 133 | +    match result { | 
|  | 134 | +        Ok(()) => Ok(written), | 
|  | 135 | +        Err(err) => Err(err), | 
|  | 136 | +    } | 
|  | 137 | +} | 
|  | 138 | + | 
|  | 139 | +async fn read_to_eof(fd: &Descriptor, offset: u64) -> Vec<u8> { | 
|  | 140 | +    let (stream, success) = fd.read_via_stream(offset); | 
|  | 141 | +    let ret = stream.collect().await; | 
|  | 142 | +    success.await.unwrap(); | 
|  | 143 | +    ret | 
|  | 144 | +} | 
|  | 145 | + | 
|  | 146 | +async fn test_io(dir: &Descriptor) { | 
|  | 147 | +    let open = |path: &str, oflags: OpenFlags, fdflags: DescriptorFlags| -> _ { | 
|  | 148 | +        dir.open_at(PathFlags::empty(), path.to_string(), oflags, fdflags) | 
|  | 149 | +    }; | 
|  | 150 | +    let open_r = |path: &str| -> _ { open(path, OpenFlags::empty(), DescriptorFlags::READ) }; | 
|  | 151 | +    let creat = |path: &str| -> _ { | 
|  | 152 | +        open( | 
|  | 153 | +            path, | 
|  | 154 | +            OpenFlags::CREATE | OpenFlags::EXCLUSIVE, | 
|  | 155 | +            DescriptorFlags::READ | DescriptorFlags::WRITE, | 
|  | 156 | +        ) | 
|  | 157 | +    }; | 
|  | 158 | +    let rm = |path: &str| dir.unlink_file_at(path.to_string()); | 
|  | 159 | + | 
|  | 160 | +    let a = open_r("a.txt").await.unwrap(); | 
|  | 161 | + | 
|  | 162 | +    pread(&a, 0, 0).await.unwrap(); | 
|  | 163 | +    pread(&a, 0, 1).await.unwrap(); | 
|  | 164 | +    pread(&a, 0, 6).await.unwrap(); | 
|  | 165 | +    pread(&a, 0, 7).await.unwrap(); | 
|  | 166 | +    pread(&a, 0, 17).await.unwrap(); | 
|  | 167 | + | 
|  | 168 | +    assert_eq!(&pread(&a, 1, 0).await.unwrap(), b"t"); | 
|  | 169 | +    assert_eq!(&pread(&a, 1, 1).await.unwrap(), b"e"); | 
|  | 170 | +    assert_eq!(&pread(&a, 1, 6).await.unwrap(), b"\n"); | 
|  | 171 | +    assert_eq!(&pread(&a, 1, 7).await.unwrap(), b""); | 
|  | 172 | +    assert_eq!(&pread(&a, 1, 17).await.unwrap(), b""); | 
|  | 173 | + | 
|  | 174 | +    assert_eq!(&read_to_eof(&a, 0).await, b"test-a\n"); | 
|  | 175 | +    assert_eq!(&read_to_eof(&a, 1).await, b"est-a\n"); | 
|  | 176 | +    assert_eq!(&read_to_eof(&a, 6).await, b"\n"); | 
|  | 177 | +    assert_eq!(&read_to_eof(&a, 7).await, b""); | 
|  | 178 | +    assert_eq!(&read_to_eof(&a, 17).await, b""); | 
|  | 179 | + | 
|  | 180 | +    // No-op on read-only fds. | 
|  | 181 | +    a.sync_data().await.unwrap(); | 
|  | 182 | +    a.sync().await.unwrap(); | 
|  | 183 | + | 
|  | 184 | +    assert_eq!(pread(&a, 1, u64::MAX).await, Err(ErrorCode::Invalid)); | 
|  | 185 | + | 
|  | 186 | +    let c = creat("c.cleanup").await.unwrap(); | 
|  | 187 | +    assert_eq!(&read_to_eof(&c, 0).await, b""); | 
|  | 188 | +    assert_eq!(pwrite(&c, 0, b"hello!").await, Ok(b"hello!".len())); | 
|  | 189 | +    assert_eq!(&read_to_eof(&c, 0).await, b"hello!"); | 
|  | 190 | +    assert_eq!(pwrite(&c, 0, b"byeee").await, Ok(b"byeee".len())); | 
|  | 191 | +    assert_eq!(&read_to_eof(&c, 0).await, b"byeee!"); | 
|  | 192 | +    assert_eq!(pappend(&c, b" laters!!").await, Ok(b" laters!!".len())); | 
|  | 193 | +    assert_eq!(&read_to_eof(&c, 0).await, b"byeee! laters!!"); | 
|  | 194 | +    c.sync_data().await.unwrap(); | 
|  | 195 | +    assert_eq!(&read_to_eof(&open_r("c.cleanup").await.unwrap(), 0).await, | 
|  | 196 | +               b"byeee! laters!!"); | 
|  | 197 | +    c.sync().await.unwrap(); | 
|  | 198 | + | 
|  | 199 | +    rm("c.cleanup").await.unwrap(); | 
|  | 200 | +} | 
|  | 201 | + | 
|  | 202 | +struct Component; | 
|  | 203 | +export!(Component); | 
|  | 204 | +impl exports::wasi::cli::run::Guest for Component { | 
|  | 205 | +    async fn run() -> Result<(), ()> { | 
|  | 206 | +        match &wasi::filesystem::preopens::get_directories()[..] { | 
|  | 207 | +            [(dir, dirname)] if dirname == "fs-tests.dir" => { | 
|  | 208 | +                test_io(dir).await; | 
|  | 209 | +            } | 
|  | 210 | +            [..] => { | 
|  | 211 | +                eprintln!("usage: run with one open dir named 'fs-tests.dir'"); | 
|  | 212 | +                process::exit(1) | 
|  | 213 | +            } | 
|  | 214 | +        }; | 
|  | 215 | +        Ok(()) | 
|  | 216 | +    } | 
|  | 217 | +} | 
|  | 218 | + | 
|  | 219 | +fn main() { | 
|  | 220 | +    unreachable!("main is a stub"); | 
|  | 221 | +} | 
0 commit comments