Software-Test Code-Coverage auf kleinen Targets

Verifysoft Technology

In sicherheitskritschen Systemen darf kein Stück Code im Produktionsbetrieb ablaufen, ohne vorher intensive Tests durchlaufen zu haben. Aber auch zur Qualitätssicherung muss Software getestet werden. Um die Codeüberdeckung zu prüfen, sind zusätzliche Prüf-Instruktionen im Quellcode erforderlich. Auf kleinen Systemen mit wenig RAM kann sich der Entwickler dann etwas einfallen lassen, damit das funktioniert.

Die Komplexität eingebetteter Software hat in den letzten Jahren zugenommen. Diese Steigerung lässt sich durch Software-Maße (Software-Metriken) wie Lines of Code (LOC), Cyclomatic Complexity (CC) oder Halstead-Metriken (hierbei werden Operatoren und Operanden gezählt) auch quantitativ nachweisen.

Um die wachsende Komplexität beherrschen zu können, müssen auch die Testaktivitäten erhöht werden. In den letzten Jahren sind so auch vermehrt White-Box-Tests in Kombination mit so genannten xUnit-Testframeworks im Umfeld von Embedded Systems zu beobachten. Bei sicherheitskritischen Systemen wie beispielsweise in der Luft- und Raumfahrt oder der Medizintechnik geben zudem Normen je nach Kritikalität des Systems vor, welche Testtechniken zwingend erforderlich sind.

Eine dieser Testtechniken stellt der kontrollflussorientierte Test dar, der zu den strukturorientierten Testverfahren gezählt werden kann. Grundlage des kontrollflussorientierten Tests ist der gerichtete Kontrollflussgraf, der die verschiedenen einfachen und geschachtelten Kontrollstrukturen einer Funktion enthält. Alle Kontrollstrukturen lassen sich auf die vier Basiskontrollstrukturen Sequenz, Abfrage, abweisende und nicht abweisende Schleife zurückführen. Ausgehend von diesem gerichteten Kontrollflussgrafen können unterschiedliche Testziele definiert werden.

In Bild 1 ist der gerichtete Kontrollflussgraf der exemplarischen Funktion uart_putc (Listing 1) dargestellt. Die einzelnen Knoten symbolisieren hierbei die Anweisungen, während die Übergänge von einer zur nächsten Anweisung als Kanten repräsentiert werden. Der Einstiegs- und Ausstiegspunkt der Funktion ist durch die Knoten Ne und Na modelliert. Die abweisende while-Schleife mit leerem Schleifenkörper wird hier im Beispiel durch die Knoten N2 und N3 repräsentiert, während die Abfrage durch die Knoten N4 bis N8 abgebildet ist. Der fehlende else-Zweig stellt die Kante von N4 nach N8 dar.

void uart_putc(char c)
{
uint8_t next_pos=(send_buf_end+1)&SEND_BUF_MASK;

/* wait till we got space in buffer */
while ((SREG & _BV(SREG_I)) && (next_pos == send_buf_start));

if     (next_pos != send_buf_start)
    {
    send_buf[send_buf_end]=c;
    send_buf_end=next_pos;
    /*Enable UDR interrupt. will immediatly call ISR if UDRE is set*/
    UCSRB = _BV(TXEN) | _BV(RXEN) | _BV(UDRIE) | _BV(RXCIE);
    }
}
Listing 1. Programmcode der Funktion uart_putc

Anforderungen an die Testabdeckung

Zur Messung des Testfortschritts gibt es unterschiedliche Ausprägungen der Testabdeckung. Aus Sicht des funktionalen Tests sollten im Rahmen von Black-Box-Tests nach Möglichkeit alle Anforderungen abgedeckt werden. Man spricht in diesem Zusammenhang von Anforderungs-Coverage. Ob hier nur ein Testfall pro Anforderung gerade bei sicherheitskritischen Systemen ausreichend ist, sollte man im Detail hinterfragen. Je nach Kritikalität der Anforderung empfiehlt es sich eher, eine bestimmte Anzahl an Positiv- und Negativ-Testfällen vorzugeben. Zur Bewertung der White-Box-Tests ist es von Interesse, welche Teile des Programmcodes durch diese Tests überhaupt erreicht wurden. Man spricht in diesem Zusammenhang von Code Coverage. Diese Art der Überdeckung wird meist vollständig den White-Box-Testverfahren zugeordnet.

In der Praxis hat sich aber auch ein effizienter Mischansatz bewährt. Nach Ausführung der funktionalen Black-Box-Tests, die als Hauptziel die oben genannte Anforderungs-Coverage haben, wird die Code Coverage gemessen, die durch diese funktionalen Tests erzielt wurde. Die nicht erreichten Teile des Programmcodes sind danach durch gezielte White-Box-Tests zu ergänzen, um damit die geforderte Code Coverage zu erreichen. Diese hybride Vorgehensweise kann als eine Variante des Grey-Box-Testens angesehen werden.