Logo Parasoft Rechercher

Découvrez GoogleTest certifié TÜV avec Agentic AI pour les tests C/C++ !
Plus de détails »

Blog Parasoft

Comment obtenir une couverture à 100% du code structurel des systèmes critiques pour la sécurité

By Ricardo Camacho • 7 mars • 7 min de lecture
7 mars | 7 min de lecture
By Ricardo Camacho
Image à droite montrant une loupe survolant un code binaire avec du texte à gauche : Comment obtenir une couverture à 100 % du code structurel des systèmes critiques pour la sécurité

L'obtention d'une couverture de code structurel des systèmes est l'une des choses cruciales que tout développeur de logiciel et ingénieur qualité doit savoir. Lisez la suite pour comprendre la couverture du code structurel et pourquoi il est essentiel pour les tests logiciels.

De nombreux ingénieurs de développement et de vérification de logiciels ne comprennent pas vraiment pourquoi il est important d'obtenir une couverture structurelle. Beaucoup le font simplement parce que cela est exigé par la norme fonctionnelle de leur industrie et ne le prennent pas au sérieux.

Les systèmes critiques pour la sécurité, comme les systèmes avancés d'aide à la conduite (ADAS), permettent le transport de passagers sans conducteur, le pilotage automatique des avions et le maintien en vie des patients grâce à des dispositifs médicaux. Des vies humaines dépendent de ces systèmes. Il est donc essentiel d' obtenir une conformité aux normes de construction . Examinons ce qu'est une conformité aux normes de construction et d'autres raisons qui expliquent son importance.

Qu'est-ce que la couverture structurelle?

En résumé, la couverture structurelle consiste à identifier le code exécuté et consigné afin de déterminer si le système a été testé de manière adéquate. Le niveau de couverture des systèmes critiques pour la sécurité dépend du niveau d'intégrité de sécurité (SIL), du niveau ASIL utilisé dans l'industrie automobile et du niveau d'assurance de développement (DAL) couramment utilisé en avionique.

Par minutie, je fais référence aux éléments structurels du code. Dans les systèmes embarqués, ils sont généralement décomposés en instruction de code, branche, décisions de condition modifiées et vous pouvez également explorer un niveau de granularité beaucoup plus fin, tel que le code objet ou le langage d'assemblage.

Vous pourriez entendre ou lire d'autres types de métriques de couverture comme la fonction, l'appel, la boucle, la condition, le saut, la décision, etc. Mais pour les systèmes embarqués critiques pour la sécurité, tout ce que vous devez savoir actuellement est le code de déclaration, de branche, de MC / DC et d'objet. Les autres types mentionnés sont un sous-ensemble et donc adressés.

Il y a actuellement un mouvement vers l'adoption de la couverture de conditions multiples (MCC), qui est plus approfondie que MC / DC. MCC nécessite un nombre beaucoup plus grand de cas de test - 2 à la puissance du nombre de conditions.

La formule: 2C

Étant donné que le MCC n'est ni officiellement recommandé ni imposé par les normes de processus industrielles ISO 26262 , DO-178 , IEC 62304, IEC 61508 ou EN 50128 , je ne le traiterai pas dans cet article de blog.

Couverture du relevé et de la succursale

La couverture des instructions est une opération simple qui consiste à couvrir chaque ligne de code d'un programme. Cependant, la complexité des instructions peut varier. Par exemple, une instruction de branchement représente une condition « si… alors… sinon… ». Les instructions comme `case` ou `switch` sont interprétées comme des branchements. Néanmoins, pour obtenir une couverture complète des branchements, il est nécessaire de couvrir l'exécution des deux chemins de décision (vrai et faux).

Organigramme avec un cercle bleu à la condition supérieure true sur la gauche pointe vers A, la condition false sur la droite pointe vers B. Une ligne relie A à B qui pointe vers un autre cercle bleu foncé encastré dans un cercle bleu plus grand.

Organigramme de la succursale

Lorsque des niveaux de sécurité plus élevés sont préoccupants, une couverture de décision conditionnelle modifiée (MC / DC) peut être requise. Les branches peuvent croître en complexité, où il y a plusieurs conditions dans une décision et chaque condition doit être testée indépendamment.

Pour les critères de couverture, cela signifie qu'il a été démontré que chaque condition de la décision affecte indépendamment le résultat de cette décision. Une table de vérité peut être utilisée pour aider à faire cette analyse comme indiqué ci-dessous.

De plus, chaque condition dans une décision du programme a pris tous les résultats possibles au moins une fois, et chaque décision du programme a pris tous les résultats possibles au moins une fois.

Dans l'exemple ci-dessous avec 4 instructions de condition, il y a 16 cas de test possibles. MC / DC n'en requiert que 5 pour cet exemple. Prenez le nombre de conditions et ajoutez 1.

La formule: (C + 1)

Tableau avec en-têtes de colonne Cas de test n degrés, C1, C2, C3, C4 et décision / résultat en haut. Sous l'en-tête du scénario de test à n degrés se trouvent les lignes 1 à 16. T ou F remplit le reste des cellules du tableau.

Table de vérité de décision MC/DC pour l'énoncé « si (((a == 0) || (b < 5)) && ((c > 7) || (d == 0))) »

Couverture du code objet

Pour les applications critiques les plus exigeantes en matière de sécurité, comme en avionique, la norme de processus DO-178B /C niveau A impose une couverture du code objet. En effet, le compilateur ou l'éditeur de liens génère du code supplémentaire qui n'est pas directement traçable aux instructions du code source. Par conséquent, une couverture au niveau assembleur est indispensable.

Imaginez la rigueur et le coût de la main-d'œuvre d'avoir à effectuer cette tâche. Heureusement, il existe Parasoft ASMTools, une solution automatisée permettant d'obtenir une couverture de code objet.

Capture d'écran de la couverture du code du langage d'assemblage Parasoft ASMTools avec une liste de tests et un pourcentage de couverture à gauche. Sur la droite se trouve le code. Certains surlignés en vert, certains en rose, une ligne en jaune.

Couverture du code de langue d'assemblage Parasoft ASMTools

Obtention de la couverture du code

La couverture du code est le plus souvent identifiée grâce à l'instrumentation du code. Instrumenté fait référence au fait que le code utilisateur soit orné d'un code supplémentaire pour vérifier pendant l'exécution si cette instruction, branche ou MC / CD a été exécutée.

En fonction de la cible ou du périphérique intégré, les données de couverture peuvent être stockées dans le système de fichiers, écrites dans la mémoire ou envoyées via divers canaux de communication, tels que le port série, le port TCP / IP, USB et même JTAG.

Instrumentation partielle

Sachez que l'instrumentation du code provoque un gonflement du code et que cette augmentation de la taille du code peut avoir un impact sur la capacité à charger le code sur votre matériel cible à mémoire limitée pour les tests.

La solution de contournement consiste à instrumenter une partie du code.

  1. Exécutez vos tests et capturez la couverture.
  2. Instrumentez l'autre partie du code.
  3. Exécutez à nouveau vos tests.
  4. Capturez la couverture.
  5. Fusionner la couverture de la précédente exécution du test.

En fonction de vos contraintes cibles, nous espérons que vous n'aurez pas trop de partitions instrumentées à traverser. Le fait de devoir réexécuter les mêmes tests encore et encore peut être très long et coûteux. Pour le mentionner rapidement, il peut également y avoir des effets de timing et de performance médiocres que l'instrumentation peut provoquer.

Obtention de la couverture de code pour les systèmes critiques de sécurité et de sûreté intégrés

Voyons comment les organisations obtiennent une couverture de code pour leurs systèmes critiques de sûreté et de sécurité intégrés.

Pour les exigences de couverture de code, telles qu'une structure obligatoire à 100%, une succursale et une couverture MC / DC, ou un 80% facultatif et personnellement souhaité, plusieurs méthodes de test sont utilisées pour atteindre vos objectifs. Les méthodes les plus courantes:

  • Test du système
  • Tests unitaires
  • Test manuel

La combinaison des mesures de couverture de ces diverses pratiques est typique. Mais comment exactement la couverture de code est-elle identifiée?

Couverture des tests du système

L'obtention d'une couverture de code par le biais de tests système est une excellente méthode pour déterminer si suffisamment de tests ont été effectués. L'approche consiste à exécuter tous vos tests système, puis à examiner quelles parties du code n'ont pas été exercées.

Le code non exécuté implique qu'il peut être nécessaire de nouveaux cas de test pour exercer le code intact où un défaut peut se cacher, et aide à répondre à la question, ai-je fait suffisamment de tests?

Lors des tests système, j'ai effectué des analyses de couverture de code , obtenant en moyenne 60 % de couverture. Les 40 % de code non exécuté sont principalement dus au code de défense de votre application.

Ce que j'entends par défensif, c'est du code qui ne s'exécutera que lorsque le système entrera dans un état défectueux ou problématique qui peut être difficile à produire. Des conditions telles que la fuite de mémoire, la corruption ou tout autre type de défaut causé par une défaillance matérielle peuvent prendre des semaines, des mois ou des années à se rencontrer.

Il existe également un code défensif exigé par vos directives de codage que les tests système ne peuvent jamais exécuter. Pour ces raisons, les tests du système ne peuvent pas vous amener à une couverture de code structurel à 100%. Vous devrez utiliser d'autres méthodes de test telles que les tests manuels et / ou unitaires pour atteindre 100%.

Sachez que les normes de processus permettent la fusion des mesures de couverture obtenues à partir de diverses méthodes de test.

Couverture des tests unitaires

Comme mentionné précédemment, les tests unitaires peuvent être utilisés en complément des tests système pour atteindre une couverture de code à 100 %. L'obtention d'une couverture de code par le biais de tests unitaires est une méthode courante, mais elle ne permet pas de déterminer si le système a été suffisamment testé, car elle se concentre sur le niveau unitaire (fonction/procédure).

L'objectif ici est de créer un ensemble de cas de test unitaires qui exercent l'unité entière au besoin de conformité de couverture requis (déclaration, succursale et MC / DC) afin d'atteindre une couverture de 100% pour cette seule unité. Ceci est répété pour chaque unité jusqu'à ce que la base de code entière soit couverte. Cependant, pour tirer le meilleur parti des tests unitaires, ne vous concentrez pas uniquement sur l'obtention d'une couverture de code. Cela peut généralement être accompli par le biais de cas de test de scénario ensoleillé.

Testez rigoureusement l'unité dans des conditions de test optimales, par beau temps comme par mauvais temps, afin de garantir sa robustesse, sa sécurité et la traçabilité des exigences de bas niveau . Considérez la couverture de code comme un sous-produit de vos cas de test et complétez-la uniquement lorsque cela s'avère nécessaire.

Pour accélérer la couverture de code grâce aux tests unitaires, Parasoft C/C++test propose des fonctionnalités configurables et automatisées de génération de cas de test . Ces cas de test peuvent être générés automatiquement pour vérifier l'utilisation de pointeurs nuls, les intervalles min-mid-max, les valeurs limites, et bien plus encore. Cette automatisation vous permettra d'obtenir rapidement une couverture de code significative. En quelques minutes, vous atteindrez un niveau de couverture de code important.

Cependant, comme pour les tests système, obtenir une couverture de code à 100 % est difficile en raison de l'utilisation de code défensif ou de la sémantique formelle des langages. Au niveau granulaire d'une unité, le code défensif peut se présenter sous la forme d'une instruction `default` dans une instruction `switch` . Si tous les cas possibles d'une instruction ` switch` sont couverts, l' instruction `default` devient inaccessible. Dans l'exemple ci-dessous, l'instruction `return 0; ` ne sera jamais exécutée car la boucle ` while (1)` est infinie.

Capture d'écran d'un retour inaccessible 0; déclaration

Retourne 0 ; instruction

Alors, comment obtenir une couverture à 100% pour ces cas particuliers?

Réponse: Les méthodes manuelles doivent être déployées.

L'utilisateur peut indiquer que l'instruction est couverte à l'aide d'un débogueur, modifier la pile d'appels et exécuter l' instruction `return 0; `. Il doit observer visuellement l'exécution et, au minimum, documenter le nom du fichier, la ligne de code et l'instruction désormais considérée comme couverte.

Cette couverture, réalisée par inspection manuelle/visuelle et par rapports, peut compléter celle obtenue par tests unitaires. La combinaison de ces deux rapports permet de prouver une couverture du code structurel à 100 %.

Couverture complète du code: couverture globale des pratiques de test
Télécharger Whitepaper

Si la couverture des tests du système a eu lieu et doit être incluse, les trois rapports de couverture (système, unité, manuel) peuvent être utilisés pour montrer et prouver une couverture et une conformité à 100%.

Capture d'écran du tableau de bord Parasoft Report Center montrant les rapports de couverture des applications sous forme de tableaux, de graphiques et de cercles de pourcentage

Exemple de rapport de couverture de code du tableau de bord Parasoft DTP

Résumé

La couverture du code structurel peut aider à répondre à la question: Ai-je effectué suffisamment de tests?

Il peut également s'agir d'une exigence de conformité que vous devez respecter. L'objectif d'obtenir une couverture de code est un moyen supplémentaire de garantir la sûreté, la sécurité et la fiabilité du code. Il montre la preuve que les tests ont été effectués. Et grâce à ces tests, des défauts ont été identifiés.

Un défaut commun que la couverture de code peut facilement identifier, qui n'a pas été mentionné dans les sections précédentes, est la découverte de code mort. Le code mort est un code qui n'est pas appelé ou invoqué de quelque manière que ce soit. C'est du code qui a probablement été laissé pour compte en raison d'un changement d'exigences ou accidentellement oublié.

La couverture peut également être obtenue grâce à diverses méthodes de test (système, unité, intégration, manuel, API). Le cumul de la couverture de ces méthodes peut être combiné pour prouver une couverture de code à 100%.

Il existe également différents niveaux de couverture (instruction, branche, MC / DC et code objet) que vous devrez peut-être exécuter là où les critères sont basés sur votre niveau SIL, ASIL ou DAL. Heureusement, Parasoft propose des solutions de test de logiciels automatisés et les méthodes que vous devez aborder pour obtenir une couverture de code structurel à 100%.

Découvrez comment vous pouvez tirer parti des techniques d'analyse de code pour votre projet intégré. Voir la vidéo.

Obtenez des commentaires critiques sur l'exhaustivité et la rigueur de votre processus de test avec le test Parasoft C / C +.
Demander une démo personnalisée