Files
five/hbrdd/mem/memrdd_test.go
Charles KWON OhJun 542373572f feat: MemRDD — in-memory database engine (Go map-based)
Complete in-memory RDD implementation:
- CRUD: Create, Append, GetValue, PutValue, Delete, Recall
- Navigation: GoTo, GoTop, GoBottom, Skip, BOF, EOF
- Index: CreateIndex (sorted slice + binary search), Seek (exact + soft)
- Bulk: Pack (remove deleted), Zap (clear all)
- Multi-open: shared table across work areas
- Driver registered as "MEMRDD", prefix "mem:"

Tests: 9 tests including 5000-record stress test
  Create, Append/Get/Put, Navigation, Delete/Pack, Zap,
  Index (string + numeric), Seek (exact + soft), Stress 5000, Multi-open

Use cases: temp tables, query results, pivot, caching
Performance: no disk I/O, no byte packing — pure Go slices

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 11:57:48 +09:00

231 lines
5.8 KiB
Go

package mem
import (
"five/hbrdd"
"five/hbrt"
"fmt"
"testing"
)
func createTestTable(t *testing.T) hbrdd.Area {
t.Helper()
drv := &MemDriver{}
area, err := drv.Create(hbrdd.CreateParams{
Path: "mem:test",
Alias: "TEST",
Fields: []hbrdd.FieldInfo{
{Name: "ID", Type: 'N', Len: 5, Dec: 0},
{Name: "NAME", Type: 'C', Len: 20, Dec: 0},
{Name: "SALARY", Type: 'N', Len: 10, Dec: 2},
{Name: "ACTIVE", Type: 'L', Len: 1, Dec: 0},
},
})
if err != nil {
t.Fatal(err)
}
return area
}
func addRecord(t *testing.T, area hbrdd.Area, id int, name string, salary float64, active bool) {
t.Helper()
area.Append()
area.PutValue(0, hbrt.MakeInt(id))
area.PutValue(1, hbrt.MakeString(name))
area.PutValue(2, hbrt.MakeDouble(salary, 10, 2))
area.PutValue(3, hbrt.MakeBool(active))
}
func TestMemRDD_Create(t *testing.T) {
area := createTestTable(t)
defer area.Close()
if area.FieldCount() != 4 { t.Errorf("fields: %d", area.FieldCount()) }
cnt, _ := area.RecCount()
if cnt != 0 { t.Errorf("count: %d", cnt) }
t.Log("Create: OK")
}
func TestMemRDD_AppendGetPut(t *testing.T) {
area := createTestTable(t)
defer area.Close()
addRecord(t, area, 1, "Charles", 15000.50, true)
addRecord(t, area, 2, "John", 8200, false)
cnt, _ := area.RecCount()
if cnt != 2 { t.Fatalf("count: %d", cnt) }
area.GoTo(1)
v, _ := area.GetValue(0)
if v.AsInt() != 1 { t.Errorf("ID: %d", v.AsInt()) }
v, _ = area.GetValue(1)
if v.AsString() != "Charles" { t.Errorf("NAME: %q", v.AsString()) }
v, _ = area.GetValue(3)
if !v.AsBool() { t.Error("ACTIVE: false") }
area.GoTo(2)
v, _ = area.GetValue(1)
if v.AsString() != "John" { t.Errorf("NAME2: %q", v.AsString()) }
t.Log("Append/Get/Put: OK")
}
func TestMemRDD_Navigation(t *testing.T) {
area := createTestTable(t)
defer area.Close()
addRecord(t, area, 1, "A", 100, true)
addRecord(t, area, 2, "B", 200, true)
addRecord(t, area, 3, "C", 300, true)
area.GoTop()
if area.RecNo() != 1 { t.Errorf("GoTop: %d", area.RecNo()) }
area.Skip(1)
if area.RecNo() != 2 { t.Errorf("Skip+1: %d", area.RecNo()) }
area.GoBottom()
if area.RecNo() != 3 { t.Errorf("GoBottom: %d", area.RecNo()) }
area.Skip(1)
if !area.EOF() { t.Error("not EOF") }
area.GoTop()
area.Skip(-1)
if !area.BOF() { t.Error("not BOF") }
t.Log("Navigation: OK")
}
func TestMemRDD_DeletePack(t *testing.T) {
area := createTestTable(t)
defer area.Close()
addRecord(t, area, 1, "Keep", 100, true)
addRecord(t, area, 2, "Del", 200, true)
addRecord(t, area, 3, "Keep2", 300, true)
area.GoTo(2)
area.Delete()
if !area.Deleted() { t.Error("not deleted") }
area.Recall()
if area.Deleted() { t.Error("recall failed") }
area.Delete()
area.Pack()
cnt, _ := area.RecCount()
if cnt != 2 { t.Errorf("pack count: %d", cnt) }
t.Log("Delete/Pack: OK")
}
func TestMemRDD_Zap(t *testing.T) {
area := createTestTable(t)
defer area.Close()
addRecord(t, area, 1, "A", 100, true)
area.Zap()
cnt, _ := area.RecCount()
if cnt != 0 { t.Errorf("zap: %d", cnt) }
t.Log("Zap: OK")
}
func TestMemRDD_Index(t *testing.T) {
area := createTestTable(t)
defer area.Close()
ma := area.(*memArea)
addRecord(t, area, 3, "Charlie", 300, true)
addRecord(t, area, 1, "Alice", 100, true)
addRecord(t, area, 2, "Bob", 200, true)
ma.CreateIndex("NAME", 1, false)
area.GoTop()
v, _ := area.GetValue(1)
if v.AsString() != "Alice" { t.Errorf("top: %q", v.AsString()) }
area.Skip(1)
v, _ = area.GetValue(1)
if v.AsString() != "Bob" { t.Errorf("skip: %q", v.AsString()) }
area.GoBottom()
v, _ = area.GetValue(1)
if v.AsString() != "Charlie" { t.Errorf("bottom: %q", v.AsString()) }
t.Log("Index: OK")
}
func TestMemRDD_Seek(t *testing.T) {
area := createTestTable(t)
defer area.Close()
ma := area.(*memArea)
addRecord(t, area, 1, "Alice", 100, true)
addRecord(t, area, 2, "Bob", 200, true)
addRecord(t, area, 3, "Charlie", 300, true)
addRecord(t, area, 4, "David", 400, true)
ma.CreateIndex("NAME", 1, false)
if !ma.Seek(hbrt.MakeString("Charlie"), false) { t.Error("exact seek failed") }
v, _ := area.GetValue(0)
if v.AsInt() != 3 { t.Errorf("seek Charlie: ID=%d", v.AsInt()) }
ma.Seek(hbrt.MakeString("Bobby"), true)
if area.EOF() { t.Error("soft seek: EOF") }
v, _ = area.GetValue(1)
if v.AsString() != "Charlie" { t.Errorf("soft: %q", v.AsString()) }
if ma.Seek(hbrt.MakeString("Zebra"), false) { t.Error("Zebra found") }
if !area.EOF() { t.Error("miss: not EOF") }
t.Log("Seek: OK")
}
func TestMemRDD_Stress5000(t *testing.T) {
area := createTestTable(t)
defer area.Close()
ma := area.(*memArea)
for i := 1; i <= 5000; i++ {
area.Append()
area.PutValue(0, hbrt.MakeInt(i))
area.PutValue(1, hbrt.MakeString(fmt.Sprintf("Name_%05d", i)))
area.PutValue(2, hbrt.MakeDouble(float64(i)*10.5, 10, 2))
area.PutValue(3, hbrt.MakeBool(i%2 == 0))
}
cnt, _ := area.RecCount()
if cnt != 5000 { t.Fatalf("count: %d", cnt) }
ma.CreateIndex("ID", 0, false)
ma.Seek(hbrt.MakeInt(2500), false)
v, _ := area.GetValue(1)
if v.AsString() != "Name_02500" { t.Errorf("seek 2500: %q", v.AsString()) }
// Full scan
area.GoTop()
n := 0
for !area.EOF() {
n++
area.Skip(1)
}
if n != 5000 { t.Errorf("scan: %d", n) }
t.Logf("Stress 5000: create+index+seek+scan OK")
}
func TestMemRDD_MultiOpen(t *testing.T) {
drv := &MemDriver{}
a1, _ := drv.Create(hbrdd.CreateParams{
Path: "mem:shared", Alias: "A",
Fields: []hbrdd.FieldInfo{{Name: "VAL", Type: 'N', Len: 5}},
})
a1.Append()
a1.PutValue(0, hbrt.MakeInt(42))
a2, err := drv.Open(hbrdd.OpenParams{Path: "mem:shared", Alias: "B"})
if err != nil { t.Fatal(err) }
a2.GoTo(1)
v, _ := a2.GetValue(0)
if v.AsInt() != 42 { t.Errorf("shared read: %d", v.AsInt()) }
a1.GoTo(1)
a1.PutValue(0, hbrt.MakeInt(99))
a2.GoTo(1)
v, _ = a2.GetValue(0)
if v.AsInt() != 99 { t.Errorf("shared write: %d", v.AsInt()) }
a1.Close()
a2.Close()
DropTable("shared")
t.Log("Multi-open: OK")
}