func waitShort(c *gc.C, w worker.Worker) error { done := make(chan error) go func() { done <- w.Wait() }() return waitForTimeout(c, done, coretesting.ShortWait) }
// CheckAlive Wait()s a short time for the supplied worker to return an error, // and fails the test if it does. If it doesn't fail, it'll leave a goroutine // running in the background, blocked on the worker's death; but that doesn't // matter, because of *course* you correctly deferred a suitable Kill helper // as soon as you created the worker in the first place. Right? Right. // // It doesn't Assert and is therefore suitable for use from any goroutine. func CheckAlive(c *gc.C, w worker.Worker) { wait := make(chan error, 1) go func() { wait <- w.Wait() }() select { case <-time.After(aliveDelay): case err := <-wait: c.Errorf("expected alive worker; failed with %v", err) } }
func checkExitsWithError(c *gc.C, w worker.Worker, expectedErr string) { errCh := make(chan error) go func() { errCh <- w.Wait() }() select { case err := <-errCh: c.Check(err, gc.ErrorMatches, expectedErr) case <-time.After(coretesting.LongWait): c.Fatal("timed out waiting for worker to exit") } }
func runWorkerAndWait(c *gc.C, w worker.Worker, expectedErr string) { doneC := make(chan error) go func() { doneC <- w.Wait() }() select { case err := <-doneC: c.Assert(err, gc.ErrorMatches, expectedErr) case <-time.After(coretesting.LongWait): c.Fatal("timed out waiting for worker to stop") } }
func waitForExit(c *gc.C, w worker.Worker) error { errCh := make(chan error) go func() { errCh <- w.Wait() }() select { case err := <-errCh: return err case <-time.After(coretesting.LongWait): c.Fatal("timed out waiting for worker to exit") } panic("can't get here") }
// CheckKilled Wait()s for the supplied worker's error, which it returns for // further analysis, or fails the test after a timeout expires. It doesn't // Assert and is therefore suitable for use from any goroutine. func CheckKilled(c *gc.C, w worker.Worker) error { wait := make(chan error, 1) go func() { wait <- w.Wait() }() select { case err := <-wait: return err case <-time.After(killTimeout): c.Errorf("timed out waiting for worker to stop") return errors.New("workertest: worker not stopping") } }
func checkNotExiting(c *gc.C, w worker.Worker) { exited := make(chan bool) go func() { w.Wait() close(exited) }() select { case <-exited: c.Fatal("worker exited unexpectedly") case <-time.After(coretesting.ShortWait): // Worker didn't exit (good) } }
// add starts two goroutines that (1) kill the catacomb's tomb with any // error encountered by the worker; and (2) kill the worker when the // catacomb starts dying. func (catacomb *Catacomb) add(w worker.Worker) { // We must wait for _both_ goroutines to exit in // arbitrary order depending on the order of the worker // and the catacomb shutting down. catacomb.wg.Add(2) go func() { defer catacomb.wg.Done() if err := w.Wait(); err != nil { catacomb.Kill(err) } }() go func() { defer catacomb.wg.Done() <-catacomb.tomb.Dying() worker.Stop(w) }() }
// add starts two goroutines that (1) kill the catacomb's tomb with any // error encountered by the worker; and (2) kill the worker when the // catacomb starts dying. func (catacomb *Catacomb) add(w worker.Worker) { // The coordination via stopped is not reliably observable, and hence not // tested, but it's yucky to leave the second goroutine running when we // don't need to. stopped := make(chan struct{}) catacomb.wg.Add(1) go func() { defer catacomb.wg.Done() defer close(stopped) if err := w.Wait(); err != nil { catacomb.Kill(err) } }() go func() { select { case <-stopped: case <-catacomb.tomb.Dying(): w.Kill() } }() }
// gotStarted updates the engine to reflect the creation of a worker. It must // only be called from the loop goroutine. func (engine *engine) gotStarted(name string, worker worker.Worker) { // Copy current info; check preconditions and abort the workers if we've // already been asked to stop it. info := engine.current[name] switch { case info.worker != nil: engine.tomb.Kill(errors.Errorf("fatal: unexpected %q manifold worker start", name)) fallthrough case info.stopping, engine.isDying(): logger.Debugf("%q manifold worker no longer required", name) worker.Kill() default: // It's fine to use this worker; update info and copy back. logger.Infof("%q manifold worker started", name) info.starting = false info.worker = worker engine.current[name] = info // Any manifold that declares this one as an input needs to be restarted. engine.bounceDependents(name) } }
func stopWorker(w worker.Worker) error { w.Kill() return w.Wait() }
// CheckKill Kill()s the supplied worker and Wait()s for its error, which it // returns for further analysis, or fails the test after a timeout expires. // It doesn't Assert and is therefore suitable for use from any goroutine. func CheckKill(c *gc.C, w worker.Worker) error { w.Kill() return CheckKilled(c, w) }
func kill(w worker.Worker) error { w.Kill() return w.Wait() }
func (s *FinishedSuite) TestFinishedWorker(c *gc.C) { // Pretty dumb test if interface is implemented // and Wait() returns nil. var fw worker.Worker = worker.FinishedWorker{} c.Assert(fw.Wait(), gc.IsNil) }