@@ -106,6 +106,9 @@ func TestStopAtEOF(t *testing.T) {
106106 if line .Text != "hello" {
107107 t .Errorf ("Expected to get 'hello', got '%s' instead" , line .Text )
108108 }
109+ if line .Num != 1 {
110+ t .Errorf ("Expected to get 1, got %d instead" , line .Num )
111+ }
109112
110113 tailTest .VerifyTailOutput (tail , []string {"there" , "world" }, false )
111114 tail .StopAtEOF ()
@@ -134,6 +137,7 @@ func TestOver4096ByteLine(t *testing.T) {
134137 tailTest .RemoveFile ("test.txt" )
135138 tailTest .Cleanup (tail , true )
136139}
140+
137141func TestOver4096ByteLineWithSetMaxLineSize (t * testing.T ) {
138142 tailTest := NewTailTest ("Over4096ByteLineMaxLineSize" , t )
139143 testString := strings .Repeat ("a" , 4097 )
@@ -219,6 +223,40 @@ func TestReOpenPolling(t *testing.T) {
219223 reOpen (t , true )
220224}
221225
226+ func TestReOpenWithCursor (t * testing.T ) {
227+ delay := 300 * time .Millisecond // account for POLL_DURATION
228+ tailTest := NewTailTest ("reopen-cursor" , t )
229+ tailTest .CreateFile ("test.txt" , "hello\n world\n " )
230+ tail := tailTest .StartTail (
231+ "test.txt" ,
232+ Config {Follow : true , ReOpen : true , Poll : true })
233+ content := []string {"hello" , "world" , "more" , "data" , "endofworld" }
234+ go tailTest .VerifyTailOutputUsingCursor (tail , content , false )
235+
236+ // deletion must trigger reopen
237+ <- time .After (delay )
238+ tailTest .RemoveFile ("test.txt" )
239+ <- time .After (delay )
240+ tailTest .CreateFile ("test.txt" , "hello\n world\n more\n data\n " )
241+
242+ // rename must trigger reopen
243+ <- time .After (delay )
244+ tailTest .RenameFile ("test.txt" , "test.txt.rotated" )
245+ <- time .After (delay )
246+ tailTest .CreateFile ("test.txt" , "hello\n world\n more\n data\n endofworld\n " )
247+
248+ // Delete after a reasonable delay, to give tail sufficient time
249+ // to read all lines.
250+ <- time .After (delay )
251+ tailTest .RemoveFile ("test.txt" )
252+ <- time .After (delay )
253+
254+ // Do not bother with stopping as it could kill the tomb during
255+ // the reading of data written above. Timings can vary based on
256+ // test environment.
257+ tailTest .Cleanup (tail , false )
258+ }
259+
222260// The use of polling file watcher could affect file rotation
223261// (detected via renames), so test these explicitly.
224262
@@ -230,6 +268,31 @@ func TestReSeekPolling(t *testing.T) {
230268 reSeek (t , true )
231269}
232270
271+ func TestReSeekWithCursor (t * testing.T ) {
272+ tailTest := NewTailTest ("reseek-cursor" , t )
273+ tailTest .CreateFile ("test.txt" , "a really long string goes here\n hello\n world\n " )
274+ tail := tailTest .StartTail (
275+ "test.txt" ,
276+ Config {Follow : true , ReOpen : false , Poll : false })
277+
278+ go tailTest .VerifyTailOutputUsingCursor (tail , []string {
279+ "a really long string goes here" , "hello" , "world" , "but" , "not" , "me" }, false )
280+
281+ // truncate now
282+ <- time .After (100 * time .Millisecond )
283+ tailTest .TruncateFile ("test.txt" , "skip\n me\n please\n but\n not\n me\n " )
284+
285+ // Delete after a reasonable delay, to give tail sufficient time
286+ // to read all lines.
287+ <- time .After (100 * time .Millisecond )
288+ tailTest .RemoveFile ("test.txt" )
289+
290+ // Do not bother with stopping as it could kill the tomb during
291+ // the reading of data written above. Timings can vary based on
292+ // test environment.
293+ tailTest .Cleanup (tail , false )
294+ }
295+
233296func TestRateLimiting (t * testing.T ) {
234297 tailTest := NewTailTest ("rate-limiting" , t )
235298 tailTest .CreateFile ("test.txt" , "hello\n world\n again\n extra\n " )
@@ -266,7 +329,10 @@ func TestTell(t *testing.T) {
266329 Location : & SeekInfo {0 , os .SEEK_SET }}
267330 tail := tailTest .StartTail ("test.txt" , config )
268331 // read noe line
269- <- tail .Lines
332+ line := <- tail .Lines
333+ if line .Num != 1 {
334+ tailTest .Errorf ("expected line to have number 1 but got %d" , line .Num )
335+ }
270336 offset , err := tail .Tell ()
271337 if err != nil {
272338 tailTest .Errorf ("Tell return error: %s" , err .Error ())
@@ -285,6 +351,9 @@ func TestTell(t *testing.T) {
285351 tailTest .Fatalf ("mismatch; expected world or again, but got %s" ,
286352 l .Text )
287353 }
354+ if l .Num < 1 || l .Num > 2 {
355+ tailTest .Errorf ("expected line number to be between 1 and 2 but got %d" , l .Num )
356+ }
288357 break
289358 }
290359 tailTest .RemoveFile ("test.txt" )
@@ -518,7 +587,7 @@ func (t TailTest) StartTail(name string, config Config) *Tail {
518587
519588func (t TailTest ) VerifyTailOutput (tail * Tail , lines []string , expectEOF bool ) {
520589 defer close (t .done )
521- t .ReadLines (tail , lines )
590+ t .ReadLines (tail , lines , false )
522591 // It is important to do this if only EOF is expected
523592 // otherwise we could block on <-tail.Lines
524593 if expectEOF {
@@ -529,27 +598,53 @@ func (t TailTest) VerifyTailOutput(tail *Tail, lines []string, expectEOF bool) {
529598 }
530599}
531600
532- func (t TailTest ) ReadLines (tail * Tail , lines []string ) {
533- for idx , line := range lines {
534- tailedLine , ok := <- tail .Lines
535- if ! ok {
536- // tail.Lines is closed and empty.
537- err := tail .Err ()
538- if err != nil {
539- t .Fatalf ("tail ended with error: %v" , err )
540- }
541- t .Fatalf ("tail ended early; expecting more: %v" , lines [idx :])
542- }
543- if tailedLine == nil {
544- t .Fatalf ("tail.Lines returned nil; not possible" )
601+ func (t TailTest ) VerifyTailOutputUsingCursor (tail * Tail , lines []string , expectEOF bool ) {
602+ defer close (t .done )
603+ t .ReadLines (tail , lines , true )
604+ // It is important to do this if only EOF is expected
605+ // otherwise we could block on <-tail.Lines
606+ if expectEOF {
607+ line , ok := <- tail .Lines
608+ if ok {
609+ t .Fatalf ("more content from tail: %+v" , line )
545610 }
546- // Note: not checking .Err as the `lines` argument is designed
547- // to match error strings as well.
548- if tailedLine .Text != line {
549- t .Fatalf (
550- "unexpected line/err from tail: " +
551- "expecting <<%s>>>, but got <<<%s>>>" ,
552- line , tailedLine .Text )
611+ }
612+ }
613+
614+ func (t TailTest ) ReadLines (tail * Tail , lines []string , useCursor bool ) {
615+ cursor := 1
616+
617+ for _ , line := range lines {
618+ for {
619+ tailedLine , ok := <- tail .Lines
620+ if ! ok {
621+ // tail.Lines is closed and empty.
622+ err := tail .Err ()
623+ if err != nil {
624+ t .Fatalf ("tail ended with error: %v" , err )
625+ }
626+ t .Fatalf ("tail ended early; expecting more: %v" , lines [cursor :])
627+ }
628+ if tailedLine == nil {
629+ t .Fatalf ("tail.Lines returned nil; not possible" )
630+ }
631+
632+ if useCursor && tailedLine .Num < cursor {
633+ // skip lines up until cursor
634+ continue
635+ }
636+
637+ // Note: not checking .Err as the `lines` argument is designed
638+ // to match error strings as well.
639+ if tailedLine .Text != line {
640+ t .Fatalf (
641+ "unexpected line/err from tail: " +
642+ "expecting <<%s>>>, but got <<<%s>>>" ,
643+ line , tailedLine .Text )
644+ }
645+
646+ cursor ++
647+ break
553648 }
554649 }
555650}
0 commit comments