Skip to content

Commit

Permalink
Start work on v3
Browse files Browse the repository at this point in the history
  • Loading branch information
LandonTClipp committed Dec 26, 2024
1 parent f6c261b commit fce2e5c
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 330 deletions.
201 changes: 25 additions & 176 deletions cmd/mockery.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ package cmd

import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"runtime/pprof"
"strings"

"github.com/chigopher/pathlib"
Expand All @@ -19,7 +15,6 @@ import (
"github.com/vektra/mockery/v2/pkg/config"
"github.com/vektra/mockery/v2/pkg/logging"
"github.com/vektra/mockery/v2/pkg/stackerr"
"golang.org/x/tools/go/packages"
)

var (
Expand Down Expand Up @@ -182,15 +177,6 @@ func GetRootAppFromViper(v *viper.Viper) (*RootApp, error) {
}

func (r *RootApp) Run() error {
var recursive bool
var filter *regexp.Regexp
var limitOne bool

if r.Quiet {
// if "quiet" flag is set, disable logging
r.Config.LogLevel = ""
}

log, err := logging.GetLogger(r.Config.LogLevel)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to initialize logger: %v\n", err)
Expand All @@ -209,11 +195,6 @@ func (r *RootApp) Run() error {
fmt.Println(logging.GetSemverInfo())
return nil
}

var osp pkg.OutputStreamProvider
if r.Config.Print {
osp = &pkg.StdoutStreamProvider{}
}
buildTags := strings.Split(r.Config.BuildTags, " ")

var boilerplate string
Expand All @@ -225,176 +206,44 @@ func (r *RootApp) Run() error {
boilerplate = string(data)
}

if !r.Config.WithExpecter {
logging.WarnDeprecated(
ctx,
"with-expecter will be permanently set to True in v3",
map[string]any{
"url": logging.DocsURL("/deprecations/#with-expecter"),
},
)
}

configuredPackages, err := r.Config.GetPackages(ctx)
if err != nil && !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("failed to determine configured packages: %w", err)
if err != nil {
return fmt.Errorf("failed to get package from config: %w", err)
}
if len(configuredPackages) != 0 {
r.Config.LogUnsupportedPackagesConfig(ctx)

configuredPackages, err := r.Config.GetPackages(ctx)
if err != nil {
return fmt.Errorf("failed to get package from config: %w", err)
}
parser := pkg.NewParser(buildTags, pkg.ParserDisableFuncMocks(r.Config.DisableFuncMocks))

if err := parser.ParsePackages(ctx, configuredPackages); err != nil {
log.Error().Err(err).Msg("unable to parse packages")
return err
}
log.Info().Msg("done loading, visiting interface nodes")
for _, iface := range parser.Interfaces() {
ifaceLog := log.
With().
Str(logging.LogKeyInterface, iface.Name).
Str(logging.LogKeyQualifiedName, iface.QualifiedName).
Logger()

ifaceCtx := ifaceLog.WithContext(ctx)

shouldGenerate, err := r.Config.ShouldGenerateInterface(ifaceCtx, iface.QualifiedName, iface.Name)
if err != nil {
return err
}
if !shouldGenerate {
ifaceLog.Debug().Msg("config doesn't specify to generate this interface, skipping.")
continue
}
ifaceLog.Debug().Msg("config specifies to generate this interface")

outputter := pkg.NewOutputter(&r.Config, boilerplate, r.Config.DryRun)
if err := outputter.Generate(ifaceCtx, iface); err != nil {
return err
}
}

if len(configuredPackages) == 0 {
log.Error().Msg("no packages specified in config")
return nil
}
parser := pkg.NewParser(buildTags, pkg.ParserDisableFuncMocks(r.Config.DisableFuncMocks))

if r.Config.Name != "" && r.Config.All {
log.Fatal().Msgf("Specify --name or --all, but not both")
} else if (r.Config.FileName != "" || r.Config.StructName != "") && r.Config.All {
log.Fatal().Msgf("Cannot specify --filename or --structname with --all")
} else if r.Config.Dir != "" && r.Config.Dir != "." && r.Config.SrcPkg != "" {
log.Fatal().Msgf("Specify --dir or --srcpkg, but not both")
} else if r.Config.Name != "" {
recursive = r.Config.Recursive
if strings.ContainsAny(r.Config.Name, regexMetadataChars) {
if filter, err = regexp.Compile(r.Config.Name); err != nil {
log.Fatal().Err(err).Msgf("Invalid regular expression provided to -name")
} else if r.Config.FileName != "" || r.Config.StructName != "" {
log.Fatal().Msgf("Cannot specify --filename or --structname with regex in --name")
}
} else {
filter = regexp.MustCompile(fmt.Sprintf("^%s$", r.Config.Name))
limitOne = true
}
} else if r.Config.All {
recursive = true
filter = regexp.MustCompile(".*")
} else {
log.Fatal().Msgf("Use --name to specify the name of the interface or --all for all interfaces found")
if err := parser.ParsePackages(ctx, configuredPackages); err != nil {
log.Error().Err(err).Msg("unable to parse packages")
return err
}
log.Info().Msg("done loading, visiting interface nodes")
for _, iface := range parser.Interfaces() {
ifaceLog := log.
With().
Str(logging.LogKeyInterface, iface.Name).
Str(logging.LogKeyQualifiedName, iface.QualifiedName).
Logger()

logging.WarnDeprecated(
ctx,
"use of the packages config will be the only way to generate mocks in v3. Please migrate your config to use the packages feature.",
map[string]any{
"url": logging.DocsURL("/features/#packages-configuration"),
"migration": logging.DocsURL("/migrating_to_packages/"),
})
ifaceCtx := ifaceLog.WithContext(ctx)

if r.Config.Profile != "" {
f, err := os.Create(r.Config.Profile)
shouldGenerate, err := r.Config.ShouldGenerateInterface(ifaceCtx, iface.QualifiedName, iface.Name)
if err != nil {
return stackerr.NewStackErrf(err, "Failed to create profile file")
}
defer f.Close()
if err := pprof.StartCPUProfile(f); err != nil {
return fmt.Errorf("failed to start CPU profile: %w", err)
}
defer pprof.StopCPUProfile()
}

baseDir := r.Config.Dir

if osp == nil {
osp = &pkg.FileOutputStreamProvider{
Config: r.Config,
BaseDir: r.Config.Output,
InPackage: r.Config.InPackage,
InPackageSuffix: r.Config.InPackageSuffix,
TestOnly: r.Config.TestOnly,
Case: r.Config.Case,
KeepTree: r.Config.KeepTree,
KeepTreeOriginalDirectory: r.Config.Dir,
FileName: r.Config.FileName,
}
}

if r.Config.SrcPkg != "" {
pkgs, err := packages.Load(&packages.Config{
Mode: packages.NeedFiles,
}, r.Config.SrcPkg)
if err != nil || len(pkgs) == 0 {
log.Fatal().Err(err).Msgf("Failed to load package %s", r.Config.SrcPkg)
return err
}

// NOTE: we only pass one package name (config.SrcPkg) to packages.Load
// it should return one package at most
pkg := pkgs[0]

if pkg.Errors != nil {
log.Fatal().Err(pkg.Errors[0]).Msgf("Failed to load package %s", r.Config.SrcPkg)
if !shouldGenerate {
ifaceLog.Debug().Msg("config doesn't specify to generate this interface, skipping.")
continue
}
ifaceLog.Debug().Msg("config specifies to generate this interface")

if len(pkg.GoFiles) == 0 {
log.Fatal().Msgf("No go files in package %s", r.Config.SrcPkg)
outputter := pkg.NewOutputter(&r.Config, boilerplate, r.Config.DryRun)
if err := outputter.Generate(ifaceCtx, iface); err != nil {
return err
}
baseDir = filepath.Dir(pkg.GoFiles[0])
}

walker := pkg.Walker{
Config: r.Config,
BaseDir: baseDir,
Recursive: recursive,
Filter: filter,
LimitOne: limitOne,
BuildTags: buildTags,
}

visitor := pkg.NewGeneratorVisitor(pkg.GeneratorVisitorConfig{
Boilerplate: boilerplate,
DisableVersionString: r.Config.DisableVersionString,
Exported: r.Config.Exported,
InPackage: r.Config.InPackage,
KeepTree: r.Config.KeepTree,
Note: r.Config.Note,
MockBuildTags: r.Config.MockBuildTags,
PackageName: r.Config.Outpkg,
PackageNamePrefix: r.Config.Packageprefix,
StructName: r.Config.StructName,
UnrollVariadic: r.Config.UnrollVariadic,
WithExpecter: r.Config.WithExpecter,
ReplaceType: r.Config.ReplaceType,
ResolveTypeAlias: r.Config.ResolveTypeAlias,
}, osp, r.Config.DryRun)

generated := walker.Walk(ctx, visitor)

if r.Config.Name != "" && !generated {
log.Error().Msgf("Unable to find '%s' in any go files under this path", r.Config.Name)
return fmt.Errorf("unable to find interface")
}

return nil
Expand Down
41 changes: 0 additions & 41 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ type Config struct {
Anchors map[string]any `mapstructure:"_anchors"`
BoilerplateFile string `mapstructure:"boilerplate-file"`
BuildTags string `mapstructure:"tags"`
Case string `mapstructure:"case"`
Config string `mapstructure:"config"`
Cpuprofile string `mapstructure:"cpuprofile"`
Dir string `mapstructure:"dir"`
Expand All @@ -53,30 +52,21 @@ type Config struct {
IncludeAutoGenerated bool `mapstructure:"include-auto-generated"`
IncludeRegex string `mapstructure:"include-regex"`
Issue845Fix bool `mapstructure:"issue-845-fix"`
KeepTree bool `mapstructure:"keeptree"`
LogLevel string `mapstructure:"log-level"`
MockBuildTags string `mapstructure:"mock-build-tags"`
MockName string `mapstructure:"mockname"`
Name string `mapstructure:"name"`
Note string `mapstructure:"note"`
Outpkg string `mapstructure:"outpkg"`
Output string `mapstructure:"output"`
Packageprefix string `mapstructure:"packageprefix"`
Packages map[string]interface{} `mapstructure:"packages"`
Print bool `mapstructure:"print"`
Profile string `mapstructure:"profile"`
Quiet bool `mapstructure:"quiet"`
Recursive bool `mapstructure:"recursive"`
ReplaceType []string `mapstructure:"replace-type"`
ResolveTypeAlias bool `mapstructure:"resolve-type-alias"`
SrcPkg string `mapstructure:"srcpkg"`
// StructName overrides the name given to the mock struct and should only be nonempty
// when generating for an exact match (non regex expression in -name).
StructName string `mapstructure:"structname"`
TestOnly bool `mapstructure:"testonly"`
UnrollVariadic bool `mapstructure:"unroll-variadic"`
Version bool `mapstructure:"version"`
WithExpecter bool `mapstructure:"with-expecter"`
// Viper throws away case-sensitivity when it marshals into this struct. This
// destroys necessary information we need, specifically around interface names.
// So, we re-read the config into this map outside of viper.
Expand Down Expand Up @@ -794,34 +784,3 @@ func (c *Config) TagName(name string) string {
}
return string(field.Tag.Get("mapstructure"))
}

// LogUnsupportedPackagesConfig is a method that will help aid migrations to the
// packages config feature. This is intended to be a temporary measure until v3
// when we can remove all legacy config options.
func (c *Config) LogUnsupportedPackagesConfig(ctx context.Context) {
log := zerolog.Ctx(ctx)
unsupportedOptions := make(map[string]any)
for _, name := range []string{"Name", "KeepTree", "Case", "Output", "TestOnly"} {
value := reflect.ValueOf(c).Elem().FieldByName(name)
var valueAsString string
if value.Kind().String() == "bool" {
valueAsString = fmt.Sprintf("%v", value.Bool())
}
if value.Kind().String() == "string" {
valueAsString = value.String()
}

if !value.IsZero() {
unsupportedOptions[c.TagName(name)] = valueAsString
}
}
if len(unsupportedOptions) == 0 {
return
}

l := log.With().
Dict("unsupported-fields", zerolog.Dict().Fields(unsupportedOptions)).
Str("url", logging.DocsURL("/configuration/#parameter-descriptions")).
Logger()
l.Error().Msg("use of unsupported options detected. mockery behavior is undefined.")
}
9 changes: 1 addition & 8 deletions pkg/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,13 @@ type GeneratorConfig struct {
Boilerplate string
DisableVersionString bool
Exported bool
InPackage bool
Issue845Fix bool
KeepTree bool
Note string
MockBuildTags string
Outpkg string
PackageNamePrefix string
StructName string
UnrollVariadic bool
WithExpecter bool
ReplaceType []string
ResolveTypeAlias bool
}

// Generator is responsible for generating the string containing
Expand Down Expand Up @@ -813,9 +808,7 @@ func (g *Generator) Generate(ctx context.Context) error {
"type %s%s struct {\n\tmock.Mock\n}\n\n", g.mockName(), g.getTypeConstraintString(ctx),
)

if g.config.WithExpecter {
g.generateExpecterStruct(ctx)
}
g.generateExpecterStruct(ctx)

for _, method := range g.iface.Methods() {
g.generateMethod(ctx, method)
Expand Down
3 changes: 0 additions & 3 deletions pkg/outputter.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,16 +333,13 @@ func (m *Outputter) Generate(ctx context.Context, iface *Interface) error {
Exported: interfaceConfig.Exported,
InPackage: interfaceConfig.InPackage,
Issue845Fix: interfaceConfig.Issue845Fix,
KeepTree: interfaceConfig.KeepTree,
Note: interfaceConfig.Note,
MockBuildTags: interfaceConfig.MockBuildTags,
Outpkg: interfaceConfig.Outpkg,
PackageNamePrefix: interfaceConfig.Packageprefix,
StructName: interfaceConfig.MockName,
UnrollVariadic: interfaceConfig.UnrollVariadic,
WithExpecter: interfaceConfig.WithExpecter,
ReplaceType: interfaceConfig.ReplaceType,
ResolveTypeAlias: interfaceConfig.ResolveTypeAlias,
}
generator := NewGenerator(ctx, g, iface, interfaceConfig.Outpkg)

Expand Down
Loading

0 comments on commit fce2e5c

Please sign in to comment.