Skip to content

Commit

Permalink
Support relative pkg paths with go modules
Browse files Browse the repository at this point in the history
There is no public API for finding and reading the go.mod file, so copy some internals
from go to locate and read the package line from the go.mod file
  • Loading branch information
dnephin committed Dec 2, 2018
1 parent 795813a commit ec29767
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 39 deletions.
126 changes: 126 additions & 0 deletions testjson/pkgpathprefix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package testjson

import (
"bytes"
"go/build"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
)

func relativePackagePath(pkgpath string) string {
if pkgpath == pkgPathPrefix {
return "."
}
return strings.TrimPrefix(pkgpath, pkgPathPrefix+"/")
}

func getPkgPathPrefix() string {
cwd, _ := os.Getwd()
if isGoModuleEnabled() {
prefix := getPkgPathPrefixFromGoModule(cwd)
if prefix != "" {
return prefix
}
}
return getPkgPathPrefixGoPath(cwd)
}

func isGoModuleEnabled() bool {
version := runtime.Version()
if strings.HasPrefix(version, "go1.10") {
return false
}
// Go modules may not be enabled if env var is unset, or set to auto, however
// we can always fall back to using GOPATH as the prefix if a go.mod is not
// found.
return os.Getenv("GO111MODULE") != "off"
}

// TODO: might not work on windows
func getPkgPathPrefixGoPath(cwd string) string {
gopaths := strings.Split(build.Default.GOPATH, string(filepath.ListSeparator))
for _, gopath := range gopaths {
gosrcpath := gopath + "/src/"
if strings.HasPrefix(cwd, gosrcpath) {
return strings.TrimPrefix(cwd, gosrcpath)
}
}
return ""
}

func getPkgPathPrefixFromGoModule(cwd string) string {
filename := goModuleFilePath(cwd)
if filename == "" {
return ""
}
raw, err := ioutil.ReadFile(filename)
if err != nil {
// TODO: log.Warn
return ""
}
return pkgPathFromGoModuleFile(raw)
}

var (
slashSlash = []byte("//")
moduleStr = []byte("module")
)

// Copy of ModulePath from golang.org/src/cmd/go/internal/modfile/read.go
func pkgPathFromGoModuleFile(mod []byte) string {
for len(mod) > 0 {
line := mod
mod = nil
if i := bytes.IndexByte(line, '\n'); i >= 0 {
line, mod = line[:i], line[i+1:]
}
if i := bytes.Index(line, slashSlash); i >= 0 {
line = line[:i]
}
line = bytes.TrimSpace(line)
if !bytes.HasPrefix(line, moduleStr) {
continue
}
line = line[len(moduleStr):]
n := len(line)
line = bytes.TrimSpace(line)
if len(line) == n || len(line) == 0 {
continue
}

if line[0] == '"' || line[0] == '`' {
p, err := strconv.Unquote(string(line))
if err != nil {
return "" // malformed quoted string or multi-line module path
}
return p
}

return string(line)
}
return "" // missing module path
}

// A rough re-implementation of FindModuleRoot from
// golang.org/src/cmd/go/internal/modload/init.go
func goModuleFilePath(cwd string) string {
dir := filepath.Clean(cwd)

for {
path := filepath.Join(dir, "go.mod")
if _, err := os.Stat(path); err == nil {
return path
}
parent := filepath.Dir(dir)
if parent == dir {
return ""
}
dir = parent
}
}

var pkgPathPrefix = getPkgPathPrefix()
29 changes: 29 additions & 0 deletions testjson/pkgpathprefix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package testjson

import (
"testing"

"gotest.tools/assert"
"gotest.tools/skip"
)

func TestRelativePackagePath(t *testing.T) {
prefix := "gotest.tools/gotestsum/testjson"
defer patchPkgPathPrefix(prefix)()
relPath := relativePackagePath(prefix + "/extra/relpath")
assert.Equal(t, relPath, "extra/relpath")

relPath = relativePackagePath(prefix)
assert.Equal(t, relPath, ".")
}

func TestGetPkgPathPrefix(t *testing.T) {
t.Run("with go path", func(t *testing.T) {
skip.If(t, isGoModuleEnabled())
assert.Equal(t, getPkgPathPrefix(), "gotest.tools/gotestsum/testjson")
})
t.Run("with go modules", func(t *testing.T) {
skip.If(t, !isGoModuleEnabled())
assert.Equal(t, getPkgPathPrefix(), "gotest.tools/gotestsum")
})
}
25 changes: 0 additions & 25 deletions testjson/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ package testjson

import (
"fmt"
"go/build"
"os"
"path/filepath"
"strings"

"github.com/fatih/color"
Expand Down Expand Up @@ -157,28 +154,6 @@ func colorEvent(event TestEvent) func(format string, a ...interface{}) string {
return color.WhiteString
}

func relativePackagePath(pkgpath string) string {
if pkgpath == pkgPathPrefix {
return "."
}
return strings.TrimPrefix(pkgpath, pkgPathPrefix+"/")
}

// TODO: might not work on windows
func getPkgPathPrefix() string {
cwd, _ := os.Getwd()
gopaths := strings.Split(build.Default.GOPATH, string(filepath.ListSeparator))
for _, gopath := range gopaths {
gosrcpath := gopath + "/src/"
if strings.HasPrefix(cwd, gosrcpath) {
return strings.TrimPrefix(cwd, gosrcpath)
}
}
return ""
}

var pkgPathPrefix = getPkgPathPrefix()

// NewEventFormatter returns a formatter for printing events.
func NewEventFormatter(format string) EventFormatter {
switch format {
Expand Down
14 changes: 0 additions & 14 deletions testjson/printer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,6 @@ func patchPkgPathPrefix(val string) func() {
return func() { pkgPathPrefix = oldVal }
}

func TestRelativePackagePath(t *testing.T) {
relPath := relativePackagePath(
"gotest.tools/gotestsum/testjson/extra/relpath")
assert.Equal(t, relPath, "extra/relpath")

relPath = relativePackagePath(
"gotest.tools/gotestsum/testjson")
assert.Equal(t, relPath, ".")
}

func TestGetPkgPathPrefix(t *testing.T) {
assert.Equal(t, pkgPathPrefix, "gotest.tools/gotestsum/testjson")
}

func TestScanTestOutputWithShortVerboseFormat(t *testing.T) {
defer patchPkgPathPrefix("github.com/gotestyourself/gotestyourself")()

Expand Down

0 comments on commit ec29767

Please sign in to comment.