github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/lease/fixture_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package lease_test
     5  
     6  import (
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/juju/clock/testclock"
    11  	"github.com/juju/loggo"
    12  	jc "github.com/juju/testing/checkers"
    13  	"github.com/prometheus/client_golang/prometheus"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	corelease "github.com/juju/juju/core/lease"
    17  	coretesting "github.com/juju/juju/testing"
    18  	"github.com/juju/juju/worker/lease"
    19  )
    20  
    21  const (
    22  	defaultMaxSleep = time.Hour
    23  )
    24  
    25  var (
    26  	defaultClockStart time.Time
    27  )
    28  
    29  func init() {
    30  	// We pick a time with a comfortable h:m:s component but:
    31  	//  (1) past the int32 unix epoch limit;
    32  	//  (2) at a 5ns offset to make sure we're not discarding precision;
    33  	//  (3) in a weird time zone.
    34  	value := "2073-03-03T01:00:00.000000005-08:40"
    35  	var err error
    36  	defaultClockStart, err = time.Parse(time.RFC3339Nano, value)
    37  	if err != nil {
    38  		panic(err)
    39  	}
    40  }
    41  
    42  // offset returns the result of defaultClockStart.Add(d); it exists to make
    43  // expiry tests easier to write.
    44  func offset(d time.Duration) time.Time {
    45  	return defaultClockStart.Add(d)
    46  }
    47  
    48  // Fixture allows us to test a *lease.Manager with a usefully-mocked
    49  // clock.Clock and corelease.Store.
    50  type Fixture struct {
    51  
    52  	// leases contains the leases the corelease.Store should report when the
    53  	// test starts up.
    54  	leases map[corelease.Key]corelease.Info
    55  
    56  	// expectCalls contains the calls that should be made to the corelease.Store
    57  	// in the course of a test. By specifying a callback you can cause the
    58  	// reported leases to change.
    59  	expectCalls []call
    60  
    61  	// expectDirty should be set for tests that purposefully abuse the manager
    62  	// to the extent that it returns an error on Wait(); tests that don't set
    63  	// this flag will check that the manager's shutdown error is nil.
    64  	expectDirty bool
    65  }
    66  
    67  // RunTest sets up a Manager and a Clock and passes them into the supplied
    68  // test function. The manager will be cleaned up afterwards.
    69  func (fix *Fixture) RunTest(c *gc.C, test func(*lease.Manager, *testclock.Clock)) {
    70  	clock := testclock.NewClock(defaultClockStart)
    71  	store := NewStore(fix.leases, fix.expectCalls, clock)
    72  	manager, err := lease.NewManager(lease.ManagerConfig{
    73  		Clock: clock,
    74  		Store: store,
    75  		Secretary: func(string) (lease.Secretary, error) {
    76  			return Secretary{}, nil
    77  		},
    78  		MaxSleep:             defaultMaxSleep,
    79  		Logger:               loggo.GetLogger("lease_test"),
    80  		PrometheusRegisterer: noopRegisterer{},
    81  	})
    82  	c.Assert(err, jc.ErrorIsNil)
    83  	var wg sync.WaitGroup
    84  	testDone := make(chan struct{})
    85  	storeDone := make(chan struct{})
    86  	wg.Add(1)
    87  	go func() {
    88  		defer wg.Done()
    89  		// Dirty tests will probably have stopped the manager anyway, but no
    90  		// sense leaving them around if things aren't exactly as we expect.
    91  		timeout := time.After(coretesting.LongWait)
    92  		select {
    93  		case <-testDone:
    94  		case <-timeout:
    95  			c.Errorf("test took >10s to complete")
    96  		}
    97  		// Wait for the store to be done in the happy path, but
    98  		// don't wait any longer than 10s total.
    99  		select {
   100  		case <-storeDone:
   101  		case <-time.After(coretesting.LongWait):
   102  			c.Errorf("store took >10s to complete")
   103  		}
   104  		manager.Kill()
   105  		err := manager.Wait()
   106  		if !fix.expectDirty {
   107  			c.Check(err, jc.ErrorIsNil)
   108  		}
   109  	}()
   110  	wg.Add(1)
   111  	go func() {
   112  		defer wg.Done()
   113  		store.Wait(c)
   114  		close(storeDone)
   115  	}()
   116  	waitAlarms(c, clock, 1)
   117  	test(manager, clock)
   118  	close(testDone)
   119  	wg.Wait()
   120  }
   121  
   122  func waitAlarms(c *gc.C, clock *testclock.Clock, count int) {
   123  	timeout := time.After(coretesting.LongWait)
   124  	for i := 0; i < count; i++ {
   125  		select {
   126  		case <-clock.Alarms():
   127  		case <-timeout:
   128  			c.Fatalf("timed out waiting for %dth alarm set", i)
   129  		}
   130  	}
   131  }
   132  
   133  type noopRegisterer struct {
   134  	prometheus.Registerer
   135  }
   136  
   137  func (noopRegisterer) Register(prometheus.Collector) error {
   138  	return nil
   139  }
   140  
   141  func (noopRegisterer) Unregister(prometheus.Collector) bool {
   142  	return false
   143  }