github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/state/leadership/util_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package leadership_test 5 6 import ( 7 "fmt" 8 "sort" 9 "sync" 10 "time" 11 12 "github.com/juju/errors" 13 jc "github.com/juju/testing/checkers" 14 gc "gopkg.in/check.v1" 15 16 "github.com/juju/juju/state/lease" 17 ) 18 19 // Client implements lease.Client for testing purposes. 20 type Client struct { 21 leases map[string]lease.Info 22 expect []call 23 failed string 24 done chan struct{} 25 } 26 27 // NewClient initializes and returns a new client configured to report 28 // the supplied leases and expect the supplied calls. 29 func NewClient(leases map[string]lease.Info, expect []call) *Client { 30 if leases == nil { 31 leases = make(map[string]lease.Info) 32 } 33 done := make(chan struct{}) 34 if len(expect) == 0 { 35 close(done) 36 } 37 return &Client{ 38 leases: leases, 39 expect: expect, 40 done: done, 41 } 42 } 43 44 // Wait will return when all expected calls have been made, or fail the test 45 // if they don't happen within a second. (You control the clock; your tests 46 // should pass in *way* less than a second of wall-clock time.) 47 func (client *Client) Wait(c *gc.C) { 48 select { 49 case <-client.done: 50 if client.failed != "" { 51 c.Fatalf(client.failed) 52 } 53 case <-time.After(time.Second): 54 c.Fatalf("Client test took way too long") 55 } 56 } 57 58 // Leases is part of the lease.Client interface. 59 func (client *Client) Leases() map[string]lease.Info { 60 result := make(map[string]lease.Info) 61 for k, v := range client.leases { 62 result[k] = v 63 } 64 return result 65 } 66 67 // call implements the bulk of the lease.Client interface. 68 func (client *Client) call(method string, args []interface{}) error { 69 select { 70 case <-client.done: 71 return errors.Errorf("Client method called after test complete: %s %v", method, args) 72 default: 73 defer func() { 74 if len(client.expect) == 0 || client.failed != "" { 75 close(client.done) 76 } 77 }() 78 } 79 80 expect := client.expect[0] 81 client.expect = client.expect[1:] 82 if expect.callback != nil { 83 expect.callback(client.leases) 84 } 85 86 if method == expect.method { 87 if ok, _ := jc.DeepEqual(args, expect.args); ok { 88 return expect.err 89 } 90 } 91 client.failed = fmt.Sprintf("unexpected Client call:\n actual: %s %v\n expect: %s %v", 92 method, args, expect.method, expect.args, 93 ) 94 return errors.New(client.failed) 95 } 96 97 // ClaimLease is part of the lease.Client interface. 98 func (client *Client) ClaimLease(name string, request lease.Request) error { 99 return client.call("ClaimLease", []interface{}{name, request}) 100 } 101 102 // ExtendLease is part of the lease.Client interface. 103 func (client *Client) ExtendLease(name string, request lease.Request) error { 104 return client.call("ExtendLease", []interface{}{name, request}) 105 } 106 107 // ExpireLease is part of the lease.Client interface. 108 func (client *Client) ExpireLease(name string) error { 109 return client.call("ExpireLease", []interface{}{name}) 110 } 111 112 // Refresh is part of the lease.Client interface. 113 func (client *Client) Refresh() error { 114 return client.call("Refresh", nil) 115 } 116 117 // call defines a expected method call on a Client; it encodes: 118 type call struct { 119 120 // method is the name of the method. 121 method string 122 123 // args is the expected arguments. 124 args []interface{} 125 126 // err is the error to return. 127 err error 128 129 // callback, if non-nil, will be passed the internal leases dict; for 130 // modification, if desired. Otherwise you can use it to, e.g., assert 131 // clock time. 132 callback func(leases map[string]lease.Info) 133 } 134 135 // Clock implements lease.Clock for testing purposes. 136 type Clock struct { 137 mu sync.Mutex 138 now time.Time 139 alarms []alarm 140 } 141 142 // NewClock returns a new clock set to the supplied time. 143 func NewClock(now time.Time) *Clock { 144 return &Clock{now: now} 145 } 146 147 // Now is part of the lease.Clock interface. 148 func (clock *Clock) Now() time.Time { 149 clock.mu.Lock() 150 defer clock.mu.Unlock() 151 return clock.now 152 } 153 154 // Alarm is part of the lease.Clock interface. 155 func (clock *Clock) Alarm(t time.Time) <-chan time.Time { 156 clock.mu.Lock() 157 defer clock.mu.Unlock() 158 notify := make(chan time.Time, 1) 159 if !clock.now.Before(t) { 160 notify <- clock.now 161 } else { 162 clock.alarms = append(clock.alarms, alarm{t, notify}) 163 sort.Sort(byTime(clock.alarms)) 164 } 165 return notify 166 } 167 168 // Advance advances the result of Now by the supplied duration, and sends 169 // the "current" time on all alarms which are no longer "in the future". 170 func (clock *Clock) Advance(d time.Duration) { 171 clock.mu.Lock() 172 defer clock.mu.Unlock() 173 clock.now = clock.now.Add(d) 174 rung := 0 175 for _, alarm := range clock.alarms { 176 if clock.now.Before(alarm.time) { 177 break 178 } 179 alarm.notify <- clock.now 180 rung++ 181 } 182 clock.alarms = clock.alarms[rung:] 183 } 184 185 // alarm records the time at which we're expected to send on notify. 186 type alarm struct { 187 time time.Time 188 notify chan time.Time 189 } 190 191 // byTime is used to sort alarms by time. 192 type byTime []alarm 193 194 func (a byTime) Len() int { return len(a) } 195 func (a byTime) Less(i, j int) bool { return a[i].time.Before(a[j].time) } 196 func (a byTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] }