Hallo Bastler! Da ich nun schon einige Libs für mich entwickelt habe, stellt sich mir mitlerweile die Frage ob man nicht auch die Tests automatesieren könnte. Mi geht es also nicht darum die Hardware zu testen sondern eher per Modultest die Funktionsfähigkeit von einzelnen Funktionen auch nach Änderungen zu verifizieren. Laut Wikipedia gibt es ja z.B. CUnit für die Überprüfung aber ich kann mir nicht vorstellen, wie das mit einem qC funzen soll. Irgendwie muss es ja eine Rückkopplung geben, ob Daten wirklich ausgegeben, Timing eingehalten wird, Leds leuchten,... Darüber hinaus würde es mich auch interessieren, wenn man auf einem qC komplexe Dinge wie TCP/IP, RTOS, ... einsetzt, wie man dann den Stack testen kann um BufferOverflows und damit auftretende Sicherheitsprobleme zu umschiffen.
0undNichtig wrote: > Laut Wikipedia gibt es ja z.B. CUnit für die Überprüfung aber ich kann > mir nicht vorstellen, wie das mit einem qC funzen soll. Irgendwie muss > es ja eine Rückkopplung geben, ob Daten wirklich ausgegeben, Timing > eingehalten wird, Leds leuchten,... LEDs wirst du wohl eher nicht in einer Bibliothek leuchten lassen. Ansonsten kann man solche Dinge mit scriptfähigen Simulatoren testen. Ist aber ein ziemlicher Aufwand.
Naja also nutzt keiner im hobby/semiprofesionellen Bereich automatische Tests? Ich hatte dann noch https://sourceforge.net/projects/embunit/ Embedded Unit gefunden, scheint aber auch nicht so das wahre zu sein :/
0undNichtig wrote: > Naja also nutzt keiner im hobby/semiprofesionellen Bereich automatische > Tests? Das wäre wohl für erstere 1000facher Overkill, denn der Aufwand, automatische Tests zu programmieren, ist erheblich. Selbst im Bereich professioneller Software wird das durchaus nicht immer gemacht - Testdeppen sind billiger...
Tests für einzelne Funktionen wie Parser, komplexe Berechnungen oder Regelschleifen sind sehr sinnvoll um Fehler zu vermeiden und nicht schwierig zu implementieren. Entweder man kompiliert sie direkt auf dem PC, führt sie (falls Assemblercode enthalten ist) mit einem Simulator aus, oder in einem Testprogramm direkt auf dem Controller (der kann dann den Testbericht z.B. über RS232 zum PC senden). Hilfreich ist es dabei auch den eigentlichen Code schon mit assert()ions zu spicken damit Ungereimtheiten sofort auffallen. So kann man auch einen TCP/IP-Stack oder ein RTOS in gewissen Grenzen testen. CUnit oder andere Frameworks braucht man dazu nicht unbedingt, die bringen nur zusätzliche und oft unnötige Komplexität mit ins Spiel. Das Gesamtprogramm inkl. I/O und Timing zu testen ist dagegen sehr aufwendig. Einmal weil man die externe Hardware nachbilden bzw. überwachen muss, und weil man das gewünschte Verhalten überhaupt erst mal exakt spezifizieren muss. Das lohnt sich im semiprofessionellen Bereich sicher nicht.
Es gibt mehrere Ansätze: 1. Testcode direkt auf dem Target 2. Target-Code auf dem PC testen Da ich für 1. zu wenig Programmspeicher hatte, habe ich mich für 2. entschieden. Ich teste mein Projekt mit cunit und Code::Blocks auf dem PC. Und ja, ich kann SW-mässig testen, ob meine LEDs richtig angesteuert werden! Genaugenommen teste ich natürlich nur den Wert in den Port-Registern, aber das genügt vollkommen.
Das mit dem korrekten Timing erachte ich als weniger wichtig. Wenn ich z.B die Implementation meiner I2C-Sende-Routine testen will, prüfe ich, ob die gewünschten Daten in der richtigen Reihenfolge generiert werden. Da die Bytes teilweise erst gesendet werden, wenn der Slave geantwortet hat, wird diese Antwort natürlich (per SW) simuliert. Wie schnell die Geschichte abläuft, interessiert hier nicht. Das Timing der I2C-Schnittstelle wird durch die I2C-Register festgelegt. Diese Einstellungen können wiederum per Unit-Test sichergestellt werden. Schade wäre es, unter dem Vorwand des nicht korrekten Timings, die Unit-Tests von vornherein wegzulassen.
Wow das klingt eigentlich genau nach dem was ich will aber ich denke so ganz 0815 sieht das nicht aus oder? Kannst du das mal ein wenig ausführen evtl. mit Minicodeschnipsel?
Mein Vorgehen: - Projekt in IDE Deiner Wahl erzeugen, alle Dateien des Zielsystems einbinden, ausser der main.c - eigene main.c erstellen, welche den Code aus cunit aufnimmt Jetzt erst mal alle Compiler-Fehler beseitigen. Das Problem mit den Ports und Registern des Zielsystems (HC11) habe ich so gelöst: Da bereits eine Struktur TS_RegsHC11 in der Datei „hc11f_r.h“ existiert, brauchte ich nichts weiter zu tun, als an geeigneter Stelle im Testcode eine globale Instanz zu erzeugen:
1 | TS_RegsHC11 RegsHC11; |
Nun kann man die HW-Register beliebig beschreiben oder lesen, z.B.:
1 | PPROG = PPROG | 0x16; |
2 | printf("\n _PPROG = %d\n", RegsHC11.U_PPROG. _PPROG); |
Dank entsprechender Makros sind auch folgende Varianten möglich:
1 | RegsHC11.U_PORTG._PORTG = InvState[InvStateIdx]; |
2 | PORTG = InvState[InvStateIdx]; |
3 | G_PortG = InvState[InvStateIdx]; |
Wahrscheinlich wirst Du nicht umhinkommen, und gewisse Funktionen mittels bedingter Compileranweisung für den Test zu deaktivieren, bzw. durch eine spezielle Testfunktion zu ersetzen. Beispielsweise wenn Du Rückmeldungen der HW simulieren willst, wie z.B. ein Eingabegerät mit Tasten. Nehmen wir an, du möchtest die Fuktion ZeigeInfo() testen. Das Zielsystem pollt mit er Funktion GetKey() auf einen Tastendruck: main UI HwDriver ¦ ZeigeInfo() ¦ ¦ ¦-------------->¦ GetKey() ¦ ¦ ¦----------->¦ ¦ ¦ Key N ¦ ¦ ¦<-----------¦ ¦ ¦ GetKey() ¦ ¦ ¦----------->¦ ¦ ¦ Key Exit ¦ ¦ ¦<-----------¦ ¦<--------------¦ ¦ Nun erstellst Du im TestCode eine eigene Funktion GetKey() mit der gleichen Signatur wie das Original, welche bei jedem Aufruf, abhängig von einem globalen Test-Status G_TestIdx, den gewünschten Key zurückliefert:
1 | unsigned char GetKey() |
2 | {
|
3 | unsigned char Key=0; |
4 | |
5 | switch (G_TestIdx) |
6 | {
|
7 | case 0: |
8 | case 1: |
9 | case 3: |
10 | case 4: |
11 | case 11: |
12 | // führe hier geeignete Tests durch
|
13 | // CU_ASSERT_xxx
|
14 | Key = K_DOWN; // liefere den simulierten Tastendruck |
15 | G_TestIdx++; // nächster Test-Index |
16 | break; |
17 | case 9: |
18 | // führe hier geeignete Tests durch
|
19 | // CU_ASSERT_xxx
|
20 | Key = K_LEFT; // liefere den simulierten Tastendruck |
21 | G_TestIdx++; // nächster Test-Index |
22 | break; |
23 | case 13: |
24 | // führe hier geeignete Tests durch
|
25 | // CU_ASSERT_xxx
|
26 | Key = K_EXIT; // genug jetzt |
27 | G_TestIdx++; // nächster Test-Index |
28 | break; |
29 | default:
|
30 | // führe hier geeignete Tests durch
|
31 | // CU_ASSERT_xxx
|
32 | G_TestIdx++; // nächster Test-Index |
33 | break; |
34 | }
|
35 | return Key; |
36 | }
|
Die Originalfunktion muss leider mittels bedingter Kompilation stillgelegt werden und kann nicht getestet werden.
1 | #ifndef GCC_CUNIT_TEST_COMPILATION
|
2 | unsigned char GetKey() |
Im Testcode zu ZeigeInfo() brauchst Du die Variable TestIdx richtig zu setzen und rufst ZeigeInfo() auf, welche die obigen Tests durchfürt und durch den simulierten ExitKey wieder zurückkommt.
Danke für die ausführliche Beschreibung, genau so die Richtung dachte ich mir! Wobei mir beim lesen wieder mal aufgefallen sit, dass durch die bedingte Kompilierung ebend der Code für außenstehende nur sehr schlecht lesbar wird :-/
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.