Data Types
This commit is contained in:
+237
-10
@@ -7,39 +7,59 @@ uses
|
||||
|
||||
type
|
||||
[TestFixture]
|
||||
[IgnoreMemoryLeaks]
|
||||
TMyTestObject = class
|
||||
public
|
||||
[Test]
|
||||
procedure TestCreateRecords;
|
||||
[TestFixtureTearDown]
|
||||
procedure FixtureTearDown;
|
||||
|
||||
[Test]
|
||||
procedure TestRecords;
|
||||
[Test]
|
||||
procedure TestRecords_Generic;
|
||||
[Test]
|
||||
procedure TestTuples;
|
||||
|
||||
[Test]
|
||||
procedure TestTuples_Generic;
|
||||
[Test]
|
||||
procedure TestArrays;
|
||||
|
||||
[Test]
|
||||
procedure TestArrays_OptimizedAndGeneric;
|
||||
[Test]
|
||||
procedure TestTexts;
|
||||
|
||||
[Test]
|
||||
procedure TestTexts_OptimizedEmpty;
|
||||
[Test]
|
||||
procedure TestFloats_OptimizedAndGeneric;
|
||||
[Test]
|
||||
procedure TestTimestamps;
|
||||
|
||||
[Test]
|
||||
procedure TestEnums;
|
||||
|
||||
[Test]
|
||||
procedure TestAsString;
|
||||
[Test]
|
||||
procedure TestAsTValue;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
System.Math,
|
||||
System.Rtti,
|
||||
System.TypInfo,
|
||||
Myc.Data.Types;
|
||||
|
||||
{ TMyTestObject }
|
||||
|
||||
procedure TMyTestObject.TestCreateRecords;
|
||||
procedure TMyTestObject.FixtureTearDown;
|
||||
begin
|
||||
// Clear global type caches to release any interfaces created during tests.
|
||||
// This prevents access violations on shutdown from dangling pointers in the registries.
|
||||
TDataType.ClearRegistries;
|
||||
end;
|
||||
|
||||
procedure TMyTestObject.TestRecords;
|
||||
var
|
||||
intType: IDataType;
|
||||
floatType: IDataType;
|
||||
@@ -48,6 +68,7 @@ var
|
||||
idValue: IDataOrdinalValue;
|
||||
floatValue: IDataFloatValue;
|
||||
begin
|
||||
// This test covers the optimized implementation for 2 fields.
|
||||
// --- 1. Setup: Define base types and a record structure ---
|
||||
intType := TDataType.Ordinal;
|
||||
floatType := TDataType.Float;
|
||||
@@ -117,6 +138,30 @@ begin
|
||||
);
|
||||
end;
|
||||
|
||||
procedure TMyTestObject.TestRecords_Generic;
|
||||
var
|
||||
dataType: IDataRecordType;
|
||||
dataValue: IDataRecordValue;
|
||||
begin
|
||||
// Test the generic implementation for records with > 2 fields.
|
||||
dataType :=
|
||||
TDataType.RecordOf(
|
||||
[
|
||||
TRecordField.Create('A', TDataType.Ordinal),
|
||||
TRecordField.Create('B', TDataType.Text),
|
||||
TRecordField.Create('C', TDataType.Float)
|
||||
]
|
||||
);
|
||||
dataValue := dataType.CreateValue([TDataValue.FromOrdinal(1), TDataValue.FromText('two'), TDataValue.FromFloat(3.0)]);
|
||||
|
||||
Assert.IsNotNull(dataValue, 'Generic record value should be created');
|
||||
Assert.AreEqual(3, dataType.FieldCount, 'Generic record should have 3 fields');
|
||||
Assert.AreEqual(Int64(1), TDataValue(dataValue.Items[0]).AsOrdinal.Value, 'Field A is incorrect');
|
||||
Assert.AreEqual('two', TDataValue(dataValue.Items[1]).AsText.Value, 'Field B is incorrect');
|
||||
Assert.AreEqual(3.0, TDataValue(dataValue.Items[2]).AsFloat.Value, 'Field C is incorrect');
|
||||
Assert.AreEqual('<A: 1, B: two, C: 3>', dataValue.AsString, 'Generic record AsString is incorrect');
|
||||
end;
|
||||
|
||||
procedure TMyTestObject.TestTuples;
|
||||
var
|
||||
intValue: IDataValue;
|
||||
@@ -124,6 +169,7 @@ var
|
||||
tuple1, tuple2, tuple3: IDataTupleValue;
|
||||
type1, type2, type3: IDataType;
|
||||
begin
|
||||
// This test covers optimized implementations for 1 and 2 elements.
|
||||
// --- 1. Setup: Create some values ---
|
||||
intValue := TDataValue.FromOrdinal(123);
|
||||
floatValue := TDataValue.FromFloat(45.67);
|
||||
@@ -154,6 +200,29 @@ begin
|
||||
Assert.AreSame(type1, type3, 'All tuple types should be the same singleton instance');
|
||||
end;
|
||||
|
||||
procedure TMyTestObject.TestTuples_Generic;
|
||||
var
|
||||
tupleValue: IDataTupleValue;
|
||||
begin
|
||||
// Test the generic implementation for tuples with > 5 elements.
|
||||
tupleValue :=
|
||||
TDataValue.FromTuple(
|
||||
[
|
||||
TDataValue.FromOrdinal(1),
|
||||
TDataValue.FromOrdinal(2),
|
||||
TDataValue.FromOrdinal(3),
|
||||
TDataValue.FromOrdinal(4),
|
||||
TDataValue.FromOrdinal(5),
|
||||
TDataValue.FromOrdinal(6)
|
||||
]
|
||||
);
|
||||
|
||||
Assert.IsNotNull(tupleValue, 'Generic tuple value should be created');
|
||||
Assert.AreEqual(6, tupleValue.ItemCount, 'Generic tuple should have 6 items');
|
||||
Assert.AreEqual(Int64(6), TDataValue(tupleValue.Items[5]).AsOrdinal.Value, 'Last item is incorrect');
|
||||
Assert.AreEqual('(1, 2, 3, 4, 5, 6)', tupleValue.AsString, 'Generic tuple AsString is incorrect');
|
||||
end;
|
||||
|
||||
procedure TMyTestObject.TestArrays;
|
||||
var
|
||||
intType: IDataType;
|
||||
@@ -210,6 +279,35 @@ begin
|
||||
);
|
||||
end;
|
||||
|
||||
procedure TMyTestObject.TestArrays_OptimizedAndGeneric;
|
||||
var
|
||||
arrayType: IDataArrayType;
|
||||
empty1, empty2, single, generic: IDataArrayValue;
|
||||
begin
|
||||
arrayType := TDataType.ArrayOfType(TDataType.Ordinal);
|
||||
|
||||
// Test optimized path for 0 elements (cached singleton per type)
|
||||
empty1 := arrayType.CreateValue([]);
|
||||
empty2 := arrayType.CreateValue([]);
|
||||
Assert.IsNotNull(empty1, 'Empty array value should be created');
|
||||
Assert.AreSame(empty1, empty2, 'Empty array value should be cached and reused');
|
||||
Assert.AreEqual(0, empty1.ElementCount, 'Empty array should have 0 elements');
|
||||
Assert.AreEqual('[]', empty1.AsString, 'Empty array AsString is incorrect');
|
||||
|
||||
// Test optimized path for 1 element
|
||||
single := arrayType.CreateValue([TDataValue.FromOrdinal(42)]);
|
||||
Assert.IsNotNull(single, 'Single element array should be created');
|
||||
Assert.AreEqual(1, single.ElementCount, 'Single element array should have 1 element');
|
||||
Assert.AreEqual(Int64(42), TDataValue(single.Items[0]).AsOrdinal.Value, 'Single element value is incorrect');
|
||||
Assert.AreEqual('[42]', single.AsString, 'Single element array AsString is incorrect');
|
||||
|
||||
// Test generic path for > 1 elements
|
||||
generic := arrayType.CreateValue([TDataValue.FromOrdinal(1), TDataValue.FromOrdinal(2), TDataValue.FromOrdinal(3)]);
|
||||
Assert.IsNotNull(generic, 'Generic array should be created');
|
||||
Assert.AreEqual(3, generic.ElementCount, 'Generic array should have 3 elements');
|
||||
Assert.AreEqual('[1, 2, 3]', generic.AsString, 'Generic array AsString is incorrect');
|
||||
end;
|
||||
|
||||
procedure TMyTestObject.TestTexts;
|
||||
var
|
||||
textType: IDataTextType;
|
||||
@@ -244,6 +342,47 @@ begin
|
||||
.WillRaise(procedure begin TDataValue(dataValue).AsText; end, EInvalidCast, 'Casting non-text to AsText should raise EInvalidCast');
|
||||
end;
|
||||
|
||||
procedure TMyTestObject.TestTexts_OptimizedEmpty;
|
||||
var
|
||||
empty1, empty2: IDataTextValue;
|
||||
begin
|
||||
// Test the singleton implementation for the empty string.
|
||||
empty1 := TDataValue.FromText('');
|
||||
empty2 := TDataValue.FromText('');
|
||||
|
||||
Assert.IsNotNull(empty1, 'Empty text value should be created');
|
||||
Assert.AreSame(empty1, empty2, 'Empty text value should be a singleton');
|
||||
Assert.AreEqual('', empty1.Value, 'Value of empty text should be empty');
|
||||
Assert.AreEqual('', empty1.AsString, 'AsString of empty text should be empty');
|
||||
end;
|
||||
|
||||
procedure TMyTestObject.TestFloats_OptimizedAndGeneric;
|
||||
var
|
||||
zero1, zero2, nan1, nan2, generic: IDataFloatValue;
|
||||
begin
|
||||
// Test singleton for 0.0
|
||||
zero1 := TDataValue.FromFloat(0.0);
|
||||
zero2 := TDataValue.FromFloat(0.0);
|
||||
Assert.IsNotNull(zero1, 'Zero float should be created');
|
||||
Assert.AreSame(zero1, zero2, 'Zero float should be a singleton');
|
||||
Assert.AreEqual(0.0, zero1.Value, 'Value of zero float is incorrect');
|
||||
|
||||
// Test singleton for NaN
|
||||
nan1 := TDataValue.FromFloat(NaN);
|
||||
nan2 := TDataValue.FromFloat(NaN);
|
||||
Assert.IsNotNull(nan1, 'NaN float should be created');
|
||||
Assert.AreSame(nan1, nan2, 'NaN float should be a singleton');
|
||||
Assert.IsTrue(IsNaN(nan1.Value), 'Value of NaN float should be NaN');
|
||||
Assert.AreEqual('NaN', nan1.AsString, 'NaN AsString is incorrect');
|
||||
|
||||
// Test generic path
|
||||
generic := TDataValue.FromFloat(123.45);
|
||||
Assert.IsNotNull(generic, 'Generic float should be created');
|
||||
Assert.AreNotSame(zero1, generic, 'Generic float should not be the zero singleton');
|
||||
Assert.AreNotSame(nan1, generic, 'Generic float should not be the NaN singleton');
|
||||
Assert.AreEqual(123.45, generic.Value, 'Value of generic float is incorrect');
|
||||
end;
|
||||
|
||||
procedure TMyTestObject.TestTimestamps;
|
||||
var
|
||||
tsType: IDataTimestampType;
|
||||
@@ -390,7 +529,95 @@ begin
|
||||
Assert.AreEqual('(10, Tuple)', tupleValue.AsString, 'Tuple AsString incorrect');
|
||||
end;
|
||||
|
||||
initialization
|
||||
TDUnitX.RegisterTestFixture(TMyTestObject);
|
||||
procedure TMyTestObject.TestAsTValue;
|
||||
var
|
||||
dataValue: IDataValue;
|
||||
tv, element: TValue;
|
||||
now: TDateTime;
|
||||
colorType: IDataEnumType;
|
||||
intArrayType: IDataArrayType;
|
||||
personType: IDataRecordType;
|
||||
begin
|
||||
// --- Ordinal ---
|
||||
dataValue := TDataValue.FromOrdinal(123);
|
||||
tv := dataValue.AsTValue;
|
||||
Assert.IsFalse(tv.IsEmpty, 'Ordinal TValue should not be empty');
|
||||
Assert.AreEqual(tkInt64, tv.Kind, 'Ordinal TValue kind should be tkInt64');
|
||||
Assert.AreEqual(Int64(123), tv.AsInt64, 'Ordinal TValue content is incorrect');
|
||||
|
||||
// --- Float ---
|
||||
dataValue := TDataValue.FromFloat(45.67);
|
||||
tv := dataValue.AsTValue;
|
||||
Assert.IsFalse(tv.IsEmpty, 'Float TValue should not be empty');
|
||||
Assert.AreEqual(tkFloat, tv.Kind, 'Float TValue kind should be tkFloat');
|
||||
Assert.AreEqual(45.67, tv.AsExtended, 'Float TValue content is incorrect');
|
||||
|
||||
// --- Text ---
|
||||
dataValue := TDataValue.FromText('Hello');
|
||||
tv := dataValue.AsTValue;
|
||||
Assert.IsFalse(tv.IsEmpty, 'Text TValue should not be empty');
|
||||
Assert.AreEqual(tkUString, tv.Kind, 'Text TValue kind should be tkUString');
|
||||
Assert.AreEqual('Hello', tv.AsString, 'Text TValue content is incorrect');
|
||||
|
||||
// --- Timestamp ---
|
||||
now := System.SysUtils.Now;
|
||||
dataValue := TDataValue.FromTimestamp(now);
|
||||
tv := dataValue.AsTValue;
|
||||
Assert.IsFalse(tv.IsEmpty, 'Timestamp TValue should not be empty');
|
||||
Assert.AreEqual(tkFloat, tv.Kind, 'Timestamp TValue kind is incorrect');
|
||||
Assert.AreEqual(now, tv.AsType<TDateTime>, 'Timestamp TValue content is incorrect');
|
||||
|
||||
// --- Enum ---
|
||||
colorType := TDataType.EnumOf('Color', ['Red', 'Green', 'Blue']);
|
||||
dataValue := colorType.CreateValue('Green');
|
||||
tv := dataValue.AsTValue;
|
||||
Assert.IsFalse(tv.IsEmpty, 'Enum TValue should not be empty');
|
||||
Assert.AreEqual(tkInteger, tv.Kind, 'Enum TValue kind should be tkInteger');
|
||||
Assert.AreEqual(1, tv.AsInteger, 'Enum TValue content is incorrect');
|
||||
|
||||
// --- Array ---
|
||||
intArrayType := TDataType.ArrayOfType(TDataType.Ordinal);
|
||||
dataValue := intArrayType.CreateValue([TDataValue.FromOrdinal(10), TDataValue.FromOrdinal(20)]);
|
||||
tv := dataValue.AsTValue;
|
||||
Assert.IsFalse(tv.IsEmpty, 'Array TValue should not be empty');
|
||||
Assert.IsTrue(tv.IsArray, 'Array TValue should be an array');
|
||||
Assert.AreEqual(tkDynArray, tv.Kind, 'Array TValue kind should be tkDynArray');
|
||||
Assert.AreEqual(NativeInt(2), tv.GetArrayLength, 'Array TValue length is incorrect');
|
||||
|
||||
// Correctly unwrap the nested TValue before asserting kind and value
|
||||
element := tv.GetArrayElement(0).AsType<TValue>;
|
||||
Assert.AreEqual(tkInt64, element.Kind, 'Unwrapped array element 0 kind is incorrect');
|
||||
Assert.AreEqual(Int64(10), element.AsInt64, 'Array TValue element 0 is incorrect');
|
||||
|
||||
element := tv.GetArrayElement(1).AsType<TValue>;
|
||||
Assert.AreEqual(Int64(20), element.AsInt64, 'Array TValue element 1 is incorrect');
|
||||
|
||||
// Check empty array
|
||||
dataValue := intArrayType.CreateValue([]);
|
||||
tv := dataValue.AsTValue;
|
||||
Assert.IsTrue(tv.IsArray, 'Empty Array TValue should be an array');
|
||||
Assert.AreEqual(NativeInt(0), tv.GetArrayLength, 'Empty Array TValue length is incorrect');
|
||||
|
||||
// --- Record ---
|
||||
personType := TDataType.RecordOf([TRecordField.Create('ID', TDataType.Ordinal), TRecordField.Create('Name', TDataType.Text)]);
|
||||
dataValue := personType.CreateValue([TDataValue.FromOrdinal(1), TDataValue.FromText('Bob')]);
|
||||
tv := dataValue.AsTValue;
|
||||
Assert.IsFalse(tv.IsEmpty, 'Record TValue should not be empty');
|
||||
Assert.IsTrue(tv.IsArray, 'Record TValue should be represented as an array');
|
||||
Assert.AreEqual(tkDynArray, tv.Kind, 'Record TValue kind should be tkDynArray');
|
||||
Assert.AreEqual(NativeInt(2), tv.GetArrayLength, 'Record TValue length is incorrect');
|
||||
Assert.AreEqual(Int64(1), tv.GetArrayElement(0).AsType<TValue>.AsInt64, 'Record TValue element 0 is incorrect');
|
||||
Assert.AreEqual('Bob', tv.GetArrayElement(1).AsType<TValue>.AsString, 'Record TValue element 1 is incorrect');
|
||||
|
||||
// --- Tuple ---
|
||||
dataValue := TDataValue.FromTuple([TDataValue.FromOrdinal(99), TDataValue.FromText('Red')]);
|
||||
tv := dataValue.AsTValue;
|
||||
Assert.IsFalse(tv.IsEmpty, 'Tuple TValue should not be empty');
|
||||
Assert.IsTrue(tv.IsArray, 'Tuple TValue should be represented as an array');
|
||||
Assert.AreEqual(tkDynArray, tv.Kind, 'Tuple TValue kind should be tkDynArray');
|
||||
Assert.AreEqual(NativeInt(2), tv.GetArrayLength, 'Tuple TValue length is incorrect');
|
||||
Assert.AreEqual(Int64(99), tv.GetArrayElement(0).AsType<TValue>.AsInt64, 'Tuple TValue element 0 is incorrect');
|
||||
Assert.AreEqual('Red', tv.GetArrayElement(1).AsType<TValue>.AsString, 'Tuple TValue element 1 is incorrect');
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user