No variadic return types (#126)

While extracting the method and parameters from the method signature,
only check and apply the variadic flag for the input parameters and not
for the return arguments.

Issue details:

For an interface with variadic arguments and slice return type, moq was
generating an invalid mock:

	type I interface {
		Func(params ...interface{}) []byte
	}

	// ...
	type IMock struct {
		// FuncFunc mocks the Func method.
		FuncFunc func(params ...interface{}) ...byte

		// calls tracks calls to the methods.
		calls struct {
			// Func holds details about calls to the Func method.
			Func []struct {
				// Params is the params argument value.
				Params []interface{}
			}
		}
	}

On attempting to generate the mock in such an instance, the command
would fail on the formatting step:

	m.Mock: go/format: 35:30: expected ';', found '...' (and 3 more errors)

See https://github.com/matryer/moq/issues/124,
https://github.com/matryer/moq/pull/125.
This commit is contained in:
Suhas Karanth 2020-06-07 18:15:40 +05:30 committed by GitHub
parent 7721994d1b
commit 4638a53893
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 127 additions and 22 deletions

View File

@ -10,6 +10,7 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"text/template" "text/template"
@ -133,8 +134,7 @@ func (m *Mocker) Mock(w io.Writer, names ...string) error {
Name: meth.Name(), Name: meth.Name(),
} }
obj.Methods = append(obj.Methods, method) obj.Methods = append(obj.Methods, method)
method.Params = m.extractArgs(sig, sig.Params(), "in%d") method.Params, method.Returns = m.extractArgs(sig)
method.Returns = m.extractArgs(sig, sig.Results(), "out%d")
} }
doc.Objects = append(doc.Objects, obj) doc.Objects = append(doc.Objects, obj)
} }
@ -182,26 +182,30 @@ func (m *Mocker) packageQualifier(pkg *types.Package) string {
return pkg.Name() return pkg.Name()
} }
func (m *Mocker) extractArgs(sig *types.Signature, list *types.Tuple, nameFormat string) []*param { func (m *Mocker) extractArgs(sig *types.Signature) (params, results []*param) {
var params []*param pp := sig.Params()
listLen := list.Len() for i := 0; i < pp.Len(); i++ {
for ii := 0; ii < listLen; ii++ { p := m.buildParam(pp.At(i), "in"+strconv.Itoa(i+1))
p := list.At(ii)
name := p.Name()
if name == "" {
name = fmt.Sprintf(nameFormat, ii+1)
}
typename := types.TypeString(p.Type(), m.packageQualifier)
// check for final variadic argument // check for final variadic argument
variadic := sig.Variadic() && ii == listLen-1 && typename[0:2] == "[]" p.Variadic = sig.Variadic() && i == pp.Len()-1 && p.Type[0:2] == "[]"
param := &param{ params = append(params, p)
Name: name,
Type: typename,
Variadic: variadic,
} }
params = append(params, param)
rr := sig.Results()
for i := 0; i < rr.Len(); i++ {
results = append(results, m.buildParam(rr.At(i), "out"+strconv.Itoa(i+1)))
} }
return params
return
}
func (m *Mocker) buildParam(v *types.Var, fallbackName string) *param {
name := v.Name()
if name == "" {
name = fallbackName
}
typ := types.TypeString(v.Type(), m.packageQualifier)
return &param{Name: name, Type: typ}
} }
func pkgInfoFromPath(srcDir string, mode packages.LoadMode) (*packages.Package, error) { func pkgInfoFromPath(srcDir string, mode packages.LoadMode) (*packages.Package, error) {

View File

@ -191,7 +191,7 @@ func TestNotCreatingEmptyDirWhenPkgIsGiven(t *testing.T) {
} }
} }
// TestVeradicArguments tests to ensure variadic work as // TestVariadicArguments tests to ensure variadic work as
// expected. // expected.
// see https://github.com/matryer/moq/issues/5 // see https://github.com/matryer/moq/issues/5
func TestVariadicArguments(t *testing.T) { func TestVariadicArguments(t *testing.T) {
@ -219,6 +219,26 @@ func TestVariadicArguments(t *testing.T) {
} }
} }
// TestSliceResult tests to ensure slice return data type works as
// expected.
// see https://github.com/matryer/moq/issues/124
func TestSliceResult(t *testing.T) {
m, err := New(Config{SrcDir: "testpackages/variadic"})
if err != nil {
t.Fatalf("moq.New: %s", err)
}
var buf bytes.Buffer
if err = m.Mock(&buf, "Echoer"); err != nil {
t.Errorf("m.Mock: %s", err)
}
golden := filepath.Join("testpackages/variadic/testdata", "echoer.golden.go")
if err := matchGoldenFile(golden, buf.Bytes()); err != nil {
t.Errorf("check golden file: %s", err)
}
}
func TestNothingToReturn(t *testing.T) { func TestNothingToReturn(t *testing.T) {
m, err := New(Config{SrcDir: "testpackages/example"}) m, err := New(Config{SrcDir: "testpackages/example"})
if err != nil { if err != nil {
@ -320,7 +340,7 @@ func TestFormatter(t *testing.T) {
func matchGoldenFile(goldenFile string, actual []byte) error { func matchGoldenFile(goldenFile string, actual []byte) error {
// To update golden files, run the following: // To update golden files, run the following:
// go test -v -run ^<Test-Name>$ github.com/matryer/moq/pkg/moq -update // go test -v -run '^<Test-Name>$' github.com/matryer/moq/pkg/moq -update
if *update { if *update {
if err := ioutil.WriteFile(goldenFile, actual, 0644); err != nil { if err := ioutil.WriteFile(goldenFile, actual, 0644); err != nil {
return fmt.Errorf("write: %s: %s", goldenFile, err) return fmt.Errorf("write: %s: %s", goldenFile, err)

View File

@ -0,0 +1,5 @@
package variadic
type Echoer interface {
Echo(ss ...string) []string
}

View File

@ -0,0 +1,76 @@
// Code generated by moq; DO NOT EDIT.
// github.com/matryer/moq
package variadic
import (
"sync"
)
var (
lockEchoerMockEcho sync.RWMutex
)
// Ensure, that EchoerMock does implement Echoer.
// If this is not the case, regenerate this file with moq.
var _ Echoer = &EchoerMock{}
// EchoerMock is a mock implementation of Echoer.
//
// func TestSomethingThatUsesEchoer(t *testing.T) {
//
// // make and configure a mocked Echoer
// mockedEchoer := &EchoerMock{
// EchoFunc: func(ss ...string) []string {
// panic("mock out the Echo method")
// },
// }
//
// // use mockedEchoer in code that requires Echoer
// // and then make assertions.
//
// }
type EchoerMock struct {
// EchoFunc mocks the Echo method.
EchoFunc func(ss ...string) []string
// calls tracks calls to the methods.
calls struct {
// Echo holds details about calls to the Echo method.
Echo []struct {
// Ss is the ss argument value.
Ss []string
}
}
}
// Echo calls EchoFunc.
func (mock *EchoerMock) Echo(ss ...string) []string {
if mock.EchoFunc == nil {
panic("EchoerMock.EchoFunc: method is nil but Echoer.Echo was just called")
}
callInfo := struct {
Ss []string
}{
Ss: ss,
}
lockEchoerMockEcho.Lock()
mock.calls.Echo = append(mock.calls.Echo, callInfo)
lockEchoerMockEcho.Unlock()
return mock.EchoFunc(ss...)
}
// EchoCalls gets all the calls that were made to Echo.
// Check the length with:
// len(mockedEchoer.EchoCalls())
func (mock *EchoerMock) EchoCalls() []struct {
Ss []string
} {
var calls []struct {
Ss []string
}
lockEchoerMockEcho.RLock()
calls = mock.calls.Echo
lockEchoerMockEcho.RUnlock()
return calls
}