github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/sys/stat_test.go (about)

     1  package sys_test
     2  
     3  import (
     4  	"io/fs"
     5  	"os"
     6  	"path"
     7  	"runtime"
     8  	"testing"
     9  	"testing/fstest"
    10  
    11  	"github.com/wasilibs/wazerox/internal/testing/require"
    12  	"github.com/wasilibs/wazerox/sys"
    13  )
    14  
    15  func Test_NewStat_t(t *testing.T) {
    16  	tmpDir := t.TempDir()
    17  	fileData := []byte{1, 2, 3, 4}
    18  
    19  	dir := path.Join(tmpDir, "dir")
    20  	require.NoError(t, os.Mkdir(dir, 0o700))
    21  	osDirInfo, err := os.Stat(dir)
    22  	require.NoError(t, err)
    23  
    24  	file := path.Join(dir, "file")
    25  	require.NoError(t, os.WriteFile(file, []byte{1, 2, 3, 4}, 0o400))
    26  	osFileInfo, err := os.Stat(file)
    27  	require.NoError(t, err)
    28  
    29  	link := path.Join(dir, "file-link")
    30  	require.NoError(t, os.Symlink(file, link))
    31  	osSymlinkInfo, err := os.Lstat(link)
    32  	require.NoError(t, err)
    33  
    34  	osFileSt := sys.NewStat_t(osFileInfo)
    35  	testFS := fstest.MapFS{
    36  		"dir": {
    37  			Mode:    osDirInfo.Mode(),
    38  			ModTime: osDirInfo.ModTime(),
    39  		},
    40  		"dir/file": {
    41  			Data:    fileData,
    42  			Mode:    osFileInfo.Mode(),
    43  			ModTime: osFileInfo.ModTime(),
    44  		},
    45  		"dir/file-sys": {
    46  			// intentionally skip other fields to prove sys is read.
    47  			Sys: &osFileSt,
    48  		},
    49  	}
    50  
    51  	fsDirInfo, err := testFS.Stat("dir")
    52  	require.NoError(t, err)
    53  	fsFileInfo, err := testFS.Stat("dir/file")
    54  	require.NoError(t, err)
    55  	fsFileInfoWithSys, err := testFS.Stat("dir/file-sys")
    56  	require.NoError(t, err)
    57  
    58  	tests := []struct {
    59  		name            string
    60  		info            fs.FileInfo
    61  		expectDevIno    bool
    62  		expectedMode    fs.FileMode
    63  		expectedSize    int64
    64  		expectAtimCtime bool
    65  	}{
    66  		{
    67  			name:            "os dir",
    68  			info:            osDirInfo,
    69  			expectDevIno:    true,
    70  			expectedMode:    fs.ModeDir | 0o0700,
    71  			expectedSize:    osDirInfo.Size(), // OS dependent
    72  			expectAtimCtime: true,
    73  		},
    74  		{
    75  			name:            "fs dir",
    76  			info:            fsDirInfo,
    77  			expectDevIno:    false,
    78  			expectedMode:    fs.ModeDir | 0o0700,
    79  			expectedSize:    0,
    80  			expectAtimCtime: false,
    81  		},
    82  		{
    83  			name:            "os file",
    84  			info:            osFileInfo,
    85  			expectDevIno:    true,
    86  			expectedMode:    0o0400,
    87  			expectedSize:    int64(len(fileData)),
    88  			expectAtimCtime: true,
    89  		},
    90  		{
    91  			name:            "fs file",
    92  			info:            fsFileInfo,
    93  			expectDevIno:    false,
    94  			expectedMode:    0o0400,
    95  			expectedSize:    int64(len(fileData)),
    96  			expectAtimCtime: false,
    97  		},
    98  		{
    99  			name:            "fs file with Stat_t in Sys",
   100  			info:            fsFileInfoWithSys,
   101  			expectDevIno:    true,
   102  			expectedMode:    0o0400,
   103  			expectedSize:    int64(len(fileData)),
   104  			expectAtimCtime: true,
   105  		},
   106  		{
   107  			name:            "os symlink",
   108  			info:            osSymlinkInfo,
   109  			expectDevIno:    true,
   110  			expectedMode:    fs.ModeSymlink,
   111  			expectedSize:    osSymlinkInfo.Size(), // OS dependent
   112  			expectAtimCtime: true,
   113  		},
   114  	}
   115  
   116  	for _, tt := range tests {
   117  		tc := tt
   118  		t.Run(tc.name, func(t *testing.T) {
   119  			st := sys.NewStat_t(tc.info)
   120  			if tc.expectDevIno && runtime.GOOS != "windows" {
   121  				require.NotEqual(t, uint64(0), st.Dev)
   122  				require.NotEqual(t, uint64(0), st.Ino)
   123  			} else {
   124  				require.Zero(t, st.Dev)
   125  				require.Zero(t, st.Ino)
   126  			}
   127  
   128  			// link mode may differ on windows, so mask
   129  			require.Equal(t, tc.expectedMode, st.Mode&tc.expectedMode)
   130  
   131  			if sys.SysParseable && runtime.GOOS != "windows" {
   132  				switch st.Nlink {
   133  				case 2, 4: // dirents may include dot entries.
   134  					require.Equal(t, fs.ModeDir, st.Mode.Type())
   135  				default:
   136  					require.Equal(t, uint64(1), st.Nlink)
   137  				}
   138  			} else { // Nlink is possibly wrong, but not zero.
   139  				require.Equal(t, uint64(1), st.Nlink)
   140  			}
   141  
   142  			require.Equal(t, tc.expectedSize, st.Size)
   143  
   144  			if tc.expectAtimCtime && sys.SysParseable {
   145  				// We don't validate times strictly because it is os-dependent
   146  				// what updates times. There are edge cases for symlinks, too.
   147  				require.NotEqual(t, sys.EpochNanos(0), st.Ctim)
   148  				require.NotEqual(t, sys.EpochNanos(0), st.Mtim)
   149  				require.NotEqual(t, sys.EpochNanos(0), st.Mtim)
   150  			} else { // mtim is used for atim and ctime
   151  				require.Equal(t, st.Mtim, st.Ctim)
   152  				require.NotEqual(t, sys.EpochNanos(0), st.Mtim)
   153  				require.Equal(t, st.Mtim, st.Atim)
   154  			}
   155  		})
   156  	}
   157  }