Example #1
0
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")
	}
}
Example #2
0
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)
	}
}
Example #3
0
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)
}
Example #4
0
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)
}
Example #5
0
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
}