Je recherche des stratégies de meilleures pratiques pour le code de test unitaire écrit pour le système embarqué. Par système embarqué, je veux dire du code tel que les pilotes de périphériques, les gestionnaires ISR, etc., des choses assez proches du métal.
La plupart des tests unitaires ne sont pas possibles sans les tester sur le matériel à l'aide d'un ICE. Parfois, l'unité intégrée doit également être connectée à d'autres stimulus tels que des interrupteurs mécaniques, des moteurs pas à pas et des ampoules. Cela se produit généralement de manière manuelle, l'automatisation serait excellente mais difficile et coûteuse à réaliser.
Mise à jour
Je suis tombé sur un framework de test C qui semble être assez efficace pour tester des projets intégrés. Il utilise les idées du matériel moqueur. Découvrez nity , CMock , et éventuellement Ceedling .
Mise à jour 06juil2016
Entré cmocka - semble être travaillé plus activement.
Je m'abstiendrais des dépendances matérielles le plus tôt possible et construirais le système sur des faisceaux d'émulation/test de logiciels, permettant toutes sortes de cadres de test. Souvent, mon PC de développement a été utilisé pour tester jusqu'à 95% ou plus du système complet. Le coût de la surcharge supplémentaire (une autre couche d'abstraction) a été facilement récupéré par le code plus propre généré à la suite de cette abstraction.
Le test des parties véritablement baremetal d'un système embarqué est généralement une application séparée (test unitaire?) Qui martèle le firmware bien au-delà de ce que les applications peuvent même espérer atteindre. L'automatisation peut être effectuée - à un coût, mais n'est pas typique.
À moins que vous n'ayez le budget nécessaire pour construire un faisceau de matériel de test unitaire comprenant l'ICE complet. C'est tout à fait correct car généralement les tests fonctionnels sont petits.
Un outil nécessaire à développer est un injecteur de signaux. Le système embarqué aura une certaine manière de s'interfacer avec un système hôte (généralement via un port série réservé au débogage). Utilisez-le pour envoyer des données de test (la meilleure option est le format ascii abrégé afin qu'il soit facilement simulé par les humains aussi).
Je suis totalement en désaccord avec cette partie de votre question: "l'automatisation serait formidable mais difficile et coûteuse à réaliser."
En utilisant TeraTerm comme injecteur de signal de port série et en écrivant certaines macros TeraTerm (prend environ 20 minutes), il existe une énorme suite de tests automatisés qui peuvent être exécutés sur n'importe quelle partie d'un système embarqué - que ce soit la couche pilote, O/S, couche 4-5, etc. TeraTerm: http://en.sourceforge.jp/projects/ttssh2/
Si le port série n'est pas disponible sur le système intégré, utilisez un outil matériel pour convertir les données du port USB/série en signaux numériques (également peu coûteux et faciles vers atteindre). En lisant ceci, j'utilise une carte microcontrôleur de 30 $ (UBW: http://www.schmalzhaus.com/UBW32/ ) pour tester un système embarqué pour la production, en injectant du stimulus via des macros TeraTerm qui est envoyé via USB/série au microcontrôleur, qui exécute un micrologiciel modifié qui exerce les entrées numériques et surveille les sorties numériques du système intégré cible. Parallèlement à cela, nous avons développé un script python (utilise pyserial et pexpect) pour automatiser l'injection et la validation des données. Rien de tout cela n'est dur et rien de tout cela n'est cher . D'après mon expérience, les gestionnaires dépensent beaucoup d'argent (comme 30 000 $ d'équipement de test) lorsque l'équipe de test est inexpérimentée et ne peut pas concevoir ces des solutions faciles - malheureusement, l'équipement de fer à repasser à usage général n'inclut souvent pas les cas de test qui captent le pire moment/etc. du système cible. Donc, la méthode peu coûteuse est préférable pour couverture de test. Croyez-le ou non.
C'est un problème très difficile.
J'ai en fait conçu un faisceau de tests unitaires pour un système embarqué, qui permettrait de simuler des événements/interruptions matérielles, et de contrôler le timing de l'exécution (pour nous assurer de couvrir tous les entrelacements possibles dus à la concurrence), et il a fallu une équipe de programmeurs plus de 2 ans pour le mettre en œuvre et le mettre en œuvre. Ce projet est un développement propriétaire, mais un projet similaire (de conception plus simple) est disponible ici .
Alors oui, l'automatisation serait géniale. Oui, c'est très difficile et coûteux à réaliser. Oui, parfois vous devez le faire. Rarement cependant, dans mon expérience, dans la plupart des cas, il est plus rapide et moins cher d'utiliser les moteurs pas à pas et les ampoules et de le faire fonctionner manuellement.
Edit: ma réponse est proche de celle de mattnz, je pense ...
Je veux relier ce problème à d'autres, tous les tests qui dépendent de quelque chose d'extérieur à votre code (comme l'horloge système, un système de fichiers persistant ou une base de données, contacter un service web externe ...). Je suggère la même politique pour chacun d'eux, isoler les deux niveaux en deux couches de code.
Vous voudrez peut-être tester physiquement chaque opération. Vérifiez que l'horloge système donne l'heure correcte, vérifiez qu'un fichier se souvient réellement de ce qui a été écrit, vérifiez qu'un périphérique reçoit une seule opération ...
Ces tests:
En ayant une couche de code pour effectuer les opérations externes réelles, en les cachant derrière une interface que vous pouvez facilement simuler, votre logique ne dépend plus des périphériques physiques réels ...
Vous pouvez tester simplement, comme tout projet régulier, vous n'êtes plus dans un code intégré difficile à tester.
Comme pour le TDD non intégré, objets fantaisie sont définitivement votre ami.
Gardez l'interface de votre matériel sous-jacent propre et simple afin que tout ce qui se situe au-dessus du niveau le plus bas puisse être simulé et que vous en profitiez beaucoup plus facilement - si vous concevez votre application intégrée en gardant à l'esprit la testabilité, les tests se dérouleront toujours beaucoup plus facilement .
De plus, le fait que vous ne puissiez pas tester en ligne avant la fin du projet ne signifie pas que vous ne devez pas non plus préparer une suite de tests en ligne.
Ceux-ci ne devraient (initialement) que tester les bits qui n'ont pas pu être testés hors ligne. Bien sûr, ce n'est pas TDD (puisque vous créez les tests à l'avance), mais votre développement TDD hors ligne devrait vous donner une bonne idée de l'apparence de votre interface matérielle et donc des tests en ligne que vous devez effectuer.
De plus, si le développement en ligne coûte beaucoup plus cher que le développement hors ligne (comme c'est le cas là où je travaille), cela pourrait vous faire gagner beaucoup de temps en ligne avec un ensemble de tests bien compris à exécuter.
Les simulateurs de CPU intégrés peuvent généralement être programmés pour simuler également le matériel. Toutes les technologies de virtualisation autres que Xen le font. Mais vous devez écrire du code qui prétend avoir des registres à une adresse physique ou, sur x86, une adresse sur le bus d'E/S, puis vous devez répondre aux lectures et écritures à ces adresses comme si votre logiciel était un physique puce dont les registres de contrôle et d'état étaient en cours d'accès.
Si vous voulez le faire, je vous suggère de modifier QEMU. Mais ce ne serait pas facile. Ce genre de chose n'est généralement effectué que lorsque vous concevez une puce personnalisée avec un microcontrôleur et d'autres cœurs pour vos E/S.
Le système de développement vendu par ARM Holdings le prévoit et est probablement plus facile à utiliser que le piratage sur QEMU, mais il est très cher.
Il existe plusieurs émulateurs Open Source ARM qui exécutent un sous-programme unique, qui lui-même peut appeler d'autres sous-programmes, que vous pouvez utiliser pour déboguer le réglage des performances des sous-programmes qui ne dépendent pas de l'accès matériel. I utilisé l'un d'entre eux avec beaucoup de succès pour optimiser un chiffreur AES pour ARM7TDMI.
Vous pouvez écrire un faisceau de tests unitaires simple en C ou C++, lier la classe ou le sous-programme testé à celui-ci, puis l'exécuter dans le simulateur.
Cela fait des années que je réfléchis à un problème similaire, comment tester à l'unité le code du noyau Linux ou Mac OS X. Cela devrait être possible, mais je n'ai jamais essayé. L'une consiste peut-être à construire un noyau complet plutôt que de tester votre code de manière isolée, avec le framework de test unitaire directement lié à votre noyau. Vous déclencheriez ensuite les tests unitaires à partir d'une sorte d'interface externe.
Il serait peut-être plus productif d'utiliser un outil de couverture de code, puis de tester votre firmware en tant que package complet via son interface externe. L'outil de couverture trouverait des chemins de code qui n'ont pas encore été testés, vous pouvez donc ajouter des tests externes supplémentaires dans le but d'obtenir plus de couverture.
Dans le développement intégré, vous effectuez souvent des analyses de limites pour vérifier que l'application entière (y compris le matériel) fonctionne. Voir aussi [~ # ~] jtag [~ # ~] pour le débogage du système.
Le test de routines logicielles pures sans lien avec le matériel peut être effectué par un framework de test unitaire C standard comme Check . Mais méfiez-vous des limitations de mémoire (en particulier stackspace etc. sur les petits appareils). Connaissez vos contrats! Vous pouvez également essayer d'abstraire les routines logicielles du matériel pour obtenir une plus grande couverture de test, mais cela est généralement coûteux en termes de performances sur les appareils intégrés tels que les petits PIC ou AVR. Cependant, vous pouvez vous moquer des ports matériels pour obtenir une plus grande couverture (et bien sûr, vous pouvez également tester cette maquette).
Vous pouvez également essayer d'utiliser des émulateurs pour les simulateurs de puce ou de circuit, mais ce type d'outils est coûteux (en particulier en combinaison) et compliqué.