github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 almostOneSecond = time.Second - time.Nanosecond 24 ) 25 26 var ( 27 defaultClockStart time.Time 28 ) 29 30 func init() { 31 // We pick a time with a comfortable h:m:s component but: 32 // (1) past the int32 unix epoch limit; 33 // (2) at a 5ns offset to make sure we're not discarding precision; 34 // (3) in a weird time zone. 35 value := "2073-03-03T01:00:00.000000005-08:40" 36 var err error 37 defaultClockStart, err = time.Parse(time.RFC3339Nano, value) 38 if err != nil { 39 panic(err) 40 } 41 } 42 43 // offset returns the result of defaultClockStart.Add(d); it exists to make 44 // expiry tests easier to write. 45 func offset(d time.Duration) time.Time { 46 return defaultClockStart.Add(d) 47 } 48 49 // almostSeconds returns a duration shorter than the supplied number of 50 // seconds by one nanosecond. 51 func almostSeconds(seconds int) time.Duration { 52 if seconds < 1 { 53 panic("expected positive time input") 54 } 55 return (time.Second * time.Duration(seconds)) - time.Nanosecond 56 } 57 58 // justAfterSeconds returns a duration longer than the supplied number of 59 // seconds by one nanosecond. 60 func justAfterSeconds(seconds int) time.Duration { 61 if seconds < 1 { 62 panic("expected positive time input") 63 } 64 return (time.Second * time.Duration(seconds)) + time.Nanosecond 65 } 66 67 // Fixture allows us to test a *lease.Manager with a usefully-mocked 68 // clock.Clock and corelease.Store. 69 type Fixture struct { 70 71 // leases contains the leases the corelease.Store should report when the 72 // test starts up. 73 leases map[corelease.Key]corelease.Info 74 75 // expectCalls contains the calls that should be made to the corelease.Store 76 // in the course of a test. By specifying a callback you can cause the 77 // reported leases to change. 78 expectCalls []call 79 80 // expectDirty should be set for tests that purposefully abuse the manager 81 // to the extent that it returns an error on Wait(); tests that don't set 82 // this flag will check that the manager's shutdown error is nil. 83 expectDirty bool 84 85 // autoexpire is whether the store should autoexpire. 86 autoexpire bool 87 } 88 89 // RunTest sets up a Manager and a Clock and passes them into the supplied 90 // test function. The manager will be cleaned up afterwards. 91 func (fix *Fixture) RunTest(c *gc.C, test func(*lease.Manager, *testclock.Clock)) { 92 clock := testclock.NewClock(defaultClockStart) 93 store := NewStore(fix.autoexpire, fix.leases, fix.expectCalls) 94 manager, err := lease.NewManager(lease.ManagerConfig{ 95 Clock: clock, 96 Store: store, 97 Secretary: func(string) (lease.Secretary, error) { 98 return Secretary{}, nil 99 }, 100 MaxSleep: defaultMaxSleep, 101 Logger: loggo.GetLogger("lease_test"), 102 PrometheusRegisterer: noopRegisterer{}, 103 }) 104 c.Assert(err, jc.ErrorIsNil) 105 var wg sync.WaitGroup 106 testDone := make(chan struct{}) 107 storeDone := make(chan struct{}) 108 wg.Add(1) 109 go func() { 110 defer wg.Done() 111 // Dirty tests will probably have stopped the manager anyway, but no 112 // sense leaving them around if things aren't exactly as we expect. 113 timeout := time.After(coretesting.LongWait) 114 select { 115 case <-testDone: 116 case <-timeout: 117 c.Errorf("test took >10s to complete") 118 // If we timed out here, then we need to timeout storeDone as well 119 timeout = time.After(0) 120 } 121 // Wait for the store to be done in the happy path, but 122 // don't wait any longer than 10s total. 123 select { 124 case <-storeDone: 125 case <-time.After(coretesting.LongWait): 126 c.Errorf("store took >10s to complete") 127 } 128 manager.Kill() 129 err := manager.Wait() 130 if !fix.expectDirty { 131 c.Check(err, jc.ErrorIsNil) 132 } 133 }() 134 wg.Add(1) 135 go func() { 136 defer wg.Done() 137 store.Wait(c) 138 close(storeDone) 139 }() 140 waitAlarms(c, clock, 1) 141 test(manager, clock) 142 close(testDone) 143 wg.Wait() 144 } 145 146 func waitAlarms(c *gc.C, clock *testclock.Clock, count int) { 147 timeout := time.After(coretesting.LongWait) 148 for i := 0; i < count; i++ { 149 select { 150 case <-clock.Alarms(): 151 case <-timeout: 152 c.Fatalf("timed out waiting for %dth alarm set", i) 153 } 154 } 155 } 156 157 type noopRegisterer struct { 158 prometheus.Registerer 159 } 160 161 func (noopRegisterer) Register(prometheus.Collector) error { 162 return nil 163 } 164 165 func (noopRegisterer) Unregister(prometheus.Collector) bool { 166 return false 167 }