github.com/avfs/avfs@v0.33.1-0.20240303173310-c6ba67c33eb7/test/test_race.go (about) 1 // 2 // Copyright 2020 The AVFS authors 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 package test 18 19 import ( 20 "os" 21 "sync" 22 "sync/atomic" 23 "testing" 24 25 "github.com/avfs/avfs" 26 ) 27 28 // TestRace tests data race conditions. 29 func (ts *Suite) TestRace(t *testing.T) { 30 vfs := ts.vfsTest 31 32 if vfs.OSType() != avfs.CurrentOSType() { 33 t.Skipf("TestRace : Current OSType = %s is different from %s OSType = %s, skipping race tests", 34 avfs.CurrentOSType(), vfs.Type(), vfs.OSType()) 35 } 36 37 ts.RunTests(t, UsrTest, 38 ts.RaceCreate, 39 ts.RaceCreateTemp, 40 ts.RaceFileClose, 41 ts.RaceMkdir, 42 ts.RaceMkdirAll, 43 ts.RaceMkdirTemp, 44 ts.RaceOpen, 45 ts.RaceOpenFile, 46 ts.RaceOpenFileExcl, 47 ts.RaceRemove, 48 ts.RaceRemoveAll, 49 ts.RaceMkdirRemoveAll) 50 } 51 52 // RaceCreate tests data race conditions for Create. 53 func (ts *Suite) RaceCreate(t *testing.T, testDir string) { 54 vfs := ts.vfsTest 55 56 ts.raceFunc(t, RaceAllOk, func() error { 57 newFile := vfs.Join(testDir, defaultFile) 58 59 f, err := vfs.Create(newFile) 60 if err == nil { 61 defer f.Close() 62 } 63 64 return err 65 }) 66 } 67 68 // RaceCreateTemp tests data race conditions for CreateTemp. 69 func (ts *Suite) RaceCreateTemp(t *testing.T, testDir string) { 70 vfs := ts.vfsTest 71 72 var fileNames sync.Map 73 74 ts.raceFunc(t, RaceAllOk, func() error { 75 fileName, err := vfs.CreateTemp(testDir, "avfs") 76 77 _, exists := fileNames.LoadOrStore(fileName, nil) 78 if exists { 79 t.Errorf("file %s already exists", fileName) 80 } 81 82 return err 83 }) 84 } 85 86 // RaceMkdir tests data race conditions for Mkdir. 87 func (ts *Suite) RaceMkdir(t *testing.T, testDir string) { 88 vfs := ts.vfsTest 89 path := vfs.Join(testDir, defaultDir) 90 91 ts.raceFunc(t, RaceOneOk, func() error { 92 return vfs.Mkdir(path, avfs.DefaultDirPerm) 93 }) 94 } 95 96 // RaceMkdirAll tests data race conditions for MkdirAll. 97 func (ts *Suite) RaceMkdirAll(t *testing.T, testDir string) { 98 vfs := ts.vfsTest 99 path := vfs.Join(testDir, defaultDir) 100 101 ts.raceFunc(t, RaceAllOk, func() error { 102 return vfs.MkdirAll(path, avfs.DefaultDirPerm) 103 }) 104 } 105 106 // RaceMkdirTemp tests data race conditions for MkdirTemp. 107 func (ts *Suite) RaceMkdirTemp(t *testing.T, testDir string) { 108 vfs := ts.vfsTest 109 110 var dirs sync.Map 111 112 ts.raceFunc(t, RaceAllOk, func() error { 113 dir, err := vfs.MkdirTemp(testDir, "RaceMkdirTemp") 114 115 _, exists := dirs.LoadOrStore(dir, nil) 116 if exists { 117 t.Errorf("directory %s already exists", dir) 118 } 119 120 return err 121 }) 122 } 123 124 // RaceOpen tests data race conditions for Open. 125 func (ts *Suite) RaceOpen(t *testing.T, testDir string) { 126 vfs := ts.vfsTest 127 roFile := ts.emptyFile(t, testDir) 128 129 ts.raceFunc(t, RaceAllOk, func() error { 130 f, err := vfs.OpenFile(roFile, os.O_RDONLY, 0) 131 if err == nil { 132 defer f.Close() 133 } 134 135 return err 136 }) 137 } 138 139 // RaceOpenFile tests data race conditions for OpenFile. 140 func (ts *Suite) RaceOpenFile(t *testing.T, testDir string) { 141 vfs := ts.vfsTest 142 newFile := vfs.Join(testDir, defaultFile) 143 144 ts.raceFunc(t, RaceAllOk, func() error { 145 f, err := vfs.OpenFile(newFile, os.O_RDWR|os.O_CREATE, avfs.DefaultFilePerm) 146 if err == nil { 147 defer f.Close() 148 } 149 150 return err 151 }) 152 } 153 154 // RaceOpenFileExcl tests data race conditions for OpenFile with O_EXCL flag. 155 func (ts *Suite) RaceOpenFileExcl(t *testing.T, testDir string) { 156 vfs := ts.vfsTest 157 newFile := vfs.Join(testDir, defaultFile) 158 159 ts.raceFunc(t, RaceOneOk, func() error { 160 f, err := vfs.OpenFile(newFile, os.O_RDWR|os.O_CREATE|os.O_EXCL, avfs.DefaultFilePerm) 161 if err == nil { 162 defer f.Close() 163 } 164 165 return err 166 }) 167 } 168 169 // RaceRemove tests data race conditions for Remove. 170 func (ts *Suite) RaceRemove(t *testing.T, testDir string) { 171 vfs := ts.vfsTest 172 path := vfs.Join(testDir, defaultDir) 173 174 ts.createDir(t, path, avfs.DefaultDirPerm) 175 176 ts.raceFunc(t, RaceUndefined, func() error { 177 return vfs.Remove(path) 178 }) 179 } 180 181 // RaceRemoveAll tests data race conditions for RemoveAll. 182 func (ts *Suite) RaceRemoveAll(t *testing.T, testDir string) { 183 vfs := ts.vfsTest 184 path := vfs.Join(testDir, defaultDir) 185 186 ts.createDir(t, path, avfs.DefaultDirPerm) 187 188 ts.raceFunc(t, RaceAllOk, func() error { 189 return vfs.RemoveAll(path) 190 }) 191 } 192 193 // RaceFileClose tests data race conditions for File.Close. 194 func (ts *Suite) RaceFileClose(t *testing.T, testDir string) { 195 f, _ := ts.openedEmptyFile(t, testDir) 196 197 ts.raceFunc(t, RaceOneOk, f.Close) 198 } 199 200 // RaceMkdirRemoveAll test data race conditions for MkdirAll and RemoveAll. 201 func (ts *Suite) RaceMkdirRemoveAll(t *testing.T, testDir string) { 202 vfs := ts.vfsTest 203 204 path := vfs.Join(testDir, "new/path/to/test") 205 206 ts.raceFunc(t, RaceUndefined, func() error { 207 return vfs.MkdirAll(path, avfs.DefaultDirPerm) 208 }, func() error { 209 return vfs.RemoveAll(path) 210 }) 211 } 212 213 // RaceResult defines the type of result expected from a race test. 214 type RaceResult uint8 215 216 const ( 217 // RaceNoneOk expects that all the results will return an error. 218 RaceNoneOk RaceResult = iota + 1 219 220 // RaceOneOk expects that only one result will be without error. 221 RaceOneOk 222 223 // RaceAllOk expects that all results will be without error. 224 RaceAllOk 225 226 // RaceUndefined does not expect anything (unpredictable results). 227 RaceUndefined 228 ) 229 230 // raceFunc tests data race conditions by running simultaneously all testFuncs in Suite.maxRace goroutines 231 // and expecting a result rr. 232 func (ts *Suite) raceFunc(t *testing.T, rr RaceResult, testFuncs ...func() error) { 233 var ( 234 wgSetup sync.WaitGroup 235 wgTeardown sync.WaitGroup 236 starter sync.RWMutex 237 wantOk uint32 238 gotOk uint32 239 wantErr uint32 240 gotErr uint32 241 ) 242 243 maxGo := ts.maxRace * len(testFuncs) 244 245 wgSetup.Add(maxGo) 246 wgTeardown.Add(maxGo) 247 248 starter.Lock() 249 250 for i := 0; i < ts.maxRace; i++ { 251 for _, testFunc := range testFuncs { 252 go func(f func() error) { 253 defer func() { 254 starter.RUnlock() 255 wgTeardown.Done() 256 }() 257 258 wgSetup.Done() 259 starter.RLock() 260 261 err := f() 262 if err != nil { 263 atomic.AddUint32(&gotErr, 1) 264 265 return 266 } 267 268 atomic.AddUint32(&gotOk, 1) 269 }(testFunc) 270 } 271 } 272 273 // All goroutines wait for the Starter lock. 274 wgSetup.Wait() 275 276 // All goroutines execute the testFuncs. 277 starter.Unlock() 278 279 // Wait for all goroutines to stop. 280 wgTeardown.Wait() 281 282 switch rr { 283 case RaceNoneOk: 284 wantOk = 0 285 case RaceOneOk: 286 wantOk = 1 287 case RaceAllOk: 288 wantOk = uint32(maxGo) 289 case RaceUndefined: 290 t.Logf("ok = %d, error = %d", gotOk, gotErr) 291 292 return 293 } 294 295 wantErr = uint32(maxGo) - wantOk 296 297 if gotOk != wantOk { 298 t.Errorf("want number of responses without error to be %d, got %d ", wantOk, gotOk) 299 } 300 301 if gotErr != wantErr { 302 t.Errorf("want number of responses with errors to be %d, got %d", wantErr, gotErr) 303 } 304 }