vulncheck/internal/buildtest: package for running go build

Add the buildtest package, which provides a clean way to run "go
build" in tests.

Modify the gosym tests to use it.

Change-Id: I906769951e243a551d3ec08b8fc3663bdec9c5cb
Reviewed-on: https://go-review.googlesource.com/c/vuln/+/398754
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
diff --git a/vulncheck/internal/buildtest/buildtest.go b/vulncheck/internal/buildtest/buildtest.go
new file mode 100644
index 0000000..e676576
--- /dev/null
+++ b/vulncheck/internal/buildtest/buildtest.go
@@ -0,0 +1,52 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package buildtest provides support for running "go build"
+// in tests.
+package buildtest
+
+import (
+	"os"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"testing"
+)
+
+// GoBuild runs "go build" on dir using the additional environment
+// variables in env. Each element of env should be of the form
+// "VAR=VALUE".
+// It returns the path to the resulting binary, and a function
+// to call when finished with the binary.
+func GoBuild(t *testing.T, dir string, env ...string) (binaryPath string, cleanup func()) {
+	switch runtime.GOOS {
+	case "android", "js", "ios":
+		t.Skipf("skipping on OS without 'go build' %s", runtime.GOOS)
+	}
+	tmpDir, err := os.MkdirTemp("", "buildtest")
+	if err != nil {
+		t.Fatal(err)
+	}
+	binaryPath = filepath.Join(tmpDir, filepath.Base(dir))
+	var exeSuffix string
+	if runtime.GOOS == "windows" {
+		exeSuffix = ".exe"
+	}
+	// Make sure we use the same version of go that is running this test.
+	goCommandPath := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
+	if _, err := os.Stat(goCommandPath); err != nil {
+		t.Fatal(err)
+	}
+	cmd := exec.Command(goCommandPath, "build", "-o", binaryPath)
+	cmd.Dir = dir
+	if len(env) > 0 {
+		cmd.Env = append(os.Environ(), env...)
+	}
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if err := cmd.Run(); err != nil {
+		t.Fatal(err)
+	}
+	return binaryPath, func() { os.RemoveAll(tmpDir) }
+}
diff --git a/vulncheck/internal/gosym/pclntab_test.go b/vulncheck/internal/gosym/pclntab_test.go
index cab8ad2..8d21bb4 100644
--- a/vulncheck/internal/gosym/pclntab_test.go
+++ b/vulncheck/internal/gosym/pclntab_test.go
@@ -10,22 +10,16 @@
 	"debug/elf"
 	"io"
 	"os"
-	"os/exec"
-	"path/filepath"
 	"runtime"
 	"strings"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp/cmpopts"
+	"golang.org/x/vuln/vulncheck/internal/buildtest"
 )
 
-var (
-	pclineTempDir    string
-	pclinetestBinary string
-)
-
-func dotest(t *testing.T) {
+func dotest(t *testing.T) (binaryName string, cleanup func()) {
 	switch runtime.GOOS {
 	case "android", "js", "ios":
 		t.Skipf("skipping on OS without 'go build' %s", runtime.GOOS)
@@ -39,37 +33,7 @@
 		t.Skipf("skipping in short mode on non-Linux system %s", runtime.GOARCH)
 	}
 
-	var err error
-	var exeSuffix string
-	if runtime.GOOS == "windows" {
-		exeSuffix = ".exe"
-	}
-	goCommandPath := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
-	if _, err := os.Stat(goCommandPath); err != nil {
-		t.Fatal(err)
-	}
-
-	pclineTempDir, err = os.MkdirTemp("", "pclinetest")
-	if err != nil {
-		t.Fatal(err)
-	}
-	pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
-	cmd := exec.Command(goCommandPath, "build", "-o", pclinetestBinary)
-	cmd.Dir = "testdata"
-	cmd.Env = append(os.Environ(), "GOOS=linux")
-	cmd.Stdout = os.Stdout
-	cmd.Stderr = os.Stderr
-	if err := cmd.Run(); err != nil {
-		t.Fatal(err)
-	}
-}
-
-func endtest() {
-	if pclineTempDir != "" {
-		os.RemoveAll(pclineTempDir)
-		pclineTempDir = ""
-		pclinetestBinary = ""
-	}
+	return buildtest.GoBuild(t, "testdata", "GOOS=linux")
 }
 
 // skipIfNotELF skips the test if we are not running on an ELF system.
@@ -217,8 +181,8 @@
 }
 
 func TestPCLine(t *testing.T) {
-	dotest(t)
-	defer endtest()
+	pclinetestBinary, cleanup := dotest(t)
+	defer cleanup()
 
 	f, tab := crack(pclinetestBinary, t)
 	defer f.Close()
@@ -283,8 +247,8 @@
 }
 
 func TestInlineTree(t *testing.T) {
-	dotest(t)
-	defer endtest()
+	pclinetestBinary, cleanup := dotest(t)
+	defer cleanup()
 
 	f, err := elf.Open(pclinetestBinary)
 	if err != nil {