github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/integration_test/itest/harness.go (about) 1 package itest 2 3 import ( 4 "context" 5 "regexp" 6 "runtime/debug" 7 "testing" 8 9 "github.com/stretchr/testify/suite" 10 11 "github.com/telepresenceio/telepresence/v2/pkg/dos" 12 ) 13 14 type Harness interface { 15 Cluster 16 17 PushHarness(ctx context.Context, setup func(ctx context.Context) bool, tearDown func(ctx context.Context)) 18 RunSuite(TestingSuite) 19 20 HarnessContext() context.Context 21 SetupSuite() 22 HarnessT() *testing.T 23 PopHarness() 24 } 25 26 type upAndDown struct { 27 setup func(ctx context.Context) bool 28 tearDown func(ctx context.Context) 29 setupWith context.Context 30 wasSetup bool 31 } 32 33 type harness struct { 34 Cluster 35 ctx context.Context 36 upAndDowns []upAndDown 37 wasSetup bool 38 } 39 40 func NewContextHarness(ctx context.Context) Harness { 41 return &harness{Cluster: GetGlobalHarness(ctx), ctx: ctx} 42 } 43 44 func (h *harness) PushHarness(ctx context.Context, setup func(ctx context.Context) bool, tearDown func(ctx context.Context)) { 45 h.upAndDowns = append(h.upAndDowns, upAndDown{setup: setup, tearDown: tearDown, setupWith: ctx}) 46 h.wasSetup = false 47 } 48 49 func (h *harness) HarnessContext() context.Context { 50 if l := len(h.upAndDowns) - 1; l >= 0 { 51 return h.upAndDowns[l].setupWith 52 } 53 return h.ctx 54 } 55 56 func (h *harness) RunSuite(s TestingSuite) { 57 if suiteEnabled(h.HarnessContext(), s) { 58 s.setContext(s.AmendSuiteContext(h.HarnessContext())) 59 h.HarnessT().Run(s.SuiteName(), func(t *testing.T) { suite.Run(t, s) }) 60 } 61 } 62 63 func suiteEnabled(ctx context.Context, s TestingSuite) bool { 64 suiteRx := dos.Getenv(ctx, "TEST_SUITE") 65 if suiteRx == "" { 66 return true 67 } 68 r, err := regexp.Compile(suiteRx) 69 if err != nil { 70 getT(ctx).Fatal(err) 71 } 72 return r.MatchString(s.SuiteName()) 73 } 74 75 // SetupSuite calls all functions that has been added with AddSetup in the order they 76 // were added. This only happens once. Repeated calls to this function on the same 77 // instance will do nothing. 78 // 79 // Repeated calls are expected since this function will be called before each 80 // test. 81 func (h *harness) SetupSuite() { 82 if h.wasSetup { 83 return 84 } 85 h.wasSetup = true 86 if err := h.GeneralError(); err != nil { 87 h.HarnessT().Fatal(err) // Immediately fail the test if a general error has been set 88 } 89 uds := h.upAndDowns 90 for i := range uds { 91 upDown := &uds[i] 92 if setup := upDown.setup; setup != nil { 93 upDown.setup = nil // Never setup twice 94 if upDown.wasSetup = safeSetUp(upDown.setupWith, setup); !upDown.wasSetup { 95 getT(h.ctx).FailNow() 96 } 97 } 98 } 99 } 100 101 // PopHarness calls the tearDown function that was pushed last and removes it. 102 func (h *harness) PopHarness() { 103 last := len(h.upAndDowns) - 1 104 if last < 0 { 105 return 106 } 107 upDown := &h.upAndDowns[last] 108 h.upAndDowns = h.upAndDowns[:last] 109 if upDown.setupWith != nil { 110 if tearDown := upDown.tearDown; tearDown != nil { 111 upDown.tearDown = nil // Never tear down twice 112 if upDown.wasSetup { 113 safeTearDown(upDown.setupWith, tearDown) 114 } 115 } 116 } 117 } 118 119 func safeSetUp(ctx context.Context, f func(context.Context) bool) bool { 120 defer failOnPanic(ctx) 121 return f(ctx) 122 } 123 124 func safeTearDown(ctx context.Context, f func(context.Context)) { 125 defer failOnPanic(ctx) 126 f(ctx) 127 } 128 129 func failOnPanic(ctx context.Context) { 130 if r := recover(); r != nil { 131 t := getT(ctx) 132 t.Errorf("test panicked: %v\n%s", r, debug.Stack()) 133 t.FailNow() 134 } 135 } 136 137 func (h *harness) HarnessT() *testing.T { 138 return getT(h.HarnessContext()) 139 }