TDD untuk mikrokontroler. Bagian 1: Penerbangan
TDD pertama untuk mikrokontroler. Bagian 2: Bagaimana mata-mata menyingkirkan ketergantungan
TDD untuk mikrokontroler. Bagian 3: berjalan di atas besi

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_EQUALHELP_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_Read32 ,- flashMemory.
- Flash_Write32- flashMemory.
- Flash_Erase- flashMemorySTM32F103C8 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 β «» , , , .