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  }