TDD para microcontroladores. Parte 1: Primeiro voo
TDD para microcontroladores. Parte 2: Como os espiões se livram das dependências de
TDD para microcontroladores. Parte 3: rodando em ferro

TDD ( – ) STM32. :
- .
- IDE .
- - .
- .
, TDD - . . - TDD . – .
-
- :
1. read , -.
2. write -.
3. erase .
4. help .
5. .
CppUTest.
– : , - UART-. UART- ASCII. - . -: .
– TDD . , ( ). .
help
– № 4 help. help UART-.
UART:
- .
- .
- .
P.S. .
, - : Configurator
.
Serial
, ( – STM32F103C8T6). , SerialSpy
.
help:
TEST(Configurator, ShouldHandleHelpCommand)
{
char helpCommand[] = "help\r\n";
LONGS_EQUAL(OK, SerialSpy_SetReceiveBuffer(serial, helpCommand, sizeof(helpCommand)));
Status status = Configurator_Handler(configurator);
LONGS_EQUAL(OK, status);
char * sendBufferPtr = NULL;
LONGS_EQUAL(OK, SerialSpy_GetSendBuffer(serial, &sendBufferPtr));
STRCMP_EQUAL(HELP_OUTPUT, sendBufferPtr);
}
:
- Arrange UART
SerialSpy
, SerialSpy_SetReceiveBuffer
; - Act
Configurator_Handler
, , UART. UART; - Assert .
SerialSpy_GetSendBuffer
, STRCMP_EQUAL
HELP_OUTPUT
.
, : SerialSpy_SetReceiveBuffer
, Configurator_Handler
SerialSpy_GetSendBuffer
. test fails (). Configurator_Handler
Configurator
.
SerialSpy_SetReceiveBuffer
SerialSpy_GetSendBuffer
, . , , , .
SerialSpy
, Configurator
Serial
. SerialSpy.h
, SerialSpy.c
– . , , . SerialSpy
:
•receiveBuffer
UART;
•sendBuffer
UART.
Configurator_Handler
: , . :
• SerialSpy_SetReceiveBuffer
;
• SerialSpy_GetSendBuffer
, .
Configurator_Handler SerialSpy
typedef enum
{
OK = 0,
FAIL = -1,
INVALID_PARAMETERS = -4,
OUT_OF_BOUNDS = -13,
} Status;
Status Configurator_Handler(Configurator * self);
Status Configurator_Handler(Configurator * self)
{
}
#include "Common.h"
Status SerialSpy_SetReceiveBuffer(Serial * self, char * data, uint32_t len);
Status SerialSpy_GetSendBuffer(Serial * self, char ** bufferPtr);
#include "SerialSpy.h"
typedef struct SerialStruct
{
char receiveBuffer[SERIAL_RECEIVE_BUFFER_SIZE];
char sendBuffer[SERIAL_SEND_BUFFER_SIZE];
} SerialStruct;
Serial * Serial_Create(void)
{
Serial * self = (Serial*)calloc(1, sizeof(SerialStruct));
return self;
}
void Serial_Destroy(Serial * self)
{
if (self == NULL)
{
return;
}
free(self);
self = NULL;
}
Status SerialSpy_SetReceiveBuffer(Serial * self, char * data, uint32_t len)
{
if (self == NULL || data == NULL)
{
return INVALID_PARAMETERS;
}
if (len > SERIAL_RECEIVE_BUFFER_SIZE)
{
return OUT_OF_BOUNDS;
}
memcpy(self->receiveBuffer, data, len);
return OK;
}
Status SerialSpy_GetSendBuffer(Serial * self, char ** bufferPtr)
{
if (self == NULL || bufferPtr == NULL)
{
return INVALID_PARAMETERS;
}
*bufferPtr = self->sendBuffer;
return OK;
}
, SerialSpy
UART. TDD unit- . . Configurator
STM32. , -, «» Visual Studio .
test-fails test-passes
test-passes Configurator_Handler
. UART Serial_ReceiveCommand
. help , help
, Serial_SendResponse
.
Configurator
UART Serial
, Serial, Configurator_Create
– Serial*
. setup()
teardown()
.
help
#include "Serial.h"
#define HELP_OUTPUT \
"Command list:\r\n \
- help\r\n \
- read: <flash_address_in_hex>\r\n \
- write: <flash_address_in_hex> <data_to_write>\r\n \
- erase: <flash_page_number_to_erase>\r\n>"
Configurator * Configurator_Create(Serial * serial);
typedef struct ConfiguratorStruct
{
char command[SERIAL_RECEIVE_BUFFER_SIZE];
Serial * serial;
} ConfiguratorStruct;
static const char helpCommand[] = "help";
Configurator * Configurator_Create(Serial * serial)
{
if (serial == NULL)
{
return NULL;
}
Configurator * self = (Configurator*)calloc(1, sizeof(ConfiguratorStruct));
if (self == NULL)
{
return NULL;
}
self->serial = serial;
return self;
}
Status Configurator_Handler(Configurator * self)
{
if (self == NULL)
{
return INVALID_PARAMETERS;
}
Status status = Serial_ReceiveCommand(self->serial, self->command);
if (status != OK)
{
return status;
}
if (strstr(self->command, helpCommand) == NULL)
{
return UNSUPPORTED;
}
Status status = Serial_SendResponse(self->serial, HELP_OUTPUT);
return status;
}
TEST_GROUP(Configurator)
{
Configurator * configurator = NULL;
Serial * serial = NULL;
void setup()
{
serial = Serial_Create();
configurator = Configurator_Create(serial);
}
void teardown()
{
Configurator_Destroy(configurator);
}
};
Serial
, . . Configurator_Handler
. SerialSpy
, . SerialSpy.c
Serial.c
– Serial.h
:
#pragma once
#include "Common.h"
#define SERIAL_RECEIVE_BUFFER_SIZE 32
#define SERIAL_SEND_BUFFER_SIZE 256
typedef struct SerialStruct Serial;
Serial * Serial_Create(void);
void Serial_Destroy(Serial * self);
Status Serial_ReceiveCommand(Serial * self, char * commandPtr);
Status Serial_SendResponse(Serial * self, char * responsePtr);
Status Serial_Clear(Serial * self);
Serial.h
ProductionCodeLib. Tests SerialSpy.c
Serial_ReceiveCommand
Serial_SendResponse
, UART .
/ SerialSpy.c
static bool IsEndOfString(char * buffer)
{
for (int i = 0; i < SERIAL_RECEIVE_BUFFER_SIZE; i++)
{
if (buffer[i] == '\n')
{
return true;
}
}
return false;
}
Status Serial_ReceiveCommand(Serial * self, char * commandPtr)
{
if (self == NULL || commandPtr == NULL)
{
return INVALID_PARAMETERS;
}
uint32_t commandLen = strlen(self->receiveBuffer);
if (commandLen == 0)
{
return NO_DATA;
}
bool isEndOfString = IsEndOfString(self->receiveBuffer);
if (isEndOfString == false)
{
return NO_DATA;
}
strncpy(commandPtr, self->receiveBuffer, commandLen);
self->receiveBuffer[SERIAL_RECEIVE_BUFFER_SIZE – 1] = 0;
return OK;
}
Status Serial_SendResponse(Serial * self, char * responsePtr)
{
if (self == NULL || responsePtr == NULL)
{
return INVALID_PARAMETERS;
}
uint32_t responseLen = strlen(responsePtr);
if (responseLen > SERIAL_SEND_BUFFER_SIZE)
{
return OUT_OF_BOUNDS;
}
strncpy(self->sendBuffer, responsePtr, responseLen);
self->sendBuffer[SERIAL_SEND_BUFFER_SIZE – 1] = 0;
return OK;
}
/ SerialSpy.c
. UART :
- –
strncpy
; - –
strlen
( \n
). strncpy
, .
:
..
OK (2 tests, 2 ran, 5 checks, 0 ignored, 0 filtered out, 0 ms)
help , . test-passes. refactor.
refactor
, . , : , . : .
, , . - «», . , .
Configurator_Handler
static
HandleCommand
. : ( help
).
static Status HandleHelpCommand(Configurator * self)
{
if (self == NULL)
{
return INVALID_PARAMETERS;
}
Status status = Serial_SendResponse(self->serial, HELP_OUTPUT);
return status;
}
static Status HandleCommand(Configurator * self)
{
if (self == NULL)
{
return INVALID_PARAMETERS;
}
if (strstr(self->command, helpCommand) != NULL)
{
return HandleHelpCommand(self);
}
return UNSUPPORTED;
}
Status Configurator_Handler(Configurator * self)
{
if (self == NULL)
{
return INVALID_PARAMETERS;
}
Status status = Serial_ReceiveCommand(self->serial, self->command);
if (status != OK)
{
return status;
}
status = HandleCommand(self);
if(status != OK)
{
return status;
}
return status;
}
refactor, fails-passes-refactor.
Configurator_Handler
:
• read
– -;
• write
– -;
• erase
– -.
, , gitlab.
-.
-
FlashSpy
Configurator
. write :
write: <flash_address_in_hex> <data_to_write>\r\n
write:
TEST(Configurator, ShouldHandleWriteFlashCommand)
{
uint32_t expectedFlashData = 0x11223344;
char writeFlashCommand[] = "write: 0x10000 0x11223344\r\n";
LONGS_EQUAL(OK, SerialSpy_SetReceiveBuffer(serial, writeFlashCommand, sizeof(writeFlashCommand)));
Status status = Configurator_Handler(configurator);
LONGS_EQUAL(OK, status);
uint32_t * flashPtr = NULL;
LONGS_EQUAL(OK, FlashSpy_GetFlashPtr(&flashPtr, 0x10000));
LONGS_EQUAL(expectedFlashData, *flashPtr);
}
4 . , - . Flash.h
:
#pragma once
#include "Common.h"
#define FLASH_PAGE_COUNT 0x80
#define FLASH_PAGE_SIZE 0x400
#define FLASH_SIZE FLASH_PAGE_COUNT * FLASH_PAGE_SIZE
Status Flash_Init(void);
Status Flash_DeInit(void);
Status Flash_Write(uint32_t address, uint32_t data);
Status Flash_Read(uint32_t address, uint32_t * dataPtr);
Status Flash_Erase(uint8_t pageNumber);
Flash.h
FlashSpy.c
: - uint8_t flash[FLASH_SIZE]
, FLASH_SIZE
. - FLASH_SIZE
, Flash_Read
, Flash_Write
Flash_Erase
.
FlashSpy.c
Tests FlashSpy_GetFlashPtr
address
. .
, , , - FlashSpy.c. flashMemory
( - STM32F103C8):
Flash_Read
32 , flashMemory
.Flash_Write
32 flashMemory
.Flash_Erase
flashMemory
STM32F103C8 0xFF memset
.
- Configurator
.
, , `FlashSpy`
#pragma once
#include "Flash.h"
Status FlashSpy_GetFlashPtr(uint32_t ** flashMemoryPtr, uint32_t address);
static uint8_t * flashMemory = NULL;
Status FlashSpy_GetFlashPtr(uint32_t ** flashMemoryPtr, uint32_t address)
{
if (flashMemory == NULL)
{
return WRONG_CONDITION;
}
if (flashMemoryPtr == NULL || address >= FLASH_SIZE)
{
return INVALID_PARAMETERS;
}
*flashMemoryPtr = (uint32_t*)&flashMemory[address];
return OK;
}
Status Flash_Init(void)
{
flashMemory = (uint8_t*)malloc(FLASH_SIZE);
if (flashMemory == NULL)
{
return FAIL;
}
memset(flashMemory, 0xFF, FLASH_SIZE);
return OK;
}
Status Flash_DeInit(void)
{
if (flashMemory == NULL)
{
return OK;
}
free(flashMemory);
flashMemory = NULL;
return OK;
}
Status Flash_Write(uint32_t address, uint32_t data)
{
if (flashMemory == NULL)
{
return WRONG_CONDITION;
}
if (address >= FLASH_SIZE)
{
return INVALID_PARAMETERS;
}
*(uint32_t*)(flashMemory + address) &= data;
return OK;
}
Status Flash_Read(uint32_t address, uint32_t * dataPtr)
{
if (flashMemory == NULL || dataPtr == NULL)
{
return WRONG_CONDITION;
}
if (address >= FLASH_SIZE)
{
return INVALID_PARAMETERS;
}
*dataPtr = *(uint32_t*)(flashMemory + address);
return OK;
}
Status Flash_Erase(uint8_t pageNumber)
{
if (flashMemory == NULL)
{
return WRONG_CONDITION;
}
if (pageNumber >= FLASH_PAGE_COUNT)
{
return INVALID_PARAMETERS;
}
uint32_t offset = pageNumber * FLASH_PAGE_SIZE;
memset(flashMemory + offset, 0xFF, FLASH_PAGE_SIZE);
return OK;
}
Flash_Write
«», - ( 0b1).
- setup()
teardown()
ConfiguratorTests.cpp.
FlashTEST_GROUP(Configurator)
{
Configurator * configurator = NULL;
Serial * serial = NULL;
void setup()
{
serial = Serial_Create();
configurator = Configurator_Create(serial);
Flash_Init();
}
void teardown()
{
Configurator_Destroy(configurator);
Flash_DeInit();
}
};
UNSUPPORTED
, . . Configurator_Handler
write. test fails.
d:\exampletdd\tests\tests\configuratortests.cpp(66): error: Failure in TEST(Configurator, ShouldHandleWriteFlashCommand)
expected < 0 0x00000000>
but was <-9 0xfffffff7>
...
Errors (1 failures, 3 tests, 3 ran, 8 checks, 0 ignored, 0 filtered out, 5 ms)
test-passes write Configurator.c. HandleWriteCommand:
- , 32 ;
- ( -);
- -
Flash_Write
; Serial_SendResponse
.
#include "Flash.h"
#define LEN_MIN_ARG sizeof("0x0")
#define LEN_WRITE_COMMAND sizeof(writeCommand) + LEN_MIN_ARG + LEN_MIN_ARG
static const char writeCommand[] = "write:";
static const char writeResponse[] = "Written successfully\r\n>";
static Status HandleWriteCommand(Configurator * self)
{
if (self == NULL)
{
return INVALID_PARAMETERS;
}
uint32_t commandLen = strlen(self->command);
if (commandLen < LEN_WRITE_COMMAND)
{
return INVALID_PARAMETERS;
}
char * flashAddressPtr = self->command + sizeof(writeCommand);
uint32_t flashAddress = strtol(flashAddressPtr, (char**)NULL, 16);
if (flashAddress > FLASH_SIZE)
{
return INVALID_PARAMETERS;
}
char * dataAddressPtr = strchr(self->command + sizeof(writeCommand), ' ');
uint32_t data = strtol(dataAddressPtr, (char**)NULL, 16);
Status status = Flash_Write(flashAddress, data);
if (status != OK)
{
return status;
}
status = Serial_SendResponse(self->serial, (char*)writeResponse);
return status;
}
static Status HandleCommand(Configurator * self)
{
if (self == NULL)
{
return INVALID_PARAMETERS;
}
if (strstr(self->command, helpCommand) != NULL)
{
return HandleHelpCommand(self);
}
else if (strstr(self->command, writeCommand) != NULL)
{
return HandleWriteCommand(self);
}
return UNSUPPORTED;
}
test-passes.
...
OK (3 tests, 3 ran, 9 checks, 0 ignored, 0 filtered out, 0 ms)
refactoring, . - .
, - . 5 - ( ). .
TDD - . . , , .
Configurator
SerialSpy
FlashSpy
UART -.
- , . .
, . . , Serial.h
Flash.h
.
, , . , unit- ShouldHandleWriteFlashCommand
. , - . .
Serial.c
Flash.c
STM32F103C8, , , . «» , .

Raccoon Security – «» , , , .