Effizienter Test von Software-Varianten Auf Nummer sicher

Sicherheitskritische Applikationen lassen sich mit entsprechenden Software-Tests am Laufen halten, ohne teuer Nachbesserungen.
Sicherheitskritische Applikationen lassen sich mit entsprechenden Software-Tests am Laufen halten, ohne teuer Nachbesserungen.

Beim Test von Software-Varianten sind sicherheitsrelevante Normen wie die ISO 26262 zu berücksichtigen. Sicherheitskritische Applikationen müssen bei einer Fehlfunktion weiter funktionieren. Darüber hinaus lassen sich mit den Tests sowohl überflüssige Arbeiten als auch Nachbesserungen vermeiden.

Die Herausforderung beim Testen von Software-Varianten besteht darin, dass jede Variante vollständig getestet werden muss. Nachfolgend wird eine Methode zur Wiederverwendung und Vererbung von Variantentests vorgestellt. Über die Definition von Basistests, die an Variantentests vererbt werden, kann redundante Arbeit vermieden werden. Bei jeder Änderung der Applikation müssen die Tests nur an einer Stelle gepflegt werden.

Problemstellung

Sicherheitskritische Normen der verschiedenen Industriezweige, wie die ISO 26262 für die Automobiltechnik oder die IEC 62304 für die Medizintechnik verlangen beim Test eine vollständige Code-Abdeckung (Code-Coverage). Das bedeutet, dass jede Software-Variante vollständig getestet werden muss. In der Praxis geschieht das oft durch Kopieren der Tests einer Variante und der Anpassung der kopierten Tests an die jeweils andere Variante.

Neue Anforderungen oder Änderungen der Software verteuern einen solchen Test der Varianten aufgrund redundant durchzuführender Änderungen in allen Varianten. Neben dem hohen Aufwand bei der Pflege und Erweiterung solcher Tests können sich zum Beispiel durch Copy&Paste sehr leicht Fehler einschleichen, durch die letztendlich auch sicherheitskritische Fehler in der Applikation unentdeckt bleiben können.

Was ist eine Variante?

Es gibt verschiedene Möglichkeiten, Software-Varianten zu erstellen, zum Beispiel C/C++ Source Code:

  • Ein-/Ausschalten von Code-Teilen durch Defines
  • Generierung von Code-Varianten über Tools, beispielsweise aus Matlab)
  • Kopieren, Umbenennen und Ändern von Quelldateien
  • Gleiche Quellen auf verschiedenen Hardware-Plattformen bei hohen Sicherheitsanforderungen ausführen

Eine Software-Variante ist durch eine bestimmte Konfiguration eines Software-Moduls, beispielsweise einer C-Quelldatei, definiert. Diese Variante muss nicht notwendigerweise funktional sein, es kann sich auch um eine abstrakte Variante handeln. Erst durch spezifische Einstellungen – in der Regel über Defines – werden aus einer abstrakten Basis-Variante die tatsächlich verwendbaren Software-Varianten.

Testziel: Code-Coverage

Für die vollständige Code-Abdeckung jeder Variante könnte das Messergebnis der Code-Coverage des variantenspezifischen Codes mit dem Messergebnis des gemeinsam genutzten Codes zusammengezählt werden. In Bild 1 ist ein einfaches Beispiel einer Code-Variante dargestellt, bei der zur Variable „level“ noch ein weiterer Wert aufaddiert werden soll.

Der gelb markierte Programmierfehler, nämlich das Fehlen des Additionsoperators in der Zeile 16, könnte unentdeckt bleiben, wenn der gemeinsam genutzte Code aus den Zeilen 19 bis 23 nicht auch in der Variante getestet wird.

Es reicht daher nicht, einzelne Teile eines mit Hilfe von Defines zusammengesetzten Varianten-Codes zu testen und die Messergebnisse der Code Coverage zusammenzuzählen: Jede Code-Variante ist wie ein eigenständiges Programm zu sehen, da ausgeblendete oder dazugekommene Teile die gemeinsamen Teile beeinflussen können.

Lösungsansatz

Anhand eines Beispiels wird im Folgenden eine Methode zur Erstellung und Pflege von Variantentests vorgestellt. Das Beispiel behandelt eine Funktion zur Statusanzeige des Tankinhalts verschiedener Fahrzeugvarianten, Pkw und Lkw. Erschwerend kommt hinzu, dass die Fahrzeugvarianten „Lkw“ mit einem Zusatztank ausgestattet sein können, dessen Pegelstand ebenfalls berücksichtigt werden soll.

Beispielfunktion

Als Beispiel soll eine Funktion dienen, die bezogen auf den Füllstand des Tanks eines Fahrzeugs einen Status zurückliefert.

Die Spezifikation ist in Bild 2 in grafischer Form gegeben: Die Funktion soll eine Warnung oder einen Alarm zurückgeben, wenn jeweils ein definierter Pegelstand unterschritten wird. Ansonsten liefert die Funktion den Wert „Normal“.

Eine einfache Implementierung dieser Funktion könnte wie in Bild 3 dargestellt aussehen. Über die Varianten-Konfiguration – #define.