test(page): cover DiskManagerTest, tempFile and PageTest
This commit is contained in:
1 parent
f92883fe98
commit
17a9f92eef
2 files changed
+145
No files matched your search
@@ -0,0 +1,116 @@
|
|||||||
|
package kestrel.store.page
|
||||||
|
|
||||||
|
import java.nio.file.Files
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class DiskManagerTest {
|
||||||
|
private fun tempFile() = Files.createTempFile("kestrel-disk", ".db").toFile().also { it.deleteOnExit() }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun allocatedPagesStartZeroedAndGrowSequentially() {
|
||||||
|
val manager = DiskManager(tempFile())
|
||||||
|
val first = manager.allocatePage()
|
||||||
|
val second = manager.allocatePage()
|
||||||
|
assertEquals(0L, first)
|
||||||
|
assertEquals(1L, second)
|
||||||
|
assertEquals(2L, manager.allocatedPageCount())
|
||||||
|
manager.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun writtenPageContentSurvivesReadBack() {
|
||||||
|
val manager = DiskManager(tempFile())
|
||||||
|
val id = manager.allocatePage()
|
||||||
|
val page = Page(id)
|
||||||
|
page.data[0] = 0x42
|
||||||
|
page.data[Page.PAGE_SIZE - 1] = 0x24
|
||||||
|
manager.writePage(page)
|
||||||
|
val reloaded = manager.readPage(id)
|
||||||
|
assertEquals(0x42, reloaded.data[0])
|
||||||
|
assertEquals(0x24, reloaded.data[Page.PAGE_SIZE - 1])
|
||||||
|
manager.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun stateSurvivesCloseAndReopen() {
|
||||||
|
val file = tempFile()
|
||||||
|
val manager = DiskManager(file)
|
||||||
|
val id = manager.allocatePage()
|
||||||
|
val page = Page(id)
|
||||||
|
page.data.fill(7)
|
||||||
|
manager.writePage(page)
|
||||||
|
manager.close()
|
||||||
|
|
||||||
|
val reopened = DiskManager(file)
|
||||||
|
assertEquals(1L, reopened.allocatedPageCount())
|
||||||
|
val reloaded = reopened.readPage(id)
|
||||||
|
assertTrue(reloaded.data.all { it == 7.toByte() })
|
||||||
|
reopened.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun freedPagesAreRecycledBeforeGrowingTheFile() {
|
||||||
|
val manager = DiskManager(tempFile())
|
||||||
|
val a = manager.allocatePage()
|
||||||
|
val b = manager.allocatePage()
|
||||||
|
manager.freePage(a)
|
||||||
|
val reused = manager.allocatePage()
|
||||||
|
assertEquals(a, reused)
|
||||||
|
assertEquals(2L, manager.allocatedPageCount())
|
||||||
|
val fresh = manager.allocatePage()
|
||||||
|
assertEquals(2L, fresh)
|
||||||
|
assertTrue(b != reused)
|
||||||
|
manager.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun freeListSurvivesReopenAndPopsMostRecentlyFreedFirst() {
|
||||||
|
val file = tempFile()
|
||||||
|
val manager = DiskManager(file)
|
||||||
|
val a = manager.allocatePage()
|
||||||
|
val b = manager.allocatePage()
|
||||||
|
manager.freePage(a)
|
||||||
|
manager.freePage(b)
|
||||||
|
manager.close()
|
||||||
|
|
||||||
|
val reopened = DiskManager(file)
|
||||||
|
assertEquals(b, reopened.allocatePage())
|
||||||
|
assertEquals(a, reopened.allocatePage())
|
||||||
|
reopened.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun corruptedPageBytesAreDetectedOnRead() {
|
||||||
|
val file = tempFile()
|
||||||
|
val manager = DiskManager(file)
|
||||||
|
val id = manager.allocatePage()
|
||||||
|
val page = Page(id)
|
||||||
|
page.data.fill(9)
|
||||||
|
manager.writePage(page)
|
||||||
|
manager.close()
|
||||||
|
|
||||||
|
// Flip a byte inside the payload region of the physical slot for `id`, after the header slot and checksum.
|
||||||
|
val slotSize = 4 + Page.PAGE_SIZE
|
||||||
|
val offset = (id + 1) * slotSize + 4 + 10
|
||||||
|
val raf = java.io.RandomAccessFile(file, "rw")
|
||||||
|
raf.seek(offset)
|
||||||
|
val original = raf.read()
|
||||||
|
raf.seek(offset)
|
||||||
|
raf.writeByte((original xor 0xFF) and 0xFF)
|
||||||
|
raf.close()
|
||||||
|
|
||||||
|
val reopened = DiskManager(file)
|
||||||
|
assertFailsWith<CorruptPageException> { reopened.readPage(id) }
|
||||||
|
reopened.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun readingUnallocatedPageIsRejected() {
|
||||||
|
val manager = DiskManager(tempFile())
|
||||||
|
assertFailsWith<IllegalArgumentException> { manager.readPage(0) }
|
||||||
|
manager.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package kestrel.store.page
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class PageTest {
|
||||||
|
@Test
|
||||||
|
fun freshPageIsZeroedAndClean() {
|
||||||
|
val page = Page(3)
|
||||||
|
assertEquals(3, page.id)
|
||||||
|
assertEquals(Page.PAGE_SIZE, page.data.size)
|
||||||
|
assertTrue(page.data.all { it == 0.toByte() })
|
||||||
|
assertFalse(page.isDirty)
|
||||||
|
assertEquals(0, page.pinCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun resetClearsDataAndDirtyFlagButKeepsId() {
|
||||||
|
val page = Page(1)
|
||||||
|
page.data[0] = 5
|
||||||
|
page.isDirty = true
|
||||||
|
page.reset()
|
||||||
|
assertTrue(page.data.all { it == 0.toByte() })
|
||||||
|
assertFalse(page.isDirty)
|
||||||
|
assertEquals(1, page.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user