- 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>
381 lines
8.1 KiB
Go
381 lines
8.1 KiB
Go
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
|
|
// All rights reserved.
|
|
|
|
package hbrt
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
"unsafe"
|
|
)
|
|
|
|
func TestValueSize(t *testing.T) {
|
|
if size := unsafe.Sizeof(Value{}); size != 24 {
|
|
t.Errorf("sizeof(Value) = %d, want 24", size)
|
|
}
|
|
}
|
|
|
|
// --- Nil ---
|
|
|
|
func TestNil(t *testing.T) {
|
|
v := MakeNil()
|
|
if !v.IsNil() {
|
|
t.Error("MakeNil().IsNil() should be true")
|
|
}
|
|
if v.Type() != tNil {
|
|
t.Errorf("MakeNil().Type() = %d, want %d", v.Type(), tNil)
|
|
}
|
|
if v.IsNumeric() || v.IsString() || v.IsLogical() {
|
|
t.Error("Nil should not match other types")
|
|
}
|
|
}
|
|
|
|
// --- Logical ---
|
|
|
|
func TestLogical(t *testing.T) {
|
|
vt := MakeBool(true)
|
|
vf := MakeBool(false)
|
|
|
|
if !vt.IsLogical() {
|
|
t.Error("MakeBool(true).IsLogical() should be true")
|
|
}
|
|
if !vt.AsBool() {
|
|
t.Error("MakeBool(true).AsBool() should be true")
|
|
}
|
|
if vf.AsBool() {
|
|
t.Error("MakeBool(false).AsBool() should be false")
|
|
}
|
|
}
|
|
|
|
// --- Integer ---
|
|
|
|
func TestInt(t *testing.T) {
|
|
tests := []int{0, 1, -1, 42, -42, math.MaxInt32, math.MinInt32}
|
|
for _, n := range tests {
|
|
v := MakeInt(n)
|
|
if !v.IsInt() {
|
|
t.Errorf("MakeInt(%d).IsInt() should be true", n)
|
|
}
|
|
if !v.IsNumeric() {
|
|
t.Errorf("MakeInt(%d).IsNumeric() should be true", n)
|
|
}
|
|
if !v.IsNumInt() {
|
|
t.Errorf("MakeInt(%d).IsNumInt() should be true", n)
|
|
}
|
|
if v.AsInt() != n {
|
|
t.Errorf("MakeInt(%d).AsInt() = %d", n, v.AsInt())
|
|
}
|
|
if v.AsNumInt() != int64(n) {
|
|
t.Errorf("MakeInt(%d).AsNumInt() = %d", n, v.AsNumInt())
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Long ---
|
|
|
|
func TestLong(t *testing.T) {
|
|
tests := []int64{0, 1, -1, math.MaxInt64, math.MinInt64, 9223372036854775807, -9223372036854775808}
|
|
for _, n := range tests {
|
|
v := MakeLong(n)
|
|
if !v.IsLong() {
|
|
t.Errorf("MakeLong(%d).IsLong() should be true", n)
|
|
}
|
|
if !v.IsNumeric() {
|
|
t.Errorf("MakeLong(%d).IsNumeric() should be true", n)
|
|
}
|
|
if v.AsLong() != n {
|
|
t.Errorf("MakeLong(%d).AsLong() = %d", n, v.AsLong())
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Double ---
|
|
|
|
func TestDouble(t *testing.T) {
|
|
tests := []struct {
|
|
val float64
|
|
length uint16
|
|
decimal uint16
|
|
}{
|
|
{0.0, 1, 0},
|
|
{3.14, 4, 2},
|
|
{-123.456, 7, 3},
|
|
{math.MaxFloat64, 255, 255},
|
|
{math.SmallestNonzeroFloat64, 255, 255},
|
|
}
|
|
for _, tt := range tests {
|
|
v := MakeDouble(tt.val, tt.length, tt.decimal)
|
|
if !v.IsDouble() {
|
|
t.Errorf("MakeDouble(%g).IsDouble() should be true", tt.val)
|
|
}
|
|
if !v.IsNumeric() {
|
|
t.Errorf("MakeDouble(%g).IsNumeric() should be true", tt.val)
|
|
}
|
|
if v.AsDouble() != tt.val {
|
|
t.Errorf("MakeDouble(%g).AsDouble() = %g", tt.val, v.AsDouble())
|
|
}
|
|
if v.Length() != tt.length {
|
|
t.Errorf("MakeDouble(%g).Length() = %d, want %d", tt.val, v.Length(), tt.length)
|
|
}
|
|
if v.Decimal() != tt.decimal {
|
|
t.Errorf("MakeDouble(%g).Decimal() = %d, want %d", tt.val, v.Decimal(), tt.decimal)
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Date ---
|
|
|
|
func TestDate(t *testing.T) {
|
|
// Julian day for 2026-03-27 ≈ 2461033
|
|
julian := int64(2461033)
|
|
v := MakeDate(julian)
|
|
if !v.IsDate() {
|
|
t.Error("MakeDate().IsDate() should be true")
|
|
}
|
|
if !v.IsDateTime() {
|
|
t.Error("MakeDate().IsDateTime() should be true")
|
|
}
|
|
if v.AsJulian() != julian {
|
|
t.Errorf("MakeDate().AsJulian() = %d, want %d", v.AsJulian(), julian)
|
|
}
|
|
if v.AsTimeMs() != 0 {
|
|
t.Error("Date should have 0 timeMs")
|
|
}
|
|
}
|
|
|
|
// --- Timestamp ---
|
|
|
|
func TestTimestamp(t *testing.T) {
|
|
julian := int64(2461033)
|
|
timeMs := int32(43200000) // 12:00:00.000
|
|
v := MakeTimestamp(julian, timeMs)
|
|
if !v.IsTimestamp() {
|
|
t.Error("MakeTimestamp().IsTimestamp() should be true")
|
|
}
|
|
if !v.IsDateTime() {
|
|
t.Error("MakeTimestamp().IsDateTime() should be true")
|
|
}
|
|
if v.AsJulian() != julian {
|
|
t.Errorf("AsJulian() = %d, want %d", v.AsJulian(), julian)
|
|
}
|
|
if v.AsTimeMs() != timeMs {
|
|
t.Errorf("AsTimeMs() = %d, want %d", v.AsTimeMs(), timeMs)
|
|
}
|
|
}
|
|
|
|
// --- String ---
|
|
|
|
func TestString(t *testing.T) {
|
|
tests := []string{"", "Hello", "Hello, World!", "한글 테스트", "こんにちは"}
|
|
for _, s := range tests {
|
|
v := MakeString(s)
|
|
if !v.IsString() {
|
|
t.Errorf("MakeString(%q).IsString() should be true", s)
|
|
}
|
|
if v.AsString() != s {
|
|
t.Errorf("MakeString(%q).AsString() = %q", s, v.AsString())
|
|
}
|
|
if v.StringLen() != len(s) {
|
|
t.Errorf("MakeString(%q).StringLen() = %d, want %d", s, v.StringLen(), len(s))
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Array ---
|
|
|
|
func TestArray(t *testing.T) {
|
|
v := MakeArray(3)
|
|
if !v.IsArray() {
|
|
t.Error("MakeArray().IsArray() should be true")
|
|
}
|
|
arr := v.AsArray()
|
|
if arr == nil {
|
|
t.Fatal("AsArray() should not be nil")
|
|
}
|
|
if len(arr.Items) != 3 {
|
|
t.Errorf("array len = %d, want 3", len(arr.Items))
|
|
}
|
|
// All items should be nil initially
|
|
for i, item := range arr.Items {
|
|
if !item.IsNil() {
|
|
t.Errorf("arr[%d] should be Nil, got type %d", i, item.Type())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestArrayFrom(t *testing.T) {
|
|
items := []Value{MakeInt(1), MakeString("two"), MakeBool(true)}
|
|
v := MakeArrayFrom(items)
|
|
arr := v.AsArray()
|
|
if len(arr.Items) != 3 {
|
|
t.Fatalf("len = %d, want 3", len(arr.Items))
|
|
}
|
|
if arr.Items[0].AsInt() != 1 {
|
|
t.Error("arr[0] should be 1")
|
|
}
|
|
if arr.Items[1].AsString() != "two" {
|
|
t.Error("arr[1] should be 'two'")
|
|
}
|
|
if !arr.Items[2].AsBool() {
|
|
t.Error("arr[2] should be true")
|
|
}
|
|
}
|
|
|
|
// --- Object ---
|
|
|
|
func TestObject(t *testing.T) {
|
|
v := MakeObject(1, 5)
|
|
if !v.IsObject() {
|
|
t.Error("MakeObject().IsObject() should be true")
|
|
}
|
|
if !v.IsArray() {
|
|
t.Error("Object.IsArray() should be true (object is array with class)")
|
|
}
|
|
arr := v.AsArray()
|
|
if arr.Class != 1 {
|
|
t.Errorf("Class = %d, want 1", arr.Class)
|
|
}
|
|
}
|
|
|
|
// --- Hash ---
|
|
|
|
func TestHash(t *testing.T) {
|
|
v := MakeHash()
|
|
if !v.IsHash() {
|
|
t.Error("MakeHash().IsHash() should be true")
|
|
}
|
|
hh := v.AsHash()
|
|
if hh == nil {
|
|
t.Fatal("AsHash() should not be nil")
|
|
}
|
|
if len(hh.Keys) != 0 {
|
|
t.Error("new hash should be empty")
|
|
}
|
|
}
|
|
|
|
// --- Block ---
|
|
|
|
func TestBlock(t *testing.T) {
|
|
called := false
|
|
v := MakeBlock(func(t *Thread) {
|
|
called = true
|
|
}, 0)
|
|
if !v.IsBlock() {
|
|
t.Error("MakeBlock().IsBlock() should be true")
|
|
}
|
|
blk := v.AsBlock()
|
|
if blk == nil {
|
|
t.Fatal("AsBlock() should not be nil")
|
|
}
|
|
blk.Fn(nil) // call it
|
|
if !called {
|
|
t.Error("block function should have been called")
|
|
}
|
|
}
|
|
|
|
// --- NumInt auto-promotion ---
|
|
|
|
func TestMakeNumInt(t *testing.T) {
|
|
// Within int32 range → Int
|
|
v1 := MakeNumInt(42)
|
|
if !v1.IsInt() {
|
|
t.Error("42 should be Int")
|
|
}
|
|
|
|
// Beyond int32 range → Long
|
|
v2 := MakeNumInt(int64(math.MaxInt32) + 1)
|
|
if !v2.IsLong() {
|
|
t.Error("MaxInt32+1 should be Long")
|
|
}
|
|
|
|
v3 := MakeNumInt(int64(math.MinInt32) - 1)
|
|
if !v3.IsLong() {
|
|
t.Error("MinInt32-1 should be Long")
|
|
}
|
|
}
|
|
|
|
// --- AsNumDouble ---
|
|
|
|
func TestAsNumDouble(t *testing.T) {
|
|
if MakeInt(42).AsNumDouble() != 42.0 {
|
|
t.Error("Int(42).AsNumDouble() should be 42.0")
|
|
}
|
|
if MakeLong(1000000000000).AsNumDouble() != 1e12 {
|
|
t.Error("Long(1e12).AsNumDouble() should be 1e12")
|
|
}
|
|
if MakeDouble(3.14, 4, 2).AsNumDouble() != 3.14 {
|
|
t.Error("Double(3.14).AsNumDouble() should be 3.14")
|
|
}
|
|
if MakeNil().AsNumDouble() != 0 {
|
|
t.Error("Nil.AsNumDouble() should be 0")
|
|
}
|
|
}
|
|
|
|
// --- Stringer ---
|
|
|
|
func TestStringer(t *testing.T) {
|
|
tests := []struct {
|
|
v Value
|
|
want string
|
|
}{
|
|
{MakeNil(), "NIL"},
|
|
{MakeBool(true), ".T."},
|
|
{MakeBool(false), ".F."},
|
|
{MakeInt(42), "42"},
|
|
{MakeLong(-999), "-999"},
|
|
{MakeString("hello"), `"hello"`},
|
|
}
|
|
for _, tt := range tests {
|
|
got := tt.v.String()
|
|
if got != tt.want {
|
|
t.Errorf("String() = %q, want %q", got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Benchmarks ---
|
|
|
|
func BenchmarkValueMakeInt(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = MakeInt(i)
|
|
}
|
|
}
|
|
|
|
func BenchmarkInterfaceMakeInt(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
var v interface{} = int64(i)
|
|
_ = v
|
|
}
|
|
}
|
|
|
|
func BenchmarkValueAddInt(b *testing.B) {
|
|
a := MakeInt(100)
|
|
for i := 0; i < b.N; i++ {
|
|
// Simulate: read int, add, write back
|
|
r := a.AsNumInt() + int64(i)
|
|
a = MakeNumInt(r)
|
|
}
|
|
}
|
|
|
|
func BenchmarkInterfaceAddInt(b *testing.B) {
|
|
var a interface{} = int64(100)
|
|
for i := 0; i < b.N; i++ {
|
|
r := a.(int64) + int64(i)
|
|
a = r
|
|
}
|
|
}
|
|
|
|
func BenchmarkValueTypeCheck(b *testing.B) {
|
|
v := MakeInt(42)
|
|
for i := 0; i < b.N; i++ {
|
|
_ = v.IsNumeric()
|
|
}
|
|
}
|
|
|
|
func BenchmarkInterfaceTypeCheck(b *testing.B) {
|
|
var v interface{} = int64(42)
|
|
for i := 0; i < b.N; i++ {
|
|
_, _ = v.(int64)
|
|
}
|
|
}
|