1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
// Copyright 2013, 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package testing
import (
"os/exec"
gc "gopkg.in/check.v1"
)
// CleanupSuite adds the ability to add cleanup functions that are called
// during either test tear down or suite tear down depending on the method
// called.
type CleanupSuite struct {
testStack []func(*gc.C)
suiteStack []func(*gc.C)
origSuite *CleanupSuite
testsStarted bool
inTest bool
tornDown bool
}
func (s *CleanupSuite) SetUpSuite(c *gc.C) {
s.suiteStack = nil
s.testStack = nil
s.origSuite = s
s.testsStarted = false
s.inTest = false
s.tornDown = false
}
func (s *CleanupSuite) TearDownSuite(c *gc.C) {
s.callStack(c, s.suiteStack)
s.suiteStack = nil
s.origSuite = nil
s.tornDown = true
}
func (s *CleanupSuite) SetUpTest(c *gc.C) {
s.testStack = nil
s.testsStarted = true
s.inTest = true
}
func (s *CleanupSuite) TearDownTest(c *gc.C) {
s.callStack(c, s.testStack)
s.testStack = nil
s.inTest = false
}
func (s *CleanupSuite) callStack(c *gc.C, stack []func(*gc.C)) {
for i := len(stack) - 1; i >= 0; i-- {
stack[i](c)
}
}
// AddCleanup pushes the cleanup function onto the stack of functions to be
// called during TearDownTest or TearDownSuite. TearDownTest will be used if
// SetUpTest has already been called, else we will use TearDownSuite
func (s *CleanupSuite) AddCleanup(cleanup func(*gc.C)) {
if s.origSuite == nil {
// This is either called before SetUpSuite or after
// TearDownSuite. Either way, we can't really trust that we're
// going to call Cleanup correctly.
if s.tornDown {
panic("unsafe to call AddCleanup after TearDownSuite")
} else {
panic("unsafe to call AddCleanup before SetUpSuite")
}
}
if s != s.origSuite {
// If you write a test like:
// func (s MySuite) TestFoo(c *gc.C) {
// s.AddCleanup(foo)
// }
// The AddCleanup call is unsafe because it modifes
// s.origSuite but that object disappears once TestFoo
// returns. So you have to use:
// func (s *MySuite) TestFoo(c *gc.C) if you want the Cleanup
// funcs.
panic("unsafe to call AddCleanup from non pointer receiver test")
}
if !s.inTest {
if s.testsStarted {
// This indicates that we are not currently in a test
// (inTest is false), but that we have already run a
// test for this test suite (testStarted is true).
// Making a Suite-level change here means that only
// some of the tests in the suite will see the change,
// which means it *isn't* a Suite (applies to all
// tests) level change.
panic("unsafe to call AddCleanup after a test has been torn down" +
" before a new test has been set up" +
" (Suite level changes only make sense before first test is run)")
}
// We either haven't called SetUpTest or we've already called
// TearDownTest, consider this a Suite level cleanup.
s.suiteStack = append(s.suiteStack, cleanup)
return
}
s.testStack = append(s.testStack, cleanup)
}
// PatchEnvironment sets the environment variable 'name' the the value passed
// in. The old value is saved and returned to the original value at test tear
// down time using a cleanup function.
func (s *CleanupSuite) PatchEnvironment(name, value string) {
restore := PatchEnvironment(name, value)
s.AddCleanup(func(*gc.C) { restore() })
}
// PatchEnvPathPrepend prepends the given path to the environment $PATH and restores the
// original path on test teardown.
func (s *CleanupSuite) PatchEnvPathPrepend(dir string) {
restore := PatchEnvPathPrepend(dir)
s.AddCleanup(func(*gc.C) { restore() })
}
// PatchValue sets the 'dest' variable the the value passed in. The old value
// is saved and returned to the original value at test tear down time using a
// cleanup function. The value must be assignable to the element type of the
// destination.
func (s *CleanupSuite) PatchValue(dest, value interface{}) {
restore := PatchValue(dest, value)
s.AddCleanup(func(*gc.C) { restore() })
}
// HookCommandOutput calls the package function of the same name to mock out
// the result of a particular comand execution, and will call the restore
// function on test teardown.
func (s *CleanupSuite) HookCommandOutput(
outputFunc *func(cmd *exec.Cmd) ([]byte, error),
output []byte,
err error,
) <-chan *exec.Cmd {
result, restore := HookCommandOutput(outputFunc, output, err)
s.AddCleanup(func(*gc.C) { restore() })
return result
}
|