it-swarm-eu.dev

Best Practice beim Unit-Test für Embedded-Entwicklung

Ich suche nach Best-Practice-Strategien für Unit-Test-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. Die Automatisierung wäre großartig, aber schwierig und teuer zu erreichen.

Update

Ich bin auf ein C-Test-Framework gestoßen, das beim Testen eingebetteter Projekte recht erfolgreich zu sein scheint. Es verwendet die Ideen, Hardware zu verspotten. Check out nity , CMock und möglicherweise Ceedling .

Update 06Jul2016

Kam über cmocka - scheint aktiver gearbeitet zu werden.

46
tehnyit

Ich würde mich zum frühestmöglichen Zeitpunkt von den Hardware-Abhängigkeiten lösen und das System auf Software-Emulations-/Test-Harnesses 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 Overhead (eine weitere Abstraktionsebene) konnten durch den saubereren Code, der als Ergebnis dieser Abstraktion generiert wurde, leicht zurückgewonnen werden.

Das Testen der wirklich baremetallischen Teile eines eingebetteten Systems ist normalerweise eine separate Anwendung (Unit-Test?), Die die Firmware weit über das hinaus hämmert, was die Anwendungen überhaupt erhoffen können. Automatisierung kann durchgeführt werden - zu einem Preis, ist aber nicht typisch.

Es sei denn, Sie haben das Budget, um einen Hardware-Kabelbaum für Unit-Tests einschließlich vollständigem ICE zu bauen. Dies ist absolut in Ordnung, da die Funktionstests im Allgemeinen klein sind.

28
mattnz

Ein notwendiges Werkzeug zur Entwicklung ist ein Signalinjektor. Das eingebettete System kann auf irgendeine Weise mit einem Host-System verbunden werden (normalerweise über eine serielle Schnittstelle, die für das Debuggen reserviert ist). Verwenden Sie diese Option, um Testdaten zu senden (die beste Option ist das knappe ASCII-Format, damit es auch von Menschen leicht simuliert werden kann).

Ich stimme diesem Teil Ihrer Frage überhaupt nicht zu: "Automatisierung wäre großartig, aber schwer und teuer zu erreichen."

Wenn Sie TeraTerm als Signalinjektor für die serielle Schnittstelle verwenden und einige TeraTerm-Makros schreiben (dauert etwa 20 Minuten), gibt es eine große Reihe automatisierter Tests, die für jeden Teil eines eingebetteten Systems ausgeführt werden können - ob Treiberschicht, O/S, Schicht 4-5 usw. TeraTerm: http://en.sourceforge.jp/projects/ttssh2/

Wenn auf dem eingebetteten System keine serielle Schnittstelle verfügbar ist, verwenden Sie ein Hardware-Tool, um USB-/serielle Portdaten in digitale Signale umzuwandeln (auch kostengünstig und einfach ) leisten). Während Sie dies lesen, verwende ich eine 30-Dollar-Mikrocontroller-Karte (UBW: http://www.schmalzhaus.com/UBW32/ ), um ein eingebettetes System für die Produktion zu testen, indem ich Stimulus über TeraTerm-Makros injiziere wird über USB/Seriell an den Mikrocontroller gesendet, auf dem eine modifizierte Firmware ausgeführt wird, die digitale Eingänge ausübt und digitale Ausgänge des eingebetteten Zielsystems überwacht. In Verbindung damit haben wir ein python Skript (verwendet Pyserial und Pexpect) entwickelt, um die Dateninjektion und Datenvalidierung zu automatisieren. Nichts davon ist schwer und keines 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 enthält das Allzweck-Big-Iron-Gerät häufig keine Testfälle die das Worst-Case-Timing/etc des Zielsystems erfassen. Daher ist die kostengünstige Methode für die Testabdeckung vorzuziehen. Ob Sie es glauben oder nicht.

15

Dies ist ein sehr schwieriges Problem.

Ich habe tatsächlich ein Unit-Testing-Kabel für ein eingebettetes System entworfen, mit dem Hardware-Ereignisse/Interrupts simuliert und der Zeitpunkt der Ausführung gesteuert werden kann (um sicherzustellen, dass wir alle möglichen Verschachtelungen aufgrund der Parallelität abdecken), und es hat ein Team von benötigt Programmierer mehr als 2 Jahre, um es tatsächlich umzusetzen und in Betrieb zu nehmen. Dieses Projekt ist eine proprietäre Entwicklung, aber ein ähnliches (einfacheres) Projekt ist verfügbar hier .

Also ja, Automatisierung wäre großartig. Ja, es ist sehr schwer und teuer zu erreichen. Ja, manchmal muss man das machen. Nach meiner Erfahrung ist es jedoch in den meisten Fällen schneller und billiger, die Schrittmotoren und Glühbirnen zu verwenden und alles manuell arbeiten zu lassen.

5
littleadv

Bearbeiten: meine Antwort ist nah an Mattnz, denke ich ...


Ich möchte dieses Problem mit anderen in Verbindung bringen, allen Tests, die von etwas außerhalb Ihres Codes abhängen (wie der Systemuhr, einem persistenten Dateisystem oder einer Datenbank, die Kontaktaufnahme mit einem externen Webdienst ...). Ich schlage für alle die gleiche Richtlinie vor: Isolieren Sie 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 Uhrzeit anzeigt, ob sich eine Datei tatsächlich an das Geschriebene erinnert, ob ein Gerät eine einzelne Operation empfängt ...

Diese Tests:

  • sollte so einfach wie möglich sein: kein Algorithmus, keine Bedingung oder Schleife
  • kann auftragsabhängig und maschinenabhängig sein: Sie müssen daher eine strenge Reihenfolge einhalten und auf jeder Hardware wiederholen
  • sind im Verlauf Ihres Projekts größtenteils stabil, sodass Sie sie nicht so oft ausführen müssen
  • daher ist es eine Option, sie manuell auszuführen. Automatisierung ist noch besser, wenn nicht zu komplex
  • Beachten Sie, dass das, was getestet wird, nicht Ihr Code ist , sondern ein Tool, das Ihr Code benötigt. Das Testen ist also für Sie möglicherweise optional könnte von einem anderen Team gemacht worden sein ...

Testen der Logik (Code, Algorithmus), die die externen Operationen miteinander verbindet

Indem Sie über eine Codeebene verfügen, um die eigentlichen externen Vorgänge auszuführen, und diese über 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 wie bei jedem normalen Projekt testen , dass Sie sich nicht mehr in einem eingebetteten, schwer zu testenden Code befinden .

4
KLE

Wie bei nicht eingebettetem TDD sind Scheinobjekte definitiv dein Freund.

Halten Sie die Schnittstelle zu Ihrer zugrunde liegenden Hardware sauber und einfach, damit alles über der untersten Ebene verspottet werden kann und Sie es viel einfacher haben. Wenn Sie Ihre eingebettete Anwendung unter Berücksichtigung der Testbarkeit entwerfen, läuft das Testen immer viel reibungsloser ab .

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 (anfangs) nur die Bits testen müssen, die nicht offline getestet werden konnten. Sicher, 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 welche Online-Tests Sie durchführen müssen.

Wenn die Online-Entwicklung viel mehr kostet als die Offline-Entwicklung (wie dort, wo ich arbeite), können Sie viel Zeit online sparen, wenn Sie eine gut verstandene Reihe von Tests durchführen müssen.

3
Mark Booth

Eingebettete 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 auf dem E/A-Bus zu haben, und dann auf Lese- und Schreibvorgänge an diesen Adressen reagieren, 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 im Allgemeinen nur, wenn Sie einen benutzerdefinierten Chip mit einem Mikrocontroller und einigen anderen Kernen für Ihre E/A entwerfen.

Das von ARM Holdings) verkaufte 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 einzelne Unterroutine ausführen, die selbst andere Unterroutinen aufrufen kann, die Sie zum Debuggen verwenden können, um die Leistung von Unterroutinen zu optimieren, die nicht vom Hardwarezugriff abhängen. I. nutzte einen davon mit großem Erfolg, 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-Kernelcode testet. Es sollte möglich sein, aber ich habe es nie versucht. Eine Möglichkeit besteht darin, einen vollständigen Kernel zu erstellen, anstatt Ihren Code isoliert zu testen, wobei das Unit-Test-Framework direkt mit Ihrem Kernel verknüpft ist. Sie würden dann die Komponententests über eine externe Schnittstelle auslösen.

Vielleicht wäre es produktiver, ein Tool zur Codeabdeckung zu verwenden und dann Ihre Firmware als Komplettpaket ü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 erzielen.

3
Mike Crawford

Bei der Embedded-Entwicklung führen Sie häufig Boundary Scans durch, um zu überprüfen, ob die gesamte Anwendung (einschließlich 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 Bezug auf die Leistung auf eingebetteten Geräten wie kleinen PICs oder AVRs normalerweise 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 Chip- oder Schaltungssimluatoren zu verwenden, aber diese Art von Werkzeugen ist teuer (insbesondere in Kombination) und kompliziert.

1
Falcon