github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/jujud/agent/util/housing_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package util_test 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 14 "github.com/juju/juju/cmd/jujud/agent/util" 15 coretesting "github.com/juju/juju/testing" 16 "github.com/juju/juju/worker" 17 "github.com/juju/juju/worker/dependency" 18 dt "github.com/juju/juju/worker/dependency/testing" 19 "github.com/juju/juju/worker/fortress" 20 "github.com/juju/juju/worker/workertest" 21 ) 22 23 type HousingSuite struct { 24 testing.IsolationSuite 25 } 26 27 var _ = gc.Suite(&HousingSuite{}) 28 29 func (*HousingSuite) TestEmptyHousingEmptyManifold(c *gc.C) { 30 manifold := util.Housing{}.Decorate(dependency.Manifold{}) 31 32 c.Check(manifold.Inputs, gc.HasLen, 0) 33 c.Check(manifold.Start, gc.IsNil) 34 c.Check(manifold.Output, gc.IsNil) 35 c.Check(manifold.Filter, gc.IsNil) 36 } 37 38 func (*HousingSuite) TestEmptyHousingPopulatedManifold(c *gc.C) { 39 manifold := util.Housing{}.Decorate(dependency.Manifold{ 40 Inputs: []string{"x", "y", "z"}, 41 Start: panicStart, 42 Output: panicOutput, 43 Filter: panicFilter, 44 }) 45 46 c.Check(manifold.Inputs, jc.DeepEquals, []string{"x", "y", "z"}) 47 c.Check(func() { 48 manifold.Start(nil) 49 }, gc.PanicMatches, "panicStart") 50 c.Check(func() { 51 manifold.Output(nil, nil) 52 }, gc.PanicMatches, "panicOutput") 53 c.Check(func() { 54 manifold.Filter(nil) 55 }, gc.PanicMatches, "panicFilter") 56 } 57 58 func (*HousingSuite) TestReplacesFilter(c *gc.C) { 59 expectIn := errors.New("tweedledum") 60 expectOut := errors.New("tweedledee") 61 manifold := util.Housing{ 62 Filter: func(in error) error { 63 c.Check(in, gc.Equals, expectIn) 64 return expectOut 65 }, 66 }.Decorate(dependency.Manifold{ 67 Filter: panicFilter, 68 }) 69 70 out := manifold.Filter(expectIn) 71 c.Check(out, gc.Equals, expectOut) 72 } 73 74 func (*HousingSuite) TestFlagsNoInput(c *gc.C) { 75 manifold := util.Housing{ 76 Flags: []string{"foo", "bar"}, 77 }.Decorate(dependency.Manifold{}) 78 79 expect := []string{"foo", "bar"} 80 c.Check(manifold.Inputs, jc.DeepEquals, expect) 81 } 82 83 func (*HousingSuite) TestFlagsNewInput(c *gc.C) { 84 manifold := util.Housing{ 85 Flags: []string{"foo", "bar"}, 86 }.Decorate(dependency.Manifold{ 87 Inputs: []string{"ping", "pong"}, 88 }) 89 90 expect := []string{"ping", "pong", "foo", "bar"} 91 c.Check(manifold.Inputs, jc.DeepEquals, expect) 92 } 93 94 func (*HousingSuite) TestFlagsExistingInput(c *gc.C) { 95 manifold := util.Housing{ 96 Flags: []string{"a", "c", "d"}, 97 }.Decorate(dependency.Manifold{ 98 Inputs: []string{"a", "b"}, 99 }) 100 101 expect := []string{"a", "b", "c", "d"} 102 c.Check(manifold.Inputs, jc.DeepEquals, expect) 103 } 104 105 func (*HousingSuite) TestFlagMissing(c *gc.C) { 106 manifold := util.Housing{ 107 Flags: []string{"flag"}, 108 }.Decorate(dependency.Manifold{}) 109 context := dt.StubContext(nil, map[string]interface{}{ 110 "flag": dependency.ErrMissing, 111 }) 112 113 worker, err := manifold.Start(context) 114 c.Check(worker, gc.IsNil) 115 c.Check(errors.Cause(err), gc.Equals, dependency.ErrMissing) 116 } 117 118 func (*HousingSuite) TestFlagBadType(c *gc.C) { 119 manifold := util.Housing{ 120 Flags: []string{"flag"}, 121 }.Decorate(dependency.Manifold{}) 122 context := dt.StubContext(nil, map[string]interface{}{ 123 "flag": false, 124 }) 125 126 worker, err := manifold.Start(context) 127 c.Check(worker, gc.IsNil) 128 c.Check(err, gc.ErrorMatches, "cannot set false into .*") 129 } 130 131 func (*HousingSuite) TestFlagBadValue(c *gc.C) { 132 manifold := util.Housing{ 133 Flags: []string{"flag"}, 134 }.Decorate(dependency.Manifold{}) 135 context := dt.StubContext(nil, map[string]interface{}{ 136 "flag": flag{false}, 137 }) 138 139 worker, err := manifold.Start(context) 140 c.Check(worker, gc.IsNil) 141 c.Check(errors.Cause(err), gc.Equals, dependency.ErrMissing) 142 } 143 144 func (*HousingSuite) TestFlagSuccess(c *gc.C) { 145 expectWorker := &struct{ worker.Worker }{} 146 manifold := util.Housing{ 147 Flags: []string{"flag"}, 148 }.Decorate(dependency.Manifold{ 149 Start: func(dependency.Context) (worker.Worker, error) { 150 return expectWorker, nil 151 }, 152 }) 153 context := dt.StubContext(nil, map[string]interface{}{ 154 "flag": flag{true}, 155 }) 156 157 worker, err := manifold.Start(context) 158 c.Check(worker, gc.Equals, expectWorker) 159 c.Check(err, jc.ErrorIsNil) 160 } 161 162 func (*HousingSuite) TestOccupyNewInput(c *gc.C) { 163 manifold := util.Housing{ 164 Occupy: "fortress", 165 }.Decorate(dependency.Manifold{ 166 Inputs: []string{"ping", "pong"}, 167 }) 168 169 expect := []string{"ping", "pong", "fortress"} 170 c.Check(manifold.Inputs, jc.DeepEquals, expect) 171 } 172 173 func (*HousingSuite) TestOccupyExistingInput(c *gc.C) { 174 manifold := util.Housing{ 175 Occupy: "fortress", 176 }.Decorate(dependency.Manifold{ 177 Inputs: []string{"citadel", "fortress", "bastion"}, 178 }) 179 180 expect := []string{"citadel", "fortress", "bastion"} 181 c.Check(manifold.Inputs, jc.DeepEquals, expect) 182 } 183 184 func (*HousingSuite) TestFlagBlocksOccupy(c *gc.C) { 185 manifold := util.Housing{ 186 Flags: []string{"flag"}, 187 Occupy: "fortress", 188 }.Decorate(dependency.Manifold{}) 189 context := dt.StubContext(nil, map[string]interface{}{ 190 "flag": dependency.ErrMissing, 191 "fortress": errors.New("never happen"), 192 }) 193 194 worker, err := manifold.Start(context) 195 c.Check(worker, gc.IsNil) 196 c.Check(errors.Cause(err), gc.Equals, dependency.ErrMissing) 197 } 198 199 func (*HousingSuite) TestOccupyMissing(c *gc.C) { 200 manifold := util.Housing{ 201 Occupy: "fortress", 202 }.Decorate(dependency.Manifold{}) 203 context := dt.StubContext(nil, map[string]interface{}{ 204 "fortress": dependency.ErrMissing, 205 }) 206 207 worker, err := manifold.Start(context) 208 c.Check(worker, gc.IsNil) 209 c.Check(errors.Cause(err), gc.Equals, dependency.ErrMissing) 210 } 211 212 func (*HousingSuite) TestOccupyBadType(c *gc.C) { 213 manifold := util.Housing{ 214 Occupy: "fortress", 215 }.Decorate(dependency.Manifold{}) 216 context := dt.StubContext(nil, map[string]interface{}{ 217 "fortress": false, 218 }) 219 220 worker, err := manifold.Start(context) 221 c.Check(worker, gc.IsNil) 222 c.Check(err, gc.ErrorMatches, "cannot set false into .*") 223 } 224 225 func (*HousingSuite) TestOccupyLocked(c *gc.C) { 226 manifold := util.Housing{ 227 Occupy: "fortress", 228 }.Decorate(dependency.Manifold{}) 229 abort := make(chan struct{}) 230 context := dt.StubContext(abort, map[string]interface{}{ 231 "fortress": newGuest(false), 232 }) 233 234 // start the start func 235 started := make(chan struct{}) 236 go func() { 237 defer close(started) 238 worker, err := manifold.Start(context) 239 c.Check(worker, gc.IsNil) 240 c.Check(errors.Cause(err), gc.Equals, fortress.ErrAborted) 241 }() 242 243 // check it's blocked... 244 select { 245 case <-time.After(coretesting.ShortWait): 246 case <-started: 247 c.Errorf("Start finished early") 248 } 249 250 // ...until the context is aborted. 251 close(abort) 252 select { 253 case <-started: 254 case <-time.After(coretesting.LongWait): 255 c.Fatalf("timed out") 256 } 257 } 258 259 func (*HousingSuite) TestOccupySuccess(c *gc.C) { 260 expectWorker := workertest.NewErrorWorker(errors.New("ignored")) 261 defer workertest.DirtyKill(c, expectWorker) 262 manifold := util.Housing{ 263 Occupy: "fortress", 264 }.Decorate(dependency.Manifold{ 265 Start: func(dependency.Context) (worker.Worker, error) { 266 return expectWorker, nil 267 }, 268 }) 269 guest := newGuest(true) 270 context := dt.StubContext(nil, map[string]interface{}{ 271 "fortress": guest, 272 }) 273 274 // wait for the start func to complete 275 started := make(chan struct{}) 276 go func() { 277 defer close(started) 278 worker, err := manifold.Start(context) 279 c.Check(worker, gc.Equals, expectWorker) 280 c.Check(err, jc.ErrorIsNil) 281 }() 282 select { 283 case <-started: 284 case <-time.After(coretesting.LongWait): 285 c.Fatalf("timed out") 286 } 287 288 // check the worker's alive 289 workertest.CheckAlive(c, expectWorker) 290 291 // check the visit keeps running... 292 select { 293 case <-time.After(coretesting.ShortWait): 294 case <-guest.done: 295 c.Fatalf("visit finished early") 296 } 297 298 // ...until the worker stops 299 expectWorker.Kill() 300 select { 301 case <-guest.done: 302 case <-time.After(coretesting.LongWait): 303 c.Fatalf("timed out") 304 } 305 } 306 307 func newGuest(unlocked bool) guest { 308 return guest{ 309 unlocked: unlocked, 310 done: make(chan struct{}), 311 } 312 } 313 314 // guest implements fortress.Guest. 315 type guest struct { 316 unlocked bool 317 done chan struct{} 318 } 319 320 // Visit is part of the fortress.Guest interface. 321 func (guest guest) Visit(visit fortress.Visit, abort fortress.Abort) error { 322 defer close(guest.done) 323 if guest.unlocked { 324 return visit() 325 } 326 <-abort 327 return fortress.ErrAborted 328 } 329 330 // flag implements util.Flag. 331 type flag struct { 332 value bool 333 } 334 335 // Check is part of the util.Flag interface. 336 func (flag flag) Check() bool { 337 return flag.value 338 } 339 340 func panicStart(dependency.Context) (worker.Worker, error) { 341 panic("panicStart") 342 } 343 344 func panicOutput(worker.Worker, interface{}) error { 345 panic("panicOutput") 346 } 347 348 func panicFilter(error) error { 349 panic("panicFilter") 350 }