github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/tests/system/parallel-runner.go (about)

     1  package main
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"math/rand"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"time"
    11  )
    12  
    13  var shuffle bool
    14  var failFast bool
    15  var nb int
    16  
    17  func main() {
    18  	flag.BoolVar(&shuffle, "shuffle", false, "Randomize the order of the tests")
    19  	flag.BoolVar(&failFast, "fail-fast", false, "Stop on the first test that fails")
    20  	flag.IntVar(&nb, "n", 4, "Number of tests to run in parallel")
    21  	flag.Parse()
    22  
    23  	tests, err := listTests()
    24  	if err != nil {
    25  		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
    26  		os.Exit(1)
    27  	}
    28  	if err := runTests(tests); err != nil {
    29  		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
    30  		os.Exit(1)
    31  	}
    32  }
    33  
    34  func listTests() ([]string, error) {
    35  	tests, err := filepath.Glob("tests/*.rb")
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	if shuffle {
    40  		rand.Shuffle(len(tests), func(i, j int) {
    41  			tests[i], tests[j] = tests[j], tests[i]
    42  		})
    43  	}
    44  	return tests, nil
    45  }
    46  
    47  type result struct {
    48  	Test string
    49  	Err  error
    50  	Out  []byte
    51  }
    52  
    53  func runTests(tests []string) error {
    54  	results := make(chan result, len(tests))
    55  	tokens := make(chan struct{}, nb)
    56  	for i := 0; i < nb; i++ {
    57  		tokens <- struct{}{}
    58  	}
    59  
    60  	for i, test := range tests {
    61  		go func(i int, test string) {
    62  			k := <-tokens
    63  			cmd := exec.Command("bundle", "exec", "ruby", test)
    64  			cmd.Env = append(os.Environ(),
    65  				fmt.Sprintf("COZY_BASE_PORT=%d", 8091+10*i),
    66  				"COZY_SKIP_LOADING_TRIGGERS=true",
    67  				"CI=true")
    68  			out, err := cmd.CombinedOutput()
    69  			results <- result{test, err, out}
    70  			tokens <- k
    71  		}(i, test)
    72  
    73  		// Starting all the tests at the same time is not a good idea, the
    74  		// stack can create conflicts on the global databases.
    75  		time.Sleep(3 * time.Second)
    76  	}
    77  
    78  	var err error
    79  	for range tests {
    80  		res := <-results
    81  		fmt.Fprintf(os.Stderr, "\n==== Run %s ====\n%s\n", res.Test, res.Out)
    82  		if res.Err != nil {
    83  			err = res.Err
    84  			if failFast {
    85  				return err
    86  			}
    87  		}
    88  	}
    89  
    90  	return err
    91  }