// This test is a copy&paste of `TestRmTouch` but swapping the rm, for truncation func TestMvTouch(t *testing.T) { require := require.New(t) assert := assert.New(t) withTempFile(t, time.Millisecond*200, func(t *testing.T, filename string, file *os.File) error { follow, err := tailer.NewFile(filename) require.NoError(err, "Failed to create tailer.File") want := make([]byte, 100) for i := 0; i < 100; i++ { want[i] = byte(i) } t.Log("Want:", want) go func() { max := len(want) step := max / 10 for i := 0; i < max; i += step { if i == max/2 { // CHANGES FROM `TestRmTouch` START HERE t.Log("Moving the old file out of the way", file) require.NoError(os.Rename(filename, filename+".old"), "Unable to rename the file") file, err = os.Create(filename) require.NoError(err, "failed to create new test file: %v", err) t.Log("File created again", file) // CHANGES FROM `TestRmTouch` STOP HERE } block := want[i : i+step] t.Logf("Writing block{%v} to file(%v)\n", block, file) file.Write(block) file.Sync() time.Sleep(time.Millisecond * time.Duration(step)) } t.Log("Finished writing out all the bytes") }() got := make([]byte, len(want)) _, err = io.ReadAtLeast(follow, got, 51) assert.NoError(err) t.Log("Got:", got) return nil }) }
// This test is a copy&paste of `TestRmTouch` but swapping the rm, for truncation func TestTruncation(t *testing.T) { require := require.New(t) assert := assert.New(t) withTempFile(t, time.Millisecond*200, func(t *testing.T, filename string, file *os.File) error { follow, err := tailer.NewFile(filename) require.NoError(err, "Failed to create tailer.File") want := make([]byte, 100) for i := 0; i < 100; i++ { want[i] = byte(i) } t.Log("Want:", want) go func() { max := len(want) step := max / 10 for i := 0; i < max; i += step { if i == max/2 { // CHANGES FROM `TestRmTouch` START HERE t.Log("truncating the file") file, err := os.OpenFile(filename, os.O_TRUNC, os.ModeTemporary) require.NoError(err, "unable to truncate file: %v", err) require.NoError(file.Close(), "failed to close the truncated file") t.Log("file truncated:", file) // CHANGES FROM `TestRmTouch` STOP HERE } block := want[i : i+step] t.Logf("Writing block{%v} to file(%v)\n", block, file) file.Write(block) file.Sync() time.Sleep(time.Millisecond * time.Duration(step)) } t.Log("Finished writing out all the bytes") }() got := make([]byte, len(want)) _, err = io.ReadAtLeast(follow, got, 51) assert.NoError(err) t.Log("Got:", got) return nil }) }
// Run for 50ms constantly trying to read from something that has nothing to read // This is pretty much here to make sure we don't let our Reader "spin" i.e. return (0, nil) func TestSpinningReader(t *testing.T) { require := require.New(t) assert := assert.New(t) withTempFile(t, time.Millisecond*150, func(t *testing.T, filename string, file *os.File) error { tail, err := tailer.NewFile(filename) require.NoError(err, "Failed to create tailer.File") // Touch the file repeatedly go func() { for _ = range time.Tick(time.Millisecond * 5) { t.Log("Touching the file") os.Chtimes(filename, time.Now(), time.Now()) } }() // Read from the file as quickly as possible var readCount int go func() { buf := make([]byte, 1000) for stop := time.After(time.Millisecond * 50); ; { select { case <-stop: return default: t.Log("Read called") _, err := tail.Read(buf) assert.NoError(err, "read error: %v", err) t.Log("Read completed") readCount++ if readCount > 5 { assert.Fail("Spinning on read") } } } }() // This sleep & the go func() of the code above is there because Read should deadlock the thread it is running in time.Sleep(time.Millisecond * 50) t.Logf("Reader read '%v' times", readCount) return nil }) }
// Can we do the most basic thing, can we follow writes to the file overtime func TestCanFollowFile(t *testing.T) { require := require.New(t) assert := assert.New(t) withTempFile(t, time.Millisecond*200, func(t *testing.T, filename string, file *os.File) error { tail, err := tailer.NewFile(filename) require.NoError(err, "Failed to create tailer.File") go func() { for i := 0; i < 10; i++ { t.Logf("Writing: %v", i) file.Write([]byte{byte(i)}) time.Sleep(time.Millisecond * 10) } }() t.Log("Preparing to read 10 bytes") _, err = io.ReadAtLeast(tail, make([]byte, 10), 10) t.Log("Read 10 bytes, err ->", err) assert.NoError(err, "Read shouldn't return an error here") return err }) }
// Make sure this adheres to a io.ReadCloser & check that reads after closes return (0, io.EOF) func TestBasicImpl(t *testing.T) { require := require.New(t) assert := assert.New(t) var ( tail io.ReadCloser n int err error ) withTempFile(t, time.Millisecond*150, func(t *testing.T, filename string, file *os.File) error { tail, err = tailer.NewFile(filename) require.NoError(err, "Failed to create tailer.File") err = tail.Close() require.NoError(err, "Failed to close tailer.File") n, err = tail.Read(make([]byte, 1)) assert.Equal(0, n, "Calls to Read after Close is called should return 0 bytes read") assert.Equal(io.EOF, err, "Calls to Read after Close is called should return io.EOF as their error") return nil }) }