Skip to content

Commit

Permalink
cmd/errtrace: Fix toolexec with go run main.go (#94)
Browse files Browse the repository at this point in the history
When compiling files passed as command line arguments, go uses a special
package name, "command-line-arguments". Similarly for tests, it uses
"command-line-arguments.test".

Rather than hardcoding these special package names, let's update the
stdlib detection to look for "-std" in the arguments passed to compile.

Update the toolexec tests to compile with files passed directly on the
command line, and verify both "go run" and "go build".
  • Loading branch information
prashantv committed Feb 18, 2024
1 parent 0d9b854 commit c3648ce
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 18 deletions.
9 changes: 9 additions & 0 deletions cmd/errtrace/slices_contains_go121.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build go1.21

package main

import "slices"

func slicesContains[T comparable](s []T, find T) bool {
return slices.Contains[[]T](s, find)
}
12 changes: 12 additions & 0 deletions cmd/errtrace/slices_contains_pre_go121.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//go:build !go1.21

package main

func slicesContains[T comparable](s []T, find T) bool {
for _, v := range s {
if v == find {
return true
}
}
return false
}
7 changes: 7 additions & 0 deletions cmd/errtrace/testdata/toolexec-test/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

import "testing"

func TestFoo(t *testing.T) {
t.Errorf("fail")
}
9 changes: 6 additions & 3 deletions cmd/errtrace/toolexec.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ func (cmd *mainCmd) toolExecRewrite(pkg string, args []string) (exitCode int) {
}

// We only modify files that import errtrace, so stdlib is never eliglble.
// To avoid unnecessary parsing, use a heuristic to detect stdlib packages --
// whether the name contains ".".
if !strings.Contains(pkg, ".") {
if isStdLib(args) {
return cmd.runOriginal(args)
}

Expand Down Expand Up @@ -224,3 +222,8 @@ func readBuildSHA() (_ string, ok bool) {
}
return sha, sha != ""
}

// isStdLib checks if the current execution is for stdlib.
func isStdLib(args []string) bool {
return slicesContains(args, "-std")
}
94 changes: 79 additions & 15 deletions cmd/errtrace/toolexec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,87 @@ func TestToolExec(t *testing.T) {
}
sort.Strings(wantTraces)

t.Run("no toolexec", func(t *testing.T) {
stdout, _ := runGo(t, testProg, "run", ".")
if lines := fileLines(stdout); len(lines) > 0 {
t.Errorf("expected no file:line, got %v", lines)
}
})
tests := []struct {
name string
goArgs func(t testing.TB) []string
wantTraces []string
}{
{
name: "no toolexec",
goArgs: func(t testing.TB) []string {
return []string{"."}
},
wantTraces: nil,
},
{
name: "toolexec with pkg",
goArgs: func(t testing.TB) []string {
return []string{"-toolexec", errTraceCmd, "."}
},
wantTraces: wantTraces,
},
{
name: "toolexec with files",
goArgs: func(t testing.TB) []string {
files, err := goListFiles([]string{testProg})
if err != nil {
t.Fatalf("list go files in %v: %v", testProg, err)
}

// TODO: Once go.1.20 is dropped, we can use slices.DeleteFunc
nonTest := files[:0]
for _, file := range files {
if !strings.HasSuffix(file, "_test.go") {
nonTest = append(nonTest, file)
}
}

args := []string{"-toolexec", errTraceCmd}
args = append(args, nonTest...)
return args
},
wantTraces: wantTraces,
},
}

t.Run("with toolexec", func(t *testing.T) {
stdout, _ := runGo(t, testProg, "run", "-toolexec", errTraceCmd, ".")
gotLines := fileLines(stdout)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
args := tt.goArgs(t)

sort.Strings(gotLines)
if d := diff.Diff(wantTraces, gotLines); d != "" {
t.Errorf("diff in traces:\n%s", d)
t.Errorf("go run output:\n%s", stdout)
}
})
verify := func(t testing.TB, stdout string) {
gotLines := fileLines(stdout)
sort.Strings(gotLines)

if d := diff.Diff(tt.wantTraces, gotLines); d != "" {
t.Errorf("diff in traces:\n%s", d)
t.Errorf("go run output:\n%s", stdout)
}
}

t.Run("go run", func(t *testing.T) {
runArgs := append([]string{"run"}, args...)
stdout, _ := runGo(t, testProg, runArgs...)
verify(t, stdout)
})

t.Run("go build", func(t *testing.T) {
outExe := filepath.Join(t.TempDir(), "main")
if runtime.GOOS == "windows" {
outExe += ".exe"
}

runArgs := append([]string{"build", "-o", outExe}, args...)
runGo(t, testProg, runArgs...)

cmd := exec.Command(outExe)
output, err := cmd.Output()
if err != nil {
t.Fatalf("run built binary: %v", err)
}
verify(t, string(output))
})
})
}
}

func findTraceLines(t testing.TB, file string) []int {
Expand Down

0 comments on commit c3648ce

Please sign in to comment.