internal/buildbinary: add more tests/slightly modify bin.go

This change modifies bin.go to return a map from binary path -> import
path of the package the binary was built from. It also adds additional
testing for buildbinary.runBuild()

Change-Id: Iee132a2c84a0a8aea7f49e69aa9e7fa0c5ae9db1
Reviewed-on: https://go-review.googlesource.com/c/pkgsite-metrics/+/509356
Run-TryBot: Maceo Thompson <maceothompson@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/internal/buildbinary/bin.go b/internal/buildbinary/bin.go
index 3db7682..47ab1a4 100644
--- a/internal/buildbinary/bin.go
+++ b/internal/buildbinary/bin.go
@@ -5,6 +5,7 @@
 package buildbinary
 
 import (
+	"fmt"
 	"os/exec"
 	"path/filepath"
 	"strings"
@@ -16,30 +17,32 @@
 // be aware if building others failed for some reason.
 
 // FindAndBuildBinaries finds and builds all possible binaries from a given module.
-func FindAndBuildBinaries(modulePath string) (binaries []string, err error) {
+func FindAndBuildBinaries(modulePath string) (binaries map[string]string, err error) {
 	defer derrors.Wrap(&err, "FindAndBuildBinaries")
 	buildTargets, err := findBinaries(modulePath)
 	if err != nil {
 		return nil, err
 	}
+	binaries = make(map[string]string)
 
-	for _, target := range buildTargets {
-		path, err := runBuild(modulePath, target)
+	for i, target := range buildTargets {
+		path, err := runBuild(modulePath, target, i)
 		if err != nil {
 			return nil, err
 		}
-		binaries = append(binaries, path)
+		binaries[path] = target
 	}
 	return binaries, nil
 }
 
 // runBuild takes a given module and import path and attempts to build a binary
-func runBuild(modulePath, importPath string) (binaryPath string, err error) {
-	cmd := exec.Command("go", "build", "-C", modulePath, importPath)
+func runBuild(modulePath, importPath string, i int) (binaryPath string, err error) {
+	binName := fmt.Sprintf("bin%d", i)
+	cmd := exec.Command("go", "build", "-C", modulePath, "-o", binName, importPath)
 	if err = cmd.Run(); err != nil {
 		return "", err
 	}
-	binaryPath = filepath.Join(modulePath, filepath.Base(importPath))
+	binaryPath = filepath.Join(modulePath, binName)
 	return binaryPath, nil
 }
 
diff --git a/internal/buildbinary/bin_test.go b/internal/buildbinary/bin_test.go
index f1561c4..9ea9a9c 100644
--- a/internal/buildbinary/bin_test.go
+++ b/internal/buildbinary/bin_test.go
@@ -5,12 +5,18 @@
 package buildbinary
 
 import (
+	"os"
+	"path/filepath"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp/cmpopts"
 )
 
+const (
+	localTestData = "../testdata"
+)
+
 func less(a, b string) bool {
 	return a < b
 }
@@ -24,7 +30,7 @@
 	}{
 		{
 			name:    "local test",
-			dir:     "../testdata/module",
+			dir:     filepath.Join(localTestData, "module"),
 			want:    []string{"golang.org/vuln"},
 			wantErr: false,
 		},
@@ -43,3 +49,44 @@
 		})
 	}
 }
+
+func TestRunBuild(t *testing.T) {
+	tests := []struct {
+		name       string
+		modulePath string
+		importPath string
+		want       string
+		wantErr    bool
+	}{
+		{
+			name:       "local test",
+			modulePath: filepath.Join(localTestData, "module"),
+			importPath: "golang.org/vuln",
+			want:       filepath.Join(localTestData, "module", "bin1"),
+		},
+		{
+			name:       "multiple binaries",
+			modulePath: filepath.Join(localTestData, "multipleBinModule"),
+			importPath: "example.com/test/multipleBinModule",
+			want:       filepath.Join(localTestData, "multipleBinModule", "bin1"),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := runBuild(tt.modulePath, tt.importPath, 1)
+			defer os.Remove(got)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("runBuild() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+
+			if diff := cmp.Diff(tt.want, got); diff != "" {
+				t.Errorf("mismatch (-want, +got):%s", diff)
+			}
+			_, err = os.Stat(got)
+			if err != nil && os.IsNotExist(err) {
+				t.Errorf("runBuild did not produce the expected binary")
+			}
+		})
+	}
+}
diff --git a/internal/testdata/multipleBinModule/go.mod b/internal/testdata/multipleBinModule/go.mod
new file mode 100644
index 0000000..b96158c
--- /dev/null
+++ b/internal/testdata/multipleBinModule/go.mod
@@ -0,0 +1,3 @@
+module example.com/test
+
+go 1.20
\ No newline at end of file
diff --git a/internal/testdata/multipleBinModule/main.go b/internal/testdata/multipleBinModule/main.go
new file mode 100644
index 0000000..91e7378
--- /dev/null
+++ b/internal/testdata/multipleBinModule/main.go
@@ -0,0 +1,7 @@
+package main
+
+import "fmt"
+
+func main() {
+	fmt.Println("Hello World")
+}
diff --git a/internal/testdata/multipleBinModule/multipleBinModule/main.go b/internal/testdata/multipleBinModule/multipleBinModule/main.go
new file mode 100644
index 0000000..80027d4
--- /dev/null
+++ b/internal/testdata/multipleBinModule/multipleBinModule/main.go
@@ -0,0 +1,11 @@
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+func main() {
+	x := math.Abs(-3)
+	fmt.Print(x)
+}
diff --git a/internal/testdata/multipleBinModule/p1/main.go b/internal/testdata/multipleBinModule/p1/main.go
new file mode 100644
index 0000000..4b0c0e7
--- /dev/null
+++ b/internal/testdata/multipleBinModule/p1/main.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+	"fmt"
+	"strings"
+)
+
+// This package doesn't use any of the code in M, but instead
+// is used to build something or as a helper
+
+func main() {
+	s := strings.Join([]string{"One", "Two"}, " ")
+	fmt.Println(s)
+}
diff --git a/internal/testdata/multipleBinModule/p2/file.go b/internal/testdata/multipleBinModule/p2/file.go
new file mode 100644
index 0000000..ad4f7c4
--- /dev/null
+++ b/internal/testdata/multipleBinModule/p2/file.go
@@ -0,0 +1,7 @@
+package p2
+
+import "fmt"
+
+func DoSmthn() {
+	fmt.Print("test")
+}