Files
MycLib/Src/Data/TestDataTypes.pas
T
Michael Schimmel c9e28a946d Data Types
2025-08-25 14:48:06 +02:00

397 lines
16 KiB
ObjectPascal

unit TestDataTypes;
interface
uses
DUnitX.TestFramework;
type
[TestFixture]
TMyTestObject = class
public
[Test]
procedure TestCreateRecords;
[Test]
procedure TestTuples;
[Test]
procedure TestArrays;
[Test]
procedure TestTexts;
[Test]
procedure TestTimestamps;
[Test]
procedure TestEnums;
[Test]
procedure TestAsString;
end;
implementation
uses
System.SysUtils,
Myc.Data.Types;
{ TMyTestObject }
procedure TMyTestObject.TestCreateRecords;
var
intType: IDataType;
floatType: IDataType;
personType1, personType2, otherType: IDataRecordType;
personValue: IDataValue;
idValue: IDataOrdinalValue;
floatValue: IDataFloatValue;
begin
// --- 1. Setup: Define base types and a record structure ---
intType := TDataType.Ordinal;
floatType := TDataType.Float;
// --- 2. Test Type Creation and Caching ---
personType1 := TDataType.RecordOf([TRecordField.Create('ID', intType), TRecordField.Create('Value', floatType)]);
// Assertions for the created type
Assert.IsNotNull(personType1, 'RecordType should be created');
Assert.AreEqual(2, personType1.FieldCount, 'FieldCount should be 2');
Assert.AreEqual('ID', personType1.Fields[0].Name, 'First field name should be ID');
Assert.AreSame(intType, personType1.Fields[0].DataType, 'First field type should be Integer');
Assert.AreEqual('Value', personType1.Fields[1].Name, 'Second field name should be Value');
Assert.AreSame(floatType, personType1.Fields[1].DataType, 'Second field type should be Float');
Assert.AreEqual(0, personType1.IndexOf('ID'), 'IndexOf ID should be 0');
Assert.AreEqual(1, personType1.IndexOf('Value'), 'IndexOf Value should be 1');
Assert.AreEqual('Record<ID: Integer, Value: Float>', personType1.Name, 'Type name should match expected format');
// Test if the same definition returns the same cached instance
personType2 := TDataType.RecordOf([TRecordField.Create('ID', intType), TRecordField.Create('Value', floatType)]);
Assert.AreSame(personType1, personType2, 'Types should be cached and return the same instance');
// Test if a different definition returns a new instance
otherType := TDataType.RecordOf([TRecordField.Create('ID', intType), TRecordField.Create('Data', floatType)]);
Assert.AreNotSame(personType1, otherType, 'Different definitions should result in different types');
// --- 3. Test Value Creation and Access ---
personValue := personType1.CreateValue([TDataValue.FromOrdinal(123), TDataValue.FromFloat(45.67)]);
Assert.IsNotNull(TDataValue(personValue).AsRecord, 'RecordValue should be created');
Assert.AreSame(personType1, TDataValue(personValue).DataType, 'Value should have the correct data type');
// Access by index
idValue := TDataValue(TDataValue(personValue).AsRecord.Items[0]).AsOrdinal;
Assert.AreEqual(Int64(123), idValue.Value, 'Value at index 0 is incorrect');
floatValue := TDataValue(TDataValue(personValue).AsRecord.Items[1]).AsFloat;
Assert.AreEqual(45.67, floatValue.Value, 'Value at index 1 is incorrect');
// Access by name
floatValue := TDataValue(TDataValue(personValue).AsRecord.Items[personType1.IndexOf('Value')]).AsFloat;
Assert.AreEqual(45.67, floatValue.Value, 'Value accessed by name is incorrect');
// --- 4. Test Validation and Error Handling ---
// Test for duplicate field names during type creation
Assert.WillRaise(
procedure begin TDataType.RecordOf([TRecordField.Create('ID', intType), TRecordField.Create('ID', floatType)]); end,
EArgumentException,
'Duplicate field names should raise an exception'
);
// Test for wrong number of items during value creation
Assert.WillRaise(
procedure begin personType1.CreateValue([TDataValue.FromOrdinal(99)]); end,
EArgumentException,
'Wrong number of items should raise exception'
);
// Test for wrong item type during value creation
Assert.WillRaise(
procedure
begin
// Passing Float instead of Integer for the first item
personType1.CreateValue([TDataValue.FromFloat(1.0), TDataValue.FromFloat(2.0)]);
end,
EArgumentException,
'Wrong item type should raise exception'
);
end;
procedure TMyTestObject.TestTuples;
var
intValue: IDataValue;
floatValue: IDataValue;
tuple1, tuple2, tuple3: IDataTupleValue;
type1, type2, type3: IDataType;
begin
// --- 1. Setup: Create some values ---
intValue := TDataValue.FromOrdinal(123);
floatValue := TDataValue.FromFloat(45.67);
// --- 2. Test Value Creation and basic properties ---
tuple1 := TDataValue.FromTuple([intValue, floatValue]);
Assert.IsNotNull(tuple1, 'Tuple value should be created');
Assert.AreEqual(2, TDataValue(tuple1).AsTuple.ItemCount, 'ItemCount should be on the value');
Assert.AreSame(intValue, TDataValue(tuple1).AsTuple.Items[0], 'Item at index 0 is incorrect');
Assert.AreSame(floatValue, TDataValue(tuple1).AsTuple.Items[1], 'Item at index 1 is incorrect');
// --- 3. Test Singleton Type Behavior ---
// Create more tuples with different structures
tuple2 := TDataValue.FromTuple([TDataValue.FromOrdinal(99), TDataValue.FromFloat(1.1)]);
tuple3 := TDataValue.FromTuple([intValue]);
// Access the DataType via the underlying interface
type1 := tuple1.DataType;
type2 := tuple2.DataType;
type3 := tuple3.DataType;
Assert.IsNotNull(type1, 'DataType interface should be accessible');
Assert.AreEqual('Tuple', type1.Name, 'The type name for all tuples should be Tuple');
// The core test: All tuples, regardless of their content, must share the exact same singleton type object.
Assert.AreSame(type1, type2, 'All tuple types should be the same singleton instance');
Assert.AreSame(type1, type3, 'All tuple types should be the same singleton instance');
end;
procedure TMyTestObject.TestArrays;
var
intType: IDataType;
floatType: IDataType;
intArrayType1, intArrayType2, floatArrayType: IDataArrayType;
arrayValue: IDataArrayValue;
v1, v2: IDataValue;
begin
// --- 1. Setup ---
intType := TDataType.Ordinal;
floatType := TDataType.Float;
// --- 2. Test Type Creation and Caching ---
intArrayType1 := TDataType.ArrayOfType(intType);
Assert.IsNotNull(intArrayType1, 'ArrayType should be created');
Assert.AreSame(intType, intArrayType1.ElementType, 'ElementType should be Integer');
Assert.AreEqual('Array<Integer>', intArrayType1.Name, 'Type name should be Array<Integer>');
// Test caching
intArrayType2 := TDataType.ArrayOfType(intType);
Assert.AreSame(intArrayType1, intArrayType2, 'Array types should be cached');
// Test uniqueness
floatArrayType := TDataType.ArrayOfType(floatType);
Assert.AreNotSame(intArrayType1, floatArrayType, 'Different element types should result in different array types');
Assert.AreEqual('Array<Float>', floatArrayType.Name, 'Type name should be Array<Float>');
// --- 3. Test Value Creation and Access ---
v1 := TDataValue.FromOrdinal(10);
v2 := TDataValue.FromOrdinal(20);
arrayValue := intArrayType1.CreateValue([v1, v2]);
Assert.IsNotNull(arrayValue, 'ArrayValue should be created');
Assert.AreEqual(2, TDataValue(arrayValue).AsArray.ElementCount, 'ElementCount should be 2');
// Access items and check values
Assert.AreEqual(Int64(10), TDataValue(TDataValue(arrayValue).AsArray.Items[0]).AsOrdinal.Value, 'Item at index 0 is incorrect');
Assert.AreEqual(Int64(20), TDataValue(TDataValue(arrayValue).AsArray.Items[1]).AsOrdinal.Value, 'Item at index 1 is incorrect');
// --- 4. Test Validation and Error Handling ---
// Test creating an array with a nil element type
Assert.WillRaise(procedure begin TDataType.ArrayOfType(nil); end, EArgumentException, 'Nil element type should raise an exception');
// Test creating a value with an incorrect element type (homogeneity check)
Assert.WillRaise(
procedure
begin
// Try to add a float value to an Array<Integer>
intArrayType1.CreateValue([v1, TDataValue.FromFloat(3.14)]);
end,
EArgumentException,
'Wrong element type should raise an exception'
);
end;
procedure TMyTestObject.TestTexts;
var
textType: IDataTextType;
textValue1, textValue2: IDataTextValue;
dataValue: IDataValue;
begin
// --- 1. Test Type Creation ---
textType := TDataType.Text;
Assert.IsNotNull(textType, 'TextType should be created');
Assert.AreEqual('Text', textType.Name, 'Type name should be Text');
Assert.AreEqual(dkText, textType.Kind, 'Type kind should be dkText');
// --- 2. Test Value Creation and Access ---
textValue1 := TDataValue.FromText('Hello World');
Assert.IsNotNull(textValue1, 'TextValue should be created');
Assert.AreSame(textType, textValue1.DataType, 'Value should have the correct data type');
Assert.AreEqual('Hello World', textValue1.Value, 'Value should be ''Hello World''');
// Test another text value
textValue2 := TDataValue.FromText('Delphi');
Assert.AreEqual('Delphi', textValue2.Value, 'Value should be ''Delphi''');
// --- 3. Test AsText casting ---
dataValue := TDataValue.FromText('Test Text');
Assert.AreEqual('Test Text', TDataValue(dataValue).AsText.Value, 'AsText should return correct value');
// Test invalid cast
dataValue := TDataValue.FromOrdinal(123);
Assert
.WillRaise(procedure begin TDataValue(dataValue).AsText; end, EInvalidCast, 'Casting non-text to AsText should raise EInvalidCast');
end;
procedure TMyTestObject.TestTimestamps;
var
tsType: IDataTimestampType;
tsValue1, tsValue2: IDataTimestampValue;
dataValue: IDataValue;
now: TDateTime;
begin
// --- 1. Test Type Creation ---
tsType := TDataType.Timestamp;
Assert.IsNotNull(tsType, 'TimestampType should be created');
Assert.AreEqual('Timestamp', tsType.Name, 'Type name should be Timestamp');
Assert.AreEqual(dkTimestamp, tsType.Kind, 'Type kind should be dkTimestamp');
// --- 2. Test Value Creation and Access ---
now := System.Sysutils.Now;
tsValue1 := TDataValue.FromTimestamp(now);
Assert.IsNotNull(tsValue1, 'TimestampValue should be created');
Assert.AreSame(tsType, tsValue1.DataType, 'Value should have the correct data type');
Assert.AreEqual(now, tsValue1.Value, 'Value should be the same');
// Test another text value
tsValue2 := TDataValue.FromTimestamp(Date);
Assert.AreEqual(Date, tsValue2.Value, 'Value should be the same');
// --- 3. Test AsTimestamp casting ---
dataValue := TDataValue.FromTimestamp(now);
Assert.AreEqual(now, TDataValue(dataValue).AsTimestamp.Value, 'AsTimestamp should return correct value');
// Test invalid cast
dataValue := TDataValue.FromOrdinal(123);
Assert.WillRaise(
procedure begin TDataValue(dataValue).AsTimestamp; end,
EInvalidCast,
'Casting non-timestamp to AsTimestamp should raise EInvalidCast'
);
end;
procedure TMyTestObject.TestEnums;
var
colorType: IDataEnumType;
red, green, blue: IDataEnumValue;
begin
// --- 1. Test Type Creation ---
colorType := TDataType.EnumOf('Color', ['Red', 'Green', 'Blue']);
Assert.IsNotNull(colorType, 'EnumType should be created');
Assert.AreEqual('Color', colorType.Name, 'Name should be Color');
Assert.AreEqual(dkEnum, colorType.Kind, 'Kind should be dkEnum');
Assert.AreEqual(3, colorType.IdentifierCount, 'IdentifierCount should be 3');
Assert.AreEqual('Red', colorType.Identifiers[0], 'Identifier at index 0 should be Red');
Assert.AreEqual('Green', colorType.Identifiers[1], 'Identifier at index 1 should be Green');
Assert.AreEqual('Blue', colorType.Identifiers[2], 'Identifier at index 2 should be Blue');
Assert.AreEqual(1, colorType.IndexOf('Green'), 'IndexOf Green should be 1');
Assert.AreEqual(-1, colorType.IndexOf('Yellow'), 'IndexOf Yellow should be -1');
// --- 2. Test Value Creation and Access ---
red := colorType.CreateValue(0);
green := colorType.CreateValue('Green');
blue := TDataValue(colorType.CreateValue(2)).AsEnum;
Assert.IsNotNull(red, 'EnumValue from index should be created');
Assert.AreSame(colorType, red.DataType, 'Red should have the correct data type');
Assert.AreEqual(0, red.Value, 'Value of Red should be 0');
Assert.AreEqual('Red', red.AsString, 'AsText of Red should be Red');
Assert.IsNotNull(green, 'EnumValue from identifier should be created');
Assert.AreSame(colorType, green.DataType, 'Green should have the correct data type');
Assert.AreEqual(1, green.Value, 'Value of Green should be 1');
Assert.AreEqual('Green', green.AsString, 'AsText of Green should be Green');
Assert.AreEqual(2, blue.Value, 'Value of Blue should be 2');
// --- 3. Test Validation and Error Handling ---
// Test creating a type with duplicate identifiers
Assert.WillRaise(
procedure begin TDataType.EnumOf('Fails', ['A', 'B', 'A']); end,
EArgumentException,
'Duplicate identifiers should raise an exception'
);
// Test creating a value with an invalid index
Assert.WillRaise(procedure begin colorType.CreateValue(3); end, EArgumentException, 'Invalid index should raise an exception');
Assert.WillRaise(procedure begin colorType.CreateValue(-1); end, EArgumentException, 'Negative index should raise an exception');
// Test creating a value with an unknown identifier
Assert.WillRaise(
procedure begin colorType.CreateValue('Yellow'); end,
EArgumentException,
'Unknown identifier should raise an exception'
);
end;
procedure TMyTestObject.TestAsString;
var
ordinalValue: IDataOrdinalValue;
floatValue: IDataFloatValue;
textValue: IDataTextValue;
tsValue: IDataTimestampValue;
enumValue: IDataEnumValue;
arrayValue: IDataArrayValue;
recordValue: IDataRecordValue;
tupleValue: IDataTupleValue;
colorType: IDataEnumType;
personType: IDataRecordType;
intArrayType: IDataArrayType;
now: TDateTime;
begin
// Ordinal
ordinalValue := TDataValue.FromOrdinal(123);
Assert.AreEqual('123', ordinalValue.AsString, 'Ordinal AsString incorrect');
// Float
floatValue := TDataValue.FromFloat(45.67);
Assert.AreEqual(FloatToStr(45.67), floatValue.AsString, 'Float AsString incorrect');
// Text
textValue := TDataValue.FromText('Hello');
Assert.AreEqual('Hello', textValue.AsString, 'Text AsString incorrect');
// Timestamp
now := System.Sysutils.Now;
tsValue := TDataValue.FromTimestamp(now);
Assert.AreEqual(DateTimeToStr(now), tsValue.AsString, 'Timestamp AsString incorrect');
// Enum
colorType := TDataType.EnumOf('Color', ['Red', 'Green', 'Blue']);
enumValue := colorType.CreateValue('Green');
Assert.AreEqual('Green', enumValue.AsString, 'Enum AsString incorrect');
// Array
intArrayType := TDataType.ArrayOfType(TDataType.Ordinal);
arrayValue := intArrayType.CreateValue([TDataValue.FromOrdinal(1), TDataValue.FromOrdinal(2)]);
Assert.AreEqual('[1, 2]', arrayValue.AsString, 'Array AsString incorrect');
// Record
personType := TDataType.RecordOf([TRecordField.Create('ID', TDataType.Ordinal), TRecordField.Create('Name', TDataType.Text)]);
recordValue := personType.CreateValue([TDataValue.FromOrdinal(1), TDataValue.FromText('Bob')]);
Assert.AreEqual('<ID: 1, Name: Bob>', recordValue.AsString, 'Record AsString incorrect');
// Tuple
tupleValue := TDataValue.FromTuple([TDataValue.FromOrdinal(10), TDataValue.FromText('Tuple')]);
Assert.AreEqual('(10, Tuple)', tupleValue.AsString, 'Tuple AsString incorrect');
end;
initialization
TDUnitX.RegisterTestFixture(TMyTestObject);
end.