Files
pspy/internal/fswatcher/fswatcher_test.go
2018-03-12 08:58:01 +01:00

219 lines
4.6 KiB
Go

package fswatcher
import (
"errors"
"fmt"
"reflect"
"strings"
"testing"
"time"
"github.com/dominicbreuker/pspy/internal/fswatcher/inotify"
)
func initObjs() (*MockInotify, *MockWalker, *FSWatcher) {
i := NewMockInotify()
w := &MockWalker{
subdirs: map[string][]string{
"mydir1": []string{"dir1", "dir2"},
"mydir2": []string{"dir3"},
"dir1": []string{"another-dir"},
},
}
fs := &FSWatcher{
i: i,
w: w,
maxWatchers: 999,
eventSize: 11,
}
return i, w, fs
}
func TestInit(t *testing.T) {
i, _, fs := initObjs()
rdirs := []string{"mydir1"}
dirs := []string{"mydir2"}
errCh, doneCh := fs.Init(rdirs, dirs)
loop:
for {
select {
case <-doneCh:
break loop
case err := <-errCh:
t.Errorf("Unexpected error: %v", err)
case <-time.After(1 * time.Second):
t.Fatalf("Test timeout")
}
}
if !reflect.DeepEqual(i.watching, []string{"mydir1", "dir1", "another-dir", "dir2", "mydir2"}) {
t.Fatalf("Watching wrong directories: %+v", i.watching)
}
}
func TestRun(t *testing.T) {
i, _, fs := initObjs()
triggerCh, eventCh, errCh := fs.Run()
// send data (len=11)
go func() {
sendInotifyData(t, i.bufReads, "name:type__") // single event
sendInotifyData(t, i.bufReads, "error:read_") // read error
sendInotifyData(t, i.bufReads, "error:parse") // parse error
sendInotifyData(t, i.bufReads, "name1:type1name2:type2") // 2 events
}()
// parse first datum
expectTrigger(t, triggerCh)
expectEvent(t, eventCh, "type__ | name")
// parse second datum
expectTrigger(t, triggerCh)
expectError(t, errCh, "reading inotify buffer: error-inotify-read")
// parse third datum
expectTrigger(t, triggerCh)
expectError(t, errCh, "parsing events: parse-event-error")
// parse fourth datum
expectTrigger(t, triggerCh)
expectEvent(t, eventCh, "type1 | name1")
expectEvent(t, eventCh, "type2 | name2")
}
const timeout = 500 * time.Millisecond
func sendInotifyData(t *testing.T, dataCh chan []byte, s string) {
select {
case dataCh <- []byte(s):
case <-time.After(timeout):
t.Fatalf("Could not send data in time: %s", s)
}
}
func expectTrigger(t *testing.T, triggerCh chan struct{}) {
select {
case <-triggerCh:
case <-time.After(timeout):
t.Fatalf("Timeout: did not receive trigger in time")
}
}
func expectEvent(t *testing.T, eventCh chan string, exp string) {
select {
case e := <-eventCh:
if strings.TrimSpace(e) != exp {
t.Errorf("Wrong event: %+v", e)
}
case <-time.After(timeout):
t.Fatalf("Timeout: did not receive event in time")
}
}
func expectError(t *testing.T, errCh chan error, exp string) {
select {
case err := <-errCh:
if err.Error() != exp {
t.Errorf("Wrong error: %v", err)
}
case <-time.After(timeout):
t.Fatalf("Timeout: did not receive error in time")
}
}
// mocks
// Mock Inotify
type MockInotify struct {
initialized bool
watching []string
bufReads chan []byte
}
func NewMockInotify() *MockInotify {
return &MockInotify{
initialized: false,
watching: make([]string, 0),
bufReads: make(chan []byte),
}
}
func (i *MockInotify) Init() error {
i.initialized = true
return nil
}
func (i *MockInotify) Watch(dir string) error {
if !i.initialized {
return errors.New("Not yet initialized")
}
i.watching = append(i.watching, dir)
return nil
}
func (i *MockInotify) NumWatchers() int {
return len(i.watching)
}
func (i *MockInotify) Read(buf []byte) (int, error) {
b := <-i.bufReads
t := strings.Split(string(b), ":")
if t[0] == "error" && t[1] == "read_" {
return -1, fmt.Errorf("error-inotify-read")
}
copy(buf, b)
return len(b), nil
}
func (i *MockInotify) ParseNextEvent(buf []byte) (*inotify.Event, uint32, error) {
s := string(buf[:11])
t := strings.Split(s, ":")
if t[0] == "error" && t[1] == "parse" {
return nil, uint32(len(buf)), fmt.Errorf("parse-event-error")
}
return &inotify.Event{Name: t[0], Op: t[1]}, 11, nil
}
func (i *MockInotify) Close() error {
if !i.initialized {
return errors.New("Not yet initialized")
}
return nil
}
// Mock Walker
type MockWalker struct {
subdirs map[string][]string
}
func (w *MockWalker) Walk(dir string, depth int) (chan string, chan error, chan struct{}) {
dirCh := make(chan string)
errCh := make(chan error)
doneCh := make(chan struct{})
go func() {
defer close(dirCh)
sendDir(w, depth, dir, dirCh)
}()
return dirCh, errCh, doneCh
}
func sendDir(w *MockWalker, depth int, dir string, dirCh chan string) {
dirCh <- dir
if depth == 0 {
return
}
subdirs, ok := w.subdirs[dir]
if !ok {
return
}
for _, sdir := range subdirs {
sendDir(w, depth-1, sdir, dirCh)
}
}