- Compiler: PP → Lexer → Parser → Analyzer → Gengo pipeline - Parser: 232/236 (98%) Harbour compatibility, registry-based dispatch - RTL: 351 Harbour-compatible functions - RDD: DBF/NTX/CDX engines with Rushmore bitmap optimization - Go Interop: IMPORT + pkg.Func() + obj:Method() with FastPath (15M calls/sec) - HB_FUNC API: Full Harbour C API compatible Go bridge - Concurrency: SPAWN/LAUNCH/GOROUTINE, <-, WATCH, PARALLEL FOR, ASYNC/AWAIT - Extensions: Multi-return, DEFER, Slice, f-string, Nil-safe ?:, CONST - Macro Compiler: Runtime AST parsing and evaluation - Debugger: TUI debugger with source display, breakpoints, stepping - FRB: Native + Pcode dual mode runtime binary - Tests: 13 packages ALL PASS Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
225 lines
4.5 KiB
Go
225 lines
4.5 KiB
Go
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
|
|
// All rights reserved.
|
|
|
|
// CDX engine test — reads CDX files created by Harbour.
|
|
|
|
package cdx
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
)
|
|
|
|
const testCDX = "/mnt/d/charles/five/dbf/cdxtest.cdx"
|
|
|
|
func TestCDXOpen(t *testing.T) {
|
|
if _, err := os.Stat(testCDX); err != nil {
|
|
t.Skip("cdxtest.cdx not found — run Harbour to create it first")
|
|
}
|
|
|
|
idx, err := OpenIndex(testCDX)
|
|
if err != nil {
|
|
t.Fatalf("OpenIndex: %v", err)
|
|
}
|
|
defer idx.Close()
|
|
|
|
t.Logf("CDX opened: %d tags", idx.TagCount())
|
|
if idx.TagCount() < 1 {
|
|
t.Fatal("Expected at least 1 tag")
|
|
}
|
|
|
|
for i := 0; i < idx.TagCount(); i++ {
|
|
tag := idx.GetTag(i)
|
|
if tag != nil {
|
|
t.Logf(" Tag %d: name=%q", i, tag.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCDXFindTag(t *testing.T) {
|
|
if _, err := os.Stat(testCDX); err != nil {
|
|
t.Skip("cdxtest.cdx not found")
|
|
}
|
|
|
|
idx, err := OpenIndex(testCDX)
|
|
if err != nil {
|
|
t.Fatalf("OpenIndex: %v", err)
|
|
}
|
|
defer idx.Close()
|
|
|
|
tag := idx.FindTag("NAME")
|
|
if tag == nil {
|
|
// Try without prefix
|
|
for i := 0; i < idx.TagCount(); i++ {
|
|
tg := idx.GetTag(i)
|
|
t.Logf("Available tag: %q", tg.Name)
|
|
}
|
|
t.Fatal("Tag BYNAME not found")
|
|
}
|
|
t.Logf("Found tag BYNAME")
|
|
}
|
|
|
|
func TestCDXGoTop(t *testing.T) {
|
|
if _, err := os.Stat(testCDX); err != nil {
|
|
t.Skip("cdxtest.cdx not found")
|
|
}
|
|
|
|
idx, err := OpenIndex(testCDX)
|
|
if err != nil {
|
|
t.Fatalf("OpenIndex: %v", err)
|
|
}
|
|
defer idx.Close()
|
|
|
|
tag := idx.FindTag("NAME")
|
|
if tag == nil {
|
|
// Try first tag
|
|
if idx.TagCount() > 0 {
|
|
tag = idx.GetTag(0)
|
|
t.Logf("Using first tag: %q", tag.Name)
|
|
} else {
|
|
t.Skip("No tags found")
|
|
}
|
|
}
|
|
|
|
ok := tag.GoTop()
|
|
t.Logf("GoTop: ok=%v, recNo=%d, key=%q, eof=%v", ok, tag.CurRecNo(), string(tag.CurKey()), tag.IsEOF())
|
|
|
|
if tag.IsEOF() {
|
|
t.Error("GoTop resulted in EOF")
|
|
}
|
|
|
|
// First by name should be "Cho" (Harbour verified)
|
|
key := string(tag.CurKey())
|
|
t.Logf("First key: %q", key)
|
|
}
|
|
|
|
func TestCDXTraverse(t *testing.T) {
|
|
if _, err := os.Stat(testCDX); err != nil {
|
|
t.Skip("cdxtest.cdx not found")
|
|
}
|
|
|
|
idx, err := OpenIndex(testCDX)
|
|
if err != nil {
|
|
t.Fatalf("OpenIndex: %v", err)
|
|
}
|
|
defer idx.Close()
|
|
|
|
tag := idx.FindTag("NAME")
|
|
if tag == nil && idx.TagCount() > 0 {
|
|
tag = idx.GetTag(0)
|
|
}
|
|
if tag == nil {
|
|
t.Skip("No tags")
|
|
}
|
|
|
|
tag.GoTop()
|
|
count := 0
|
|
prev := ""
|
|
for !tag.IsEOF() {
|
|
key := string(tag.CurKey())
|
|
if key < prev {
|
|
t.Errorf("Not sorted: %q < %q at %d", key, prev, count)
|
|
break
|
|
}
|
|
prev = key
|
|
count++
|
|
if count > 200 {
|
|
t.Error("Too many iterations, possible infinite loop")
|
|
break
|
|
}
|
|
tag.SkipNext()
|
|
}
|
|
t.Logf("Traversed %d keys", count)
|
|
if count != 100 {
|
|
t.Errorf("Expected 100 keys, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestCDXSeek(t *testing.T) {
|
|
if _, err := os.Stat(testCDX); err != nil {
|
|
t.Skip("cdxtest.cdx not found")
|
|
}
|
|
|
|
idx, err := OpenIndex(testCDX)
|
|
if err != nil {
|
|
t.Fatalf("OpenIndex: %v", err)
|
|
}
|
|
defer idx.Close()
|
|
|
|
tag := idx.FindTag("NAME")
|
|
if tag == nil && idx.TagCount() > 0 {
|
|
tag = idx.GetTag(0)
|
|
}
|
|
if tag == nil {
|
|
t.Skip("No tags")
|
|
}
|
|
|
|
// Seek "Park" — should find it
|
|
searchKey := make([]byte, 20)
|
|
copy(searchKey, []byte("Park"))
|
|
for i := 4; i < 20; i++ {
|
|
searchKey[i] = ' '
|
|
}
|
|
|
|
recNo, found := tag.Seek(searchKey)
|
|
t.Logf("Seek 'Park': found=%v recNo=%d", found, recNo)
|
|
if !found {
|
|
t.Error("Expected to find 'Park'")
|
|
}
|
|
}
|
|
|
|
func TestCDXGoBottom(t *testing.T) {
|
|
if _, err := os.Stat(testCDX); err != nil {
|
|
t.Skip("cdxtest.cdx not found")
|
|
}
|
|
|
|
idx, err := OpenIndex(testCDX)
|
|
if err != nil {
|
|
t.Fatalf("OpenIndex: %v", err)
|
|
}
|
|
defer idx.Close()
|
|
|
|
tag := idx.FindTag("NAME")
|
|
if tag == nil && idx.TagCount() > 0 {
|
|
tag = idx.GetTag(0)
|
|
}
|
|
if tag == nil {
|
|
t.Skip("No tags")
|
|
}
|
|
|
|
tag.GoBottom()
|
|
t.Logf("GoBottom: recNo=%d key=%q", tag.CurRecNo(), string(tag.CurKey()))
|
|
|
|
// Last by name should be "Yoon" (Harbour verified)
|
|
key := string(tag.CurKey())
|
|
if len(key) >= 4 && key[:4] != "Yoon" {
|
|
t.Errorf("Last key = %q, expected 'Yoon...'", key)
|
|
}
|
|
}
|
|
|
|
func TestCDXCityTag(t *testing.T) {
|
|
if _, err := os.Stat(testCDX); err != nil {
|
|
t.Skip("cdxtest.cdx not found")
|
|
}
|
|
|
|
idx, err := OpenIndex(testCDX)
|
|
if err != nil {
|
|
t.Fatalf("OpenIndex: %v", err)
|
|
}
|
|
defer idx.Close()
|
|
|
|
tag := idx.FindTag("CITY")
|
|
if tag == nil {
|
|
t.Skip("BYCITY tag not found")
|
|
}
|
|
|
|
tag.GoTop()
|
|
t.Logf("City GoTop: recNo=%d key=%q", tag.CurRecNo(), string(tag.CurKey()))
|
|
|
|
// First by city should be "London" (Harbour verified)
|
|
key := string(tag.CurKey())
|
|
if len(key) >= 6 && key[:6] != "London" {
|
|
t.Logf("First city key = %q (expected 'London...')", key)
|
|
}
|
|
}
|