|
@@ -3,6 +3,7 @@ package mock
|
|
|
import (
|
|
|
"fmt"
|
|
|
"reflect"
|
|
|
+ "regexp"
|
|
|
"runtime"
|
|
|
"strings"
|
|
|
"sync"
|
|
@@ -25,6 +26,7 @@ type TestingT interface {
|
|
|
// Call represents a method call and is used for setting expectations,
|
|
|
// as well as recording activity.
|
|
|
type Call struct {
|
|
|
+ Parent *Mock
|
|
|
|
|
|
// The name of the method that was or will be called.
|
|
|
Method string
|
|
@@ -47,123 +49,79 @@ type Call struct {
|
|
|
// Holds a handler used to manipulate arguments content that are passed by
|
|
|
// reference. It's useful when mocking methods such as unmarshalers or
|
|
|
// decoders.
|
|
|
- Run func(Arguments)
|
|
|
+ RunFn func(Arguments)
|
|
|
+}
|
|
|
+
|
|
|
+func newCall(parent *Mock, methodName string, methodArguments ...interface{}) *Call {
|
|
|
+ return &Call{
|
|
|
+ Parent: parent,
|
|
|
+ Method: methodName,
|
|
|
+ Arguments: methodArguments,
|
|
|
+ ReturnArguments: make([]interface{}, 0),
|
|
|
+ Repeatability: 0,
|
|
|
+ WaitFor: nil,
|
|
|
+ RunFn: nil,
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-// Mock is the workhorse used to track activity on another object.
|
|
|
-// For an example of its usage, refer to the "Example Usage" section at the top of this document.
|
|
|
-type Mock struct {
|
|
|
-
|
|
|
- // The method name that is currently
|
|
|
- // being referred to by the On method.
|
|
|
- onMethodName string
|
|
|
-
|
|
|
- // An array of the arguments that are
|
|
|
- // currently being referred to by the On method.
|
|
|
- onMethodArguments Arguments
|
|
|
-
|
|
|
- // Represents the calls that are expected of
|
|
|
- // an object.
|
|
|
- ExpectedCalls []Call
|
|
|
-
|
|
|
- // Holds the calls that were made to this mocked object.
|
|
|
- Calls []Call
|
|
|
-
|
|
|
- // TestData holds any data that might be useful for testing. Testify ignores
|
|
|
- // this data completely allowing you to do whatever you like with it.
|
|
|
- testData objx.Map
|
|
|
-
|
|
|
- mutex sync.Mutex
|
|
|
+func (self *Call) lock() {
|
|
|
+ self.Parent.mutex.Lock()
|
|
|
}
|
|
|
|
|
|
-// TestData holds any data that might be useful for testing. Testify ignores
|
|
|
-// this data completely allowing you to do whatever you like with it.
|
|
|
-func (m *Mock) TestData() objx.Map {
|
|
|
-
|
|
|
- if m.testData == nil {
|
|
|
- m.testData = make(objx.Map)
|
|
|
- }
|
|
|
-
|
|
|
- return m.testData
|
|
|
+func (self *Call) unlock() {
|
|
|
+ self.Parent.mutex.Unlock()
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- Setting expectations
|
|
|
-*/
|
|
|
+func (self *Call) Return(returnArguments ...interface{}) *Call {
|
|
|
+ self.lock()
|
|
|
+ defer self.unlock()
|
|
|
|
|
|
-// On starts a description of an expectation of the specified method
|
|
|
-// being called.
|
|
|
-//
|
|
|
-// Mock.On("MyMethod", arg1, arg2)
|
|
|
-func (m *Mock) On(methodName string, arguments ...interface{}) *Mock {
|
|
|
- m.onMethodName = methodName
|
|
|
- m.onMethodArguments = arguments
|
|
|
-
|
|
|
- for _, arg := range arguments {
|
|
|
- if v := reflect.ValueOf(arg); v.Kind() == reflect.Func {
|
|
|
- panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg))
|
|
|
- }
|
|
|
- }
|
|
|
+ self.ReturnArguments = returnArguments
|
|
|
|
|
|
- return m
|
|
|
-}
|
|
|
-
|
|
|
-// Return finishes a description of an expectation of the method (and arguments)
|
|
|
-// specified in the most recent On method call.
|
|
|
-//
|
|
|
-// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2)
|
|
|
-func (m *Mock) Return(returnArguments ...interface{}) *Mock {
|
|
|
- m.mutex.Lock()
|
|
|
- defer m.mutex.Unlock()
|
|
|
-
|
|
|
- m.ExpectedCalls = append(m.ExpectedCalls, Call{m.onMethodName, m.onMethodArguments, returnArguments, 0, nil, nil})
|
|
|
- return m
|
|
|
+ return self
|
|
|
}
|
|
|
|
|
|
// Once indicates that that the mock should only return the value once.
|
|
|
//
|
|
|
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once()
|
|
|
-func (m *Mock) Once() {
|
|
|
- m.mutex.Lock()
|
|
|
- m.ExpectedCalls[len(m.ExpectedCalls)-1].Repeatability = 1
|
|
|
- m.mutex.Unlock()
|
|
|
+func (self *Call) Once() *Call {
|
|
|
+ return self.Times(1)
|
|
|
}
|
|
|
|
|
|
// Twice indicates that that the mock should only return the value twice.
|
|
|
//
|
|
|
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice()
|
|
|
-func (m *Mock) Twice() {
|
|
|
- m.mutex.Lock()
|
|
|
- m.ExpectedCalls[len(m.ExpectedCalls)-1].Repeatability = 2
|
|
|
- m.mutex.Unlock()
|
|
|
+func (self *Call) Twice() *Call {
|
|
|
+ return self.Times(2)
|
|
|
}
|
|
|
|
|
|
// Times indicates that that the mock should only return the indicated number
|
|
|
// of times.
|
|
|
//
|
|
|
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
|
|
|
-func (m *Mock) Times(i int) {
|
|
|
- m.mutex.Lock()
|
|
|
- m.ExpectedCalls[len(m.ExpectedCalls)-1].Repeatability = i
|
|
|
- m.mutex.Unlock()
|
|
|
+func (self *Call) Times(i int) *Call {
|
|
|
+ self.lock()
|
|
|
+ defer self.unlock()
|
|
|
+ self.Repeatability = i
|
|
|
+ return self
|
|
|
}
|
|
|
|
|
|
// WaitUntil sets the channel that will block the mock's return until its closed
|
|
|
// or a message is received.
|
|
|
//
|
|
|
// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second))
|
|
|
-func (m *Mock) WaitUntil(w <-chan time.Time) *Mock {
|
|
|
- m.mutex.Lock()
|
|
|
- m.ExpectedCalls[len(m.ExpectedCalls)-1].WaitFor = w
|
|
|
- m.mutex.Unlock()
|
|
|
- return m
|
|
|
+func (self *Call) WaitUntil(w <-chan time.Time) *Call {
|
|
|
+ self.lock()
|
|
|
+ defer self.unlock()
|
|
|
+ self.WaitFor = w
|
|
|
+ return self
|
|
|
}
|
|
|
|
|
|
// After sets how long to block until the call returns
|
|
|
//
|
|
|
// Mock.On("MyMethod", arg1, arg2).After(time.Second)
|
|
|
-func (m *Mock) After(d time.Duration) *Mock {
|
|
|
- return m.WaitUntil(time.After(d))
|
|
|
+func (self *Call) After(d time.Duration) *Call {
|
|
|
+ return self.WaitUntil(time.After(d))
|
|
|
}
|
|
|
|
|
|
// Run sets a handler to be called before returning. It can be used when
|
|
@@ -174,24 +132,85 @@ func (m *Mock) After(d time.Duration) *Mock {
|
|
|
// arg := args.Get(0).(*map[string]interface{})
|
|
|
// arg["foo"] = "bar"
|
|
|
// })
|
|
|
-func (m *Mock) Run(fn func(Arguments)) *Mock {
|
|
|
- m.mutex.Lock()
|
|
|
- m.ExpectedCalls[len(m.ExpectedCalls)-1].Run = fn
|
|
|
- m.mutex.Unlock()
|
|
|
- return m
|
|
|
+func (self *Call) Run(fn func(Arguments)) *Call {
|
|
|
+ self.lock()
|
|
|
+ defer self.unlock()
|
|
|
+ self.RunFn = fn
|
|
|
+ return self
|
|
|
+}
|
|
|
+
|
|
|
+// On chains a new expectation description onto the mocked interface. This
|
|
|
+// allows syntax like.
|
|
|
+//
|
|
|
+// Mock.
|
|
|
+// On("MyMethod", 1).Return(nil).
|
|
|
+// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error"))
|
|
|
+func (self *Call) On(methodName string, arguments ...interface{}) *Call {
|
|
|
+ return self.Parent.On(methodName, arguments...)
|
|
|
+}
|
|
|
+
|
|
|
+// Mock is the workhorse used to track activity on another object.
|
|
|
+// For an example of its usage, refer to the "Example Usage" section at the top
|
|
|
+// of this document.
|
|
|
+type Mock struct {
|
|
|
+ // Represents the calls that are expected of
|
|
|
+ // an object.
|
|
|
+ ExpectedCalls []*Call
|
|
|
+
|
|
|
+ // Holds the calls that were made to this mocked object.
|
|
|
+ Calls []Call
|
|
|
+
|
|
|
+ // TestData holds any data that might be useful for testing. Testify ignores
|
|
|
+ // this data completely allowing you to do whatever you like with it.
|
|
|
+ testData objx.Map
|
|
|
+
|
|
|
+ mutex sync.Mutex
|
|
|
+}
|
|
|
+
|
|
|
+// TestData holds any data that might be useful for testing. Testify ignores
|
|
|
+// this data completely allowing you to do whatever you like with it.
|
|
|
+func (m *Mock) TestData() objx.Map {
|
|
|
+
|
|
|
+ if m.testData == nil {
|
|
|
+ m.testData = make(objx.Map)
|
|
|
+ }
|
|
|
+
|
|
|
+ return m.testData
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- Recording and responding to activity
|
|
|
+ Setting expectations
|
|
|
*/
|
|
|
|
|
|
+// On starts a description of an expectation of the specified method
|
|
|
+// being called.
|
|
|
+//
|
|
|
+// Mock.On("MyMethod", arg1, arg2)
|
|
|
+func (self *Mock) On(methodName string, arguments ...interface{}) *Call {
|
|
|
+ for _, arg := range arguments {
|
|
|
+ if v := reflect.ValueOf(arg); v.Kind() == reflect.Func {
|
|
|
+ panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ self.mutex.Lock()
|
|
|
+ defer self.mutex.Unlock()
|
|
|
+ c := newCall(self, methodName, arguments...)
|
|
|
+ self.ExpectedCalls = append(self.ExpectedCalls, c)
|
|
|
+ return c
|
|
|
+}
|
|
|
+
|
|
|
+// /*
|
|
|
+// Recording and responding to activity
|
|
|
+// */
|
|
|
+
|
|
|
func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *Call) {
|
|
|
for i, call := range m.expectedCalls() {
|
|
|
if call.Method == method && call.Repeatability > -1 {
|
|
|
|
|
|
_, diffCount := call.Arguments.Diff(arguments)
|
|
|
if diffCount == 0 {
|
|
|
- return i, &call
|
|
|
+ return i, call
|
|
|
}
|
|
|
|
|
|
}
|
|
@@ -209,7 +228,7 @@ func (m *Mock) findClosestCall(method string, arguments ...interface{}) (bool, *
|
|
|
_, tempDiffCount := call.Arguments.Diff(arguments)
|
|
|
if tempDiffCount < diffCount || diffCount == 0 {
|
|
|
diffCount = tempDiffCount
|
|
|
- closestCall = &call
|
|
|
+ closestCall = call
|
|
|
}
|
|
|
|
|
|
}
|
|
@@ -247,6 +266,14 @@ func (m *Mock) Called(arguments ...interface{}) Arguments {
|
|
|
panic("Couldn't get the caller information")
|
|
|
}
|
|
|
functionPath := runtime.FuncForPC(pc).Name()
|
|
|
+ //Next four lines are required to use GCCGO function naming conventions.
|
|
|
+ //For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock
|
|
|
+ //uses inteface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree
|
|
|
+ //With GCCGO we need to remove interface information starting from pN<dd>.
|
|
|
+ re := regexp.MustCompile("\\.pN\\d+_")
|
|
|
+ if re.MatchString(functionPath) {
|
|
|
+ functionPath = re.Split(functionPath, -1)[0]
|
|
|
+ }
|
|
|
parts := strings.Split(functionPath, ".")
|
|
|
functionName := parts[len(parts)-1]
|
|
|
|
|
@@ -272,17 +299,16 @@ func (m *Mock) Called(arguments ...interface{}) Arguments {
|
|
|
switch {
|
|
|
case call.Repeatability == 1:
|
|
|
call.Repeatability = -1
|
|
|
- m.ExpectedCalls[found] = *call
|
|
|
+
|
|
|
case call.Repeatability > 1:
|
|
|
call.Repeatability -= 1
|
|
|
- m.ExpectedCalls[found] = *call
|
|
|
}
|
|
|
m.mutex.Unlock()
|
|
|
}
|
|
|
|
|
|
// add the call
|
|
|
m.mutex.Lock()
|
|
|
- m.Calls = append(m.Calls, Call{functionName, arguments, make([]interface{}, 0), 0, nil, nil})
|
|
|
+ m.Calls = append(m.Calls, *newCall(m, functionName, arguments...))
|
|
|
m.mutex.Unlock()
|
|
|
|
|
|
// block if specified
|
|
@@ -290,12 +316,11 @@ func (m *Mock) Called(arguments ...interface{}) Arguments {
|
|
|
<-call.WaitFor
|
|
|
}
|
|
|
|
|
|
- if call.Run != nil {
|
|
|
- call.Run(arguments)
|
|
|
+ if call.RunFn != nil {
|
|
|
+ call.RunFn(arguments)
|
|
|
}
|
|
|
|
|
|
return call.ReturnArguments
|
|
|
-
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -390,10 +415,10 @@ func (m *Mock) methodWasCalled(methodName string, expected []interface{}) bool {
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
-func (m *Mock) expectedCalls() []Call {
|
|
|
+func (m *Mock) expectedCalls() []*Call {
|
|
|
m.mutex.Lock()
|
|
|
defer m.mutex.Unlock()
|
|
|
- return append([]Call{}, m.ExpectedCalls...)
|
|
|
+ return append([]*Call{}, m.ExpectedCalls...)
|
|
|
}
|
|
|
|
|
|
func (m *Mock) calls() []Call {
|