Skip to content

Commit

Permalink
Add YAML configuration support
Browse files Browse the repository at this point in the history
  • Loading branch information
kelveny committed Jun 5, 2021
1 parent 229c058 commit 5be5a30
Show file tree
Hide file tree
Showing 20 changed files with 852 additions and 95 deletions.
62 changes: 60 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ go install github.com/kelveny/mockcompose
## Usage

```
mockcompose can be launched with following options
mockcompose generates mocking implementation for Go classes, interfaces and functions.
-c string
name of the source class to generate against
-help
if set, print usage information
-i string
name of the source interface to generate against
-mock value
Expand Down Expand Up @@ -305,4 +306,61 @@ func TestMockVariadic(t *testing.T) {
<br/>

### __Answer__: If you've gone through FAQ answers above, you know that `mockcompose` of course can!
<br/>

### 5. How do I configure `go generate` in YAML?
<br/>

### __Answer__: Check out `mockcompose` self-test example [yaml](https://github.com/kelveny/mockcompose/tree/main/test/yaml)
<br/>

`go generate` configuration: mocks.go
```go
//go:generate mockcompose
package yaml
```

`go generate` YAML configuration file: .mockcompose.yaml
```yaml
mockcompose:
- name: mockFmt
testOnly: true
sourcePkg: fmt
mock:
- Sprintf
- name: mockJson
testOnly: true
sourcePkg: encoding/json
mock:
- Marshal
- name: mockSampleClz
testOnly: true
className: sampleClz
real:
- "methodThatUsesGlobalFunction,fmt=fmtMock"
- name: mockSampleClz2
testOnly: true
className: sampleClz
real:
- "methodThatUsesMultileGlobalFunctions,fmt=fmtMock:json=jsonMock"
- name: mockSampleClz3
testOnly: true
className: sampleClz
real:
- "methodThatUsesMultileGlobalFunctions,fmt=fmtMock"
- name: MockSampleInterface
testOnly: true
interfaceName: SampleInterface
- name: mockFoo
testOnly: true
interfaceName: Foo
sourcePkg: github.com/kelveny/mockcompose/test/foo
- name: mockFmtclonedFuncs
testOnly: true
real:
- "functionThatUsesMultileGlobalFunctions,fmt=fmtMock:json=jsonMock"
- "functionThatUsesGlobalFunction,fmt=fmtMock"
- "functionThatUsesMultileGlobalFunctions2,fmt=fmtMock"
```
If `mockcompose` detects `.mockcompose.yaml` or `.mockcompose.yml` in package directory, it will load code generation configuration from the file.

26 changes: 16 additions & 10 deletions cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,22 @@ package %s
`
)

type commandOptions struct {
mockName *string
mockPkg *string
clzName *string
intfName *string
srcPkg *string
testOnly *bool

methodsToClone stringSlice
methodsToMock stringSlice
// must be public for it to be used in loading YAML configuration
type CommandOptions struct {
MockName string `yaml:"name"`
MockPkg string `yaml:"mockPkg"`
ClzName string `yaml:"className"`
IntfName string `yaml:"interfaceName"`
SrcPkg string `yaml:"sourcePkg"`
TestOnly bool `yaml:"testOnly"`

MethodsToClone []string `yaml:"real,flow"`
MethodsToMock []string `yaml:"mock,flow"`
}

// must be public for it to be used in loading YAML configuration
type Config struct {
Mockcompose []CommandOptions `yaml:"mockcompose,flow"`
}

type parsedFileGenerator interface {
Expand Down
194 changes: 129 additions & 65 deletions cmd/mockcompose.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ package cmd
import (
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"

yaml "gopkg.in/yaml.v2"

"github.com/kelveny/mockcompose/pkg/gofile"
"github.com/kelveny/mockcompose/pkg/logger"
Expand All @@ -24,112 +28,97 @@ mockcompose generates mocking implementation for Go classes, interfaces and func
os.Exit(1)
}

func Execute() {
var methodsToClone stringSlice
var methodsToMock stringSlice

vb := flag.Bool("v", false, "if set, print verbose logging messages")
testOnly := flag.Bool("testonly", true, "if set, append _test to generated file name")
prtVersion := flag.Bool("version", false, "if set, print version information")
mockName := flag.String("n", "", "name of the generated class")
mockPkg := flag.String("pkg", "", "name of the package that the generated class resides")
clzName := flag.String("c", "", "name of the source class to generate against")
srcPkg := flag.String("p", "", "path of the source package in which to search interfaces and functions")
intfName := flag.String("i", "", "name of the source interface to generate against")
flag.Var(&methodsToClone, "real", "name of the method function to be cloned from source class or source function")
flag.Var(&methodsToMock, "mock", "name of the function to be mocked")

flag.Parse()

if *prtVersion {
fmt.Println(GetSemverInfo())
os.Exit(0)
func loadConfig() *Config {
pkgDir, err := filepath.Abs("")
logger.Log(logger.VERBOSE, "Check directory %s for YAML configuration\n", pkgDir)
if err != nil {
logger.Log(logger.ERROR, "Error in accessing file system. error: %s\n", err)
os.Exit(1)
}

if *vb {
logger.LogLevel = int(logger.VERBOSE)
if cfg := loadYamlConfig(filepath.Join(pkgDir, ".mockcompose.yaml")); cfg != nil {
return cfg
}

logger.Log(logger.VERBOSE, "Set logging to verbose mode\n")
if cfg := loadYamlConfig(filepath.Join(pkgDir, ".mockcompose.yml")); cfg != nil {
return cfg
}

if *mockPkg == "" {
*mockPkg = gofile.DerivePackage()
return nil
}

logger.Log(logger.VERBOSE, "Derive package name as: %s\n", *mockPkg)
}
fmt.Println()
func loadYamlConfig(yamlFile string) *Config {
yamlConfig, err := ioutil.ReadFile(yamlFile)
if err == nil {
cfg := Config{}

if *mockName == "" {
usage()
os.Exit(1)
err := yaml.Unmarshal(yamlConfig, &cfg)
if err != nil {
logger.Log(logger.ERROR, "Failed to load YAML config: %s\n", err)
}
return &cfg
}

options := &commandOptions{
mockName,
mockPkg,
clzName,
intfName,
srcPkg,
testOnly,
methodsToClone,
methodsToMock,
}
return nil
}

func executeOptions(options *CommandOptions) {
var g parsedFileGenerator
if *clzName != "" {
if len(methodsToClone) == 0 {

if options.ClzName != "" {
if len(options.MethodsToClone) == 0 {
logger.Log(logger.ERROR, "Please specify at least one real method name with -real option\n")
os.Exit(1)
}

g = &classMethodGenerator{
clzName: *options.clzName,
mockPkgName: *options.mockPkg,
mockName: *options.mockName,
methodsToClone: options.methodsToClone,
methodsToMock: options.methodsToMock,
clzName: options.ClzName,
mockPkgName: options.MockPkg,
mockName: options.MockName,
methodsToClone: options.MethodsToClone,
methodsToMock: options.MethodsToMock,
}
} else if *intfName != "" {
} else if options.IntfName != "" {
g = &interfaceMockGenerator{
mockPkgName: *options.mockPkg,
mockName: *options.mockName,
intfName: *options.intfName,
mockPkgName: options.MockPkg,
mockName: options.MockName,
intfName: options.IntfName,
}

if *options.srcPkg != "" {
if options.SrcPkg != "" {
scanPackageToGenerate(g.(loadedPackageGenerator), options)
return
}
} else {
if len(methodsToMock) == 0 && len(methodsToClone) == 0 {
if len(options.MethodsToMock) == 0 && len(options.MethodsToClone) == 0 {
logger.Log(logger.ERROR, "no function to mock or clone\n")
os.Exit(1)
}

if len(methodsToMock) > 0 && len(methodsToClone) > 0 {
if len(options.MethodsToMock) > 0 && len(options.MethodsToClone) > 0 {
logger.Log(logger.ERROR, "option -real and option -mock are exclusive in function clone generation\n")
os.Exit(1)
}

if len(methodsToClone) > 0 {
if *options.srcPkg != "" {
if len(options.MethodsToClone) > 0 {
if options.SrcPkg != "" {
logger.Log(logger.PROMPT,
"No source package support in function clone generation, ignore source package %s\n",
*options.srcPkg)
options.SrcPkg)
}
g = &functionCloneGenerator{
mockPkgName: *options.mockPkg,
mockName: *options.mockName,
methodsToClone: *&options.methodsToClone,
mockPkgName: options.MockPkg,
mockName: options.MockName,
methodsToClone: options.MethodsToClone,
}
} else {
g = &functionMockGenerator{
mockPkgName: *options.mockPkg,
mockName: *options.mockName,
methodsToMock: *&options.methodsToMock,
mockPkgName: options.MockPkg,
mockName: options.MockName,
methodsToMock: options.MethodsToMock,
}

if *options.srcPkg != "" {
if options.SrcPkg != "" {
scanPackageToGenerate(g.(loadedPackageGenerator), options)
return
}
Expand All @@ -138,3 +127,78 @@ func Execute() {

scanCWDToGenerate(g, options)
}

func Execute() {
var methodsToClone stringSlice
var methodsToMock stringSlice

vb := flag.Bool("v", false, "if set, print verbose logging messages")
testOnly := flag.Bool("testonly", true, "if set, append _test to generated file name")
prtVersion := flag.Bool("version", false, "if set, print version information")
help := flag.Bool("help", false, "if set, print usage information")
mockName := flag.String("n", "", "name of the generated class")
mockPkg := flag.String("pkg", "", "name of the package that the generated class resides")
clzName := flag.String("c", "", "name of the source class to generate against")
srcPkg := flag.String("p", "", "path of the source package in which to search interfaces and functions")
intfName := flag.String("i", "", "name of the source interface to generate against")
flag.Var(&methodsToClone, "real", "name of the method function to be cloned from source class or source function")
flag.Var(&methodsToMock, "mock", "name of the function to be mocked")

flag.Parse()

if *prtVersion {
fmt.Println(GetSemverInfo())
os.Exit(0)
}

if *help {
usage()
os.Exit(0)
}

if *vb {
logger.LogLevel = int(logger.VERBOSE)

logger.Log(logger.VERBOSE, "Set logging to verbose mode\n")
}

if cfg := loadConfig(); cfg != nil {
logger.Log(logger.VERBOSE, "Found mockcompose YAML configuration, ignore command line options\n")

derivedPkg := gofile.DerivePackage()
for _, options := range cfg.Mockcompose {
if options.MockPkg == "" {
options.MockPkg = derivedPkg
}

executeOptions(&options)
}

return
}

if *mockPkg == "" {
*mockPkg = gofile.DerivePackage()

logger.Log(logger.VERBOSE, "Derive package name as: %s\n", *mockPkg)
}
fmt.Println()

if *mockName == "" {
usage()
os.Exit(1)
}

options := &CommandOptions{
MockName: *mockName,
MockPkg: *mockPkg,
ClzName: *clzName,
IntfName: *intfName,
SrcPkg: *srcPkg,
TestOnly: *testOnly,
MethodsToClone: methodsToClone,
MethodsToMock: methodsToMock,
}

executeOptions(options)
}
Loading

0 comments on commit 5be5a30

Please sign in to comment.