github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/sys/stdio_test.go (about)

     1  package sys
     2  
     3  import (
     4  	"io/fs"
     5  	"os"
     6  	"testing"
     7  
     8  	"github.com/bananabytelabs/wazero/internal/testing/require"
     9  )
    10  
    11  func TestStdio(t *testing.T) {
    12  	// simulate regular file attached to stdin
    13  	f, err := os.CreateTemp(t.TempDir(), "somefile")
    14  	require.NoError(t, err)
    15  	defer f.Close()
    16  
    17  	stdin, err := stdinFileEntry(os.Stdin)
    18  	require.NoError(t, err)
    19  	stdinStat, err := os.Stdin.Stat()
    20  	require.NoError(t, err)
    21  
    22  	stdinNil, err := stdinFileEntry(nil)
    23  	require.NoError(t, err)
    24  
    25  	stdinFile, err := stdinFileEntry(f)
    26  	require.NoError(t, err)
    27  
    28  	stdout, err := stdioWriterFileEntry("stdout", os.Stdout)
    29  	require.NoError(t, err)
    30  	stdoutStat, err := os.Stdout.Stat()
    31  	require.NoError(t, err)
    32  
    33  	stdoutNil, err := stdioWriterFileEntry("stdout", nil)
    34  	require.NoError(t, err)
    35  
    36  	stdoutFile, err := stdioWriterFileEntry("stdout", f)
    37  	require.NoError(t, err)
    38  
    39  	stderr, err := stdioWriterFileEntry("stderr", os.Stderr)
    40  	require.NoError(t, err)
    41  	stderrStat, err := os.Stderr.Stat()
    42  	require.NoError(t, err)
    43  
    44  	stderrNil, err := stdioWriterFileEntry("stderr", nil)
    45  	require.NoError(t, err)
    46  
    47  	stderrFile, err := stdioWriterFileEntry("stderr", f)
    48  	require.NoError(t, err)
    49  
    50  	tests := []struct {
    51  		name string
    52  		f    *FileEntry
    53  		// Depending on how the tests run, os.Stdin won't necessarily be a char
    54  		// device. We compare against an os.File, to account for this.
    55  		expectedType fs.FileMode
    56  	}{
    57  		{
    58  			name:         "stdin",
    59  			f:            stdin,
    60  			expectedType: stdinStat.Mode().Type(),
    61  		},
    62  		{
    63  			name:         "stdin noop",
    64  			f:            stdinNil,
    65  			expectedType: fs.ModeDevice,
    66  		},
    67  		{
    68  			name:         "stdin file",
    69  			f:            stdinFile,
    70  			expectedType: 0, // normal file
    71  		},
    72  		{
    73  			name:         "stdout",
    74  			f:            stdout,
    75  			expectedType: stdoutStat.Mode().Type(),
    76  		},
    77  		{
    78  			name:         "stdout noop",
    79  			f:            stdoutNil,
    80  			expectedType: fs.ModeDevice,
    81  		},
    82  		{
    83  			name:         "stdout file",
    84  			f:            stdoutFile,
    85  			expectedType: 0, // normal file
    86  		},
    87  		{
    88  			name:         "stderr",
    89  			f:            stderr,
    90  			expectedType: stderrStat.Mode().Type(),
    91  		},
    92  		{
    93  			name:         "stderr noop",
    94  			f:            stderrNil,
    95  			expectedType: fs.ModeDevice,
    96  		},
    97  		{
    98  			name:         "stderr file",
    99  			f:            stderrFile,
   100  			expectedType: 0, // normal file
   101  		},
   102  	}
   103  
   104  	for _, tt := range tests {
   105  		tc := tt
   106  
   107  		t.Run(tc.name+" Stat", func(t *testing.T) {
   108  			st, errno := tc.f.File.Stat()
   109  			require.EqualErrno(t, 0, errno)
   110  			require.Equal(t, tc.expectedType, st.Mode&fs.ModeType)
   111  			require.Equal(t, uint64(1), st.Nlink)
   112  
   113  			// Fake times are needed to pass wasi-testsuite.
   114  			// See https://github.com/WebAssembly/wasi-testsuite/blob/af57727/tests/rust/src/bin/fd_filestat_get.rs#L1-L19
   115  			require.Zero(t, st.Ctim)
   116  			require.Zero(t, st.Mtim)
   117  			require.Zero(t, st.Atim)
   118  		})
   119  
   120  		buf := make([]byte, 5)
   121  		switch tc.f {
   122  		case stdinNil:
   123  			t.Run(tc.name+" returns zero on Read", func(t *testing.T) {
   124  				n, errno := tc.f.File.Read(buf)
   125  				require.EqualErrno(t, 0, errno)
   126  				require.Zero(t, n) // like reading io.EOF
   127  			})
   128  		case stdoutNil, stderrNil:
   129  			// This is important because some code will loop forever attempting
   130  			// to write data. This happened in TestShortHash.
   131  			t.Run(tc.name+" returns length on Write", func(t *testing.T) {
   132  				n, errno := tc.f.File.Write(buf)
   133  				require.EqualErrno(t, 0, errno)
   134  				require.Equal(t, len(buf), n) // like io.Discard
   135  			})
   136  		}
   137  	}
   138  }