]> go.fuhry.dev Git - fsnotify.git/commitdiff
deflake inotify stress test (#177)
authorPatrick <patrick@dropbox.com>
Wed, 5 Oct 2016 03:39:39 +0000 (20:39 -0700)
committerNathan Youngman <git@nathany.com>
Wed, 5 Oct 2016 03:39:39 +0000 (21:39 -0600)
* deflake inotify stress test

* tab -> space

* add sleep between creation / deletion to ensure creation events are triggered.

inotify_test.go

index 2527cad1fe0e37883addc49af4421e6d05d4168c..a4bb202d1f2484cae10f115a5089b8bd01c6486e 100644 (file)
@@ -10,10 +10,9 @@ import (
        "fmt"
        "os"
        "path/filepath"
+       "strings"
        "testing"
        "time"
-
-       "golang.org/x/sys/unix"
 )
 
 func TestInotifyCloseRightAway(t *testing.T) {
@@ -154,10 +153,14 @@ func TestInotifyCloseCreate(t *testing.T) {
        }
 }
 
+// This test verifies the watcher can keep up with file creations/deletions
+// when under load.
 func TestInotifyStress(t *testing.T) {
+       maxNumToCreate := 1000
+
        testDir := tempMkdir(t)
        defer os.RemoveAll(testDir)
-       testFile := filepath.Join(testDir, "testfile")
+       testFilePrefix := filepath.Join(testDir, "testfile")
 
        w, err := NewWatcher()
        if err != nil {
@@ -165,84 +168,85 @@ func TestInotifyStress(t *testing.T) {
        }
        defer w.Close()
 
-       killchan := make(chan struct{})
-       defer close(killchan)
-
        err = w.Add(testDir)
        if err != nil {
                t.Fatalf("Failed to add testDir: %v", err)
        }
 
-       proc, err := os.FindProcess(os.Getpid())
-       if err != nil {
-               t.Fatalf("Error finding process: %v", err)
-       }
+       doneChan := make(chan struct{})
+       // The buffer ensures that the file generation goroutine is never blocked.
+       errChan := make(chan error, 2*maxNumToCreate)
 
        go func() {
-               for {
-                       select {
-                       case <-time.After(5 * time.Millisecond):
-                               err := proc.Signal(unix.SIGUSR1)
-                               if err != nil {
-                                       t.Fatalf("Signal failed: %v", err)
-                               }
-                       case <-killchan:
-                               return
+               for i := 0; i < maxNumToCreate; i++ {
+                       testFile := fmt.Sprintf("%s%d", testFilePrefix, i)
+
+                       handle, err := os.Create(testFile)
+                       if err != nil {
+                               errChan <- fmt.Errorf("Create failed: %v", err)
+                               continue
                        }
-               }
-       }()
 
-       go func() {
-               for {
-                       select {
-                       case <-time.After(11 * time.Millisecond):
-                               err := w.poller.wake()
-                               if err != nil {
-                                       t.Fatalf("Wake failed: %v", err)
-                               }
-                       case <-killchan:
-                               return
+                       err = handle.Close()
+                       if err != nil {
+                               errChan <- fmt.Errorf("Close failed: %v", err)
+                               continue
                        }
                }
-       }()
 
-       go func() {
-               for {
-                       select {
-                       case <-killchan:
-                               return
-                       default:
-                               handle, err := os.Create(testFile)
-                               if err != nil {
-                                       t.Fatalf("Create failed: %v", err)
-                               }
-                               handle.Close()
-                               time.Sleep(time.Millisecond)
-                               err = os.Remove(testFile)
-                               if err != nil {
-                                       t.Fatalf("Remove failed: %v", err)
-                               }
+               // If we delete a newly created file too quickly, inotify will skip the
+               // create event and only send the delete event.
+               time.Sleep(100 * time.Millisecond)
+
+               for i := 0; i < maxNumToCreate; i++ {
+                       testFile := fmt.Sprintf("%s%d", testFilePrefix, i)
+                       err = os.Remove(testFile)
+                       if err != nil {
+                               errChan <- fmt.Errorf("Remove failed: %v", err)
                        }
                }
+
+               close(doneChan)
        }()
 
        creates := 0
        removes := 0
-       after := time.After(5 * time.Second)
-       for {
+
+       finished := false
+       after := time.After(10 * time.Second)
+       for !finished {
                select {
                case <-after:
-                       if creates-removes > 1 || creates-removes < -1 {
-                               t.Fatalf("Creates and removes should not be off by more than one: %d creates, %d removes", creates, removes)
+                       t.Fatalf("Not done")
+               case <-doneChan:
+                       finished = true
+               case err := <-errChan:
+                       t.Fatalf("Got an error from file creator goroutine: %v", err)
+               case err := <-w.Errors:
+                       t.Fatalf("Got an error from watcher: %v", err)
+               case evt := <-w.Events:
+                       if !strings.HasPrefix(evt.Name, testFilePrefix) {
+                               t.Fatalf("Got an event for an unknown file: %s", evt.Name)
+                       }
+                       if evt.Op == Create {
+                               creates++
                        }
-                       if creates < 50 {
-                               t.Fatalf("Expected at least 50 creates, got %d", creates)
+                       if evt.Op == Remove {
+                               removes++
                        }
-                       return
+               }
+       }
+
+       // Drain remaining events from channels
+       count := 0
+       for count < 10 {
+               select {
+               case err := <-errChan:
+                       t.Fatalf("Got an error from file creator goroutine: %v", err)
                case err := <-w.Errors:
                        t.Fatalf("Got an error from watcher: %v", err)
                case evt := <-w.Events:
-                       if evt.Name != testFile {
+                       if !strings.HasPrefix(evt.Name, testFilePrefix) {
                                t.Fatalf("Got an event for an unknown file: %s", evt.Name)
                        }
                        if evt.Op == Create {
@@ -251,8 +255,20 @@ func TestInotifyStress(t *testing.T) {
                        if evt.Op == Remove {
                                removes++
                        }
+                       count = 0
+               default:
+                       count++
+                       // Give the watcher chances to fill the channels.
+                       time.Sleep(time.Millisecond)
                }
        }
+
+       if creates-removes > 1 || creates-removes < -1 {
+               t.Fatalf("Creates and removes should not be off by more than one: %d creates, %d removes", creates, removes)
+       }
+       if creates < 50 {
+               t.Fatalf("Expected at least 50 creates, got %d", creates)
+       }
 }
 
 func TestInotifyRemoveTwice(t *testing.T) {