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  }