Tester les logiciels embarqués avec Google Test

introduction

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.

Outil de test

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.

Création de test

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`.

Création de Makefile

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) 

Essai

Quand je lance make, ça ressemble à ceci: test.png J'ai réussi. (Parce que c'est fait pour passer)

en conclusion

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

Tester les logiciels embarqués avec Google Test
Authentifier Google avec Django
Jugement des nombres premiers avec Python
Renforcez avec le test de code ⑦
Renforcez avec le test de code ③
Renforcez avec le test de code ⑤
Renforcez avec le test de code ④
Jugement des nombres premiers avec python
Renforcez avec le test de code ②
Renforcez avec le test de code ①
Renforcez avec le test de code ⑧
Renforcez avec le test de code ⑨
Essayez Google Mock avec C
Test unitaire du flacon avec pytest
Tester la sortie standard avec Pytest
Étudiez Python avec Google Colaboratory
À propos de l'apprentissage avec Google Colab
Faites de la programmation embarquée par développement piloté par les tests avec googletest ~ SOLID design ~
Test de charge Websocket avec Locust
Montez Google Drive avec google-drive-ocamlfuse
Accédez à Google Drive avec Python
Essayez OpenCV avec Google Colaboratory
Traduire - Aide de PHP_UML avec Google Traduction
Clustering embarqué profond avec Chainer 2.0
Développement piloté par les tests avec Django Partie 3
Aide-mémoire personnel Google Test / Mock
Développement piloté par les tests avec Django Partie 4
Développement piloté par les tests avec Django Partie 6
Développement piloté par les tests avec Django Partie 2
Développement Google AppEngine avec Docker
Sortie du journal de test unitaire avec python
Détection des fonctionnalités OpenCV avec Google Colaboratory
Développement piloté par les tests avec Django Partie 1
Jouez avec Turtle sur Google Colab
Présentation de l'API Google Map avec rails
Développement piloté par les tests avec Django Partie 5
Contrôle des relances de test avec Luigi + pytest