forked from juju/testing
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cleanup.go
140 lines (126 loc) · 4.33 KB
/
cleanup.go
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
}