Files
five/hbrt/ops_compare_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

454 lines
8.0 KiB
Go

// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
// All rights reserved.
package hbrt
import "testing"
// --- Equal ---
func TestEqualNilNil(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushNil()
th.PushNil()
th.Equal()
if !th.pop().AsBool() {
t.Error("NIL == NIL should be true")
}
th.EndProc()
}
func TestEqualNilOther(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushNil()
th.PushInt(0)
th.Equal()
if th.pop().AsBool() {
t.Error("NIL == 0 should be false")
}
th.EndProc()
}
func TestEqualIntInt(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(42)
th.PushInt(42)
th.Equal()
if !th.pop().AsBool() {
t.Error("42 == 42 should be true")
}
th.PushInt(42)
th.PushInt(99)
th.Equal()
if th.pop().AsBool() {
t.Error("42 == 99 should be false")
}
th.EndProc()
}
func TestEqualIntDouble(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
// Cross-type numeric: Int == Double
th.PushInt(42)
th.PushDouble(42.0, 4, 1)
th.Equal()
if !th.pop().AsBool() {
t.Error("42 == 42.0 should be true")
}
th.EndProc()
}
func TestEqualString(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushString("hello")
th.PushString("hello")
th.Equal()
if !th.pop().AsBool() {
t.Error(`"hello" == "hello" should be true`)
}
th.PushString("hello")
th.PushString("world")
th.Equal()
if th.pop().AsBool() {
t.Error(`"hello" == "world" should be false`)
}
th.EndProc()
}
func TestEqualLogical(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushBool(true)
th.PushBool(true)
th.Equal()
if !th.pop().AsBool() {
t.Error(".T. == .T. should be true")
}
th.PushBool(true)
th.PushBool(false)
th.Equal()
if th.pop().AsBool() {
t.Error(".T. == .F. should be false")
}
th.EndProc()
}
func TestEqualDate(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.push(MakeDate(2461033))
th.push(MakeDate(2461033))
th.Equal()
if !th.pop().AsBool() {
t.Error("same date should be equal")
}
th.push(MakeDate(2461033))
th.push(MakeDate(2461034))
th.Equal()
if th.pop().AsBool() {
t.Error("different dates should not be equal")
}
th.EndProc()
}
func TestEqualTimestamp(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.push(MakeTimestamp(2461033, 43200000))
th.push(MakeTimestamp(2461033, 43200000))
th.Equal()
if !th.pop().AsBool() {
t.Error("same timestamp should be equal")
}
// Same date, different time
th.push(MakeTimestamp(2461033, 43200000))
th.push(MakeTimestamp(2461033, 43200001))
th.Equal()
if th.pop().AsBool() {
t.Error("different time should not be equal")
}
th.EndProc()
}
func TestEqualArrayIdentity(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
a := MakeArray(3)
th.push(a)
th.push(a) // same pointer
th.Equal()
if !th.pop().AsBool() {
t.Error("same array should be equal (pointer identity)")
}
th.push(a)
th.push(MakeArray(3)) // different pointer
th.Equal()
if th.pop().AsBool() {
t.Error("different arrays should not be equal")
}
th.EndProc()
}
// --- NotEqual ---
func TestNotEqual(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(1)
th.PushInt(2)
th.NotEqual()
if !th.pop().AsBool() {
t.Error("1 != 2 should be true")
}
th.PushInt(5)
th.PushInt(5)
th.NotEqual()
if th.pop().AsBool() {
t.Error("5 != 5 should be false")
}
th.EndProc()
}
// --- Relational ---
func TestLessInt(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(1)
th.PushInt(2)
th.Less()
if !th.pop().AsBool() {
t.Error("1 < 2 should be true")
}
th.PushInt(2)
th.PushInt(1)
th.Less()
if th.pop().AsBool() {
t.Error("2 < 1 should be false")
}
th.PushInt(1)
th.PushInt(1)
th.Less()
if th.pop().AsBool() {
t.Error("1 < 1 should be false")
}
th.EndProc()
}
func TestLessEqualInt(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(1)
th.PushInt(1)
th.LessEqual()
if !th.pop().AsBool() {
t.Error("1 <= 1 should be true")
}
th.PushInt(2)
th.PushInt(1)
th.LessEqual()
if th.pop().AsBool() {
t.Error("2 <= 1 should be false")
}
th.EndProc()
}
func TestGreater(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(10)
th.PushInt(5)
th.Greater()
if !th.pop().AsBool() {
t.Error("10 > 5 should be true")
}
th.EndProc()
}
func TestGreaterEqual(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(5)
th.PushInt(5)
th.GreaterEqual()
if !th.pop().AsBool() {
t.Error("5 >= 5 should be true")
}
th.EndProc()
}
func TestLessString(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushString("abc")
th.PushString("def")
th.Less()
if !th.pop().AsBool() {
t.Error(`"abc" < "def" should be true`)
}
th.PushString("def")
th.PushString("abc")
th.Less()
if th.pop().AsBool() {
t.Error(`"def" < "abc" should be false`)
}
th.EndProc()
}
func TestLessDate(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.push(MakeDate(2461033))
th.push(MakeDate(2461034))
th.Less()
if !th.pop().AsBool() {
t.Error("earlier date < later date should be true")
}
th.EndProc()
}
func TestLessTimestamp(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
// Same day, earlier time
th.push(MakeTimestamp(2461033, 10000))
th.push(MakeTimestamp(2461033, 20000))
th.Less()
if !th.pop().AsBool() {
t.Error("earlier timestamp should be less")
}
th.EndProc()
}
func TestLessTypeMismatch(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
defer func() {
r := recover()
if r == nil {
t.Error("expected panic on type mismatch comparison")
}
}()
th.PushInt(1)
th.PushString("hello")
th.Less() // should panic
}
// --- Logical operators ---
func TestNot(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushBool(true)
th.Not()
if th.pop().AsBool() {
t.Error("NOT .T. should be .F.")
}
th.PushBool(false)
th.Not()
if !th.pop().AsBool() {
t.Error("NOT .F. should be .T.")
}
th.EndProc()
}
func TestAnd(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
tests := []struct {
a, b, want bool
}{
{true, true, true},
{true, false, false},
{false, true, false},
{false, false, false},
}
for _, tt := range tests {
th.PushBool(tt.a)
th.PushBool(tt.b)
th.And()
if th.pop().AsBool() != tt.want {
t.Errorf("%v .AND. %v should be %v", tt.a, tt.b, tt.want)
}
}
th.EndProc()
}
func TestOr(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
tests := []struct {
a, b, want bool
}{
{true, true, true},
{true, false, true},
{false, true, true},
{false, false, false},
}
for _, tt := range tests {
th.PushBool(tt.a)
th.PushBool(tt.b)
th.Or()
if th.pop().AsBool() != tt.want {
t.Errorf("%v .OR. %v should be %v", tt.a, tt.b, tt.want)
}
}
th.EndProc()
}
// --- PopLogical ---
func TestPopLogical(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushBool(true)
if !th.PopLogical() {
t.Error("PopLogical(.T.) should be true")
}
th.PushBool(false)
if th.PopLogical() {
t.Error("PopLogical(.F.) should be false")
}
th.EndProc()
}
func TestPopLogicalPanicOnNonBool(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
defer func() {
if r := recover(); r == nil {
t.Error("expected panic on PopLogical with non-boolean")
}
}()
th.PushInt(42)
th.PopLogical()
}
// --- EqualIntIs optimization ---
func TestEqualIntIs(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(10)
if !th.EqualIntIs(10) {
t.Error("10 == 10 should be true")
}
th.PushInt(10)
if th.EqualIntIs(20) {
t.Error("10 == 20 should be false")
}
th.PushDouble(10.0, 4, 1)
if !th.EqualIntIs(10) {
t.Error("10.0 == 10 should be true")
}
th.EndProc()
}
// --- Cross-type numeric comparison ---
func TestCompareIntVsLong(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(42)
th.PushLong(42)
th.Equal()
if !th.pop().AsBool() {
t.Error("Int(42) == Long(42) should be true")
}
th.EndProc()
}
func TestCompareIntVsDouble(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(5)
th.PushDouble(10.0, 4, 1)
th.Less()
if !th.pop().AsBool() {
t.Error("Int(5) < Double(10.0) should be true")
}
th.EndProc()
}