Skip to content

Commit

Permalink
Add some tests for watch events
Browse files Browse the repository at this point in the history
  • Loading branch information
dnephin committed May 8, 2022
1 parent 14592d7 commit 57ba68e
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 8 deletions.
5 changes: 4 additions & 1 deletion cmd/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ import (
)

func runWatcher(opts *options) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

w := &watchRuns{opts: *opts}
return filewatcher.Watch(opts.packages, w.run)
return filewatcher.Watch(ctx, opts.packages, w.run)
}

type watchRuns struct {
Expand Down
5 changes: 4 additions & 1 deletion internal/filewatcher/term_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"bufio"
"context"
"fmt"
"io"
"os"

"golang.org/x/sys/unix"
Expand Down Expand Up @@ -62,13 +63,15 @@ func enableNonBlockingRead(fd int) (func(), error) {
return reset, nil
}

var stdin io.Reader = os.Stdin

// Monitor the terminal for key presses. If the key press is associated with an
// action, an event will be sent to channel returned by Events.
func (r *terminal) Monitor(ctx context.Context) {
if r == nil {
return
}
in := bufio.NewReader(os.Stdin)
in := bufio.NewReader(stdin)
for {
char, err := in.ReadByte()
if err != nil {
Expand Down
9 changes: 4 additions & 5 deletions internal/filewatcher/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ type Event struct {

// Watch dirs for filesystem events, and run tests when .go files are saved.
// nolint: gocyclo
func Watch(dirs []string, run func(Event) error) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

func Watch(ctx context.Context, dirs []string, run func(Event) error) error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return fmt.Errorf("failed to create file watcher: %w", err)
Expand All @@ -61,6 +58,8 @@ func Watch(dirs []string, run func(Event) error) error {
h := &fsEventHandler{last: time.Now(), fn: run}
for {
select {
case <-ctx.Done():
return nil
case <-timer.C:
return fmt.Errorf("exceeded idle timeout while watching files")

Expand Down Expand Up @@ -234,7 +233,7 @@ type fsEventHandler struct {
fn func(opts Event) error
}

const floodThreshold = 250 * time.Millisecond
var floodThreshold = 250 * time.Millisecond

func (h *fsEventHandler) handleEvent(event fsnotify.Event) error {
if event.Op&(fsnotify.Write|fsnotify.Create) == 0 {
Expand Down
2 changes: 1 addition & 1 deletion internal/filewatcher/watch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"gotest.tools/v3/fs"
)

func TestHandler_HandleEvent(t *testing.T) {
func TestFSEventHandler_HandleEvent(t *testing.T) {
type testCase struct {
name string
last time.Time
Expand Down
110 changes: 110 additions & 0 deletions internal/filewatcher/watch_unix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//go:build !windows && !aix
// +build !windows,!aix

package filewatcher

import (
"context"
"io"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"gotest.tools/v3/assert"
"gotest.tools/v3/fs"
)

func TestWatch(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
dir := fs.NewDir(t, t.Name())

r, w := io.Pipe()
patchStdin(t, r)
patchFloodThreshold(t, 0)

chEvents := make(chan Event, 1)
capture := func(event Event) error {
chEvents <- event
return nil
}

go func() {
err := Watch(ctx, []string{dir.Path()}, capture)
assert.Check(t, err)
}()

t.Run("run all tests", func(t *testing.T) {
_, err := w.Write([]byte("a"))
assert.NilError(t, err)

event := <-chEvents
expected := Event{PkgPath: "./..."}
assert.DeepEqual(t, event, expected, cmpEvent)
})

t.Run("run tests on file change", func(t *testing.T) {
fs.Apply(t, dir, fs.WithFile("file.go", ""))

event := <-chEvents
expected := Event{PkgPath: "./" + dir.Path()}
assert.DeepEqual(t, event, expected, cmpEvent)

t.Run("and rerun", func(t *testing.T) {
_, err := w.Write([]byte("r"))
assert.NilError(t, err)

event := <-chEvents
expected := Event{PkgPath: "./" + dir.Path(), useLastPath: true}
assert.DeepEqual(t, event, expected, cmpEvent)
})

t.Run("and debug", func(t *testing.T) {
_, err := w.Write([]byte("d"))
assert.NilError(t, err)

event := <-chEvents
expected := Event{
PkgPath: "./" + dir.Path(),
useLastPath: true,
Debug: true,
}
assert.DeepEqual(t, event, expected, cmpEvent)
})

t.Run("and update", func(t *testing.T) {
_, err := w.Write([]byte("u"))
assert.NilError(t, err)

event := <-chEvents
expected := Event{
PkgPath: "./" + dir.Path(),
Args: []string{"-update"},
useLastPath: true,
}
assert.DeepEqual(t, event, expected, cmpEvent)
})
})
}

var cmpEvent = cmp.Options{
cmp.AllowUnexported(Event{}),
cmpopts.IgnoreTypes(make(chan struct{})),
}

func patchStdin(t *testing.T, in io.Reader) {
orig := stdin
stdin = in
t.Cleanup(func() {
stdin = orig
})
}

func patchFloodThreshold(t *testing.T, d time.Duration) {
orig := floodThreshold
floodThreshold = d
t.Cleanup(func() {
floodThreshold = orig
})
}

0 comments on commit 57ba68e

Please sign in to comment.