2012-05-29 23:10:54 -06:00
|
|
|
// Copyright 2012 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.
|
|
|
|
|
|
|
|
// +build cgo
|
|
|
|
|
|
|
|
package runtime_test
|
|
|
|
|
|
|
|
import (
|
2016-04-29 16:20:27 -06:00
|
|
|
"bytes"
|
2016-02-25 22:16:45 -07:00
|
|
|
"fmt"
|
2016-02-18 12:04:05 -07:00
|
|
|
"internal/testenv"
|
2016-05-04 12:34:54 -06:00
|
|
|
"os"
|
2015-01-02 22:12:34 -07:00
|
|
|
"os/exec"
|
2013-08-07 14:04:28 -06:00
|
|
|
"runtime"
|
2014-10-28 19:53:09 -06:00
|
|
|
"strings"
|
2012-05-29 23:10:54 -06:00
|
|
|
"testing"
|
2016-02-18 12:04:05 -07:00
|
|
|
"time"
|
2012-05-29 23:10:54 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestCgoCrashHandler(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2013-02-20 01:15:02 -07:00
|
|
|
testCrashHandler(t, true)
|
2012-05-29 23:10:54 -06:00
|
|
|
}
|
2013-02-28 01:07:26 -07:00
|
|
|
|
|
|
|
func TestCgoSignalDeadlock(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2013-08-07 14:04:28 -06:00
|
|
|
if testing.Short() && runtime.GOOS == "windows" {
|
|
|
|
t.Skip("Skipping in short mode") // takes up to 64 seconds
|
|
|
|
}
|
2015-12-21 08:29:21 -07:00
|
|
|
got := runTestProg(t, "testprogcgo", "CgoSignalDeadlock")
|
2013-02-28 01:07:26 -07:00
|
|
|
want := "OK\n"
|
|
|
|
if got != want {
|
2015-12-21 08:29:21 -07:00
|
|
|
t.Fatalf("expected %q, but got:\n%s", want, got)
|
2013-02-28 01:07:26 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-07 14:31:52 -06:00
|
|
|
func TestCgoTraceback(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2015-12-21 08:29:21 -07:00
|
|
|
got := runTestProg(t, "testprogcgo", "CgoTraceback")
|
2013-08-07 14:31:52 -06:00
|
|
|
want := "OK\n"
|
|
|
|
if got != want {
|
2015-12-21 08:29:21 -07:00
|
|
|
t.Fatalf("expected %q, but got:\n%s", want, got)
|
2013-08-07 14:31:52 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-29 14:16:13 -06:00
|
|
|
func TestCgoCallbackGC(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2016-08-17 17:22:24 -06:00
|
|
|
switch runtime.GOOS {
|
|
|
|
case "plan9", "windows":
|
2015-07-29 14:16:13 -06:00
|
|
|
t.Skipf("no pthreads on %s", runtime.GOOS)
|
|
|
|
}
|
2015-09-10 18:15:17 -06:00
|
|
|
if testing.Short() {
|
|
|
|
switch {
|
|
|
|
case runtime.GOOS == "dragonfly":
|
|
|
|
t.Skip("see golang.org/issue/11990")
|
|
|
|
case runtime.GOOS == "linux" && runtime.GOARCH == "arm":
|
|
|
|
t.Skip("too slow for arm builders")
|
2016-05-04 23:07:50 -06:00
|
|
|
case runtime.GOOS == "linux" && (runtime.GOARCH == "mips64" || runtime.GOARCH == "mips64le"):
|
|
|
|
t.Skip("too slow for mips64x builders")
|
2015-09-10 18:15:17 -06:00
|
|
|
}
|
2015-08-02 21:43:25 -06:00
|
|
|
}
|
2015-12-21 08:29:21 -07:00
|
|
|
got := runTestProg(t, "testprogcgo", "CgoCallbackGC")
|
2015-07-29 14:16:13 -06:00
|
|
|
want := "OK\n"
|
|
|
|
if got != want {
|
2015-12-21 08:29:21 -07:00
|
|
|
t.Fatalf("expected %q, but got:\n%s", want, got)
|
2015-07-29 14:16:13 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-28 19:53:09 -06:00
|
|
|
func TestCgoExternalThreadPanic(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2014-10-29 17:24:37 -06:00
|
|
|
if runtime.GOOS == "plan9" {
|
2014-10-28 22:02:29 -06:00
|
|
|
t.Skipf("no pthreads on %s", runtime.GOOS)
|
|
|
|
}
|
2015-12-21 08:29:21 -07:00
|
|
|
got := runTestProg(t, "testprogcgo", "CgoExternalThreadPanic")
|
2014-10-28 19:53:09 -06:00
|
|
|
want := "panic: BOOM"
|
|
|
|
if !strings.Contains(got, want) {
|
|
|
|
t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-27 17:15:38 -07:00
|
|
|
func TestCgoExternalThreadSIGPROF(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2014-12-27 17:15:38 -07:00
|
|
|
// issue 9456.
|
2014-12-31 23:10:39 -07:00
|
|
|
switch runtime.GOOS {
|
|
|
|
case "plan9", "windows":
|
2014-12-27 17:15:38 -07:00
|
|
|
t.Skipf("no pthreads on %s", runtime.GOOS)
|
2014-12-31 23:10:39 -07:00
|
|
|
case "darwin":
|
2015-04-11 17:00:53 -06:00
|
|
|
if runtime.GOARCH != "arm" && runtime.GOARCH != "arm64" {
|
2015-02-26 16:05:47 -07:00
|
|
|
// static constructor needs external linking, but we don't support
|
|
|
|
// external linking on OS X 10.6.
|
|
|
|
out, err := exec.Command("uname", "-r").Output()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("uname -r failed: %v", err)
|
|
|
|
}
|
|
|
|
// OS X 10.6 == Darwin 10.x
|
|
|
|
if strings.HasPrefix(string(out), "10.") {
|
|
|
|
t.Skipf("no external linking on OS X 10.6")
|
|
|
|
}
|
2014-12-31 23:10:39 -07:00
|
|
|
}
|
2014-12-27 17:15:38 -07:00
|
|
|
}
|
2015-11-11 17:05:49 -07:00
|
|
|
if runtime.GOARCH == "ppc64" {
|
2014-12-16 16:34:55 -07:00
|
|
|
// TODO(austin) External linking not implemented on
|
|
|
|
// ppc64 (issue #8912)
|
|
|
|
t.Skipf("no external linking on ppc64")
|
|
|
|
}
|
2016-10-05 08:31:11 -06:00
|
|
|
|
|
|
|
exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
got, err := testEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("exit status: %v\n%s", err, got)
|
|
|
|
}
|
|
|
|
|
|
|
|
if want := "OK\n"; string(got) != want {
|
2015-12-21 08:29:21 -07:00
|
|
|
t.Fatalf("expected %q, but got:\n%s", want, got)
|
2014-12-27 17:15:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-21 23:34:48 -06:00
|
|
|
func TestCgoExternalThreadSignal(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2015-07-21 23:34:48 -06:00
|
|
|
// issue 10139
|
|
|
|
switch runtime.GOOS {
|
|
|
|
case "plan9", "windows":
|
|
|
|
t.Skipf("no pthreads on %s", runtime.GOOS)
|
|
|
|
}
|
2016-10-05 08:31:11 -06:00
|
|
|
|
|
|
|
exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
got, err := testEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("exit status: %v\n%s", err, got)
|
|
|
|
}
|
|
|
|
|
|
|
|
want := []byte("OK\n")
|
|
|
|
if !bytes.Equal(got, want) {
|
2015-12-21 08:29:21 -07:00
|
|
|
t.Fatalf("expected %q, but got:\n%s", want, got)
|
2015-07-21 23:34:48 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-15 22:46:22 -06:00
|
|
|
func TestCgoDLLImports(t *testing.T) {
|
|
|
|
// test issue 9356
|
|
|
|
if runtime.GOOS != "windows" {
|
|
|
|
t.Skip("skipping windows specific test")
|
|
|
|
}
|
2015-12-21 08:29:21 -07:00
|
|
|
got := runTestProg(t, "testprogcgo", "CgoDLLImportsMain")
|
2015-03-15 22:46:22 -06:00
|
|
|
want := "OK\n"
|
|
|
|
if got != want {
|
|
|
|
t.Fatalf("expected %q, but got %v", want, got)
|
|
|
|
}
|
|
|
|
}
|
2015-12-19 11:17:10 -07:00
|
|
|
|
|
|
|
func TestCgoExecSignalMask(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2015-12-19 11:17:10 -07:00
|
|
|
// Test issue 13164.
|
|
|
|
switch runtime.GOOS {
|
|
|
|
case "windows", "plan9":
|
|
|
|
t.Skipf("skipping signal mask test on %s", runtime.GOOS)
|
|
|
|
}
|
|
|
|
got := runTestProg(t, "testprogcgo", "CgoExecSignalMask")
|
|
|
|
want := "OK\n"
|
|
|
|
if got != want {
|
|
|
|
t.Errorf("expected %q, got %v", want, got)
|
|
|
|
}
|
|
|
|
}
|
2016-01-08 17:56:02 -07:00
|
|
|
|
|
|
|
func TestEnsureDropM(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2016-01-08 17:56:02 -07:00
|
|
|
// Test for issue 13881.
|
2016-01-18 10:01:48 -07:00
|
|
|
switch runtime.GOOS {
|
|
|
|
case "windows", "plan9":
|
|
|
|
t.Skipf("skipping dropm test on %s", runtime.GOOS)
|
|
|
|
}
|
2016-01-08 17:56:02 -07:00
|
|
|
got := runTestProg(t, "testprogcgo", "EnsureDropM")
|
|
|
|
want := "OK\n"
|
|
|
|
if got != want {
|
|
|
|
t.Errorf("expected %q, got %v", want, got)
|
|
|
|
}
|
|
|
|
}
|
2016-02-18 12:04:05 -07:00
|
|
|
|
|
|
|
// Test for issue 14387.
|
|
|
|
// Test that the program that doesn't need any cgo pointer checking
|
|
|
|
// takes about the same amount of time with it as without it.
|
|
|
|
func TestCgoCheckBytes(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2016-02-18 12:04:05 -07:00
|
|
|
// Make sure we don't count the build time as part of the run time.
|
|
|
|
testenv.MustHaveGoBuild(t)
|
|
|
|
exe, err := buildTestProg(t, "testprogcgo")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2016-02-25 22:16:45 -07:00
|
|
|
// Try it 10 times to avoid flakiness.
|
|
|
|
const tries = 10
|
|
|
|
var tot1, tot2 time.Duration
|
|
|
|
for i := 0; i < tries; i++ {
|
|
|
|
cmd := testEnv(exec.Command(exe, "CgoCheckBytes"))
|
|
|
|
cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0", fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
|
2016-02-18 12:04:05 -07:00
|
|
|
|
2016-02-25 22:16:45 -07:00
|
|
|
start := time.Now()
|
|
|
|
cmd.Run()
|
|
|
|
d1 := time.Since(start)
|
2016-02-18 12:04:05 -07:00
|
|
|
|
2016-02-25 22:16:45 -07:00
|
|
|
cmd = testEnv(exec.Command(exe, "CgoCheckBytes"))
|
|
|
|
cmd.Env = append(cmd.Env, fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i))
|
2016-02-18 12:04:05 -07:00
|
|
|
|
2016-02-25 22:16:45 -07:00
|
|
|
start = time.Now()
|
|
|
|
cmd.Run()
|
|
|
|
d2 := time.Since(start)
|
2016-02-18 12:04:05 -07:00
|
|
|
|
2016-02-25 22:16:45 -07:00
|
|
|
if d1*20 > d2 {
|
|
|
|
// The slow version (d2) was less than 20 times
|
|
|
|
// slower than the fast version (d1), so OK.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
tot1 += d1
|
|
|
|
tot2 += d2
|
2016-02-18 12:04:05 -07:00
|
|
|
}
|
2016-02-25 22:16:45 -07:00
|
|
|
|
|
|
|
t.Errorf("cgo check too slow: got %v, expected at most %v", tot2/tries, (tot1/tries)*20)
|
2016-02-18 12:04:05 -07:00
|
|
|
}
|
2016-02-21 11:56:08 -07:00
|
|
|
|
|
|
|
func TestCgoPanicDeadlock(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2016-02-21 11:56:08 -07:00
|
|
|
// test issue 14432
|
|
|
|
got := runTestProg(t, "testprogcgo", "CgoPanicDeadlock")
|
|
|
|
want := "panic: cgo error\n\n"
|
|
|
|
if !strings.HasPrefix(got, want) {
|
|
|
|
t.Fatalf("output does not start with %q:\n%s", want, got)
|
|
|
|
}
|
|
|
|
}
|
2016-03-04 12:29:55 -07:00
|
|
|
|
|
|
|
func TestCgoCCodeSIGPROF(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2016-03-04 12:29:55 -07:00
|
|
|
got := runTestProg(t, "testprogcgo", "CgoCCodeSIGPROF")
|
|
|
|
want := "OK\n"
|
|
|
|
if got != want {
|
|
|
|
t.Errorf("expected %q got %v", want, got)
|
|
|
|
}
|
|
|
|
}
|
2015-12-11 18:16:48 -07:00
|
|
|
|
|
|
|
func TestCgoCrashTraceback(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2015-12-11 18:16:48 -07:00
|
|
|
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
|
|
|
|
t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
|
|
|
|
}
|
|
|
|
got := runTestProg(t, "testprogcgo", "CrashTraceback")
|
|
|
|
for i := 1; i <= 3; i++ {
|
|
|
|
if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) {
|
|
|
|
t.Errorf("missing cgo symbolizer:%d", i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-04-27 15:18:29 -06:00
|
|
|
|
|
|
|
func TestCgoTracebackContext(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2016-04-27 15:18:29 -06:00
|
|
|
got := runTestProg(t, "testprogcgo", "TracebackContext")
|
|
|
|
want := "OK\n"
|
|
|
|
if got != want {
|
|
|
|
t.Errorf("expected %q got %v", want, got)
|
|
|
|
}
|
|
|
|
}
|
2016-04-29 16:20:27 -06:00
|
|
|
|
2016-06-07 22:46:25 -06:00
|
|
|
func testCgoPprof(t *testing.T, buildArg, runArg string) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2016-04-29 16:20:27 -06:00
|
|
|
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
|
|
|
|
t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
|
|
|
|
}
|
|
|
|
testenv.MustHaveGoRun(t)
|
|
|
|
|
2016-06-07 22:46:25 -06:00
|
|
|
exe, err := buildTestProg(t, "testprogcgo", buildArg)
|
2016-04-29 16:20:27 -06:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2016-06-07 22:46:25 -06:00
|
|
|
got, err := testEnv(exec.Command(exe, runArg)).CombinedOutput()
|
2016-04-29 16:20:27 -06:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
fn := strings.TrimSpace(string(got))
|
2016-05-04 12:34:54 -06:00
|
|
|
defer os.Remove(fn)
|
2016-04-29 16:20:27 -06:00
|
|
|
|
2017-02-17 13:27:12 -07:00
|
|
|
for try := 0; try < 2; try++ {
|
|
|
|
cmd := testEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1"))
|
|
|
|
// Check that pprof works both with and without explicit executable on command line.
|
|
|
|
if try == 0 {
|
|
|
|
cmd.Args = append(cmd.Args, exe, fn)
|
|
|
|
} else {
|
|
|
|
cmd.Args = append(cmd.Args, fn)
|
2016-06-20 10:16:17 -06:00
|
|
|
}
|
|
|
|
|
2017-02-17 13:27:12 -07:00
|
|
|
found := false
|
|
|
|
for i, e := range cmd.Env {
|
|
|
|
if strings.HasPrefix(e, "PPROF_TMPDIR=") {
|
|
|
|
cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
|
|
|
|
}
|
2016-04-29 16:20:27 -06:00
|
|
|
|
2017-02-17 13:27:12 -07:00
|
|
|
top, err := cmd.CombinedOutput()
|
|
|
|
t.Logf("%s:\n%s", cmd.Args, top)
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
} else if !bytes.Contains(top, []byte("cpuHog")) {
|
|
|
|
t.Error("missing cpuHog in pprof output")
|
|
|
|
}
|
2016-04-29 16:20:27 -06:00
|
|
|
}
|
|
|
|
}
|
2016-05-27 17:03:44 -06:00
|
|
|
|
2016-06-07 22:46:25 -06:00
|
|
|
func TestCgoPprof(t *testing.T) {
|
|
|
|
testCgoPprof(t, "", "CgoPprof")
|
|
|
|
}
|
2016-05-27 17:03:44 -06:00
|
|
|
|
2016-06-07 22:46:25 -06:00
|
|
|
func TestCgoPprofPIE(t *testing.T) {
|
|
|
|
testCgoPprof(t, "-ldflags=-extldflags=-pie", "CgoPprof")
|
|
|
|
}
|
2016-05-27 17:03:44 -06:00
|
|
|
|
2016-06-07 22:46:25 -06:00
|
|
|
func TestCgoPprofThread(t *testing.T) {
|
|
|
|
testCgoPprof(t, "", "CgoPprofThread")
|
2016-05-27 17:03:44 -06:00
|
|
|
}
|
2016-09-21 10:44:40 -06:00
|
|
|
|
2016-10-04 08:11:55 -06:00
|
|
|
func TestCgoPprofThreadNoTraceback(t *testing.T) {
|
|
|
|
testCgoPprof(t, "", "CgoPprofThreadNoTraceback")
|
|
|
|
}
|
|
|
|
|
2016-09-21 10:44:40 -06:00
|
|
|
func TestRaceProf(t *testing.T) {
|
|
|
|
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
|
|
|
|
t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
|
|
|
|
}
|
|
|
|
|
|
|
|
testenv.MustHaveGoRun(t)
|
|
|
|
|
|
|
|
// This test requires building various packages with -race, so
|
|
|
|
// it's somewhat slow.
|
|
|
|
if testing.Short() {
|
|
|
|
t.Skip("skipping test in -short mode")
|
|
|
|
}
|
|
|
|
|
|
|
|
exe, err := buildTestProg(t, "testprogcgo", "-race")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
got, err := testEnv(exec.Command(exe, "CgoRaceprof")).CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
want := "OK\n"
|
|
|
|
if string(got) != want {
|
|
|
|
t.Errorf("expected %q got %s", want, got)
|
|
|
|
}
|
|
|
|
}
|
2016-10-03 17:58:34 -06:00
|
|
|
|
|
|
|
func TestRaceSignal(t *testing.T) {
|
2016-11-03 23:28:01 -06:00
|
|
|
t.Parallel()
|
2016-10-03 17:58:34 -06:00
|
|
|
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
|
|
|
|
t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
|
|
|
|
}
|
|
|
|
|
|
|
|
testenv.MustHaveGoRun(t)
|
|
|
|
|
|
|
|
// This test requires building various packages with -race, so
|
|
|
|
// it's somewhat slow.
|
|
|
|
if testing.Short() {
|
|
|
|
t.Skip("skipping test in -short mode")
|
|
|
|
}
|
|
|
|
|
|
|
|
exe, err := buildTestProg(t, "testprogcgo", "-race")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
got, err := testEnv(exec.Command(exe, "CgoRaceSignal")).CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
t.Logf("%s\n", got)
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
want := "OK\n"
|
|
|
|
if string(got) != want {
|
|
|
|
t.Errorf("expected %q got %s", want, got)
|
|
|
|
}
|
|
|
|
}
|