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  }