blob: 4d78c271bada10ea351279bd4f4a73b691b7f89c [file] [log] [blame]
// Copyright 2020 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 main_test
import (
"fmt"
"internal/testenv"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"reflect"
"runtime"
"sort"
"strings"
"sync"
"testing"
)
func TestMain(m *testing.M) {
os.Exit(testMain(m))
}
func testMain(m *testing.M) int {
dir, err := ioutil.TempDir("", "go2gotest")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer os.RemoveAll(dir)
testTempDir = dir
return m.Run()
}
// testTempDir is a temporary directory the tests can use.
var testTempDir string
// testGo2go is the version of cmd/go2go run by the tests.
var testGo2go string
// testGo2goOnce ensures that testGo2go is built only once.
var testGo2goOnce sync.Once
// testGo2goErr is an error that occurred when building testGo2go.
// In the normal case this is nil.
var testGo2goErr error
// buildGo2go builds an up-to-date version of cmd/go2go.
// This is not run from TestMain because it's simpler if it has a *testing.T.
func buildGo2go(t *testing.T) {
t.Helper()
testenv.MustHaveGoBuild(t)
testGo2goOnce.Do(func() {
testGo2go = filepath.Join(testTempDir, "go2go.exe")
t.Logf("running [go build -o %s]", testGo2go)
out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", testGo2go).CombinedOutput()
if len(out) > 0 {
t.Logf("%s", out)
}
testGo2goErr = err
})
if testGo2goErr != nil {
t.Fatal("failed to build testgo2go program:", testGo2goErr)
}
}
func TestGO2PATH(t *testing.T) {
t.Parallel()
buildGo2go(t)
copyFile := func(path string, info os.FileInfo, err error) error {
if err != nil {
t.Fatal(err)
}
newPath := filepath.Join(testTempDir, path)
if info.IsDir() {
if err := os.MkdirAll(newPath, 0o755); err != nil {
t.Fatal(err)
}
return nil
}
data, err := ioutil.ReadFile(path)
if err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(newPath, data, 0o444); err != nil {
t.Fatal(err)
}
return nil
}
if err := filepath.Walk("testdata/go2path/src", copyFile); err != nil {
t.Fatal(err)
}
d, err := os.Open(filepath.Join(testTempDir, "testdata/go2path/src"))
if err != nil {
t.Fatal(err)
}
defer d.Close()
dirs, err := d.Readdirnames(-1)
if err != nil {
t.Fatal(err)
}
sort.Strings(dirs)
for _, dir := range dirs {
t.Run(dir, func(t *testing.T) {
cmd := exec.Command(testGo2go, "test")
cmd.Dir = filepath.Join(testTempDir, "testdata", "go2path", "src", dir)
cmd.Env = append(os.Environ(), "GO2PATH="+filepath.Join(testTempDir, "testdata", "go2path"))
t.Logf("running [%s test] in %s", testGo2go, cmd.Dir)
out, err := cmd.CombinedOutput()
if len(out) > 0 {
t.Log(dir)
t.Logf("%s", out)
}
if err != nil {
t.Errorf("error testing %s: %v", dir, err)
}
})
}
}
type testFile struct {
name, contents string
}
type testFiles []testFile
func (tfs testFiles) create(t *testing.T, gopath string) {
t.Helper()
for _, tf := range tfs {
fn := filepath.Join(gopath, "src", tf.name)
if err := os.MkdirAll(filepath.Dir(fn), 0o755); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(fn, []byte(tf.contents), 0o444); err != nil {
t.Fatal(err)
}
}
}
func TestGO2PATHEqGOPATH(t *testing.T) {
t.Parallel()
buildGo2go(t)
gopath := t.TempDir()
testFiles{
{
"pkg/p.go",
`package pkg; func F() {}`,
},
{
"cmd/cmd.go2",
`package main; import "pkg"; func main() { pkg.F() }`,
},
}.create(t, gopath)
t.Log("go2go build")
cmdDir := filepath.Join(gopath, "src", "cmd")
cmd := exec.Command(testGo2go, "build")
cmd.Dir = cmdDir
cmd.Env = append(os.Environ(),
"GOPATH="+gopath,
"GO2PATH="+gopath,
"GO111MODULE=off",
)
out, err := cmd.CombinedOutput()
if len(out) > 0 {
t.Logf("%s", out)
}
if err != nil {
t.Fatalf(`error running "go2go build": %v`, err)
}
t.Log("./cmd")
cmdName := "./cmd"
if runtime.GOOS == "windows" {
cmdName += ".exe"
}
cmd = exec.Command(cmdName)
cmd.Dir = cmdDir
out, err = cmd.CombinedOutput()
if err != nil {
t.Fatalf("error running %q: %v", cmdName, err)
}
if len(out) != 0 {
t.Errorf("unexpected output: %q", out)
}
}
const buildSource = `
package main
import "fmt"
func PrintSlice[Elem any](s []Elem) {
for _, v := range s {
fmt.Println(v)
}
}
func main() {
PrintSlice([]string{"hello", "world"})
}
`
func TestBuild(t *testing.T) {
t.Parallel()
buildGo2go(t)
gopath := t.TempDir()
testFiles{
{
"hello/hello.go2",
buildSource,
},
}.create(t, gopath)
t.Log("go2go build")
dir := filepath.Join(gopath, "src", "hello")
cmd := exec.Command(testGo2go, "build")
cmd.Dir = dir
out, err := cmd.CombinedOutput()
if len(out) > 0 {
t.Logf("%s", out)
}
if err != nil {
t.Fatalf(`error running "go2go build": %v`, err)
}
runHello(t, dir)
}
func runHello(t *testing.T, dir string) {
cmdName := "./hello"
if runtime.GOOS == "windows" {
cmdName += ".exe"
}
cmd := exec.Command(cmdName)
cmd.Dir = dir
out, err := cmd.CombinedOutput()
t.Log("./hello")
if len(out) > 0 {
t.Logf("%s", out)
}
if err != nil {
t.Fatalf("error running hello: %v", err)
}
got := strings.Split(strings.TrimSpace(string(out)), "\n")
want := []string{"hello", "world"}
if !reflect.DeepEqual(got, want) {
t.Errorf("hello output %v, want %v", got, want)
}
}
func TestBuildPackage(t *testing.T) {
t.Parallel()
buildGo2go(t)
gopath := t.TempDir()
testFiles{
{
"cmd/hello/hello.go2",
buildSource,
},
}.create(t, gopath)
t.Log("go2go build")
cmd := exec.Command(testGo2go, "build", "cmd/hello")
cmd.Dir = gopath
cmd.Env = append(os.Environ(),
"GO2PATH="+gopath,
)
out, err := cmd.CombinedOutput()
if len(out) > 0 {
t.Logf("%s", out)
}
if err != nil {
t.Fatalf(`error running "go2go build": %v`, err)
}
runHello(t, gopath)
}
func TestTransitiveGo1(t *testing.T) {
t.Parallel()
buildGo2go(t)
gopath := t.TempDir()
testFiles{
{
"a/a.go2",
`package a; func ident[T any](v T) T { return v }; func F1(v int) int { return ident(v) }`,
},
{
"b/b.go",
`package b; import "a"; func F2(v int) int { return a.F1(v) }`,
},
{
"c/c.go2",
`package main; import ("fmt"; "b"); func main() { fmt.Println(b.F2(42)) }`,
},
}.create(t, gopath)
t.Log("go2go build")
cmd := exec.Command(testGo2go, "build", "c")
cmd.Dir = gopath
cmd.Env = append(os.Environ(),
"GO2PATH="+gopath,
)
out, err := cmd.CombinedOutput()
if len(out) > 0 {
t.Logf("%s", out)
}
if err != nil {
t.Fatalf(`error running "go2go build": %v`, err)
}
}
func TestBuildWithTags(t *testing.T) {
t.Parallel()
buildGo2go(t)
gopath := t.TempDir()
testFiles{
{
"a/a.go2",
`package a; func ident[T any](v T) T { return v }; func F1(s string) string { return ident(s) }`,
},
{
"b/b_appengine.go",
`// +build appengine
package b; import "a"; func F2(s string) string { return a.F1(s) + " App Engine!" }`,
},
{
"b/b.go",
`// +build !appengine
package b; import "a"; func F2(s string) string { return a.F1(s) + " World!" }`,
},
{
"c/c.go2",
`package main; import ("fmt"; "b"); func main() { fmt.Println(b.F2("Hello")) }`,
},
}.create(t, gopath)
t.Log("go2go build")
cmd := exec.Command(testGo2go, "build", "-tags=appengine", "c")
cmd.Dir = gopath
cmd.Env = append(os.Environ(),
"GO2PATH="+gopath,
)
out, err := cmd.CombinedOutput()
if len(out) > 0 {
t.Logf("%s", out)
}
if err != nil {
t.Fatalf(`error running "go2go build": %v`, err)
}
cmdName := "./c"
if runtime.GOOS == "windows" {
cmdName += ".exe"
}
cmd = exec.Command(cmdName)
cmd.Dir = gopath
out, err = cmd.CombinedOutput()
t.Log("./c")
if len(out) > 0 {
t.Logf("%s", out)
}
if err != nil {
t.Fatalf("error running c: %v", err)
}
got := strings.Split(strings.TrimSpace(string(out)), "\n")[0]
want := "Hello App Engine!"
if !reflect.DeepEqual(got, want) {
t.Errorf("c output %v, want %v", got, want)
}
}