1 package wasi_snapshot_preview1
2
3 import (
4 "os"
5 "testing"
6
7 experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
8 "github.com/tetratelabs/wazero/internal/fstest"
9 "github.com/tetratelabs/wazero/internal/sys"
10 "github.com/tetratelabs/wazero/internal/sysfs"
11 "github.com/tetratelabs/wazero/internal/testing/require"
12 "github.com/tetratelabs/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,
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,
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,
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,
126 0, 0, 0, 0, 0, 0, 0, 0,
127 1, 0, 0, 0,
128 4, 0, 0, 0,
129 '-',
130 }
131 dirent2 = []byte{
132 2, 0, 0, 0, 0, 0, 0, 0,
133 0, 0, 0, 0, 0, 0, 0, 0,
134 2, 0, 0, 0,
135 3, 0, 0, 0,
136 'a', '-',
137 }
138 dirent3 = []byte{
139 3, 0, 0, 0, 0, 0, 0, 0,
140 0, 0, 0, 0, 0, 0, 0, 0,
141 3, 0, 0, 0,
142 4, 0, 0, 0,
143 'a', 'b', '-',
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
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 }
354
View as plain text