Skip to content

Commit 0fb96a3

Browse files
committed
#5 debugging failed test on appveyor.com (Windows)
1 parent c329d11 commit 0fb96a3

File tree

2 files changed

+84
-19
lines changed

2 files changed

+84
-19
lines changed

tailer/fswatcher/fswatcher.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ func (t *fileTailer) Errors() chan Error {
8686
return t.errors
8787
}
8888

89+
// Close() triggers the shutdown of the file tailer.
90+
// The file tailer will eventually terminate,
91+
// but after Close() returns it might still be running in the background for a few milliseconds.
8992
func (t *fileTailer) Close() {
9093
// Closing the done channel will stop the consumer loop.
9194
// Deferred functions within the consumer loop will close the producer loop.

tailer/fswatcher_test.go

Lines changed: 81 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -95,26 +95,30 @@ func TestAll(t *testing.T) {
9595
// * directories with the xattr com.apple.FinderInfo (like everything in /tmp) are hidden
9696
// In order to test this, we must create a log file somewhere outside of /tmp, so we use $HOME.
9797
func TestVisibleInOSXFinder(t *testing.T) {
98+
ctx := setUp(t, "visible in macOS finder", closeFileAfterEachLine, fseventTailer, _nocreate, mv)
99+
100+
// replace ctx.basedir with a directory in $HOME
98101
currentUser, err := user.Current()
99102
if err != nil {
100-
t.Fatalf("failed to get current user: %v", err)
103+
fatalf(t, ctx, "failed to get current user: %v", err)
101104
}
102105
testDir, err := ioutil.TempDir(currentUser.HomeDir, "grok_exporter_test_dir_")
103106
if err != nil {
104-
t.Fatalf("failed to create test directory: %v", err.Error())
107+
fatalf(t, ctx, "failed to create test directory: %v", err.Error())
105108
}
106109
defer func() {
107110
err := os.RemoveAll(testDir)
108111
if err != nil {
109-
t.Fatalf("%v: Failed to remove test directory after running the tests: %v", testDir, err.Error())
112+
fatalf(t, ctx, "%v: Failed to remove test directory after running the tests: %v", testDir, err.Error())
110113
}
111114
}()
112-
ctx := setUp(t, "visible in macOS finder", closeFileAfterEachLine, fseventTailer, _nocreate, mv)
113115
err = os.RemoveAll(ctx.basedir)
114116
if err != nil {
115-
t.Fatalf("%v: failed to remove temp dir: %v", ctx.basedir, err)
117+
fatalf(t, ctx, "%v: failed to remove temp dir: %v", ctx.basedir, err)
116118
}
117119
ctx.basedir = testDir
120+
121+
// run simple test in the new directory
118122
test := [][]string{
119123
{"log", "line 1", "test.log"},
120124
{"start file tailer", "test.log"},
@@ -156,21 +160,24 @@ func runTest(t *testing.T, ctx *context, cmds [][]string) {
156160
}
157161

158162
func closeTailer(t *testing.T, ctx *context) {
163+
// Note: This function checks if the Lines() channel gets closed.
164+
// While it's good to check this, it doesn't guarantee that the tailer is
165+
// fully shut down. There might be an fseventProducerLoop running in the
166+
// background, or a hanging system call keeping the log directory open.
167+
// There are tests for that like counting the number of goroutines
168+
// in assertGoroutinesTerminated() or making sure the log directory
169+
// can be removed in tearDown().
159170
timeout := 5 * time.Second
160171
if ctx.tailer != nil {
161172
ctx.tailer.Close()
162-
// wait until closed
173+
// check if the lines channel gets closed
163174
select {
164175
case line, open := <-ctx.tailer.Lines():
165176
if open {
166-
msg := fmt.Sprintf("read unexpected line line from file %q: %q", line.File, line.Line)
167-
ctx.log.Error(msg)
168-
t.Fatal(msg)
177+
fatalf(t, ctx, "read unexpected line line from file %q: %q", line.File, line.Line)
169178
}
170179
case <-time.After(timeout):
171-
msg := fmt.Sprintf("failed to shut down the tailer. timeout after %v seconds", timeout)
172-
ctx.log.Error(msg)
173-
fatalf(t, ctx, msg)
180+
fatalf(t, ctx, "failed to shut down the tailer. timeout after %v seconds", timeout)
174181
}
175182
}
176183
}
@@ -468,7 +475,7 @@ func startFileTailer(t *testing.T, ctx *context, params []string) {
468475
for _, g := range globs {
469476
parsedGlob, err := glob.Parse(filepath.Join(ctx.basedir, g))
470477
if err != nil {
471-
t.Fatal(err)
478+
fatalf(t, ctx, "%v", err)
472479
}
473480
parsedGlobs = append(parsedGlobs, parsedGlob)
474481
}
@@ -723,9 +730,64 @@ func (l *keepOpenLogFileWriter) close(t *testing.T, ctx *context) {
723730
}
724731

725732
func tearDown(t *testing.T, ctx *context) {
726-
err := os.RemoveAll(ctx.basedir)
733+
deleteRecursively(t, ctx, ctx.basedir)
734+
}
735+
736+
func deleteRecursively(t *testing.T, ctx *context, file string) {
737+
// os.RemoveAll() fails on Windows with "Access is denied".
738+
// Remove each file individually to learn which file cannot be removed.
739+
fileInfo, err := os.Stat(file)
740+
if err != nil {
741+
fatalf(t, ctx, "tearDown: stat(%q) failed: %v", file, err)
742+
}
743+
if fileInfo.IsDir() {
744+
for _, childInfo := range ls(t, ctx, file) {
745+
deleteRecursively(t, ctx, path.Join(file, childInfo.Name()))
746+
}
747+
}
748+
ctx.log.Debugf("tearDown: removing %q", file)
749+
delete(t, ctx, file)
750+
//if err != nil {
751+
// // We get "The directory is not empty" here on Windows.
752+
// // Let's print what files are in here.
753+
// var filesInDir []string
754+
// for _, fileInDir := range ls(t, ctx, file) {
755+
// filesInDir = append(filesInDir, fileInDir.Name())
756+
// }
757+
// fatalf(t, ctx, "tearDown: %q: remove failed: %v. number of files in dir: %v, list of file in dir: %#v", file, err, len(filesInDir), filesInDir)
758+
//}
759+
}
760+
761+
func delete(t *testing.T, ctx *context, file string) {
762+
// repeat a few times in case the file or directory is still used by the file system watcher.
763+
var (
764+
err error
765+
timeout = 5 * time.Second
766+
timePassed = 0 * time.Second
767+
)
768+
for timePassed < timeout {
769+
err = os.Remove(file)
770+
if err == nil {
771+
// Check if the file or directory is really removed. On Windows, os.Remove() sometimes
772+
// returns no error when removing the log directory, but the directory is still there.
773+
_, err = os.Stat(file)
774+
if err != nil {
775+
if os.IsNotExist(err) {
776+
// os.Remove(file) was successful, the file or directory is gone.
777+
return
778+
} else {
779+
fatalf(t, ctx, "tearDown: %q: stat failed: %v", file, err)
780+
}
781+
}
782+
}
783+
// os.Stat() successful. The file or directory is still there. Try again.
784+
time.Sleep(200 * time.Millisecond)
785+
timePassed += 200 * time.Millisecond
786+
}
727787
if err != nil {
728-
fatalf(t, ctx, "%v: failed to remove the test directory after running the tests: %v", ctx.basedir, err)
788+
fatalf(t, ctx, "tearDown: %q: failed to remove file or directory: %v", file, err)
789+
} else {
790+
fatalf(t, ctx, "tearDown: %q: failed to remove file or directory", file)
729791
}
730792
}
731793

@@ -803,18 +865,18 @@ func runTestShutdown(t *testing.T, mode string) {
803865
select {
804866
case _, open := <-tailer.Errors():
805867
if open {
806-
t.Fatalf("error channel not closed")
868+
fatalf(t, ctx, "error channel not closed")
807869
}
808870
case <-time.After(5 * time.Second):
809-
t.Fatalf("timeout while waiting for errors channel to be closed.")
871+
fatalf(t, ctx, "timeout while waiting for errors channel to be closed.")
810872
}
811873
select {
812874
case _, open := <-tailer.Lines():
813875
if open {
814-
t.Fatalf("lines channel not closed")
876+
fatalf(t, ctx, "lines channel not closed")
815877
}
816878
case <-time.After(5 * time.Second):
817-
t.Fatalf("timeout while waiting for errors channel to be closed.")
879+
fatalf(t, ctx, "timeout while waiting for errors channel to be closed.")
818880
}
819881
assertGoroutinesTerminated(t, ctx, nGoroutinesBefore)
820882
}

0 commit comments

Comments
 (0)