--- /dev/null
+// Copyright 2017 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 errorstest
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func path(file string) string {
+ return filepath.Join("src", file)
+}
+
+func check(t *testing.T, file string) {
+ t.Run(file, func(t *testing.T) {
+ t.Parallel()
+
+ contents, err := ioutil.ReadFile(path(file))
+ if err != nil {
+ t.Fatal(err)
+ }
+ var errors []*regexp.Regexp
+ for i, line := range bytes.Split(contents, []byte("\n")) {
+ if !bytes.Contains(line, []byte("ERROR HERE")) {
+ continue
+ }
+ var re *regexp.Regexp
+ frags := bytes.SplitAfterN(line, []byte("ERROR HERE: "), 1)
+ if len(frags) == 1 {
+ re = regexp.MustCompile(regexp.QuoteMeta(fmt.Sprintf("%s:%d:", file, i+1)))
+ } else {
+ re, err = regexp.Compile(string(frags[1]))
+ if err != nil {
+ t.Errorf("Invalid regexp after `ERROR HERE: `: %q", frags[1])
+ continue
+ }
+ }
+ errors = append(errors, re)
+ }
+ if len(errors) == 0 {
+ t.Fatalf("cannot find ERROR HERE")
+ }
+ expect(t, file, errors)
+ })
+}
+
+func expect(t *testing.T, file string, errors []*regexp.Regexp) {
+ cmd := exec.Command("go", "build", "-gcflags=-C", path(file))
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Errorf("expected cgo to fail but it succeeded")
+ }
+
+ lines := bytes.Split(out, []byte("\n"))
+ for _, re := range errors {
+ found := false
+ for _, line := range lines {
+ if re.Match(line) {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("expected error output to contain %q", re)
+ }
+ }
+
+ if t.Failed() {
+ t.Logf("actual output:\n%s", out)
+ }
+}
+
+func sizeofLongDouble(t *testing.T) int {
+ cmd := exec.Command("go", "run", path("long_double_size.go"))
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%#q: %v:\n%s", strings.Join(cmd.Args, " "), err, out)
+ }
+
+ i, err := strconv.Atoi(strings.TrimSpace(string(out)))
+ if err != nil {
+ t.Fatalf("long_double_size.go printed invalid size: %s", out)
+ }
+ return i
+}
+
+func TestReportsTypeErrors(t *testing.T) {
+ for _, file := range []string{
+ "err1.go",
+ "err2.go",
+ "err3.go",
+ "issue7757.go",
+ "issue8442.go",
+ "issue11097a.go",
+ "issue11097b.go",
+ "issue13129.go",
+ "issue13423.go",
+ "issue13635.go",
+ "issue13830.go",
+ "issue16116.go",
+ "issue16591.go",
+ "issue18452.go",
+ "issue18889.go",
+ } {
+ check(t, file)
+ }
+
+ if sizeofLongDouble(t) > 8 {
+ check(t, "err4.go")
+ }
+}
+
+func TestToleratesOptimizationFlag(t *testing.T) {
+ for _, cflags := range []string{
+ "",
+ "-O",
+ } {
+ cflags := cflags
+ t.Run(cflags, func(t *testing.T) {
+ t.Parallel()
+
+ cmd := exec.Command("go", "build", path("issue14669.go"))
+ cmd.Env = append(os.Environ(), "CGO_CFLAGS="+cflags)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("%#q: %v:\n%s", strings.Join(cmd.Args, " "), err, out)
+ }
+ })
+ }
+}
+
+func TestMallocCrashesOnNil(t *testing.T) {
+ t.Parallel()
+
+ cmd := exec.Command("go", "run", path("malloc.go"))
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Logf("%#q:\n%s", strings.Join(cmd.Args, " "), out)
+ t.Fatalf("succeeded unexpectedly")
+ }
+}
// Tests that cgo detects invalid pointer passing at runtime.
-package main
+package errorstest
import (
"bufio"
"bytes"
"fmt"
- "io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
- "runtime"
"strings"
- "sync"
+ "testing"
)
// ptrTest is the tests without the boilerplate.
},
}
-func main() {
- os.Exit(doTests())
+func TestPointerChecks(t *testing.T) {
+ for _, pt := range ptrTests {
+ pt := pt
+ t.Run(pt.name, func(t *testing.T) {
+ testOne(t, pt)
+ })
+ }
}
-func doTests() int {
- gopath, err := ioutil.TempDir("", "cgoerrors")
+func testOne(t *testing.T, pt ptrTest) {
+ t.Parallel()
+
+ gopath, err := ioutil.TempDir("", filepath.Base(t.Name()))
if err != nil {
- fmt.Fprintln(os.Stderr, err)
- return 2
+ t.Fatal(err)
}
defer os.RemoveAll(gopath)
- if err := os.MkdirAll(filepath.Join(gopath, "src"), 0777); err != nil {
- fmt.Fprintln(os.Stderr, err)
- return 2
- }
-
- workers := runtime.NumCPU() + 1
-
- var wg sync.WaitGroup
- c := make(chan int)
- errs := make(chan int)
- for i := 0; i < workers; i++ {
- wg.Add(1)
- go func() {
- worker(gopath, c, errs)
- wg.Done()
- }()
- }
-
- for i := range ptrTests {
- c <- i
- }
- close(c)
-
- go func() {
- wg.Wait()
- close(errs)
- }()
-
- tot := 0
- for e := range errs {
- tot += e
- }
- return tot
-}
-
-func worker(gopath string, c, errs chan int) {
- e := 0
- for i := range c {
- if !doOne(gopath, i) {
- e++
- }
- }
- if e > 0 {
- errs <- e
- }
-}
-
-func doOne(gopath string, i int) bool {
- t := &ptrTests[i]
-
- dir := filepath.Join(gopath, "src", fmt.Sprintf("dir%d", i))
- if err := os.Mkdir(dir, 0777); err != nil {
- fmt.Fprintln(os.Stderr, err)
- return false
+ src := filepath.Join(gopath, "src")
+ if err := os.Mkdir(src, 0777); err != nil {
+ t.Fatal(err)
}
- name := filepath.Join(dir, fmt.Sprintf("t%d.go", i))
+ name := filepath.Join(src, fmt.Sprintf("%s.go", filepath.Base(t.Name())))
f, err := os.Create(name)
if err != nil {
- fmt.Fprintln(os.Stderr, err)
- return false
+ t.Fatal(err)
}
b := bufio.NewWriter(f)
fmt.Fprintln(b, `package main`)
fmt.Fprintln(b)
fmt.Fprintln(b, `/*`)
- fmt.Fprintln(b, t.c)
+ fmt.Fprintln(b, pt.c)
fmt.Fprintln(b, `*/`)
fmt.Fprintln(b, `import "C"`)
fmt.Fprintln(b)
- for _, imp := range t.imports {
+ for _, imp := range pt.imports {
fmt.Fprintln(b, `import "`+imp+`"`)
}
- if len(t.imports) > 0 {
+ if len(pt.imports) > 0 {
fmt.Fprintln(b)
}
- if len(t.support) > 0 {
- fmt.Fprintln(b, t.support)
+ if len(pt.support) > 0 {
+ fmt.Fprintln(b, pt.support)
fmt.Fprintln(b)
}
fmt.Fprintln(b, `func main() {`)
- fmt.Fprintln(b, t.body)
+ fmt.Fprintln(b, pt.body)
fmt.Fprintln(b, `}`)
if err := b.Flush(); err != nil {
- fmt.Fprintf(os.Stderr, "flushing %s: %v\n", name, err)
- return false
+ t.Fatalf("flushing %s: %v", name, err)
}
if err := f.Close(); err != nil {
- fmt.Fprintf(os.Stderr, "closing %s: %v\n", name, err)
- return false
+ t.Fatalf("closing %s: %v", name, err)
}
- for _, e := range t.extra {
- if err := ioutil.WriteFile(filepath.Join(dir, e.name), []byte(e.contents), 0644); err != nil {
- fmt.Fprintf(os.Stderr, "writing %s: %v\n", e.name, err)
- return false
+ for _, e := range pt.extra {
+ if err := ioutil.WriteFile(filepath.Join(src, e.name), []byte(e.contents), 0644); err != nil {
+ t.Fatalf("writing %s: %v", e.name, err)
}
}
- ok := true
+ args := func(cmd *exec.Cmd) string {
+ return strings.Join(cmd.Args, " ")
+ }
cmd := exec.Command("go", "build")
- cmd.Dir = dir
+ cmd.Dir = src
cmd.Env = addEnv("GOPATH", gopath)
buf, err := cmd.CombinedOutput()
if err != nil {
- fmt.Fprintf(os.Stderr, "test %s failed to build: %v\n%s", t.name, err, buf)
- return false
+ t.Logf("%#q:\n%s", args(cmd), buf)
+ t.Fatalf("failed to build: %v", err)
}
- exe := filepath.Join(dir, filepath.Base(dir))
+ exe := filepath.Join(src, filepath.Base(src))
cmd = exec.Command(exe)
- cmd.Dir = dir
+ cmd.Dir = src
- if t.expensive {
+ if pt.expensive {
cmd.Env = cgocheckEnv("1")
buf, err := cmd.CombinedOutput()
if err != nil {
- var errbuf bytes.Buffer
- if t.fail {
- fmt.Fprintf(&errbuf, "test %s marked expensive but failed when not expensive: %v\n", t.name, err)
+ t.Logf("%#q:\n%s", args(cmd), buf)
+ if pt.fail {
+ t.Fatalf("test marked expensive, but failed when not expensive: %v", err)
} else {
- fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=1: %v\n", t.name, err)
+ t.Errorf("failed unexpectedly with GODEBUG=cgocheck=1: %v", err)
}
- reportTestOutput(&errbuf, t.name, buf)
- os.Stderr.Write(errbuf.Bytes())
- ok = false
}
cmd = exec.Command(exe)
- cmd.Dir = dir
+ cmd.Dir = src
}
- if t.expensive {
+ if pt.expensive {
cmd.Env = cgocheckEnv("2")
}
buf, err = cmd.CombinedOutput()
-
- if t.fail {
+ if pt.fail {
if err == nil {
- var errbuf bytes.Buffer
- fmt.Fprintf(&errbuf, "test %s did not fail as expected\n", t.name)
- reportTestOutput(&errbuf, t.name, buf)
- os.Stderr.Write(errbuf.Bytes())
- ok = false
+ t.Logf("%#q:\n%s", args(cmd), buf)
+ t.Fatalf("did not fail as expected")
} else if !bytes.Contains(buf, []byte("Go pointer")) {
- var errbuf bytes.Buffer
- fmt.Fprintf(&errbuf, "test %s output does not contain expected error (failed with %v)\n", t.name, err)
- reportTestOutput(&errbuf, t.name, buf)
- os.Stderr.Write(errbuf.Bytes())
- ok = false
+ t.Logf("%#q:\n%s", args(cmd), buf)
+ t.Fatalf("did not print expected error (failed with %v)", err)
}
} else {
if err != nil {
- var errbuf bytes.Buffer
- fmt.Fprintf(&errbuf, "test %s failed unexpectedly: %v\n", t.name, err)
- reportTestOutput(&errbuf, t.name, buf)
- os.Stderr.Write(errbuf.Bytes())
- ok = false
+ t.Logf("%#q:\n%s", args(cmd), buf)
+ t.Fatalf("failed unexpectedly: %v", err)
}
- if !t.expensive && ok {
+ if !pt.expensive {
// Make sure it passes with the expensive checks.
cmd := exec.Command(exe)
- cmd.Dir = dir
+ cmd.Dir = src
cmd.Env = cgocheckEnv("2")
buf, err := cmd.CombinedOutput()
if err != nil {
- var errbuf bytes.Buffer
- fmt.Fprintf(&errbuf, "test %s failed unexpectedly with expensive checks: %v\n", t.name, err)
- reportTestOutput(&errbuf, t.name, buf)
- os.Stderr.Write(errbuf.Bytes())
- ok = false
+ t.Logf("%#q:\n%s", args(cmd), buf)
+ t.Fatalf("failed unexpectedly with expensive checks: %v", err)
}
}
}
- if t.fail && ok {
+ if pt.fail {
cmd = exec.Command(exe)
- cmd.Dir = dir
+ cmd.Dir = src
cmd.Env = cgocheckEnv("0")
buf, err := cmd.CombinedOutput()
if err != nil {
- var errbuf bytes.Buffer
- fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=0: %v\n", t.name, err)
- reportTestOutput(&errbuf, t.name, buf)
- os.Stderr.Write(errbuf.Bytes())
- ok = false
+ t.Logf("%#q:\n%s", args(cmd), buf)
+ t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err)
}
}
-
- return ok
-}
-
-func reportTestOutput(w io.Writer, name string, buf []byte) {
- fmt.Fprintf(w, "=== test %s output ===\n", name)
- fmt.Fprintf(w, "%s", buf)
- fmt.Fprintf(w, "=== end of test %s output ===\n", name)
}
func cgocheckEnv(val string) []string {