]> Cypherpunks repositories - gostls13.git/commitdiff
os: make TestProgWideChdir detect more possible failure cases
authormiller <millerresearch@gmail.com>
Wed, 1 Mar 2023 16:55:45 +0000 (16:55 +0000)
committerGopher Robot <gobot@golang.org>
Wed, 24 May 2023 00:38:42 +0000 (00:38 +0000)
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>

src/os/os_test.go

index 0a6be3516a383dfc6b74f02d9aff391b47520928..ad30756cbd30f41503c2c1246a0b05727901067e 100644 (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) {