Ich suche nach Best-Practice-Strategien für Unit-Testing-Code, der für eingebettete Systeme geschrieben wurde. Mit eingebettetem System meine ich Code wie Gerätetreiber, ISR-Handler usw., Dinge, die dem Metall ziemlich nahe kommen.
Die meisten Unit-Tests sind nicht möglich, ohne sie mit Hilfe eines ICE auf der Hardware zu testen. Manchmal muss die eingebettete Einheit auch an andere Reize wie mechanische Schalter, Schrittmotoren und Glühbirnen angeschlossen werden. Dies geschieht normalerweise manuell, eine Automatisierung wäre zwar großartig, aber schwer und teuer zu erreichen.
Aktualisieren
Ich bin auf ein C-Testframework gestoßen, das beim Testen von eingebetteten Projekten ziemlich erfolgreich zu sein scheint. Es nutzt die Idee, Hardware zu verspotten. Schauen Sie sich Unity , CMock und möglicherweise Ceedling an .
Update 06Jul2016
Kam über cmocka - scheint aktiver bearbeitet zu werden.
quelle
Antworten:
Ich würde mich zum frühestmöglichen Zeitpunkt von den Hardware-Abhängigkeiten lösen und das System auf Software-Emulation / Test-Harness aufbauen, um alle Arten von Test-Frameworks zu ermöglichen. Oft wurde mein Entwicklungs-PC verwendet, um 95% oder mehr des gesamten Systems zu testen. Die Kosten für den zusätzlichen Aufwand (eine weitere Abstraktionsebene) wurden durch den saubereren Code, der als Ergebnis dieser Abstraktion generiert wurde, leicht zurückgewonnen.
Das Testen der wirklich baremetallischen Teile eines eingebetteten Systems ist normalerweise eine separate Anwendung (Unit Test?), Die die Firmware weit über das hinaus bringt, was die Anwendungen sich erhoffen können. Automatisierung ist möglich - kostenpflichtig, aber nicht typisch.
Es sei denn, Sie haben das Budget, einen Hardware-Kabelbaum für den Komponententest einschließlich ICE zu bauen. Dies ist absolut in Ordnung, da die Funktionstests im Allgemeinen klein sind.
quelle
Ein notwendiges Werkzeug zur Entwicklung ist ein Signalinjektor. Das eingebettete System kann auf irgendeine Weise mit einem Host-System verbunden werden (normalerweise über einen seriellen Port, der für das Debuggen reserviert ist). Verwenden Sie diese Option, um Testdaten zu senden (die beste Option ist im knappen ASCII-Format, damit sie auch von Menschen problemlos simuliert werden können).
Ich stimme diesem Teil Ihrer Frage überhaupt nicht zu: "Automatisierung wäre großartig, aber schwer und teuer zu erreichen."
Wenn TeraTerm als serieller Port-Signalinjektor verwendet wird und einige TeraTerm-Makros geschrieben werden (dauert ca. 20 Minuten), gibt es eine riesige Reihe automatisierter Tests, die für alle Teile eines eingebetteten Systems ausgeführt werden können - ob Treiberschicht, Betriebssystem, Schicht 4-5 usw. TeraTerm: http://en.sourceforge.jp/projects/ttssh2/
Wenn auf dem eingebetteten System keine serielle Schnittstelle verfügbar ist, konvertieren Sie die Daten der USB- / seriellen Schnittstelle mit einem Hardware-Tool in digitale Signale (auch kostengünstig und einfach zu erreichen). Während Sie dies lesen, benutze ich eine 30-Dollar-Mikrocontroller-Platine (UBW: http://www.schmalzhaus.com/UBW32/ ), um ein eingebettetes System für die Produktion zu testen, indem über TeraTerm-Makros, die über USB / seriell an gesendet werden, Stimulus injiziert wird der Mikrocontroller, auf dem eine modifizierte Firmware ausgeführt wird, die die digitalen Eingänge ausübt und die digitalen Ausgänge des eingebetteten Zielsystems überwacht. In Verbindung damit haben wir ein Python-Skript entwickelt (verwendet pyserial und pexpect), um die Dateninjektion und Datenvalidierung zu automatisieren. Nichts davon ist schwer und nichts davon ist teuer. Nach meiner Erfahrung geben die Manager viel Geld aus (z. B. 30.000 US-Dollar für Testgeräte), wenn das Testteam unerfahren ist und sich diese einfachen Lösungen nicht vorstellen kann - leider enthalten die allgemeinen Big-Iron-Geräte häufig keine Testfälle die den Worst-Case-Timing / etc des Zielsystems abfangen. Daher ist die kostengünstige Methode für die Testabdeckung vorzuziehen. Glaub es oder nicht.
quelle
Das ist ein sehr schwieriges Problem.
Ich habe ein Unit-Testing-Kabel für ein Embedded-System entwickelt, mit dem Hardware-Ereignisse / Interrupts simuliert und der Zeitpunkt der Ausführung gesteuert werden kann (um sicherzustellen, dass alle möglichen Interleavings aufgrund der Parallelität abgedeckt sind) Programmierer mehr als 2 Jahre, um es tatsächlich zu implementieren und zum Laufen zu bringen. Dieses Projekt ist eine Eigenentwicklung, aber ein ähnliches Projekt (einfacher im Design) ist hier verfügbar .
Also ja, Automatisierung wäre toll. Ja, es ist sehr schwer und teuer zu erreichen. Ja, manchmal muss man das machen. Selten jedoch ist es meiner Erfahrung nach in den meisten Fällen schneller und billiger, die Schrittmotoren und Glühbirnen zu verwenden und alles manuell arbeiten zu lassen.
quelle
Edit: Meine Antwort ist in der Nähe von Mattnz, denke ich ...
Ich möchte dieses Problem mit anderen in Verbindung bringen, mit allen Tests, die von etwas außerhalb Ihres Codes abhängen (wie der Systemuhr, einem dauerhaften Dateisystem oder einer Datenbank, die Kontaktaufnahme mit einem externen Webdienst ...). Ich schlage die gleiche Richtlinie für alle vor, isoliere die beiden Ebenen in zwei Codeebenen.
Testen einer einzelnen externen Operation
Möglicherweise möchten Sie jede Operation physisch testen. Überprüfen Sie, ob die Systemuhr die richtige Zeit angibt, ob sich eine Datei tatsächlich an das erinnert, was geschrieben wurde, ob ein Gerät eine einzelne Operation empfängt ...
Diese Tests:
Testen der Logik (Code, Algorithmus), die die externen Operationen verbindet
Indem Sie eine Codeschicht für die eigentlichen externen Vorgänge haben und diese hinter einer Schnittstelle verbergen, die Sie leicht verspotten können, ist Ihre Logik nicht mehr von den tatsächlichen physischen Geräten abhängig ...
Sie können einfach testen, wie bei jedem normalen Projekt, Sie sind nicht mehr in einem schwer zu testenden Code eingebettet .
quelle
Embedded-CPU-Simulatoren können im Allgemeinen so programmiert werden, dass sie auch Hardware simulieren. Alle anderen Virtualisierungstechnologien als Xen tun dies. Sie müssen jedoch Code schreiben, der vorgibt, einige Register an einer physischen Adresse oder auf x86 eine Adresse am E / A-Bus zu haben, und dann auf Lese- und Schreibvorgänge an diesen Adressen antworten, als wäre Ihre Software eine physische Chip, auf dessen Steuer- und Statusregister zugegriffen wurde.
Wenn Sie dies tun möchten, würde ich vorschlagen, QEMU zu ändern. Aber es wäre nicht einfach. Dies geschieht in der Regel nur, wenn Sie einen benutzerdefinierten Chip mit einem Mikrocontroller und einigen anderen Kernen für Ihre E / A entwerfen.
Das von ARM Holdings vertriebene Entwicklungssystem sieht dies vor und ist wahrscheinlich einfacher zu handhaben als das Hacken auf QEMU, aber sehr teuer.
Es gibt mehrere Open Source ARM-Emulatoren, die eine einzige Unterroutine ausführen, die selbst andere Unterroutinen aufrufen kann, die Sie zum Debuggen und Optimieren der Leistung von Unterroutinen verwenden können, die nicht vom Hardwarezugriff abhängig sind. Ich habe eine davon mit großem Erfolg genutzt, um einen AES-Verschlüsseler für ARM7TDMI zu optimieren.
Sie können ein einfaches Unit-Test-Harness in C oder C ++ schreiben, die zu testende Klasse oder Subroutine damit verknüpfen und dann im Simulator ausführen.
Ich habe jahrelang über ein ähnliches Problem nachgedacht, wie man Linux- oder Mac OS X-Kernel-Code testet. Es sollte möglich sein, aber ich habe es nie versucht. Eine Möglichkeit besteht möglicherweise darin, einen vollständigen Kernel zu erstellen, anstatt den Code isoliert zu testen, wobei das Unit-Test-Framework direkt in den Kernel eingebunden ist. Sie würden dann die Unit-Tests von einer externen Schnittstelle aus starten.
Vielleicht wäre es produktiver, ein Tool zur Codeabdeckung zu verwenden und dann Ihre Firmware als vollständiges Paket über die externe Schnittstelle zu testen. Das Coverage-Tool findet Codepfade, die noch nicht getestet wurden, sodass Sie zusätzliche externe Tests hinzufügen können, um mehr Coverage zu erhalten.
quelle
Wie bei nicht eingebettetem TDD sind Scheinobjekte definitiv Ihr Freund.
Halten Sie die Schnittstelle zu Ihrer zugrunde liegenden Hardware sauber und einfach, damit alles, was über der niedrigsten Ebene liegt, verfälscht wird. Wenn Sie Ihre eingebettete Anwendung mit Blick auf die Testbarkeit entwerfen, werden die Tests immer reibungsloser verlaufen .
Nur weil Sie möglicherweise erst spät im Projekt online testen können, bedeutet dies nicht, dass Sie auch keine Reihe von Online-Tests vorbereiten sollten.
Diese sollten (zunächst) nur die Bits testen müssen, die offline nicht getestet werden konnten. Klar, es ist kein TDD (da Sie die Tests im Voraus erstellen), aber Ihre Offline-TDD-Entwicklung sollte Ihnen eine gute Vorstellung davon geben, wie Ihre Hardwareschnittstelle aussehen muss und somit welche Onlinetests Sie durchführen müssen.
Wenn die Online-Entwicklung viel mehr kostet als die Offline-Entwicklung (wie bei meiner Arbeit), können Sie dadurch viel Zeit sparen, wenn Sie online über eine Reihe von Tests verfügen, die gut verstanden werden.
quelle
Bei der Embedded-Entwicklung führen Sie häufig Boundary-Scans durch , um zu überprüfen, ob die gesamte Anwendung (einschließlich der Hardware) funktioniert. Siehe auch JTAG für das System-Debugging.
Das Testen von reinen Softwareroutinen ohne Verbindung zur Hardware kann mit einem Standard-C-Unit-Test-Framework wie Check durchgeführt werden . Achten Sie jedoch auf Speicherbeschränkungen (insbesondere Stapelspeicher usw. auf kleinen Geräten). Kennen Sie Ihre Verträge! Sie können auch versuchen, die Softwareroutinen von der Hardware zu abstrahieren, um eine größere Testabdeckung zu erzielen. Dies ist jedoch in der Regel in Bezug auf die Leistung auf eingebetteten Geräten wie kleinen PICs oder AVRs kostspielig. Sie können jedoch Hardware-Ports verspotten, um eine größere Abdeckung zu erzielen (und natürlich können Sie diese Verspottung auch testen).
Sie können auch versuchen, Emulatoren für die Chip- oder Schaltkreissimulatoren zu verwenden, diese Tools sind jedoch teuer (insbesondere in Kombination) und kompliziert.
quelle