Wait for goroutines to finish before leaving test functions.

VerifyTailOutput is started in a goroutine, but the test function
doesn't wait for this goroutine to exit before returning, so any errors
raised by VerifyTailOutput after the function returns are lost.

Also add a failing test for tailing a file with a relative path.
This commit is contained in:
David Sansome 2015-09-29 18:01:06 +10:00
parent 4b368d1590
commit 0da4e86639
1 changed files with 61 additions and 30 deletions

View File

@ -6,15 +6,14 @@
package tail package tail
import ( import (
_ "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"strings" "strings"
"testing" "testing"
"time" "time"
"./watch"
"github.com/ActiveState/tail/ratelimiter" "github.com/ActiveState/tail/ratelimiter"
"github.com/ActiveState/tail/watch"
) )
func init() { func init() {
@ -44,6 +43,40 @@ func TestMustExist(t *testing.T) {
tail.Cleanup() tail.Cleanup()
} }
func TestWaitsForFileToExist(_t *testing.T) {
t := NewTailTest("waits-for-file-to-exist", _t)
tail := t.StartTail("test.txt", Config{})
go t.VerifyTailOutput(tail, []string{"hello", "world"})
<-time.After(100 * time.Millisecond)
t.CreateFile("test.txt", "hello\nworld\n")
t.Cleanup(tail)
}
func TestWaitsForFileToExistRelativePath(_t *testing.T) {
t := NewTailTest("waits-for-file-to-exist-relative", _t)
oldWD, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
os.Chdir(t.path)
defer os.Chdir(oldWD)
tail, err := TailFile("test.txt", Config{})
if err != nil {
t.Fatal(err)
}
go t.VerifyTailOutput(tail, []string{"hello", "world"})
<-time.After(100 * time.Millisecond)
if err := ioutil.WriteFile("test.txt", []byte("hello\nworld\n"), 0600); err != nil {
t.Fatal(err)
}
t.Cleanup(tail)
}
func TestStop(t *testing.T) { func TestStop(t *testing.T) {
tail, err := TailFile("_no_such_file", Config{Follow: true, MustExist: false}) tail, err := TailFile("_no_such_file", Config{Follow: true, MustExist: false})
if err != nil { if err != nil {
@ -65,8 +98,7 @@ func MaxLineSizeT(_t *testing.T, follow bool, fileContent string, expected []str
// to read all lines. // to read all lines.
<-time.After(100 * time.Millisecond) <-time.After(100 * time.Millisecond)
t.RemoveFile("test.txt") t.RemoveFile("test.txt")
tail.Stop() t.Cleanup(tail)
tail.Cleanup()
} }
func TestMaxLineSizeFollow(_t *testing.T) { func TestMaxLineSizeFollow(_t *testing.T) {
@ -89,9 +121,9 @@ func TestOver4096ByteLine(_t *testing.T) {
// to read all lines. // to read all lines.
<-time.After(100 * time.Millisecond) <-time.After(100 * time.Millisecond)
t.RemoveFile("test.txt") t.RemoveFile("test.txt")
tail.Stop() t.Cleanup(tail)
tail.Cleanup()
} }
func TestOver4096ByteLineWithSetMaxLineSize(_t *testing.T) { func TestOver4096ByteLineWithSetMaxLineSize(_t *testing.T) {
t := NewTailTest("Over4096ByteLineMaxLineSize", _t) t := NewTailTest("Over4096ByteLineMaxLineSize", _t)
testString := strings.Repeat("a", 4097) testString := strings.Repeat("a", 4097)
@ -103,8 +135,7 @@ func TestOver4096ByteLineWithSetMaxLineSize(_t *testing.T) {
// to read all lines. // to read all lines.
<-time.After(100 * time.Millisecond) <-time.After(100 * time.Millisecond)
t.RemoveFile("test.txt") t.RemoveFile("test.txt")
// tail.Stop() t.Cleanup(tail)
tail.Cleanup()
} }
func TestLocationFull(_t *testing.T) { func TestLocationFull(_t *testing.T) {
@ -117,8 +148,7 @@ func TestLocationFull(_t *testing.T) {
// to read all lines. // to read all lines.
<-time.After(100 * time.Millisecond) <-time.After(100 * time.Millisecond)
t.RemoveFile("test.txt") t.RemoveFile("test.txt")
tail.Stop() t.Cleanup(tail)
tail.Cleanup()
} }
func TestLocationFullDontFollow(_t *testing.T) { func TestLocationFullDontFollow(_t *testing.T) {
@ -132,8 +162,7 @@ func TestLocationFullDontFollow(_t *testing.T) {
t.AppendFile("test.txt", "more\ndata\n") t.AppendFile("test.txt", "more\ndata\n")
<-time.After(100 * time.Millisecond) <-time.After(100 * time.Millisecond)
tail.Stop() t.Cleanup(tail)
tail.Cleanup()
} }
func TestLocationEnd(_t *testing.T) { func TestLocationEnd(_t *testing.T) {
@ -149,8 +178,7 @@ func TestLocationEnd(_t *testing.T) {
// to read all lines. // to read all lines.
<-time.After(100 * time.Millisecond) <-time.After(100 * time.Millisecond)
t.RemoveFile("test.txt") t.RemoveFile("test.txt")
tail.Stop() t.Cleanup(tail)
tail.Cleanup()
} }
func TestLocationMiddle(_t *testing.T) { func TestLocationMiddle(_t *testing.T) {
@ -167,8 +195,7 @@ func TestLocationMiddle(_t *testing.T) {
// to read all lines. // to read all lines.
<-time.After(100 * time.Millisecond) <-time.After(100 * time.Millisecond)
t.RemoveFile("test.txt") t.RemoveFile("test.txt")
tail.Stop() t.Cleanup(tail)
tail.Cleanup()
} }
func _TestReOpen(_t *testing.T, poll bool) { func _TestReOpen(_t *testing.T, poll bool) {
@ -199,7 +226,7 @@ func _TestReOpen(_t *testing.T, poll bool) {
<-time.After(delay) <-time.After(delay)
t.RenameFile("test.txt", "test.txt.rotated") t.RenameFile("test.txt", "test.txt.rotated")
<-time.After(delay) <-time.After(delay)
t.CreateFile("test.txt", "endofworld") t.CreateFile("test.txt", "endofworld\n")
// Delete after a reasonable delay, to give tail sufficient time // Delete after a reasonable delay, to give tail sufficient time
// to read all lines. // to read all lines.
@ -207,11 +234,7 @@ func _TestReOpen(_t *testing.T, poll bool) {
t.RemoveFile("test.txt") t.RemoveFile("test.txt")
<-time.After(delay) <-time.After(delay)
// Do not bother with stopping as it could kill the tomb during t.Cleanup(tail)
// the reading of data written above. Timings can vary based on
// test environment.
// tail.Stop()
tail.Cleanup()
} }
// The use of polling file watcher could affect file rotation // The use of polling file watcher could affect file rotation
@ -250,11 +273,7 @@ func _TestReSeek(_t *testing.T, poll bool) {
<-time.After(100 * time.Millisecond) <-time.After(100 * time.Millisecond)
t.RemoveFile("test.txt") t.RemoveFile("test.txt")
// Do not bother with stopping as it could kill the tomb during t.Cleanup(tail)
// the reading of data written above. Timings can vary based on
// test environment.
// tail.Stop()
tail.Cleanup()
} }
// The use of polling file watcher could affect file rotation // The use of polling file watcher could affect file rotation
@ -293,8 +312,7 @@ func TestRateLimiting(_t *testing.T) {
<-time.After(100 * time.Millisecond) <-time.After(100 * time.Millisecond)
t.RemoveFile("test.txt") t.RemoveFile("test.txt")
// tail.Stop() t.Cleanup(tail)
tail.Cleanup()
} }
func TestTell(_t *testing.T) { func TestTell(_t *testing.T) {
@ -336,11 +354,12 @@ func TestTell(_t *testing.T) {
type TailTest struct { type TailTest struct {
Name string Name string
path string path string
done chan struct{}
*testing.T *testing.T
} }
func NewTailTest(name string, t *testing.T) TailTest { func NewTailTest(name string, t *testing.T) TailTest {
tt := TailTest{name, ".test/" + name, t} tt := TailTest{name, ".test/" + name, make(chan struct{}), t}
err := os.MkdirAll(tt.path, os.ModeTemporary|0700) err := os.MkdirAll(tt.path, os.ModeTemporary|0700)
if err != nil { if err != nil {
tt.Fatal(err) tt.Fatal(err)
@ -408,7 +427,19 @@ func (t TailTest) StartTail(name string, config Config) *Tail {
return tail return tail
} }
func (t TailTest) Cleanup(tail *Tail) {
<-time.After(100 * time.Millisecond)
tail.Stop()
tail.Cleanup()
// VerifyTailOutput runs in a goroutine so ensure it's finished before ending
// the test, otherwise its failures will be ignored.
<-t.done
}
func (t TailTest) VerifyTailOutput(tail *Tail, lines []string) { func (t TailTest) VerifyTailOutput(tail *Tail, lines []string) {
defer close(t.done)
for idx, line := range lines { for idx, line := range lines {
tailedLine, ok := <-tail.Lines tailedLine, ok := <-tail.Lines
if !ok { if !ok {