OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / tendermint / tmlibs / autofile / group_test.go
1 package autofile
2
3 import (
4         "errors"
5         "fmt"
6         "io"
7         "io/ioutil"
8         "os"
9         "strconv"
10         "strings"
11         "testing"
12
13         "github.com/stretchr/testify/assert"
14         "github.com/stretchr/testify/require"
15
16         cmn "github.com/tendermint/tmlibs/common"
17 )
18
19 // NOTE: Returned group has ticker stopped
20 func createTestGroup(t *testing.T, headSizeLimit int64) *Group {
21         testID := cmn.RandStr(12)
22         testDir := "_test_" + testID
23         err := cmn.EnsureDir(testDir, 0700)
24         require.NoError(t, err, "Error creating dir")
25         headPath := testDir + "/myfile"
26         g, err := OpenGroup(headPath)
27         require.NoError(t, err, "Error opening Group")
28         g.SetHeadSizeLimit(headSizeLimit)
29         g.stopTicker()
30         require.NotEqual(t, nil, g, "Failed to create Group")
31         return g
32 }
33
34 func destroyTestGroup(t *testing.T, g *Group) {
35         err := os.RemoveAll(g.Dir)
36         require.NoError(t, err, "Error removing test Group directory")
37 }
38
39 func assertGroupInfo(t *testing.T, gInfo GroupInfo, minIndex, maxIndex int, totalSize, headSize int64) {
40         assert.Equal(t, minIndex, gInfo.MinIndex)
41         assert.Equal(t, maxIndex, gInfo.MaxIndex)
42         assert.Equal(t, totalSize, gInfo.TotalSize)
43         assert.Equal(t, headSize, gInfo.HeadSize)
44 }
45
46 func TestCheckHeadSizeLimit(t *testing.T) {
47         g := createTestGroup(t, 1000*1000)
48
49         // At first, there are no files.
50         assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 0, 0)
51
52         // Write 1000 bytes 999 times.
53         for i := 0; i < 999; i++ {
54                 err := g.WriteLine(cmn.RandStr(999))
55                 require.NoError(t, err, "Error appending to head")
56         }
57         g.Flush()
58         assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 999000, 999000)
59
60         // Even calling checkHeadSizeLimit manually won't rotate it.
61         g.checkHeadSizeLimit()
62         assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 999000, 999000)
63
64         // Write 1000 more bytes.
65         err := g.WriteLine(cmn.RandStr(999))
66         require.NoError(t, err, "Error appending to head")
67         g.Flush()
68
69         // Calling checkHeadSizeLimit this time rolls it.
70         g.checkHeadSizeLimit()
71         assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 1000000, 0)
72
73         // Write 1000 more bytes.
74         err = g.WriteLine(cmn.RandStr(999))
75         require.NoError(t, err, "Error appending to head")
76         g.Flush()
77
78         // Calling checkHeadSizeLimit does nothing.
79         g.checkHeadSizeLimit()
80         assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 1001000, 1000)
81
82         // Write 1000 bytes 999 times.
83         for i := 0; i < 999; i++ {
84                 err = g.WriteLine(cmn.RandStr(999))
85                 require.NoError(t, err, "Error appending to head")
86         }
87         g.Flush()
88         assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 2000000, 1000000)
89
90         // Calling checkHeadSizeLimit rolls it again.
91         g.checkHeadSizeLimit()
92         assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2000000, 0)
93
94         // Write 1000 more bytes.
95         _, err = g.Head.Write([]byte(cmn.RandStr(999) + "\n"))
96         require.NoError(t, err, "Error appending to head")
97         g.Flush()
98         assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2001000, 1000)
99
100         // Calling checkHeadSizeLimit does nothing.
101         g.checkHeadSizeLimit()
102         assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2001000, 1000)
103
104         // Cleanup
105         destroyTestGroup(t, g)
106 }
107
108 func TestSearch(t *testing.T) {
109         g := createTestGroup(t, 10*1000)
110
111         // Create some files in the group that have several INFO lines in them.
112         // Try to put the INFO lines in various spots.
113         for i := 0; i < 100; i++ {
114                 // The random junk at the end ensures that this INFO linen
115                 // is equally likely to show up at the end.
116                 _, err := g.Head.Write([]byte(fmt.Sprintf("INFO %v %v\n", i, cmn.RandStr(123))))
117                 require.NoError(t, err, "Failed to write to head")
118                 g.checkHeadSizeLimit()
119                 for j := 0; j < 10; j++ {
120                         _, err1 := g.Head.Write([]byte(cmn.RandStr(123) + "\n"))
121                         require.NoError(t, err1, "Failed to write to head")
122                         g.checkHeadSizeLimit()
123                 }
124         }
125
126         // Create a search func that searches for line
127         makeSearchFunc := func(target int) SearchFunc {
128                 return func(line string) (int, error) {
129                         parts := strings.Split(line, " ")
130                         if len(parts) != 3 {
131                                 return -1, errors.New("Line did not have 3 parts")
132                         }
133                         i, err := strconv.Atoi(parts[1])
134                         if err != nil {
135                                 return -1, errors.New("Failed to parse INFO: " + err.Error())
136                         }
137                         if target < i {
138                                 return 1, nil
139                         } else if target == i {
140                                 return 0, nil
141                         } else {
142                                 return -1, nil
143                         }
144                 }
145         }
146
147         // Now search for each number
148         for i := 0; i < 100; i++ {
149                 t.Log("Testing for i", i)
150                 gr, match, err := g.Search("INFO", makeSearchFunc(i))
151                 require.NoError(t, err, "Failed to search for line")
152                 assert.True(t, match, "Expected Search to return exact match")
153                 line, err := gr.ReadLine()
154                 require.NoError(t, err, "Failed to read line after search")
155                 if !strings.HasPrefix(line, fmt.Sprintf("INFO %v ", i)) {
156                         t.Fatal("Failed to get correct line")
157                 }
158                 // Make sure we can continue to read from there.
159                 cur := i + 1
160                 for {
161                         line, err := gr.ReadLine()
162                         if err == io.EOF {
163                                 if cur == 99+1 {
164                                         // OK!
165                                         break
166                                 } else {
167                                         t.Fatal("Got EOF after the wrong INFO #")
168                                 }
169                         } else if err != nil {
170                                 t.Fatal("Error reading line", err)
171                         }
172                         if !strings.HasPrefix(line, "INFO ") {
173                                 continue
174                         }
175                         if !strings.HasPrefix(line, fmt.Sprintf("INFO %v ", cur)) {
176                                 t.Fatalf("Unexpected INFO #. Expected %v got:\n%v", cur, line)
177                         }
178                         cur += 1
179                 }
180                 gr.Close()
181         }
182
183         // Now search for something that is too small.
184         // We should get the first available line.
185         {
186                 gr, match, err := g.Search("INFO", makeSearchFunc(-999))
187                 require.NoError(t, err, "Failed to search for line")
188                 assert.False(t, match, "Expected Search to not return exact match")
189                 line, err := gr.ReadLine()
190                 require.NoError(t, err, "Failed to read line after search")
191                 if !strings.HasPrefix(line, "INFO 0 ") {
192                         t.Error("Failed to fetch correct line, which is the earliest INFO")
193                 }
194                 err = gr.Close()
195                 require.NoError(t, err, "Failed to close GroupReader")
196         }
197
198         // Now search for something that is too large.
199         // We should get an EOF error.
200         {
201                 gr, _, err := g.Search("INFO", makeSearchFunc(999))
202                 assert.Equal(t, io.EOF, err)
203                 assert.Nil(t, gr)
204         }
205
206         // Cleanup
207         destroyTestGroup(t, g)
208 }
209
210 func TestRotateFile(t *testing.T) {
211         g := createTestGroup(t, 0)
212         g.WriteLine("Line 1")
213         g.WriteLine("Line 2")
214         g.WriteLine("Line 3")
215         g.Flush()
216         g.RotateFile()
217         g.WriteLine("Line 4")
218         g.WriteLine("Line 5")
219         g.WriteLine("Line 6")
220         g.Flush()
221
222         // Read g.Head.Path+"000"
223         body1, err := ioutil.ReadFile(g.Head.Path + ".000")
224         assert.NoError(t, err, "Failed to read first rolled file")
225         if string(body1) != "Line 1\nLine 2\nLine 3\n" {
226                 t.Errorf("Got unexpected contents: [%v]", string(body1))
227         }
228
229         // Read g.Head.Path
230         body2, err := ioutil.ReadFile(g.Head.Path)
231         assert.NoError(t, err, "Failed to read first rolled file")
232         if string(body2) != "Line 4\nLine 5\nLine 6\n" {
233                 t.Errorf("Got unexpected contents: [%v]", string(body2))
234         }
235
236         // Cleanup
237         destroyTestGroup(t, g)
238 }
239
240 func TestFindLast1(t *testing.T) {
241         g := createTestGroup(t, 0)
242
243         g.WriteLine("Line 1")
244         g.WriteLine("Line 2")
245         g.WriteLine("# a")
246         g.WriteLine("Line 3")
247         g.Flush()
248         g.RotateFile()
249         g.WriteLine("Line 4")
250         g.WriteLine("Line 5")
251         g.WriteLine("Line 6")
252         g.WriteLine("# b")
253         g.Flush()
254
255         match, found, err := g.FindLast("#")
256         assert.NoError(t, err)
257         assert.True(t, found)
258         assert.Equal(t, "# b", match)
259
260         // Cleanup
261         destroyTestGroup(t, g)
262 }
263
264 func TestFindLast2(t *testing.T) {
265         g := createTestGroup(t, 0)
266
267         g.WriteLine("Line 1")
268         g.WriteLine("Line 2")
269         g.WriteLine("Line 3")
270         g.Flush()
271         g.RotateFile()
272         g.WriteLine("# a")
273         g.WriteLine("Line 4")
274         g.WriteLine("Line 5")
275         g.WriteLine("# b")
276         g.WriteLine("Line 6")
277         g.Flush()
278
279         match, found, err := g.FindLast("#")
280         assert.NoError(t, err)
281         assert.True(t, found)
282         assert.Equal(t, "# b", match)
283
284         // Cleanup
285         destroyTestGroup(t, g)
286 }
287
288 func TestFindLast3(t *testing.T) {
289         g := createTestGroup(t, 0)
290
291         g.WriteLine("Line 1")
292         g.WriteLine("# a")
293         g.WriteLine("Line 2")
294         g.WriteLine("# b")
295         g.WriteLine("Line 3")
296         g.Flush()
297         g.RotateFile()
298         g.WriteLine("Line 4")
299         g.WriteLine("Line 5")
300         g.WriteLine("Line 6")
301         g.Flush()
302
303         match, found, err := g.FindLast("#")
304         assert.NoError(t, err)
305         assert.True(t, found)
306         assert.Equal(t, "# b", match)
307
308         // Cleanup
309         destroyTestGroup(t, g)
310 }
311
312 func TestFindLast4(t *testing.T) {
313         g := createTestGroup(t, 0)
314
315         g.WriteLine("Line 1")
316         g.WriteLine("Line 2")
317         g.WriteLine("Line 3")
318         g.Flush()
319         g.RotateFile()
320         g.WriteLine("Line 4")
321         g.WriteLine("Line 5")
322         g.WriteLine("Line 6")
323         g.Flush()
324
325         match, found, err := g.FindLast("#")
326         assert.NoError(t, err)
327         assert.False(t, found)
328         assert.Empty(t, match)
329
330         // Cleanup
331         destroyTestGroup(t, g)
332 }
333
334 func TestWrite(t *testing.T) {
335         g := createTestGroup(t, 0)
336
337         written := []byte("Medusa")
338         g.Write(written)
339         g.Flush()
340
341         read := make([]byte, len(written))
342         gr, err := g.NewReader(0)
343         require.NoError(t, err, "failed to create reader")
344
345         _, err = gr.Read(read)
346         assert.NoError(t, err, "failed to read data")
347         assert.Equal(t, written, read)
348
349         // Cleanup
350         destroyTestGroup(t, g)
351 }
352
353 // test that Read reads the required amount of bytes from all the files in the
354 // group and returns no error if n == size of the given slice.
355 func TestGroupReaderRead(t *testing.T) {
356         g := createTestGroup(t, 0)
357
358         professor := []byte("Professor Monster")
359         g.Write(professor)
360         g.Flush()
361         g.RotateFile()
362         frankenstein := []byte("Frankenstein's Monster")
363         g.Write(frankenstein)
364         g.Flush()
365
366         totalWrittenLength := len(professor) + len(frankenstein)
367         read := make([]byte, totalWrittenLength)
368         gr, err := g.NewReader(0)
369         require.NoError(t, err, "failed to create reader")
370
371         n, err := gr.Read(read)
372         assert.NoError(t, err, "failed to read data")
373         assert.Equal(t, totalWrittenLength, n, "not enough bytes read")
374         professorPlusFrankenstein := professor
375         professorPlusFrankenstein = append(professorPlusFrankenstein, frankenstein...)
376         assert.Equal(t, professorPlusFrankenstein, read)
377
378         // Cleanup
379         destroyTestGroup(t, g)
380 }
381
382 // test that Read returns an error if number of bytes read < size of
383 // the given slice. Subsequent call should return 0, io.EOF.
384 func TestGroupReaderRead2(t *testing.T) {
385         g := createTestGroup(t, 0)
386
387         professor := []byte("Professor Monster")
388         g.Write(professor)
389         g.Flush()
390         g.RotateFile()
391         frankenstein := []byte("Frankenstein's Monster")
392         frankensteinPart := []byte("Frankenstein")
393         g.Write(frankensteinPart) // note writing only a part
394         g.Flush()
395
396         totalLength := len(professor) + len(frankenstein)
397         read := make([]byte, totalLength)
398         gr, err := g.NewReader(0)
399         require.NoError(t, err, "failed to create reader")
400
401         // 1) n < (size of the given slice), io.EOF
402         n, err := gr.Read(read)
403         assert.Equal(t, io.EOF, err)
404         assert.Equal(t, len(professor)+len(frankensteinPart), n, "Read more/less bytes than it is in the group")
405
406         // 2) 0, io.EOF
407         n, err = gr.Read([]byte("0"))
408         assert.Equal(t, io.EOF, err)
409         assert.Equal(t, 0, n)
410
411         // Cleanup
412         destroyTestGroup(t, g)
413 }
414
415 func TestMinIndex(t *testing.T) {
416         g := createTestGroup(t, 0)
417
418         assert.Zero(t, g.MinIndex(), "MinIndex should be zero at the beginning")
419
420         // Cleanup
421         destroyTestGroup(t, g)
422 }
423
424 func TestMaxIndex(t *testing.T) {
425         g := createTestGroup(t, 0)
426
427         assert.Zero(t, g.MaxIndex(), "MaxIndex should be zero at the beginning")
428
429         g.WriteLine("Line 1")
430         g.Flush()
431         g.RotateFile()
432
433         assert.Equal(t, 1, g.MaxIndex(), "MaxIndex should point to the last file")
434
435         // Cleanup
436         destroyTestGroup(t, g)
437 }