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  }