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.

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