diff --git a/misc/cgo/testsanitizers/testdata/tsan11.go b/misc/cgo/testsanitizers/testdata/tsan11.go index 70ac9c8ae2..189e10f699 100644 --- a/misc/cgo/testsanitizers/testdata/tsan11.go +++ b/misc/cgo/testsanitizers/testdata/tsan11.go @@ -45,7 +45,7 @@ static void register_handler(int signo) { import "C" func main() { - ch := make(chan os.Signal) + ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGUSR2) C.register_handler(C.int(syscall.SIGUSR1)) diff --git a/misc/cgo/testsanitizers/testdata/tsan12.go b/misc/cgo/testsanitizers/testdata/tsan12.go index 3e767eee1f..0ef545d09b 100644 --- a/misc/cgo/testsanitizers/testdata/tsan12.go +++ b/misc/cgo/testsanitizers/testdata/tsan12.go @@ -22,7 +22,7 @@ import ( import "C" func main() { - ch := make(chan os.Signal) + ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGUSR1) if err := exec.Command("true").Run(); err != nil { diff --git a/src/cmd/dist/exec.go b/src/cmd/dist/exec.go new file mode 100644 index 0000000000..67305530ae --- /dev/null +++ b/src/cmd/dist/exec.go @@ -0,0 +1,53 @@ +// Copyright 2021 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 + +import ( + "os" + "os/exec" + "strings" +) + +// setDir sets cmd.Dir to dir, and also adds PWD=dir to cmd's environment. +func setDir(cmd *exec.Cmd, dir string) { + cmd.Dir = dir + setEnv(cmd, "PWD", dir) +} + +// setEnv sets cmd.Env so that key = value. +// +// It first removes any existing values for key, so it is safe to call +// even from within cmdbootstrap. +func setEnv(cmd *exec.Cmd, key, value string) { + kv := key + "=" + value + if cmd.Env == nil { + cmd.Env = os.Environ() + } + + prefix := kv[:len(key)+1] + for i, entry := range cmd.Env { + if strings.HasPrefix(entry, prefix) { + cmd.Env[i] = kv + return + } + } + + cmd.Env = append(cmd.Env, kv) +} + +// unsetEnv sets cmd.Env so that key is not present in the environment. +func unsetEnv(cmd *exec.Cmd, key string) { + if cmd.Env == nil { + cmd.Env = os.Environ() + } + + prefix := key + "=" + for i, entry := range cmd.Env { + if strings.HasPrefix(entry, prefix) { + cmd.Env = append(cmd.Env[:i], cmd.Env[i+1:]...) + return + } + } +} diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index f40fa926df..beb7c4650c 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -522,7 +522,8 @@ func (t *tester) registerTests() { heading: "GOOS=ios on darwin/amd64", fn: func(dt *distTest) error { cmd := t.addCmd(dt, "src", t.goTest(), t.timeout(300), "-run=SystemRoots", "crypto/x509") - cmd.Env = append(os.Environ(), "GOOS=ios", "CGO_ENABLED=1") + setEnv(cmd, "GOOS", "ios") + setEnv(cmd, "CGO_ENABLED", "1") return nil }, }) @@ -542,7 +543,7 @@ func (t *tester) registerTests() { cmd := t.addCmd(dt, "src", t.goTest(), t.timeout(300), "runtime", "-cpu=1,2,4", "-quick") // We set GOMAXPROCS=2 in addition to -cpu=1,2,4 in order to test runtime bootstrap code, // creation of first goroutines and first garbage collections in the parallel setting. - cmd.Env = append(os.Environ(), "GOMAXPROCS=2") + setEnv(cmd, "GOMAXPROCS", "2") return nil }, }) @@ -563,7 +564,7 @@ func (t *tester) registerTests() { return nil } cmd := exec.Command("go", "test") - cmd.Dir = filepath.Join(os.Getenv("GOROOT"), "src/cmd/go/testdata/testterminal18153") + setDir(cmd, filepath.Join(os.Getenv("GOROOT"), "src/cmd/go/testdata/testterminal18153")) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() @@ -600,16 +601,13 @@ func (t *tester) registerTests() { return err } - // Run `go test fmt` in the moved GOROOT. + // Run `go test fmt` in the moved GOROOT, without explicitly setting + // GOROOT in the environment. The 'go' command should find itself. cmd := exec.Command(filepath.Join(moved, "bin", "go"), "test", "fmt") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - // Don't set GOROOT in the environment. - for _, e := range os.Environ() { - if !strings.HasPrefix(e, "GOROOT=") && !strings.HasPrefix(e, "GOCACHE=") { - cmd.Env = append(cmd.Env, e) - } - } + unsetEnv(cmd, "GOROOT") + unsetEnv(cmd, "GOCACHE") // TODO(bcmills): ...why‽ err := cmd.Run() if rerr := os.Rename(moved, goroot); rerr != nil { @@ -736,11 +734,9 @@ func (t *tester) registerTests() { heading: "../misc/swig/callback", fn: func(dt *distTest) error { cmd := t.addCmd(dt, "misc/swig/callback", t.goTest()) - cmd.Env = append(os.Environ(), - "CGO_CFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option", - "CGO_CXXFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option", - "CGO_LDFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option", - ) + setEnv(cmd, "CGO_CFLAGS", "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option") + setEnv(cmd, "CGO_CXXFLAGS", "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option") + setEnv(cmd, "CGO_LDFLAGS", "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option") return nil }, }, @@ -892,9 +888,9 @@ func (t *tester) registerSeqTest(name, dirBanner string, cmdline ...interface{}) func (t *tester) bgDirCmd(dir, bin string, args ...string) *exec.Cmd { cmd := exec.Command(bin, args...) if filepath.IsAbs(dir) { - cmd.Dir = dir + setDir(cmd, dir) } else { - cmd.Dir = filepath.Join(goroot, dir) + setDir(cmd, filepath.Join(goroot, dir)) } return cmd } @@ -1132,7 +1128,8 @@ func (t *tester) runHostTest(dir, pkg string) error { defer os.Remove(f.Name()) cmd := t.dirCmd(dir, t.goTest(), "-c", "-o", f.Name(), pkg) - cmd.Env = append(os.Environ(), "GOARCH="+gohostarch, "GOOS="+gohostos) + setEnv(cmd, "GOARCH", gohostarch) + setEnv(cmd, "GOOS", gohostos) if err := cmd.Run(); err != nil { return err } @@ -1141,7 +1138,7 @@ func (t *tester) runHostTest(dir, pkg string) error { func (t *tester) cgoTest(dt *distTest) error { cmd := t.addCmd(dt, "misc/cgo/test", t.goTest()) - cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=auto") + setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=auto") // Skip internal linking cases on linux/arm64 to support GCC-9.4 and above. // See issue #39466. @@ -1149,7 +1146,7 @@ func (t *tester) cgoTest(dt *distTest) error { if t.internalLink() && !skipInternalLink { cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=internal") - cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=internal") + setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=internal") } pair := gohostos + "-" + goarch @@ -1161,9 +1158,9 @@ func (t *tester) cgoTest(dt *distTest) error { break } cmd := t.addCmd(dt, "misc/cgo/test", t.goTest()) - cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=external") + setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=external") - cmd = t.addCmd(dt, "misc/cgo/test", t.goTest(), "-ldflags", "-linkmode=external -s") + t.addCmd(dt, "misc/cgo/test", t.goTest(), "-ldflags", "-linkmode=external -s") if t.supportedBuildmode("pie") { t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie") @@ -1181,10 +1178,10 @@ func (t *tester) cgoTest(dt *distTest) error { "openbsd-386", "openbsd-amd64", "openbsd-arm", "openbsd-arm64", "openbsd-mips64": cmd := t.addCmd(dt, "misc/cgo/test", t.goTest()) - cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=external") + setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=external") // cgo should be able to cope with both -g arguments and colored // diagnostics. - cmd.Env = append(cmd.Env, "CGO_CFLAGS=-g0 -fdiagnostics-color") + setEnv(cmd, "CGO_CFLAGS", "-g0 -fdiagnostics-color") t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", "-linkmode=auto") t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", "-linkmode=external") @@ -1217,7 +1214,7 @@ func (t *tester) cgoTest(dt *distTest) error { // than -static in -extldflags, so test both. // See issue #16651. cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=static") - cmd.Env = append(os.Environ(), "CGO_LDFLAGS=-static -pthread") + setEnv(cmd, "CGO_LDFLAGS", "-static -pthread") } } @@ -1456,7 +1453,7 @@ func (t *tester) raceTest(dt *distTest) error { // We shouldn't need to redo all of misc/cgo/test too. // The race buildler will take care of this. // cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-race") - // cmd.Env = append(os.Environ(), "GOTRACEBACK=2") + // setEnv(cmd, "GOTRACEBACK", "2") } if t.extLink() { // Test with external linking; see issue 9133. @@ -1486,7 +1483,8 @@ func (t *tester) testDirTest(dt *distTest, shard, shards int) error { }) cmd := t.dirCmd("test", "go", "build", "-o", runtest.exe, "run.go") - cmd.Env = append(os.Environ(), "GOOS="+gohostos, "GOARCH="+gohostarch) + setEnv(cmd, "GOOS", gohostos) + setEnv(cmd, "GOARCH", gohostarch) runtest.err = cmd.Run() }) if runtest.err != nil { @@ -1650,7 +1648,7 @@ func (t *tester) runPrecompiledStdTest(timeout time.Duration) error { bin := t.prebuiltGoPackageTestBinary() fmt.Fprintf(os.Stderr, "# %s: using pre-built %s...\n", stdMatches[0], bin) cmd := exec.Command(bin, "-test.short="+short(), "-test.timeout="+timeout.String()) - cmd.Dir = filepath.Dir(bin) + setDir(cmd, filepath.Dir(bin)) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Start(); err != nil { diff --git a/src/cmd/dist/util.go b/src/cmd/dist/util.go index df60145d1e..28fe5e1d8d 100644 --- a/src/cmd/dist/util.go +++ b/src/cmd/dist/util.go @@ -72,7 +72,7 @@ func run(dir string, mode int, cmd ...string) string { } xcmd := exec.Command(cmd[0], cmd[1:]...) - xcmd.Dir = dir + setDir(xcmd, dir) var data []byte var err error diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go index fddd1147e6..f2c276008d 100644 --- a/src/crypto/rand/rand.go +++ b/src/crypto/rand/rand.go @@ -23,3 +23,21 @@ var Reader io.Reader func Read(b []byte) (n int, err error) { return io.ReadFull(Reader, b) } + +// batched returns a function that calls f to populate a []byte by chunking it +// into subslices of, at most, readMax bytes. +func batched(f func([]byte) error, readMax int) func([]byte) error { + return func(out []byte) error { + for len(out) > 0 { + read := len(out) + if read > readMax { + read = readMax + } + if err := f(out[:read]); err != nil { + return err + } + out = out[read:] + } + return nil + } +} diff --git a/src/crypto/rand/rand_batched.go b/src/crypto/rand/rand_batched.go index d7c5bf3562..8df715fdd1 100644 --- a/src/crypto/rand/rand_batched.go +++ b/src/crypto/rand/rand_batched.go @@ -8,6 +8,7 @@ package rand import ( + "errors" "internal/syscall/unix" ) @@ -16,20 +17,6 @@ func init() { altGetRandom = batched(getRandomBatch, maxGetRandomRead) } -// batched returns a function that calls f to populate a []byte by chunking it -// into subslices of, at most, readMax bytes. -func batched(f func([]byte) bool, readMax int) func([]byte) bool { - return func(buf []byte) bool { - for len(buf) > readMax { - if !f(buf[:readMax]) { - return false - } - buf = buf[readMax:] - } - return len(buf) == 0 || f(buf) - } -} - // If the kernel is too old to support the getrandom syscall(), // unix.GetRandom will immediately return ENOSYS and we will then fall back to // reading from /dev/urandom in rand_unix.go. unix.GetRandom caches the ENOSYS @@ -37,7 +24,10 @@ func batched(f func([]byte) bool, readMax int) func([]byte) bool { // If the kernel supports the getrandom() syscall, unix.GetRandom will block // until the kernel has sufficient randomness (as we don't use GRND_NONBLOCK). // In this case, unix.GetRandom will not return an error. -func getRandomBatch(p []byte) (ok bool) { +func getRandomBatch(p []byte) error { n, err := unix.GetRandom(p, 0) - return n == len(p) && err == nil + if n != len(p) { + return errors.New("short read") + } + return err } diff --git a/src/crypto/rand/rand_batched_test.go b/src/crypto/rand/rand_batched_test.go index 2d20922c82..b56345e50f 100644 --- a/src/crypto/rand/rand_batched_test.go +++ b/src/crypto/rand/rand_batched_test.go @@ -9,20 +9,21 @@ package rand import ( "bytes" + "errors" "testing" ) func TestBatched(t *testing.T) { - fillBatched := batched(func(p []byte) bool { + fillBatched := batched(func(p []byte) error { for i := range p { p[i] = byte(i) } - return true + return nil }, 5) p := make([]byte, 13) - if !fillBatched(p) { - t.Fatal("batched function returned false") + if err := fillBatched(p); err != nil { + t.Fatalf("batched function returned error: %s", err) } expected := []byte{0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2} if !bytes.Equal(expected, p) { @@ -31,15 +32,15 @@ func TestBatched(t *testing.T) { } func TestBatchedError(t *testing.T) { - b := batched(func(p []byte) bool { return false }, 5) - if b(make([]byte, 13)) { - t.Fatal("batched function should have returned false") + b := batched(func(p []byte) error { return errors.New("") }, 5) + if b(make([]byte, 13)) == nil { + t.Fatal("batched function should have returned an error") } } func TestBatchedEmpty(t *testing.T) { - b := batched(func(p []byte) bool { return false }, 5) - if !b(make([]byte, 0)) { - t.Fatal("empty slice should always return true") + b := batched(func(p []byte) error { return errors.New("") }, 5) + if err := b(make([]byte, 0)); err != nil { + t.Fatalf("empty slice should always return nil: %s", err) } } diff --git a/src/crypto/rand/rand_getentropy.go b/src/crypto/rand/rand_getentropy.go index dd725372ad..b1c19f3d0d 100644 --- a/src/crypto/rand/rand_getentropy.go +++ b/src/crypto/rand/rand_getentropy.go @@ -15,7 +15,7 @@ func init() { altGetRandom = getEntropy } -func getEntropy(p []byte) (ok bool) { +func getEntropy(p []byte) error { // getentropy(2) returns a maximum of 256 bytes per call for i := 0; i < len(p); i += 256 { end := i + 256 @@ -24,8 +24,8 @@ func getEntropy(p []byte) (ok bool) { } err := unix.GetEntropy(p[i:end]) if err != nil { - return false + return err } } - return true + return nil } diff --git a/src/crypto/rand/rand_unix.go b/src/crypto/rand/rand_unix.go index 34f4481a9b..526e54a5c9 100644 --- a/src/crypto/rand/rand_unix.go +++ b/src/crypto/rand/rand_unix.go @@ -52,7 +52,7 @@ type devReader struct { // altGetRandom if non-nil specifies an OS-specific function to get // urandom-style randomness. -var altGetRandom func([]byte) (ok bool) +var altGetRandom func([]byte) (err error) func warnBlocked() { println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel") @@ -66,7 +66,7 @@ func (r *devReader) Read(b []byte) (n int, err error) { t := time.AfterFunc(60*time.Second, warnBlocked) defer t.Stop() } - if altGetRandom != nil && r.name == urandomDevice && altGetRandom(b) { + if altGetRandom != nil && r.name == urandomDevice && altGetRandom(b) == nil { return len(b), nil } r.mu.Lock() diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go index 7379f1489a..6c0655c72b 100644 --- a/src/crypto/rand/rand_windows.go +++ b/src/crypto/rand/rand_windows.go @@ -9,7 +9,6 @@ package rand import ( "internal/syscall/windows" - "os" ) func init() { Reader = &rngReader{} } @@ -17,16 +16,11 @@ func init() { Reader = &rngReader{} } type rngReader struct{} func (r *rngReader) Read(b []byte) (n int, err error) { - // RtlGenRandom only accepts 2**32-1 bytes at a time, so truncate. - inputLen := uint32(len(b)) - - if inputLen == 0 { - return 0, nil + // RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at + // most 1<<31-1 bytes at a time so that this works the same on 32-bit + // and 64-bit systems. + if err := batched(windows.RtlGenRandom, 1<<31-1)(b); err != nil { + return 0, err } - - err = windows.RtlGenRandom(b) - if err != nil { - return 0, os.NewSyscallError("RtlGenRandom", err) - } - return int(inputLen), nil + return len(b), nil } diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go index 969f357834..d0a2550cbc 100644 --- a/src/crypto/tls/conn.go +++ b/src/crypto/tls/conn.go @@ -32,6 +32,7 @@ type Conn struct { // handshakeStatus is 1 if the connection is currently transferring // application data (i.e. is not currently processing a handshake). + // handshakeStatus == 1 implies handshakeErr == nil. // This field is only to be accessed with sync/atomic. handshakeStatus uint32 // constant after handshake; protected by handshakeMutex @@ -1396,6 +1397,13 @@ func (c *Conn) HandshakeContext(ctx context.Context) error { } func (c *Conn) handshakeContext(ctx context.Context) (ret error) { + // Fast sync/atomic-based exit if there is no handshake in flight and the + // last one succeeded without an error. Avoids the expensive context setup + // and mutex for most Read and Write calls. + if c.handshakeComplete() { + return nil + } + handshakeCtx, cancel := context.WithCancel(ctx) // Note: defer this before starting the "interrupter" goroutine // so that we can tell the difference between the input being canceled and @@ -1454,6 +1462,9 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) { if c.handshakeErr == nil && !c.handshakeComplete() { c.handshakeErr = errors.New("tls: internal error: handshake should have had a result") } + if c.handshakeErr != nil && c.handshakeComplete() { + panic("tls: internal error: handshake returned an error but is marked successful") + } return c.handshakeErr } diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go index 109ff7a01d..1b2bf4dcd3 100644 --- a/src/crypto/tls/handshake_server_tls13.go +++ b/src/crypto/tls/handshake_server_tls13.go @@ -10,6 +10,7 @@ import ( "crypto" "crypto/hmac" "crypto/rsa" + "encoding/binary" "errors" "hash" "io" @@ -745,6 +746,19 @@ func (hs *serverHandshakeStateTLS13) sendSessionTickets() error { } m.lifetime = uint32(maxSessionTicketLifetime / time.Second) + // ticket_age_add is a random 32-bit value. See RFC 8446, section 4.6.1 + // The value is not stored anywhere; we never need to check the ticket age + // because 0-RTT is not supported. + ageAdd := make([]byte, 4) + _, err = hs.c.config.rand().Read(ageAdd) + if err != nil { + return err + } + m.ageAdd = binary.LittleEndian.Uint32(ageAdd) + + // ticket_nonce, which must be unique per connection, is always left at + // zero because we only ever send one ticket per connection. + if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil { return err } diff --git a/src/os/exec/exec.go b/src/os/exec/exec.go index 0c49575511..505de58e84 100644 --- a/src/os/exec/exec.go +++ b/src/os/exec/exec.go @@ -374,6 +374,9 @@ func lookExtensions(path, dir string) (string, error) { // The Wait method will return the exit code and release associated resources // once the command exits. func (c *Cmd) Start() error { + if c.Path == "" && c.lookPathErr == nil { + c.lookPathErr = errors.New("exec: no command") + } if c.lookPathErr != nil { c.closeDescriptors(c.closeAfterStart) c.closeDescriptors(c.closeAfterWait) diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go index d854e0de84..a951be718d 100644 --- a/src/os/exec/exec_test.go +++ b/src/os/exec/exec_test.go @@ -1156,3 +1156,11 @@ func TestChildCriticalEnv(t *testing.T) { t.Error("no SYSTEMROOT found") } } + +func TestNoPath(t *testing.T) { + err := new(exec.Cmd).Start() + want := "exec: no command" + if err == nil || err.Error() != want { + t.Errorf("new(Cmd).Start() = %v, want %q", err, want) + } +} diff --git a/src/path/filepath/path.go b/src/path/filepath/path.go index b56534dead..8300a32cb1 100644 --- a/src/path/filepath/path.go +++ b/src/path/filepath/path.go @@ -117,9 +117,21 @@ func Clean(path string) string { case os.IsPathSeparator(path[r]): // empty path element r++ - case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])): + case path[r] == '.' && r+1 == n: // . element r++ + case path[r] == '.' && os.IsPathSeparator(path[r+1]): + // ./ element + r++ + + for r < len(path) && os.IsPathSeparator(path[r]) { + r++ + } + if out.w == 0 && volumeNameLen(path[r:]) > 0 { + // When joining prefix "." and an absolute path on Windows, + // the prefix should not be removed. + out.append('.') + } case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])): // .. element: remove to last separator r += 2 diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go index bc5509b49c..ed17a8854d 100644 --- a/src/path/filepath/path_test.go +++ b/src/path/filepath/path_test.go @@ -93,6 +93,9 @@ var wincleantests = []PathTest{ {`//host/share/foo/../baz`, `\\host\share\baz`}, {`\\a\b\..\c`, `\\a\b\c`}, {`\\a\b`, `\\a\b`}, + {`.\c:`, `.\c:`}, + {`.\c:\foo`, `.\c:\foo`}, + {`.\c:foo`, `.\c:foo`}, } func TestClean(t *testing.T) { diff --git a/src/path/filepath/path_windows_test.go b/src/path/filepath/path_windows_test.go index 76a459ac96..3edafb5a85 100644 --- a/src/path/filepath/path_windows_test.go +++ b/src/path/filepath/path_windows_test.go @@ -530,3 +530,29 @@ func TestNTNamespaceSymlink(t *testing.T) { t.Errorf(`EvalSymlinks(%q): got %q, want %q`, filelink, got, want) } } + +func TestIssue52476(t *testing.T) { + tests := []struct { + lhs, rhs string + want string + }{ + {`..\.`, `C:`, `..\C:`}, + {`..`, `C:`, `..\C:`}, + {`.`, `:`, `:`}, + {`.`, `C:`, `.\C:`}, + {`.`, `C:/a/b/../c`, `.\C:\a\c`}, + {`.`, `\C:`, `.\C:`}, + {`C:\`, `.`, `C:\`}, + {`C:\`, `C:\`, `C:\C:`}, + {`C`, `:`, `C\:`}, + {`\.`, `C:`, `\C:`}, + {`\`, `C:`, `\C:`}, + } + + for _, test := range tests { + got := filepath.Join(test.lhs, test.rhs) + if got != test.want { + t.Errorf(`Join(%q, %q): got %q, want %q`, test.lhs, test.rhs, got, test.want) + } + } +} diff --git a/src/runtime/gc_test.go b/src/runtime/gc_test.go index 5e7c6c574f..0ec5331534 100644 --- a/src/runtime/gc_test.go +++ b/src/runtime/gc_test.go @@ -21,6 +21,7 @@ import ( ) func TestGcSys(t *testing.T) { + t.Skip("skipping known-flaky test; golang.org/issue/37331") if os.Getenv("GOGC") == "off" { t.Skip("skipping test; GOGC=off in environment") } diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go index 8c76a9123c..097d1b5c6a 100644 --- a/src/runtime/runtime-gdb_test.go +++ b/src/runtime/runtime-gdb_test.go @@ -427,6 +427,9 @@ func TestGdbBacktrace(t *testing.T) { got, err := exec.Command("gdb", args...).CombinedOutput() t.Logf("gdb output:\n%s", got) if err != nil { + if bytes.Contains(got, []byte("internal-error: wait returned unexpected status 0x0")) { + testenv.SkipFlaky(t, 43068) + } t.Fatalf("gdb exited with error: %v", err) }