Skip to content

Commit

Permalink
Add test cases (#64)
Browse files Browse the repository at this point in the history
  • Loading branch information
emanuel-skrenkovic authored May 7, 2024
1 parent ce9aad4 commit 1e71d45
Show file tree
Hide file tree
Showing 44 changed files with 1,435 additions and 0 deletions.
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,53 @@ get merged *if* there's a decent signal they're correct. Merely "it compiles" it
not enough, because so much of this relies on runtime behaviour. Either a new
test should be added, or patches should be tested manually.

### Test Scripts
Support for test scripts akin to ones in [fsnotify](https://github.com/fsnotify/fsnotify)
have been added.

All the scripts are in the `testdata` folder, and test can be ran individually
using the pattern:

```go test -run TestScript/<path to test script file>```

Scripts are written using a simple DSL which runs a few available shell commands,
and also allows setting/unsetting the watcher, as well as verifying the output.

Available commands:
```
watch path [ops] # Watch the path, reporting events for it. Nothing is
# watched by default.
unwatch path # Stop watching the path.
watchlist n # Assert watchlist length.
stop # Stop running the script; for debugging.
touch path
mkdir [-p] dir
ln -s target link # Only ln -s supported.
mv src dst
rm [-r] path
chmod mode path # Octal only
sleep time-in-ms
cat path # Read path (does nothing with the data; just reads it).
echo str >>path # Append "str" to "path".
echo str >path # Truncate "path" and write "str".
```

Output can be verified in the scripts using the events and event flags emitted
by FSEvents. Assertions are defined in the `Output` section of the test script.
All the flags in test script assertions are equivalent to the ones defined in
`wrap.go` with the type EventFlags.

The output section format:
```
Output:
# Comment
EventFlag1|EventFlag2 path # Comment
```


Really quick FSEvents overview
==============================
For those new to FSEvents itself (the Apple technology), here's a really quick
Expand Down
162 changes: 162 additions & 0 deletions fsevents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,44 @@ package fsevents

import (
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"time"
)

func TestScript(t *testing.T) {
err := filepath.Walk("./testdata", func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}

if info.IsDir() {
return nil
}

n := strings.Split(filepath.ToSlash(path), "/")
t.Run(strings.Join(n[1:], "/"), func(t *testing.T) {
t.Parallel()

d, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}

parseScript(t, string(d))
})

return nil
})
if err != nil {
t.Fatal(err)
}
}

func TestBasicExample(t *testing.T) {
path, err := os.MkdirTemp("", "fsexample")
if err != nil {
Expand Down Expand Up @@ -169,3 +201,133 @@ func TestRegistry(t *testing.T) {
t.Error("failed to delete registry")
}
}

func TestMany(t *testing.T) {
tmp := t.TempDir()

path, err := filepath.EvalSymlinks(tmp)
if err != nil {
t.Fatal(err)
}

dev, err := DeviceForPath(path)
if err != nil {
t.Fatal(err)
}

es := &EventStream{
Paths: []string{path},
Latency: 0,
Device: dev,
Flags: FileEvents | NoDefer,
}

err = es.Start()
if err != nil {
t.Fatal(err)
}

events := make(map[string]EventFlags, 810)

wait := make(chan struct{})

go func() {
for {
select {
case msg := <-es.Events:
for _, event := range msg {
if _, ok := events[event.Path]; !ok {
events[event.Path] = event.Flags
} else {
events[event.Path] = events[event.Path].set(event.Flags)
}
}
case <-time.After(3 * time.Second):
wait <- struct{}{}
}
}
}()

const data = "data"

var wg sync.WaitGroup
wg.Add(200)

for i := 0; i < 100; i++ {
i := i

go func() {
defer wg.Done()
echoAppend(t, data, fmt.Sprintf("%s/file-%d", path, i))
}()

go func() {
defer wg.Done()
mkdir(t, fmt.Sprintf("%s/dir-%d", path, i))
}()
}
wg.Wait()

wg.Add(100)
for i := 0; i < 100; i++ {
i := i

go func() {
defer wg.Done()
echoAppend(t, data, fmt.Sprintf("%s/dir-%d/file-%d", path, i, i))
}()
}
wg.Wait()

wg.Add(200)
for i := 0; i < 100; i++ {
i := i
go func() {
defer wg.Done()
rm(t, fmt.Sprintf("%s/file-%d", tmp, i))
}()
go func() {
defer wg.Done()
rmAll(t, fmt.Sprintf("%s/dir-%d", tmp, i))
}()
}
wg.Wait()

select {
case <-wait:
case <-time.After(5 * time.Second):
t.Fatal("timed out waiting for events")
}

const fileExpectedFlags = ItemIsFile | ItemCreated | ItemModified | ItemRemoved
const dirExpectedFlags = ItemIsDir | ItemCreated | ItemRemoved

for p, flags := range events {
if p == strings.TrimPrefix(path, "/") {
continue
}

switch {
case flags.hasFlag(ItemIsFile):
if flags.String() != fileExpectedFlags.String() {
t.Fatalf(
"file flags for path '%s' did not match expected '%s' found '%s'",
p,
fileExpectedFlags.String(),
flags.String(),
)
}
case flags.hasFlag(ItemIsDir):
if flags.String() != dirExpectedFlags.String() {
t.Fatalf(
"file flags for path '%s' did not match expected '%s' found '%s'",
p,
dirExpectedFlags.String(),
flags.String(),
)
}
default:
t.Fatal("unrecognized flag")
}
}
}
Loading

0 comments on commit 1e71d45

Please sign in to comment.