github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/imports/wasi_snapshot_preview1/fs_unit_test.go (about) 1 package wasi_snapshot_preview1 2 3 import ( 4 "os" 5 "testing" 6 7 experimentalsys "github.com/bananabytelabs/wazero/experimental/sys" 8 "github.com/bananabytelabs/wazero/internal/fstest" 9 "github.com/bananabytelabs/wazero/internal/sys" 10 "github.com/bananabytelabs/wazero/internal/sysfs" 11 "github.com/bananabytelabs/wazero/internal/testing/require" 12 "github.com/bananabytelabs/wazero/internal/wasip1" 13 ) 14 15 func Test_maxDirents(t *testing.T) { 16 tests := []struct { 17 name string 18 dirents []experimentalsys.Dirent 19 bufLen uint32 20 expectedBufToWrite uint32 21 expectedDirentCount int 22 expectedTruncatedLen uint32 23 }{ 24 { 25 name: "no entries", 26 }, 27 { 28 name: "can't fit one", 29 dirents: testDirents, 30 bufLen: 23, 31 expectedBufToWrite: 23, 32 expectedDirentCount: 1, 33 expectedTruncatedLen: 23, 34 }, 35 { 36 name: "only fits header", 37 dirents: testDirents, 38 bufLen: wasip1.DirentSize, 39 expectedBufToWrite: wasip1.DirentSize, 40 expectedDirentCount: 1, 41 expectedTruncatedLen: wasip1.DirentSize, 42 }, 43 { 44 name: "one", 45 dirents: testDirents, 46 bufLen: 25, 47 expectedBufToWrite: 25, 48 expectedDirentCount: 1, 49 }, 50 { 51 name: "one but not room for two's name", 52 dirents: testDirents, 53 bufLen: 25 + 25, 54 expectedBufToWrite: 25 + wasip1.DirentSize, 55 expectedDirentCount: 2, 56 expectedTruncatedLen: wasip1.DirentSize, // can write DirentSize 57 }, 58 { 59 name: "two", 60 dirents: testDirents, 61 bufLen: 25 + 26, 62 expectedBufToWrite: 25 + 26, 63 expectedDirentCount: 2, 64 }, 65 { 66 name: "two but not three's dirent", 67 dirents: testDirents, 68 bufLen: 25 + 26 + 20, 69 expectedBufToWrite: 25 + 26 + 20, 70 expectedDirentCount: 3, 71 expectedTruncatedLen: 20, // 20 + 4 == DirentSize 72 }, 73 { 74 name: "two but not three's name", 75 dirents: testDirents, 76 bufLen: 25 + 26 + 25, 77 expectedBufToWrite: 25 + 26 + wasip1.DirentSize, 78 expectedDirentCount: 3, 79 expectedTruncatedLen: wasip1.DirentSize, // can write DirentSize 80 }, 81 { 82 name: "three", 83 dirents: testDirents, 84 bufLen: 25 + 26 + 27, 85 expectedBufToWrite: 25 + 26 + 27, 86 expectedDirentCount: 3, 87 }, 88 { 89 name: "max", 90 dirents: testDirents, 91 bufLen: 100, 92 expectedBufToWrite: 25 + 26 + 27, 93 expectedDirentCount: 3, 94 }, 95 } 96 97 for _, tt := range tests { 98 tc := tt 99 100 t.Run(tc.name, func(t *testing.T) { 101 bufToWrite, direntCount, truncatedLen := maxDirents(tc.dirents, tc.bufLen) 102 require.Equal(t, tc.expectedBufToWrite, bufToWrite) 103 require.Equal(t, tc.expectedDirentCount, direntCount) 104 require.Equal(t, tc.expectedTruncatedLen, truncatedLen) 105 }) 106 } 107 } 108 109 var ( 110 testDirents = func() []experimentalsys.Dirent { 111 dPath := "dir" 112 d, errno := sysfs.OpenFSFile(fstest.FS, dPath, experimentalsys.O_RDONLY, 0) 113 if errno != 0 { 114 panic(errno) 115 } 116 defer d.Close() 117 dirents, errno := d.Readdir(-1) 118 if errno != 0 { 119 panic(errno) 120 } 121 return dirents 122 }() 123 124 dirent1 = []byte{ 125 1, 0, 0, 0, 0, 0, 0, 0, // d_next = 1 126 0, 0, 0, 0, 0, 0, 0, 0, // d_ino = 0 127 1, 0, 0, 0, // d_namlen = 1 character 128 4, 0, 0, 0, // d_type = regular_file 129 '-', // name 130 } 131 dirent2 = []byte{ 132 2, 0, 0, 0, 0, 0, 0, 0, // d_next = 2 133 0, 0, 0, 0, 0, 0, 0, 0, // d_ino = 0 134 2, 0, 0, 0, // d_namlen = 1 character 135 3, 0, 0, 0, // d_type = directory 136 'a', '-', // name 137 } 138 dirent3 = []byte{ 139 3, 0, 0, 0, 0, 0, 0, 0, // d_next = 3 140 0, 0, 0, 0, 0, 0, 0, 0, // d_ino = 0 141 3, 0, 0, 0, // d_namlen = 3 characters 142 4, 0, 0, 0, // d_type = regular_file 143 'a', 'b', '-', // name 144 } 145 ) 146 147 func Test_writeDirents(t *testing.T) { 148 tests := []struct { 149 name string 150 dirents []experimentalsys.Dirent 151 entryCount int 152 truncatedLen uint32 153 expected []byte 154 }{ 155 { 156 name: "none", 157 dirents: testDirents, 158 }, 159 { 160 name: "one", 161 dirents: testDirents, 162 entryCount: 1, 163 expected: dirent1, 164 }, 165 { 166 name: "two", 167 dirents: testDirents, 168 entryCount: 2, 169 expected: append(dirent1, dirent2...), 170 }, 171 { 172 name: "two with truncated dirent", 173 dirents: testDirents, 174 entryCount: 3, 175 truncatedLen: wasip1.DirentSize, 176 expected: append(append(dirent1, dirent2...), dirent3[:wasip1.DirentSize]...), 177 }, 178 { 179 name: "two with truncated smaller than dirent", 180 dirents: testDirents, 181 entryCount: 3, 182 truncatedLen: 5, 183 expected: append(append(dirent1, dirent2...), 0, 0, 0, 0, 0), 184 }, 185 { 186 name: "three", 187 dirents: testDirents, 188 entryCount: 3, 189 expected: append(append(dirent1, dirent2...), dirent3...), 190 }, 191 } 192 193 for _, tt := range tests { 194 tc := tt 195 196 t.Run(tc.name, func(t *testing.T) { 197 d_next := uint64(1) 198 buf := make([]byte, len(tc.expected)) 199 writeDirents(buf, tc.dirents, d_next, tc.entryCount, tc.truncatedLen) 200 require.Equal(t, tc.expected, buf) 201 }) 202 } 203 } 204 205 func Test_openFlags(t *testing.T) { 206 tests := []struct { 207 name string 208 dirflags, oflags, fdflags uint16 209 rights uint32 210 expectedOpenFlags experimentalsys.Oflag 211 }{ 212 { 213 name: "oflags=0", 214 expectedOpenFlags: experimentalsys.O_NOFOLLOW | experimentalsys.O_RDONLY, 215 }, 216 { 217 name: "oflags=O_CREAT", 218 oflags: wasip1.O_CREAT, 219 expectedOpenFlags: experimentalsys.O_NOFOLLOW | experimentalsys.O_RDWR | experimentalsys.O_CREAT, 220 }, 221 { 222 name: "oflags=O_DIRECTORY", 223 oflags: wasip1.O_DIRECTORY, 224 expectedOpenFlags: experimentalsys.O_NOFOLLOW | experimentalsys.O_DIRECTORY, 225 }, 226 { 227 name: "oflags=O_EXCL", 228 oflags: wasip1.O_EXCL, 229 expectedOpenFlags: experimentalsys.O_NOFOLLOW | experimentalsys.O_RDONLY | experimentalsys.O_EXCL, 230 }, 231 { 232 name: "oflags=O_TRUNC", 233 oflags: wasip1.O_TRUNC, 234 expectedOpenFlags: experimentalsys.O_NOFOLLOW | experimentalsys.O_RDWR | experimentalsys.O_TRUNC, 235 }, 236 { 237 name: "fdflags=FD_APPEND", 238 fdflags: wasip1.FD_APPEND, 239 expectedOpenFlags: experimentalsys.O_NOFOLLOW | experimentalsys.O_RDWR | experimentalsys.O_APPEND, 240 }, 241 { 242 name: "oflags=O_TRUNC|O_CREAT", 243 oflags: wasip1.O_TRUNC | wasip1.O_CREAT, 244 expectedOpenFlags: experimentalsys.O_NOFOLLOW | experimentalsys.O_RDWR | experimentalsys.O_TRUNC | experimentalsys.O_CREAT, 245 }, 246 { 247 name: "dirflags=LOOKUP_SYMLINK_FOLLOW", 248 dirflags: wasip1.LOOKUP_SYMLINK_FOLLOW, 249 expectedOpenFlags: experimentalsys.O_RDONLY, 250 }, 251 { 252 name: "rights=FD_READ", 253 rights: wasip1.RIGHT_FD_READ, 254 expectedOpenFlags: experimentalsys.O_NOFOLLOW | experimentalsys.O_RDONLY, 255 }, 256 { 257 name: "rights=FD_WRITE", 258 rights: wasip1.RIGHT_FD_WRITE, 259 expectedOpenFlags: experimentalsys.O_NOFOLLOW | experimentalsys.O_WRONLY, 260 }, 261 { 262 name: "rights=FD_READ|FD_WRITE", 263 rights: wasip1.RIGHT_FD_READ | wasip1.RIGHT_FD_WRITE, 264 expectedOpenFlags: experimentalsys.O_NOFOLLOW | experimentalsys.O_RDWR, 265 }, 266 } 267 268 for _, tt := range tests { 269 tc := tt 270 271 t.Run(tc.name, func(t *testing.T) { 272 openFlags := openFlags(tc.dirflags, tc.oflags, tc.fdflags, tc.rights) 273 require.Equal(t, tc.expectedOpenFlags, openFlags) 274 }) 275 } 276 } 277 278 func Test_getWasiFiletype_DevNull(t *testing.T) { 279 st, err := os.Stat(os.DevNull) 280 require.NoError(t, err) 281 282 ft := getWasiFiletype(st.Mode()) 283 284 // Should be a character device, and not contain permissions 285 require.Equal(t, wasip1.FILETYPE_CHARACTER_DEVICE, ft) 286 } 287 288 func Test_isPreopenedStdio(t *testing.T) { 289 tests := []struct { 290 name string 291 fd int32 292 f *sys.FileEntry 293 expected bool 294 }{ 295 { 296 name: "stdin", 297 fd: sys.FdStdin, 298 f: &sys.FileEntry{IsPreopen: true}, 299 expected: true, 300 }, 301 { 302 name: "stdin re-opened", 303 fd: sys.FdStdin, 304 f: &sys.FileEntry{IsPreopen: false}, 305 expected: false, 306 }, 307 { 308 name: "stdout", 309 fd: sys.FdStdout, 310 f: &sys.FileEntry{IsPreopen: true}, 311 expected: true, 312 }, 313 { 314 name: "stdout re-opened", 315 fd: sys.FdStdout, 316 f: &sys.FileEntry{IsPreopen: false}, 317 expected: false, 318 }, 319 { 320 name: "stderr", 321 fd: sys.FdStderr, 322 f: &sys.FileEntry{IsPreopen: true}, 323 expected: true, 324 }, 325 { 326 name: "stderr re-opened", 327 fd: sys.FdStderr, 328 f: &sys.FileEntry{IsPreopen: false}, 329 expected: false, 330 }, 331 { 332 name: "not stdio pre-open", 333 fd: sys.FdPreopen, 334 f: &sys.FileEntry{IsPreopen: true}, 335 expected: false, 336 }, 337 { 338 name: "random file", 339 fd: 42, 340 f: &sys.FileEntry{}, 341 expected: false, 342 }, 343 } 344 345 for _, tt := range tests { 346 tc := tt 347 348 t.Run(tc.name, func(t *testing.T) { 349 ok := isPreopenedStdio(tc.fd, tc.f) 350 require.Equal(t, tc.expected, ok) 351 }) 352 } 353 }