I sometimes find that I want to run the go command from inside a test case,
for example to build generated code, or to verify that code that should
raise an error at compile time does in fact cause that error.

That raises the question, is there a reliable way to find the go command
from inside a test case. Including for the case where one gives some code
and test cases to someone else, and they are running 'go test' in their own
environment? If there is no reliable way, should there be? Or is the answer
"don't do that!"?

It's particularly frustrating, because in the normal usage, there is a go
command available (one running 'go test', and one used to build the test
binary), but I can't see a way to find it. Ideally, I would like to find
the go command used to build the test binary.

To date, I've been using a recipe I found some time ago through an Internet
search (I've forgotten the source, so can't credit it):

        gocmd := runtime.GOROOT() + "/bin/go"
        if runtime.GOOS == "windows" {
                gocmd += ".exe"
        }

(Of course, this should use filepath.Join instead of string concatenation.)

I recently tried to look into how reliable this is; here are my conclusions
so far.

runtime.GOROOT returns the value of the environment variable GOROOT if it
is set, and the GOROOT used to compile the (test) binary if not. So if
$GOROOT is not set, we should be ok.

With the default compiler gc, if $GOROOT is set, then 'go test' checks that
it seems to point to a valid go installation for the same version of go as
the one running 'go test', exiting if either check fails. That $GOROOT is
passed to the test binary, so will be returned by runtime.GOROOT, but it
should be OK to use.

With gccgo, as far as I can tell 'go test' does not check $GOROOT at all,
and passes it on to the test binary unchanged:

> cat try_test.go
package main_test


import (

"os"

"runtime"

"testing"

)


func TestFoo(t *testing.T) {

t.Error("version", runtime.Version())

t.Error("$GOROOT", os.Getenv("GOROOT"))

t.Error("runtime.GOROOT", runtime.GOROOT())

}

> go test
--- FAIL: TestFoo (0.00s)
    try_test.go:10: version unknown
    try_test.go:11: $GOROOT
    try_test.go:12: runtime.GOROOT /usr
FAIL
exit status 1
FAIL try 0.068s

> GOROOT=foo go test
--- FAIL: TestFoo (0.00s)
    try_test.go:10: version unknown
    try_test.go:11: $GOROOT foo
    try_test.go:12: runtime.GOROOT foo
FAIL
exit status 1
FAIL try 0.063s


So, when using gccgo, we can't depend on the value of $GOROOT at all. It
might be set to something completely unusable, like "foo". Or perhaps the
user normally uses gc and has $GOROOT set correspondingly, but one day
decides to test that the code works with gccgo, but doesn't unset $GOROOT -
in which case the test binary may be built with gccgo, but when the test
case looks for the go command, it will find gc, not gccgo.

So perhaps the strategy is, if the test binary was built with gc and
$GOROOT is set, just skip the test. Is there a better option?

Finally, there is also the case where the binary is built and saved with
'go test -c', and then run at some later time, possibly on another
computer. $GOROOT might be set to anything. Even if it isn't set, the go
installation that was used to build the test binary (returned by
runtime.GOROOT) might not exist any more, or might have been updated to
another version of go. In one sense, I'm not too worried about the 'go test
-c' case though, on the theory that people doing this are responsible for
correctly setting things up before running the test binary.

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAADvV_ujALYEqzA4zrqXS%3D_oBL7M%2B4kYTNyspmwMP4b%3DnSPHnw%40mail.gmail.com.

Reply via email to