github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fstest/test_all/test_all.go (about)

     1  // Run tests for all the remotes.  Run this with package names which
     2  // need integration testing.
     3  //
     4  // See the `test` target in the Makefile.
     5  package main
     6  
     7  /* FIXME
     8  
     9  Make TesTrun have a []string of flags to try - that then makes it generic
    10  
    11  */
    12  
    13  import (
    14  	"flag"
    15  	"log"
    16  	"math/rand"
    17  	"os"
    18  	"path"
    19  	"regexp"
    20  	"strings"
    21  	"time"
    22  
    23  	_ "github.com/rclone/rclone/backend/all" // import all fs
    24  	"github.com/rclone/rclone/fs/config/configfile"
    25  	"github.com/rclone/rclone/lib/pacer"
    26  )
    27  
    28  var (
    29  	// Flags
    30  	maxTries     = flag.Int("maxtries", 5, "Number of times to try each test")
    31  	maxN         = flag.Int("n", 20, "Maximum number of tests to run at once")
    32  	testRemotes  = flag.String("remotes", "", "Comma separated list of remotes to test, e.g. 'TestSwift:,TestS3'")
    33  	testBackends = flag.String("backends", "", "Comma separated list of backends to test, e.g. 's3,googlecloudstorage")
    34  	testTests    = flag.String("tests", "", "Comma separated list of tests to test, e.g. 'fs/sync,fs/operations'")
    35  	clean        = flag.Bool("clean", false, "Instead of testing, clean all left over test directories")
    36  	runOnly      = flag.String("run", "", "Run only those tests matching the regexp supplied")
    37  	timeout      = flag.Duration("timeout", 60*time.Minute, "Maximum time to run each test for before giving up")
    38  	race         = flag.Bool("race", false, "If set run the tests under the race detector")
    39  	configFile   = flag.String("config", "fstest/test_all/config.yaml", "Path to config file")
    40  	outputDir    = flag.String("output", path.Join(os.TempDir(), "rclone-integration-tests"), "Place to store results")
    41  	emailReport  = flag.String("email", "", "Set to email the report to the address supplied")
    42  	dryRun       = flag.Bool("dry-run", false, "Print commands which would be executed only")
    43  	urlBase      = flag.String("url-base", "https://pub.rclone.org/integration-tests/", "Base for the online version")
    44  	uploadPath   = flag.String("upload", "", "Set this to an rclone path to upload the results here")
    45  	verbose      = flag.Bool("verbose", false, "Set to enable verbose logging in the tests")
    46  	listRetries  = flag.Int("list-retries", -1, "Number or times to retry listing - set to override the default")
    47  )
    48  
    49  // if matches then is definitely OK in the shell
    50  var shellOK = regexp.MustCompile("^[A-Za-z0-9./_:-]+$")
    51  
    52  // converts an argv style input into a shell command
    53  func toShell(args []string) (result string) {
    54  	for _, arg := range args {
    55  		if result != "" {
    56  			result += " "
    57  		}
    58  		if shellOK.MatchString(arg) {
    59  			result += arg
    60  		} else {
    61  			result += "'" + arg + "'"
    62  		}
    63  	}
    64  	return result
    65  }
    66  
    67  func main() {
    68  	flag.Parse()
    69  	conf, err := NewConfig(*configFile)
    70  	if err != nil {
    71  		log.Println("test_all should be run from the root of the rclone source code")
    72  		log.Fatal(err)
    73  	}
    74  	configfile.Install()
    75  
    76  	// Seed the random number generator
    77  	randInstance := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
    78  
    79  	// Filter selection
    80  	if *testRemotes != "" {
    81  		conf.filterBackendsByRemotes(strings.Split(*testRemotes, ","))
    82  	}
    83  	if *testBackends != "" {
    84  		conf.filterBackendsByBackends(strings.Split(*testBackends, ","))
    85  	}
    86  	if *testTests != "" {
    87  		conf.filterTests(strings.Split(*testTests, ","))
    88  	}
    89  
    90  	// Just clean the directories if required
    91  	if *clean {
    92  		err := cleanRemotes(conf)
    93  		if err != nil {
    94  			log.Fatalf("Failed to clean: %v", err)
    95  		}
    96  		return
    97  	}
    98  
    99  	var names []string
   100  	for _, remote := range conf.Backends {
   101  		names = append(names, remote.Remote)
   102  	}
   103  	log.Printf("Testing remotes: %s", strings.Join(names, ", "))
   104  
   105  	// Runs we will do for this test in random order
   106  	runs := conf.MakeRuns()
   107  	randInstance.Shuffle(len(runs), runs.Swap)
   108  
   109  	// Create Report
   110  	report := NewReport()
   111  
   112  	// Make the test binaries, one per Path found in the tests
   113  	done := map[string]struct{}{}
   114  	for _, run := range runs {
   115  		if _, found := done[run.Path]; !found {
   116  			done[run.Path] = struct{}{}
   117  			if !run.NoBinary {
   118  				run.MakeTestBinary()
   119  				defer run.RemoveTestBinary()
   120  			}
   121  		}
   122  	}
   123  
   124  	// workaround for cache backend as we run simultaneous tests
   125  	_ = os.Setenv("RCLONE_CACHE_DB_WAIT_TIME", "30m")
   126  
   127  	// start the tests
   128  	results := make(chan *Run, len(runs))
   129  	awaiting := 0
   130  	tokens := pacer.NewTokenDispenser(*maxN)
   131  	for _, run := range runs {
   132  		tokens.Get()
   133  		go func(run *Run) {
   134  			defer tokens.Put()
   135  			run.Run(report.LogDir, results)
   136  		}(run)
   137  		awaiting++
   138  	}
   139  
   140  	// Wait for the tests to finish
   141  	for ; awaiting > 0; awaiting-- {
   142  		t := <-results
   143  		report.RecordResult(t)
   144  	}
   145  
   146  	// Log and exit
   147  	report.End()
   148  	report.LogSummary()
   149  	report.LogJSON()
   150  	report.LogHTML()
   151  	report.EmailHTML()
   152  	report.Upload()
   153  	if !report.AllPassed() {
   154  		os.Exit(1)
   155  	}
   156  }