@@ -3,6 +3,8 @@ package iofs
33import (
44 "errors"
55 "io/fs"
6+ "path/filepath"
7+ "runtime"
68 "strings"
79 "testing"
810 "testing/fstest"
@@ -38,24 +40,7 @@ func TestWithFSTest(t *testing.T) {
3840
3941 err := fstest .TestFS (iofs , created_files ... )
4042 if err != nil {
41- if unwrapped := errors .Unwrap (err ); unwrapped != nil {
42- err = unwrapped
43- }
44- if errs , ok := err .(errorList ); ok {
45- for _ , e := range errs .Unwrap () {
46-
47- if strings .Contains (e .Error (), "ModTime" ) {
48- // Memfs returns the current time for Stat().ModTime(), which triggers
49- // a diff complaint in fstest. We can ignore this, or store modtimes
50- // for every file in Memfs (at a cost of 16 bytes / file).
51- t .Log ("Skipping ModTime error (ok)." )
52- } else {
53- t .Errorf ("Unexpected fstest error: %v" , e )
54- }
55- }
56- } else {
57- t .Fatalf ("Failed to test fs:\n %v" , err )
58- }
43+ checkFsTestError (t , err , files )
5944 }
6045}
6146
@@ -100,3 +85,64 @@ func makeFile(fs billyfs.Basic, t *testing.T, filename string, contents string)
10085 t .Fatalf ("failed to write to file %s: %v" , filename , err )
10186 }
10287}
88+
89+ func checkFsTestError (t * testing.T , err error , files map [string ]string ) {
90+ t .Helper ()
91+
92+ if unwrapped := errors .Unwrap (err ); unwrapped != nil {
93+ err = unwrapped
94+ }
95+
96+ // Go >= 1.23 (after https://cs.opensource.google/go/go/+/74cce866f865c3188a34309e4ebc7a5c9ed0683d)
97+ // has nicely-Joined wrapped errors. Try that first.
98+ if errs , ok := err .(errorList ); ok {
99+ for _ , e := range errs .Unwrap () {
100+
101+ if strings .Contains (e .Error (), "ModTime" ) {
102+ // Memfs returns the current time for Stat().ModTime(), which triggers
103+ // a diff complaint in fstest. We can ignore this, or store modtimes
104+ // for every file in Memfs (at a cost of 16 bytes / file).
105+ t .Log ("Skipping ModTime error (ok)." )
106+ } else {
107+ t .Errorf ("Unexpected fstest error: %v" , e )
108+ }
109+ }
110+ } else {
111+ if runtime .Version () >= "go1.23" {
112+ t .Fatalf ("Failed to test fs:\n %v" , err )
113+ }
114+ // filter lines from the error text corresponding to the above errors;
115+ // output looks like:
116+ // bar.txt: mismatch:
117+ // entry.Info() = bar.txt IsDir=false Mode=-rw-rw-rw- Size=14 ModTime=2024-09-17 10:09:00.377023639 +0000 UTC m=+0.002625548
118+ // file.Stat() = bar.txt IsDir=false Mode=-rw-rw-rw- Size=14 ModTime=2024-09-17 10:09:00.376907011 +0000 UTC m=+0.002508970
119+ //
120+ // bar.txt: fs.Stat(...) = bar.txt IsDir=false Mode=-rw-rw-rw- Size=14 ModTime=2024-09-17 10:09:00.381356651 +0000 UTC m=+0.006959191
121+ // want bar.txt IsDir=false Mode=-rw-rw-rw- Size=14 ModTime=2024-09-17 10:09:00.376907011 +0000 UTC m=+0.002508970
122+ // bar.txt: fsys.Stat(...) = bar.txt IsDir=false Mode=-rw-rw-rw- Size=14 ModTime=2024-09-17 10:09:00.381488617 +0000 UTC m=+0.007090346
123+ // want bar.txt IsDir=false Mode=-rw-rw-rw- Size=14 ModTime=2024-09-17 10:09:00.376907011 +0000 UTC m=+0.002508970
124+ // We filter on "empty line" or "ModTime" or "$filename: mismatch" to ignore these.
125+ lines := strings .Split (err .Error (), "\n " )
126+ filtered := make ([]string , 0 , len (lines ))
127+ filename_mismatches := make (map [string ]struct {}, len (files ) * 2 )
128+ for name , _ := range files {
129+ for dirname := name ; dirname != "." ; dirname = filepath .Dir (dirname ) {
130+ filename_mismatches [dirname + ": mismatch:" ] = struct {}{}
131+ }
132+ }
133+ for _ , line := range lines {
134+ line = strings .TrimSpace (line )
135+ if line == "" || strings .Contains (line , "ModTime=" ) {
136+ continue
137+ }
138+
139+ if _ , ok := filename_mismatches [line ]; ok {
140+ continue
141+ }
142+ filtered = append (filtered , line )
143+ }
144+ if len (filtered ) > 0 {
145+ t .Fatalf ("Failed to test fs:\n %s" , strings .Join (filtered , "\n " ))
146+ }
147+ }
148+ }
0 commit comments