forked from juju/juju
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds new testing infrastructure for filling a cache with objects from…
… state directly. Recruits new test controller in logging config API server tests.
- Loading branch information
Showing
4 changed files
with
272 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
// Copyright 2019 Canonical Ltd. | ||
// Licensed under the AGPLv3, see LICENCE file for details. | ||
|
||
package cachetest | ||
|
||
import ( | ||
"time" | ||
|
||
jc "github.com/juju/testing/checkers" | ||
gc "gopkg.in/check.v1" | ||
|
||
"github.com/juju/juju/core/cache" | ||
"github.com/juju/juju/state" | ||
"github.com/juju/juju/testing" | ||
) | ||
|
||
// TestController wraps a cache controller for testing. | ||
// It allows synchronisation of state objects with the cache | ||
// without the need for a multi-watcher and cache worker. | ||
type TestController struct { | ||
*cache.Controller | ||
|
||
matchers []func(interface{}) bool | ||
changes chan interface{} | ||
} | ||
|
||
// NewTestController returns creates and returns a new test controller | ||
// with an initial set of matchers for receiving cache event notifications. | ||
// The controller can be instantiated like this in suite/test setups in order | ||
// to retain a common set of matchers, but `Init` should be called in each | ||
// test (see below). | ||
func NewTestController(matchers ...func(interface{}) bool) *TestController { | ||
return &TestController{ | ||
matchers: matchers, | ||
} | ||
} | ||
|
||
// Init instantiates the inner cache controller and returns a channel for | ||
// synchronising tests. Based on the input matchers, cache events for those | ||
// types will be sent on the channel when the cache processes them. | ||
// | ||
// NOTE: It is recommended to perform this initialisation in the actual test | ||
// method rather than `SetupSuite` or `SetupTest` as different gc.C references | ||
// are supplied to each of those methods. | ||
func (tc *TestController) Init(c *gc.C, matchers ...func(interface{}) bool) <-chan interface{} { | ||
events := make(chan interface{}) | ||
matchers = append(tc.matchers, matchers...) | ||
|
||
notify := func(change interface{}) { | ||
send := false | ||
for _, m := range matchers { | ||
if m(change) { | ||
send = true | ||
break | ||
} | ||
} | ||
|
||
if send { | ||
c.Logf("sending %#v", change) | ||
select { | ||
case events <- change: | ||
case <-time.After(testing.LongWait): | ||
c.Fatalf("change not processed by test") | ||
} | ||
} | ||
} | ||
|
||
tc.changes = make(chan interface{}) | ||
cc, err := cache.NewController(cache.ControllerConfig{ | ||
Changes: tc.changes, | ||
Notify: notify, | ||
}) | ||
c.Assert(err, jc.ErrorIsNil) | ||
tc.Controller = cc | ||
|
||
return events | ||
} | ||
|
||
// UpdateModel updates the current model for the input state in the cache. | ||
func (tc *TestController) UpdateModel(c *gc.C, m *state.Model) { | ||
tc.SendChange(ModelChange(c, m)) | ||
} | ||
|
||
// UpdateCharm updates the input state charm in the cache. | ||
func (tc *TestController) UpdateCharm(modelUUID string, ch *state.Charm) { | ||
tc.SendChange(CharmChange(modelUUID, ch)) | ||
} | ||
|
||
// UpdateApplication updates the input state application in the cache. | ||
func (tc *TestController) UpdateApplication(c *gc.C, modelUUID string, app *state.Application) { | ||
tc.SendChange(ApplicationChange(c, modelUUID, app)) | ||
} | ||
|
||
// UpdateMachine updates the input state machine in the cache. | ||
func (tc *TestController) UpdateMachine(c *gc.C, modelUUID string, machine *state.Machine) { | ||
tc.SendChange(MachineChange(c, modelUUID, machine)) | ||
} | ||
|
||
func (tc *TestController) SendChange(change interface{}) { | ||
tc.changes <- change | ||
} | ||
|
||
// NextChange returns the next change processed by the cache that satisfies a | ||
// matcher, or fails the test with a time-out. | ||
// This method should receive the channel returned by a call to `Init`. | ||
func (tc *TestController) NextChange(c *gc.C, changes <-chan interface{}) interface{} { | ||
var obtained interface{} | ||
select { | ||
case obtained = <-changes: | ||
case <-time.After(testing.LongWait): | ||
c.Fatalf("change not processed by test") | ||
} | ||
return obtained | ||
} |
Oops, something went wrong.