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 }