Logo Parasoft
Icône blanche représentant un monde intégré

Nous sommes nominés pour le prix Embedded Award 2026 dans la catégorie Outils et nous serions ravis de recevoir votre soutien ! Votez pour C/C++test CT >>

En savoir plus sur le test Parasoft C/C++.

Rejoignez notre démo de produit mensuelle de 30 minutes.

Inscrivez-vous

WEBINAIRE

Tout ce que vous devez savoir sur la couverture du code structurel pour C et C++

La couverture structurelle est l'identification du code qui a été exécuté et enregistré. Il existe plusieurs raisons pour lesquelles il est important d'effectuer cette activité pour les systèmes embarqués critiques pour la sûreté et la sécurité. La première consiste à déterminer si le logiciel a été testé de manière adéquate. Une autre consiste à satisfaire aux exigences de conformité et de certification. Vous voudrez peut-être également vous assurer qu'il n'y a pas de code mort dans votre logiciel.

Cette présentation plonge en profondeur dans couverture de code structurelle pour le développement C et C++, en mettant l'accent sur son importance pour les systèmes critiques pour la sécurité et en explorant divers critères de mesure tels que la couverture des instructions, des branches et des MC/DC. Nous aborderons également des méthodes pratiques pour obtenir et automatiser la couverture du code.

Nous vous montrerons la couverture structurelle du code pour les instructions, les succursales et les MC/DC, ainsi que des rapports et des mesures automatisés pour la couverture et la complexité du code.

Comprendre la couverture du code

La couverture de code consiste à identifier les parties de votre code qui ont été réellement exécutées lors des tests. C'est un indicateur clé pour déterminer si votre logiciel a été testé de manière suffisamment approfondie. Pour les systèmes embarqués critiques pour la sécurité, c'est essentiel pour la conformité et la certification. De plus, elle vous aide à identifier et à éliminer le code mort, c'est-à-dire le code qui n'a jamais été exécuté.

Fondamentalement, la couverture du code répond à la question : « Ai-je suffisamment testé ? » Elle permet également de détecter les bugs cachés dans les sections non testées. La grande question est : voulez-vous risquer ces zones non testées ? Dans de nombreux secteurs réglementés, vous n'avez peut-être pas le choix : vous devrez peut-être atteindre une couverture de 100 %.

À retenir

  • Objectif : Identifiez le code exécuté, assurez des tests adéquats, satisfaites à la conformité et trouvez le code mort.
  • Mécanisme: Généralement réalisé grâce à l'instrumentation de code, où du code supplémentaire est ajouté pour enregistrer l'exécution.
  • Métrique: Les types courants incluent la déclaration, la branche (décision) et la couverture de condition/décision modifiée (MC/DC).
  • Normes: Les secteurs tels que l’automobile (ISO 26262), l’aérospatiale (DO-178C) et le médical (IEC 62304) ont des exigences de couverture spécifiques basées sur les niveaux d’intégrité de sécurité.
  • Automation: Les outils peuvent automatiser la génération de cas de test et l’analyse de couverture, en s’intégrant dans les IDE et les pipelines CI/CD.
  • Défis: Atteindre une couverture à 100 % peut être difficile en raison d'un code défensif ou d'un code inaccessible, nécessitant souvent une inspection visuelle (débogueur) ou une conception de cas de test spécifique.

Points forts de la démonstration

Au cours de la démonstration, les fonctionnalités clés sont présentées :

  • Intégration de l'EDI : Visualisation des mesures de couverture directement dans les IDE comme Eclipse et VS Code, affichant la couverture des lignes, des instructions, des branches et des MC/DC.
  • Génération de tests automatisés : Génération de tests unitaires qui contribuent aux objectifs de couverture.
  • Couverture de l'application (ligne de commande) : Utilisation d'outils tels que C/C++test pour instrumenter, créer et exécuter des tests sur des applications en dehors de l'IDE, générant des journaux de couverture qui peuvent ensuite être réimportés dans un IDE pour analyse.
  • Test du matériel cible : Prise en charge de la collecte de données de couverture sur des cibles intégrées, avec des options d'optimisation de la taille ou de la vitesse.
  • Tableau de bord de couverture : Rapports et analyses centralisés des données de couverture à l'aide de plateformes telles que DTP.

Comment fonctionne la couverture du code

Le moyen le plus courant de obtenir une couverture de code L'instrumentation du code consiste à ajouter de petits morceaux de code à votre code source d'origine. Ces ajouts permettent de suivre l'exécution d'une instruction, d'une décision ou d'une branche. L'instrumentation enregistre ensuite ces informations, permettant aux outils de calculer un pourcentage de couverture et de visualiser les parties du code concernées.

Votre code peut être surligné en vert (testé) avec quelques parties en rouge (non testé). Certains outils affichent du code partiellement couvert, souvent en jaune. Cela peut se produire avec un if énoncé comportant plusieurs conditions, où tous les résultats possibles de ces conditions n'ont pas été testés.

Types de couverture structurelle

Lorsque nous parlons de couverture structurelle, nous examinons différentes manières de mesurer la rigueur des tests de la structure du code. Bien que les termes puissent parfois être surchargés ou interprétés différemment selon les secteurs, les principaux types pour les applications C et C++ critiques pour la sécurité sont :

  • Couverture du relevé : Garantit que chaque instruction exécutable du code a été exécutée au moins une fois.
  • Couverture des succursales (ou couverture des décisions) : Garantit que chaque branche possible (comme la true et false résultats d'une if (instruction) a été exécutée.
  • Couverture de condition/décision modifiée (MC/DC) : Il s'agit d'une norme plus rigoureuse, souvent requise pour les systèmes à haute assurance. Elle garantit que chaque condition d'une décision a été démontrée indépendamment comme affectant son résultat. Pour une décision comme celle-ci (A && B), MC/DC nécessite des tests qui montrent A changer le résultat tout en B est fixe, et B changer le résultat tout en A est corrigé, en plus de tester toutes les branches.

D'autres types existent, comme la couverture conditionnelle (test de conditions individuelles) et la couverture de ligne (garantie de l'exécution de chaque ligne, qui peut différer de la couverture d'instruction si plusieurs instructions sont sur une même ligne). Cependant, les instructions, les branches et les MC/DC sont les plus fréquemment citées dans les normes de sécurité.

Automatisation de la couverture du code

Déterminer manuellement les cas de test exacts nécessaires pour atteindre des objectifs de couverture spécifiques peut être extrêmement chronophage. Heureusement, les outils peuvent apporter une aide précieuse.

Conseiller en couverture Les fonctionnalités peuvent analyser votre code et suggérer des valeurs de paramètres ou des conditions préalables spécifiques pour vos tests unitaires, afin d'atteindre des lignes ou des branches spécifiques. Cela peut considérablement accélérer la création des cas de test.

Génération automatisée de cas de test unitaire est une autre fonctionnalité puissante. Les outils peuvent générer automatiquement une suite de tests unitaires conçus non seulement pour vérifier les fonctionnalités, mais aussi pour répondre à vos exigences de couverture structurelle. Ces tests générés incluent souvent différents types de vérifications, comme des tests de pointeur nul, des tests de valeurs limites et des tests mid-max, afin de détecter d'éventuels bugs et d'améliorer la couverture.

Combinaison de couvertures provenant de différentes méthodes

Il est rare qu'une seule méthode de test permette d'atteindre une couverture de 100 %, surtout si votre objectif est élevé. Voici comment combiner différentes méthodes :

  • Test du système : L'exécution de vos tests système existants sur du code instrumenté peut couvrir une grande partie de votre application avec un minimum d'effort supplémentaire. Cependant, les tests système omettent souvent les chemins de code défensifs qui ne se déclenchent qu'en cas de défaillance (comme une corruption de mémoire ou une panne matérielle), ce qui entraîne généralement une couverture d'environ 60 %.
  • Test unitaire La création de tests unitaires ciblés est essentielle pour tester ces chemins de code défensifs ou d'autres scénarios spécifiques que les tests système oublient. En concevant des tests unitaires adaptés à des conditions spécifiques, vous pouvez considérablement augmenter votre couverture globale.
  • Test manuel : Même les tests manuels peuvent contribuer aux données de couverture lorsqu'ils sont exécutés sur du code instrumenté.

Les outils modernes permettent de fusionner les résultats de couverture issus de ces différentes méthodes de test. Vous obtenez ainsi une vue consolidée de la couverture totale de votre code, répondant ainsi aux exigences de conformité qui autorisent la combinaison des métriques.

Relever les défis

  • Code inaccessible : Il arrive que le code soit structuré de telle manière qu'il soit impossible d'y accéder par des tests normaux (par exemple, une boucle infinie suivie d'une instruction de retour). Dans ce cas, les normes autorisent souvent la couverture par inspection. Cela signifie que vous pouvez confirmer visuellement que le code est inaccessible ou utiliser des outils de débogage pour le parcourir pas à pas, en documentant pourquoi il est considéré comme couvert.
  • Gonflement du code : L'instrumentation peut parfois augmenter la taille de votre exécutable, l'empêchant ainsi de s'adapter à un matériel cible aux ressources limitées. Une solution courante consiste à instrumenter la moitié du code, à exécuter des tests, à capturer la couverture, puis à désinstrumenter la première moitié, à instrumenter la seconde, à exécuter des tests et à fusionner les résultats de la couverture.
  • Couverture du code d'assemblage : Pour les niveaux de sécurité les plus élevés (comme DO-178C DAL A), une analyse de couverture peut être requise au niveau de l'assembleur ou du code objet. En effet, les compilateurs et les éditeurs de liens peuvent générer du code dont la source n'est pas directement traçable. Des outils spécialisés permettent d'automatiser le processus de collecte de la couverture structurelle à partir du code objet exécutable.

Intégration de la couverture dans CI/CD

Pour un développement moderne, l'intégration de la couverture de code à votre pipeline d'intégration continue/livraison continue (CI/CD) est essentielle. Des outils peuvent instrumenter votre application lors du processus de build, collecter des données de couverture brutes lors de tests automatisés, puis générer des rapports sur ces données dans votre IDE ou un tableau de bord central (comme la plateforme de tests de développement (DTP) de Parasoft).

Cela permet un retour continu sur la qualité et les risques du code, favorisant ainsi une meilleure prise de décision tout au long du cycle de développement. Les intégrations avec des outils CI/CD populaires comme Jenkins, GitLab et Azure DevOps sont courantes.

Conclusion

La couverture structurelle du code est une pratique essentielle pour garantir la qualité, la sécurité et la fiabilité des logiciels, en particulier dans les secteurs réglementés. En comprenant les différents types de couverture, en tirant parti de l'automatisation et en intégrant l'analyse de couverture à votre workflow de développement, vous pouvez répondre efficacement aux exigences de conformité et créer des logiciels plus robustes.