golang / mock

GoMock is a mocking framework for the Go programming language.
Apache License 2.0
9.32k stars 607 forks source link

mockgen always tries to pull latest version instead of required version(go.mod without vendor) in docker golang:1.13 #354

Closed tocrafty closed 4 years ago

tocrafty commented 4 years ago

docker image: golang:1.13 go version: go1.13.5 gomock version: all after v1.1.0 gomock work mode: reflect mode

However, in OSX, without docker, mockgen works well with go.mod. Very strange!

minicuts commented 4 years ago

@tocrafty That sounds that you invoked the go run in a different directory than in the one that contains go.mod

Can you share a reproducer? There are many edge cases to using mockgen with modules. And the behavior is quite subtle (especially when you are not explicitly setting the GO111MODULE might change in the future)

tocrafty commented 4 years ago

This is a test to reproduce the bug.

mockgen_test is a simple project with only one golang interface III which has two methods g() and f(). There are two commits in master branch. The commit before master has only one method f().

To mock the interface III, I start a new project with two files:

// go.mod
module test_mockgen
go 1.13

require (
        github.com/tocrafty/mockgen_test v0.0.0-20191213124124-4cc03a747f1f
)

and

# Makefile
mockgen_version ?= latest

.PHONY: docker
docker:
        docker run --rm \
                -v $(shell pwd):/mockgen \
                -w /mockgen \
                golang:1.13 \
                make no-docker out=out-docker.go mockgen_version=$(mockgen_version)

.PHONY: no-docker
no-docker: out = out.go
no-docker: mockgen
        # make sure we are in directory with go.mod
        @echo $(shell ls -al go.mod)
        # print the expect mockgen_test version in go.mod
        @grep "github.com/tocrafty/mockgen_test" go.mod

        # make sure GO11MODULE is on
        GO111MODULE=on mockgen github.com/tocrafty/mockgen_test III > $(out)

        # if we got a mocked function g(), it means mockgen used a wrong version of mogckgen_test.
        @if grep "g()" $(out); then \
                echo "used wrong version"; \
        else \
                echo "ok"; \
        fi

.PHONY: mockgen
mockgen:
        GO111MODULE=on go get github.com/golang/mock/mockgen@$(mockgen_version)

make no-docker gives ok(it runs locally on my osx). mockgen_version=v1.1.0 make docker also gives ok. However, any version after v1.1.0 all give used wrong version. The output of make docker indicated that mockgen always tried to pull latest version of mockgen_test.

➜ make docker
docker run --rm \
        -v /Users/yanbo/data/tmp/golang/mockgen:/mockgen \
        -w /mockgen \
        golang:1.13 \
        make no-docker out=out-docker.go mockgen_version=latest
GO111MODULE=on go get github.com/golang/mock/mockgen@latest
go: finding github.com/golang/mock v1.3.1
go: downloading github.com/golang/mock v1.3.1
go: extracting github.com/golang/mock v1.3.1
go: downloading golang.org/x/tools v0.0.0-20190425150028-36563e24a262
go: extracting golang.org/x/tools v0.0.0-20190425150028-36563e24a262
go: finding golang.org/x/tools v0.0.0-20190425150028-36563e24a262
# make sure we are in directory with go.mod
-rw-r--r-- 1 root root 154 Jan 21 07:19 go.mod
# print the expect mockgen_test version in go.mod
    github.com/tocrafty/mockgen_test v0.0.0-20191213124124-4cc03a747f1f
# make sure GO11MODULE is on
GO111MODULE=on mockgen github.com/tocrafty/mockgen_test III > out-docker.go
go: finding github.com/tocrafty/mockgen_test latest
go: downloading github.com/tocrafty/mockgen_test v0.0.0-20191213124830-295cf4692195
go: extracting github.com/tocrafty/mockgen_test v0.0.0-20191213124830-295cf4692195
# if we got a mocked function g(), it means mockgen used a wrong version of mogckgen_test.
func (m *MockIII) g() {
func (mr *MockIIIMockRecorder) g() *gomock.Call {
used wrong version
minicuts commented 4 years ago

I can reproduce this. Let me look into this.

minicuts commented 4 years ago

Let me layout the root cause of the bug. I don't have a fix yet - I need to think a bit more what approach to take.

When we are reflecting a module/package (in your case mockgen_test) we are running so called prog.go program. There are possibly several attempts to run prog.go program in different directories. The first attempt is to run it in the the same directory as the input package (here is the code https://github.com/golang/mock/blob/master/mockgen/reflect.go#L156 ) The problem with that approach is, that when modules are used, that first attempt is to run the prog.go in module cache .../mod/pkg/... . And thus the go.mod in the working dir /mockgen is not considered.

In docker image the process runs under root and this first attempt to run prog.go in module cache directory succeeds.

Without docker the first attempt to run prog.go fails (because of lack of permissions on the module cache directory) and the second attempt runs prog.go in the working dir /mockgen (this piece of code https://github.com/golang/mock/blob/master/mockgen/reflect.go#L164). That second attempt correctly resolves your required version of mockgen_test in go.mod

minicuts commented 4 years ago

@tocrafty can you now retest with the latest mockgen?

GO111MODULE=on go get github.com/golang/mock/mockgen@latest
tocrafty commented 4 years ago

@nguyenfilip Seem not work. mockgen v1.4.0 still tryies to pull the latest version, not th version required in go.mod.

minicuts commented 4 years ago

@tocrafty Sorry I meant if you can try with master:

GO111MODULE=on go get github.com/golang/mock/mockgen@master
tocrafty commented 4 years ago

It worked. Thanks.