github.com/quay/claircore@v1.5.28/test/integration/integration.go (about) 1 // Package integration is a helper for running integration tests. 2 package integration 3 4 import ( 5 "context" 6 "errors" 7 "io" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "sync" 12 "testing" 13 "time" 14 ) 15 16 // Skip will skip the current test or benchmark if this package was built without 17 // the "integration" build tag unless the "CI" environment variable is defined 18 // and the "short" flag is not provided. 19 // 20 // This should be used as an annotation at the top of the function, like 21 // (*testing.T).Parallel(). 22 // 23 // See the example for usage. 24 func Skip(t testing.TB) { 25 switch { 26 case testing.Short(): 27 t.Skip(`skipping integration test: short tests`) 28 case inCI && integrationTag: 29 t.Log(`enabling integration test: environment variable "CI" is defined`) 30 case integrationTag: 31 t.Skip("skipping integration test: integration tag not provided") 32 } 33 } 34 35 // Skip is an internal variant of [Skip] that reports the decision and 36 // doesn't log. 37 // 38 // Because this calls [testing.Short], it cannot be used in the [package block] 39 // or an init function. 40 // 41 // [package block]: https://go.dev/ref/spec#Declarations_and_scope 42 func skip() bool { 43 return testing.Short() || integrationTag && !inCI 44 } 45 46 var inCI, inGHA, externalDB bool 47 48 func init() { 49 _, inCI = os.LookupEnv("CI") 50 _, inGHA = os.LookupEnv("GITHUB_ACTIONS") 51 _, externalDB = os.LookupEnv(EnvPGConnString) 52 _, actuallyAct := os.LookupEnv("ACT") 53 54 inGHA = inGHA && !actuallyAct 55 } 56 57 // CacheDir reports a directory for caching test data and creates it if 58 // necessary. 59 func CacheDir(t testing.TB) string { 60 cacheOnce.Do(func() { 61 d, err := os.UserCacheDir() 62 if err != nil { 63 t.Fatalf("unable to determine test cache dir: %v", err) 64 } 65 if err := os.MkdirAll(d, 0o755); err != nil { 66 t.Fatalf("unable to create test cache dir: %v", err) 67 } 68 d = filepath.Join(d, `clair-testing`) 69 switch err := os.Mkdir(d, 0o755); { 70 case errors.Is(err, nil): // Make cachedir tag 71 p := filepath.Join(d, `CACHEDIR.TAG`) 72 f, err := os.Create(p) 73 if err != nil { 74 // If we can't create this file, we're going to have a hell of a 75 // time creating other ones. 76 t.Fatalf("tried to create %q but failed: %v", p, err) 77 } 78 defer f.Close() 79 if _, err := io.WriteString(f, cachedirtag); err != nil { 80 t.Logf("error writing %q contents: %v", p, err) 81 } 82 case errors.Is(err, os.ErrExist): // Pre-existing 83 default: 84 t.Fatalf("unable to create test cache dir: %v", err) 85 } 86 cacheDir = d 87 }) 88 if cacheDir == "" { 89 t.Fatal("test cache dir error, check previous tests") 90 } 91 return cacheDir 92 } 93 94 var ( 95 cacheOnce sync.Once 96 cacheDir string 97 ) 98 99 const cachedirtag = `Signature: 8a477f597d28d172789f06886806bc55 100 # This file is a cache directory tag created for "github.com/quay/claircore" test data. 101 # For information about cache directory tags, see: 102 # http://www.brynosaurus.com/cachedir/ 103 ` 104 105 // PackageCacheDir reports a directory for caching per-package test data and 106 // creates it if necessary. 107 func PackageCacheDir(t testing.TB) string { 108 pkgCacheOnce.Do(func() { 109 ctx := context.Background() 110 done := func() {} 111 if d, ok := t.(deadliner); ok { 112 if dl, ok := d.Deadline(); ok { 113 ctx, done = context.WithDeadline(ctx, dl) 114 } 115 // If the above is false, then the test explicitly asked for no 116 // timeout. 117 } else { 118 // Absurdly high timeout. Even higher than the previous 5 seconds. 119 ctx, done = context.WithTimeout(ctx, 60*time.Second) 120 } 121 defer done() 122 // This exec'ing is needed be cause test binaries are not built with 123 // full debug.BuildInfo filled out. 124 out, err := exec.CommandContext(ctx, `go`, `list`, `-m`).Output() 125 if err != nil { 126 if exit := new(exec.ExitError); errors.As(err, &exit) { 127 t.Logf("exit code: %d", exit.ExitCode()) 128 t.Logf("stderr:\n%s", string(exit.Stderr)) 129 } 130 t.Fatal(err) 131 } 132 skip := len(out) - 1 133 out, err = exec.CommandContext(ctx, `go`, `list`, `.`).Output() 134 if err != nil { 135 if exit := new(exec.ExitError); errors.As(err, &exit) { 136 t.Logf("exit code: %d", exit.ExitCode()) 137 t.Logf("stderr:\n%s", string(exit.Stderr)) 138 } 139 t.Fatal(err) 140 } 141 // Swap separators, except for the one at the module/package boundary. 142 for i, b := range out { 143 if b == '/' && i != skip { 144 out[i] = '_' 145 } 146 } 147 // Join the resulting path (with the newline chomped) with the cache 148 // root. 149 d := CacheDir(t) 150 d = filepath.Join(d, string(out[:len(out)-1])) 151 if err := os.MkdirAll(d, 0o755); err != nil { 152 t.Fatalf("unable to create per-package test cache dir %q: %v", d, err) 153 } 154 pkgCacheDir = d 155 }) 156 if pkgCacheDir == "" { 157 t.Fatal("test cache dir error, check previous tests") 158 } 159 return pkgCacheDir 160 } 161 162 // Deadliner is implemented by [testing.T] to report the test deadline. 163 type deadliner interface { 164 Deadline() (deadline time.Time, ok bool) 165 } 166 167 var pkgCacheOnce sync.Once 168 var pkgCacheDir string