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  }