2ae606f132
* Internal registry for disambiguated imports, vars - Move functionality in the moq package partially into internal/{registry,template}. - Leverage registry to assign unique package and variable/method parameter names. Use import aliases if present in interface source package. BREAKING CHANGE: When the interface definition does not mention the parameter names, the field names in call info anonymous struct will be different. The new field names are generated using the type info (string -> s, int -> n, chan int -> intCh, []MyType -> myTypes, map[string]int -> stringToInt etc.). For example, for a string parameter previously if the field name was 'In1', the new field could be 'S' or 'S1' (depends on number of string method parameters). * Refactor golden file tests to be table-driven * Fix sync pkg alias handling for moq generation * Improve, add tests (increase coverage) * Use $.Foo in template, avoid declaring variables $ is set to the data argument passed to Execute, that is, to the starting value of dot. Variables were declared to be able to refer to the parent context. * Consistent template field formatting * Use tabs in generated Godoc comments' example code * Minor simplification * go generate * Fix conflict for generated param name of pointer type Excellent work by @sudo-suhas.
191 lines
4.8 KiB
Go
191 lines
4.8 KiB
Go
package template
|
|
|
|
import (
|
|
"io"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"github.com/matryer/moq/internal/registry"
|
|
)
|
|
|
|
// Template is the Moq template. It is capable of generating the Moq
|
|
// implementation for the given template.Data.
|
|
type Template struct {
|
|
tmpl *template.Template
|
|
}
|
|
|
|
// New returns a new instance of Template.
|
|
func New() (Template, error) {
|
|
tmpl, err := template.New("moq").Funcs(templateFuncs).Parse(moqTemplate)
|
|
if err != nil {
|
|
return Template{}, err
|
|
}
|
|
|
|
return Template{tmpl: tmpl}, nil
|
|
}
|
|
|
|
// Execute generates and writes the Moq implementation for the given
|
|
// data.
|
|
func (t Template) Execute(w io.Writer, data Data) error {
|
|
return t.tmpl.Execute(w, data)
|
|
}
|
|
|
|
// moqTemplate is the template for mocked code.
|
|
// language=GoTemplate
|
|
var moqTemplate = `// Code generated by moq; DO NOT EDIT.
|
|
// github.com/matryer/moq
|
|
|
|
package {{.PkgName}}
|
|
|
|
import (
|
|
{{- range .Imports}}
|
|
{{. | ImportStatement}}
|
|
{{- end}}
|
|
)
|
|
|
|
{{range $i, $mock := .Mocks -}}
|
|
|
|
{{- if not $.SkipEnsure -}}
|
|
// Ensure, that {{.MockName}} does implement {{$.SrcPkgQualifier}}{{.InterfaceName}}.
|
|
// If this is not the case, regenerate this file with moq.
|
|
var _ {{$.SrcPkgQualifier}}{{.InterfaceName}} = &{{.MockName}}{}
|
|
{{- end}}
|
|
|
|
// {{.MockName}} is a mock implementation of {{$.SrcPkgQualifier}}{{.InterfaceName}}.
|
|
//
|
|
// func TestSomethingThatUses{{.InterfaceName}}(t *testing.T) {
|
|
//
|
|
// // make and configure a mocked {{$.SrcPkgQualifier}}{{.InterfaceName}}
|
|
// mocked{{.InterfaceName}} := &{{.MockName}}{
|
|
{{- range .Methods}}
|
|
// {{.Name}}Func: func({{.ArgList}}) {{.ReturnArgTypeList}} {
|
|
// panic("mock out the {{.Name}} method")
|
|
// },
|
|
{{- end}}
|
|
// }
|
|
//
|
|
// // use mocked{{.InterfaceName}} in code that requires {{$.SrcPkgQualifier}}{{.InterfaceName}}
|
|
// // and then make assertions.
|
|
//
|
|
// }
|
|
type {{.MockName}} struct {
|
|
{{- range .Methods}}
|
|
// {{.Name}}Func mocks the {{.Name}} method.
|
|
{{.Name}}Func func({{.ArgList}}) {{.ReturnArgTypeList}}
|
|
{{end}}
|
|
// calls tracks calls to the methods.
|
|
calls struct {
|
|
{{- range .Methods}}
|
|
// {{.Name}} holds details about calls to the {{.Name}} method.
|
|
{{.Name}} []struct {
|
|
{{- range .Params}}
|
|
// {{.Name | Exported}} is the {{.Name}} argument value.
|
|
{{.Name | Exported}} {{.TypeString}}
|
|
{{- end}}
|
|
}
|
|
{{- end}}
|
|
}
|
|
{{- range .Methods}}
|
|
lock{{.Name}} {{$.Imports | SyncPkgQualifier}}.RWMutex
|
|
{{- end}}
|
|
}
|
|
{{range .Methods}}
|
|
// {{.Name}} calls {{.Name}}Func.
|
|
func (mock *{{$mock.MockName}}) {{.Name}}({{.ArgList}}) {{.ReturnArgTypeList}} {
|
|
{{- if not $.StubImpl}}
|
|
if mock.{{.Name}}Func == nil {
|
|
panic("{{$mock.MockName}}.{{.Name}}Func: method is nil but {{$mock.InterfaceName}}.{{.Name}} was just called")
|
|
}
|
|
{{- end}}
|
|
callInfo := struct {
|
|
{{- range .Params}}
|
|
{{.Name | Exported}} {{.TypeString}}
|
|
{{- end}}
|
|
}{
|
|
{{- range .Params}}
|
|
{{.Name | Exported}}: {{.Name}},
|
|
{{- end}}
|
|
}
|
|
mock.lock{{.Name}}.Lock()
|
|
mock.calls.{{.Name}} = append(mock.calls.{{.Name}}, callInfo)
|
|
mock.lock{{.Name}}.Unlock()
|
|
{{- if .Returns}}
|
|
{{- if $.StubImpl}}
|
|
if mock.{{.Name}}Func == nil {
|
|
var (
|
|
{{- range .Returns}}
|
|
{{.Name}} {{.TypeString}}
|
|
{{- end}}
|
|
)
|
|
return {{.ReturnArgNameList}}
|
|
}
|
|
{{- end}}
|
|
return mock.{{.Name}}Func({{.ArgCallList}})
|
|
{{- else}}
|
|
{{- if $.StubImpl}}
|
|
if mock.{{.Name}}Func == nil {
|
|
return
|
|
}
|
|
{{- end}}
|
|
mock.{{.Name}}Func({{.ArgCallList}})
|
|
{{- end}}
|
|
}
|
|
|
|
// {{.Name}}Calls gets all the calls that were made to {{.Name}}.
|
|
// Check the length with:
|
|
// len(mocked{{$mock.InterfaceName}}.{{.Name}}Calls())
|
|
func (mock *{{$mock.MockName}}) {{.Name}}Calls() []struct {
|
|
{{- range .Params}}
|
|
{{.Name | Exported}} {{.TypeString}}
|
|
{{- end}}
|
|
} {
|
|
var calls []struct {
|
|
{{- range .Params}}
|
|
{{.Name | Exported}} {{.TypeString}}
|
|
{{- end}}
|
|
}
|
|
mock.lock{{.Name}}.RLock()
|
|
calls = mock.calls.{{.Name}}
|
|
mock.lock{{.Name}}.RUnlock()
|
|
return calls
|
|
}
|
|
{{end -}}
|
|
{{end -}}`
|
|
|
|
// This list comes from the golint codebase. Golint will complain about any of
|
|
// these being mixed-case, like "Id" instead of "ID".
|
|
var golintInitialisms = []string{
|
|
"ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "LHS",
|
|
"QPS", "RAM", "RHS", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "UID", "UUID", "URI",
|
|
"URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS",
|
|
}
|
|
|
|
var templateFuncs = template.FuncMap{
|
|
"ImportStatement": func(imprt *registry.Package) string {
|
|
if imprt.Alias == "" {
|
|
return `"` + imprt.Path() + `"`
|
|
}
|
|
return imprt.Alias + ` "` + imprt.Path() + `"`
|
|
},
|
|
"SyncPkgQualifier": func(imports []*registry.Package) string {
|
|
for _, imprt := range imports {
|
|
if imprt.Path() == "sync" {
|
|
return imprt.Qualifier()
|
|
}
|
|
}
|
|
|
|
return "sync"
|
|
},
|
|
"Exported": func(s string) string {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
for _, initialism := range golintInitialisms {
|
|
if strings.ToUpper(s) == initialism {
|
|
return initialism
|
|
}
|
|
}
|
|
return strings.ToUpper(s[0:1]) + s[1:]
|
|
},
|
|
}
|