Go: Testing Standard Library Changes

I had a wonderful time at GopherCon 2017. One of the highlights was being a mentor at the Go Contributor Workshop. This involved two 1.5-hour sessions where Go developers of all skill levels were walked through the process of making their first contributions to the Go project. Every table at the session had two or more mentors to help. Mentors included members of the Go team and community members familiar with the process.

Any contribution to open source can be intimidating, but a large project like Go even more so. Then there’s Gerrit (the code review tool used by Go) and the Contributor License Agreement (CLA) to contend with. While there are reasons for these complications, I don’t think anyone would claim the process is as easy as it should be. The workshop was a great step to getting more people involved in the project. Big shout out to Steve Francia, Jessie Frazelle, and everyone else involved in making it happen!

The Problem

Now to the meat of this article. During the workshop contributors were encouraged to write a new example for standard library functions. Go examples can be written to be testable, and if you’re going to write a new test, it’s probably a good idea to run that test before submitting to Gerrit.

This can present a problem. Normally, running tests involves running go test [package], which executes the tests with the version of Go currently installed. In most cases this is what you would want, and it will work for some standard library packages, but sometimes it will present confusing error messages.

$ cd ~/golang/src/go/types
$ go test
# _/Users/kale/golang/src/go/types
api_test.go:14:2: use of internal package not allowed
FAIL _/Users/kale/golang/src/go/types [setup failed]

In the example above, one of the test files imports "internal/testenv”, because go resolves to my default 1.8 install the toolchain attempts to import that package from [Go 1.8 install]/src/internal/testenv. This is not allowed because internal packages can only be imported if they share a common root directory.

A Solution?

I could try setting GOROOT to match the development checkout of Go.

$ GOROOT="$HOME/golang" go test -v
go: open /Users/kale/golang/src/runtime/internal/sys/zversion.go: no such file or directory

Now there’s a different error, this one is because zversion.go is generated when the toolchain is built and embeds the Go version in the binary.

We could probably hack our way around this too, and we might be able to get this working in packages with simpler dependencies, but there are a number of other mismatches that may come up. So let’s do it the right way.

The Right Way

What we really want to do is run the tests with a version of the Go toolchain built from the source we’re working on.

# Path that the Go repo was cloned to.
$ GODEV=$HOME/golang
# Move into the src directory.
$ cd $GODEV/src
# Set the bootstrap Go install to the current installation.
$ GOROOT_BOOTSTRAP=$(go env GOROOT) ./make.bash
[output omitted]
# Use the newly built toolchain to run the test.
$ $GODEV/bin/go test -v go/types
[output truncated]
=== RUN ExampleInfo
--- PASS: ExampleInfo (0.00s)
PASS
ok go/types 7.129s

In this case we didn’t have to set GOROOT at all. When the toolchain is built the build location will become the default GOROOT.

Note: If you haveGOROOT set already, you will need to change it or unset it for this to work.

Building the toolchain can take a couple minutes. make.bash builds, but does not run all the tests. all.bash builds and runs tests, which will take much longer.

Note: For Windows there are make.bat and all.bat.

The toolchain doesn’t need to be rebuilt every time a change is made unless you’re modifying and testing the toolchain itself.

Future

Based on my observations at the workshop, I opened https://golang.org/issue/21037 to at least make the build process a little easier by removing the need to explicitly set GOROOT_BOOTSTRAP.

PS: Thanks to Steven Klassen for proof reading this post.

Additional Reading

Workshop Slides:

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.