From ddf39f2d57e38cb30e1d6e0be1638e3e4bb4946b Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Wed, 30 Dec 2015 00:18:31 +0000 Subject: [PATCH] Replace test_all.sh with test_all.go which is cross platform and parallel --- CONTRIBUTING.md | 2 +- Makefile | 2 +- fs/test_all.go | 159 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/test_all.sh | 32 ---------- notes.txt | 33 +--------- 5 files changed, 162 insertions(+), 66 deletions(-) create mode 100644 fs/test_all.go delete mode 100755 fs/test_all.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c2f6f823f..6b6be576e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -105,7 +105,7 @@ but they can be run against any of the remotes. If you want to run all the integration tests against all the remotes, then run in that directory - ./test_all.sh + go run test_all.go ## Making a release ## diff --git a/Makefile b/Makefile index 863da7dd6..ab18e6deb 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ rclone: test: rclone go test ./... - cd fs && ./test_all.sh + cd fs && go run test_all.go check: rclone go vet ./... diff --git a/fs/test_all.go b/fs/test_all.go new file mode 100644 index 000000000..a80630d04 --- /dev/null +++ b/fs/test_all.go @@ -0,0 +1,159 @@ +// +build ignore + +// Run tests for all the remotes +// +// Run with go run test_all.go +package main + +import ( + "flag" + "log" + "os" + "os/exec" + "runtime" + "strings" + "time" +) + +var ( + remotes = []string{ + "TestSwift:", + "TestS3:", + "TestDrive:", + "TestGoogleCloudStorage:", + "TestDropbox:", + "TestAmazonCloudDrive:", + "TestOneDrive:", + "TestHubic:", + } + binary = "fs.test" + // Flags + maxTries = flag.Int("maxtries", 3, "Number of times to try each test") + runTests = flag.String("run", "", "Comma separated list of remotes to test, eg 'TestSwift:,TestS3'") +) + +// test holds info about a running test +type test struct { + remote string + subdir bool + cmdLine []string + cmdString string + try int + err error + output []byte +} + +// newTest creates a new test +func newTest(remote string, subdir bool) *test { + t := &test{ + remote: remote, + subdir: subdir, + cmdLine: []string{"./" + binary, "-test.v", "-remote", remote}, + try: 1, + } + if subdir { + t.cmdLine = append(t.cmdLine, "-subdir") + } + t.cmdString = strings.Join(t.cmdLine, " ") + return t +} + +// trial runs a single test +func (t *test) trial() { + log.Printf("%q - Starting (try %d/%d)", t.cmdString, t.try, *maxTries) + cmd := exec.Command(t.cmdLine[0], t.cmdLine[1:]...) + start := time.Now() + t.output, t.err = cmd.CombinedOutput() + duration := time.Since(start) + if t.passed() { + log.Printf("%q - Finished OK in %v (try %d/%d)", t.cmdString, duration, t.try, *maxTries) + } else { + log.Printf("%q - Finished ERROR in %v (try %d/%d): %v", t.cmdString, duration, t.try, *maxTries, t.err) + } +} + +// passed returns true if the test passed +func (t *test) passed() bool { + return t.err == nil +} + +// run runs all the trials for this test +func (t *test) run(result chan<- *test) { + for try := 1; try <= *maxTries; try++ { + t.trial() + if t.passed() { + break + } + } + if !t.passed() { + log.Println("------------------------------------------------------------") + log.Println(string(t.output)) + log.Println("------------------------------------------------------------") + } + result <- t +} + +// makeTestBinary makes the binary we will run +func makeTestBinary() { + if runtime.GOOS == "windows" { + binary += ".exe" + } + log.Printf("Making test binary %q", binary) + err := exec.Command("go", "test", "-c", "-o", binary).Run() + if err != nil { + log.Fatalf("Failed to make test binary: %v", err) + } + if _, err := os.Stat(binary); err != nil { + log.Fatalf("Couldn't find test binary %q", binary) + } +} + +// removeTestBinary removes the binary made in makeTestBinary +func removeTestBinary() { + err := os.Remove(binary) // Delete the binary when finished + if err != nil { + log.Printf("Error removing test binary %q: %v", binary, err) + } +} + +func main() { + flag.Parse() + if *runTests != "" { + remotes = strings.Split(*runTests, ",") + } + log.Printf("Testing remotes: %s", strings.Join(remotes, ", ")) + + start := time.Now() + makeTestBinary() + defer removeTestBinary() + + // start the tests + results := make(chan *test, 8) + awaiting := 0 + for _, remote := range remotes { + awaiting += 2 + go newTest(remote, false).run(results) + go newTest(remote, true).run(results) + } + + // Wait for the tests to finish + var failed []*test + for ; awaiting > 0; awaiting-- { + t := <-results + if !t.passed() { + failed = append(failed, t) + } + } + duration := time.Since(start) + + // Summarise results + if len(failed) == 0 { + log.Printf("PASS: All tests finished OK in %v", duration) + } else { + log.Printf("FAIL: %d tests failed in %v", len(failed), duration) + for _, t := range failed { + log.Printf(" * %s", t.cmdString) + } + os.Exit(1) + } +} diff --git a/fs/test_all.sh b/fs/test_all.sh deleted file mode 100755 index b9ad722d3..000000000 --- a/fs/test_all.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -go install - -REMOTES=" -TestSwift: -TestS3: -TestDrive: -TestGoogleCloudStorage: -TestDropbox: -TestAmazonCloudDrive: -TestOneDrive: -TestHubic: -" - -function test_remote { - args=$@ - echo "@go test $args" - go test $args || { - echo "*** test $args FAILED ***" - exit 1 - } -} - -test_remote -test_remote --subdir -for remote in $REMOTES; do - test_remote --remote $remote - test_remote --remote $remote --subdir -done - -echo "All OK" diff --git a/notes.txt b/notes.txt index 6817563a3..479f9d3cf 100644 --- a/notes.txt +++ b/notes.txt @@ -1,51 +1,20 @@ -Perhaps make Md5sum() and Modtime() optional. Define the zero values -"" and 0. Make it so we can support remotes which can't do those. - -Fix the docs - * factor the README.md into the docs directory - * create it as part of make by assembling other parts - * write long docs about each flag - Change lsd command so it doesn't show -1 * Make sure all Fses show -1 for objects Zero for dates etc * Make test? Put the TestRemote names into the Fs description -Make test_all.sh use the TestRemote name automatically - -Run errcheck and go vet in the make file -.. Also race detector? -.. go tool vet -shadow +Make fs/test_all.go use the TestRemote name automatically Get rid of Storable? -Write developer manual - Todo * FIXME: More -dry-run checks for object transfer * Might be quicker to check md5sums first? for swift <-> swift certainly, and maybe for small files - * swift: Ignoring the pseudo directories - * if object.PseudoDirectory { - * fmt.Printf("%9s %19s %s\n", "Directory", "-", fs.Remote()) - * Make Account wrapper - * make Account do progress meter - * -timeout: Make all timeouts be settable with command line parameters - * Add max object size to fs metadata - 5GB for swift, infinite for local, ? for s3 - * tie into -max-size flag * FIXME Make NewFs to return err.IsAnObject so can put the LimitedFs creation in common code? Or try for as much as possible? * FIXME Account all the transactons (ls etc) using a different Roundtripper wrapper which wraps the transactions? -More rsync features - * include - * exclude - * max size - * -c, --checksum skip based on checksum, not mod-time & size - -Ideas for flags - * --retries N flag which would make rclone retry a sync until successful or it tried N times. - Ideas * could do encryption - put IV into metadata? * optimise remote copy container to another container using remote