You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

437 lines
11 KiB

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