github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/singular/singular_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package singular_test 5 6 import ( 7 "fmt" 8 "sync" 9 stdtesting "testing" 10 "time" 11 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/testing" 16 "github.com/juju/juju/worker" 17 "github.com/juju/juju/worker/singular" 18 ) 19 20 var _ = gc.Suite(&singularSuite{}) 21 22 func TestPackage(t *stdtesting.T) { 23 gc.TestingT(t) 24 } 25 26 type singularSuite struct { 27 testing.BaseSuite 28 } 29 30 func (*singularSuite) TestWithMasterError(c *gc.C) { 31 expectErr := fmt.Errorf("an error") 32 conn := &fakeConn{ 33 isMasterErr: expectErr, 34 } 35 r, err := singular.New(newRunner(), conn) 36 c.Check(err, gc.ErrorMatches, "cannot get master status: an error") 37 c.Check(r, gc.IsNil) 38 } 39 40 func (s *singularSuite) TestWithIsMasterTrue(c *gc.C) { 41 // When IsMaster returns true, workers get started on the underlying 42 // runner as usual. 43 s.PatchValue(&singular.PingInterval, 1*time.Millisecond) 44 underlyingRunner := newRunner() 45 conn := &fakeConn{ 46 isMaster: true, 47 } 48 r, err := singular.New(underlyingRunner, conn) 49 c.Assert(err, jc.ErrorIsNil) 50 51 started := make(chan struct{}, 1) 52 err = r.StartWorker("worker", func() (worker.Worker, error) { 53 return worker.NewSimpleWorker(func(stop <-chan struct{}) error { 54 started <- struct{}{} 55 <-stop 56 return nil 57 }), nil 58 }) 59 select { 60 case <-started: 61 case <-time.After(testing.LongWait): 62 c.Fatalf("timed out waiting for worker to start") 63 } 64 65 err = worker.Stop(r) 66 c.Assert(err, jc.ErrorIsNil) 67 } 68 69 var errFatal = fmt.Errorf("fatal error") 70 71 func (s *singularSuite) TestWithIsMasterFalse(c *gc.C) { 72 // When IsMaster returns false, dummy workers are started that 73 // do nothing except wait for the pinger to return an error. 74 75 s.PatchValue(&singular.PingInterval, testing.ShortWait/10) 76 77 underlyingRunner := newRunner() 78 conn := &fakeConn{ 79 isMaster: false, 80 pinged: make(chan struct{}, 5), 81 } 82 r, err := singular.New(underlyingRunner, conn) 83 c.Assert(err, jc.ErrorIsNil) 84 85 err = r.StartWorker("worker", func() (worker.Worker, error) { 86 c.Errorf("worker unexpectedly started") 87 return nil, fmt.Errorf("no worker") 88 }) 89 c.Assert(err, jc.ErrorIsNil) 90 91 timeout := time.NewTimer(testing.LongWait) 92 for i := 0; i < cap(conn.pinged); i++ { 93 select { 94 case <-conn.pinged: 95 case <-timeout.C: 96 c.Fatalf("timed out waiting for ping") 97 } 98 } 99 // Cause the ping to return an error; the underlying runner 100 // should then exit with the error it returned, because it's 101 // fatal. 102 conn.setPingErr(errFatal) 103 runWithTimeout(c, "wait for underlying runner", func() { 104 err = underlyingRunner.Wait() 105 }) 106 c.Assert(err, gc.Equals, errFatal) 107 108 // Make sure there are no more pings after the ping interval by 109 // draining the channel and then making sure that at most 110 // one ping arrives within the next ShortWait. 111 loop1: 112 for { 113 select { 114 case <-conn.pinged: 115 default: 116 break loop1 117 } 118 } 119 timeout.Reset(testing.ShortWait) 120 n := 0 121 loop2: 122 for { 123 select { 124 case <-conn.pinged: 125 c.Assert(n, jc.LessThan, 2) 126 n++ 127 case <-timeout.C: 128 break loop2 129 } 130 } 131 } 132 133 func (s *singularSuite) TestPingCalledOnceOnlyForSeveralWorkers(c *gc.C) { 134 // Patch the ping interval to a large value, start several workers 135 // and check that Ping is only called once. 136 s.PatchValue(&singular.PingInterval, testing.LongWait) 137 138 underlyingRunner := newRunner() 139 conn := &fakeConn{ 140 isMaster: false, 141 pinged: make(chan struct{}, 2), 142 } 143 144 r, err := singular.New(underlyingRunner, conn) 145 c.Assert(err, jc.ErrorIsNil) 146 147 for i := 0; i < 5; i++ { 148 name := fmt.Sprint("worker", i) 149 err := r.StartWorker(name, func() (worker.Worker, error) { 150 c.Errorf("worker unexpectedly started") 151 return nil, fmt.Errorf("no worker") 152 }) 153 c.Assert(err, jc.ErrorIsNil) 154 } 155 time.Sleep(testing.ShortWait) 156 n := 0 157 loop: 158 for { 159 select { 160 case <-conn.pinged: 161 n++ 162 default: 163 break loop 164 } 165 } 166 c.Assert(n, gc.Equals, 1) 167 } 168 169 func newRunner() worker.Runner { 170 return worker.NewRunner( 171 func(err error) bool { 172 return err == errFatal 173 }, 174 func(err0, err1 error) bool { return true }, 175 ) 176 } 177 178 type fakeConn struct { 179 isMaster bool 180 isMasterErr error 181 182 pinged chan struct{} 183 184 mu sync.Mutex 185 pingErr error 186 } 187 188 func (c *fakeConn) IsMaster() (bool, error) { 189 return c.isMaster, c.isMasterErr 190 } 191 192 func (c *fakeConn) Ping() error { 193 c.mu.Lock() 194 defer c.mu.Unlock() 195 select { 196 case c.pinged <- struct{}{}: 197 default: 198 } 199 return c.pingErr 200 } 201 202 func (c *fakeConn) setPingErr(err error) { 203 c.mu.Lock() 204 defer c.mu.Unlock() 205 c.pingErr = err 206 } 207 208 func runWithTimeout(c *gc.C, description string, f func()) { 209 done := make(chan struct{}) 210 go func() { 211 f() 212 close(done) 213 }() 214 select { 215 case <-done: 216 return 217 case <-time.After(testing.LongWait): 218 c.Fatalf("time out, %s", description) 219 } 220 }