Source file
src/os/stat_test.go
1
2
3
4
5 package os_test
6
7 import (
8 "errors"
9 "internal/testenv"
10 "io/fs"
11 "os"
12 "path/filepath"
13 "runtime"
14 "testing"
15 )
16
17 type testStatAndLstatParams struct {
18 isLink bool
19 statCheck func(*testing.T, string, fs.FileInfo)
20 lstatCheck func(*testing.T, string, fs.FileInfo)
21 }
22
23
24 func testStatAndLstat(t *testing.T, path string, params testStatAndLstatParams) {
25
26 sfi, err := os.Stat(path)
27 if err != nil {
28 t.Error(err)
29 return
30 }
31 params.statCheck(t, path, sfi)
32
33
34 lsfi, err := os.Lstat(path)
35 if err != nil {
36 t.Error(err)
37 return
38 }
39 params.lstatCheck(t, path, lsfi)
40
41 if params.isLink {
42 if os.SameFile(sfi, lsfi) {
43 t.Errorf("stat and lstat of %q should not be the same", path)
44 }
45 } else {
46 if !os.SameFile(sfi, lsfi) {
47 t.Errorf("stat and lstat of %q should be the same", path)
48 }
49 }
50
51
52 f, err := os.Open(path)
53 if err != nil {
54 t.Error(err)
55 return
56 }
57 defer f.Close()
58
59 sfi2, err := f.Stat()
60 if err != nil {
61 t.Error(err)
62 return
63 }
64 params.statCheck(t, path, sfi2)
65
66 if !os.SameFile(sfi, sfi2) {
67 t.Errorf("stat of open %q file and stat of %q should be the same", path, path)
68 }
69
70 if params.isLink {
71 if os.SameFile(sfi2, lsfi) {
72 t.Errorf("stat of opened %q file and lstat of %q should not be the same", path, path)
73 }
74 } else {
75 if !os.SameFile(sfi2, lsfi) {
76 t.Errorf("stat of opened %q file and lstat of %q should be the same", path, path)
77 }
78 }
79
80 parentdir, base := filepath.Split(path)
81 if parentdir == "" || base == "" {
82
83
84 return
85 }
86
87 parent, err := os.Open(parentdir)
88 if err != nil {
89 t.Error(err)
90 return
91 }
92 defer parent.Close()
93
94 fis, err := parent.Readdir(-1)
95 if err != nil {
96 t.Error(err)
97 return
98 }
99 var lsfi2 fs.FileInfo
100 for _, fi2 := range fis {
101 if fi2.Name() == base {
102 lsfi2 = fi2
103 break
104 }
105 }
106 if lsfi2 == nil {
107 t.Errorf("failed to find %q in its parent", path)
108 return
109 }
110 params.lstatCheck(t, path, lsfi2)
111
112 if !os.SameFile(lsfi, lsfi2) {
113 t.Errorf("lstat of %q file in %q directory and %q should be the same", lsfi2.Name(), parentdir, path)
114 }
115 }
116
117
118 func testIsDir(t *testing.T, path string, fi fs.FileInfo) {
119 t.Helper()
120 if !fi.IsDir() {
121 t.Errorf("%q should be a directory", path)
122 }
123 if fi.Mode()&fs.ModeSymlink != 0 {
124 t.Errorf("%q should not be a symlink", path)
125 }
126 }
127
128
129 func testIsSymlink(t *testing.T, path string, fi fs.FileInfo) {
130 t.Helper()
131 if fi.IsDir() {
132 t.Errorf("%q should not be a directory", path)
133 }
134 if fi.Mode()&fs.ModeSymlink == 0 {
135 t.Errorf("%q should be a symlink", path)
136 }
137 }
138
139
140 func testIsFile(t *testing.T, path string, fi fs.FileInfo) {
141 t.Helper()
142 if fi.IsDir() {
143 t.Errorf("%q should not be a directory", path)
144 }
145 if fi.Mode()&fs.ModeSymlink != 0 {
146 t.Errorf("%q should not be a symlink", path)
147 }
148 }
149
150 func testDirStats(t *testing.T, path string) {
151 params := testStatAndLstatParams{
152 isLink: false,
153 statCheck: testIsDir,
154 lstatCheck: testIsDir,
155 }
156 testStatAndLstat(t, path, params)
157 }
158
159 func testFileStats(t *testing.T, path string) {
160 params := testStatAndLstatParams{
161 isLink: false,
162 statCheck: testIsFile,
163 lstatCheck: testIsFile,
164 }
165 testStatAndLstat(t, path, params)
166 }
167
168 func testSymlinkStats(t *testing.T, path string, isdir bool) {
169 params := testStatAndLstatParams{
170 isLink: true,
171 lstatCheck: testIsSymlink,
172 }
173 if isdir {
174 params.statCheck = testIsDir
175 } else {
176 params.statCheck = testIsFile
177 }
178 testStatAndLstat(t, path, params)
179 }
180
181 func testSymlinkSameFile(t *testing.T, path, link string) {
182 pathfi, err := os.Stat(path)
183 if err != nil {
184 t.Error(err)
185 return
186 }
187
188 linkfi, err := os.Stat(link)
189 if err != nil {
190 t.Error(err)
191 return
192 }
193 if !os.SameFile(pathfi, linkfi) {
194 t.Errorf("os.Stat(%q) and os.Stat(%q) are not the same file", path, link)
195 }
196
197 linkfi, err = os.Lstat(link)
198 if err != nil {
199 t.Error(err)
200 return
201 }
202 if os.SameFile(pathfi, linkfi) {
203 t.Errorf("os.Stat(%q) and os.Lstat(%q) are the same file", path, link)
204 }
205 }
206
207 func testSymlinkSameFileOpen(t *testing.T, link string) {
208 f, err := os.Open(link)
209 if err != nil {
210 t.Error(err)
211 return
212 }
213 defer f.Close()
214
215 fi, err := f.Stat()
216 if err != nil {
217 t.Error(err)
218 return
219 }
220
221 fi2, err := os.Stat(link)
222 if err != nil {
223 t.Error(err)
224 return
225 }
226
227 if !os.SameFile(fi, fi2) {
228 t.Errorf("os.Open(%q).Stat() and os.Stat(%q) are not the same file", link, link)
229 }
230 }
231
232 func TestDirAndSymlinkStats(t *testing.T) {
233 testenv.MustHaveSymlink(t)
234 t.Parallel()
235
236 tmpdir := t.TempDir()
237 dir := filepath.Join(tmpdir, "dir")
238 if err := os.Mkdir(dir, 0777); err != nil {
239 t.Fatal(err)
240 }
241 testDirStats(t, dir)
242
243 dirlink := filepath.Join(tmpdir, "link")
244 if err := os.Symlink(dir, dirlink); err != nil {
245 t.Fatal(err)
246 }
247 testSymlinkStats(t, dirlink, true)
248 testSymlinkSameFile(t, dir, dirlink)
249 testSymlinkSameFileOpen(t, dirlink)
250
251 linklink := filepath.Join(tmpdir, "linklink")
252 if err := os.Symlink(dirlink, linklink); err != nil {
253 t.Fatal(err)
254 }
255 testSymlinkStats(t, linklink, true)
256 testSymlinkSameFile(t, dir, linklink)
257 testSymlinkSameFileOpen(t, linklink)
258 }
259
260 func TestFileAndSymlinkStats(t *testing.T) {
261 testenv.MustHaveSymlink(t)
262 t.Parallel()
263
264 tmpdir := t.TempDir()
265 file := filepath.Join(tmpdir, "file")
266 if err := os.WriteFile(file, []byte(""), 0644); err != nil {
267 t.Fatal(err)
268 }
269 testFileStats(t, file)
270
271 filelink := filepath.Join(tmpdir, "link")
272 if err := os.Symlink(file, filelink); err != nil {
273 t.Fatal(err)
274 }
275 testSymlinkStats(t, filelink, false)
276 testSymlinkSameFile(t, file, filelink)
277 testSymlinkSameFileOpen(t, filelink)
278
279 linklink := filepath.Join(tmpdir, "linklink")
280 if err := os.Symlink(filelink, linklink); err != nil {
281 t.Fatal(err)
282 }
283 testSymlinkStats(t, linklink, false)
284 testSymlinkSameFile(t, file, linklink)
285 testSymlinkSameFileOpen(t, linklink)
286 }
287
288
289 func TestSymlinkWithTrailingSlash(t *testing.T) {
290 testenv.MustHaveSymlink(t)
291 t.Parallel()
292
293 tmpdir := t.TempDir()
294 dir := filepath.Join(tmpdir, "dir")
295 if err := os.Mkdir(dir, 0777); err != nil {
296 t.Fatal(err)
297 }
298 dirlink := filepath.Join(tmpdir, "link")
299 if err := os.Symlink(dir, dirlink); err != nil {
300 t.Fatal(err)
301 }
302 dirlinkWithSlash := dirlink + string(os.PathSeparator)
303
304 testDirStats(t, dirlinkWithSlash)
305
306 fi1, err := os.Stat(dir)
307 if err != nil {
308 t.Error(err)
309 return
310 }
311 fi2, err := os.Stat(dirlinkWithSlash)
312 if err != nil {
313 t.Error(err)
314 return
315 }
316 if !os.SameFile(fi1, fi2) {
317 t.Errorf("os.Stat(%q) and os.Stat(%q) are not the same file", dir, dirlinkWithSlash)
318 }
319 }
320
321 func TestStatConsole(t *testing.T) {
322 if runtime.GOOS != "windows" {
323 t.Skip("skipping on non-Windows")
324 }
325 t.Parallel()
326 consoleNames := []string{
327 "CONIN$",
328 "CONOUT$",
329 "CON",
330 }
331 for _, name := range consoleNames {
332 params := testStatAndLstatParams{
333 isLink: false,
334 statCheck: testIsFile,
335 lstatCheck: testIsFile,
336 }
337 testStatAndLstat(t, name, params)
338 testStatAndLstat(t, `\\.\`+name, params)
339 }
340 }
341
342 func TestClosedStat(t *testing.T) {
343
344 switch runtime.GOOS {
345 case "windows", "plan9":
346 t.Skipf("skipping on %s", runtime.GOOS)
347 }
348
349 t.Parallel()
350 f, err := os.Open("testdata/hello")
351 if err != nil {
352 t.Fatal(err)
353 }
354 if err := f.Close(); err != nil {
355 t.Fatal(err)
356 }
357 _, err = f.Stat()
358 if err == nil {
359 t.Error("Stat succeeded on closed File")
360 } else if !errors.Is(err, os.ErrClosed) {
361 t.Errorf("error from Stat on closed file did not match ErrClosed: %q, type %T", err, err)
362 }
363 }
364
View as plain text