github.com/moitias/moq@v0.0.0-20240223074357-5eb0f0ba4054/pkg/moq/moq_test.go (about)

     1  package moq
     2  
     3  import (
     4  	"bytes"
     5  	"flag"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/pmezard/go-difflib/difflib"
    16  )
    17  
    18  var update = flag.Bool("update", false, "Update golden files.")
    19  
    20  func TestMoq(t *testing.T) {
    21  	m, err := New(Config{SrcDir: "testpackages/example"})
    22  	if err != nil {
    23  		t.Fatalf("moq.New: %s", err)
    24  	}
    25  	var buf bytes.Buffer
    26  	err = m.Mock(&buf, "PersonStore")
    27  	if err != nil {
    28  		t.Errorf("m.Mock: %s", err)
    29  	}
    30  	s := buf.String()
    31  	// assertions of things that should be mentioned
    32  	strs := []string{
    33  		"package example",
    34  		"type PersonStoreMock struct",
    35  		"CreateFunc func(ctx context.Context, person *Person, confirm bool) error",
    36  		"GetFunc func(ctx context.Context, id string) (*Person, error)",
    37  		"func (mock *PersonStoreMock) Create(ctx context.Context, person *Person, confirm bool) error",
    38  		"func (mock *PersonStoreMock) Get(ctx context.Context, id string) (*Person, error)",
    39  		"panic(\"PersonStoreMock.CreateFunc: method is nil but PersonStore.Create was just called\")",
    40  		"panic(\"PersonStoreMock.GetFunc: method is nil but PersonStore.Get was just called\")",
    41  		"mock.lockGet.Lock()",
    42  		"mock.calls.Get = append(mock.calls.Get, callInfo)",
    43  		"mock.lockGet.Unlock()",
    44  		"// ID is the id argument value",
    45  	}
    46  	for _, str := range strs {
    47  		if !strings.Contains(s, str) {
    48  			t.Errorf("expected but missing: \"%s\"", str)
    49  		}
    50  	}
    51  }
    52  
    53  func TestMoqWithStaticCheck(t *testing.T) {
    54  	m, err := New(Config{SrcDir: "testpackages/example"})
    55  	if err != nil {
    56  		t.Fatalf("moq.New: %s", err)
    57  	}
    58  	var buf bytes.Buffer
    59  	err = m.Mock(&buf, "PersonStore")
    60  	if err != nil {
    61  		t.Errorf("m.Mock: %s", err)
    62  	}
    63  	s := buf.String()
    64  	// assertions of things that should be mentioned
    65  	strs := []string{
    66  		"package example",
    67  		"var _ PersonStore = &PersonStoreMock{}",
    68  		"type PersonStoreMock struct",
    69  		"CreateFunc func(ctx context.Context, person *Person, confirm bool) error",
    70  		"GetFunc func(ctx context.Context, id string) (*Person, error)",
    71  		"func (mock *PersonStoreMock) Create(ctx context.Context, person *Person, confirm bool) error",
    72  		"func (mock *PersonStoreMock) Get(ctx context.Context, id string) (*Person, error)",
    73  		"panic(\"PersonStoreMock.CreateFunc: method is nil but PersonStore.Create was just called\")",
    74  		"panic(\"PersonStoreMock.GetFunc: method is nil but PersonStore.Get was just called\")",
    75  		"mock.lockGet.Lock()",
    76  		"mock.calls.Get = append(mock.calls.Get, callInfo)",
    77  		"mock.lockGet.Unlock()",
    78  		"// ID is the id argument value",
    79  	}
    80  	for _, str := range strs {
    81  		if !strings.Contains(s, str) {
    82  			t.Errorf("expected but missing: \"%s\"", str)
    83  		}
    84  	}
    85  }
    86  
    87  func TestMoqWithAlias(t *testing.T) {
    88  	m, err := New(Config{SrcDir: "testpackages/example"})
    89  	if err != nil {
    90  		t.Fatalf("moq.New: %s", err)
    91  	}
    92  	var buf bytes.Buffer
    93  	err = m.Mock(&buf, "PersonStore:AnotherPersonStoreMock")
    94  	if err != nil {
    95  		t.Errorf("m.Mock: %s", err)
    96  	}
    97  	s := buf.String()
    98  	// assertions of things that should be mentioned
    99  	strs := []string{
   100  		"package example",
   101  		"type AnotherPersonStoreMock struct",
   102  		"CreateFunc func(ctx context.Context, person *Person, confirm bool) error",
   103  		"GetFunc func(ctx context.Context, id string) (*Person, error)",
   104  		"func (mock *AnotherPersonStoreMock) Create(ctx context.Context, person *Person, confirm bool) error",
   105  		"func (mock *AnotherPersonStoreMock) Get(ctx context.Context, id string) (*Person, error)",
   106  		"panic(\"AnotherPersonStoreMock.CreateFunc: method is nil but PersonStore.Create was just called\")",
   107  		"panic(\"AnotherPersonStoreMock.GetFunc: method is nil but PersonStore.Get was just called\")",
   108  		"mock.lockGet.Lock()",
   109  		"mock.calls.Get = append(mock.calls.Get, callInfo)",
   110  		"mock.lockGet.Unlock()",
   111  		"// ID is the id argument value",
   112  	}
   113  	for _, str := range strs {
   114  		if !strings.Contains(s, str) {
   115  			t.Errorf("expected but missing: \"%s\"", str)
   116  		}
   117  	}
   118  }
   119  
   120  func TestMoqExplicitPackage(t *testing.T) {
   121  	m, err := New(Config{SrcDir: "testpackages/example", PkgName: "different"})
   122  	if err != nil {
   123  		t.Fatalf("moq.New: %s", err)
   124  	}
   125  	var buf bytes.Buffer
   126  	err = m.Mock(&buf, "PersonStore")
   127  	if err != nil {
   128  		t.Errorf("m.Mock: %s", err)
   129  	}
   130  	s := buf.String()
   131  	// assertions of things that should be mentioned
   132  	strs := []string{
   133  		"package different",
   134  		"type PersonStoreMock struct",
   135  		"CreateFunc func(ctx context.Context, person *example.Person, confirm bool) error",
   136  		"GetFunc func(ctx context.Context, id string) (*example.Person, error)",
   137  		"func (mock *PersonStoreMock) Create(ctx context.Context, person *example.Person, confirm bool) error",
   138  		"func (mock *PersonStoreMock) Get(ctx context.Context, id string) (*example.Person, error)",
   139  	}
   140  	for _, str := range strs {
   141  		if !strings.Contains(s, str) {
   142  			t.Errorf("expected but missing: \"%s\"", str)
   143  		}
   144  	}
   145  }
   146  
   147  func TestMoqExplicitPackageWithStaticCheck(t *testing.T) {
   148  	m, err := New(Config{SrcDir: "testpackages/example", PkgName: "different"})
   149  	if err != nil {
   150  		t.Fatalf("moq.New: %s", err)
   151  	}
   152  	var buf bytes.Buffer
   153  	err = m.Mock(&buf, "PersonStore")
   154  	if err != nil {
   155  		t.Errorf("m.Mock: %s", err)
   156  	}
   157  	s := buf.String()
   158  	// assertions of things that should be mentioned
   159  	strs := []string{
   160  		"package different",
   161  		"var _ example.PersonStore = &PersonStoreMock{}",
   162  		"type PersonStoreMock struct",
   163  		"CreateFunc func(ctx context.Context, person *example.Person, confirm bool) error",
   164  		"GetFunc func(ctx context.Context, id string) (*example.Person, error)",
   165  		"func (mock *PersonStoreMock) Create(ctx context.Context, person *example.Person, confirm bool) error",
   166  		"func (mock *PersonStoreMock) Get(ctx context.Context, id string) (*example.Person, error)",
   167  	}
   168  	for _, str := range strs {
   169  		if !strings.Contains(s, str) {
   170  			t.Errorf("expected but missing: \"%s\"", str)
   171  		}
   172  	}
   173  }
   174  
   175  func TestMoqSkipEnsure(t *testing.T) {
   176  	m, err := New(Config{SrcDir: "testpackages/example", PkgName: "different", SkipEnsure: true})
   177  	if err != nil {
   178  		t.Fatalf("moq.New: %s", err)
   179  	}
   180  	var buf bytes.Buffer
   181  	err = m.Mock(&buf, "PersonStore")
   182  	if err != nil {
   183  		t.Errorf("m.Mock: %s", err)
   184  	}
   185  	s := buf.String()
   186  	// assertions of things that should be mentioned
   187  	strs := []string{
   188  		"package different",
   189  		"type PersonStoreMock struct",
   190  		"CreateFunc func(ctx context.Context, person *example.Person, confirm bool) error",
   191  		"GetFunc func(ctx context.Context, id string) (*example.Person, error)",
   192  		"func (mock *PersonStoreMock) Create(ctx context.Context, person *example.Person, confirm bool) error",
   193  		"func (mock *PersonStoreMock) Get(ctx context.Context, id string) (*example.Person, error)",
   194  	}
   195  	for _, str := range strs {
   196  		if !strings.Contains(s, str) {
   197  			t.Errorf("expected but missing: \"%s\"", str)
   198  		}
   199  	}
   200  }
   201  
   202  func TestNotCreatingEmptyDirWhenPkgIsGiven(t *testing.T) {
   203  	m, err := New(Config{SrcDir: "testpackages/example", PkgName: "different"})
   204  	if err != nil {
   205  		t.Fatalf("moq.New: %s", err)
   206  	}
   207  	var buf bytes.Buffer
   208  	err = m.Mock(&buf, "PersonStore")
   209  	if err != nil {
   210  		t.Errorf("m.Mock: %s", err)
   211  	}
   212  	s := buf.String()
   213  	if len(s) == 0 {
   214  		t.Fatalf("mock should be generated")
   215  	}
   216  	if _, err := os.Stat("testpackages/example/different"); !os.IsNotExist(err) {
   217  		t.Fatalf("no empty dir should be created by moq")
   218  	}
   219  }
   220  
   221  // TestVariadicArguments tests to ensure variadic work as
   222  // expected.
   223  // see https://github.com/matryer/moq/issues/5
   224  func TestVariadicArguments(t *testing.T) {
   225  	m, err := New(Config{SrcDir: "testpackages/variadic"})
   226  	if err != nil {
   227  		t.Fatalf("moq.New: %s", err)
   228  	}
   229  	var buf bytes.Buffer
   230  	err = m.Mock(&buf, "Greeter")
   231  	if err != nil {
   232  		t.Errorf("m.Mock: %s", err)
   233  	}
   234  	s := buf.String()
   235  	// assertions of things that should be mentioned
   236  	strs := []string{
   237  		"package variadic",
   238  		"type GreeterMock struct",
   239  		"GreetFunc func(ctx context.Context, names ...string) string",
   240  		"return mock.GreetFunc(ctx, names...)",
   241  	}
   242  	for _, str := range strs {
   243  		if !strings.Contains(s, str) {
   244  			t.Errorf("expected but missing: \"%s\"", str)
   245  		}
   246  	}
   247  }
   248  
   249  func TestNothingToReturn(t *testing.T) {
   250  	m, err := New(Config{SrcDir: "testpackages/example"})
   251  	if err != nil {
   252  		t.Fatalf("moq.New: %s", err)
   253  	}
   254  	var buf bytes.Buffer
   255  	err = m.Mock(&buf, "PersonStore")
   256  	if err != nil {
   257  		t.Errorf("m.Mock: %s", err)
   258  	}
   259  	s := buf.String()
   260  	if strings.Contains(s, `return mock.ClearCacheFunc(id)`) {
   261  		t.Errorf("should not have return for items that have no return arguments")
   262  	}
   263  	// assertions of things that should be mentioned
   264  	strs := []string{
   265  		"mock.ClearCacheFunc(id)",
   266  	}
   267  	for _, str := range strs {
   268  		if !strings.Contains(s, str) {
   269  			t.Errorf("expected but missing: \"%s\"", str)
   270  		}
   271  	}
   272  }
   273  
   274  func TestImports(t *testing.T) {
   275  	m, err := New(Config{SrcDir: "testpackages/imports/two"})
   276  	if err != nil {
   277  		t.Fatalf("moq.New: %s", err)
   278  	}
   279  	var buf bytes.Buffer
   280  	err = m.Mock(&buf, "DoSomething")
   281  	if err != nil {
   282  		t.Errorf("m.Mock: %s", err)
   283  	}
   284  	s := buf.String()
   285  	strs := []string{
   286  		`	"sync"`,
   287  		`	"github.com/moitias/moq/pkg/moq/testpackages/imports/one"`,
   288  	}
   289  	for _, str := range strs {
   290  		if !strings.Contains(s, str) {
   291  			t.Errorf("expected but missing: \"%s\"", str)
   292  		}
   293  		if len(strings.Split(s, str)) > 2 {
   294  			t.Errorf("more than one: \"%s\"", str)
   295  		}
   296  	}
   297  }
   298  
   299  func TestMockGolden(t *testing.T) {
   300  	cases := []struct {
   301  		name       string
   302  		cfg        Config
   303  		interfaces []string
   304  		goldenFile string
   305  	}{
   306  		{
   307  			// Tests to ensure slice return data type works as expected.
   308  			// See https://github.com/matryer/moq/issues/124
   309  			name:       "SliceResult",
   310  			cfg:        Config{SrcDir: "testpackages/variadic"},
   311  			interfaces: []string{"Echoer"},
   312  			goldenFile: filepath.Join("testpackages/variadic", "echoer.golden.go"),
   313  		},
   314  		{
   315  			// Tests generation of mock where a method on the interface uses a
   316  			// blank identifier.
   317  			// See https://github.com/matryer/moq/issues/70
   318  			name:       "BlankID",
   319  			cfg:        Config{SrcDir: "testpackages/blankid"},
   320  			interfaces: []string{"Swallower"},
   321  			goldenFile: filepath.Join("testpackages/blankid", "swallower.golden.go"),
   322  		},
   323  		{
   324  			name:       "ChannelNames",
   325  			cfg:        Config{SrcDir: "testpackages/channels", StubImpl: true},
   326  			interfaces: []string{"Queuer"},
   327  			goldenFile: filepath.Join("testpackages/channels", "queuer_moq.golden.go"),
   328  		},
   329  		{
   330  			// Tests generation of mock when the interface imports a different
   331  			// package by the same name as it's own.
   332  			// See https://github.com/matryer/moq/issues/94
   333  			name:       "PkgShadow",
   334  			cfg:        Config{SrcDir: "testpackages/shadow/http", PkgName: "mock"},
   335  			interfaces: []string{"Thing"},
   336  			goldenFile: filepath.Join("testpackages/shadow/mock", "thing_moq.golden.go"),
   337  		},
   338  		{
   339  			// Tests generation of mock when a method parameter shadows an
   340  			// imported package name.
   341  			name:       "ParamShadow",
   342  			cfg:        Config{SrcDir: "testpackages/shadow"},
   343  			interfaces: []string{"Shadower"},
   344  			goldenFile: filepath.Join("testpackages/shadow", "shadower_moq.golden.go"),
   345  		},
   346  		{
   347  			name:       "ImportAlias",
   348  			cfg:        Config{SrcDir: "testpackages/importalias"},
   349  			interfaces: []string{"MiddleMan"},
   350  			goldenFile: filepath.Join("testpackages/importalias", "middleman_moq.golden.go"),
   351  		},
   352  		{
   353  			// Tests conflict resolution for generated names of method
   354  			// parameters.
   355  			name:       "ParamNameConflict",
   356  			cfg:        Config{SrcDir: "testpackages/paramconflict"},
   357  			interfaces: []string{"Interface"},
   358  			goldenFile: filepath.Join("testpackages/paramconflict", "iface_moq.golden.go"),
   359  		},
   360  		{
   361  			// Tests generation of names for unnamed method parameters.
   362  			name:       "GenerateParamNames",
   363  			cfg:        Config{SrcDir: "testpackages/genparamname"},
   364  			interfaces: []string{"Interface"},
   365  			goldenFile: filepath.Join("testpackages/genparamname", "iface_moq.golden.go"),
   366  		},
   367  		{
   368  			name:       "SyncImport",
   369  			cfg:        Config{SrcDir: "testpackages/syncimport"},
   370  			interfaces: []string{"Syncer"},
   371  			goldenFile: filepath.Join("testpackages/syncimport", "syncer_moq.golden.go"),
   372  		},
   373  		{
   374  			// Tests anonymous imports are not included in the generated mock.
   375  			name:       "AnonymousImport",
   376  			cfg:        Config{SrcDir: "testpackages/anonimport"},
   377  			interfaces: []string{"Example"},
   378  			goldenFile: filepath.Join("testpackages/anonimport", "iface_moq.golden.go"),
   379  		},
   380  		{
   381  			name:       "ShadowTypes",
   382  			cfg:        Config{SrcDir: "testpackages/shadowtypes"},
   383  			interfaces: []string{"ShadowTypes"},
   384  			goldenFile: filepath.Join("testpackages/shadowtypes", "shadowtypes_moq.golden.go"),
   385  		},
   386  		{
   387  			name:       "Generics",
   388  			cfg:        Config{SrcDir: "testpackages/generics"},
   389  			interfaces: []string{"GenericStore1", "GenericStore2", "AliasStore"},
   390  			goldenFile: filepath.Join("testpackages/generics", "generics_moq.golden.go"),
   391  		},
   392  		{
   393  			name:       "Generic Return Argument Type",
   394  			cfg:        Config{SrcDir: "testpackages/genericreturn"},
   395  			interfaces: []string{"IFooBar"},
   396  			goldenFile: filepath.Join("testpackages/genericreturn", "genericreturn.golden.go"),
   397  		},
   398  		{
   399  			name:       "TransientImport",
   400  			cfg:        Config{SrcDir: "testpackages/transientimport"},
   401  			interfaces: []string{"Transient"},
   402  			goldenFile: filepath.Join("testpackages/transientimport", "transient_moq.golden.go"),
   403  		},
   404  		{
   405  			name:       "WithResets",
   406  			cfg:        Config{SrcDir: "testpackages/withresets", WithResets: true},
   407  			interfaces: []string{"ResetStore"},
   408  			goldenFile: filepath.Join("testpackages/withresets", "withresets_moq.golden.go"),
   409  		},
   410  	}
   411  	for _, tc := range cases {
   412  		t.Run(tc.name, func(t *testing.T) {
   413  			m, err := New(tc.cfg)
   414  			if err != nil {
   415  				t.Fatalf("moq.New: %s", err)
   416  			}
   417  
   418  			var buf bytes.Buffer
   419  			if err = m.Mock(&buf, tc.interfaces...); err != nil {
   420  				t.Errorf("m.Mock: %s", err)
   421  				return
   422  			}
   423  
   424  			if err := matchGoldenFile(tc.goldenFile, buf.Bytes()); err != nil {
   425  				t.Errorf("check golden file: %s", err)
   426  			}
   427  		})
   428  	}
   429  }
   430  
   431  func TestFormatter(t *testing.T) {
   432  	cases := []struct {
   433  		name string
   434  		conf Config
   435  	}{
   436  		{name: "gofmt", conf: Config{SrcDir: "testpackages/imports/two"}},
   437  		{name: "goimports", conf: Config{SrcDir: "testpackages/imports/two", Formatter: "goimports"}},
   438  		{name: "noop", conf: Config{SrcDir: "testpackages/imports/two", Formatter: "noop"}},
   439  	}
   440  	for _, tc := range cases {
   441  		t.Run(tc.name, func(t *testing.T) {
   442  			m, err := New(tc.conf)
   443  			if err != nil {
   444  				t.Fatalf("moq.New: %s", err)
   445  			}
   446  			var buf bytes.Buffer
   447  			err = m.Mock(&buf, "DoSomething:"+tc.name+"Mock")
   448  			if err != nil {
   449  				t.Errorf("m.Mock: %s", err)
   450  			}
   451  
   452  			golden := filepath.Join("testpackages/imports/two", tc.name+".golden.go")
   453  			if err := matchGoldenFile(golden, buf.Bytes()); err != nil {
   454  				t.Errorf("check golden file: %s", err)
   455  			}
   456  		})
   457  	}
   458  }
   459  
   460  func matchGoldenFile(goldenFile string, actual []byte) error {
   461  	// To update golden files, run the following:
   462  	// go test -v -run '^<Test-Name>$' github.com/matryer/moq/pkg/moq -update
   463  	if *update {
   464  		if err := os.MkdirAll(filepath.Dir(goldenFile), 0o750); err != nil {
   465  			return fmt.Errorf("create dir: %s", err)
   466  		}
   467  		if err := ioutil.WriteFile(goldenFile, actual, 0o600); err != nil {
   468  			return fmt.Errorf("write: %s", err)
   469  		}
   470  
   471  		return nil
   472  	}
   473  
   474  	expected, err := ioutil.ReadFile(goldenFile)
   475  	if err != nil {
   476  		return fmt.Errorf("read: %s: %s", goldenFile, err)
   477  	}
   478  
   479  	// Normalise newlines
   480  	actual, expected = normalize(actual), normalize(expected)
   481  	if !bytes.Equal(expected, actual) {
   482  		diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
   483  			A:        difflib.SplitLines(string(expected)),
   484  			B:        difflib.SplitLines(string(actual)),
   485  			FromFile: "Expected",
   486  			ToFile:   "Actual",
   487  			Context:  1,
   488  		})
   489  		if err != nil {
   490  			return fmt.Errorf("diff: %s", err)
   491  		}
   492  		return fmt.Errorf("match: %s:\n%s", goldenFile, diff)
   493  	}
   494  
   495  	return nil
   496  }
   497  
   498  func TestVendoredPackages(t *testing.T) {
   499  	m, err := New(Config{SrcDir: "testpackages/vendoring/user"})
   500  	if err != nil {
   501  		t.Fatalf("moq.New: %s", err)
   502  	}
   503  	var buf bytes.Buffer
   504  	err = m.Mock(&buf, "Service")
   505  	if err != nil {
   506  		t.Errorf("mock error: %s", err)
   507  	}
   508  	s := buf.String()
   509  	// assertions of things that should be mentioned
   510  	strs := []string{
   511  		`"github.com/sudo-suhas/moq-test-pkgs/somerepo"`,
   512  	}
   513  	for _, str := range strs {
   514  		if !strings.Contains(s, str) {
   515  			t.Errorf("expected but missing: \"%s\"", str)
   516  		}
   517  	}
   518  }
   519  
   520  func TestVendoredInterface(t *testing.T) {
   521  	m, err := New(Config{
   522  		SrcDir:  "testpackages/vendoring/vendor/github.com/sudo-suhas/moq-test-pkgs/somerepo",
   523  		PkgName: "someother",
   524  	})
   525  	if err != nil {
   526  		t.Fatalf("moq.New: %s", err)
   527  	}
   528  	var buf bytes.Buffer
   529  	err = m.Mock(&buf, "SomeService")
   530  	if err != nil {
   531  		t.Errorf("mock error: %s", err)
   532  	}
   533  	s := buf.String()
   534  	// assertions of things that should be mentioned
   535  	strs := []string{
   536  		`"github.com/sudo-suhas/moq-test-pkgs/somerepo"`,
   537  	}
   538  	for _, str := range strs {
   539  		if !strings.Contains(s, str) {
   540  			t.Errorf("expected but missing: \"%s\"", str)
   541  		}
   542  	}
   543  	incorrectImport := `"github.com/moitias/moq/pkg/moq/testpackages/vendoring/vendor/github.com/sudo-suhas/moq-test-pkgs/somerepo"`
   544  	if strings.Contains(s, incorrectImport) {
   545  		t.Errorf("unexpected import: %s", incorrectImport)
   546  	}
   547  }
   548  
   549  func TestVendoredBuildConstraints(t *testing.T) {
   550  	m, err := New(Config{SrcDir: "testpackages/buildconstraints/user"})
   551  	if err != nil {
   552  		t.Fatalf("moq.New: %s", err)
   553  	}
   554  	var buf bytes.Buffer
   555  	err = m.Mock(&buf, "Service")
   556  	if err != nil {
   557  		t.Errorf("mock error: %s", err)
   558  	}
   559  	s := buf.String()
   560  	// assertions of things that should be mentioned
   561  	strs := []string{
   562  		`"github.com/sudo-suhas/moq-test-pkgs/buildconstraints"`,
   563  	}
   564  	for _, str := range strs {
   565  		if !strings.Contains(s, str) {
   566  			t.Errorf("expected but missing: \"%s\"", str)
   567  		}
   568  	}
   569  }
   570  
   571  // TestDotImports tests for https://github.com/matryer/moq/issues/21.
   572  func TestDotImports(t *testing.T) {
   573  	preDir, err := os.Getwd()
   574  	if err != nil {
   575  		t.Errorf("Getwd: %s", err)
   576  	}
   577  	err = os.Chdir("testpackages/dotimport")
   578  	if err != nil {
   579  		t.Errorf("Chdir: %s", err)
   580  	}
   581  	defer func() {
   582  		err := os.Chdir(preDir)
   583  		if err != nil {
   584  			t.Errorf("Chdir back: %s", err)
   585  		}
   586  	}()
   587  	m, err := New(Config{SrcDir: ".", PkgName: "moqtest_test"})
   588  	if err != nil {
   589  		t.Fatalf("moq.New: %s", err)
   590  	}
   591  	var buf bytes.Buffer
   592  	err = m.Mock(&buf, "Service")
   593  	if err != nil {
   594  		t.Errorf("mock error: %s", err)
   595  	}
   596  	s := buf.String()
   597  	if strings.Contains(s, `"."`) {
   598  		t.Error("contains invalid dot import")
   599  	}
   600  }
   601  
   602  func TestEmptyInterface(t *testing.T) {
   603  	m, err := New(Config{SrcDir: "testpackages/emptyinterface"})
   604  	if err != nil {
   605  		t.Fatalf("moq.New: %s", err)
   606  	}
   607  	var buf bytes.Buffer
   608  	err = m.Mock(&buf, "Empty")
   609  	if err != nil {
   610  		t.Errorf("mock error: %s", err)
   611  	}
   612  	s := buf.String()
   613  	if strings.Contains(s, `"sync"`) {
   614  		t.Error("contains sync import, although this package isn't used")
   615  	}
   616  }
   617  
   618  func TestGoGenerateVendoredPackages(t *testing.T) {
   619  	cmd := exec.Command("go", "generate", "./...")
   620  	cmd.Dir = "testpackages/gogenvendoring"
   621  	stdout, err := cmd.StdoutPipe()
   622  	if err != nil {
   623  		t.Errorf("StdoutPipe: %s", err)
   624  	}
   625  	defer stdout.Close()
   626  	err = cmd.Start()
   627  	if err != nil {
   628  		t.Errorf("Start: %s", err)
   629  	}
   630  	buf := bytes.NewBuffer(nil)
   631  	io.Copy(buf, stdout)
   632  	err = cmd.Wait()
   633  	if err != nil {
   634  		if exitErr, ok := err.(*exec.ExitError); ok {
   635  			t.Errorf("Wait: %s %s", exitErr, string(exitErr.Stderr))
   636  		} else {
   637  			t.Errorf("Wait: %s", err)
   638  		}
   639  	}
   640  	s := buf.String()
   641  	if strings.Contains(s, `vendor/`) {
   642  		t.Error("contains vendor directory in import path")
   643  	}
   644  }
   645  
   646  func TestImportedPackageWithSameName(t *testing.T) {
   647  	m, err := New(Config{SrcDir: "testpackages/samenameimport"})
   648  	if err != nil {
   649  		t.Fatalf("moq.New: %s", err)
   650  	}
   651  	var buf bytes.Buffer
   652  	err = m.Mock(&buf, "Example")
   653  	if err != nil {
   654  		t.Errorf("mock error: %s", err)
   655  	}
   656  	s := buf.String()
   657  	if !strings.Contains(s, `a samename.A`) {
   658  		t.Error("missing samename.A to address the struct A from the external package samename")
   659  	}
   660  }
   661  
   662  func TestParseError(t *testing.T) {
   663  	_, err := New(Config{SrcDir: "testpackages/_parseerror/service"})
   664  	if err == nil {
   665  		t.Errorf("expected error but got nil")
   666  		return
   667  	}
   668  	// The first clause is the common error. Only in go version 1.19.10 the error differs
   669  	if !strings.Contains(err.Error(), `could not import github.com/matryer/notexist (invalid package name: "")`) && !strings.Contains(err.Error(), "stderr: go build github.com/matryer/notexist") {
   670  		t.Errorf("unexpected error: %s", err.Error())
   671  	}
   672  }
   673  
   674  func TestMockError(t *testing.T) {
   675  	m, err := New(Config{SrcDir: "testpackages/example"})
   676  	if err != nil {
   677  		t.Fatalf("moq.New: %s", err)
   678  	}
   679  	cases := []struct {
   680  		name     string
   681  		namePair string
   682  		wantErr  string
   683  	}{
   684  		{
   685  			name:     "TypeNotFound",
   686  			namePair: "DoesNotExist",
   687  			wantErr:  "interface not found: DoesNotExist",
   688  		},
   689  		{
   690  			name:     "UnexpectedType",
   691  			namePair: "Person",
   692  			wantErr:  "Person (github.com/matryer/moq/pkg/moq/testpackages/example.Person) is not an interface",
   693  		},
   694  	}
   695  	for _, tc := range cases {
   696  		t.Run(tc.name, func(t *testing.T) {
   697  			err := m.Mock(ioutil.Discard, tc.namePair)
   698  			if err == nil {
   699  				t.Errorf("expected error but got nil")
   700  				return
   701  			}
   702  			if !strings.Contains(err.Error(), tc.wantErr) {
   703  				t.Errorf("unexpected error: %s", err.Error())
   704  			}
   705  		})
   706  	}
   707  }
   708  
   709  // normalize normalizes \r\n (windows) and \r (mac)
   710  // into \n (unix)
   711  func normalize(d []byte) []byte {
   712  	// Source: https://www.programming-books.io/essential/go/normalize-newlines-1d3abcf6f17c4186bb9617fa14074e48
   713  	// replace CR LF \r\n (windows) with LF \n (unix)
   714  	d = bytes.Replace(d, []byte{13, 10}, []byte{10}, -1)
   715  	// replace CF \r (mac) with LF \n (unix)
   716  	d = bytes.Replace(d, []byte{13}, []byte{10}, -1)
   717  	return d
   718  }