github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/vfs/test_vfs/test_vfs.go (about) 1 // Test the VFS to exhaustion, specifically looking for deadlocks 2 // 3 // Run on a mounted filesystem 4 package main 5 6 import ( 7 "flag" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "log" 12 "math" 13 "math/rand" 14 "os" 15 "path" 16 "sync" 17 "sync/atomic" 18 "time" 19 20 "github.com/ncw/rclone/lib/file" 21 ) 22 23 var ( 24 nameLength = flag.Int("name-length", 10, "Length of names to create") 25 verbose = flag.Bool("v", false, "Set to show more info") 26 number = flag.Int("n", 4, "Number of tests to run simultaneously") 27 iterations = flag.Int("i", 100, "Iterations of the test") 28 timeout = flag.Duration("timeout", 10*time.Second, "Inactivity time to detect a deadlock") 29 testNumber int32 30 ) 31 32 // Seed the random number generator 33 func init() { 34 rand.Seed(time.Now().UnixNano()) 35 36 } 37 38 // RandomString create a random string for test purposes 39 func RandomString(n int) string { 40 const ( 41 vowel = "aeiou" 42 consonant = "bcdfghjklmnpqrstvwxyz" 43 digit = "0123456789" 44 ) 45 pattern := []string{consonant, vowel, consonant, vowel, consonant, vowel, consonant, digit} 46 out := make([]byte, n) 47 p := 0 48 for i := range out { 49 source := pattern[p] 50 p = (p + 1) % len(pattern) 51 out[i] = source[rand.Intn(len(source))] 52 } 53 return string(out) 54 } 55 56 // Test contains stats about the running test which work for files or 57 // directories 58 type Test struct { 59 dir string 60 name string 61 created bool 62 handle *os.File 63 tests []func() 64 isDir bool 65 number int32 66 prefix string 67 timer *time.Timer 68 } 69 70 // NewTest creates a new test and fills in the Tests 71 func NewTest(Dir string) *Test { 72 t := &Test{ 73 dir: Dir, 74 name: RandomString(*nameLength), 75 isDir: rand.Intn(2) == 0, 76 number: atomic.AddInt32(&testNumber, 1), 77 timer: time.NewTimer(*timeout), 78 } 79 width := int(math.Floor(math.Log10(float64(*number)))) + 1 80 t.prefix = fmt.Sprintf("%*d: %s: ", width, t.number, t.path()) 81 if t.isDir { 82 t.tests = []func(){ 83 t.list, 84 t.rename, 85 t.mkdir, 86 t.rmdir, 87 } 88 } else { 89 t.tests = []func(){ 90 t.list, 91 t.rename, 92 t.open, 93 t.close, 94 t.remove, 95 t.read, 96 t.write, 97 } 98 } 99 return t 100 } 101 102 // kick the deadlock timeout 103 func (t *Test) kick() { 104 if !t.timer.Stop() { 105 <-t.timer.C 106 } 107 t.timer.Reset(*timeout) 108 } 109 110 // randomTest runs a random test 111 func (t *Test) randomTest() { 112 t.kick() 113 i := rand.Intn(len(t.tests)) 114 t.tests[i]() 115 } 116 117 // logf logs things - not shown unless -v 118 func (t *Test) logf(format string, a ...interface{}) { 119 if *verbose { 120 log.Printf(t.prefix+format, a...) 121 } 122 } 123 124 // errorf logs errors 125 func (t *Test) errorf(format string, a ...interface{}) { 126 log.Printf(t.prefix+"ERROR: "+format, a...) 127 } 128 129 // list test 130 func (t *Test) list() { 131 t.logf("list") 132 fis, err := ioutil.ReadDir(t.dir) 133 if err != nil { 134 t.errorf("%s: failed to read directory: %v", t.dir, err) 135 return 136 } 137 if t.created && len(fis) == 0 { 138 t.errorf("%s: expecting entries in directory, got none", t.dir) 139 return 140 } 141 found := false 142 for _, fi := range fis { 143 if fi.Name() == t.name { 144 found = true 145 } 146 } 147 if t.created { 148 if !found { 149 t.errorf("%s: expecting to find %q in directory, got none", t.dir, t.name) 150 return 151 } 152 } else { 153 if found { 154 t.errorf("%s: not expecting to find %q in directory, got none", t.dir, t.name) 155 return 156 } 157 } 158 } 159 160 // path returns the current path to the item 161 func (t *Test) path() string { 162 return path.Join(t.dir, t.name) 163 } 164 165 // rename test 166 func (t *Test) rename() { 167 if !t.created { 168 return 169 } 170 t.logf("rename") 171 NewName := RandomString(*nameLength) 172 newPath := path.Join(t.dir, NewName) 173 err := os.Rename(t.path(), newPath) 174 if err != nil { 175 t.errorf("failed to rename to %q: %v", newPath, err) 176 return 177 } 178 t.name = NewName 179 } 180 181 // close test 182 func (t *Test) close() { 183 if t.handle == nil { 184 return 185 } 186 t.logf("close") 187 err := t.handle.Close() 188 t.handle = nil 189 if err != nil { 190 t.errorf("failed to close: %v", err) 191 return 192 } 193 } 194 195 // open test 196 func (t *Test) open() { 197 t.close() 198 t.logf("open") 199 handle, err := file.OpenFile(t.path(), os.O_RDWR|os.O_CREATE, 0666) 200 if err != nil { 201 t.errorf("failed to open: %v", err) 202 return 203 } 204 t.handle = handle 205 t.created = true 206 } 207 208 // read test 209 func (t *Test) read() { 210 if t.handle == nil { 211 return 212 } 213 t.logf("read") 214 bytes := make([]byte, 10) 215 _, err := t.handle.Read(bytes) 216 if err != nil && err != io.EOF { 217 t.errorf("failed to read: %v", err) 218 return 219 } 220 } 221 222 // write test 223 func (t *Test) write() { 224 if t.handle == nil { 225 return 226 } 227 t.logf("write") 228 bytes := make([]byte, 10) 229 _, err := t.handle.Write(bytes) 230 if err != nil { 231 t.errorf("failed to write: %v", err) 232 return 233 } 234 } 235 236 // remove test 237 func (t *Test) remove() { 238 if !t.created { 239 return 240 } 241 t.logf("remove") 242 err := os.Remove(t.path()) 243 if err != nil { 244 t.errorf("failed to remove: %v", err) 245 return 246 } 247 t.created = false 248 } 249 250 // mkdir test 251 func (t *Test) mkdir() { 252 if t.created { 253 return 254 } 255 t.logf("mkdir") 256 err := os.Mkdir(t.path(), 0777) 257 if err != nil { 258 t.errorf("failed to mkdir %q", t.path()) 259 return 260 } 261 t.created = true 262 } 263 264 // rmdir test 265 func (t *Test) rmdir() { 266 if !t.created { 267 return 268 } 269 t.logf("rmdir") 270 err := os.Remove(t.path()) 271 if err != nil { 272 t.errorf("failed to rmdir %q", t.path()) 273 return 274 } 275 t.created = false 276 } 277 278 // Tidy removes any stray files and stops the deadlock timer 279 func (t *Test) Tidy() { 280 t.timer.Stop() 281 if !t.isDir { 282 t.close() 283 t.remove() 284 } else { 285 t.rmdir() 286 } 287 t.logf("finished") 288 } 289 290 // RandomTests runs random tests with deadlock detection 291 func (t *Test) RandomTests(iterations int, quit chan struct{}) { 292 var finished = make(chan struct{}) 293 go func() { 294 for i := 0; i < iterations; i++ { 295 t.randomTest() 296 } 297 close(finished) 298 }() 299 select { 300 case <-finished: 301 case <-quit: 302 quit <- struct{}{} 303 case <-t.timer.C: 304 t.errorf("deadlock detected") 305 quit <- struct{}{} 306 } 307 } 308 309 func main() { 310 flag.Parse() 311 args := flag.Args() 312 if len(args) != 1 { 313 log.Fatalf("%s: Syntax [opts] <directory>", os.Args[0]) 314 } 315 dir := args[0] 316 _ = os.MkdirAll(dir, 0777) 317 318 var ( 319 wg sync.WaitGroup 320 quit = make(chan struct{}, *iterations) 321 ) 322 for i := 0; i < *number; i++ { 323 wg.Add(1) 324 go func() { 325 defer wg.Done() 326 t := NewTest(dir) 327 defer t.Tidy() 328 t.RandomTests(*iterations, quit) 329 }() 330 } 331 wg.Wait() 332 }