425 lines
14 KiB
C++
425 lines
14 KiB
C++
/*
|
|
============================================================================
|
|
Name : unittest.c
|
|
Author :
|
|
Version :
|
|
Copyright : Your copyright notice
|
|
Description : Hello World in C, Ansi-style
|
|
============================================================================
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <inttypes.h>
|
|
#include <platform/dma_ll.h>
|
|
#include <f4ll_c/crcscheduler.h>
|
|
#include <gtest/gtest.h>
|
|
#include <platform/mockme.h>
|
|
#include <thread>
|
|
#include <future>
|
|
|
|
extern "C" void Crc_StartNextTask(struct crcstatus_t *status);
|
|
|
|
DMA_TypeDef * DMA1 __attribute__((weak));
|
|
DMA_TypeDef * DMA2 __attribute__((weak));
|
|
|
|
static DMA_TypeDef dma1, dma2;
|
|
static crcstatus_t crcStatus;
|
|
static crcslot_t slot1, slot2;
|
|
static crctask_t tasks1[2], tasks2[2];
|
|
static CRC_TypeDef fakeCrc;
|
|
|
|
static DMA_TypeDef *expectedDma;
|
|
static uint32_t expectedStream;
|
|
static uint32_t expectedSrcAddress;
|
|
static uint32_t expectedDstAddress;
|
|
static uint32_t expectedLength;
|
|
static void *expectedCustomPtr;
|
|
static uint8_t expectedSuccess;
|
|
static uint32_t expectedCrc;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
DEFINE_MOCK_RET(uint32_t, __get_PRIMASK, mock) {
|
|
RETURN_MOCK_PREDEF(__get_PRIMASK, mock);
|
|
}
|
|
|
|
DEFINE_MOCK_VAR(uint32_t, __set_PRIMASK, mock, lastprimask);
|
|
DEFINE_MOCK(__set_PRIMASK, mock, uint32_t primask) {
|
|
MOCK_STORE(__set_PRIMASK, mock, lastprimask, primask);
|
|
LEAVE_MOCK;
|
|
}
|
|
|
|
DEFINE_MOCK_VAR(crcslot_t *, __disable_irq, mock, compare);
|
|
DEFINE_MOCK(__disable_irq, mock) {
|
|
if(!MOCK_VAR(__disable_irq, mock, callcount)) {
|
|
EXPECT_EQ(crcStatus.firstSlot, MOCK_VAR(__disable_irq, mock, compare));
|
|
}
|
|
LEAVE_MOCK;
|
|
}
|
|
|
|
DEFINE_MOCK(LL_DMA_EnableIT_TC, mock, DMA_TypeDef *dma, uint32_t stream) {
|
|
EXPECT_EQ(expectedDma, dma);
|
|
EXPECT_EQ(expectedStream, stream);
|
|
LEAVE_MOCK;
|
|
}
|
|
|
|
DEFINE_MOCK(LL_DMA_EnableIT_TE, mock, DMA_TypeDef *dma, uint32_t stream) {
|
|
EXPECT_EQ(expectedDma, dma);
|
|
EXPECT_EQ(expectedStream, stream);
|
|
LEAVE_MOCK;
|
|
}
|
|
|
|
DEFINE_MOCK(LL_DMA_SetM2MDstAddress, mock, DMA_TypeDef *dma, uint32_t stream, uint32_t address) {
|
|
EXPECT_EQ(expectedDma, dma);
|
|
EXPECT_EQ(expectedStream, stream);
|
|
EXPECT_EQ(expectedDstAddress, address);
|
|
LEAVE_MOCK;
|
|
}
|
|
|
|
DEFINE_MOCK(LL_DMA_SetM2MSrcAddress, mock, DMA_TypeDef *dma, uint32_t stream, uint32_t address) {
|
|
EXPECT_EQ(expectedDma, dma);
|
|
EXPECT_EQ(expectedStream, stream);
|
|
EXPECT_EQ(expectedSrcAddress, address);
|
|
LEAVE_MOCK;
|
|
}
|
|
|
|
DEFINE_MOCK(LL_DMA_SetDataLength, mock, DMA_TypeDef *dma, uint32_t stream, uint32_t length) {
|
|
EXPECT_EQ(expectedDma, dma);
|
|
EXPECT_EQ(expectedStream, stream);
|
|
EXPECT_EQ(expectedLength, length);
|
|
LEAVE_MOCK;
|
|
}
|
|
|
|
DEFINE_MOCK(LL_DMA_EnableStream, mock, DMA_TypeDef *dma, uint32_t stream)
|
|
{
|
|
EXPECT_EQ(expectedDma, dma);
|
|
EXPECT_EQ(expectedStream, stream);
|
|
LEAVE_MOCK;
|
|
}
|
|
|
|
DEFINE_MOCK(LL_DMA_DisableStream, mock, DMA_TypeDef *dma, uint32_t stream)
|
|
{
|
|
EXPECT_EQ(expectedDma, dma);
|
|
EXPECT_EQ(expectedStream, stream);
|
|
LEAVE_MOCK;
|
|
}
|
|
|
|
DEFINE_MOCK(Crc_StartNextTask, mock, struct crcstatus_t *status)
|
|
{
|
|
EXPECT_EQ(status, &crcStatus);
|
|
LEAVE_MOCK
|
|
}
|
|
|
|
void FakeCallback_1(void*, uint32_t, uint8_t) {}
|
|
void FakeCallback_2(void*, uint32_t, uint8_t) {}
|
|
void FakeCallback_3(void*, uint32_t, uint8_t) {}
|
|
|
|
void FakeCallbackCheck(void* ptr, uint32_t crc, uint8_t success)
|
|
{
|
|
EXPECT_EQ(ptr, expectedCustomPtr);
|
|
EXPECT_EQ(crc, expectedCrc);
|
|
EXPECT_EQ(success, expectedSuccess);
|
|
}
|
|
|
|
DEFINE_MOCK(Dma_Init, mock, struct dmainfo_t * info, DMA_TypeDef *dma, uint32_t stream)
|
|
{
|
|
LEAVE_MOCK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
TEST(CrcScheduler, InitStatus)
|
|
{
|
|
expectedDma = DMA2;
|
|
expectedStream = LL_DMA_STREAM_4;
|
|
expectedDstAddress = (uint32_t)&fakeCrc;
|
|
memset(&crcStatus, 0xff, sizeof(crcStatus));
|
|
|
|
ACTIVATE_MOCK(LL_DMA_EnableIT_TC, mock);
|
|
ACTIVATE_MOCK(LL_DMA_EnableIT_TE, mock);
|
|
ACTIVATE_MOCK(LL_DMA_SetM2MDstAddress, mock);
|
|
ACTIVATE_MOCK(Dma_Init, mock);
|
|
|
|
Crc_InitStatus(&crcStatus, &fakeCrc, DMA2, LL_DMA_STREAM_4);
|
|
EXPECT_EQ(crcStatus.crcUnit, &fakeCrc);
|
|
EXPECT_EQ(crcStatus.activeSlot, nullptr);
|
|
EXPECT_EQ(crcStatus.firstSlot, nullptr);
|
|
EXPECT_EQ(MOCK_VAR(Dma_Init, mock, callcount), 1);
|
|
}
|
|
|
|
|
|
TEST(CrcScheduler, AttachTask_single)
|
|
{
|
|
ACTIVATE_MOCK_RV(__get_PRIMASK, mock, 1);
|
|
ACTIVATE_MOCK(__set_PRIMASK, mock);
|
|
ACTIVATE_MOCK(__disable_irq, mock);
|
|
MOCK_STORE(__disable_irq, mock, compare, nullptr);
|
|
DMA1 = &dma1;
|
|
DMA2 = &dma2;
|
|
Crc_InitStatus(&crcStatus, &fakeCrc, DMA2, LL_DMA_STREAM_4);
|
|
|
|
Crc_AttachTasks(&crcStatus, &slot1, tasks1, 2);
|
|
|
|
EXPECT_EQ(MOCK_VAR(__get_PRIMASK, mock, callcount), 1);
|
|
EXPECT_EQ(MOCK_VAR(__set_PRIMASK, mock, callcount), 1);
|
|
EXPECT_EQ(MOCK_VAR(__disable_irq, mock, callcount), 1);
|
|
EXPECT_EQ(crcStatus.firstSlot, &slot1);
|
|
EXPECT_EQ(slot1.next, nullptr);
|
|
EXPECT_EQ(slot1.count, 2);
|
|
EXPECT_EQ(crcStatus.activeSlot, nullptr);
|
|
}
|
|
|
|
// Are tasks attached in the expected order internally
|
|
TEST(CrcScheduler, AttachTask_multiple)
|
|
{
|
|
ACTIVATE_MOCK_RV(__get_PRIMASK, mock, 1);
|
|
ACTIVATE_MOCK(__set_PRIMASK, mock);
|
|
ACTIVATE_MOCK(__disable_irq, mock);
|
|
MOCK_STORE(__disable_irq, mock, compare, nullptr);
|
|
DMA1 = &dma1;
|
|
DMA2 = &dma2;
|
|
Crc_InitStatus(&crcStatus, NULL, DMA2, LL_DMA_STREAM_4);
|
|
|
|
Crc_AttachTasks(&crcStatus, &slot1, tasks1, 2);
|
|
MOCK_STORE(__disable_irq, mock, compare, &slot1);
|
|
Crc_AttachTasks(&crcStatus, &slot2, tasks2, 2);
|
|
|
|
EXPECT_EQ(__get_PRIMASK_mock_callcount, 2);
|
|
EXPECT_EQ(__set_PRIMASK_mock_callcount, 2);
|
|
EXPECT_EQ(__disable_irq_mock_callcount, 2);
|
|
EXPECT_EQ(crcStatus.firstSlot, &slot2);
|
|
EXPECT_EQ(slot2.next, &slot1);
|
|
EXPECT_EQ(slot1.next, nullptr);
|
|
EXPECT_EQ(slot2.count, 2);
|
|
EXPECT_EQ(crcStatus.activeSlot, nullptr);
|
|
}
|
|
|
|
// No blocking should occur if the the task is not busy
|
|
TEST(CrcScheduler, Enqueue_nowait)
|
|
{
|
|
uint32_t fakeCrcResult;
|
|
uint8_t testData[] = "qwerty";
|
|
expectedDma = DMA2;
|
|
expectedStream = LL_DMA_STREAM_4;
|
|
expectedSrcAddress = (uint32_t)testData;
|
|
|
|
memset(tasks1, 0, sizeof(tasks1));
|
|
memset(&fakeCrc, 0, sizeof(fakeCrc));
|
|
|
|
Crc_InitStatus(&crcStatus, &fakeCrc, DMA2, LL_DMA_STREAM_4);
|
|
Crc_AttachTasks(&crcStatus, &slot1, tasks1, 2);
|
|
expectedLength = 2;
|
|
|
|
ACTIVATE_MOCK(LL_DMA_SetM2MSrcAddress, mock);
|
|
ACTIVATE_MOCK(LL_DMA_SetDataLength, mock);
|
|
ACTIVATE_MOCK(LL_DMA_EnableStream, mock);
|
|
|
|
EXPECT_TRUE(Crc_Enqueue(&crcStatus, &slot1, 0, testData, sizeof(testData), FakeCallback_1, &fakeCrcResult));
|
|
|
|
//first task should be picked up before return;
|
|
EXPECT_EQ(slot1.tasks[0].address, nullptr);
|
|
EXPECT_EQ((uintptr_t)slot1.tasks[0].callback, (uintptr_t)FakeCallback_1);
|
|
EXPECT_EQ(MOCK_VAR(LL_DMA_SetM2MSrcAddress, mock, callcount), 1);
|
|
EXPECT_EQ(MOCK_VAR(LL_DMA_SetDataLength, mock, callcount), 1);
|
|
EXPECT_EQ(MOCK_VAR(LL_DMA_EnableStream, mock, callcount), 1);
|
|
|
|
EXPECT_FALSE(Crc_Enqueue(&crcStatus, &slot1, 1, testData, 4, FakeCallback_1, &fakeCrcResult));
|
|
|
|
// second task should be queued
|
|
EXPECT_EQ(slot1.tasks[1].address, testData);
|
|
EXPECT_EQ((uintptr_t)slot1.tasks[1].callback, (uintptr_t)FakeCallback_1);
|
|
EXPECT_EQ(slot1.tasks[1].wordCount, 1);
|
|
|
|
// should be no new calls to hardware handling functions
|
|
EXPECT_EQ(MOCK_VAR(LL_DMA_SetM2MSrcAddress, mock, callcount), 1);
|
|
EXPECT_EQ(MOCK_VAR(LL_DMA_SetDataLength, mock, callcount), 1);
|
|
EXPECT_EQ(MOCK_VAR(LL_DMA_EnableStream, mock, callcount), 1);
|
|
}
|
|
|
|
// When trying to enqueue for a busy task it should blok firs
|
|
// then when the previously blocked task finishes it should
|
|
// enqueue the new one
|
|
TEST(CrcScheduler, Enqueue_shouldblockthencontinue)
|
|
{
|
|
uint8_t testData[] = "qwerty";
|
|
uint32_t fakeCrcResult;
|
|
|
|
memset(tasks1, 0, sizeof(tasks1));
|
|
memset(&fakeCrc, 0, sizeof(fakeCrc));
|
|
|
|
Crc_InitStatus(&crcStatus, &fakeCrc, DMA2, LL_DMA_STREAM_4);
|
|
Crc_AttachTasks(&crcStatus, &slot1, tasks1, 2);
|
|
Crc_Enqueue(&crcStatus, &slot1, 0, testData, sizeof(testData), FakeCallback_1, &fakeCrcResult);
|
|
|
|
// black magic to test if the function blocks (at least for 100ms)
|
|
std::promise<bool> promisedFinished;
|
|
auto futureResult = promisedFinished.get_future();
|
|
pthread_t th;
|
|
std::thread t([&testData](std::promise<bool>& finished) {
|
|
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
|
|
Crc_Enqueue(&crcStatus, &slot1, 0, testData, sizeof(testData), FakeCallback_1, nullptr);
|
|
finished.set_value(true);
|
|
}, std::ref(promisedFinished));
|
|
th = t.native_handle();
|
|
t.detach();
|
|
EXPECT_EQ(futureResult.wait_for(std::chrono::milliseconds(100)), std::future_status::timeout);
|
|
tasks1[0].callback = nullptr;
|
|
tasks1[0].callbackParam = nullptr;
|
|
auto waitResult(futureResult.wait_for(std::chrono::milliseconds(100)));
|
|
EXPECT_NE(waitResult, std::future_status::timeout);
|
|
if(waitResult == std::future_status::timeout)
|
|
pthread_cancel(th);
|
|
}
|
|
|
|
// StartNextTask should start the scheduled tasks in predefined order
|
|
TEST(CrcScheduler, Crc_StartNextTask)
|
|
{
|
|
uint8_t testData[] = "qwerty";
|
|
uint32_t fakeCrcResult1, fakeCrcResult2, fakeCrcResult3;
|
|
|
|
memset(tasks1, 0, sizeof(tasks1));
|
|
memset(tasks2, 0, sizeof(tasks2));
|
|
memset(&fakeCrc, 0, sizeof(fakeCrc));
|
|
|
|
Crc_InitStatus(&crcStatus, &fakeCrc, DMA2, LL_DMA_STREAM_4);
|
|
Crc_AttachTasks(&crcStatus, &slot1, tasks1, 2);
|
|
Crc_AttachTasks(&crcStatus, &slot2, tasks2, 2);
|
|
Crc_Enqueue(&crcStatus, &slot1, 0, testData, sizeof(testData), FakeCallback_1, &fakeCrcResult1);
|
|
Crc_Enqueue(&crcStatus, &slot1, 1, testData, sizeof(testData), FakeCallback_2, &fakeCrcResult2);
|
|
Crc_Enqueue(&crcStatus, &slot2, 0, testData, sizeof(testData), FakeCallback_3, &fakeCrcResult3);
|
|
|
|
EXPECT_EQ(crcStatus.activeSlot, &slot1);
|
|
EXPECT_EQ(crcStatus.activeTask, 0);
|
|
crcStatus.activeSlot->tasks[crcStatus.activeTask].callback = nullptr;
|
|
crcStatus.activeSlot->tasks[crcStatus.activeTask].callbackParam = nullptr;
|
|
|
|
ACTIVATE_MOCK(LL_DMA_SetM2MSrcAddress, mock);
|
|
ACTIVATE_MOCK(LL_DMA_SetDataLength, mock);
|
|
ACTIVATE_MOCK(LL_DMA_EnableStream, mock);
|
|
|
|
expectedDma = DMA2;
|
|
expectedStream = LL_DMA_STREAM_4;
|
|
expectedSrcAddress = (uint32_t)testData;
|
|
expectedLength = (sizeof(testData) + 3) / 4;
|
|
|
|
Crc_StartNextTask(&crcStatus);
|
|
|
|
EXPECT_EQ(crcStatus.activeSlot, &slot2);
|
|
EXPECT_EQ(crcStatus.activeTask, 0);
|
|
EXPECT_EQ(LL_DMA_SetM2MSrcAddress_mock_callcount, 1);
|
|
EXPECT_EQ(LL_DMA_SetDataLength_mock_callcount, 1);
|
|
EXPECT_EQ(LL_DMA_EnableStream_mock_callcount, 1);
|
|
crcStatus.activeSlot->tasks[crcStatus.activeTask].callback = nullptr;
|
|
crcStatus.activeSlot->tasks[crcStatus.activeTask].callbackParam = nullptr;
|
|
|
|
Crc_StartNextTask(&crcStatus);
|
|
|
|
EXPECT_EQ(crcStatus.activeSlot, &slot1);
|
|
EXPECT_EQ(crcStatus.activeTask, 1);
|
|
EXPECT_EQ(LL_DMA_SetM2MSrcAddress_mock_callcount, 2);
|
|
EXPECT_EQ(LL_DMA_SetDataLength_mock_callcount, 2);
|
|
EXPECT_EQ(LL_DMA_EnableStream_mock_callcount, 2);
|
|
}
|
|
|
|
// HandleDmaIrq should start the next scheduled task or
|
|
// disable the CRC DMA engine if there is no other task scheduled.
|
|
TEST(CrcScheduler, HandleDmaIrq_callback)
|
|
{
|
|
uint8_t testData[] = "qwerty";
|
|
uint32_t FakeCustomData1, FakeCustomData2;
|
|
|
|
|
|
memset(tasks1, 0, sizeof(tasks1));
|
|
memset(tasks2, 0, sizeof(tasks2));
|
|
memset(&fakeCrc, 0, sizeof(fakeCrc));
|
|
|
|
fakeCrc.DR = 0xa5a55a5a;
|
|
|
|
Crc_InitStatus(&crcStatus, &fakeCrc, DMA2, LL_DMA_STREAM_4);
|
|
Crc_AttachTasks(&crcStatus, &slot1, tasks1, 2);
|
|
Crc_AttachTasks(&crcStatus, &slot2, tasks2, 2);
|
|
Crc_Enqueue(&crcStatus, &slot1, 1, testData, sizeof(testData), FakeCallbackCheck, &FakeCustomData1);
|
|
// we need to set this up here to check if HandleDmaIrq calls Crc_StartNextTask or not;
|
|
Crc_Enqueue(&crcStatus, &slot2, 0, testData, sizeof(testData), FakeCallbackCheck, &FakeCustomData2);
|
|
|
|
ACTIVATE_MOCK(LL_DMA_DisableStream, mock);
|
|
ACTIVATE_MOCK(LL_DMA_SetM2MSrcAddress, mock);
|
|
ACTIVATE_MOCK(LL_DMA_SetDataLength, mock);
|
|
ACTIVATE_MOCK(LL_DMA_EnableStream, mock);
|
|
ACTIVATE_MOCK(Crc_StartNextTask, mock);
|
|
|
|
expectedDma = DMA2;
|
|
expectedStream = LL_DMA_STREAM_4;
|
|
|
|
expectedCustomPtr = &FakeCustomData1;
|
|
expectedCrc = fakeCrc.DR;
|
|
expectedSuccess = 1;
|
|
|
|
DMA2->HISR |= DMA_HISR_TCIF4;
|
|
DMA2->HIFCR = 0;
|
|
|
|
Crc_HandleDmaIrq(&crcStatus);
|
|
|
|
EXPECT_EQ(LL_DMA_DisableStream_mock_callcount, 1);
|
|
|
|
EXPECT_EQ(DMA2->HIFCR & DMA_HIFCR_CTCIF4, DMA_HIFCR_CTCIF4);
|
|
|
|
expectedCustomPtr = &FakeCustomData2;
|
|
DMA2->HISR |= DMA_HISR_TCIF4 | DMA_HISR_TEIF4;
|
|
DMA2->HIFCR = 0;
|
|
crcStatus.activeSlot->tasks[crcStatus.activeTask].callback = nullptr;
|
|
crcStatus.activeSlot->tasks[crcStatus.activeTask].callbackParam = nullptr;
|
|
|
|
Crc_HandleDmaIrq(&crcStatus);
|
|
|
|
EXPECT_EQ(DMA2->HIFCR & (DMA_HIFCR_CTCIF4 | DMA_HIFCR_CTEIF4), DMA_HIFCR_CTCIF4 | DMA_HIFCR_CTEIF4);
|
|
EXPECT_EQ(LL_DMA_DisableStream_mock_callcount, 2);
|
|
}
|
|
|
|
// Crc_StartNextTask starts executing the next task and removes it from the queue
|
|
// Test if IsTaskQueued reflects this behaviour correctly
|
|
TEST(CrcScheduler, IsTaskQueued)
|
|
{
|
|
uint8_t testData[] = "qwerty";
|
|
uint32_t FakeCustomData1;
|
|
|
|
Crc_InitStatus(&crcStatus, &fakeCrc, DMA2, LL_DMA_STREAM_4);
|
|
Crc_AttachTasks(&crcStatus, &slot1, tasks1, 2);
|
|
Crc_Enqueue(&crcStatus, &slot1, 0, testData, sizeof(testData), FakeCallback_1, &FakeCustomData1);
|
|
Crc_Enqueue(&crcStatus, &slot1, 1, testData, sizeof(testData), FakeCallback_1, &FakeCustomData1);
|
|
|
|
EXPECT_EQ(Crc_IsTaskQueued(&slot1, 0), 0);
|
|
EXPECT_NE(Crc_IsTaskQueued(&slot1, 1), 0);
|
|
|
|
Crc_StartNextTask(&crcStatus);
|
|
|
|
EXPECT_EQ(Crc_IsTaskQueued(&slot1, 1), 0);
|
|
}
|
|
|
|
// Crc_HandleDmaIrq completes the active task and start executing the next (by calling StartNextTask)
|
|
// Crc_IsTaskBusy should reflect these changes
|
|
TEST(CrcScheduler, IsTaskBusy)
|
|
{
|
|
uint8_t testData[] = "qwerty";
|
|
uint32_t FakeCustomData1;
|
|
|
|
Crc_InitStatus(&crcStatus, &fakeCrc, DMA2, LL_DMA_STREAM_4);
|
|
Crc_AttachTasks(&crcStatus, &slot1, tasks1, 2);
|
|
Crc_Enqueue(&crcStatus, &slot1, 0, testData, sizeof(testData), FakeCallback_1, &FakeCustomData1);
|
|
Crc_Enqueue(&crcStatus, &slot1, 1, testData, sizeof(testData), FakeCallback_1, &FakeCustomData1);
|
|
|
|
EXPECT_NE(Crc_IsTaskBusy(&slot1, 0), 0);
|
|
EXPECT_NE(Crc_IsTaskBusy(&slot1, 1), 0);
|
|
|
|
DMA2->HISR |= DMA_HISR_TCIF4;
|
|
Crc_HandleDmaIrq(&crcStatus);
|
|
|
|
EXPECT_EQ(Crc_IsTaskBusy(&slot1, 0), 0);
|
|
EXPECT_NE(Crc_IsTaskBusy(&slot1, 1), 0);
|
|
|
|
DMA2->HISR |= DMA_HISR_TCIF4;
|
|
Crc_HandleDmaIrq(&crcStatus);
|
|
|
|
EXPECT_EQ(Crc_IsTaskBusy(&slot1, 0), 0);
|
|
EXPECT_EQ(Crc_IsTaskBusy(&slot1, 1), 0);
|
|
}
|
|
|