Testen Sie eingebettete Software mit Google Test

Einführung

Bisher habe ich TDD nicht hauptsächlich für die Embedded-Entwicklung durchgeführt, aber ich habe Embedded-Programmierung durch testgetriebene Entwicklung gelesen und wollte es vorstellen. Ist angestiegen. Ich studiere während der Entwicklung des STM32 Nucleo Board, also hoffe ich, TDD darauf anzuwenden.

Testwerkzeug

Unity wird in dem Buch vorgestellt, aber ich werde Google Test verwenden. Eingeführt Google Test / Google Mock Kann es in 1 Minute erledigt werden? Keine Installation erforderlich, Google Testbeispiel für Linux Ich bezog mich auf. Google Test hat 1.8.0 von [hier] heruntergeladen (https://github.com/google/googletest/releases).

Testerstellung

Im Fall von TDD muss der Test nicht zuerst erstellt werden, aber dieses Mal werde ich den Code testen, den ich bereits erstellt habe. Das Testziel ist der USART-Treiber, der von STM32 Nucleo Board Bare Metal Hallo Welt erstellt wurde. Der USART-Treiber wurde geändert, um das Testen zu vereinfachen. (Der Quellcode lautet hier) Da auf Empfang und Übertragung gewartet wird, ist es schwierig, nur normale Testfälle zu implementieren. Daher werden wir Mock verwenden, um eine Alternative zur HW-abhängigen E / A-Implementierung zu implementieren. Ich kann die detaillierte Grammatik nicht erklären. Weitere Informationen finden Sie im Google Test Manual.

test_usart.cpp


//Testfallbeschreibungsdatei
#include "gtest/gtest.h"
#include "gmock/gmock.h"

//Ich möchte die zu testende Funktion aufrufen können
// extern "C"Nicht als C ohne interpretiert
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);
}
}

Die MockIo-Klasse ersetzt die vom USART-Treiber verwendete Registerkonfigurationsschnittstelle io_reg.h. Dies wird beim Ausführen des Tests verwendet, damit festgestellt werden kann, ob die Registereinstellungen und -sequenzen korrekt sind. Die Funktion DelegateToVirtual () beschreibt den Prozess, den Sie ausführen möchten, wenn Sie Mock aufrufen. Zum Beispiel ON_CALL(*this, SetBit(_, _)).WillByDefault(Invoke(this,&MockIo::FakeSetBit)); Wenn Sie schreiben, wird FakeSetBit gleichzeitig mit dem Aufruf von SetBit aufgerufen. Dies geschieht, weil die Pseudoregistereinstellungen nicht nur mit Mock beibehalten werden können.

Schreiben Sie als Nächstes einen Testfall.

test_usart.Fortsetzung von cpp


RCC_TypeDef *virtualRcc;
GPIO_TypeDef *virtualGpio;
USART_TypeDef *virtualUsart;

//UsartTest ist wie ein Gruppenname, der Testfälle zusammenfasst
class UsartTest : public ::testing::Test {
    protected:
        //Gruppierte Testfälle vor dem Ausführen jedes Testfalls
        //Rufen Sie diese Funktion auf. Der Testcode ist sauberer, wenn Sie einen allgemeinen Initialisierungsprozess einschließen.
        virtual void SetUp()
        {
    		mock = new MockIo();
			//Die Testumgebung erstellt einen Pseudoregisterbereich, da die Register nicht dem Speicher zugeordnet sind
    		virtualRcc = new RCC_TypeDef();
    		virtualGpio = new GPIO_TypeDef();
    		virtualUsart = new USART_TypeDef();
    		UsartCreate(virtualRcc, virtualGpio, virtualUsart);
        }
        //Eine Funktion, die nach dem Ausführen eines Testfalls wie SetUp aufgerufen wird. Beschreiben Sie die allgemeine Bereinigung.
        virtual void TearDown()
        {
    		delete mock;
			delete virtualRcc;
			delete virtualGpio;
			delete VirtualUsart;
        }
};

//Testfall
TEST_F(UsartTest, Init)
{
	mock->DelegateToVirtual();

	EXPECT_CALL(*mock, SetBit(_, _)).Times(6); //Die Anzahl der Male spielt keine Rolle, ist also subtil, sondern weil es eine Warnung gibt
	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);
}

Zur Zeit schreibe ich einen Test für jede Schnittstelle von usart_driver.h. Der Teil, in dem HW das Register wie die Empfangsvervollständigung setzt, wird durch Setzen des Rückgabewerts mit "EXPECT_CALL" realisiert.

Makefile-Erstellung

Es wird in der Host-Umgebung anstelle der Zielumgebung erstellt. Platzieren Sie Google Test an der Position von GTEST_DIR. Führen Sie zuerst make gtest -gen aus und führen Sie den Test mit make aus.

# Makefile
# gtest_main.cc ist die Hauptfunktion von Google Test.
# gmock-gtest-all.cc ist eine Datei, die alle Google-Tests enthält
# -Beachten Sie auch, dass lpthread beigefügt ist.
#Nach make oder make all erstellen und ausführen.

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) 

Testlauf

Wenn ich make starte, sieht es so aus: test.png Ich bestand. (Weil es gemacht ist, um zu bestehen)

abschließend

Ich bin mir nicht sicher, ob dies aufgrund mangelnder Erfahrung für Testfälle ausreicht. Jetzt, da ich Tests schreiben kann, möchte ich TDD tatsächlich einbeziehen und Erfahrungen sammeln. Ich denke, dass es viele Dinge gibt, die ohne die eigentliche Maschine nicht bestätigt werden können, aber ich hoffe, dass sich die Designqualität durch Berücksichtigung des Tests verbessern wird.

Recommended Posts

Testen Sie eingebettete Software mit Google Test
Authentifizieren Sie Google mit Django
Primzahlbeurteilung mit Python
Mit Codetest stärken ⑦
Mit Codetest stärken ⑤
Mit Codetest stärken ④
Primzahlbeurteilung mit Python
Mit Codetest stärken ②
Mit Codetest stärken ①
Mit Codetest stärken ⑧
Mit Codetest stärken ⑨
Versuchen Sie Google Mock mit C.
Testen Sie den Kolben mit einem Pytest
Testen Sie die Standardausgabe mit Pytest
Lernen Sie Python mit Google Colaboratory
Über das Lernen mit Google Colab
Führen Sie Embedded-Programmierung durch testgetriebene Entwicklung mit googletest ~ SOLID design ~ durch
Laden Sie den Test-Websocket mit Locust
Hängen Sie Google Drive mit google-drive-ocamlfuse ein
Greifen Sie mit Python auf Google Drive zu
Probieren Sie OpenCV mit Google Colaboratory aus
Übersetzen - Hilfe von PHP_UML mit Google Übersetzung
Deep Embedded Clustering mit Chainer 2.0
Testgetriebene Entwicklung mit Django Teil 3
Persönlicher Spickzettel von Google Test / Mock
Testgetriebene Entwicklung mit Django Teil 4
Testgetriebene Entwicklung mit Django Teil 6
Testgetriebene Entwicklung mit Django Teil 2
Google AppEngine-Entwicklung mit Docker
Unit Test Log Ausgabe mit Python
OpenCV-Funktionserkennung mit Google Colaboratory
Testgetriebene Entwicklung mit Django Teil 1
Spielen Sie mit Turtle auf Google Colab
Einführung der Google Map API mit Schienen
Testgetriebene Entwicklung mit Django Teil 5
Kontrollieren von Testwiederholungen mit Luigi + pytest