testwrapper.go 1.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // testwrapper is a wrapper for retrying flaky tests, using the -exec flag of
  4. // 'go test'. Tests that are flaky can use the 'flakytest' subpackage to mark
  5. // themselves as flaky and be retried on failure.
  6. package main
  7. import (
  8. "context"
  9. "errors"
  10. "log"
  11. "os"
  12. "os/exec"
  13. )
  14. const (
  15. retryStatus = 123
  16. maxIterations = 3
  17. )
  18. func main() {
  19. ctx := context.Background()
  20. debug := os.Getenv("TS_TESTWRAPPER_DEBUG") != ""
  21. log.SetPrefix("testwrapper: ")
  22. if !debug {
  23. log.SetFlags(0)
  24. }
  25. for i := 1; i <= maxIterations; i++ {
  26. if i > 1 {
  27. log.Printf("retrying flaky tests (%d of %d)", i, maxIterations)
  28. }
  29. cmd := exec.CommandContext(ctx, os.Args[1], os.Args[2:]...)
  30. cmd.Stdout = os.Stdout
  31. cmd.Stderr = os.Stderr
  32. cmd.Env = append(os.Environ(), "TS_IN_TESTWRAPPER=1")
  33. err := cmd.Run()
  34. if err == nil {
  35. return
  36. }
  37. var exitErr *exec.ExitError
  38. if !errors.As(err, &exitErr) {
  39. if debug {
  40. log.Printf("error isn't an ExitError")
  41. }
  42. os.Exit(1)
  43. }
  44. if code := exitErr.ExitCode(); code != retryStatus {
  45. if debug {
  46. log.Printf("code (%d) != retryStatus (%d)", code, retryStatus)
  47. }
  48. os.Exit(code)
  49. }
  50. }
  51. log.Printf("test did not pass in %d iterations", maxIterations)
  52. os.Exit(1)
  53. }