Jusqu'à présent, je n'ai pas fait de TDD principalement pour le développement embarqué, mais j'ai lu Programmation embarquée par développement piloté par les tests et je veux l'introduire. A augmenté. J'étudie tout en faisant Développement de STM32 Nucleo Board, j'espère donc appliquer TDD à cela.
Unity est introduit dans le livre, mais j'utiliserai Google Test. Introduit Google Test / Google Mock Cela peut-il être fait en 1 minute? Aucune installation requise, exemple de test Google pour Linux Je l'ai mentionné. Google Test a téléchargé la version 1.8.0 depuis ici.
Dans le cas de TDD, le test n'a pas besoin d'être créé au préalable, mais cette fois je vais tester le code que j'ai déjà créé. La cible du test est le pilote USART créé par STM32 Nucleo Board bare metal hello world. Le pilote USART a été modifié pour faciliter le test. (Le code source est ici) Puisqu'il y a attente de réception et attente de transmission, il est difficile à mettre en œuvre avec seulement des cas de test normaux. Par conséquent, nous utiliserons Mock pour implémenter une alternative à l'implémentation d'E / S dépendant du matériel. Je ne peux pas expliquer la grammaire détaillée, veuillez donc vous référer au Manuel de test Google.
test_usart.cpp
//Fichier de description de cas de test
#include "gtest/gtest.h"
#include "gmock/gmock.h"
//Je souhaite pouvoir appeler la fonction testée
// extern "C"Non interprété comme C sans
extern "C" {
#include "usart_driver.h"
#include "stm32f303x8.h"
}
using ::testing::_;
using ::testing::Invoke;
class MockIo{
public:
MOCK_METHOD2(SetBit, void (__IO void*, uint32_t ));
MOCK_METHOD2(ClearBit, void (__IO void*, uint32_t ));
MOCK_METHOD2(ReadBit, uint32_t (__IO void*, uint32_t ));
MOCK_METHOD1(ClearReg, void (__IO void* ));
MOCK_METHOD2(WriteReg, void (__IO void*, uint32_t ));
MOCK_METHOD1(ReadReg, uint32_t (__IO void* ));
void FakeSetBit(__IO void* address, uint32_t bit){
*((uint32_t*)address) |= bit;
}
void FakeClearBit(__IO void* address, uint32_t bit){
*((uint32_t*)address) &= ~bit;
}
void FakeClearReg(__IO void* address){
*((uint32_t*)address) = 0;
}
void FakeWriteReg(__IO void* address, uint32_t data){
*((uint32_t*)address) = data;
}
void DelegateToVirtual() {
ON_CALL(*this, SetBit(_, _)).WillByDefault(Invoke(this, &MockIo::FakeSetBit));
ON_CALL(*this, ClearBit(_, _)).WillByDefault(Invoke(this, &MockIo::FakeClearBit));
ON_CALL(*this, ClearReg(_)).WillByDefault(Invoke(this, &MockIo::FakeClearReg));
ON_CALL(*this, WriteReg(_, _)).WillByDefault(Invoke(this, &MockIo::FakeWriteReg));
}
};
MockIo *mock;
extern "C" {
void SetBit(__IO void* address, uint32_t data){
mock->SetBit(address, data);
}
void ClearBit(__IO void* address, uint32_t data){
mock->ClearBit(address, data);
}
void ReadBit(__IO void* address, uint32_t data){
mock->ReadBit(address, data);
}
void ClearReg(__IO void* address){
mock->ClearReg(address);
}
void WriteReg(__IO void* address, uint32_t data){
mock->WriteReg(address, data);
}
uint32_t ReadReg(__IO void* address){
return mock->ReadReg(address);
}
}
La classe MockIo remplace l'interface de configuration de registre io_reg.h utilisée par le pilote USART.
Ceci est utilisé lors de l'exécution du test, il est donc possible de déterminer si les paramètres et les séquences du registre sont corrects.
La fonction DelegateToVirtual () décrit le processus que vous souhaitez exécuter lorsque vous appelez Mock.
Par exemple
ON_CALL(*this, SetBit(_, _)).WillByDefault(Invoke(this,&MockIo::FakeSetBit));
Si vous écrivez, FakeSetBit sera appelé en même temps que SetBit est appelé.
Cela est fait car les paramètres de pseudo-registre ne peuvent pas être conservés uniquement avec Mock.
Ensuite, écrivez un cas de test.
test_usart.Poursuite du cpp
RCC_TypeDef *virtualRcc;
GPIO_TypeDef *virtualGpio;
USART_TypeDef *virtualUsart;
//UsartTest est comme un nom de groupe qui regroupe les cas de test
class UsartTest : public ::testing::Test {
protected:
//Cas de test groupés avant d'exécuter chaque cas de test
//Appelez cette fonction. Le code de test sera plus propre si vous incluez un processus d'initialisation commun.
virtual void SetUp()
{
mock = new MockIo();
//L'environnement de test crée une zone de pseudo-registres car les registres ne sont pas mappés à la mémoire
virtualRcc = new RCC_TypeDef();
virtualGpio = new GPIO_TypeDef();
virtualUsart = new USART_TypeDef();
UsartCreate(virtualRcc, virtualGpio, virtualUsart);
}
//Une fonction appelée après l'exécution d'un cas de test comme SetUp. Décrivez le nettoyage courant.
virtual void TearDown()
{
delete mock;
delete virtualRcc;
delete virtualGpio;
delete VirtualUsart;
}
};
//cas de test
TEST_F(UsartTest, Init)
{
mock->DelegateToVirtual();
EXPECT_CALL(*mock, SetBit(_, _)).Times(6); //Le nombre de fois n'a pas d'importance, donc c'est subtil, mais parce qu'il y a un avertissement
EXPECT_CALL(*mock, ClearReg(_)).Times(3);
EXPECT_CALL(*mock, WriteReg(_, _)).Times(1);
UsartInit();
EXPECT_EQ(RCC_AHBENR_GPIOAEN, virtualRcc->AHBENR & RCC_AHBENR_GPIOAEN);
EXPECT_EQ(GPIO_MODER_MODER2_1|GPIO_MODER_MODER15_1, virtualGpio->MODER);
EXPECT_EQ(0x700, virtualGpio->AFR[0]);
EXPECT_EQ(0x70000000, virtualGpio->AFR[1]);
EXPECT_EQ(RCC_APB1ENR_USART2EN, virtualRcc->APB1ENR);
EXPECT_EQ(8000000L/115200L, virtualUsart->BRR);
EXPECT_EQ(USART_CR1_RE|USART_CR1_TE|USART_CR1_UE, virtualUsart->CR1);
}
using ::testing::Return;
TEST_F(UsartTest, IsReadEnable)
{
EXPECT_CALL(*mock, ReadBit(&virtualUsart->ISR, USART_ISR_RXNE)).WillOnce(Return(0));
EXPECT_EQ(0, UsartIsReadEnable());
EXPECT_CALL(*mock, ReadBit(&virtualUsart->ISR, USART_ISR_RXNE)).WillOnce(Return(USART_ISR_RXNE));
EXPECT_EQ(USART_ISR_RXNE, UsartIsReadEnable());
}
TEST_F(UsartTest, IsWriteEnable)
{
EXPECT_CALL(*mock, ReadBit(&virtualUsart->ISR, USART_ISR_TXE)).WillOnce(Return(0));
EXPECT_EQ(0, UsartIsWriteEnable());
EXPECT_CALL(*mock, ReadBit(&virtualUsart->ISR, USART_ISR_TXE)).WillOnce(Return(USART_ISR_TXE));
EXPECT_EQ(USART_ISR_TXE, UsartIsWriteEnable());
}
TEST_F(UsartTest, Read)
{
EXPECT_CALL(*mock, ReadBit(&virtualUsart->ISR, USART_ISR_RXNE)).WillRepeatedly(Return(0));
EXPECT_CALL(*mock, ReadBit(&virtualUsart->ISR, USART_ISR_RXNE)).WillRepeatedly(Return(USART_ISR_RXNE));
EXPECT_CALL(*mock, ReadReg(&virtualUsart->RDR)).WillRepeatedly(Return('a'));
EXPECT_EQ('a', UsartRead());
}
TEST_F(UsartTest, Write)
{
mock->DelegateToVirtual();
char c = 's';
EXPECT_CALL(*mock, ReadBit(&virtualUsart->ISR, USART_ISR_TXE)).WillRepeatedly(Return(0));
EXPECT_CALL(*mock, ReadBit(&virtualUsart->ISR, USART_ISR_TXE)).WillRepeatedly(Return(USART_ISR_TXE));
EXPECT_CALL(*mock, WriteReg(&virtualUsart->TDR, c));
UsartWrite(c);
EXPECT_EQ(c, virtualUsart->TDR);
}
Pour le moment, j'écris un test pour chaque interface de usart_driver.h. La partie où HW définit le registre tel que l'achèvement de la réception est réalisée en définissant la valeur de retour avec ʻEXPECT_CALL`.
Il sera construit dans l'environnement hôte au lieu de l'environnement cible.
Placez Google Test à la position GTEST_DIR.
Commencez par lancer make gtest -gen
et lancez le test avec make
.
# Makefile
# gtest_main.cc est la fonction principale fournie par Google Test,
# gmock-gtest-all.cc est un fichier contenant tous les tests Google
# -Notez également que lpthread est attaché.
#Après avoir créé ou créé tout, créez et exécutez.
TESTNAME = test_usart
GTEST_DIR = ../../../../../..
TEST_DIR = .
CODE_DIR = ..
INC_DIR = ../../..
INCLUDE = -I$(GTEST_DIR) -I$(INC_DIR)/include
SRCS = $(CODE_DIR)/usart_driver.c
OBJECTS = usart_driver.o
all: $(OBJECTS) $(TESTNAME)
./$(TESTNAME)
$(TESTNAME): $(OBJECTS)
g++ -o $(TESTNAME) test_usart.cpp $(GTEST_DIR)/googletest/googletest/src/gtest_main.cc $(GTEST_DIR)/gmock-gtest-all.cc $(INCLUDE) -lpthread $(OBJECTS) -D DEBUG_GTEST
$(OBJECTS): $(SRCS)
gcc -c $(SRCS) $(INCLUDE) -DEBUG_GTEST
clean:
rm *.o $(TESTNAME)
gtest-gen:
python $(GTEST_DIR)/googletest/googlemock/scripts/fuse_gmock_files.py $(GTEST_DIR)
Quand je lance make, ça ressemble à ceci: J'ai réussi. (Parce que c'est fait pour passer)
Je ne suis pas sûr que cela soit suffisant pour les cas de test en raison du manque d'expérience. Cependant, maintenant que je peux écrire des tests, j'aimerais intégrer le TDD et acquérir de l'expérience. Je pense qu'il y a beaucoup de choses qui ne peuvent pas être confirmées sans la machine réelle, mais j'espère que la qualité de la conception s'améliorera en considérant le test.
Recommended Posts