os: make TestProgWideChdir detect more possible failure cases

This test is meant to detect the effect of Chdir not being
observed in other concurrent goroutines, possible in Plan 9
because each M runs in a separate OS process with its own
working directory. The test depends on Getwd to report the
correct working directory, but if Chdir fails then Getwd
may fail for the same reasons. We add a consistency check
that Stat(Getwd()) and Stat(".") refer to the same file.

Also change channel usage and add a sync.WaitGroup to
ensure test goroutines are not left blocked or running
when the main test function exits.

For #58802

Change-Id: I80d554fcf3617427c28bbe16e5e396367dcfe673
Reviewed-on: https://go-review.googlesource.com/c/go/+/472555
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: David du Colombier <0intro@gmail.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
miller 2023-03-01 16:55:45 +00:00 committed by Gopher Robot
parent e0db8ab6e0
commit 392fbaa0f3
1 changed files with 53 additions and 38 deletions

View File

@ -1696,11 +1696,35 @@ func TestChdirAndGetwd(t *testing.T) {
// Test that Chdir+Getwd is program-wide.
func TestProgWideChdir(t *testing.T) {
const N = 10
const ErrPwd = "Error!"
c := make(chan bool)
cpwd := make(chan string, N)
var wg sync.WaitGroup
hold := make(chan struct{})
done := make(chan struct{})
d := t.TempDir()
oldwd, err := Getwd()
if err != nil {
t.Fatalf("Getwd: %v", err)
}
defer func() {
if err := Chdir(oldwd); err != nil {
// It's not safe to continue with tests if we can't get back to
// the original working directory.
panic(err)
}
}()
// Note the deferred Wait must be called after the deferred close(done),
// to ensure the N goroutines have been released even if the main goroutine
// calls Fatalf. It must be called before the Chdir back to the original
// directory, and before the deferred deletion implied by TempDir,
// so as not to interfere while the N goroutines are still running.
defer wg.Wait()
defer close(done)
for i := 0; i < N; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
// Lock half the goroutines in their own operating system
// thread to exercise more scheduler possibilities.
if i%2 == 1 {
@ -1711,57 +1735,48 @@ func TestProgWideChdir(t *testing.T) {
// See issue 9428.
runtime.LockOSThread()
}
hasErr, closed := <-c
if !closed && hasErr {
cpwd <- ErrPwd
select {
case <-done:
return
case <-hold:
}
// Getwd might be wrong
f0, err := Stat(".")
if err != nil {
t.Error(err)
return
}
pwd, err := Getwd()
if err != nil {
t.Errorf("Getwd on goroutine %d: %v", i, err)
cpwd <- ErrPwd
t.Errorf("Getwd: %v", err)
return
}
if pwd != d {
t.Errorf("Getwd() = %q, want %q", pwd, d)
return
}
f1, err := Stat(pwd)
if err != nil {
t.Error(err)
return
}
if !SameFile(f0, f1) {
t.Errorf(`Samefile(Stat("."), Getwd()) reports false (%s != %s)`, f0.Name(), f1.Name())
return
}
cpwd <- pwd
}(i)
}
oldwd, err := Getwd()
if err != nil {
c <- true
t.Fatalf("Getwd: %v", err)
}
d, err := MkdirTemp("", "test")
if err != nil {
c <- true
t.Fatalf("TempDir: %v", err)
}
defer func() {
if err := Chdir(oldwd); err != nil {
t.Fatalf("Chdir: %v", err)
}
RemoveAll(d)
}()
if err := Chdir(d); err != nil {
c <- true
if err = Chdir(d); err != nil {
t.Fatalf("Chdir: %v", err)
}
// OS X sets TMPDIR to a symbolic link.
// So we resolve our working directory again before the test.
d, err = Getwd()
if err != nil {
c <- true
t.Fatalf("Getwd: %v", err)
}
close(c)
for i := 0; i < N; i++ {
pwd := <-cpwd
if pwd == ErrPwd {
t.FailNow()
}
if pwd != d {
t.Errorf("Getwd returned %q; want %q", pwd, d)
}
}
close(hold)
wg.Wait()
}
func TestSeek(t *testing.T) {