...

Source file src/github.com/ory/x/watcherx/directory_test.go

Documentation: github.com/ory/x/watcherx

     1  package watcherx
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"runtime"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestWatchDirectory(t *testing.T) {
    18  	t.Run("case=notifies about file creation in directory", func(t *testing.T) {
    19  		ctx, c, dir, cancel := setup(t)
    20  		defer cancel()
    21  
    22  		_, err := WatchDirectory(ctx, dir, c)
    23  		require.NoError(t, err)
    24  		fileName := filepath.Join(dir, "example")
    25  		f, err := os.Create(fileName)
    26  		require.NoError(t, err)
    27  		require.NoError(t, f.Close())
    28  
    29  		assertChange(t, <-c, "", fileName)
    30  	})
    31  
    32  	t.Run("case=notifies about file write in directory", func(t *testing.T) {
    33  		ctx, c, dir, cancel := setup(t)
    34  		defer cancel()
    35  
    36  		fileName := filepath.Join(dir, "example")
    37  		f, err := os.Create(fileName)
    38  		require.NoError(t, err)
    39  		_, err = WatchDirectory(ctx, dir, c)
    40  		require.NoError(t, err)
    41  
    42  		_, err = fmt.Fprintf(f, "content")
    43  		require.NoError(t, f.Close())
    44  
    45  		assertChange(t, <-c, "content", fileName)
    46  	})
    47  
    48  	t.Run("case=nofifies about file delete in directory", func(t *testing.T) {
    49  		ctx, c, dir, cancel := setup(t)
    50  		defer cancel()
    51  
    52  		fileName := filepath.Join(dir, "example")
    53  		f, err := os.Create(fileName)
    54  		require.NoError(t, err)
    55  		require.NoError(t, f.Close())
    56  
    57  		_, err = WatchDirectory(ctx, dir, c)
    58  		require.NoError(t, err)
    59  		require.NoError(t, os.Remove(fileName))
    60  
    61  		assertRemove(t, <-c, fileName)
    62  	})
    63  
    64  	t.Run("case=notifies about file in child directory", func(t *testing.T) {
    65  		ctx, c, dir, cancel := setup(t)
    66  		defer cancel()
    67  
    68  		childDir := filepath.Join(dir, "child")
    69  		require.NoError(t, os.Mkdir(childDir, 0777))
    70  
    71  		_, err := WatchDirectory(ctx, dir, c)
    72  		require.NoError(t, err)
    73  
    74  		fileName := filepath.Join(childDir, "example")
    75  		f, err := os.Create(fileName)
    76  		require.NoError(t, err)
    77  		require.NoError(t, f.Close())
    78  
    79  		assertChange(t, <-c, "", fileName)
    80  	})
    81  
    82  	t.Run("case=watches new child directory", func(t *testing.T) {
    83  		ctx, c, dir, cancel := setup(t)
    84  		defer cancel()
    85  
    86  		_, err := WatchDirectory(ctx, dir, c)
    87  		require.NoError(t, err)
    88  
    89  		childDir := filepath.Join(dir, "child")
    90  		require.NoError(t, os.Mkdir(childDir, 0777))
    91  		fileName := filepath.Join(childDir, "example")
    92  		// there's not much we can do about this timeout as it takes some time until the new watcher is created
    93  		time.Sleep(time.Millisecond)
    94  		f, err := os.Create(fileName)
    95  		require.NoError(t, err)
    96  		require.NoError(t, f.Close())
    97  
    98  		assertChange(t, <-c, "", fileName)
    99  	})
   100  
   101  	t.Run("case=does not notify on directory deletion", func(t *testing.T) {
   102  		if runtime.GOOS != "linux" {
   103  			t.Skip("skipping test because IN_DELETE_SELF is unreliable on windows and macOS")
   104  		}
   105  
   106  		ctx, c, dir, cancel := setup(t)
   107  		defer cancel()
   108  
   109  		childDir := filepath.Join(dir, "child")
   110  		require.NoError(t, os.Mkdir(childDir, 0777))
   111  
   112  		_, err := WatchDirectory(ctx, dir, c)
   113  		require.NoError(t, err)
   114  
   115  		require.NoError(t, os.Remove(childDir))
   116  
   117  		select {
   118  		case e := <-c:
   119  			t.Logf("got unexpected event %T: %+v", e, e)
   120  			t.FailNow()
   121  		case <-time.After(2 * time.Millisecond):
   122  			// expected to not receive an event (1ms is what the watcher waits for the second event)
   123  		}
   124  	})
   125  
   126  	t.Run("case=notifies only for files on batch delete", func(t *testing.T) {
   127  		ctx, c, dir, cancel := setup(t)
   128  		defer cancel()
   129  
   130  		childDir := filepath.Join(dir, "child")
   131  		subChildDir := filepath.Join(childDir, "subchild")
   132  		require.NoError(t, os.MkdirAll(subChildDir, 0777))
   133  		f1 := filepath.Join(subChildDir, "f1")
   134  		f, err := os.Create(f1)
   135  		require.NoError(t, err)
   136  		require.NoError(t, f.Close())
   137  		f2 := filepath.Join(childDir, "f2")
   138  		f, err = os.Create(f2)
   139  		require.NoError(t, err)
   140  		require.NoError(t, f.Close())
   141  
   142  		_, err = WatchDirectory(ctx, dir, c)
   143  		require.NoError(t, err)
   144  
   145  		require.NoError(t, os.RemoveAll(childDir))
   146  
   147  		events := []Event{<-c, <-c}
   148  		if events[0].Source() > events[1].Source() {
   149  			events[1], events[0] = events[0], events[1]
   150  		}
   151  		assertRemove(t, events[0], f2)
   152  		assertRemove(t, events[1], f1)
   153  	})
   154  
   155  	t.Run("case=sends event when requested", func(t *testing.T) {
   156  		ctx, c, dir, cancel := setup(t)
   157  		defer cancel()
   158  
   159  		// buffered channel to allow usage of DispatchNow().done
   160  		c = make(EventChannel, 4)
   161  
   162  		files := map[string]string{
   163  			"a":                     "foo",
   164  			"b":                     "bar",
   165  			"c":                     "baz",
   166  			filepath.Join("d", "a"): "sub dir content",
   167  		}
   168  		for fn, fc := range files {
   169  			fp := filepath.Join(dir, fn)
   170  			require.NoError(t, os.MkdirAll(filepath.Dir(fp), 0700))
   171  			require.NoError(t, ioutil.WriteFile(fp, []byte(fc), 0600))
   172  		}
   173  
   174  		d, err := WatchDirectory(ctx, dir, c)
   175  		require.NoError(t, err)
   176  		done, err := d.DispatchNow()
   177  		require.NoError(t, err)
   178  
   179  		// wait for d.DispatchNow to be done
   180  		select {
   181  		case <-time.After(time.Second):
   182  			t.Log("Waiting for done timed out.")
   183  			t.FailNow()
   184  		case eventsSend := <-done:
   185  			assert.Equal(t, 4, eventsSend)
   186  		}
   187  
   188  		// because filepath.WalkDir walks lexicographically, we can assume the events come in lex order
   189  		assertChange(t, <-c, files["a"], filepath.Join(dir, "a"))
   190  		assertChange(t, <-c, files["b"], filepath.Join(dir, "b"))
   191  		assertChange(t, <-c, files["c"], filepath.Join(dir, "c"))
   192  		assertChange(t, <-c, files[filepath.Join("d", "a")], filepath.Join(dir, "d", "a"))
   193  	})
   194  }
   195  

View as plain text