func TestWatchModifyFile(t *testing.T) { path, err := ioutil.TempDir("", "redgreen") if err != nil { t.Fatalf("create temp dir: %v", err) } defer os.RemoveAll(path) f, err := os.Create(filepath.Join(path, "foo")) if err != nil { t.Fatalf("create temp file: %v", err) } done := make(chan struct{}) defer close(done) out, err := redgreen.Watch(done, path, 0) if err != nil { t.Fatalf("Watch(done, %q, 0) = %v, want nil", path, err) } // Modifying a file should trigger a watch event. f.WriteString("test") err = f.Sync() if err != nil { t.Fatalf("sync temp file: %v", err) } select { case <-out: case <-time.After(time.Second): t.Fatalf("timed out waiting for watch event") } }
func TestWatchBadPath(t *testing.T) { done := make(chan struct{}) path := "does/not/exist" _, err := redgreen.Watch(done, path, 0) if err == nil { t.Fatalf("Watch(done, %q, 0) = %v, want not nil", path, err) } }
func TestWatchDoneBlockedOnOut(t *testing.T) { t.Skip("bad test, fails on Go 1.6") var outWasOpen, outWasClosed bool test := func() { path, err := ioutil.TempDir("", "redgreen") if err != nil { t.Fatalf("create temp dir: %v", err) } defer os.RemoveAll(path) done := make(chan struct{}) out, err := redgreen.Watch(done, path, 0) if err != nil { t.Fatalf("Watch(done, %q, 0) = %v, want nil", path, err) } // Create a file to trigger a watch event, but never receive from out, // so that Watch's goroutine is blocked on the send operation. _, err = os.Create(filepath.Join(path, "foo")) if err != nil { t.Fatalf("create temp file: %v", err) } // Signal that we are done. close(done) // Ensure that out is eventually closed. timeout := time.Second select { case _, isOpen := <-out: // Since we receive from out here, we can observe that // the receive operation either succeed or not with // equal probability. if isOpen { outWasOpen = true } else { outWasClosed = true } case <-time.After(timeout): t.Fatalf("receive from channel timed out") } mustBeClosedTimeoutESC(out, timeout, t) } var runs int for !(outWasOpen && outWasClosed) { // 2 or 3 runs should be enough. The probability of needing more // than 16 runs to observe both outWasOpen and outWasClosed is // (1/2)^16 ≈ 0.001 if runs > 16 { t.Fatalf("failed to observe both conditions: outWasOpen=%v, outWasClosed=%v", outWasOpen, outWasClosed) } test() runs++ } t.Logf("#runs: %d", runs) }
func TestWatchDoneBlockedOnIn(t *testing.T) { path, err := ioutil.TempDir("", "redgreen") if err != nil { t.Fatalf("create temp dir: %v", err) } defer os.RemoveAll(path) done := make(chan struct{}) out, err := redgreen.Watch(done, path, 0) if err != nil { t.Fatalf("Watch(done, %q, 0) = %v, want nil", path, err) } // No file system events should have been triggered, Watch's goroutine // is blocked on receiving events. // Signal that we are done. close(done) // Ensure that out is eventually closed. mustBeClosedTimeoutESC(out, time.Second, t) }
func do() error { // Initialize and defer termination of termbox. if !debug { if err := termbox.Init(); err != nil { return err } defer termbox.Close() termbox.HideCursor() termbox.SetOutputMode(termbox.Output256) } // wg waits for all goroutines started by this function to return. var wg sync.WaitGroup defer wg.Wait() // Closing done signals all goroutines to terminate. done := make(chan struct{}) defer close(done) w, err := redgreen.Watch(done, ".", 200*time.Millisecond) if err != nil { return err } runSpec := redgreen.RunSpec{Command: testCommand, Timeout: timeout} run := make(chan redgreen.RunSpec, 1) res := redgreen.Run(done, run) // Trigger an initial run of the test command. run <- runSpec // Run tests every time a file is created/removed/modified. wg.Add(1) go func() { defer wg.Done() for range w { run <- runSpec } }() state := make(chan redgreen.State) wg.Add(1) go func() { defer wg.Done() redgreen.Render(done, state) }() s := redgreen.State{Debug: debug} var mu sync.RWMutex // synchronizes access to s. // Render initial state. state <- s // Render after every test command result. wg.Add(1) go func() { defer wg.Done() for r := range res { mu.Lock() s.Results = append(s.Results, r) mu.Unlock() mu.RLock() state <- s mu.RUnlock() } }() if debug { // Wait for Ctrl-C. ch := make(chan os.Signal, 1) signal.Notify(ch, os.Interrupt) <-ch } else { // Block until Esc is pressed. for { e := termbox.PollEvent() if e.Type == termbox.EventKey && e.Key == termbox.KeyEsc { break } if e.Type == termbox.EventResize { mu.RLock() state <- s mu.RUnlock() } } } return nil }