Indicator with new types
This commit is contained in:
+89
-14
@@ -8,7 +8,7 @@ uses
|
|||||||
System.RTTI;
|
System.RTTI;
|
||||||
|
|
||||||
type
|
type
|
||||||
TDataKind = (dkOrdinal, dkFloat, dkText, dkTimestamp, dkRecord, dkTuple, dkArray, dkEnum);
|
TDataKind = (dkOrdinal, dkFloat, dkText, dkTimestamp, dkRecord, dkTuple, dkArray, dkEnum, dkMethod);
|
||||||
|
|
||||||
IDataType = interface
|
IDataType = interface
|
||||||
function GetName: String;
|
function GetName: String;
|
||||||
@@ -76,6 +76,28 @@ type
|
|||||||
property Identifiers[Idx: Integer]: string read GetIdentifier; default;
|
property Identifiers[Idx: Integer]: string read GetIdentifier; default;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// A method that transforms one data value into another.
|
||||||
|
TMethodProc = reference to function(const AValue: IDataValue): IDataValue;
|
||||||
|
|
||||||
|
// Represents an executable method data value.
|
||||||
|
IDataMethodValue = interface(IDataValue)
|
||||||
|
{$region 'private'}
|
||||||
|
function GetValue: TMethodProc;
|
||||||
|
{$endregion}
|
||||||
|
property Value: TMethodProc read GetValue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Represents the type of a method data value, including its signature.
|
||||||
|
IDataMethodType = interface(IDataType)
|
||||||
|
{$region 'private'}
|
||||||
|
function GetArgType: IDataType;
|
||||||
|
function GetResultType: IDataType;
|
||||||
|
{$endregion}
|
||||||
|
property ArgType: IDataType read GetArgType;
|
||||||
|
property ResultType: IDataType read GetResultType;
|
||||||
|
function CreateValue(const AValue: TMethodProc): IDataMethodValue;
|
||||||
|
end;
|
||||||
|
|
||||||
IDataRecordValue = interface(IDataValue)
|
IDataRecordValue = interface(IDataValue)
|
||||||
function GetItem(Idx: Integer): IDataValue;
|
function GetItem(Idx: Integer): IDataValue;
|
||||||
property Items[Idx: Integer]: IDataValue read GetItem; default;
|
property Items[Idx: Integer]: IDataValue read GetItem; default;
|
||||||
@@ -125,18 +147,15 @@ type
|
|||||||
FDataType: IDataType;
|
FDataType: IDataType;
|
||||||
function GetName: String; inline;
|
function GetName: String; inline;
|
||||||
function GetKind: TDataKind; inline;
|
function GetKind: TDataKind; inline;
|
||||||
|
|
||||||
class var
|
class var
|
||||||
FArrayTypeRegistry: TDictionary<IDataType, IDataArrayType>;
|
FArrayTypeRegistry: TDictionary<IDataType, IDataArrayType>;
|
||||||
class var
|
|
||||||
FRecordTypeRegistry: TDictionary<TArray<TRecordField>, IDataRecordType>;
|
FRecordTypeRegistry: TDictionary<TArray<TRecordField>, IDataRecordType>;
|
||||||
|
FMethodTypeRegistry: TDictionary<TPair<IDataType, IDataType>, IDataMethodType>;
|
||||||
class constructor CreateClass;
|
class constructor CreateClass;
|
||||||
class destructor DestroyClass;
|
class destructor DestroyClass;
|
||||||
public
|
public
|
||||||
constructor Create(const ADataType: IDataType);
|
constructor Create(const ADataType: IDataType);
|
||||||
|
|
||||||
class procedure ClearRegistries; static;
|
|
||||||
|
|
||||||
class operator Implicit(const A: IDataType): TDataType; overload;
|
class operator Implicit(const A: IDataType): TDataType; overload;
|
||||||
class operator Implicit(const A: TDataType): IDataType; overload;
|
class operator Implicit(const A: TDataType): IDataType; overload;
|
||||||
|
|
||||||
@@ -146,7 +165,8 @@ type
|
|||||||
class function Text: IDataTextType; static;
|
class function Text: IDataTextType; static;
|
||||||
class function Timestamp: IDataTimestampType; static;
|
class function Timestamp: IDataTimestampType; static;
|
||||||
class function Tuple: IDataTupleType; static;
|
class function Tuple: IDataTupleType; static;
|
||||||
class function ArrayOfType(const AElementType: IDataType): IDataArrayType; static;
|
class function MethodOf(const AArgType, AResultType: IDataType): IDataMethodType; static;
|
||||||
|
class function ArrayOf(const AElementType: IDataType): IDataArrayType; static;
|
||||||
class function RecordOf(const Fields: TArray<TRecordField>): IDataRecordType; overload; static;
|
class function RecordOf(const Fields: TArray<TRecordField>): IDataRecordType; overload; static;
|
||||||
class function RecordOf(const Fields: array of TRecordField): IDataRecordType; overload; static;
|
class function RecordOf(const Fields: array of TRecordField): IDataRecordType; overload; static;
|
||||||
class function EnumOf(const AName: string; const AIdentifiers: array of string): IDataEnumType; static;
|
class function EnumOf(const AName: string; const AIdentifiers: array of string): IDataEnumType; static;
|
||||||
@@ -160,6 +180,7 @@ type
|
|||||||
function AsTuple: IDataTupleType;
|
function AsTuple: IDataTupleType;
|
||||||
function AsArray: IDataArrayType;
|
function AsArray: IDataArrayType;
|
||||||
function AsEnum: IDataEnumType;
|
function AsEnum: IDataEnumType;
|
||||||
|
function AsMethod: IDataMethodType;
|
||||||
|
|
||||||
property DataType: IDataType read FDataType;
|
property DataType: IDataType read FDataType;
|
||||||
property Name: String read GetName;
|
property Name: String read GetName;
|
||||||
@@ -182,6 +203,7 @@ type
|
|||||||
function AsTuple: IDataTupleValue;
|
function AsTuple: IDataTupleValue;
|
||||||
function AsArray: IDataArrayValue;
|
function AsArray: IDataArrayValue;
|
||||||
function AsEnum: IDataEnumValue;
|
function AsEnum: IDataEnumValue;
|
||||||
|
function AsMethod: IDataMethodValue;
|
||||||
|
|
||||||
class operator Implicit(const A: IDataValue): TDataValue; overload;
|
class operator Implicit(const A: IDataValue): TDataValue; overload;
|
||||||
class operator Implicit(const A: TDataValue): IDataValue; overload;
|
class operator Implicit(const A: TDataValue): IDataValue; overload;
|
||||||
@@ -192,6 +214,7 @@ type
|
|||||||
class function FromText(const AValue: string): IDataTextValue; static;
|
class function FromText(const AValue: string): IDataTextValue; static;
|
||||||
class function FromTimestamp(const AValue: TDateTime): IDataTimestampValue; static;
|
class function FromTimestamp(const AValue: TDateTime): IDataTimestampValue; static;
|
||||||
class function FromTuple(const AItems: array of IDataValue): IDataTupleValue; static;
|
class function FromTuple(const AItems: array of IDataValue): IDataTupleValue; static;
|
||||||
|
class function FromMethod(const AMethodType: IDataMethodType; const AValue: TMethodProc): IDataMethodValue; static;
|
||||||
|
|
||||||
property DataValue: IDataValue read FDataValue;
|
property DataValue: IDataValue read FDataValue;
|
||||||
property DataType: IDataType read GetDataType;
|
property DataType: IDataType read GetDataType;
|
||||||
@@ -209,7 +232,8 @@ uses
|
|||||||
Myc.Data.Types.Arrays,
|
Myc.Data.Types.Arrays,
|
||||||
Myc.Data.Types.Records,
|
Myc.Data.Types.Records,
|
||||||
Myc.Data.Types.Tuple,
|
Myc.Data.Types.Tuple,
|
||||||
Myc.Data.Types.Enum;
|
Myc.Data.Types.Enum,
|
||||||
|
Myc.Data.Types.Method;
|
||||||
|
|
||||||
{ TRecordField }
|
{ TRecordField }
|
||||||
|
|
||||||
@@ -230,15 +254,34 @@ class constructor TDataType.CreateClass;
|
|||||||
begin
|
begin
|
||||||
FArrayTypeRegistry := TDictionary<IDataType, IDataArrayType>.Create;
|
FArrayTypeRegistry := TDictionary<IDataType, IDataArrayType>.Create;
|
||||||
FRecordTypeRegistry := TDictionary<TArray<TRecordField>, IDataRecordType>.Create(TRecordFieldComparer.Create);
|
FRecordTypeRegistry := TDictionary<TArray<TRecordField>, IDataRecordType>.Create(TRecordFieldComparer.Create);
|
||||||
|
FMethodTypeRegistry :=
|
||||||
|
TDictionary<TPair<IDataType, IDataType>, IDataMethodType>.Create(
|
||||||
|
TEqualityComparer<TPair<IDataType, IDataType>>.Construct(
|
||||||
|
function(const Left, Right: TPair<IDataType, IDataType>): Boolean
|
||||||
|
begin
|
||||||
|
Result := (Left.Key = Right.Key) and (Left.Value = Right.Value);
|
||||||
|
end,
|
||||||
|
function(const Value: TPair<IDataType, IDataType>): Integer
|
||||||
|
var
|
||||||
|
hash1, hash2: NativeInt;
|
||||||
|
begin
|
||||||
|
hash1 := NativeInt(Value.Key);
|
||||||
|
hash2 := NativeInt(Value.Value);
|
||||||
|
// Simple XOR combination for pointer hashes
|
||||||
|
Result := Integer(hash1 xor hash2);
|
||||||
|
end
|
||||||
|
)
|
||||||
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class destructor TDataType.DestroyClass;
|
class destructor TDataType.DestroyClass;
|
||||||
begin
|
begin
|
||||||
FArrayTypeRegistry.Free;
|
FArrayTypeRegistry.Free;
|
||||||
FRecordTypeRegistry.Free;
|
FRecordTypeRegistry.Free;
|
||||||
|
FMethodTypeRegistry.Free;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TDataType.ArrayOfType(const AElementType: IDataType): IDataArrayType;
|
class function TDataType.ArrayOf(const AElementType: IDataType): IDataArrayType;
|
||||||
begin
|
begin
|
||||||
if not Assigned(AElementType) then
|
if not Assigned(AElementType) then
|
||||||
raise EArgumentException.Create('Cannot create an array type with a nil element type.');
|
raise EArgumentException.Create('Cannot create an array type with a nil element type.');
|
||||||
@@ -277,6 +320,13 @@ begin
|
|||||||
Result := IDataFloatType(FDataType);
|
Result := IDataFloatType(FDataType);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TDataType.AsMethod: IDataMethodType;
|
||||||
|
begin
|
||||||
|
if (FDataType.Kind <> dkMethod) then
|
||||||
|
raise EInvalidCast.Create('Method expected');
|
||||||
|
Result := IDataMethodType(FDataType);
|
||||||
|
end;
|
||||||
|
|
||||||
function TDataType.AsOrdinal: IDataOrdinalType;
|
function TDataType.AsOrdinal: IDataOrdinalType;
|
||||||
begin
|
begin
|
||||||
if (FDataType.Kind <> dkOrdinal) then
|
if (FDataType.Kind <> dkOrdinal) then
|
||||||
@@ -312,12 +362,6 @@ begin
|
|||||||
Result := IDataTupleType(FDataType);
|
Result := IDataTupleType(FDataType);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class procedure TDataType.ClearRegistries;
|
|
||||||
begin
|
|
||||||
FArrayTypeRegistry.Clear;
|
|
||||||
FRecordTypeRegistry.Clear;
|
|
||||||
end;
|
|
||||||
|
|
||||||
class function TDataType.Float: IDataFloatType;
|
class function TDataType.Float: IDataFloatType;
|
||||||
begin
|
begin
|
||||||
Result := TDataFloatType.Singleton;
|
Result := TDataFloatType.Singleton;
|
||||||
@@ -331,6 +375,23 @@ begin
|
|||||||
Result := '';
|
Result := '';
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
class function TDataType.MethodOf(const AArgType, AResultType: IDataType): IDataMethodType;
|
||||||
|
var
|
||||||
|
key: TPair<IDataType, IDataType>;
|
||||||
|
begin
|
||||||
|
key := TPair<IDataType, IDataType>.Create(AArgType, AResultType);
|
||||||
|
TMonitor.Enter(FMethodTypeRegistry);
|
||||||
|
try
|
||||||
|
if not FMethodTypeRegistry.TryGetValue(key, Result) then
|
||||||
|
begin
|
||||||
|
Result := TDataMethodType.Create(AArgType, AResultType);
|
||||||
|
FMethodTypeRegistry.Add(key, Result);
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
TMonitor.Exit(FMethodTypeRegistry);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
class function TDataType.Ordinal: IDataOrdinalType;
|
class function TDataType.Ordinal: IDataOrdinalType;
|
||||||
begin
|
begin
|
||||||
Result := TDataOrdinalType.Singleton;
|
Result := TDataOrdinalType.Singleton;
|
||||||
@@ -417,6 +478,13 @@ begin
|
|||||||
Result := IDataFloatValue(FDataValue);
|
Result := IDataFloatValue(FDataValue);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TDataValue.AsMethod: IDataMethodValue;
|
||||||
|
begin
|
||||||
|
if FDataValue.DataType.Kind <> dkMethod then
|
||||||
|
raise EInvalidCast.Create('Method expected');
|
||||||
|
Result := IDataMethodValue(FDataValue);
|
||||||
|
end;
|
||||||
|
|
||||||
function TDataValue.AsText: IDataTextValue;
|
function TDataValue.AsText: IDataTextValue;
|
||||||
begin
|
begin
|
||||||
if FDataValue.DataType.Kind <> dkText then
|
if FDataValue.DataType.Kind <> dkText then
|
||||||
@@ -469,6 +537,13 @@ begin
|
|||||||
Result := TDataFloatType.Singleton.CreateValue(AValue);
|
Result := TDataFloatType.Singleton.CreateValue(AValue);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
class function TDataValue.FromMethod(const AMethodType: IDataMethodType; const AValue: TMethodProc): IDataMethodValue;
|
||||||
|
begin
|
||||||
|
if not Assigned(AMethodType) then
|
||||||
|
raise EArgumentException.Create('AMethodType');
|
||||||
|
Result := AMethodType.CreateValue(AValue);
|
||||||
|
end;
|
||||||
|
|
||||||
class function TDataValue.FromText(const AValue: string): IDataTextValue;
|
class function TDataValue.FromText(const AValue: string): IDataTextValue;
|
||||||
begin
|
begin
|
||||||
Result := TDataTextType.Singleton.CreateValue(AValue);
|
Result := TDataTextType.Singleton.CreateValue(AValue);
|
||||||
|
|||||||
+91
-19
@@ -10,9 +10,6 @@ type
|
|||||||
[IgnoreMemoryLeaks]
|
[IgnoreMemoryLeaks]
|
||||||
TMyTestObject = class
|
TMyTestObject = class
|
||||||
public
|
public
|
||||||
[TestFixtureTearDown]
|
|
||||||
procedure FixtureTearDown;
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
procedure TestRecords;
|
procedure TestRecords;
|
||||||
[Test]
|
[Test]
|
||||||
@@ -39,6 +36,8 @@ type
|
|||||||
procedure TestAsString;
|
procedure TestAsString;
|
||||||
[Test]
|
[Test]
|
||||||
procedure TestAsTValue;
|
procedure TestAsTValue;
|
||||||
|
[Test]
|
||||||
|
procedure TestMethods;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
@@ -50,15 +49,6 @@ uses
|
|||||||
System.TypInfo,
|
System.TypInfo,
|
||||||
Myc.Data.Types;
|
Myc.Data.Types;
|
||||||
|
|
||||||
{ TMyTestObject }
|
|
||||||
|
|
||||||
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;
|
procedure TMyTestObject.TestRecords;
|
||||||
var
|
var
|
||||||
intType: IDataType;
|
intType: IDataType;
|
||||||
@@ -236,18 +226,18 @@ begin
|
|||||||
floatType := TDataType.Float;
|
floatType := TDataType.Float;
|
||||||
|
|
||||||
// --- 2. Test Type Creation and Caching ---
|
// --- 2. Test Type Creation and Caching ---
|
||||||
intArrayType1 := TDataType.ArrayOfType(intType);
|
intArrayType1 := TDataType.ArrayOf(intType);
|
||||||
|
|
||||||
Assert.IsNotNull(intArrayType1, 'ArrayType should be created');
|
Assert.IsNotNull(intArrayType1, 'ArrayType should be created');
|
||||||
Assert.AreSame(intType, intArrayType1.ElementType, 'ElementType should be Integer');
|
Assert.AreSame(intType, intArrayType1.ElementType, 'ElementType should be Integer');
|
||||||
Assert.AreEqual('Array<Integer>', intArrayType1.Name, 'Type name should be Array<Integer>');
|
Assert.AreEqual('Array<Integer>', intArrayType1.Name, 'Type name should be Array<Integer>');
|
||||||
|
|
||||||
// Test caching
|
// Test caching
|
||||||
intArrayType2 := TDataType.ArrayOfType(intType);
|
intArrayType2 := TDataType.ArrayOf(intType);
|
||||||
Assert.AreSame(intArrayType1, intArrayType2, 'Array types should be cached');
|
Assert.AreSame(intArrayType1, intArrayType2, 'Array types should be cached');
|
||||||
|
|
||||||
// Test uniqueness
|
// Test uniqueness
|
||||||
floatArrayType := TDataType.ArrayOfType(floatType);
|
floatArrayType := TDataType.ArrayOf(floatType);
|
||||||
Assert.AreNotSame(intArrayType1, floatArrayType, 'Different element types should result in different array types');
|
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>');
|
Assert.AreEqual('Array<Float>', floatArrayType.Name, 'Type name should be Array<Float>');
|
||||||
|
|
||||||
@@ -265,7 +255,7 @@ begin
|
|||||||
|
|
||||||
// --- 4. Test Validation and Error Handling ---
|
// --- 4. Test Validation and Error Handling ---
|
||||||
// Test creating an array with a nil element type
|
// 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');
|
Assert.WillRaise(procedure begin TDataType.ArrayOf(nil); end, EArgumentException, 'Nil element type should raise an exception');
|
||||||
|
|
||||||
// Test creating a value with an incorrect element type (homogeneity check)
|
// Test creating a value with an incorrect element type (homogeneity check)
|
||||||
Assert.WillRaise(
|
Assert.WillRaise(
|
||||||
@@ -284,7 +274,7 @@ var
|
|||||||
arrayType: IDataArrayType;
|
arrayType: IDataArrayType;
|
||||||
empty1, empty2, single, generic: IDataArrayValue;
|
empty1, empty2, single, generic: IDataArrayValue;
|
||||||
begin
|
begin
|
||||||
arrayType := TDataType.ArrayOfType(TDataType.Ordinal);
|
arrayType := TDataType.ArrayOf(TDataType.Ordinal);
|
||||||
|
|
||||||
// Test optimized path for 0 elements (cached singleton per type)
|
// Test optimized path for 0 elements (cached singleton per type)
|
||||||
empty1 := arrayType.CreateValue([]);
|
empty1 := arrayType.CreateValue([]);
|
||||||
@@ -515,7 +505,7 @@ begin
|
|||||||
Assert.AreEqual('Green', enumValue.AsString, 'Enum AsString incorrect');
|
Assert.AreEqual('Green', enumValue.AsString, 'Enum AsString incorrect');
|
||||||
|
|
||||||
// Array
|
// Array
|
||||||
intArrayType := TDataType.ArrayOfType(TDataType.Ordinal);
|
intArrayType := TDataType.ArrayOf(TDataType.Ordinal);
|
||||||
arrayValue := intArrayType.CreateValue([TDataValue.FromOrdinal(1), TDataValue.FromOrdinal(2)]);
|
arrayValue := intArrayType.CreateValue([TDataValue.FromOrdinal(1), TDataValue.FromOrdinal(2)]);
|
||||||
Assert.AreEqual('[1, 2]', arrayValue.AsString, 'Array AsString incorrect');
|
Assert.AreEqual('[1, 2]', arrayValue.AsString, 'Array AsString incorrect');
|
||||||
|
|
||||||
@@ -576,7 +566,7 @@ begin
|
|||||||
Assert.AreEqual(1, tv.AsInteger, 'Enum TValue content is incorrect');
|
Assert.AreEqual(1, tv.AsInteger, 'Enum TValue content is incorrect');
|
||||||
|
|
||||||
// --- Array ---
|
// --- Array ---
|
||||||
intArrayType := TDataType.ArrayOfType(TDataType.Ordinal);
|
intArrayType := TDataType.ArrayOf(TDataType.Ordinal);
|
||||||
dataValue := intArrayType.CreateValue([TDataValue.FromOrdinal(10), TDataValue.FromOrdinal(20)]);
|
dataValue := intArrayType.CreateValue([TDataValue.FromOrdinal(10), TDataValue.FromOrdinal(20)]);
|
||||||
tv := dataValue.AsTValue;
|
tv := dataValue.AsTValue;
|
||||||
Assert.IsFalse(tv.IsEmpty, 'Array TValue should not be empty');
|
Assert.IsFalse(tv.IsEmpty, 'Array TValue should not be empty');
|
||||||
@@ -620,4 +610,86 @@ begin
|
|||||||
Assert.AreEqual('Red', tv.GetArrayElement(1).AsType<TValue>.AsString, 'Tuple TValue element 1 is incorrect');
|
Assert.AreEqual('Red', tv.GetArrayElement(1).AsType<TValue>.AsString, 'Tuple TValue element 1 is incorrect');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TMyTestObject.TestMethods;
|
||||||
|
var
|
||||||
|
ordinalType, textType: IDataType;
|
||||||
|
methodType1, methodType2, otherMethodType: IDataMethodType;
|
||||||
|
myFunc: TMethodProc;
|
||||||
|
methodValue: IDataMethodValue;
|
||||||
|
inputValue, resultValue: IDataValue;
|
||||||
|
tv: TValue;
|
||||||
|
begin
|
||||||
|
// --- 1. Setup: Define base types ---
|
||||||
|
ordinalType := TDataType.Ordinal;
|
||||||
|
textType := TDataType.Text;
|
||||||
|
|
||||||
|
// --- 2. Test Type Creation and Caching ---
|
||||||
|
methodType1 := TDataType.MethodOf(ordinalType, textType);
|
||||||
|
|
||||||
|
// Assertions for the created type
|
||||||
|
Assert.IsNotNull(methodType1, 'MethodType should be created');
|
||||||
|
Assert.AreEqual(dkMethod, methodType1.Kind, 'Kind should be dkMethod');
|
||||||
|
Assert.AreSame(ordinalType, methodType1.ArgType, 'ArgType should be Ordinal');
|
||||||
|
Assert.AreSame(textType, methodType1.ResultType, 'ResultType should be Text');
|
||||||
|
Assert.AreEqual('Method<Integer -> Text>', methodType1.Name, 'Type name should match expected format');
|
||||||
|
|
||||||
|
// Test if the same definition returns the same cached instance
|
||||||
|
methodType2 := TDataType.MethodOf(ordinalType, textType);
|
||||||
|
Assert.AreSame(methodType1, methodType2, 'Method types should be cached and return the same instance');
|
||||||
|
|
||||||
|
// Test if a different definition returns a new instance
|
||||||
|
otherMethodType := TDataType.MethodOf(textType, ordinalType);
|
||||||
|
Assert.AreNotSame(methodType1, otherMethodType, 'Different signatures should result in different types');
|
||||||
|
Assert.AreEqual('Method<Text -> Integer>', otherMethodType.Name, 'Name of other method type is incorrect');
|
||||||
|
|
||||||
|
// --- 3. Test Value Creation ---
|
||||||
|
// Define a function that matches the signature Ordinal -> Text
|
||||||
|
myFunc :=
|
||||||
|
function(const AValue: IDataValue): IDataValue
|
||||||
|
var
|
||||||
|
ordinalValue: IDataOrdinalValue;
|
||||||
|
val: Int64;
|
||||||
|
begin
|
||||||
|
ordinalValue := TDataValue(AValue).AsOrdinal;
|
||||||
|
val := ordinalValue.Value;
|
||||||
|
Result := TDataValue.FromText(val.ToString);
|
||||||
|
end;
|
||||||
|
|
||||||
|
methodValue := TDataValue.FromMethod(methodType1, myFunc);
|
||||||
|
Assert.IsNotNull(methodValue, 'MethodValue should be created');
|
||||||
|
Assert.AreSame(methodType1, methodValue.DataType, 'Value should have the correct data type');
|
||||||
|
|
||||||
|
// --- 4. Test Execution (Simulated) ---
|
||||||
|
inputValue := TDataValue.FromOrdinal(42);
|
||||||
|
// Execute the function retrieved from the value
|
||||||
|
resultValue := methodValue.Value(inputValue);
|
||||||
|
|
||||||
|
Assert.IsNotNull(resultValue, 'Execution result should not be nil');
|
||||||
|
Assert.AreSame(textType, resultValue.DataType, 'Result value should have Text type');
|
||||||
|
Assert.AreEqual('42', TDataValue(resultValue).AsText.Value, 'Result value content is incorrect');
|
||||||
|
|
||||||
|
// --- 5. Test AsString and AsTValue ---
|
||||||
|
Assert.AreEqual('<METHOD>', methodValue.AsString, 'Method AsString is incorrect');
|
||||||
|
|
||||||
|
tv := methodValue.AsTValue;
|
||||||
|
Assert.IsFalse(tv.IsEmpty, 'Method TValue should not be empty');
|
||||||
|
Assert.AreEqual(tkInterface, tv.Kind, 'TValue kind for a TMethodProc should be tkInterface, as it is a reference-to-function');
|
||||||
|
Assert.IsTrue(TypeInfo(TMethodProc) = tv.TypeInfo, 'TValue should hold TMethodProc type info');
|
||||||
|
|
||||||
|
// --- 6. Test Validation and Error Handling ---
|
||||||
|
// Test creating a value with a nil type
|
||||||
|
Assert.WillRaise(
|
||||||
|
procedure begin TDataValue.FromMethod(nil, myFunc); end,
|
||||||
|
EArgumentException,
|
||||||
|
'FromMethod with nil type should raise an exception'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test creating a value with a nil function
|
||||||
|
Assert.WillRaise(
|
||||||
|
procedure begin TDataValue.FromMethod(methodType1, nil); end,
|
||||||
|
EArgumentException,
|
||||||
|
'FromMethod with nil function should raise an exception'
|
||||||
|
);
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
+2
-1
@@ -31,7 +31,8 @@ uses
|
|||||||
Myc.Data.Types.Records in 'Myc.Data.Types.Records.pas',
|
Myc.Data.Types.Records in 'Myc.Data.Types.Records.pas',
|
||||||
Myc.Data.Types.Float in 'Myc.Data.Types.Float.pas',
|
Myc.Data.Types.Float in 'Myc.Data.Types.Float.pas',
|
||||||
Myc.Data.Types.Arrays in 'Myc.Data.Types.Arrays.pas',
|
Myc.Data.Types.Arrays in 'Myc.Data.Types.Arrays.pas',
|
||||||
TestDataTypes in '..\Src\Data\TestDataTypes.pas';
|
TestDataTypes in '..\Src\Data\TestDataTypes.pas',
|
||||||
|
Myc.Data.Types.Method in '..\Src\Data\Myc.Data.Types.Method.pas';
|
||||||
|
|
||||||
{ keep comment here to protect the following conditional from being removed by the IDE when adding a unit }
|
{ keep comment here to protect the following conditional from being removed by the IDE when adding a unit }
|
||||||
{$IFNDEF TESTINSIGHT}
|
{$IFNDEF TESTINSIGHT}
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ $(PreBuildEvent)]]></PreBuildEvent>
|
|||||||
<DCCReference Include="Myc.Data.Types.Float.pas"/>
|
<DCCReference Include="Myc.Data.Types.Float.pas"/>
|
||||||
<DCCReference Include="Myc.Data.Types.Arrays.pas"/>
|
<DCCReference Include="Myc.Data.Types.Arrays.pas"/>
|
||||||
<DCCReference Include="..\Src\Data\TestDataTypes.pas"/>
|
<DCCReference Include="..\Src\Data\TestDataTypes.pas"/>
|
||||||
|
<DCCReference Include="..\Src\Data\Myc.Data.Types.Method.pas"/>
|
||||||
<BuildConfiguration Include="Base">
|
<BuildConfiguration Include="Base">
|
||||||
<Key>Base</Key>
|
<Key>Base</Key>
|
||||||
</BuildConfiguration>
|
</BuildConfiguration>
|
||||||
|
|||||||
Reference in New Issue
Block a user