220 lines
6.7 KiB
ObjectPascal
220 lines
6.7 KiB
ObjectPascal
unit Test.Myc.Ast.RTL;
|
|
|
|
interface
|
|
|
|
uses
|
|
DUnitX.TestFramework,
|
|
System.SysUtils,
|
|
System.Generics.Collections,
|
|
System.Math,
|
|
System.DateUtils,
|
|
Myc.Data.Scalar,
|
|
Myc.Data.Value,
|
|
Myc.Ast.Types,
|
|
Myc.Ast.Scope,
|
|
Myc.Ast.RTL,
|
|
Myc.Ast.RTL.Core;
|
|
|
|
type
|
|
[TestFixture]
|
|
TTestMycAstRTL = class
|
|
private
|
|
FScope: IExecutionScope;
|
|
|
|
function Call(const Name: string; const Args: array of TDataValue): TDataValue;
|
|
function ValI(V: Int64): TDataValue;
|
|
function ValF(V: Double): TDataValue;
|
|
public
|
|
[Setup]
|
|
procedure Setup;
|
|
|
|
// --- Registration ---
|
|
[Test]
|
|
[IgnoreMemoryLeaks]
|
|
procedure RTL_Registration_SymbolsArePresent;
|
|
|
|
// --- Floating Point Math ---
|
|
[Test]
|
|
[TestCase('Add_FF', '+,1.5,2.5,4.0')]
|
|
[TestCase('Div_FF', '/,10,2,5.0')]
|
|
procedure RTL_Math_Float(const Op: string; A, B, Expected: Double);
|
|
|
|
// --- Integer Math (New) ---
|
|
[Test]
|
|
[TestCase('Div_Int', 'div,10,3,3')]
|
|
[TestCase('Mod_Int', 'mod,10,3,1')]
|
|
procedure RTL_Math_Integer(const Op: string; A, B, Expected: Int64);
|
|
|
|
// --- Bitwise Operations (New) ---
|
|
[Test]
|
|
[TestCase('BitAnd', 'and,3,2,2')] // 011 & 010 = 010 (2)
|
|
[TestCase('BitOr', 'or,1,2,3')] // 001 | 010 = 011 (3)
|
|
[TestCase('BitXor', 'xor,3,1,2')] // 011 ^ 001 = 010 (2)
|
|
[TestCase('Shl', 'shl,1,2,4')] // 1 << 2 = 4
|
|
[TestCase('Shr', 'shr,4,1,2')] // 4 >> 1 = 2
|
|
procedure RTL_Bitwise(const Op: string; A, B, Expected: Int64);
|
|
|
|
// --- Rounding (New) ---
|
|
[Test]
|
|
[TestCase('Round_Up', '3.6,4')]
|
|
[TestCase('Round_Down', '3.4,3')]
|
|
[TestCase('Round_Mid', '3.5,4')] // Banker's rounding or standard? Delphi default is Banker's.
|
|
procedure RTL_Round_Works(A: Double; Expected: Int64);
|
|
|
|
// --- Comparisons ---
|
|
[Test]
|
|
[TestCase('Eq_True', '=,10,10,1')]
|
|
[TestCase('Neq_True', '<>,10,20,1')]
|
|
procedure RTL_Comparison_Ordinals(const Op: string; A, B: Int64; ExpectedBool: Int64);
|
|
|
|
// --- DateTime Logic (New) ---
|
|
[Test]
|
|
procedure RTL_DateTime_ConstructionAndMath;
|
|
|
|
// --- Error Handling ---
|
|
[Test]
|
|
procedure RTL_DivByZero_ThrowException;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Myc.Ast;
|
|
|
|
{ TTestMycAstRTL }
|
|
|
|
procedure TTestMycAstRTL.Setup;
|
|
begin
|
|
FScope := TAst.CreateScope(nil, nil, False);
|
|
Myc.Ast.RTL.RegisterRtlFunctions(FScope);
|
|
end;
|
|
|
|
// --- Helpers ---
|
|
|
|
function TTestMycAstRTL.ValI(V: Int64): TDataValue;
|
|
begin
|
|
Result := TDataValue(TScalar.FromInt64(V));
|
|
end;
|
|
|
|
function TTestMycAstRTL.ValF(V: Double): TDataValue;
|
|
begin
|
|
Result := TDataValue(TScalar.FromDouble(V));
|
|
end;
|
|
|
|
function TTestMycAstRTL.Call(const Name: string; const Args: array of TDataValue): TDataValue;
|
|
var
|
|
addr: TResolvedAddress;
|
|
funcVal: TDataValue;
|
|
func: TDataValue.TFunc;
|
|
argArray: TArray<TDataValue>;
|
|
i: Integer;
|
|
begin
|
|
addr := FScope.Resolve(Name);
|
|
Assert.AreNotEqual(TAddressKind.akUnresolved, addr.Kind, 'Function not found: ' + Name);
|
|
|
|
funcVal := FScope[addr];
|
|
func := funcVal.AsMethod();
|
|
|
|
SetLength(argArray, Length(Args));
|
|
for i := 0 to High(Args) do
|
|
argArray[i] := Args[i];
|
|
|
|
Result := func(argArray);
|
|
end;
|
|
|
|
// --- Tests ---
|
|
|
|
procedure TTestMycAstRTL.RTL_Registration_SymbolsArePresent;
|
|
begin
|
|
Assert.AreNotEqual(TAddressKind.akUnresolved, FScope.Resolve('div').Kind);
|
|
Assert.AreNotEqual(TAddressKind.akUnresolved, FScope.Resolve('mod').Kind);
|
|
Assert.AreNotEqual(TAddressKind.akUnresolved, FScope.Resolve('date').Kind);
|
|
Assert.AreNotEqual(TAddressKind.akUnresolved, FScope.Resolve('round').Kind);
|
|
end;
|
|
|
|
procedure TTestMycAstRTL.RTL_Math_Float(const Op: string; A, B, Expected: Double);
|
|
var
|
|
res: TDataValue;
|
|
begin
|
|
res := Call(Op, [ValF(A), ValF(B)]);
|
|
Assert.AreEqual(TDataValueKind.vkScalar, res.Kind);
|
|
Assert.AreEqual(Expected, res.AsScalar.Value.AsDouble, 0.00001);
|
|
end;
|
|
|
|
procedure TTestMycAstRTL.RTL_Math_Integer(const Op: string; A, B, Expected: Int64);
|
|
var
|
|
res: TDataValue;
|
|
begin
|
|
res := Call(Op, [ValI(A), ValI(B)]);
|
|
Assert.AreEqual(TDataValueKind.vkScalar, res.Kind);
|
|
Assert.AreEqual(TScalar.TKind.Ordinal, res.AsScalar.Kind);
|
|
Assert.AreEqual(Expected, res.AsScalar.Value.AsInt64);
|
|
end;
|
|
|
|
procedure TTestMycAstRTL.RTL_Bitwise(const Op: string; A, B, Expected: Int64);
|
|
var
|
|
res: TDataValue;
|
|
begin
|
|
res := Call(Op, [ValI(A), ValI(B)]);
|
|
Assert.AreEqual(TScalar.TKind.Ordinal, res.AsScalar.Kind);
|
|
Assert.AreEqual(Expected, res.AsScalar.Value.AsInt64);
|
|
end;
|
|
|
|
procedure TTestMycAstRTL.RTL_Round_Works(A: Double; Expected: Int64);
|
|
var
|
|
res: TDataValue;
|
|
begin
|
|
res := Call('round', [ValF(A)]);
|
|
Assert.AreEqual(TScalar.TKind.Ordinal, res.AsScalar.Kind);
|
|
Assert.AreEqual(Expected, res.AsScalar.Value.AsInt64);
|
|
end;
|
|
|
|
procedure TTestMycAstRTL.RTL_Comparison_Ordinals(const Op: string; A, B: Int64; ExpectedBool: Int64);
|
|
var
|
|
res: TDataValue;
|
|
begin
|
|
res := Call(Op, [ValI(A), ValI(B)]);
|
|
// Note: TScalar.Equal/NotEqual returns Boolean Kind now!
|
|
// The test case expects integer 0 or 1, so we convert or check bool.
|
|
// TScalar.Implicit(Boolean) -> Int64 (0/1) works.
|
|
|
|
Assert.AreEqual(TDataValueKind.vkScalar, res.Kind);
|
|
Assert.AreEqual(TScalar.TKind.Boolean, res.AsScalar.Kind);
|
|
|
|
var asInt: Int64 := res.AsScalar; // Implicit conversion
|
|
Assert.AreEqual(ExpectedBool, asInt);
|
|
end;
|
|
|
|
procedure TTestMycAstRTL.RTL_DateTime_ConstructionAndMath;
|
|
var
|
|
d, d2, diff: TDataValue;
|
|
expectedDate: TDateTime;
|
|
begin
|
|
// 1. Test Date(Y, M, D)
|
|
expectedDate := EncodeDate(2023, 10, 5);
|
|
d := Call('date', [ValI(2023), ValI(10), ValI(5)]);
|
|
|
|
Assert.AreEqual(TScalar.TKind.DateTime, d.AsScalar.Kind);
|
|
Assert.AreEqual(expectedDate, d.AsScalar.Value.AsDouble, 0.001);
|
|
|
|
// 2. Test Date + Int (Days)
|
|
d2 := Call('+', [d, ValI(2)]); // Add 2 days
|
|
Assert.AreEqual(TScalar.TKind.DateTime, d2.AsScalar.Kind);
|
|
Assert.AreEqual(expectedDate + 2, d2.AsScalar.Value.AsDouble, 0.001);
|
|
|
|
// 3. Test Date - Date (Diff in days)
|
|
diff := Call('-', [d2, d]);
|
|
Assert.AreEqual(TScalar.TKind.Float, diff.AsScalar.Kind);
|
|
Assert.AreEqual(2.0, diff.AsScalar.Value.AsDouble, 0.001);
|
|
end;
|
|
|
|
procedure TTestMycAstRTL.RTL_DivByZero_ThrowException;
|
|
begin
|
|
// Integer div
|
|
Assert.WillRaise(procedure begin Call('div', [ValI(10), ValI(0)]); end);
|
|
// Float divide (explicit check in RTL)
|
|
Assert.WillRaise(procedure begin Call('/', [ValF(10.0), ValF(0.0)]); end);
|
|
end;
|
|
|
|
end.
|