Skip to content

Commit

Permalink
upgrade golang.org/x/tools, add integration tests #2
Browse files Browse the repository at this point in the history
  • Loading branch information
flier committed Feb 20, 2024
1 parent 1595f33 commit f9e318d
Show file tree
Hide file tree
Showing 74 changed files with 2,429 additions and 7,399 deletions.
163 changes: 163 additions & 0 deletions cmd/bitflags/endtoend_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package main

import (
"flag"
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
"sync"
"testing"

"github.com/flier/go-bitflags/internal/testenv"
)

// This file contains a test that compiles and runs each program in testdata
// after generating the string method for its type. The rule is that for testdata/x.go
// we run `bitflags -type X` and then compile and run the program. The resulting
// binary panics if the String method for X is not correct, including for error cases.

func TestMain(m *testing.M) {
if os.Getenv("BITFLAGS_TEST_IS_BITFLAGS") != "" {
main()
os.Exit(0)
}

// Inform subprocesses that they should run the `cmd/bitflags` main instead of
// running tests. It's a close approximation to building and running the real
// command, and much less complicated and expensive to build and clean up.
os.Setenv("BITFLAGS_TEST_IS_BITFLAGS", "1")

flag.Parse()
if testing.Verbose() {
os.Setenv("GOPACKAGESDEBUG", "true")
}

os.Exit(m.Run())
}

func TestEndToEnd(t *testing.T) {
testenv.NeedsTool(t, "go")

executable := executablePath(t)
// Read the testdata directory.
fd, err := os.Open("testdata")
if err != nil {
t.Fatal(err)
}
defer fd.Close()
names, err := fd.Readdirnames(-1)
if err != nil {
t.Fatalf("Readdirnames: %s", err)
}
// Generate, compile, and run the test programs.
for _, name := range names {
if name == "typeparams" {
// ignore the directory containing the tests with type params
continue
}
if !strings.HasSuffix(name, ".go") {
t.Errorf("%s is not a Go file", name)
continue
}
if strings.HasPrefix(name, "tag_") || strings.HasPrefix(name, "vary_") {
// This file is used for tag processing in TestTags or TestConstValueChange, below.
continue
}
t.Run(name, func(t *testing.T) {
if name == "cgo.go" {
testenv.NeedsTool(t, "cgo")
}
bitflagsCompileAndRun(t, t.TempDir(), executable, typeName(name), name)
})
}
}

var exe struct {
path string
err error
once sync.Once
}

func executablePath(t *testing.T) string {
testenv.NeedsExec(t)

exe.once.Do(func() {
exe.path, exe.err = os.Executable()
})
if exe.err != nil {
t.Fatal(exe.err)
}
return exe.path
}

// a type name for stringer. use the last component of the file name with the .go
func typeName(fname string) string {
// file names are known to be ascii and end .go
base := path.Base(fname)
return fmt.Sprintf("%c%s", base[0]+'A'-'a', base[1:len(base)-len(".go")])
}

// stringerCompileAndRun runs stringer for the named file and compiles and
// runs the target binary in directory dir. That binary will panic if the String method is incorrect.
func bitflagsCompileAndRun(t *testing.T, dir, executable, typeName, fileName string) {
t.Logf("run: %s %s\n", fileName, typeName)
source := filepath.Join(dir, path.Base(fileName))
err := copyFile(source, filepath.Join("testdata", fileName))
if err != nil {
t.Fatalf("copying file to temporary directory: %s", err)
}
stringSource := filepath.Join(dir, typeName+"_string.go")
// Run stringer in temporary directory.
err = run(t, executable, "-type", typeName, "-output", stringSource, source)
if err != nil {
t.Fatal(err)
}
// Run the binary in the temporary directory.
err = run(t, "go", "run", stringSource, source)
if err != nil {
t.Fatal(err)
}
}

// copyFile copies the from file to the to file.
func copyFile(to, from string) error {
toFd, err := os.Create(to)
if err != nil {
return err
}
defer toFd.Close()
fromFd, err := os.Open(from)
if err != nil {
return err
}
defer fromFd.Close()
_, err = io.Copy(toFd, fromFd)
return err
}

// run runs a single command and returns an error if it does not succeed.
// os/exec should have this function, to be honest.
func run(t testing.TB, name string, arg ...string) error {
t.Helper()
return runInDir(t, ".", name, arg...)
}

// runInDir runs a single command in directory dir and returns an error if
// it does not succeed.
func runInDir(t testing.TB, dir, name string, arg ...string) error {
t.Helper()
cmd := testenv.Command(t, name, arg...)
cmd.Dir = dir
cmd.Env = append(os.Environ(), "GO111MODULE=auto")
out, err := cmd.CombinedOutput()
if len(out) > 0 {
t.Logf("%s", out)
}
if err != nil {
return fmt.Errorf("%v: %v", cmd, err)
}
return nil
}
30 changes: 15 additions & 15 deletions cmd/bitflags/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Bitflags is a tool to automate the creation of methods that satisfy the fmt.Stringer
// interface. Given the name of a (signed or unsigned) integer type T that has constants
// defined, bitflags will create a new self-contained Go source file implementing
//
// func (t T) String() string
//
// The file is created in the same package and directory as the package that defines T.
// It has helpful defaults designed for use with go generate.
//
Expand Down Expand Up @@ -95,25 +97,14 @@ type config struct {
}

const (
prog = "bitflags"
author = "Flier Lu <[email protected]>"
repo = "https://github.com/flier/go-bitflags"
summary = `
{{ .Name }} [flags] -type T [directory]
{{ .Name }} [flags] -type T files... # Must be a single package
For more information, see:
https://github.com/flier/go-bitflags/cmd/bitflags`
prog = "bitflags"
author = "Flier Lu <[email protected]>"
repo = "https://github.com/flier/go-bitflags"
)

func main() {
var b strings.Builder
template.Must(template.New("summary").Parse(summary)).Execute(&b, map[string]interface{}{"Name": prog})
summary := strings.TrimRight(b.String(), "\n")

c := config{}
opts.New(&c).Author(author).Summary(summary).Repo(repo).Parse()
opts.New(&c).Summary(summary()).Repo(repo).Author(author).Parse()

g := gen.New(c.TrimPrefix, c.LineComment)

Expand Down Expand Up @@ -165,6 +156,15 @@ func main() {
}
}

func summary() string {
var b strings.Builder
template.Must(template.New("summary").Parse(`
{{ .Name }} [flags] -type T [directory]
{{ .Name }} [flags] -type T files... # Must be a single package
`)).Execute(&b, map[string]interface{}{"Name": prog})
return strings.Trim(b.String(), "\n")
}

func isDirectory(name string) bool {
info, err := os.Stat(name)
if err != nil {
Expand Down
11 changes: 7 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
module github.com/flier/go-bitflags

go 1.16
go 1.18

require (
github.com/jpillora/opts v1.2.0
golang.org/x/mod v0.15.0
golang.org/x/tools v0.18.0
)

require (
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jpillora/opts v1.2.0
github.com/posener/complete v1.2.3 // indirect
golang.org/x/sys v0.1.0 // indirect
golang.org/x/tools v0.1.8
)
34 changes: 5 additions & 29 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,11 @@ github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSg
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
71 changes: 71 additions & 0 deletions internal/goroot/importcfg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 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 goroot is a copy of package internal/goroot
// in the main GO repot. It provides a utility to produce
// an importcfg and import path to package file map mapping
// standard library packages to the locations of their export
// data files.
package goroot

import (
"bytes"
"fmt"
"os/exec"
"strings"
"sync"
)

// Importcfg returns an importcfg file to be passed to the
// Go compiler that contains the cached paths for the .a files for the
// standard library.
func Importcfg() (string, error) {
var icfg bytes.Buffer

m, err := PkgfileMap()
if err != nil {
return "", err
}
fmt.Fprintf(&icfg, "# import config")
for importPath, export := range m {
fmt.Fprintf(&icfg, "\npackagefile %s=%s", importPath, export)
}
s := icfg.String()
return s, nil
}

var (
stdlibPkgfileMap map[string]string
stdlibPkgfileErr error
once sync.Once
)

// PkgfileMap returns a map of package paths to the location on disk
// of the .a file for the package.
// The caller must not modify the map.
func PkgfileMap() (map[string]string, error) {
once.Do(func() {
m := make(map[string]string)
output, err := exec.Command("go", "list", "-export", "-e", "-f", "{{.ImportPath}} {{.Export}}", "std", "cmd").Output()
if err != nil {
stdlibPkgfileErr = err
}
for _, line := range strings.Split(string(output), "\n") {
if line == "" {
continue
}
sp := strings.SplitN(line, " ", 2)
if len(sp) != 2 {
err = fmt.Errorf("determining pkgfile map: invalid line in go list output: %q", line)
return
}
importPath, export := sp[0], sp[1]
if export != "" {
m[importPath] = export
}
}
stdlibPkgfileMap = m
})
return stdlibPkgfileMap, stdlibPkgfileErr
}
Loading

0 comments on commit f9e318d

Please sign in to comment.