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