github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/dependency/util_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package dependency_test 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 jc "github.com/juju/testing/checkers" 11 gc "gopkg.in/check.v1" 12 "gopkg.in/tomb.v1" 13 14 coretesting "github.com/juju/juju/testing" 15 "github.com/juju/juju/worker" 16 "github.com/juju/juju/worker/dependency" 17 "github.com/juju/juju/worker/workertest" 18 ) 19 20 type engineFixture struct { 21 isFatal dependency.IsFatalFunc 22 worstError dependency.WorstErrorFunc 23 filter dependency.FilterFunc 24 dirty bool 25 } 26 27 func (fix *engineFixture) isFatalFunc() dependency.IsFatalFunc { 28 if fix.isFatal != nil { 29 return fix.isFatal 30 } 31 return neverFatal 32 } 33 34 func (fix *engineFixture) worstErrorFunc() dependency.WorstErrorFunc { 35 if fix.worstError != nil { 36 return fix.worstError 37 } 38 return firstError 39 } 40 41 func (fix *engineFixture) run(c *gc.C, test func(*dependency.Engine)) { 42 config := dependency.EngineConfig{ 43 IsFatal: fix.isFatalFunc(), 44 WorstError: fix.worstErrorFunc(), 45 Filter: fix.filter, // can be nil anyway 46 ErrorDelay: coretesting.ShortWait / 2, 47 BounceDelay: coretesting.ShortWait / 10, 48 } 49 50 engine, err := dependency.NewEngine(config) 51 c.Assert(err, jc.ErrorIsNil) 52 defer fix.kill(c, engine) 53 test(engine) 54 } 55 56 func (fix *engineFixture) kill(c *gc.C, engine *dependency.Engine) { 57 if fix.dirty { 58 workertest.DirtyKill(c, engine) 59 } else { 60 workertest.CleanKill(c, engine) 61 } 62 } 63 64 type manifoldHarness struct { 65 inputs []string 66 errors chan error 67 starts chan struct{} 68 requireResources bool 69 ignoreExternalKill bool 70 } 71 72 func newManifoldHarness(inputs ...string) *manifoldHarness { 73 return &manifoldHarness{ 74 inputs: inputs, 75 errors: make(chan error, 1000), 76 starts: make(chan struct{}, 1000), 77 requireResources: true, 78 } 79 } 80 81 func newResourceIgnoringManifoldHarness(inputs ...string) *manifoldHarness { 82 mh := newManifoldHarness(inputs...) 83 mh.requireResources = false 84 return mh 85 } 86 87 // newErrorIgnoringManifoldHarness starts a minimal worker that ignores 88 // fatal errors - and will never die. 89 // This is potentially nasty, but it's useful in tests where we want 90 // to generate fatal errors but not race on which one the engine see first. 91 func newErrorIgnoringManifoldHarness(inputs ...string) *manifoldHarness { 92 mh := newManifoldHarness(inputs...) 93 mh.ignoreExternalKill = true 94 return mh 95 } 96 97 func (mh *manifoldHarness) Manifold() dependency.Manifold { 98 return dependency.Manifold{ 99 Inputs: mh.inputs, 100 Start: mh.start, 101 } 102 } 103 104 func (mh *manifoldHarness) start(context dependency.Context) (worker.Worker, error) { 105 for _, resourceName := range mh.inputs { 106 if err := context.Get(resourceName, nil); err != nil { 107 if mh.requireResources { 108 return nil, err 109 } 110 } 111 } 112 w := &minimalWorker{tomb.Tomb{}, mh.ignoreExternalKill} 113 go func() { 114 defer w.tomb.Done() 115 mh.starts <- struct{}{} 116 select { 117 case <-w.tombDying(): 118 case err := <-mh.errors: 119 w.tomb.Kill(err) 120 } 121 }() 122 return w, nil 123 } 124 125 func (mh *manifoldHarness) AssertOneStart(c *gc.C) { 126 mh.AssertStart(c) 127 mh.AssertNoStart(c) 128 } 129 130 func (mh *manifoldHarness) AssertStart(c *gc.C) { 131 select { 132 case <-mh.starts: 133 case <-time.After(coretesting.LongWait): 134 c.Fatalf("never started") 135 } 136 } 137 138 func (mh *manifoldHarness) AssertNoStart(c *gc.C) { 139 select { 140 case <-time.After(coretesting.ShortWait): 141 case <-mh.starts: 142 c.Fatalf("started unexpectedly") 143 } 144 } 145 146 func (mh *manifoldHarness) InjectError(c *gc.C, err error) { 147 select { 148 case mh.errors <- err: 149 case <-time.After(coretesting.LongWait): 150 c.Fatalf("never sent") 151 } 152 } 153 154 func newTracedManifoldHarness(inputs ...string) *tracedManifoldHarness { 155 return &tracedManifoldHarness{ 156 &manifoldHarness{ 157 inputs: inputs, 158 errors: make(chan error, 1000), 159 starts: make(chan struct{}, 1000), 160 ignoreExternalKill: false, 161 }, 162 } 163 } 164 165 type tracedManifoldHarness struct { 166 *manifoldHarness 167 } 168 169 func (mh *tracedManifoldHarness) Manifold() dependency.Manifold { 170 return dependency.Manifold{ 171 Inputs: mh.inputs, 172 Start: mh.start, 173 } 174 } 175 176 func (mh *tracedManifoldHarness) start(context dependency.Context) (worker.Worker, error) { 177 for _, resourceName := range mh.inputs { 178 if err := context.Get(resourceName, nil); err != nil { 179 return nil, errors.Trace(err) 180 } 181 } 182 w := &minimalWorker{tomb.Tomb{}, mh.ignoreExternalKill} 183 go func() { 184 defer w.tomb.Done() 185 mh.starts <- struct{}{} 186 select { 187 case <-w.tombDying(): 188 case err := <-mh.errors: 189 w.tomb.Kill(err) 190 } 191 }() 192 return w, nil 193 } 194 195 type minimalWorker struct { 196 tomb tomb.Tomb 197 ignoreExternalKill bool 198 } 199 200 func (w *minimalWorker) tombDying() <-chan struct{} { 201 if w.ignoreExternalKill { 202 return nil 203 } 204 return w.tomb.Dying() 205 } 206 207 func (w *minimalWorker) Kill() { 208 w.tomb.Kill(nil) 209 } 210 211 func (w *minimalWorker) Wait() error { 212 return w.tomb.Wait() 213 } 214 215 func (w *minimalWorker) Report() map[string]interface{} { 216 return map[string]interface{}{ 217 "key1": "hello there", 218 } 219 } 220 221 func startMinimalWorker(_ dependency.Context) (worker.Worker, error) { 222 w := &minimalWorker{} 223 go func() { 224 <-w.tomb.Dying() 225 w.tomb.Done() 226 }() 227 return w, nil 228 } 229 230 func isFatalIf(expect error) func(error) bool { 231 return func(actual error) bool { 232 return actual == expect 233 } 234 } 235 236 func neverFatal(_ error) bool { 237 return false 238 } 239 240 func alwaysFatal(_ error) bool { 241 return true 242 } 243 244 func firstError(err, _ error) error { 245 return err 246 }