Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| // 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 | |
| } |