engine: resources: Add CheckApply event detection to resource tests
This adds the ability to wait with a timeout for CheckApply happenings in a resource. This helps avoid unnecessary long sleeping and timing guesses. This also adds a cleanup function to run at the end.
This commit is contained in:
@@ -22,6 +22,7 @@ package resources
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -92,6 +93,65 @@ func NewStartupStep(ms uint) Step {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type changedStep struct {
|
||||||
|
ms uint
|
||||||
|
expect bool // what checkOK value we're expecting
|
||||||
|
ch chan bool // set by test harness, filled with checkOK values
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *changedStep) Action() error {
|
||||||
|
select {
|
||||||
|
case checkOK, ok := <-obj.ch: // from CheckApply() in test Process loop
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("channel closed unexpectedly")
|
||||||
|
}
|
||||||
|
if checkOK != obj.expect {
|
||||||
|
return fmt.Errorf("got unexpected checkOK value of: %t", checkOK)
|
||||||
|
}
|
||||||
|
case <-time.After(time.Duration(obj.ms) * time.Millisecond):
|
||||||
|
return fmt.Errorf("took too long to startup")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (obj *changedStep) Expect() error { return nil }
|
||||||
|
|
||||||
|
// NewChangedStep waits up to this many ms for a CheckApply action to occur. Watch function to startup.
|
||||||
|
func NewChangedStep(ms uint, expect bool) Step {
|
||||||
|
return &changedStep{
|
||||||
|
ms: ms,
|
||||||
|
expect: expect,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type clearChangedStep struct {
|
||||||
|
ms uint
|
||||||
|
ch chan bool // set by test harness, filled with checkOK values
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *clearChangedStep) Action() error {
|
||||||
|
// read all pending events...
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case _, ok := <-obj.ch: // from CheckApply() in test Process loop
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("channel closed unexpectedly")
|
||||||
|
}
|
||||||
|
case <-time.After(time.Duration(obj.ms) * time.Millisecond):
|
||||||
|
return nil // done waiting
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (obj *clearChangedStep) Expect() error { return nil }
|
||||||
|
|
||||||
|
// NewClearChangedStep waits up to this many ms for additional CheckApply
|
||||||
|
// actions to occur, and flushes them all so that a future NewChangedStep won't
|
||||||
|
// see unwanted events.
|
||||||
|
func NewClearChangedStep(ms uint) Step {
|
||||||
|
return &clearChangedStep{
|
||||||
|
ms: ms,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestResources1(t *testing.T) {
|
func TestResources1(t *testing.T) {
|
||||||
type test struct { // an individual test
|
type test struct { // an individual test
|
||||||
name string
|
name string
|
||||||
@@ -101,6 +161,7 @@ func TestResources1(t *testing.T) {
|
|||||||
experrstr string // expected error prefix
|
experrstr string // expected error prefix
|
||||||
timeline []Step // TODO: this could be a generator that keeps pushing out steps until it's done!
|
timeline []Step // TODO: this could be a generator that keeps pushing out steps until it's done!
|
||||||
expect func() error // function to check for expected state
|
expect func() error // function to check for expected state
|
||||||
|
cleanup func() error // function to run as cleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
@@ -152,11 +213,14 @@ func TestResources1(t *testing.T) {
|
|||||||
r.Content = &contents
|
r.Content = &contents
|
||||||
|
|
||||||
timeline := []Step{
|
timeline := []Step{
|
||||||
NewStartupStep(3000), // startup
|
NewStartupStep(1000 * 60), // startup
|
||||||
|
NewChangedStep(1000*60, false), // did we do something?
|
||||||
fileExpect(p, s), // check initial state
|
fileExpect(p, s), // check initial state
|
||||||
|
NewClearChangedStep(1000 * 15), // did we do something?
|
||||||
fileWrite(p, "this is whatever\n"), // change state
|
fileWrite(p, "this is whatever\n"), // change state
|
||||||
sleep(1000), // wait for converge
|
NewChangedStep(1000*60, false), // did we do something?
|
||||||
fileExpect(p, s), // check again
|
fileExpect(p, s), // check again
|
||||||
|
sleep(1), // we can sleep too!
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases = append(testCases, test{
|
testCases = append(testCases, test{
|
||||||
@@ -165,6 +229,7 @@ func TestResources1(t *testing.T) {
|
|||||||
fail: false,
|
fail: false,
|
||||||
timeline: timeline,
|
timeline: timeline,
|
||||||
expect: func() error { return nil },
|
expect: func() error { return nil },
|
||||||
|
cleanup: func() error { return os.Remove(p) },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +245,7 @@ func TestResources1(t *testing.T) {
|
|||||||
}
|
}
|
||||||
names = append(names, tc.name)
|
names = append(names, tc.name)
|
||||||
t.Run(fmt.Sprintf("test #%d (%s)", index, tc.name), func(t *testing.T) {
|
t.Run(fmt.Sprintf("test #%d (%s)", index, tc.name), func(t *testing.T) {
|
||||||
res, fail, experr, experrstr, timeline, expect := tc.res, tc.fail, tc.experr, tc.experrstr, tc.timeline, tc.expect
|
res, fail, experr, experrstr, timeline, expect, cleanup := tc.res, tc.fail, tc.experr, tc.experrstr, tc.timeline, tc.expect, tc.cleanup
|
||||||
|
|
||||||
t.Logf("\n\ntest #%d: Res: %+v\n", index, res)
|
t.Logf("\n\ntest #%d: Res: %+v\n", index, res)
|
||||||
defer t.Logf("test #%d: done!", index)
|
defer t.Logf("test #%d: done!", index)
|
||||||
@@ -217,6 +282,7 @@ func TestResources1(t *testing.T) {
|
|||||||
t.Logf("test #%d: err: %+v", index, err)
|
t.Logf("test #%d: err: %+v", index, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changedChan := make(chan bool, 1) // buffered!
|
||||||
readyChan := make(chan struct{})
|
readyChan := make(chan struct{})
|
||||||
eventChan := make(chan struct{})
|
eventChan := make(chan struct{})
|
||||||
doneChan := make(chan struct{})
|
doneChan := make(chan struct{})
|
||||||
@@ -254,6 +320,13 @@ func TestResources1(t *testing.T) {
|
|||||||
// run init
|
// run init
|
||||||
t.Logf("test #%d: running Init", index)
|
t.Logf("test #%d: running Init", index)
|
||||||
err = res.Init(init)
|
err = res.Init(init)
|
||||||
|
defer func() {
|
||||||
|
t.Logf("test #%d: running cleanup()", index)
|
||||||
|
if err := cleanup(); err != nil {
|
||||||
|
t.Errorf("test #%d: FAIL", index)
|
||||||
|
t.Errorf("test #%d: could not cleanup: %+v", index, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
closeFn := func() {
|
closeFn := func() {
|
||||||
// run close (we don't ever expect an error on close!)
|
// run close (we don't ever expect an error on close!)
|
||||||
t.Logf("test #%d: running Close", index)
|
t.Logf("test #%d: running Close", index)
|
||||||
@@ -319,10 +392,16 @@ func TestResources1(t *testing.T) {
|
|||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
for ix, step := range timeline {
|
for ix, step := range timeline {
|
||||||
|
|
||||||
// magic setting of important value...
|
// magic setting of important values...
|
||||||
if s, ok := step.(*startupStep); ok {
|
if s, ok := step.(*startupStep); ok {
|
||||||
s.ch = readyChan
|
s.ch = readyChan
|
||||||
}
|
}
|
||||||
|
if s, ok := step.(*changedStep); ok {
|
||||||
|
s.ch = changedChan
|
||||||
|
}
|
||||||
|
if s, ok := step.(*clearChangedStep); ok {
|
||||||
|
s.ch = changedChan
|
||||||
|
}
|
||||||
|
|
||||||
t.Logf("test #%d: step(%d)...", index, ix)
|
t.Logf("test #%d: step(%d)...", index, ix)
|
||||||
if err := step.Action(); err != nil {
|
if err := step.Action(); err != nil {
|
||||||
@@ -356,8 +435,11 @@ func TestResources1(t *testing.T) {
|
|||||||
t.Errorf("test #%d: CheckApply failed: %s", index, err.Error())
|
t.Errorf("test #%d: CheckApply failed: %s", index, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_ = checkOK // TODO: do we look at this?
|
select {
|
||||||
|
// send a msg if we can, but never block
|
||||||
|
case changedChan <- checkOK:
|
||||||
|
default:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("test #%d: waiting for shutdown", index)
|
t.Logf("test #%d: waiting for shutdown", index)
|
||||||
|
|||||||
Reference in New Issue
Block a user