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