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 }