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

360 lines
6.0 KiB
Go

// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
// All rights reserved.
package hbrt
import (
"math"
"testing"
)
// --- Plus ---
func TestPlusIntInt(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(10)
th.PushInt(20)
th.Plus()
r := th.pop()
if !r.IsNumInt() || r.AsNumInt() != 30 {
t.Errorf("10 + 20 = %v, want 30", r)
}
th.EndProc()
}
func TestPlusIntOverflow(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushLong(math.MaxInt64)
th.PushLong(1)
th.Plus()
r := th.pop()
if !r.IsDouble() {
t.Errorf("MaxInt64 + 1 should overflow to Double, got type %d", r.Type())
}
th.EndProc()
}
func TestPlusDoubleDouble(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushDouble(1.5, 3, 1)
th.PushDouble(2.3, 3, 1)
th.Plus()
r := th.pop()
if !r.IsDouble() {
t.Fatal("expected Double")
}
if math.Abs(r.AsDouble()-3.8) > 1e-10 {
t.Errorf("1.5 + 2.3 = %g, want 3.8", r.AsDouble())
}
th.EndProc()
}
func TestPlusStringConcat(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushString("Hello, ")
th.PushString("World!")
th.Plus()
r := th.pop()
if r.AsString() != "Hello, World!" {
t.Errorf("string concat = %q, want %q", r.AsString(), "Hello, World!")
}
th.EndProc()
}
func TestPlusDateInt(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.push(MakeDate(2461033)) // 2026-03-27
th.PushInt(10)
th.Plus()
r := th.pop()
if !r.IsDate() || r.AsJulian() != 2461043 {
t.Errorf("Date + 10 = %v, want Date(2461043)", r)
}
th.EndProc()
}
func TestPlusDecimalPropagation(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushDouble(1.2, 4, 1) // 1 decimal
th.PushDouble(3.456, 5, 3) // 3 decimals
th.Plus()
r := th.pop()
// Result should have max(1, 3) = 3 decimals
if r.Decimal() != 3 {
t.Errorf("decimal = %d, want 3", r.Decimal())
}
th.EndProc()
}
// --- Minus ---
func TestMinusIntInt(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(30)
th.PushInt(20)
th.Minus()
r := th.pop()
if r.AsNumInt() != 10 {
t.Errorf("30 - 20 = %d, want 10", r.AsNumInt())
}
th.EndProc()
}
func TestMinusDateDate(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.push(MakeDate(2461043))
th.push(MakeDate(2461033))
th.Minus()
r := th.pop()
if !r.IsLong() || r.AsLong() != 10 {
t.Errorf("Date - Date = %v, want 10", r)
}
th.EndProc()
}
// --- Mult ---
func TestMultIntInt(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(6)
th.PushInt(7)
th.Mult()
r := th.pop()
if r.AsNumInt() != 42 {
t.Errorf("6 * 7 = %d, want 42", r.AsNumInt())
}
th.EndProc()
}
func TestMultDecimalRule(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushDouble(1.5, 3, 1) // 1 decimal
th.PushDouble(2.5, 3, 1) // 1 decimal
th.Mult()
r := th.pop()
// Mult decimal = dec1 + dec2 = 2
if r.Decimal() != 2 {
t.Errorf("decimal = %d, want 2", r.Decimal())
}
if math.Abs(r.AsDouble()-3.75) > 1e-10 {
t.Errorf("1.5 * 2.5 = %g, want 3.75", r.AsDouble())
}
th.EndProc()
}
func TestMultIntOverflow(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushLong(math.MaxInt64)
th.PushLong(2)
th.Mult()
r := th.pop()
if !r.IsDouble() {
t.Errorf("MaxInt64 * 2 should overflow to Double")
}
th.EndProc()
}
// --- Divide ---
func TestDivide(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(10)
th.PushInt(3)
th.Divide()
r := th.pop()
if !r.IsDouble() {
t.Error("division should always return Double")
}
if math.Abs(r.AsDouble()-3.333333) > 0.001 {
t.Errorf("10 / 3 = %g", r.AsDouble())
}
th.EndProc()
}
func TestDivideByZero(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
defer func() {
r := recover()
if r == nil {
t.Error("expected panic on division by zero")
}
hbErr, ok := r.(*HbError)
if !ok {
t.Errorf("expected HbError, got %T", r)
}
if hbErr.GenCode != 1340 {
t.Errorf("GenCode = %d, want 1340 (EG_ZERODIV)", hbErr.GenCode)
}
}()
th.PushInt(10)
th.PushInt(0)
th.Divide()
}
// --- Modulus ---
func TestModulus(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(10)
th.PushInt(3)
th.Modulus()
r := th.pop()
if !r.IsDouble() || r.AsDouble() != 1.0 {
t.Errorf("10 %% 3 = %v, want 1.0", r)
}
th.EndProc()
}
// --- Power ---
func TestPower(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(2)
th.PushInt(10)
th.Power()
r := th.pop()
if r.AsDouble() != 1024.0 {
t.Errorf("2 ** 10 = %g, want 1024", r.AsDouble())
}
th.EndProc()
}
// --- Negate ---
func TestNegate(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(42)
th.Negate()
if th.pop().AsNumInt() != -42 {
t.Error("negate 42 should be -42")
}
th.PushDouble(3.14, 4, 2)
th.Negate()
r := th.pop()
if r.AsDouble() != -3.14 {
t.Errorf("negate 3.14 = %g", r.AsDouble())
}
th.EndProc()
}
// --- Inc / Dec ---
func TestIncDec(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(10)
th.Inc()
if th.peek().AsNumInt() != 11 {
t.Error("Inc(10) should be 11")
}
th.Dec()
th.Dec()
if th.pop().AsNumInt() != 9 {
t.Error("Dec(Dec(11)) should be 9")
}
th.EndProc()
}
// --- AddInt optimization ---
func TestAddInt(t *testing.T) {
th := newTestThread()
th.Frame(0, 0)
th.PushInt(100)
th.AddInt(50)
if th.pop().AsNumInt() != 150 {
t.Error("100 + 50 should be 150")
}
th.push(MakeDate(2461033))
th.AddInt(7)
r := th.pop()
if !r.IsDate() || r.AsJulian() != 2461040 {
t.Errorf("Date + 7 = %v", r)
}
th.EndProc()
}
// --- LocalAdd optimization ---
func TestLocalAdd(t *testing.T) {
th := newTestThread()
th.Frame(0, 2)
th.LocalSetInt(1, 100)
th.PushInt(50)
th.LocalAdd(1)
if th.Local(1).AsNumInt() != 150 {
t.Errorf("local += 50: got %d, want 150", th.Local(1).AsNumInt())
}
th.EndProc()
}
// --- LocalAddInt optimization ---
func TestLocalAddInt(t *testing.T) {
th := newTestThread()
th.Frame(0, 2)
th.LocalSetInt(1, 0)
for i := int64(1); i <= 10; i++ {
th.LocalAddInt(1, i)
}
if th.Local(1).AsNumInt() != 55 {
t.Errorf("sum 1..10 = %d, want 55", th.Local(1).AsNumInt())
}
th.EndProc()
}