Welchen Code testen Sie eigentlich?

Autor / Redakteur: Niroshan Rajadurai * / Franz Graser |

Die in der Entwicklung verwendete Testumgebung muss so genau wie möglich mit der Zielumgebung übereinstimmen . Dabei muss man insbesondere sicherstellen, dass der geprüfte Objektcode mit dem Objektcode identisch ist, der im Feld zum Einsatz kommt.

Anbieter zum Thema

Das T-Shirt des Online-Versenders Zazzle fasst die Crux vieler Softwaretester griffig zusammen: Immer wieder das Gleiche zu tun und dennoch ein anderes Resultat zu erwarten.
Das T-Shirt des Online-Versenders Zazzle fasst die Crux vieler Softwaretester griffig zusammen: Immer wieder das Gleiche zu tun und dennoch ein anderes Resultat zu erwarten.
(Bild: zazzle.de)

Beim Konzipieren einer Testumgebung sollte man auf eine perfekte Übereinstimmung mit der Zielumgebung in Bezug auf folgende Kriterien achten:

Target: Die Test-Hardware sollte der Ziel-Hardware entsprechen.

Compiler: Die Testumgebung sollte mit den gleichen Werkzeugen mit den gleichen Versionen übersetzt werden.

Objektcode: Compiler- und Linker-Einstellungen sowie zugelinkte Bibliotheken sollten mit denen der Produktion übereinstimmen.

Modul- und Integrationstests sind entscheidende Komponenten eines übergreifenden Testprozesses für Software-Applikationen. Besonders nützlich sind sie vor allem bei Applikationen, die 100-prozentige Codeabdeckung vor ihrem Feldeinsatz erfordern. Es gibt zahlreiche Werkzeuge die bei der automatischen Erstellung von Test-Frameworks und der Verwaltung von Testfällen helfen.

Einer der häufig missverstandenen und vielleicht auch beschönigten Aspekte bei der Auswahl von Testwerkzeugen betrifft die Art und Weise, wie diese Werkzeuge die Syntax des Anwendungscodes analysieren und das Test-Framework aufbauen. Auch wenn der Kern der Programmiersprachen C und C++ durch ANSI- oder ISO-Standards bestimmt wird, fügen Compiler-Anbieter Spracherweiterungen hinzu, um ihre Compiler für eine bestimmte Architektur zu optimieren. Zwei gängige Spracherweiterungen sind zum Beispiel:

__packed: dient zur Deaktivierung von Füllzeichen, die ein Compiler bei der Erzeugung von Datenobjekten normalerweise verwendet

__irq: dient zur Definition einer Funktion, die mit einem Interrupt-Vektor verknüpft wird. Dieses Schlüsselwort weist den Compiler in der Regel dazu an, Code zu erstellen, der eine Stack Überprüfung unterbindet.

Die Herausforderung für den Parser eines Modultest-Automatisierungswerkzeugs ist es, diese Compiler-spezifischen Erweiterungen/Schlüsselworte zu verstehen und einen dafür geeigneten Testcode zu generieren. Man darf sich allerdings fragen, was "geeignet" in diesem Zusammenhang bedeutet: Viele Automatisierungswerkzeuge führen lediglich Makros ein, um diese Erweiterungen per #define auf Null zu setzen.

#define __packed=””

#define __irq =””

Mit diesem Vorgehen kann der Werkzeug-Anbieter eine breite Palette von Compilern unterstützen, ohne seinen Parser für jeden Compiler-Anbieter anpassen zu müssen. Allerdings hat dieses Vorgehen auch gravierende Nachteile: Es verändert den zu testenden Code und erzeugt im Vergleich zu den auf einem realen System laufenden Binärdaten eine völlig andere Binärdatei.

Dies kann dazu führen, dass es im Code nicht-überprüfte Grenzbedingungen (aufgrund von unterschiedlich dimensionierten Typen) gibt, oder dass sich im schlimmsten Fall wegen der Auslassung eines Schlüsselworts die Bedeutung des Codes komplett verändert. Die folgenden Beispiele verdeutlichen die Auswirkungen von derartig eingesetzten Makros.

Fallbeispiel: Compiler-Schlüsselwort __packed

Das Schlüsselwort __packed wird verwendet, um den Compiler von der Ausrichtung der Datenstrukturen auf Byte- oder Wort-Grenzen abzuhalten, um eine effizientere Datenspeicherung zu erzielen, oder um ein Mapping auf eine externe Datenquelle zu erzielen. Im folgenden Beispiel bewirkt das Schlüsselwort __packed, dass sich die Größe des Struct-Typen von 8 Bytes auf 5 Bytes verringert, weil auf die Ausrichtung der Datentypen auf "Long Word" verzichtet wurde.

Fallbeispiel: Compiler-Schlüsselwort __irq

Das Schlüsselwort __irq weist den Compiler an, dass die betreffende Funktion für die Bearbeitung eines Interrupts verwendet wird.

In diesem Beispiel wird gezeigt, was geschieht, wenn man das Schlüsselwort entfernt.

Man kann erkennen, dass beide Funktionen das Register R1 nutzen. Im Fall der __irq-Funktion wird der Inhalt von R1 gesichert und wiederhergestellt. Die Funktion ohne das Schlüsselwort __irq nutzt R1 ohne vorherige Wiederherstellung. Der Compiler weiß, dass bei Zugriff aus einer Interrupt-Funktion der Inhalt des Registers R1 unbestimmt ist und daher zunächst wiederhergestellt werden muss.

Bildergalerie

Welchen Code testen Sie nun wirklich?

Handelt es sich also bei dem Code, den Sie im Modultest geprüft haben, wirklich um den, der auf ihrem Ziel-System laufen wird? Oder handelt es sich dabei nicht eher um eine modifizierte Version des Codes, die erstellt wurde, um dem Werkzeuganbieter das Leben zu erleichtern?

Eine Interpretation von Compiler-Schlüsselworten durch Testwerkzeuge kann – wie die beiden oben angesprochenen Beispiele zeigen – erhebliche Auswirkungen auf den erzeugten Objektcode haben. Wird dies also nicht korrekt erledigt, dann entspricht der erzeugte Code nicht mehr dem, der auf dem Zielsystem ausgeführt wird.

Angesichts einer dramatischen Zunahme der Code-Komplexität und -Wiederverwendung wird der gleiche Code oft auf vielen unterschiedlichen Hardwarebausteinen und -Systemen eingesetzt. Wechselt man zwischen unterschiedlichen CPU-Familien und Tool Chains, so ist es wichtig, dass man jede Variante überprüft, und dass man sicherstellt, dass die Tests unter Berücksichtigung der endgültigen Zielumgebung ausgeführt werden.

Bei der Verwendung von Werkzeugen zur Test Automatisierung kommt es darauf an, Werkzeuge einzusetzen, die nicht nur den standardisierten C- und C++- Sprachumfang, sondern auch die von Ihren Entwicklungswerkzeugen verwendeten Schlüsselwort-Erweiterungen unterstützen.

Das Unternehmen Vector Software hat viel Know-how in seine VectorCAST-Technologie investiert, um ein Höchstmaß an Übereinstimmung zwischen der Testumgebung und der Zielumgebung zu gewährleisten. Vector-Produkte kommen bei der Zertifizierung sicherheitskritischer Systeme nach höchsten Sicherheitsstandards zum Einsatz. Die Entwicklungsprozesse und -Produkte von Vector Software wurden vom TÜV für den Einsatz bei Kunden zertifiziert, die sicherheitskritische Anwendungen erstellen. Sie dürfen also mehr von ihren Testwerkzeugen erwarten!

* * Niroshan Rajadurai ist Direktor EMEA bei Vector Software.

(ID:42308972)