Coder Social home page Coder Social logo

counterfeiter's People

Contributors

beono avatar davidnewhall avatar dennisme avatar dependabot-preview[bot] avatar dependabot[bot] avatar dnephin avatar glyn avatar hoegaarden avatar howardjohn avatar jhvhs avatar joefitzgerald avatar kehrlann avatar martinxsliu avatar maxbrunsfeld avatar nilsjpwerner avatar nkovacs avatar ralph7c2 avatar rittneje avatar robdimsdale avatar rosenhouse avatar sheelc avatar socalnick avatar spenczar avatar sykesm avatar tinygrasshopper avatar tjarratt avatar tomwhoiscontrary avatar vito avatar zikaeroh avatar zmb3 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

counterfeiter's Issues

Fails to identify functions that return multiple args of a single type

When trying to generate fakes for interface that has a method that looks like this:

type Hello interface{
  SayHello() (a, b string)
}

The output is the following:

type FakeHello struct {
    SayHelloStub        func() (a, b string)
    sayHelloMutex       sync.RWMutex
    sayHelloArgsForCall []struct{}
    sayHelloReturns     struct {
        result1 string
    }
}

func (fake *FakeHello) SayHello() (a, b string) {
    fake.sayHelloMutex.Lock()
    fake.sayHelloArgsForCall = append(fake.sayHelloArgsForCall, struct{}{})
    fake.sayHelloMutex.Unlock()
    if fake.SayHelloStub != nil {
        return fake.SayHelloStub()
    } else {
        return fake.sayHelloReturns.result1
    }
}

func (fake *FakeHello) SayHelloCallCount() int {
    fake.sayHelloMutex.RLock()
    defer fake.sayHelloMutex.RUnlock()
    return len(fake.sayHelloArgsForCall)
}

func (fake *FakeHello) SayHelloReturns(result1 string) {
    fake.SayHelloStub = nil
    fake.sayHelloReturns = struct {
        result1 string
    }{result1}
}

The second return value is missing from the sayHelloReturns struct, so the fake is not implementing the Hello interface.

Remove "fix" directory

If publish new version or tag a new release, remove the fix directory so tab completion works better.

Race condition when calling different methods in parallel

I am seeing a race condition sporadically when calling different methods in parallel. It only happens when different methods are called, since calling the same method in parallel leads to the method mutex being invoked, and preventing race conditions.

An example test case (nested within the existing when two methods are called at the same time describe block):

        It("records its calls without race conditions", func(done Done) {
            go fake.DoNothing()
            go fake.DoThings("abc", 1)
            go fake.DoASlice([]byte{})

            Eventually(len(fake.Invocations()["DoNothing"]), 1.0).Should(Equal(1))
            Eventually(len(fake.Invocations()["DoThings"]), 1.0).Should(Equal(1))
            Eventually(len(fake.Invocations()["DoASlice"]), 1.0).Should(Equal(1))

            close(done)
        })

Leads to output like:

WARNING: DATA RACE
Write by goroutine 17:
  github.com/maxbrunsfeld/counterfeiter/fixtures/fixturesfakes.(*FakeSomething).DoThings()
      /Users/bazza/go/src/github.com/maxbrunsfeld/counterfeiter/fixtures/fixturesfakes/fake_something.go:43 +0x297

Previous read by goroutine 15:
  github.com/maxbrunsfeld/counterfeiter_test.glob.func1.14.2()
      /Users/bazza/go/src/github.com/maxbrunsfeld/counterfeiter/counterfeiter_test.go:195 +0x197

(and a lot more redacted for the sake of brevity)

Attempting to sleuth a little, it seems like this was probably a result of trying to record invocations. The counterfeit method for DoASlice for instance looks like this:

func (fake *FakeSomething) DoASlice(arg1 []byte) {
    var arg1Copy []byte
    if arg1 != nil {
        arg1Copy = make([]byte, len(arg1))
        copy(arg1Copy, arg1)
    }
    fake.doASliceMutex.Lock()
    fake.doASliceArgsForCall = append(fake.doASliceArgsForCall, struct {
        arg1 []byte
    }{arg1Copy})
    fake.guard("DoASlice")
    fake.invocations["DoASlice"] = append(fake.invocations["DoASlice"], []interface{}{arg1Copy})
    fake.doASliceMutex.Unlock()
    if fake.DoASliceStub != nil {
        fake.DoASliceStub(arg1)
    }
}

Note that there is a mutex for the specific DoASlice method, but not for the call to fake.guard and fake.invocations["DoASlice"] which both mutate the fake.invocations field. If calls were made to multiple methods on the same fake, they would both mutate the fake.invocations field at the same time.

Unfortunately I can't get the test case to be 100% deterministic, since one goroutine might complete before the other starts. However, running 3 methods in parallel (as in the above test case) seems to be more consistent than just 2.

Import cycle when using the fake in the same package as the interface

This causes an import cycle:

package counterfeit

//go:generate counterfeiter . Fooer

type Fooer interface {
	Foo() bool
}

func Bar(f Fooer) string {
	if f.Foo() {
		return "foo"
	} else {
		return "bar"
	}
}
package counterfeit

import (
	"temp/counterfeit/counterfeitfakes"
	"testing"
)

func TestBar(t *testing.T) {
	fake := &counterfeitfakes.FakeFooer{}

	Bar(fake)
}

This is caused by the interface check at the bottom of fake_fooer.go:

var _ counterfeit.Fooer = new(FakeFooer)

One solution is to change the test file's package to counterfeit_tests, but that doesn't work if I want to test unexported functions.

#8 is similar, but that one is solvable by renaming the import. This isn't.

Is this check really necessary though? If you try to use the fake in a test, and it no longer satisfies the interface, it'll fail to compile anyway (well, except if you're doing something tricky with interface{}).
Could you maybe add an option to disable this check?

wrong import is generated for hyphenated package

Before the source code is passed to goimports, counterfeiter generates this import into fixturesfakes/fake_imports_go_hyphen_package.go (from https://github.com/maxbrunsfeld/counterfeiter/blob/b8c427d36da1fb0067515646ab4063195f8f89e0/fixtures/imports_go_hyphen_package.go):

go_hyphenpackage "github.com/maxbrunsfeld/counterfeiter/fixtures/go-hyphenpackage"

However, that's not how the package is used in the code. It's used as hyphenpackage, so goimports removes this unused import, and inserts a new one. Since I have a fork, and the fork's name is shorter, it selects the fixture from my fork.

Sending output to stdout produces invalid Go

I'm evaluating Counterfeiter as compared to mockery, mockgen, etc. Something I noticed is that this invocation:

$ counterfeiter .  XYZClient - > /tmp/z

Produces a file ending with a diagnostic message:

$ tail -3 /tmp/z
var _ XYZClient = new(FakeXYZClient)

Wrote `FakeXYZClient` to `xyzfakes/fake_xyzclient.go`

Probably this message (and any other similar) should go to stderr instead.

Tests are not locally rerunnable

Running the test suit with ./scripts/test.sh works the first time but hangs on race condition the second time.

I was able to rerun the tests after running git clean -fx

Examples for changing output using -o don't seem to work

The help manual for counterfeiter prints:

OPTIONS
	-o
		Path to the file or directory for the generated fakes.
		This also determines the package name that will be used.
		By default, the generated fakes will be generated in
		the package "xyzfakes" which is nested in package "xyz",
		where "xyz" is the name of referenced package.

I tried using -o without success. Here's what I did:

I have an interface in packageA named MyInterface.

From the root of my project, I run:

counterfeiter -o ./fakes MyInterface ./packageA

And the output is:

No such file or directory ''

I tried many other variations and couldn't get any to work. Suggestions?

Generate counterfeiter fakes for stdlib interfaces

Often times we want to use interfaces that exist in the stdlib

Since counterfeiter requires you to point it at the source file which has the interface definition, this is pretty much impossible to do programatically, especially in go:generate comments

This would be a huge win.

unused imports in generated files

The fake seems to just copy all the import statements from the real file. We have a member of a value object in the real file that required an import, but which is not used anywhere in the fake. So we can't build after generating a fake for one of these files because the fake has an unused import we have to remove manually.

Counterfeiter allows ambiguous naming when importing packages

When counterfeiting the following interface, with imports:

import (
	semver "github.com/cppforlife/go-semi-semantic/version"
	boshrel "github.com/cloudfoundry/bosh-cli/release"
)

type ReleaseDir interface {
	BuildRelease(name string, version semver.Version, force bool) (boshrel.Release, error)
	FinalizeRelease(release boshrel.Release, force bool) error
}

Counterfeiter generates functions that look like the following:

func (fake *FakeReleaseDir) FinalizeRelease(release release.Release, force bool) error {
	fake.finalizeReleaseMutex.Lock()
	fake.finalizeReleaseArgsForCall = append(fake.finalizeReleaseArgsForCall, struct {
		// WARNING, WARNING, AMBIGUOUS REFERENCE!
		release release.Release
		force   bool
	}{release, force})
	fake.recordInvocation("FinalizeRelease", []interface{}{release, force})
	fake.finalizeReleaseMutex.Unlock()
	if fake.FinalizeReleaseStub != nil {
		return fake.FinalizeReleaseStub(release, force)
	} else {
		return fake.finalizeReleaseReturns.result1
	}
}

and

func (fake *FakeReleaseDir) BuildRelease(name string, version version.Version, force bool) (release.Release, error) {
	fake.buildReleaseMutex.Lock()
	fake.buildReleaseArgsForCall = append(fake.buildReleaseArgsForCall, struct {
		name    string
		// WARNING, WARNING, AMBIGUOUS REFERENCE!
		version version.Version
		force   bool
	}{name, version, force})
	fake.recordInvocation("BuildRelease", []interface{}{name, version, force})
	fake.buildReleaseMutex.Unlock()
	if fake.BuildReleaseStub != nil {
		return fake.BuildReleaseStub(name, version, force)
	} else {
		return fake.buildReleaseReturns.result1, fake.buildReleaseReturns.result2
	}
}

Notice the ambiguous use of version and release, inside the method it is unclear whether release.Release is a reference to a package-namespaced type or a data-member of the method parameter release

Love,
Zak

Proposal: Fakes should be generated into a "___fakes" package

I've been using counterfeiter a lot lately. One thing I notice is that once I generate fakes for two or more packages I'm likely to want to use fakes from multiple packages in a single test. This often means I have to alias my imports...

import (
  "net/http"
  "os"

  dbFakes   "github.com/tjarratt/foo/database/fakes"
  httpFakes "github.com/tjarratt/foo/http/fakes"
)

This kind of sucks because existing tooling doesn't have a great answer to aliased packages (I'm looking at you, vim, emacs, Intelli-J and Sublime), and you constantly have to remember if you can use the fakes package name as is, or if you need to alias it.

What if (hear me out here), we followed the standard library model, and put our fakes into a "__fakes" package? For example, "net/http" has a "net/http/httptest" package that exposes some convenient test helpers. I believe that pivotal-golang/lager does this as well. This pattern seems to be emergent, and convenient since the _fakes package name is much less likely to collide, but still captures the same spirit of the standard library.

For existing code using the fakes package, we could write a "go fix" script that automatically rewrites your code and puts the source files in the (new) correct package. That should be a relatively painless way of dealing with the breakage for current users. (Tip of the hat to @robdimsdale for suggesting this).

I would like to issue a PR to counterfeiter that changes fakes to xyzfakes where xyz is the name of the package being faked.

Thoughts? Concerns? This is a breaking change, so I wanted to put this out here and solicit feedback first.

Create spies for type aliased functions

Given a type aliased function such as

type RequestFactory func(api.Filter, map[string]interface{}) (*http.Request, error)

a valid counterfeit/spy can be generated that looks like

package fakes

import (
    "net/http"
    "sync"

    "github.com/cloudfoundry-incubator/diego-enabler/api"
    "github.com/cloudfoundry-incubator/diego-enabler/commands"
)

type FakeRequestFactory struct {
    Stub        func(api.Filter, map[string]interface{}) (*http.Request, error)
    mutex       sync.RWMutex
    argsForCall []struct {
        arg1 api.Filter
        arg2 map[string]interface{}
    }
    returns     struct {
        result1 *http.Request
        result2 error
    }
}

func (fake *FakeRequestFactory) Spy(arg1 api.Filter, arg2 map[string]interface{}) (*http.Request, error) {
    fake.mutex.Lock()
    fake.argsForCall = append(fake.argsForCall, struct {
        arg1 api.Filter
        arg2 map[string]interface{}
    }{arg1, arg2})
    fake.mutex.Unlock()
    if fake.Stub != nil {
        return fake.Stub(arg1, arg2)
    } else {
        return fake.returns.result1, fake.returns.result2
    }
}

func (fake *FakeRequestFactory) CallCount() int {
    fake.mutex.RLock()
    defer fake.mutex.RUnlock()
    return len(fake.argsForCall)
}

func (fake *FakeRequestFactory) ArgsForCall(i int) (api.Filter, map[string]interface{}) {
    fake.mutex.RLock()
    defer fake.mutex.RUnlock()
    return fake.argsForCall[i].arg1, fake.argsForCall[i].arg2
}

func (fake *FakeRequestFactory) Returns(result1 *http.Request, result2 error) {
    fake.Stub = nil
    fake.returns = struct {
        result1 *http.Request
        result2 error
    }{result1, result2}
}

var _ commands.RequestFactory = new(FakeRequestFactory).Spy

This spy can be used in a very similar way as other spies:

var fakeRequestFactory *fakes.FakeRequestFactory

BeforeEach(func() {
  fakeRequestFactory = new(fakes.FakeRequestFactory)
})

It("does stuff", func() {
  fakeRequestFactory.Returns(new(http.Request), errors.New("BOOM"))

  results := someFuncWhichUsesIt(fakeRequestFactory.Spy)

  Expect(fakeRequestFactory.CallCount()).To(Equal(1))
})

Allow caller to set a custom output package for fakes

The counterfeiter CLI allows you to specify -o and --fake-name flags. But it should also allow you to define the package name and path that is generated, for example:

$ counterfeiter github.com/my/package Something -p github.com/my/package/tests/fakes
Wrote `FakeSomething` to `github.com/my/package/tests/fakes/fake_something.go`

An additional complexity here is that I would like to be able to export the fakes to the same package as the interface definition. Reason: currently it is not possible to use counterfeiter-generated fakes in the package defining the interface (e.g. in unit tests) because this results in a cyclic dependency; allowing fakes to reside in the same package fixes this issue.

$ counterfeiter github.com/my/package Something -p github.com/my/package
Wrote `FakeSomething` to `github.com/my/package/fake_something.go`

Counterfeiter should generate fakes for interfaces whose package name does not match directory name

I have an interface:

import "google.golang.org/api/storage/v1"

type StorageService interface {
    Buckets(string) (*storage.Buckets, error)
}

counterfeiter panics with

$ counterfeiter . StorageService
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x1239a8]

goroutine 1 [running]:
panic(0x199d00, 0xc42000e0d0)
    /usr/local/Cellar/go/1.7.1/libexec/src/runtime/panic.go:500 +0x1a1
github.com/maxbrunsfeld/counterfeiter/astutil.InjectAlias.func1(0x406420, 0xc4200a6a20, 0xc41fffa401)
    /Users/pivotal/go/src/github.com/maxbrunsfeld/counterfeiter/astutil/mutator.go:11 +0x988
go/ast.inspector.Visit(0xc42023cf60, 0x406420, 0xc4200a6a20, 0x2f56e0, 0xc42023cf60)
    /usr/local/Cellar/go/1.7.1/libexec/src/go/ast/walk.go:373 +0x3a
go/ast.Walk(0x2f56e0, 0xc42023cf60, 0x406420, 0xc4200a6a20)
    /usr/local/Cellar/go/1.7.1/libexec/src/go/ast/walk.go:52 +0x63
go/ast.Walk(0x2f56e0, 0xc42023cf60, 0x2f5e20, 0xc42009ab80)
    /usr/local/Cellar/go/1.7.1/libexec/src/go/ast/walk.go:74 +0xd22
go/ast.Walk(0x2f56e0, 0xc42023cf60, 0x2f5e60, 0xc42007ecf0)
    /usr/local/Cellar/go/1.7.1/libexec/src/go/ast/walk.go:84 +0x152
go/ast.Walk(0x2f56e0, 0xc42023cf60, 0x2f5ee0, 0xc4200a6aa0)
    /usr/local/Cellar/go/1.7.1/libexec/src/go/ast/walk.go:168 +0x22df
go/ast.Inspect(0x2f5ee0, 0xc4200a6aa0, 0xc42023cf60)
    /usr/local/Cellar/go/1.7.1/libexec/src/go/ast/walk.go:385 +0x4b
github.com/maxbrunsfeld/counterfeiter/astutil.InjectAlias(0xc4200a6aa0, 0xc42007fe90, 0xc42007ff20)
    /Users/pivotal/go/src/github.com/maxbrunsfeld/counterfeiter/astutil/mutator.go:23 +0x92
github.com/maxbrunsfeld/counterfeiter/generator.CodeGenerator.fixup(0x7fff5fbff788, 0xe, 0xc42007b760, 0x1, 0x1, 0xc42009a816, 0x27, 0xc42007a750, 0x3, 0x1, ...)
    /Users/pivotal/go/src/github.com/maxbrunsfeld/counterfeiter/generator/generator.go:191 +0x80
github.com/maxbrunsfeld/counterfeiter/generator.CodeGenerator.buildASTForFake(0x7fff5fbff788, 0xe, 0xc42007b760, 0x1, 0x1, 0xc42009a816, 0x27, 0xc42007a750, 0x3, 0x1, ...)
    /Users/pivotal/go/src/github.com/maxbrunsfeld/counterfeiter/generator/generator.go:61 +0x1a3
github.com/maxbrunsfeld/counterfeiter/generator.CodeGenerator.GenerateFake(0x7fff5fbff788, 0xe, 0xc42007b760, 0x1, 0x1, 0xc42009a816, 0x27, 0xc42007a750, 0x3, 0x1, ...)
    /Users/pivotal/go/src/github.com/maxbrunsfeld/counterfeiter/generator/generator.go:29 +0xc2
main.main()
    /Users/pivotal/go/src/github.com/maxbrunsfeld/counterfeiter/main.go:57 +0x3f9

The issue is fixed when I alias the import:

import storage "google.golang.org/api/storage/v1"

type StorageService interface {
    Buckets(string) (*storage.Buckets, error)
}

Test script handling of generated fixtures

Firstly, why is the output of the counterfeiting redirected to /dev/null? At one point, i broke counterfeiter so that it panicked when run, but because the output is thrown away, the test script gave me no clue as to what was going on. I think it would be more useful to let the output go to the console. It's a bit more verbose, but when counterfeiting works, only one line per package.

Secondly, the last line in the script cleans up arguments/argumentsfakes, but this is never created. Should it be, or can this line be removed?

Thirdly, the tests don't use the generated fakes. Rather, they use the checked-in fakes. Is this deliberate, and if so, is this wise?

If it is deliberate, then the test script is really doing two completely separate things: on the one hand, generating and compiling a set of fakes, which is a kind of smoke test, and on the other, running the tests. I think it would be helpful to rearrange the test script slightly to make this clearer; at present, it looks like the counterfeiting is setup for the tests, which is not the case.

If this is not deliberate, and the test script is supposed to be regenerating the checked-in fakes, then let's fix it!

Personally, i would suggest that the test script should regenerate the checked-in fakes. The way testing works at the moment is a bit weird; if i change the counterfeiting code, it's not enough to re-run the tests, or even the test script; i have to manually regenerate the fakes, and then run the tests. This seems scandalously error-prone - if i forget to re-generate the fakes, i can check in broken code, but have the build pass.

I'm happy to PR an updated script which fixes any of these problems.

only log errors

hi,

i think it's cleaner to just output error messages and discard logs ala Wrote A to a.go.
It should be self evident by the exit code being zero that everything went fine.

Fakes handle nil slices incorrectly

In #33 i changed how slices are handled in fakes: they are copied, rather than stored by reference.

However, i forgot about nil. Because of the unique way Go represents slices, a nil pointer to a slice looks quite like an empty slice, and my copying code doesn't distinguish between them. Passing a nil to a fake leads to an empty slice being stored.

Sorry about this. I'll try to PR a fix for this later on today.

Readme should have go get command for install

For CLI tools it can be helpful to have the go get command at the top of the readme.

This helps users not have to lookup the location of main.go (some projects nest it inside the cmd directory).

Include a dash -v flag on the counterfeiter command

I was trying to figure out if I had the latest version of counterfeiter and was imaging that I could run something along the lines of

counterfeiter -v

Maybe I get a git sha back of the latest commit? A friendly version number would be nice but maybe I am asking too much?

Counterfeiter doesn't add required imports if the package name doesn't match the folder

test/potato.go:

package test

import (
	git "github.com/libgit2/git2go"
)

type Potato interface {
	Tomato(git.Oid)
}

after running counterfeiter . Potato

test/testfakes/fake_potato.go:

// This file was generated by counterfeiter
package testfakes

import (
	"counterfeiter-bug-poc/test"
	"sync"
)

type FakePotato struct {
	TomatoStub        func(git2go.Oid)
	tomatoMutex       sync.RWMutex
	tomatoArgsForCall []struct {
		arg1 git2go.Oid
	}
	invocations      map[string][][]interface{}
	invocationsMutex sync.RWMutex
}

func (fake *FakePotato) Tomato(arg1 git2go.Oid) {
	fake.tomatoMutex.Lock()
	fake.tomatoArgsForCall = append(fake.tomatoArgsForCall, struct {
		arg1 git2go.Oid
	}{arg1})
	fake.recordInvocation("Tomato", []interface{}{arg1})
	fake.tomatoMutex.Unlock()
	if fake.TomatoStub != nil {
		fake.TomatoStub(arg1)
	}
}

func (fake *FakePotato) TomatoCallCount() int {
	fake.tomatoMutex.RLock()
	defer fake.tomatoMutex.RUnlock()
	return len(fake.tomatoArgsForCall)
}

func (fake *FakePotato) TomatoArgsForCall(i int) git2go.Oid {
	fake.tomatoMutex.RLock()
	defer fake.tomatoMutex.RUnlock()
	return fake.tomatoArgsForCall[i].arg1
}

func (fake *FakePotato) Invocations() map[string][][]interface{} {
	fake.invocationsMutex.RLock()
	defer fake.invocationsMutex.RUnlock()
	fake.tomatoMutex.RLock()
	defer fake.tomatoMutex.RUnlock()
	return fake.invocations
}

func (fake *FakePotato) recordInvocation(key string, args []interface{}) {
	fake.invocationsMutex.Lock()
	defer fake.invocationsMutex.Unlock()
	if fake.invocations == nil {
		fake.invocations = map[string][][]interface{}{}
	}
	if fake.invocations[key] == nil {
		fake.invocations[key] = [][]interface{}{}
	}
	fake.invocations[key] = append(fake.invocations[key], args)
}

var _ test.Potato = new(FakePotato)

Handle byte slices and buffers more fruitfully

Fakes save the arguments to calls in a pretty straightforward way: value types are copied, and references to reference types are copied. This works fine for value types, and for reference types in the great majority of cases. However, it falls down a bit where the argument is a reference type which is subsequently mutated, and where you care about its value at the moment of the call, rather than its identity. For example, a byte slice being used as a buffer while working through a stream, or a bytes.Buffer (I struggle to think of any more examples!).

By way of example, this morning i was counterfeiting an interface with this method:

PutBlock(container, name, blockID string, chunk []byte) error

I was testing code that 'put' several different 'blocks', one after the other. Let's say that i was uploading the alphabet in chunks of three letters. One test looked like:

_, _, _, chunk := blobClient.PutBlockArgsForCall(0)
Expect(chunk).To(Equal([]byte{'a', 'b', 'c'}))

This failed; according to the fake, the chunk was {'y', 'z'}. That's because the fake saved the pointer to the slice, and the slice was subsequently filled with more data. It would have been really useful if the fake had somehow saved the {'a', 'b', 'c'} that was in the slice when it was passed.

I ended up modifying the fake by hand to save a copy of the chunk:

copiedChunk := make([]byte, len(chunk))
copy(copiedChunk, chunk)
// ...
fake.putBlockArgsForCall = append(fake.putBlockArgsForCall, struct {
    container string
    name      string
    blockID   string
    chunk     []byte
}{container, name, blockID, copiedChunk})

Of course, this will be wiped away if i ever regenerate the fakes.

It would be great if there was a way to fix this properly. Capturing all byte slices as copies would be a breaking change, so perhaps a bad idea. But could there be some way to specify it?

Counterfeiter generated fakes do not satisfy go lint.

Here's some sample output from a fake logger (let me know if you need more info but it should be clear enough to highlight the issue):

fakes/fake_logger.go:1:1: package comment should be of the form "Package fakes ..."
fakes/fake_logger.go:9:6: exported type FakeLogger should have comment or be unexported
fakes/fake_logger.go:17:1: exported method FakeLogger.LogLine should have comment or be unexported
fakes/fake_logger.go:28:1: exported method FakeLogger.LogLineCallCount should have comment or be unexported
fakes/fake_logger.go:34:1: exported method FakeLogger.LogLineArgsForCall should have comment or be unexported

Fails to generate interfaces with aliased sub-interfaces

Counterfeiter fails to generate fakes for the following interface types.

import alias "github.com/..../something"

type MyInterface interface {
  alias.ImportedInterface
}

If you change the findImportPath to the following piece of code, the problem is half way fixed.

func findImportPath(importSpecs []*ast.ImportSpec, alias string) string {
    for _, spec := range importSpecs {
        importPath := strings.Trim(spec.Path.Value, `"`)
        if path.Base(importPath) == alias {
            return importPath
        }
        if (spec.Name != nil) && (spec.Name.Name == alias) {
            return importPath
        }
    }
    return ""
}

However, imports are still messed up. I think the import section in the generated fake has the alias left in front, whereas the parameters in the methods of the aliased interface (as they are copy-pasted from the interface declaration file) don't have any alias. During goimport the import is considered unused and removed resulting in the fake not compiling.

Blows up with interface embedding

With these interfaces:

type A interface {
  Foo() int
}

type B interface {
  A
}

I get an error like:

garden(master*): counterfeiter warden/ Backend
panic: interface conversion: ast.Expr is *ast.Ident, not *ast.FuncType

goroutine 16 [running]:
runtime.panic(0x1a9d00, 0x20856e140)
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/panic.c:279 +0xf5
github.com/maxbrunsfeld/counterfeiter/generator.CodeGenerator.typeDecl(0x7fff5fbff9f6, 0x7, 0x208412472, 0xb, 0x2083e0a63, 0x5, 0x208426400, 0x2084186f0, 0x1, 0x1, ...)
    /Users/Alex/go/src/github.com/maxbrunsfeld/counterfeiter/generator/generator.go:108 +0x10f
github.com/maxbrunsfeld/counterfeiter/generator.CodeGenerator.sourceFile(0x7fff5fbff9f6, 0x7, 0x208412472, 0xb, 0x2083e0a63, 0x5, 0x208426400, 0x2084186f0, 0x1, 0x1, ...)
    /Users/Alex/go/src/github.com/maxbrunsfeld/counterfeiter/generator/generator.go:38 +0x90
github.com/maxbrunsfeld/counterfeiter/generator.CodeGenerator.GenerateFake(0x7fff5fbff9f6, 0x7, 0x208412472, 0xb, 0x2083e0a63, 0x5, 0x208426400, 0x2084186f0, 0x1, 0x1, ...)
    /Users/Alex/go/src/github.com/maxbrunsfeld/counterfeiter/generator/generator.go:26 +0xca
main.main()
    /Users/Alex/go/src/github.com/maxbrunsfeld/counterfeiter/main.go:82 +0x5f1

goroutine 17 [runnable]:
runtime.MHeap_Scavenger()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/mheap.c:507
runtime.goexit()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:1445

goroutine 18 [runnable]:
bgsweep()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/mgc0.c:1976
runtime.goexit()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:1445

goroutine 19 [runnable]:
runfinq()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/mgc0.c:2606
runtime.goexit()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:1445

I suppose it should 'flatten' the interface and bring in all of B's methods as well.

counterfeiter does not check vendor directory when generating using import path

When you have a vendored library in your project and you use a fully qualified import path to generate a fake, counterfeiter won't search for it in the vendored directory.

E.g.:

mkdir -p vendor/foo/bar/baz/
echo "package baz\ntype Baz interface {\nBaz() string\n}\n" > vendor/foo/bar/baz/baz.go
counterfeiter foo/bar/baz.Baz

Package 'foo/bar/baz' not found on GOPATH

Fails to find package when dirname and package do not match

The golang convention of having your package name match your directory name is really convenient, but you can also create packages whose name does not match their directory name. In this case, it seems like counterfeiter can't find the package.

Example:

> find .
imDoinYouAFavor
imDoinYouAFavor/Jake.go

imDoinYouAFavor/Jake.go

package forgetAboutItJake

type ItsChinaTown interface{}
> counterfeiter ./imDoinYouAFavor/Jake.go  ItsChinaTown
Couldn't find package 'imDoinYouAFavor' in directory

> echo $?
1

I also tried to use the package name instead of the dirname but that didn't seem to work either.

Remote mocks -

Can you mock something remote? So for example:

counterfeiter github.com/dancannon/gorethin WriteResponse

Fails to identify functions that receive multiple args of a single type

We tried to generate a fake for an interface that looked like this:

type SecurityGroupSpaceBinder interface {
    BindSpace(securityGroupGuid, spaceGuid string) error

Counterfeiter generated this fake:

type FakeSecurityGroupSpaceBinder struct {
    BindSpaceStub        func(securityGroupGuid, spaceGuid string) error
    bindSpaceMutex       sync.RWMutex
    bindSpaceArgsForCall []struct {
        arg1 string
    }
    bindSpaceReturns struct {
        result1 error
    }
}

The problem being that it expects only a single string argument instead of two. It's easy enough to work around this by always specifying the type for each arg, but I thought it would be worth sharing this bug.

Strange GOPATH behavior in docker

If your GOPATH contains multiple directories the order in which they appear seems to matter. I'm running this in a docker container (golang:latest) with my GOPATH=/root/go:/usergo and then trying to run counterfeiter on a file in /usergo (usergo is a mounted volume from the host machine). I get a Could not find interface error from counterfeiter. But if I follow the exact same steps with my GOPATH set to /usergo:/root/go then everything works fine.

I can't reproduce this on my local machine but this behavior is consistent on golang:latest

packages in a directory with a hyphen in the last part, cause a Node.format error

I have a package 'bar' defined in a project/foo-bar/bar.go

When trying to run counterfeiter
$ counterfeiter project/foo-bar Bar
I get format.Node internal error (1:12: expected ':' , found '-')

This comes from line 59 in this file https://golang.org/src/go/format/format.go
I believe this is caused by the way the fake package name is created by parsing directories and package name.

The commit that introduced this behavior is 9db5229

I plan on digging into it this weekend and making a PR, but feel free to beat me to it.

copied variable names overlap imports

type Registry interface {
    ListComponents(ctx api.Context, labels labels.Selector, fields fields.Selector) (*api.ComponentList, error)
    ...
}
import (
    "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
    "github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
    "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
)

...

func (fake *MockRegistry) ListComponents(ctx api.Context, labels labels.Selector, fields fields.Selector) (*api.ComponentList, error) {
    fake.listComponentsMutex.Lock()
    fake.listComponentsArgsForCall = append(fake.listComponentsArgsForCall, struct {
        ctx    api.Context
        labels labels.Selector
        fields fields.Selector
    }{ctx, labels, fields})
    fake.listComponentsMutex.Unlock()
    if fake.ListComponentsStub != nil {
        return fake.ListComponentsStub(ctx, labels, fields)
    } else {
        return fake.listComponentsReturns.result1, fake.listComponentsReturns.result2
    }
}

See labels labels.Selector.

In this case, labels is the variable name used by the interface definition, which shadows the import. Shadowing is technically fine for usage, as long as the function using it doesn't need to access the import with the same name. Tho it may not pass linting...

Invalid import path on Windows

I am unable to run counterfeiter on Windows. I tryed the same command on MacOS and I'm getting no error.

counterfeiter-test> counterfeiter services/io_services.go IoService
format.Node internal error (5:2: invalid import path: "github.com\\xcomponent\\counterfeiter-test\\services")

I tried running from a vanilla project with a single interface and I'm getting the same result. Any additional information could help you out ?

use import- instead of source-path

Hi,

I think using the import path would be handier than the filesystem path to locate the interface.

It can be looked up converted to a fs path fairly easy by using the go/build package.

// findRelImport tries to find the import string in the $GOPATH
// and constructs a filepath relative to the current wroking directory
func findRelImport(imp string) string {
    p, err := build.Default.Import(imp, "", build.FindOnly)
    checkPanic(err)

    cwd, err := os.Getwd()
    checkPanic(err)

    p.Dir, err = filepath.Rel(cwd, p.Dir)
    checkPanic(err)

    return p.Dir
}

Having this made moving and renaming packages much more comfortable with other tools but I guess we should have both options for compatibility and flexibility.

I can whip up a preliminary PR if you agree.

Counterfeiter uses GOPATH as import path for embedded interfaces from packages in vendor

When embedding interfaces, counterfeiter resolves the package path to packages in the GOPATH for vendored libraries. The expected behaviour would be that it resolves these package paths to vendor first before looking in the GOPATH. This can lead to subtle issues like the wrong fake being generated for interfaces with embedded interfaces from vendored libraries because another version of the package happens to be installed in the GOPATH as well.

I've reproduced the issue here: github.com/beatrichartz/counterfeiter-vendor-issue

if my package imports another package of the same name, the fake imports both

with:

package metrics

import (
    "os"
    "time"

    "github.com/cloudfoundry-incubator/executor/api"
    "github.com/cloudfoundry-incubator/executor/registry"
    "github.com/cloudfoundry/dropsonde/autowire/metrics"
)

type Source interface {
    CurrentCapacity() registry.Capacity
    TotalCapacity() registry.Capacity
    GetAllContainers() []api.Container
}

type Reporter struct {
    Source   Source
    Interval time.Duration
}

func (reporter *Reporter) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
    close(ready)

    for {
        metrics.SendValue("capacity.memory", 128, "Metric")
    }
}

running:

counterfeiter . Source

results in:

// This file was generated by counterfeiter
package fakes

import (
    "sync"

    "github.com/cloudfoundry-incubator/executor/api"
    "github.com/cloudfoundry-incubator/executor/metrics"
    "github.com/cloudfoundry-incubator/executor/registry"
    "github.com/cloudfoundry/dropsonde/autowire/metrics"
)

type FakeSource struct {
    CurrentCapacityStub        func() registry.Capacity
    currentCapacityMutex       sync.RWMutex
    currentCapacityArgsForCall []struct{}
    currentCapacityReturns struct {
        result1 registry.Capacity
    }
    TotalCapacityStub        func() registry.Capacity
    totalCapacityMutex       sync.RWMutex
    totalCapacityArgsForCall []struct{}
    totalCapacityReturns struct {
        result1 registry.Capacity
    }
    GetAllContainersStub        func() []api.Container
    getAllContainersMutex       sync.RWMutex
    getAllContainersArgsForCall []struct{}
    getAllContainersReturns struct {
        result1 []api.Container
    }
}

func (fake *FakeSource) CurrentCapacity() registry.Capacity {
    fake.currentCapacityMutex.Lock()
    defer fake.currentCapacityMutex.Unlock()
    fake.currentCapacityArgsForCall = append(fake.currentCapacityArgsForCall, struct{}{})
    if fake.CurrentCapacityStub != nil {
        return fake.CurrentCapacityStub()
    } else {
        return fake.currentCapacityReturns.result1
    }
}

func (fake *FakeSource) CurrentCapacityCallCount() int {
    fake.currentCapacityMutex.RLock()
    defer fake.currentCapacityMutex.RUnlock()
    return len(fake.currentCapacityArgsForCall)
}

func (fake *FakeSource) CurrentCapacityReturns(result1 registry.Capacity) {
    fake.currentCapacityReturns = struct {
        result1 registry.Capacity
    }{result1}
}

func (fake *FakeSource) TotalCapacity() registry.Capacity {
    fake.totalCapacityMutex.Lock()
    defer fake.totalCapacityMutex.Unlock()
    fake.totalCapacityArgsForCall = append(fake.totalCapacityArgsForCall, struct{}{})
    if fake.TotalCapacityStub != nil {
        return fake.TotalCapacityStub()
    } else {
        return fake.totalCapacityReturns.result1
    }
}

func (fake *FakeSource) TotalCapacityCallCount() int {
    fake.totalCapacityMutex.RLock()
    defer fake.totalCapacityMutex.RUnlock()
    return len(fake.totalCapacityArgsForCall)
}

func (fake *FakeSource) TotalCapacityReturns(result1 registry.Capacity) {
    fake.totalCapacityReturns = struct {
        result1 registry.Capacity
    }{result1}
}

func (fake *FakeSource) GetAllContainers() []api.Container {
    fake.getAllContainersMutex.Lock()
    defer fake.getAllContainersMutex.Unlock()
    fake.getAllContainersArgsForCall = append(fake.getAllContainersArgsForCall, struct{}{})
    if fake.GetAllContainersStub != nil {
        return fake.GetAllContainersStub()
    } else {
        return fake.getAllContainersReturns.result1
    }
}

func (fake *FakeSource) GetAllContainersCallCount() int {
    fake.getAllContainersMutex.RLock()
    defer fake.getAllContainersMutex.RUnlock()
    return len(fake.getAllContainersArgsForCall)
}

func (fake *FakeSource) GetAllContainersReturns(result1 []api.Container) {
    fake.getAllContainersReturns = struct {
        result1 []api.Container
    }{result1}
}

var _ metrics.Source = new(FakeSource)

it should probably not

counterfeiter drops numbers from my package

I have a package called md5sum with an interface FileSummer. When I run counterfeiter on that, it creates md5sum/md5sumfakes/fake_file_summer.go but the package inside that generated file is mdsum - counterfeiter dropped the 5 from the package name.

cc @tjarratt

nil pointer dereference exception when generating fake

Generating fakes for any interface including src-d/go-git/CloneOptions as a method parameter results in the following nil pointer dereference:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x52c3b2]

goroutine 1 [running]:
github.com/maxbrunsfeld/counterfeiter/astutil.InjectAlias.func1(0x70be00, 0xc420156880, 0x70bf01)
        /go/src/github.com/maxbrunsfeld/counterfeiter/astutil/mutator.go:9 +0x132
go/ast.inspector.Visit(0xc420162340, 0x70be00, 0xc420156880, 0x70b700, 0xc420162340)
        /usr/local/go/src/go/ast/walk.go:373 +0x3a
go/ast.Walk(0x70b700, 0xc420162340, 0x70be00, 0xc420156880)
        /usr/local/go/src/go/ast/walk.go:52 +0x66
go/ast.Walk(0x70b700, 0xc420162340, 0x70be40, 0xc420158570)
        /usr/local/go/src/go/ast/walk.go:84 +0x156
go/ast.Walk(0x70b700, 0xc420162340, 0x70bec0, 0xc4201558c0)
        /usr/local/go/src/go/ast/walk.go:165 +0x2473
go/ast.Inspect(0x70bec0, 0xc4201558c0, 0xc420162340)
        /usr/local/go/src/go/ast/walk.go:385 +0x4b
github.com/maxbrunsfeld/counterfeiter/astutil.InjectAlias(0xc4201558c0, 0xc420158c00, 0xc420158c90)
        /go/src/github.com/maxbrunsfeld/counterfeiter/astutil/mutator.go:23 +0x94
github.com/maxbrunsfeld/counterfeiter/generator.CodeGenerator.fixup(0x7ffd9dd7ce28, 0x4, 0xc420164030, 0x1, 0x1, 0xc42004a708, 0x29, 0xc420012970, 0x4, 0x1, ...)
        /go/src/github.com/maxbrunsfeld/counterfeiter/generator/generator.go:202 +0x80
github.com/maxbrunsfeld/counterfeiter/generator.CodeGenerator.buildASTForFake(0x7ffd9dd7ce28, 0x4, 0xc420164030, 0x1, 0x1, 0xc42004a708, 0x29, 0xc420012970, 0x4, 0x1, ...)
        /go/src/github.com/maxbrunsfeld/counterfeiter/generator/generator.go:61 +0x199
github.com/maxbrunsfeld/counterfeiter/generator.CodeGenerator.GenerateFake(0x7ffd9dd7ce28, 0x4, 0xc420164030, 0x1, 0x1, 0xc42004a708, 0x29, 0xc420012970, 0x4, 0x1, ...)
        /go/src/github.com/maxbrunsfeld/counterfeiter/generator/generator.go:29 +0xa2
main.generateFake(0x7ffd9dd7ce28, 0x4, 0xc42004a700, 0x31, 0x0, 0x0, 0xc42004a800, 0x39, 0xc42004a82d, 0x4, ...)
        /go/src/github.com/maxbrunsfeld/counterfeiter/main.go:64 +0x1dc
main.main()
        /go/src/github.com/maxbrunsfeld/counterfeiter/main.go:43 +0x29f
util/vgit/vGit.go:3: running "counterfeiter": exit status 2

import cycle when writing to the same package

when writing the counterfeit to the same package, the source package is still imported and used in the generated code leading to import cycle not allowed errors.

package cycle

type Data string

//go:generate counterfeiter -o tester.go . Tester
type Tester interface {
    Test() Data
}
[cryptix@higgs ~/go/src/github.com/cryptix/exp/cycle:master] go generate
Wrote `FakeTester` to `tester.go`
[cryptix@higgs ~/go/src/github.com/cryptix/exp/cycle:master] go test                            
can't load package: import cycle not allowed
package github.com/cryptix/exp/cycle
    imports github.com/cryptix/exp/cycle
import cycle not allowed
package github.com/cryptix/exp/cycle
    imports github.com/cryptix/exp/cycle
[cryptix@higgs ~/go/src/github.com/cryptix/exp/cycle:master] 

On Windows: `is not on GOPATH` error when running counterfeiter

We invoked scripts/test.sh on a Windows 10 64-bit machine.

scripts/test.sh

Generating fakes used by tests...

Path 'C:\Users\saint\go\src\github.com\maxbrunsfeld\counterfeiter\arguments' is not on GOPATH
Path 'C:\Users\saint\AppData\Local\Temp\symlinked_fixtures' is not on GOPATH
exit status 1

Our GOPATH:

echo %GOPATH%
C:\Users\saint\go

We believe that error is triggered on this line, which mistakenly splits the GOPATH on :, not realizing that in Windows, the : is part of the path (e.g C:\) and not necessarily a separator.

We are using go 1.8:

go version
go version go1.8 windows/amd64

Race condition when changing stubs across goroutines

package race_test

import (
    "github.com/yourcode/yourcodefakes"
    . "github.com/onsi/ginkgo"
)

var _ = Describe("data race", func() {
    It("has a race condition", func() {
        myHandler := new(yourcodefakes.FakeHandler)

        go func() {
            for {
                myHandler.SomeMethod()
            }
        }()

        myHandler.SomeMethodStub = func() string {
            return "some string"
        }
    })
})

Is there a preferred way to change the stub without causing a race condition? We experimented with exposing the mutex for SomeMethod by manually modifying the generated counterfeiter fakes, and locking when reassigning SomeMethodStub, eg:

                myHandler.SomeMethodMutex.Lock()
        myHandler.SomeMethodStub = func() string {
            return "some string"
        }
                myHandler.SomeMethodMutex.Unlock()

This fixes the race condition but obviously this requires editing the fake (non-ideal).

If we hid the re-assignment of the stub into a method, we could Lock/Unlock it in that function instead.

// This file was generated by counterfeiter
package yourcodefakes

import (
    "sync"

    "yourcode"
)

type FakeHandler struct {
    someMethodStub        func() string
    someMethodMutex       sync.RWMutex
    someMethodArgsForCall []struct{}
    someMethodReturns     struct {
        result1 string
    }
    invocations      map[string][][]interface{}
    invocationsMutex sync.RWMutex
}

func (fake *FakeHandler) SomeMethod() string {
    fake.someMethodMutex.Lock()
    fake.someMethodArgsForCall = append(fake.someMethodArgsForCall, struct{}{})
    fake.recordInvocation("SomeMethod", []interface{}{})
    fake.someMethodMutex.Unlock()
    if fake.someMethodStub != nil {
        return fake.someMethodStub()
    } else {
        return fake.someMethodReturns.result1
    }
}

func (fake *FakeHandler) SomeMethodCalls(stub func() string) {
      fake.someMethodMutex.Lock()
      defer fake.someMethodMutex.Unlock()

      fake.someMethodStub = stub
}

...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.