Rejoignez-nous le 30 avril : dévoilement de Parasoft C/C++test CT pour l'excellence en matière de tests continus et de conformité | En savoir plus

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

Portrait de Ricardo Camacho, directeur de la conformité de la sûreté et de la sécurité
7 mars 2023
7 min lire

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 ADAS peuvent transporter des passagers sans chauffeur, permettre aux pilotes automatiques de faire voler des personnes à travers nos cieux et maintenir les patients en vie avec des dispositifs médicaux. La vie des gens dépend de ces systèmes. Il est vital de obtenir la couverture du code structurel. Examinons ce qu'est la couverture structurelle et d'autres raisons pour lesquelles elle est importante.

Qu'est-ce que la couverture structurelle?

En un mot, la couverture structurelle est l'identification du code qui a été exécuté et enregistré dans le but de déterminer si le système a été correctement testé. La rigueur de la couverture des systèmes critiques pour la sécurité dépend du niveau d'intégrité de la sécurité (SIL), de l'ASIL dans l'industrie automobile et du niveau d'assurance de développement (DAL) couramment utilisé dans l'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 pas officiellement recommandé ou mandaté par les normes de processus de l'industrie ISO 26262, DO-178, CEI 62304, CEI 61508 or EN 50128, Je ne le couvrirai pas dans ce billet de blog.

Couverture du relevé et de la succursale

La couverture des relevés est la plus simple et représente chaque ligne de code d'un programme. Cependant, les instructions de code peuvent avoir différents degrés de complexité. Par exemple, une instruction branch représente un si alors autrement condition dans le code. Des déclarations comme boîtier ou interrupteur sont interprétés comme une branche. Néanmoins, si vous souhaitez obtenir une couverture pour les succursales, cela signifie que l'exécution des chemins de décision vrai et faux doit être couverte.

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.
Tableau de vérité des décisions MC / DC pour "Si (((a == 0) || (b <5)) && ((c> 7) || (d == 0)))" Déclaration

Couverture du code objet

Pour les applications critiques pour la sécurité les plus strictes, comme dans l'avionique, la norme de processus DO-178B / C niveau A, impose la couverture du code objet. Cela est dû au fait qu'un compilateur ou un éditeur de liens génère du code supplémentaire qui n'est pas directement traçable aux instructions de code source. Par conséquent, la couverture au niveau de l'assemblage doit être effectuée.

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?

Quand j'ai joué couverture de code pendant les tests du système, la métrique résultante moyenne est une couverture de 60%. Une grande partie du code non exécuté à 40% est due au code défensif 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é, les tests unitaires peuvent être utilisés comme une approche complémentaire aux tests système pour obtenir une couverture à 100 %. L'obtention d'une couverture de code par le biais de tests unitaires est l'une des méthodes les plus utilisées, mais elle n'indique pas si vous avez suffisamment testé le système, car l'accent est mis sur le niveau de l'unité (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é.

Exercez véritablement l'unité à travers des scénarios de jours ensoleillés et pluvieux, en garantissant robustesse, sûreté, sécurité et bas niveau traçabilité des exigences. Laissez la couverture de code être un sous-produit de vos cas de test et remplissez la couverture si nécessaire.

Pour aider à accélérer la couverture du code par le biais de tests unitaires, des capacités de génération de cas de test configurables et automatisées existent dans Parasoft C / C ++test . Les cas de test peuvent être générés automatiquement pour tester l'utilisation de pointeurs nuls, de plages min-mid-max, de valeurs limites et bien plus encore. Cette automatisation peut vous mener loin. En quelques minutes, vous obtiendrez une couverture de code substantielle.

Cependant, comme dans les tests de système, obtenir une couverture de code à 100% est difficile à atteindre en raison de l'utilisation de code défensif ou de la sémantique du langage formel. Au niveau granulaire d'une unité, le code défensif peut prendre la forme d'un défaut déclaration dans un interrupteur. Si tous les cas possibles dans un interrupteur est capturé, cela laisse le défaut déclaration inaccessible. Dans l'exemple ci-dessous, le 0 revenir; ne sera jamais exécuté car le while (1) est infini.

Capture d'écran d'un retour inaccessible 0; déclaration
Inaccessible 0 revenir; Déclaration

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

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

L'utilisateur peut étiqueter ou noter l'instruction comme couverte à l'aide d'un débogueur, modifier la pile d'appels et exécuter le 0 revenir; déclaration. Assistez visuellement à l'exécution et, au minimum, documentez le nom du fichier, la ligne de code et l'instruction de code qui est maintenant considérée comme couverte.

Cette couverture effectuée par le biais d'une inspection manuelle / visuelle et de rapports peut être utilisée pour compléter la couverture capturée par des tests unitaires. L'ajout des deux rapports de couverture peut être utilisé pour prouver la couverture du code structurel à 100%.

Couverture complète du code: couverture globale des pratiques de test

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
PAO Parasoft Exemple de rapport de couverture de code de tableau de bord

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 +.