github.com/bazelbuild/bazel-watcher@v0.25.2/internal/ibazel/fswatcher/fsnotify/fsnotify_test.go (about)

     1  // Copyright 2017 The Bazel Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package fsnotify
    16  
    17  import (
    18  	"errors"
    19  	"runtime/debug"
    20  	"testing"
    21  
    22  	"github.com/fsnotify/fsnotify"
    23  )
    24  
    25  type mockFSNotifyWatcher struct {
    26  	recentlyAddedFiles   map[string]struct{}
    27  	recentlyRemovedFiles map[string]struct{}
    28  	closed               bool
    29  }
    30  
    31  func (w *mockFSNotifyWatcher) Add(name string) error {
    32  	if _, ok := w.recentlyAddedFiles[name]; ok {
    33  		return errors.New("Already added file " + name)
    34  	}
    35  	w.recentlyAddedFiles[name] = struct{}{}
    36  	return nil
    37  }
    38  func (w *mockFSNotifyWatcher) Remove(name string) error {
    39  	if _, ok := w.recentlyRemovedFiles[name]; ok {
    40  		return errors.New("Already removed file " + name)
    41  	}
    42  	w.recentlyRemovedFiles[name] = struct{}{}
    43  	return nil
    44  }
    45  func (w *mockFSNotifyWatcher) Close() error {
    46  	if w.closed {
    47  		return errors.New("Already closed")
    48  	}
    49  	w.closed = true
    50  	return nil
    51  }
    52  func (w *mockFSNotifyWatcher) Events() chan fsnotify.Event {
    53  	return nil
    54  }
    55  
    56  func (w *mockFSNotifyWatcher) Reset() {
    57  	w.recentlyAddedFiles = make(map[string]struct{}, 0)
    58  	w.recentlyRemovedFiles = make(map[string]struct{}, 0)
    59  	w.closed = false
    60  }
    61  
    62  func (w *mockFSNotifyWatcher) assertRecentlyAdded(t *testing.T, added []string) {
    63  	k := keys(w.recentlyAddedFiles)
    64  	if val, ok := containsAll(k, added); !ok {
    65  		t.Errorf("Expected Add(\"%s\") not to have been called", val)
    66  		debug.PrintStack()
    67  	}
    68  	if val, ok := containsAll(added, k); !ok {
    69  		t.Errorf("Expected Add(\"%s\") to have been called", val)
    70  		debug.PrintStack()
    71  	}
    72  }
    73  
    74  func (w *mockFSNotifyWatcher) assertRecentlyRemoved(t *testing.T, removed []string) {
    75  	k := keys(w.recentlyRemovedFiles)
    76  	if val, ok := containsAll(k, removed); !ok {
    77  		t.Errorf("Expected Remove(\"%s\") not to have been called", val)
    78  		debug.PrintStack()
    79  	}
    80  	if val, ok := containsAll(removed, k); !ok {
    81  		t.Errorf("Expected Remove(\"%s\") to have been called", val)
    82  		debug.PrintStack()
    83  	}
    84  }
    85  
    86  func (w *mockFSNotifyWatcher) assertClosed(t *testing.T, closed bool) {
    87  	if w.closed != closed {
    88  		assertion := "to"
    89  		if !closed {
    90  			assertion = "not to"
    91  		}
    92  		t.Errorf("Expected Close() %s have been called", assertion)
    93  		debug.PrintStack()
    94  	}
    95  }
    96  
    97  func newWatcher() (*realFSNotifyWatcher, *mockFSNotifyWatcher) {
    98  	mock := &mockFSNotifyWatcher{}
    99  	mock.Reset()
   100  	watcher := &realFSNotifyWatcher{wrapper: mock}
   101  	return watcher, mock
   102  }
   103  
   104  func TestWatchedFilesState(t *testing.T) {
   105  	watcher, mock := newWatcher()
   106  
   107  	mock.Reset()
   108  	watcher.UpdateAll([]string{
   109  		"/path/a",
   110  		"/path/b",
   111  		"/path/c",
   112  	})
   113  	mock.assertRecentlyAdded(t, []string{
   114  		"/path/a",
   115  		"/path/b",
   116  		"/path/c",
   117  	})
   118  	mock.assertRecentlyRemoved(t, []string{})
   119  
   120  	mock.Reset()
   121  	watcher.UpdateAll([]string{
   122  		"/path/a",
   123  		"/path/b",
   124  	})
   125  	mock.assertRecentlyAdded(t, []string{})
   126  	mock.assertRecentlyRemoved(t, []string{
   127  		"/path/c",
   128  	})
   129  
   130  	mock.Reset()
   131  	watcher.UpdateAll([]string{
   132  		"/path/a",
   133  		"/path/d",
   134  	})
   135  	mock.assertRecentlyAdded(t, []string{
   136  		"/path/d",
   137  	})
   138  	mock.assertRecentlyRemoved(t, []string{
   139  		"/path/b",
   140  	})
   141  
   142  	mock.Reset()
   143  	watcher.UpdateAll([]string{})
   144  	mock.assertRecentlyAdded(t, []string{})
   145  	mock.assertRecentlyRemoved(t, []string{
   146  		"/path/a",
   147  		"/path/d",
   148  	})
   149  
   150  	mock.Reset()
   151  	watcher.UpdateAll([]string{
   152  		"/other/1",
   153  		"/other/2",
   154  		"/other/4",
   155  	})
   156  	mock.assertRecentlyAdded(t, []string{
   157  		"/other/1",
   158  		"/other/2",
   159  		"/other/4",
   160  	})
   161  	mock.assertRecentlyRemoved(t, []string{})
   162  
   163  	mock.assertClosed(t, false)
   164  	watcher.Close()
   165  	mock.assertClosed(t, true)
   166  }
   167  
   168  // Equal tells whether a and b contain the same elements, regardless of order
   169  func containsAll(a, b []string) (string, bool) {
   170  OUTER:
   171  	for _, v1 := range a {
   172  		for _, v2 := range b {
   173  			if v1 == v2 {
   174  				continue OUTER
   175  			}
   176  		}
   177  		return v1, false
   178  	}
   179  	return "", true
   180  }
   181  
   182  func keys(m map[string]struct{}) []string {
   183  	keys := make([]string, len(m))
   184  	i := 0
   185  	for k := range m {
   186  		keys[i] = k
   187  		i++
   188  	}
   189  	return keys
   190  }