pagedstoragefile_test.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. /*
  2. * EliasDB
  3. *
  4. * Copyright 2016 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. */
  10. package paging
  11. import (
  12. "flag"
  13. "fmt"
  14. "os"
  15. "testing"
  16. "devt.de/krotik/common/fileutil"
  17. "devt.de/krotik/eliasdb/storage/file"
  18. "devt.de/krotik/eliasdb/storage/paging/view"
  19. )
  20. const DBDIR = "pagingtest"
  21. const InvalidFileName = "**" + "\x00"
  22. // Main function for all tests in this package
  23. func TestMain(m *testing.M) {
  24. flag.Parse()
  25. // Setup
  26. if res, _ := fileutil.PathExists(DBDIR); res {
  27. os.RemoveAll(DBDIR)
  28. }
  29. err := os.Mkdir(DBDIR, 0770)
  30. if err != nil {
  31. fmt.Print("Could not create test directory:", err.Error())
  32. os.Exit(1)
  33. }
  34. // Run the tests
  35. res := m.Run()
  36. // Teardown
  37. err = os.RemoveAll(DBDIR)
  38. if err != nil {
  39. fmt.Print("Could not remove test directory:", err.Error())
  40. }
  41. os.Exit(res)
  42. }
  43. func TestPagedStorageFileInitialisation(t *testing.T) {
  44. sf, err := file.NewDefaultStorageFile(DBDIR+"/test1", true)
  45. if err != nil {
  46. t.Error(err.Error())
  47. return
  48. }
  49. record, err := sf.Get(0)
  50. if err != nil {
  51. t.Error(err)
  52. return
  53. }
  54. _, err = NewPagedStorageFile(sf)
  55. if err != file.ErrAlreadyInUse {
  56. t.Error("Init of PageStorageFile should fail if header record is not available")
  57. return
  58. }
  59. sf.ReleaseInUse(record)
  60. psf, err := NewPagedStorageFile(sf)
  61. if err != nil {
  62. t.Error(err)
  63. return
  64. }
  65. if psf.StorageFile() != sf {
  66. t.Error("Unexpected StorageFile contained in PagedStorageFile")
  67. return
  68. }
  69. if psf.Header().record != record {
  70. t.Error("Unexpected Record contained in PagedStorageFileHeader")
  71. return
  72. }
  73. if err := psf.Close(); err != nil {
  74. t.Error(err)
  75. return
  76. }
  77. }
  78. func TestPagedStorageFilePageManagement(t *testing.T) {
  79. sf, err := file.NewDefaultStorageFile(DBDIR+"/test2", true)
  80. if err != nil {
  81. t.Error(err.Error())
  82. return
  83. }
  84. psf, err := NewPagedStorageFile(sf)
  85. if err != nil {
  86. t.Error(err)
  87. return
  88. }
  89. if psf.FreePage(0) != ErrHeader {
  90. t.Error("Attempting to free the header record should cause a specific error")
  91. return
  92. }
  93. if _, err := psf.AllocatePage(view.TypeFreePage); err != ErrFreePage {
  94. t.Error("It should not be possible to allocate a free page")
  95. return
  96. }
  97. plist := make([]uint64, 0, 5)
  98. for i := 0; i < 5; i++ {
  99. p, err := psf.AllocatePage(view.TypeDataPage)
  100. if err != nil {
  101. t.Error(err)
  102. }
  103. plist = append(plist, p)
  104. }
  105. record, err := sf.Get(3)
  106. if err != nil {
  107. t.Error(err)
  108. return
  109. }
  110. if record.ReadUInt16(0) != 0x1991 {
  111. t.Error("Unexpected page header")
  112. return
  113. }
  114. sf.ReleaseInUse(record)
  115. if psf.First(view.TypeDataPage) != plist[0] {
  116. t.Error("Unexpected first page")
  117. return
  118. }
  119. if psf.Last(view.TypeDataPage) != plist[len(plist)-1] {
  120. t.Error("Unexpected last page")
  121. return
  122. }
  123. if psf.First(view.TypeFreePage) != 0 {
  124. t.Error("Unexpected first free page - no free pages should be available")
  125. return
  126. }
  127. record, err = sf.Get(3)
  128. if err != nil {
  129. t.Error(err)
  130. return
  131. }
  132. if err := psf.FreePage(3); err != file.ErrAlreadyInUse {
  133. t.Error(err)
  134. return
  135. }
  136. sf.ReleaseInUse(record)
  137. if err := psf.FreePage(3); err != nil {
  138. t.Error(err)
  139. return
  140. }
  141. if err := psf.FreePage(3); err != ErrFreePage {
  142. t.Error("Attempting to free a page which is already free should cause an error")
  143. return
  144. }
  145. if psf.First(view.TypeFreePage) != 3 {
  146. t.Error("Unexpected first free page after freeing a page")
  147. return
  148. }
  149. checkPrevAndNext(t, psf, 3, 0, 0)
  150. if psf.FreePage(3) != ErrFreePage {
  151. t.Error("Attempting to free a free page should not be possible")
  152. return
  153. }
  154. if err := psf.FreePage(5); err != nil {
  155. t.Error(err)
  156. return
  157. }
  158. checkPrevAndNext(t, psf, 5, 0, 3)
  159. // Check that the second element has still the prev pointer pointing to 0
  160. checkPrevAndNext(t, psf, 3, 0, 0)
  161. // Check that the pointers for DATA pages are correct
  162. checkPrevAndNext(t, psf, 1, 0, 2)
  163. checkPrevAndNext(t, psf, 2, 1, 4)
  164. checkPrevAndNext(t, psf, 4, 2, 0)
  165. ptr, err := psf.AllocatePage(view.TypeTranslationPage)
  166. if err != nil {
  167. t.Error(err)
  168. return
  169. }
  170. if ptr != 5 {
  171. t.Error("New allocated page should be the last freed page")
  172. return
  173. }
  174. // Check data pointers
  175. checkPrevAndNext(t, psf, 1, 0, 2)
  176. checkPrevAndNext(t, psf, 2, 1, 4)
  177. checkPrevAndNext(t, psf, 4, 2, 0)
  178. // Check free pointers
  179. checkPrevAndNext(t, psf, 3, 0, 0)
  180. // Check translation pointers
  181. checkPrevAndNext(t, psf, 5, 0, 0)
  182. // Check the newly allocated page
  183. record, err = sf.Get(5)
  184. if err != nil {
  185. t.Error(err)
  186. return
  187. }
  188. // Record should have the translation page header
  189. if record.ReadUInt16(0) != 0x1992 {
  190. t.Error("Unexpected page header")
  191. return
  192. }
  193. pv := view.GetPageView(record)
  194. if pv.String() != "PageView: 5 (type:2 previous page:0 next page:0)" {
  195. t.Error("Unexpected pageview was returned:", pv)
  196. return
  197. }
  198. sf.ReleaseInUse(record)
  199. // Test allocation error - Using record 3 causes an error when getting the
  200. // first element of the free list. Using record 5 causes an error when
  201. // inserting the newly allocated record into the list
  202. record, err = sf.Get(3)
  203. if err != nil {
  204. t.Error(err)
  205. return
  206. }
  207. _, err = psf.AllocatePage(view.TypeTranslationPage)
  208. if err != file.ErrAlreadyInUse {
  209. t.Error(err)
  210. return
  211. }
  212. sf.ReleaseInUse(record)
  213. record, err = sf.Get(5)
  214. if err != nil {
  215. t.Error(err)
  216. return
  217. }
  218. _, err = psf.AllocatePage(view.TypeTranslationPage)
  219. if err != file.ErrAlreadyInUse {
  220. t.Error(err)
  221. return
  222. }
  223. sf.ReleaseInUse(record)
  224. _, err = psf.AllocatePage(view.TypeTranslationPage)
  225. if err != nil {
  226. t.Error(err)
  227. return
  228. }
  229. record, err = sf.Get(7)
  230. if err != nil {
  231. t.Error(err)
  232. return
  233. }
  234. _, err = psf.AllocatePage(view.TypeTranslationPage)
  235. if err != file.ErrAlreadyInUse {
  236. t.Error(err)
  237. }
  238. sf.ReleaseInUse(record)
  239. // Check data pointers
  240. checkPrevAndNext(t, psf, 1, 0, 2)
  241. checkPrevAndNext(t, psf, 2, 1, 4)
  242. checkPrevAndNext(t, psf, 4, 2, 0)
  243. // Check error case next record is in use
  244. record, err = sf.Get(4)
  245. if err != nil {
  246. t.Error(err)
  247. return
  248. }
  249. // Check we can't get Prev info when record is in use
  250. if _, err := psf.Prev(4); err != file.ErrAlreadyInUse {
  251. t.Error(err)
  252. return
  253. }
  254. if err := psf.FreePage(2); err != file.ErrAlreadyInUse {
  255. t.Error(err)
  256. return
  257. }
  258. sf.ReleaseInUse(record)
  259. // Check error case previous record is in use
  260. record, err = sf.Get(2)
  261. if err != nil {
  262. t.Error(err)
  263. return
  264. }
  265. if err := psf.FreePage(4); err != file.ErrAlreadyInUse {
  266. t.Error(err)
  267. return
  268. }
  269. sf.ReleaseInUse(record)
  270. if err := psf.FreePage(1); err != nil {
  271. t.Error(err)
  272. return
  273. }
  274. record, err = sf.Get(2)
  275. if err != nil {
  276. t.Error(err)
  277. return
  278. }
  279. if err := psf.Close(); err != file.ErrInUse {
  280. t.Error(err)
  281. return
  282. }
  283. sf.ReleaseInUse(record)
  284. if err := psf.Close(); err != nil {
  285. t.Error(err)
  286. return
  287. }
  288. }
  289. func TestPagedStorageFileTransactionPageManagement(t *testing.T) {
  290. sf, err := file.NewDefaultStorageFile(DBDIR+"/test3", false)
  291. if err != nil {
  292. t.Error(err.Error())
  293. return
  294. }
  295. psf, err := NewPagedStorageFile(sf)
  296. if err != nil {
  297. t.Error(err)
  298. return
  299. }
  300. if err := psf.Rollback(); err != nil {
  301. t.Error(err)
  302. return
  303. }
  304. plist := make([]uint64, 0, 5)
  305. for i := 0; i < 5; i++ {
  306. p, err := psf.AllocatePage(view.TypeDataPage)
  307. if err != nil {
  308. t.Error(err)
  309. }
  310. plist = append(plist, p)
  311. }
  312. if err := psf.Flush(); err != nil {
  313. t.Error(err)
  314. return
  315. }
  316. // Check that the pointers for DATA pages are correct
  317. checkPrevAndNext(t, psf, 1, 0, 2)
  318. checkPrevAndNext(t, psf, 2, 1, 3)
  319. checkPrevAndNext(t, psf, 3, 2, 4)
  320. checkPrevAndNext(t, psf, 4, 3, 5)
  321. checkPrevAndNext(t, psf, 5, 4, 0)
  322. // Now break it in a way that the datastructure is broken
  323. record, err := sf.Get(2)
  324. if err != nil {
  325. t.Error(err)
  326. return
  327. }
  328. if err := psf.FreePage(3); err != file.ErrAlreadyInUse {
  329. t.Error(err)
  330. return
  331. }
  332. sf.ReleaseInUse(record)
  333. // At this point page 3 is marked as free but the data pointers
  334. // of page 2 and 4 have not been updated
  335. checkPrevAndNext(t, psf, 1, 0, 2)
  336. checkPrevAndNext(t, psf, 2, 1, 3)
  337. checkPrevAndNext(t, psf, 3, 0, 0)
  338. checkPrevAndNext(t, psf, 4, 3, 5)
  339. checkPrevAndNext(t, psf, 5, 4, 0)
  340. if err := psf.Rollback(); err != nil {
  341. t.Error(err)
  342. return
  343. }
  344. checkPrevAndNext(t, psf, 1, 0, 2)
  345. checkPrevAndNext(t, psf, 2, 1, 3)
  346. checkPrevAndNext(t, psf, 3, 2, 4)
  347. checkPrevAndNext(t, psf, 4, 3, 5)
  348. checkPrevAndNext(t, psf, 5, 4, 0)
  349. if err := psf.Close(); err != nil {
  350. t.Error(err)
  351. return
  352. }
  353. // Test error cases
  354. sf, err = file.NewDefaultStorageFile(DBDIR+"/test3-1", false)
  355. if err != nil {
  356. t.Error(err.Error())
  357. return
  358. }
  359. psf, err = NewPagedStorageFile(sf)
  360. if err != nil {
  361. t.Error(err)
  362. return
  363. }
  364. record, err = sf.Get(4)
  365. if err := psf.Flush(); err != file.ErrInUse {
  366. t.Error(err)
  367. return
  368. }
  369. sf.ReleaseInUse(record)
  370. psf.header.record = nil
  371. if err := psf.Flush(); err != file.ErrInUse {
  372. t.Error(err)
  373. return
  374. }
  375. sf.ReleaseInUseID(0, false)
  376. record, err = sf.Get(4)
  377. if err := psf.Rollback(); err != file.ErrInUse {
  378. t.Error(err)
  379. return
  380. }
  381. sf.ReleaseInUse(record)
  382. psf.header.record = nil
  383. sf.ReleaseInUseID(0, false)
  384. if err := psf.Close(); err != nil {
  385. t.Error(err)
  386. return
  387. }
  388. }
  389. func checkPrevAndNext(t *testing.T, psf *PagedStorageFile, rid uint64,
  390. prev uint64, next uint64) {
  391. p, err := psf.Prev(rid)
  392. if err != nil {
  393. t.Error(err)
  394. return
  395. }
  396. if p != prev {
  397. t.Error("Unexpected previous pointer:", p, "expected:", prev)
  398. return
  399. }
  400. n, err := psf.Next(rid)
  401. if err != nil {
  402. t.Error(err)
  403. return
  404. }
  405. if n != next {
  406. t.Error("Unexpected next pointer:", n, "expected:", next)
  407. return
  408. }
  409. }