pagedstoragefile_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  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 sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != 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. err = psf.FreePage(3)
  133. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse {
  134. t.Error(err)
  135. return
  136. }
  137. sf.ReleaseInUse(record)
  138. if err := psf.FreePage(3); err != nil {
  139. t.Error(err)
  140. return
  141. }
  142. if err := psf.FreePage(3); err != ErrFreePage {
  143. t.Error("Attempting to free a page which is already free should cause an error")
  144. return
  145. }
  146. if psf.First(view.TypeFreePage) != 3 {
  147. t.Error("Unexpected first free page after freeing a page")
  148. return
  149. }
  150. checkPrevAndNext(t, psf, 3, 0, 0)
  151. if psf.FreePage(3) != ErrFreePage {
  152. t.Error("Attempting to free a free page should not be possible")
  153. return
  154. }
  155. if err := psf.FreePage(5); err != nil {
  156. t.Error(err)
  157. return
  158. }
  159. checkPrevAndNext(t, psf, 5, 0, 3)
  160. // Check that the second element has still the prev pointer pointing to 0
  161. checkPrevAndNext(t, psf, 3, 0, 0)
  162. // Check that the pointers for DATA pages are correct
  163. checkPrevAndNext(t, psf, 1, 0, 2)
  164. checkPrevAndNext(t, psf, 2, 1, 4)
  165. checkPrevAndNext(t, psf, 4, 2, 0)
  166. ptr, err := psf.AllocatePage(view.TypeTranslationPage)
  167. if err != nil {
  168. t.Error(err)
  169. return
  170. }
  171. if ptr != 5 {
  172. t.Error("New allocated page should be the last freed page")
  173. return
  174. }
  175. // Check data pointers
  176. checkPrevAndNext(t, psf, 1, 0, 2)
  177. checkPrevAndNext(t, psf, 2, 1, 4)
  178. checkPrevAndNext(t, psf, 4, 2, 0)
  179. // Check free pointers
  180. checkPrevAndNext(t, psf, 3, 0, 0)
  181. // Check translation pointers
  182. checkPrevAndNext(t, psf, 5, 0, 0)
  183. // Check the newly allocated page
  184. record, err = sf.Get(5)
  185. if err != nil {
  186. t.Error(err)
  187. return
  188. }
  189. // Record should have the translation page header
  190. if record.ReadUInt16(0) != 0x1992 {
  191. t.Error("Unexpected page header")
  192. return
  193. }
  194. pv := view.GetPageView(record)
  195. if pv.String() != "PageView: 5 (type:2 previous page:0 next page:0)" {
  196. t.Error("Unexpected pageview was returned:", pv)
  197. return
  198. }
  199. sf.ReleaseInUse(record)
  200. // Test allocation error - Using record 3 causes an error when getting the
  201. // first element of the free list. Using record 5 causes an error when
  202. // inserting the newly allocated record into the list
  203. record, err = sf.Get(3)
  204. if err != nil {
  205. t.Error(err)
  206. return
  207. }
  208. _, err = psf.AllocatePage(view.TypeTranslationPage)
  209. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse {
  210. t.Error(err)
  211. return
  212. }
  213. sf.ReleaseInUse(record)
  214. record, err = sf.Get(5)
  215. if err != nil {
  216. t.Error(err)
  217. return
  218. }
  219. _, err = psf.AllocatePage(view.TypeTranslationPage)
  220. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse {
  221. t.Error(err)
  222. return
  223. }
  224. sf.ReleaseInUse(record)
  225. _, err = psf.AllocatePage(view.TypeTranslationPage)
  226. if err != nil {
  227. t.Error(err)
  228. return
  229. }
  230. record, err = sf.Get(7)
  231. if err != nil {
  232. t.Error(err)
  233. return
  234. }
  235. _, err = psf.AllocatePage(view.TypeTranslationPage)
  236. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse {
  237. t.Error(err)
  238. }
  239. sf.ReleaseInUse(record)
  240. // Check data pointers
  241. checkPrevAndNext(t, psf, 1, 0, 2)
  242. checkPrevAndNext(t, psf, 2, 1, 4)
  243. checkPrevAndNext(t, psf, 4, 2, 0)
  244. // Check error case next record is in use
  245. record, err = sf.Get(4)
  246. if err != nil {
  247. t.Error(err)
  248. return
  249. }
  250. // Check we can't get Prev info when record is in use
  251. _, err = psf.Prev(4)
  252. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse {
  253. t.Error(err)
  254. return
  255. }
  256. err = psf.FreePage(2)
  257. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse {
  258. t.Error(err)
  259. return
  260. }
  261. sf.ReleaseInUse(record)
  262. // Check error case previous record is in use
  263. record, err = sf.Get(2)
  264. if err != nil {
  265. t.Error(err)
  266. return
  267. }
  268. err = psf.FreePage(4)
  269. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse {
  270. t.Error(err)
  271. return
  272. }
  273. sf.ReleaseInUse(record)
  274. if err := psf.FreePage(1); err != nil {
  275. t.Error(err)
  276. return
  277. }
  278. record, err = sf.Get(2)
  279. if err != nil {
  280. t.Error(err)
  281. return
  282. }
  283. err = psf.Close()
  284. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrInUse {
  285. t.Error(err)
  286. return
  287. }
  288. sf.ReleaseInUse(record)
  289. if err := psf.Close(); err != nil {
  290. t.Error(err)
  291. return
  292. }
  293. }
  294. func TestPagedStorageFileTransactionPageManagement(t *testing.T) {
  295. sf, err := file.NewDefaultStorageFile(DBDIR+"/test3", false)
  296. if err != nil {
  297. t.Error(err.Error())
  298. return
  299. }
  300. psf, err := NewPagedStorageFile(sf)
  301. if err != nil {
  302. t.Error(err)
  303. return
  304. }
  305. if err := psf.Rollback(); err != nil {
  306. t.Error(err)
  307. return
  308. }
  309. plist := make([]uint64, 0, 5)
  310. for i := 0; i < 5; i++ {
  311. p, err := psf.AllocatePage(view.TypeDataPage)
  312. if err != nil {
  313. t.Error(err)
  314. }
  315. plist = append(plist, p)
  316. }
  317. if err := psf.Flush(); err != nil {
  318. t.Error(err)
  319. return
  320. }
  321. // Check that the pointers for DATA pages are correct
  322. checkPrevAndNext(t, psf, 1, 0, 2)
  323. checkPrevAndNext(t, psf, 2, 1, 3)
  324. checkPrevAndNext(t, psf, 3, 2, 4)
  325. checkPrevAndNext(t, psf, 4, 3, 5)
  326. checkPrevAndNext(t, psf, 5, 4, 0)
  327. // Now break it in a way that the datastructure is broken
  328. record, err := sf.Get(2)
  329. if err != nil {
  330. t.Error(err)
  331. return
  332. }
  333. err = psf.FreePage(3)
  334. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse {
  335. t.Error(err)
  336. return
  337. }
  338. sf.ReleaseInUse(record)
  339. // At this point page 3 is marked as free but the data pointers
  340. // of page 2 and 4 have not been updated
  341. checkPrevAndNext(t, psf, 1, 0, 2)
  342. checkPrevAndNext(t, psf, 2, 1, 3)
  343. checkPrevAndNext(t, psf, 3, 0, 0)
  344. checkPrevAndNext(t, psf, 4, 3, 5)
  345. checkPrevAndNext(t, psf, 5, 4, 0)
  346. if err := psf.Rollback(); err != nil {
  347. t.Error(err)
  348. return
  349. }
  350. checkPrevAndNext(t, psf, 1, 0, 2)
  351. checkPrevAndNext(t, psf, 2, 1, 3)
  352. checkPrevAndNext(t, psf, 3, 2, 4)
  353. checkPrevAndNext(t, psf, 4, 3, 5)
  354. checkPrevAndNext(t, psf, 5, 4, 0)
  355. if err := psf.Close(); err != nil {
  356. t.Error(err)
  357. return
  358. }
  359. // Test error cases
  360. sf, err = file.NewDefaultStorageFile(DBDIR+"/test3-1", false)
  361. if err != nil {
  362. t.Error(err.Error())
  363. return
  364. }
  365. psf, err = NewPagedStorageFile(sf)
  366. if err != nil {
  367. t.Error(err)
  368. return
  369. }
  370. record, err = sf.Get(4)
  371. err = psf.Flush()
  372. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrInUse {
  373. t.Error(err)
  374. return
  375. }
  376. sf.ReleaseInUse(record)
  377. psf.header.record = nil
  378. err = psf.Flush()
  379. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrInUse {
  380. t.Error(err)
  381. return
  382. }
  383. sf.ReleaseInUseID(0, false)
  384. record, err = sf.Get(4)
  385. err = psf.Rollback()
  386. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrInUse {
  387. t.Error(err)
  388. return
  389. }
  390. sf.ReleaseInUse(record)
  391. psf.header.record = nil
  392. sf.ReleaseInUseID(0, false)
  393. if err := psf.Close(); err != nil {
  394. t.Error(err)
  395. return
  396. }
  397. }
  398. func checkPrevAndNext(t *testing.T, psf *PagedStorageFile, rid uint64,
  399. prev uint64, next uint64) {
  400. p, err := psf.Prev(rid)
  401. if err != nil {
  402. t.Error(err)
  403. return
  404. }
  405. if p != prev {
  406. t.Error("Unexpected previous pointer:", p, "expected:", prev)
  407. return
  408. }
  409. n, err := psf.Next(rid)
  410. if err != nil {
  411. t.Error(err)
  412. return
  413. }
  414. if n != next {
  415. t.Error("Unexpected next pointer:", n, "expected:", next)
  416. return
  417. }
  418. }