mirror of
https://github.com/DominicBreuker/pspy.git
synced 2025-12-21 11:44:51 +00:00
219 lines
4.6 KiB
Go
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)
|
|
}
|
|
}
|