Files
LeechCore/tests/device_fpga_session_linux_test.c
2026-07-30 03:42:57 -04:00

207 lines
6.0 KiB
C

#include "../leechcore/device_fpga_session.h"
#include <stdio.h>
#define TEST_FT_TIMEOUT_STATUS 19
static int g_cFailures;
#define ASSERT_TRUE(value) \
do { \
if(!(value)) { \
printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #value); \
g_cFailures++; \
} \
} while(0)
#define ASSERT_EQ(actual, expected) \
do { \
unsigned long long _actual = (unsigned long long)(actual); \
unsigned long long _expected = (unsigned long long)(expected); \
if(_actual != _expected) { \
printf( \
"FAIL %s:%d: %s=%llu expected %llu\n", \
__FILE__, __LINE__, #actual, _actual, _expected); \
g_cFailures++; \
} \
} while(0)
typedef struct tdWAIT_SCRIPT {
ULONG pStatus[8];
ULONG pTransferred[8];
BOOL pfWaitArguments[8];
DWORD cResults;
DWORD iResult;
DWORD cGetCalls;
DWORD cSleepCalls;
DWORD dwAdvanceOnBlockingMs;
QWORD qwNow;
} WAIT_SCRIPT, *PWAIT_SCRIPT;
static ULONG WINAPI ScriptedGetOverlappedResult(
_In_ HANDLE hFTDI,
_In_ LPOVERLAPPED pOverlapped,
_Out_ PULONG pulLengthTransferred,
_In_ BOOL fWait
)
{
PWAIT_SCRIPT pScript = (PWAIT_SCRIPT)hFTDI;
DWORD iResult = pScript->iResult;
UNREFERENCED_PARAMETER(pOverlapped);
if(pScript->cGetCalls < 8) {
pScript->pfWaitArguments[pScript->cGetCalls] = fWait;
}
pScript->cGetCalls++;
if(fWait) {
pScript->qwNow += pScript->dwAdvanceOnBlockingMs;
}
if(iResult >= pScript->cResults) {
iResult = pScript->cResults - 1;
} else {
pScript->iResult++;
}
*pulLengthTransferred = pScript->pTransferred[iResult];
return pScript->pStatus[iResult];
}
static QWORD ScriptedTick(_In_ PVOID pvContext)
{
return ((PWAIT_SCRIPT)pvContext)->qwNow;
}
static VOID ScriptedSleep(
_In_ PVOID pvContext,
_In_ DWORD dwMilliseconds
)
{
PWAIT_SCRIPT pScript = (PWAIT_SCRIPT)pvContext;
pScript->cSleepCalls++;
pScript->qwNow += dwMilliseconds;
}
static DEVICE_FPGA_SESSION_WAIT_RESULT RunWait(
_Inout_ PWAIT_SCRIPT pScript,
_In_ BOOL fUseEventWait,
_In_ DWORD dwTimeoutMs
)
{
BYTE pbOverlapped[64] = { 0 };
return DeviceFPGA_Session_WaitOverlapped(
pScript,
(LPOVERLAPPED)pbOverlapped,
ScriptedGetOverlappedResult,
fUseEventWait,
dwTimeoutMs,
1,
pScript,
ScriptedTick,
ScriptedSleep);
}
static VOID TestWaitUsesBoundedBlockingDriverCall(VOID)
{
WAIT_SCRIPT Script = {
.pStatus = { DEVICE_FPGA_SESSION_FT_OK },
.pTransferred = { 0x1234 },
.cResults = 1
};
DEVICE_FPGA_SESSION_WAIT_RESULT Result = RunWait(&Script, TRUE, 1000);
ASSERT_EQ(Result.outcome, DEVICE_FPGA_SESSION_WAIT_COMPLETED);
ASSERT_EQ(Result.status, DEVICE_FPGA_SESSION_FT_OK);
ASSERT_EQ(Result.cbTransferred, 0x1234);
ASSERT_EQ(Script.cGetCalls, 1);
ASSERT_EQ(Script.cSleepCalls, 0);
ASSERT_TRUE(Script.pfWaitArguments[0]);
}
static VOID TestBlockingIncompleteFallsBackToDeadlinePolling(VOID)
{
WAIT_SCRIPT Script = {
.pStatus = {
DEVICE_FPGA_SESSION_FT_IO_INCOMPLETE,
DEVICE_FPGA_SESSION_FT_IO_INCOMPLETE,
DEVICE_FPGA_SESSION_FT_OK
},
.pTransferred = { 0, 0, 0x1234 },
.cResults = 3
};
DEVICE_FPGA_SESSION_WAIT_RESULT Result = RunWait(&Script, TRUE, 1000);
ASSERT_EQ(Result.outcome, DEVICE_FPGA_SESSION_WAIT_COMPLETED);
ASSERT_EQ(Result.cbTransferred, 0x1234);
ASSERT_EQ(Script.cGetCalls, 3);
ASSERT_EQ(Script.cSleepCalls, 1);
ASSERT_EQ(Script.qwNow, 1);
ASSERT_TRUE(Script.pfWaitArguments[0]);
ASSERT_TRUE(!Script.pfWaitArguments[1]);
ASSERT_TRUE(!Script.pfWaitArguments[2]);
}
static VOID TestBlockingTimeoutMapsToTimedOut(VOID)
{
WAIT_SCRIPT Script = {
.pStatus = { TEST_FT_TIMEOUT_STATUS },
.cResults = 1
};
DEVICE_FPGA_SESSION_WAIT_RESULT Result = RunWait(&Script, TRUE, 1000);
ASSERT_EQ(Result.outcome, DEVICE_FPGA_SESSION_WAIT_TIMED_OUT);
ASSERT_EQ(Result.status, TEST_FT_TIMEOUT_STATUS);
ASSERT_EQ(Result.cbTransferred, 0);
ASSERT_EQ(Script.cGetCalls, 1);
ASSERT_EQ(Script.cSleepCalls, 0);
ASSERT_TRUE(Script.pfWaitArguments[0]);
}
static VOID TestBlockingIncompleteKeepsOriginalDeadline(VOID)
{
WAIT_SCRIPT Script = {
.pStatus = {
DEVICE_FPGA_SESSION_FT_IO_INCOMPLETE,
DEVICE_FPGA_SESSION_FT_IO_INCOMPLETE
},
.cResults = 2,
.dwAdvanceOnBlockingMs = 1000
};
DEVICE_FPGA_SESSION_WAIT_RESULT Result = RunWait(&Script, TRUE, 1000);
ASSERT_EQ(Result.outcome, DEVICE_FPGA_SESSION_WAIT_TIMED_OUT);
ASSERT_EQ(Result.status, DEVICE_FPGA_SESSION_FT_IO_INCOMPLETE);
ASSERT_EQ(Script.cGetCalls, 2);
ASSERT_EQ(Script.cSleepCalls, 0);
ASSERT_EQ(Script.qwNow, 1000);
ASSERT_TRUE(Script.pfWaitArguments[0]);
ASSERT_TRUE(!Script.pfWaitArguments[1]);
}
static VOID TestExplicitPollingStaysNonblocking(VOID)
{
WAIT_SCRIPT Script = {
.pStatus = {
DEVICE_FPGA_SESSION_FT_IO_INCOMPLETE,
DEVICE_FPGA_SESSION_FT_OK
},
.pTransferred = { 0, 0x1234 },
.cResults = 2
};
DEVICE_FPGA_SESSION_WAIT_RESULT Result = RunWait(&Script, FALSE, 1000);
ASSERT_EQ(Result.outcome, DEVICE_FPGA_SESSION_WAIT_COMPLETED);
ASSERT_EQ(Result.cbTransferred, 0x1234);
ASSERT_EQ(Script.cGetCalls, 2);
ASSERT_EQ(Script.cSleepCalls, 1);
ASSERT_TRUE(!Script.pfWaitArguments[0]);
ASSERT_TRUE(!Script.pfWaitArguments[1]);
}
int main(void)
{
TestWaitUsesBoundedBlockingDriverCall();
TestBlockingIncompleteFallsBackToDeadlinePolling();
TestBlockingTimeoutMapsToTimedOut();
TestBlockingIncompleteKeepsOriginalDeadline();
TestExplicitPollingStaysNonblocking();
if(g_cFailures) {
printf("%d Linux wait assertion(s) failed.\n", g_cFailures);
return 1;
}
printf("PASS: 5 Linux FPGA session wait cases.\n");
return 0;
}