Files
five/hbrt/value_test.go
Charles KWON OhJun 59568f3301 Five v0.9 — Harbour + Go fusion language
- 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>
2026-03-31 09:41:50 +09:00

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)
}
}