blob: 839530faa2ddd74caedecb612b37e85b153d1c97 [file] [log] [blame]
/*
Types and functions to help with testing code that uses exec.Run.
*/
package exec
import (
"regexp"
"sync"
)
// CommandCollector collects arguments to the Run method for later inspection. Safe for use in
// multiple goroutines as long as the function passed to SetDelegateRun is.
// Example usage:
// mock := CommandCollector{}
// SetRunForTesting(mock.Run)
// defer SetRunForTesting(DefaultRun)
// err := Run(&Command{
// Name: "touch",
// Args: []string{"/tmp/file"},
// })
// assert.Equal(t, "touch /tmp/file"", DebugString(mock.Commands()[0]))
type CommandCollector struct {
mutex sync.RWMutex
commands []*Command
delegateRun func(*Command) error
}
func (c *CommandCollector) Commands() []*Command {
c.mutex.RLock()
defer c.mutex.RUnlock()
// TODO(benjaminwagner): Can I just return c.commands?
result := make([]*Command, len(c.commands))
copy(result, c.commands)
return result
}
func (c *CommandCollector) ClearCommands() {
c.mutex.Lock()
defer c.mutex.Unlock()
c.commands = nil
}
func (c *CommandCollector) SetDelegateRun(delegateRun func(*Command) error) {
c.mutex.Lock()
defer c.mutex.Unlock()
c.delegateRun = delegateRun
}
// Collects command into c and delegates to the function specified by SetDelegateRun. Returns nil
// if SetDelegateRun has not been called. The command will be visible in Commands() before the
// SetDelegateRun function is called.
func (c *CommandCollector) Run(command *Command) error {
c.mutex.Lock()
c.commands = append(c.commands, command)
delegateRun := c.delegateRun
c.mutex.Unlock()
if delegateRun == nil {
return nil
} else {
return delegateRun(command)
}
}
// Provides a Run method that returns based on regexp matches of DebugString(command). Safe for use
// in multiple goroutines.
// Example usage:
// mock := MockRun{}
// SetRunForTesting(mock.Run)
// defer SetRunForTesting(DefaultRun)
// mock.AddRule("touch /tmp/bar", fmt.Errorf("baz"))
// assert.NoError(t, Run(&Command{
// Name: "touch",
// Args: []string{"/tmp/foo"},
// }))
// err := Run(&Command{
// Name: "touch",
// Args: []string{"/tmp/bar"},
// })
// assert.Error(t, err)
// assert.Contains(t, err.Error(), "baz")
type MockRun struct {
mutex sync.RWMutex
matchers []*regexp.Regexp
results []error
}
// Panics if expr is not a valid regexp.
func (m *MockRun) AddRule(expr string, err error) {
m.mutex.Lock()
defer m.mutex.Unlock()
m.matchers = append(m.matchers, regexp.MustCompile(expr))
m.results = append(m.results, err)
}
// Tries to match DebugString(command) against the regexps in the order of the calls to AddRule,
// with the first matched giving the return value. Returns nil if no regexps match.
func (m *MockRun) Run(command *Command) error {
m.mutex.RLock()
defer m.mutex.RUnlock()
commandStr := DebugString(command)
for i, matcher := range m.matchers {
if matcher.FindStringIndex(commandStr) != nil {
return m.results[i]
}
}
return nil
}