github.com/netdata/go.d.plugin@v0.58.1/pkg/logs/reader_test.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package logs 4 5 import ( 6 "bufio" 7 "fmt" 8 "io" 9 "os" 10 "path/filepath" 11 "testing" 12 "time" 13 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 func TestReader_Read(t *testing.T) { 19 reader, teardown := prepareTestReader(t) 20 defer teardown() 21 22 r := testReader{bufio.NewReader(reader)} 23 filename := reader.CurrentFilename() 24 numLogs := 5 25 var sum int 26 27 for i := 0; i < 10; i++ { 28 appendLogs(t, filename, time.Millisecond*10, numLogs) 29 n, err := r.readUntilEOF() 30 sum += n 31 32 assert.Equal(t, io.EOF, err) 33 assert.Equal(t, numLogs*(i+1), sum) 34 } 35 } 36 37 func TestReader_Read_HandleFileRotation(t *testing.T) { 38 reader, teardown := prepareTestReader(t) 39 defer teardown() 40 41 r := testReader{bufio.NewReader(reader)} 42 filename := reader.CurrentFilename() 43 numLogs := 5 44 rotateFile(t, filename) 45 appendLogs(t, filename, time.Millisecond*10, numLogs) 46 47 n, err := r.readUntilEOFTimes(maxEOF) 48 assert.Equal(t, io.EOF, err) 49 assert.Equal(t, 0, n) 50 51 appendLogs(t, filename, time.Millisecond*10, numLogs) 52 n, err = r.readUntilEOF() 53 assert.Equal(t, io.EOF, err) 54 assert.Equal(t, numLogs, n) 55 } 56 57 func TestReader_Read_HandleFileRotationWithDelay(t *testing.T) { 58 reader, teardown := prepareTestReader(t) 59 defer teardown() 60 61 r := testReader{bufio.NewReader(reader)} 62 filename := reader.CurrentFilename() 63 _ = os.Remove(filename) 64 65 // trigger reopen first time 66 n, err := r.readUntilEOFTimes(maxEOF) 67 assert.Equal(t, ErrNoMatchedFile, err) 68 assert.Equal(t, 0, n) 69 70 f, err := os.Create(filename) 71 require.NoError(t, err) 72 _ = f.Close() 73 74 // trigger reopen 2nd time 75 n, err = r.readUntilEOF() 76 assert.Equal(t, io.EOF, err) 77 assert.Equal(t, 0, n) 78 79 numLogs := 5 80 appendLogs(t, filename, time.Millisecond*10, numLogs) 81 n, err = r.readUntilEOF() 82 assert.Equal(t, io.EOF, err) 83 assert.Equal(t, numLogs, n) 84 } 85 86 func TestReader_Close(t *testing.T) { 87 reader, teardown := prepareTestReader(t) 88 defer teardown() 89 90 assert.NoError(t, reader.Close()) 91 assert.Nil(t, reader.file) 92 } 93 94 func TestReader_Close_NilFile(t *testing.T) { 95 var r Reader 96 assert.NoError(t, r.Close()) 97 } 98 99 func TestOpen(t *testing.T) { 100 tempFileName1 := prepareTempFile(t, "*-web_log-open-test-1.log") 101 tempFileName2 := prepareTempFile(t, "*-web_log-open-test-2.log") 102 tempFileName3 := prepareTempFile(t, "*-web_log-open-test-3.log") 103 defer func() { 104 _ = os.Remove(tempFileName1) 105 _ = os.Remove(tempFileName2) 106 _ = os.Remove(tempFileName3) 107 }() 108 109 makePath := func(s string) string { 110 return filepath.Join(os.TempDir(), s) 111 } 112 113 tests := []struct { 114 name string 115 path string 116 exclude string 117 err bool 118 }{ 119 { 120 name: "match without exclude", 121 path: makePath("*-web_log-open-test-[1-3].log"), 122 }, 123 { 124 name: "match with exclude", 125 path: makePath("*-web_log-open-test-[1-3].log"), 126 exclude: makePath("*-web_log-open-test-[2-3].log"), 127 }, 128 { 129 name: "exclude everything", 130 path: makePath("*-web_log-open-test-[1-3].log"), 131 exclude: makePath("*"), 132 err: true, 133 }, 134 { 135 name: "no match", 136 path: makePath("*-web_log-no-match-test-[1-3].log"), 137 err: true, 138 }, 139 { 140 name: "bad path pattern", 141 path: "[qw", 142 err: true, 143 }, 144 { 145 name: "bad exclude path pattern", 146 path: "[qw", 147 err: true, 148 }, 149 } 150 151 for _, tt := range tests { 152 t.Run(tt.name, func(t *testing.T) { 153 r, err := Open(tt.path, tt.exclude, nil) 154 155 if tt.err { 156 assert.Error(t, err) 157 } else { 158 assert.NoError(t, err) 159 assert.NotNil(t, r.file) 160 _ = r.Close() 161 } 162 }) 163 } 164 } 165 166 func TestReader_CurrentFilename(t *testing.T) { 167 reader, teardown := prepareTestReader(t) 168 defer teardown() 169 170 assert.Equal(t, reader.file.Name(), reader.CurrentFilename()) 171 } 172 173 type testReader struct { 174 *bufio.Reader 175 } 176 177 func (r *testReader) readUntilEOF() (n int, err error) { 178 for { 179 _, err = r.ReadBytes('\n') 180 if err != nil { 181 break 182 } 183 n++ 184 } 185 return n, err 186 } 187 188 func (r *testReader) readUntilEOFTimes(times int) (sum int, err error) { 189 var n int 190 for i := 0; i < times; i++ { 191 n, err = r.readUntilEOF() 192 if err != io.EOF { 193 break 194 } 195 sum += n 196 } 197 return sum, err 198 } 199 200 func prepareTempFile(t *testing.T, pattern string) string { 201 t.Helper() 202 f, err := os.CreateTemp("", pattern) 203 require.NoError(t, err) 204 return f.Name() 205 } 206 207 func prepareTestReader(t *testing.T) (reader *Reader, teardown func()) { 208 t.Helper() 209 filename := prepareTempFile(t, "*-web_log-test.log") 210 f, err := os.Open(filename) 211 require.NoError(t, err) 212 213 teardown = func() { 214 _ = os.Remove(filename) 215 _ = reader.file.Close() 216 } 217 reader = &Reader{ 218 file: f, 219 path: filename, 220 } 221 return reader, teardown 222 } 223 224 func rotateFile(t *testing.T, filename string) { 225 t.Helper() 226 require.NoError(t, os.Remove(filename)) 227 f, err := os.Create(filename) 228 require.NoError(t, err) 229 _ = f.Close() 230 } 231 232 func appendLogs(t *testing.T, filename string, interval time.Duration, numOfLogs int) { 233 t.Helper() 234 base := filepath.Base(filename) 235 file, err := os.OpenFile(filename, os.O_RDWR|os.O_APPEND, os.ModeAppend) 236 require.NoError(t, err) 237 require.NotNil(t, file) 238 defer func() { _ = file.Close() }() 239 240 for i := 0; i < numOfLogs; i++ { 241 _, err = fmt.Fprintln(file, "line", i, "filename", base) 242 require.NoError(t, err) 243 time.Sleep(interval) 244 } 245 }