github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/sysfs/poll_windows_test.go (about) 1 package sysfs 2 3 import ( 4 "net" 5 "os" 6 "syscall" 7 "testing" 8 "time" 9 10 "github.com/tetratelabs/wazero/experimental/sys" 11 "github.com/tetratelabs/wazero/internal/testing/require" 12 ) 13 14 func TestPoll_Windows(t *testing.T) { 15 type result struct { 16 n int 17 err sys.Errno 18 } 19 20 pollToChannel := func(fd uintptr, timeoutMillis int32, ch chan result) { 21 r := result{} 22 fds := []pollFd{{fd: fd, events: _POLLIN}} 23 r.n, r.err = _poll(fds, timeoutMillis) 24 ch <- r 25 close(ch) 26 } 27 28 t.Run("poll returns sys.ENOSYS when n == 0 and timeoutMillis is negative", func(t *testing.T) { 29 n, errno := _poll(nil, -1) 30 require.Equal(t, -1, n) 31 require.EqualErrno(t, sys.ENOSYS, errno) 32 }) 33 34 t.Run("peekNamedPipe should report the correct state of incoming data in the pipe", func(t *testing.T) { 35 r, w, err := os.Pipe() 36 require.NoError(t, err) 37 defer r.Close() 38 defer w.Close() 39 rh := syscall.Handle(r.Fd()) 40 41 // Ensure the pipe has no data. 42 n, err := peekNamedPipe(rh) 43 require.Zero(t, err) 44 require.Zero(t, n) 45 46 // Write to the channel. 47 msg, err := syscall.ByteSliceFromString("test\n") 48 require.NoError(t, err) 49 _, err = write(w, msg) 50 require.EqualErrno(t, 0, err) 51 52 // Ensure the pipe has data. 53 n, err = peekNamedPipe(rh) 54 require.Zero(t, err) 55 require.Equal(t, 6, int(n)) 56 }) 57 58 t.Run("peekPipes should return an error on invalid handle", func(t *testing.T) { 59 fds := []pollFd{{fd: uintptr(syscall.InvalidHandle)}} 60 _, err := peekPipes(fds) 61 require.EqualErrno(t, sys.EBADF, err) 62 }) 63 64 t.Run("peekAll should return an error on invalid handle", func(t *testing.T) { 65 fds := []pollFd{{fd: uintptr(syscall.InvalidHandle)}} 66 _, _, err := peekAll(fds, nil) 67 require.EqualErrno(t, sys.EBADF, err) 68 }) 69 70 t.Run("poll should return successfully with a regular file", func(t *testing.T) { 71 f, err := os.CreateTemp(t.TempDir(), "test") 72 require.NoError(t, err) 73 defer f.Close() 74 75 fds := []pollFd{{fd: f.Fd()}} 76 77 n, errno := _poll(fds, 0) 78 require.Zero(t, errno) 79 require.Equal(t, 1, n) 80 }) 81 82 t.Run("peekAll should return successfully with a pipe", func(t *testing.T) { 83 r, w, err := os.Pipe() 84 require.NoError(t, err) 85 defer r.Close() 86 defer w.Close() 87 88 fds := []pollFd{{fd: r.Fd()}} 89 90 npipes, nsockets, errno := peekAll(fds, nil) 91 require.Zero(t, errno) 92 require.Equal(t, 0, npipes) 93 require.Equal(t, 0, nsockets) 94 95 w.Write([]byte("wazero")) 96 npipes, nsockets, errno = peekAll(fds, nil) 97 require.Zero(t, errno) 98 require.Equal(t, 1, npipes) 99 require.Equal(t, 0, nsockets) 100 }) 101 102 t.Run("peekAll should return successfully with a socket", func(t *testing.T) { 103 listen, err := net.Listen("tcp", "127.0.0.1:0") 104 require.NoError(t, err) 105 defer listen.Close() 106 107 conn, err := listen.(*net.TCPListener).SyscallConn() 108 require.NoError(t, err) 109 110 fds := []pollFd{} 111 conn.Control(func(fd uintptr) { 112 fds = append(fds, pollFd{fd: fd, events: _POLLIN}) 113 }) 114 115 npipes, nsockets, errno := peekAll(nil, fds) 116 require.Zero(t, errno) 117 require.Equal(t, 0, npipes) 118 require.Equal(t, 0, nsockets) 119 120 tcpAddr, err := net.ResolveTCPAddr("tcp", listen.Addr().String()) 121 require.NoError(t, err) 122 tcp, err := net.DialTCP("tcp", nil, tcpAddr) 123 require.NoError(t, err) 124 tcp.Write([]byte("wazero")) 125 126 conn.Control(func(fd uintptr) { 127 fds[0].fd = fd 128 }) 129 npipes, nsockets, errno = peekAll(nil, fds) 130 require.Zero(t, errno) 131 require.Equal(t, 0, npipes) 132 require.Equal(t, 1, nsockets) 133 }) 134 135 t.Run("poll should return immediately when duration is zero (no data)", func(t *testing.T) { 136 r, w, err := os.Pipe() 137 defer r.Close() 138 defer w.Close() 139 140 require.NoError(t, err) 141 fds := []pollFd{{fd: r.Fd(), events: _POLLIN}} 142 n, err := _poll(fds, 0) 143 require.Zero(t, err) 144 require.Zero(t, n) 145 }) 146 147 t.Run("poll should return immediately when duration is zero (data)", func(t *testing.T) { 148 r, w, err := os.Pipe() 149 require.NoError(t, err) 150 defer r.Close() 151 defer w.Close() 152 fds := []pollFd{{fd: r.Fd(), events: _POLLIN}} 153 154 // Write to the channel immediately. 155 msg, err := syscall.ByteSliceFromString("test\n") 156 require.NoError(t, err) 157 _, err = write(w, msg) 158 require.EqualErrno(t, 0, err) 159 160 // Verify that the write is reported. 161 n, err := _poll(fds, 0) 162 require.Zero(t, err) 163 require.Equal(t, 1, n) 164 }) 165 166 t.Run("poll should wait forever when duration is nil (no writes)", func(t *testing.T) { 167 r, w, err := os.Pipe() 168 require.NoError(t, err) 169 defer r.Close() 170 defer w.Close() 171 172 ch := make(chan result, 1) 173 go pollToChannel(r.Fd(), -1, ch) 174 175 // Wait a little, then ensure no writes occurred. 176 <-time.After(500 * time.Millisecond) 177 require.Equal(t, 0, len(ch)) 178 }) 179 180 t.Run("poll should wait forever when duration is nil", func(t *testing.T) { 181 r, w, err := os.Pipe() 182 require.NoError(t, err) 183 defer r.Close() 184 defer w.Close() 185 186 ch := make(chan result, 1) 187 go pollToChannel(r.Fd(), -1, ch) 188 189 // Wait a little, then ensure no writes occurred. 190 <-time.After(100 * time.Millisecond) 191 require.Equal(t, 0, len(ch)) 192 193 // Write a message to the pipe. 194 msg, err := syscall.ByteSliceFromString("test\n") 195 require.NoError(t, err) 196 _, err = write(w, msg) 197 require.EqualErrno(t, 0, err) 198 199 // Ensure that the write occurs (panic after an arbitrary timeout). 200 select { 201 case <-time.After(500 * time.Millisecond): 202 t.Fatal("unreachable!") 203 case r := <-ch: 204 require.Zero(t, r.err) 205 require.NotEqual(t, 0, r.n) 206 } 207 }) 208 209 t.Run("poll should wait for the given duration", func(t *testing.T) { 210 r, w, err := os.Pipe() 211 require.NoError(t, err) 212 defer r.Close() 213 defer w.Close() 214 215 ch := make(chan result, 1) 216 go pollToChannel(r.Fd(), 500, ch) 217 218 // Wait a little, then ensure no writes occurred. 219 <-time.After(100 * time.Millisecond) 220 require.Equal(t, 0, len(ch)) 221 222 // Write a message to the pipe. 223 msg, err := syscall.ByteSliceFromString("test\n") 224 require.NoError(t, err) 225 _, err = write(w, msg) 226 require.EqualErrno(t, 0, err) 227 228 // Ensure that the write occurs before the timer expires. 229 select { 230 case <-time.After(500 * time.Millisecond): 231 panic("no data!") 232 case r := <-ch: 233 require.Zero(t, r.err) 234 require.Equal(t, 1, r.n) 235 } 236 }) 237 238 t.Run("poll should timeout after the given duration", func(t *testing.T) { 239 r, w, err := os.Pipe() 240 require.NoError(t, err) 241 defer r.Close() 242 defer w.Close() 243 244 ch := make(chan result, 1) 245 go pollToChannel(r.Fd(), 200, ch) 246 247 // Ensure that the timer has expired. 248 res := <-ch 249 require.Zero(t, res.err) 250 require.Zero(t, res.n) 251 }) 252 253 t.Run("poll should return when a write occurs before the given duration", func(t *testing.T) { 254 r, w, err := os.Pipe() 255 require.NoError(t, err) 256 defer r.Close() 257 defer w.Close() 258 259 ch := make(chan result, 1) 260 go pollToChannel(r.Fd(), 800, ch) 261 262 <-time.After(300 * time.Millisecond) 263 require.Equal(t, 0, len(ch)) 264 265 msg, err := syscall.ByteSliceFromString("test\n") 266 require.NoError(t, err) 267 _, err = write(w, msg) 268 require.EqualErrno(t, 0, err) 269 270 res := <-ch 271 require.Zero(t, res.err) 272 require.Equal(t, 1, res.n) 273 }) 274 275 t.Run("poll should return when a regular file is given", func(t *testing.T) { 276 f, err := os.CreateTemp(t.TempDir(), "ex") 277 require.NoError(t, err) 278 defer f.Close() 279 280 require.NoError(t, err) 281 fds := []pollFd{{fd: f.Fd(), events: _POLLIN}} 282 n, errno := _poll(fds, 0) 283 require.Zero(t, errno) 284 require.Equal(t, 1, n) 285 }) 286 }