EInvalidCast с фиктивной функцией, возвращающей тип указателя
Я написал интерфейс, чтобы обернуть Windows Threadpool API, и многие из этих функций возвращают простой Pointer
типы.
Сейчас я пишу тесты и хочу использовать инфраструктуру delphi-mocks для макета интерфейса оболочки.
Проблема в том, что TMock
интерфейс настройки занимает TValue
объект, чтобы указать возвращаемое значение по умолчанию для ложных функций, и я не могу найти способ, как сделать это правильно из доступных TValue
функции. Хотя я видел этоekPointer
является действительнымTTypeKind
значение.
Когда вызванная функция вызывается, я получаюEInvalidCast
исключение из соответствующего вызова RTTI.
Это происходит конкретно, когда вызов RTTI пытается привести значение возвращаемого типа из неявногоTValue
объект.
Мой код выглядит примерно так1:
type
PTP_POOL = Pointer;
PTP_CLEANUP_GROUP = Pointer;
IThreadPoolApi = interface(IInterface)
{...}
function CreateThreadPool() : PTP_POOL;
function CreateThreadpoolCleanupGroup() : PTP_CLEANUP_GROUP;
{...}
end;
тестируемый класс
type
TThreadPool = class(TInterfacedObject, IThreadPool)
FTPApi : IThreadPoolApi;
FPTPPool : PTP_POOL;
FPTPCleanupGroup : PTP_CLEANUP_GROUP;
{...}
public
constructor Create(iapi : IThreadPoolApi);
end;
implementation
constructor TThreadPool.Create(iapi : IThreadPoolApi);
begin
inherited Create();
FTPApi := iapi;
{**** Here I get a EInvalidCast exception when mocking the interface ****}
FPTPPool := FTPApi.CreateThreadPool();
if(not assigned(FPTPPool)) then
begin
{Raise exception}
raise EThreadPoolError('Cannot create TP thread pool');
end;
{**** This case should be tested ****}
FPTPCleanupGroup := FTPApi.CreateThreadpoolCleanupGroup();
if(not assigned(FPTPPool)) then
begin
{Raise exception}
raise EThreadPoolError('Cannot create TP cleanup group');
end;
{...}
end;
и тестирование
procedure ThreadPoolTest.TestFail_CreateThreadpoolCleanupGroup();
var
apiMock : TMock<IThreadPoolApi>;
testproc : TTestLocalMethod;
begin
apiMock := TMock<IThreadPoolApi>Create();
{**** Needed to reach the call of CreateThreadpoolCleanupGroup
but EInvalidCast is raised ****}
apiMock.Setup.WillReturnDefault('CreateThreadPool',PTP_POOL($FFFFFFFF));
{**** The case to be tested ****}
apiMock.Setup.WillExecute('CreateThreadpoolCleanupGroup',
function (const args : TArray<TValue>; const ReturnType : TRttiType)
: TValue
begin
result := nil;
end);
testproc :=
procedure()
var
threadpool : IThreadPool;
begin
threadpool := TThreadPool.Create(apiMock);
end;
DUnitX.Assert.WillRaise(testproc,EThreadPoolError,
'Cannot create TP cleanup group');
end;
TL;DR;
Итак, вопрос:
Что мне для этого нужноTValue
создан правильно, чтобы содержатьPTP_POOL
тип указателя?
1)Это слишком много кода для настройки MCVE, поэтому я просто набросал его здесь, чтобы дать вам представление, см.{**** highlighted comments ****}
2 ответа
При назначении необработанного указателя на TValue
, использовать TValue.From<T>()
метод, например:
TValue.From<PTP_POOL>(nil);
Или, если вы хотите вернуть буквальное значение:
TValue.From<PTP_POOL>(PTP_POOL(value));
TValue
не имеет неявного преобразования для необработанных указателей, но оно имеет неявное преобразование для TObject
а также TClass
указатели.
Если вы назначите нетипизированный нулевой указатель напрямую TValue
это вызывает TValue.Implicit(TObject)
, который вернет TValue.Empty
свойство, когда передается нулевой указатель.
Если вы назначаете типизированный TObject
указатель прямо на TValue
это вызывает TValue.Implicit(TClass)
, который возвращает TValue.Empty
если указатель равен нулю, в противном случае он возвращает TValue
типа TClass
со значением указателя, даже если он на самом деле не указывает на допустимый тип класса.
Если вы передаете указатель на TValue.From<T>()
, он возвращает TValue
типа T
со значением указателя, даже если это ноль.
Эта разница тонкая, но очень важная.
Что мне для этого нужно
TValue
создан правильно, чтобы содержатьPTP_POOL
тип указателя?
Похоже, @Remy уже ответил на мой вопрос в своем комментарии.
Чтобы это значение указателя содержалось в TValue
экземпляр он должен быть создан с использованием общего TValue.From<T>()
функция:
apiMock.Setup.WillReturnDefault('CreateThreadPool',
TValue.From<PTP_POOL>(PTP_POOL($FFFFFFFF)));
и немного кастинга.