Example #1
0
func TestRunClosedInput(t *testing.T) {
	done := make(chan struct{})
	in := make(chan redgreen.RunSpec)
	out := redgreen.Run(done, in)

	// Signal that there will be no more input.
	close(in)

	// Ensure that out is eventually closed.
	mustBeClosedTimeout(out, time.Second, t)
}
Example #2
0
func TestRunDoneBlockedOnOut(t *testing.T) {
	var outWasOpen, outWasClosed bool
	test := func() {
		done := make(chan struct{})
		in := make(chan redgreen.RunSpec)
		out := redgreen.Run(done, in)

		// Send a single command but don't receive from out, so that
		// Run's goroutine will be blocked on sending to out.
		in <- redgreen.RunSpec{Command: []string{"true"}}

		// 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")
		}
		mustBeClosedTimeout(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 #3
0
func TestRunDoneBlockedOnIn(t *testing.T) {
	done := make(chan struct{})
	in := make(chan redgreen.RunSpec)
	out := redgreen.Run(done, in)

	// Test the execution of a single command.
	in <- redgreen.RunSpec{Command: []string{"true"}}
	if res := <-out; res.Error != nil {
		t.Fatalf("got %#v, want nil", res.Error)
	}

	// There is no more input, Run's goroutine should be blocked on
	// receiving from in.

	// Signal that we are done.
	close(done)

	// Ensure that out is eventually closed.
	mustBeClosedTimeout(out, time.Second, t)
}
Example #4
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
}