package stats import ( "os" "path/filepath" "reflect" "testing" ) func TestTailer(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "r.log") var got []string collect := func(l string) { got = append(got, l) } appendTo := func(p, s string) { f, err := os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) if err != nil { t.Fatal(err) } f.WriteString(s) f.Close() } tl := &Tailer{Path: path} // The file doesn't exist yet. if n, err := tl.Read(collect); n != 0 || err != nil { t.Fatalf("missing file: %d, %v", n, err) } appendTo(path, "one\ntwo\nthr") tl.Read(collect) appendTo(path, "ee\n") tl.Read(collect) if !reflect.DeepEqual(got, []string{"one", "two", "three"}) { t.Fatalf("got %q", got) } // named rotates: renames the file and starts a new one. Lines written // to the old file before the rename are not lost. appendTo(path, "four\n") os.Rename(path, path+".0") appendTo(path, "five\n") tl.Read(collect) if !reflect.DeepEqual(got[3:], []string{"four", "five"}) { t.Fatalf("after rotation got %q", got) } // Restart: resume from the saved position. pos := tl.Position() tl.Close() appendTo(path, "six\n") tl2 := &Tailer{Path: path} tl2.Resume(pos) got = nil tl2.Read(collect) if !reflect.DeepEqual(got, []string{"six"}) { t.Fatalf("after resume got %q", got) } // Rotation while jarld was down: finish the rotated file first. pos = tl2.Position() tl2.Close() appendTo(path, "seven\n") os.Rename(path, path+".0") appendTo(path, "eight\n") tl3 := &Tailer{Path: path} tl3.Resume(pos) got = nil tl3.Read(collect) if !reflect.DeepEqual(got, []string{"seven", "eight"}) { t.Fatalf("after offline rotation got %q", got) } // Truncation: start over. os.WriteFile(path, []byte("x\n"), 0o644) got = nil tl3.Read(collect) tl3.Read(collect) if !reflect.DeepEqual(got, []string{"x"}) { t.Fatalf("after truncation got %q", got) } tl3.Close() }