Die Code-Coverage wird beim Testen von Software ermittelt. Basierend auf dieser Auswertung werden neue Testfälle abgeleitet, überflüssige Tests gestrichen und unvollständige Tests geändert oder ersetzt. Darüber hinaus lassen sich mit Code-Coverage-Analyse Code-Abschnitte finden, die niemals ausgeführt wurden. Dieser so genannte „dead code“ verschwendet Speicher und ist eine mögliche Gefahrenquelle, wenn dieser Code ungetestet in ein Endprodukt mit einfließt.
Aktuelle Standards für die Entwicklung sicherheitskritischer Software in Märkten wie Luftfahrt, Automobil oder Medizin (etwa DO-178B, ISO 26262, IEC 62304) empfehlen die Messung von Code-Coverage ausdrücklich, und zwar in der Regel auf Quellcode-Ebene. Boris Beizer beschreibt in seinem Buch „Software Testing Techniques“, dass gemessene 100% Statement-Coverage auf Quellcode-Ebene nur 75% oder sogar noch weniger Statements auf Objektcode-Ebene entsprechen.
Hintergründe
In Hochsprachen (C, C++, ADA …) geschriebene Programme laufen nicht direkt auf dem Prozessor oder Mikrocontroller: Präprozessor, Compiler, Optimierer, Assembler, Linker und Konverter erzeugen zum Beispiel aus C-Quellcode den tatsächlich ausgeführten Objektcode. In jedem Schritt werden Informationen hinzugefügt und entfernt. Diese Modifikationen betreffen direkt die Code-Coverage-Analyse auf Quellcode-Ebene, weil
- der Compiler oder Bibliotheksfunktionen Objektcode hinzufügen
- es schwierig festzustellen ist, wann Objektcode durch den Compiler oder eine Bibliotheksfunktion eingefügt wurde
- der hinzugefügte Objektcode nicht 100% im Testszenario abgedeckt wird
- es sehr schwierig herauszufinden ist, welcher Objektcode zu welcher Quellcode-Zeile gehört (üblicherweise werden Code-Coverage-Ergebnisse im Quellcode angezeigt).
Die Beispiele im ausführlichen Artikel (siehe Link) zeigen, wie die Verwendung von komplexen Instruktionen (etwa „for“-Schleife), komplexen Bedingungen („if“-Statement) oder Bibliotheks- oder Betriebssystemfunktionen Objektcode hinzufügen, der nicht direkt rückverfolgbar ist zu Anweisungen im Quellcode.
Beispiel: Compiler-Optimierungen
Um die Ausführung zu beschleunigen oder Speicher zu sparen, können Compiler:
- mehrere Quellcode-Zeilen zusammenfassen (etwa einzelne String-Konstanten, Vereinfachung von Ausdrücken, …)
- Code-Zeilen für unnötig erachten (toten Code löschen, nicht benutzte Symbole entfernen, identische Teilausdrücke streichen, …)
- die Bearbeitungsreihenfolge ändern (Schleifenumkehr, Schleifenzusammenfassung, …).
Bei jeder Code-Coverage-Messung sollte man daher überprüfen, welche Quellcode-Zeile Objektcode erzeugt, und wenn nicht, überprüfen warum. Jede Codeänderung mit anschließender Kompilierung kann die Compileroptimierung verändern und kann zu ungetesteten Codepfaden führen.
Nicht blind vertrauen
Code-Coverage-Analysen werden immer häufiger gefordert und das in beinahe jedem Markt. Code-Coverage, die auf Objektcode basiert, ist eine komfortable Methode, so nahe wie möglich am Endprodukt zu testen. Um einen möglichst hohen Code-Coverage-Grad zu erreichen, sollte man den Objektcode überprüfen und Testfälle erzeugen, um den (noch nicht) getesteten Code abzudecken.
iSystem-Werkzeuge stellen die Code-Coverage-Analyse im gleichen Tool zur Verfügung, das auch für die Entwicklung von Code verwendet wird. Auf diesem Weg werden Entwicklungskosten und -Zeit optimal genutzt, um Softwarefehler so früh wie möglich im Entwicklungsprozess zu finden und zu beheben.
(lei)
Weblinks
- Code Coverage: Relation between Source and Object Code
- Offline versus Realtime Execution Coverage
- Hayhurst, K. J., Veerhusen D. S., Chilenski J. J., Rierson, L. K.: NASA / TM-2001-210876 A Practical Tutuorial on Modified Condition/Decsion Coverage, NASA Center for Aerospace Information (CASI)
- Difference between Source and Object code Coverage