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.

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