github.com/ianic/xnet/aio@v0.0.0-20230924160527-cee7f41ab201/loop_test.go (about)

     1  package aio
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net"
     7  	"os"
     8  	"runtime"
     9  	"syscall"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  func TestNetworkConnectWriteStdLib(t *testing.T) {
    17  	listen, err := net.Listen("tcp", "127.0.0.1:0")
    18  	require.NoError(t, err)
    19  	_, portStr, err := net.SplitHostPort(listen.Addr().String())
    20  	require.NoError(t, err)
    21  	// t.Logf("running test server at port %s", portStr)
    22  
    23  	data := testRandomBuf(t, 1024)
    24  	go func() {
    25  		testSender(t, fmt.Sprintf("127.0.0.1:%s", portStr), data)
    26  	}()
    27  
    28  	var readBuffer []byte
    29  	conn, err := listen.Accept()
    30  	require.NoError(t, err)
    31  	chunk := make([]byte, 8)
    32  	for {
    33  		n, err := conn.Read(chunk)
    34  		if n == 0 {
    35  			break
    36  		}
    37  		require.NoError(t, err)
    38  		require.True(t, n >= 0)
    39  		readBuffer = append(readBuffer, chunk[:n]...)
    40  	}
    41  	conn.Close()
    42  	listen.Close()
    43  
    44  	require.Equal(t, data, readBuffer)
    45  }
    46  
    47  func TestTCPListener(t *testing.T) {
    48  	loop, err := New(Options{
    49  		RingEntries:      16,
    50  		RecvBuffersCount: 8,
    51  		RecvBufferLen:    1024,
    52  	})
    53  	require.NoError(t, err)
    54  	defer loop.Close()
    55  
    56  	conn := testConn{}
    57  	// called when tcp listener accepts tcp connection
    58  	tcpAccepted := func(fd int, tc *TCPConn) {
    59  		// t.Logf("accepted fd %d\n", fd)
    60  		tc.Bind(&conn)
    61  	}
    62  	// start listener
    63  	lsn, err := loop.Listen("[::1]:0", tcpAccepted)
    64  	require.NoError(t, err)
    65  	// t.Logf("tcp listener started at port %d", lsn.Port())
    66  
    67  	data := testRandomBuf(t, 1024*4)
    68  	go func() {
    69  		testSender(t, fmt.Sprintf("[::1]:%d", lsn.port), data)
    70  	}()
    71  
    72  	// accept connection
    73  	loop.runOnce()
    74  	lsn.close(false)
    75  	// run until connection is closed
    76  	loop.runUntilDone()
    77  
    78  	require.True(t, len(conn.received) >= 4)
    79  	testRequireEqualBuffers(t, data, conn.received)
    80  
    81  	require.True(t, conn.closed, "conn.Closed should be called")
    82  }
    83  
    84  func TestTCPConnectSend(t *testing.T) {
    85  	listen, err := net.Listen("tcp", "[::1]:0")
    86  	require.NoError(t, err)
    87  	_, portStr, err := net.SplitHostPort(listen.Addr().String())
    88  	addr := fmt.Sprintf("[::1]:%s", portStr)
    89  	require.NoError(t, err)
    90  
    91  	data := testRandomBuf(t, 4096)
    92  	closer := &testCloserConn{}
    93  	loopDone := make(chan struct{})
    94  	go func() {
    95  		loop, err := New(DefaultOptions)
    96  		require.NoError(t, err)
    97  		defer loop.Close()
    98  
    99  		loop.Dial(addr, func(fd int, tc *TCPConn, err error) {
   100  			require.NoError(t, err)
   101  			closer.tc = tc
   102  			tc.Bind(closer)
   103  			tc.Send(data)
   104  		})
   105  		loop.runUntilDone()
   106  		runtime.GC() // checks that pinned pointers are unpinned
   107  		close(loopDone)
   108  	}()
   109  
   110  	var readBuffer []byte
   111  	conn, err := listen.Accept()
   112  	require.NoError(t, err)
   113  	chunk := make([]byte, 1024)
   114  	for {
   115  		n, err := conn.Read(chunk)
   116  		if n == 0 {
   117  			break
   118  		}
   119  		//t.Logf("received chunk %d", n)
   120  		require.NoError(t, err)
   121  		require.True(t, n >= 0)
   122  		readBuffer = append(readBuffer, chunk[:n]...)
   123  	}
   124  	conn.Close()
   125  	listen.Close()
   126  
   127  	require.Equal(t, data, readBuffer)
   128  	<-loopDone
   129  
   130  	require.True(t, closer.closed)
   131  }
   132  
   133  func testSender(t *testing.T, addr string, data []byte) {
   134  	conn, err := net.DialTimeout("tcp", addr, time.Second)
   135  	require.Nil(t, err)
   136  	require.NotNil(t, conn)
   137  	n, err := conn.Write(data)
   138  	require.NoError(t, err)
   139  	require.Equal(t, len(data), n)
   140  	conn.Close()
   141  }
   142  
   143  type testConn struct {
   144  	received [][]byte
   145  	closed   bool
   146  }
   147  
   148  func (c *testConn) Received(buf []byte) {
   149  	c.received = append(c.received, toOwn(buf))
   150  }
   151  func (c *testConn) Sent() {}
   152  func (c *testConn) Closed(error) {
   153  	c.closed = true
   154  }
   155  
   156  type testCloserConn struct {
   157  	tc     *TCPConn
   158  	closed bool
   159  }
   160  
   161  func (c *testCloserConn) Received(buf []byte) {}
   162  func (c *testCloserConn) Sent() {
   163  	c.tc.Close()
   164  }
   165  func (c *testCloserConn) Closed(error) {
   166  	c.closed = true
   167  }
   168  
   169  func toOwn(buf []byte) []byte {
   170  	own := make([]byte, len(buf))
   171  	copy(own, buf)
   172  	return own
   173  }
   174  
   175  func testRequireEqualBuffers(t *testing.T, expected []byte, actual [][]byte) {
   176  	nn := 0
   177  	for _, buf := range actual {
   178  		require.True(t, len(expected) >= nn+len(buf))
   179  		require.Equal(t, expected[nn:nn+len(buf)], buf)
   180  		nn += len(buf)
   181  	}
   182  	require.Equal(t, len(expected), nn)
   183  }
   184  
   185  func testRandomBuf(t *testing.T, size int) []byte {
   186  	f, err := os.Open("/dev/random")
   187  	require.NoError(t, err)
   188  	buf := make([]byte, size)
   189  	_, err = io.ReadFull(f, buf)
   190  	require.NoError(t, err)
   191  	return buf
   192  }
   193  
   194  func TestErrnoTemporary(t *testing.T) {
   195  	require.True(t, (&ErrErrno{Errno: syscall.EINTR}).Temporary())
   196  	require.True(t, TemporaryError(syscall.EINTR))
   197  	require.True(t, TemporaryError(syscall.ETIME))
   198  	require.True(t, TemporaryError(syscall.ENOBUFS))
   199  }