Erreur, défaut, anomalie 1) On constate une anomalie 2) due à un défaut (on dit aussi une faute (!) )du logiciel 3) défaut du à une erreur du programmeur (ex. : confusion d ’un I avec un 1) Tout défaut ne provoque pas nécessairement une anomalie.
Les tests « Program testing can be used to show the presence of bugs, but never to show their absence! » Edsger Wybe Dijkstra in Notes On Structured Programming, 1970, at the end of section 3, On The Reliability of Mechanisms.
Six classes de défauts (fautes) - Calcul x := x + 2; au lieu de x := y + 2; - Logique if (a>b) then au lieu de if (a < b) then - Entrée/sortie mauvaise description, conversion, formatage - Traitement des données mauvais accès ou manip (variable non définie , mauvaise utilisation d ’un pointeur, débordement d ’un tableau - Interface on appelle p1 au lieu de p2, mauvais passage de paramètres - Définition de données : mauvais type, simple précision au lieu de double
Test vs débogage - quand on teste on ne corrige pas - débogage : correction des défauts
Test réussi ? Un test réussi n ’est pas un test qui n ’a pas trouvé de défauts, mais un test qui a effectivement trouvé un défaut (ou une anomalie) G.J. Myers
50 % ou plus du coût D ’un projet !
K.Gödel avec …
Gödel ! 1931 Un système formel : un ensemble d ’axiomes et règles de déduction On produit des théorèmes (par preuve) Si on prend l ’ensemble des entiers muni des opérations classiques, il existe toujours des énoncés qu ’il est impossible de démontrer. Pire ! Si on rajoute ces énoncés à l ’ensemble des axiomes initiaux, on obtient un système comportant de nouveaux énoncés non démontrables
Idée de Gödel Idée de base : l ’autoréférence, voir Hoffstadter : Gödel, Escher, Bach Cette phrase est fausse. - si elle est vraie, alors ce qu ’elle dit est vraie, i.e. elle est fausse - si elle est fausse, alors elle est vraie
Gödel (suite) Deux niveaux de langage : - celui de la phrase - celui qui parle de la phrase Gödel a construit un énoncé analogue à la première phrase à l ’intérieur d ’un système mathématique. - numérote tous les signes utilisés dans un théorème concernant les entiers (comme un code ASCII des maths !) - à tout théorème, il fait correspondre un entier (son code) - construit un théor. T qui dit qu ’il n ’existe pas de théor.ayant un code C, code qui est la codification de T.
I.e. a pu construire un théorème niant sa propre existence. Ce théorème avait été construit de manière formelle, mais il était impossible de décider sur sa valeur de vérité.
Puis Turing et Church... Il existe des algorithmes qu ’on ne peut écrire Exemple : un algo qui, sans utiliser des fichiers de recopie ou autres artifices, donne comme résultat d ’exécution l ’impression du texte source. a := 1; b := 2; Il faudr ait ajouter writeln (‘ a := 1; ’); writeln (‘ b := 2; ’); Mais comme ces 2 instructions appartiennent aussi au code source, il faut ajouter : writeln(‘ writeln (‘ ‘ a := 1; ’’);); writeln (‘ writeln (‘ ‘ b := 2; ’’););
Limitations Un programme T doit lire le code source de n ’importe quel programme B et nous dire si ce dernier (pour un ens de données d ’E) va s ’arrêter. (* prog B1 *) 10 : goto 10; (*prog B3*) read (a) while (a < 10) do begin b := a + 2; end; (*prog B2*) a := 0; while (a <> 1) do begin a := a + 2; end;
Limitations (suite) T doit dire que : B1 et B2 ont une boucle infinie que B3 boucle infiniment si a prend une valeur < 10. Si T repère une boucle infinie, message d ’erreur et s ’arrête si non, T boucle (* Prog T *) Lire le code source de B et ses entrées Analyser B pour savoir si risque de boucler indéfiniment. Si (B boucle indéfiniment) alors écrire (‘ Le prog B boucle indéfiniment. ’) sinon tant que vrai faire début (* on se fait en bouclage infini *) fin;
Limitations (suite) Si on fournit à T son propre code. Soit T boucle indéfiniment Soit s ’arrête. Si T boucle indéfiniment, T doit s ’arrêter et le signaler. C ’est impossible, puisqu ’on a supposé que T boucle indéfiniment ! Cette hypothèse étant exclue, on en déduit que T s ’arrête. Mais nous avons prévu que dans cette situation, B est mis en bouclage infini… donc que T ne se termine pas. Contradiction
Jeu de test, scénario de test DT = {x = 5, y = 5, b = 58} Jeux de test : ensemble de données de test Scénario de test : séquence d ’actions
Oracle de test Exemple d ’oracle automatique : Pour la calcul de la racine carrée, c ’est : résultat au carré = entrée ?
Critères de test Fiable : si produit uniquement des jeux de tests réussis ou, uniquement des jeux de tests non réussis. Si on sait que le critère est fiable, il suffit de passer un seul jeu de test pour savoir que tous les autres seront réussis ou non. Valide : si produit au moins un jeu de test non réussi dans le cas où le programme n ’est pas correct. .
Analogie : le crible Crible valide : on est sûr qu ’il existe un trou assez petit pour ne pas laisser passer la plus petite bille. Mais le crible ne va pas arrêter toutes les billes ! Car une bille petite pourra passer par un trou plus grand : le jeu de test a réussi: on n ’a pas capturé le défaut Crible fiable : les tailles de tous les trous sont égales. Soit toutes les billes vont passer (on ne détecte aucun défaut, jeux de test réussis) Soit toutes les billes sont arrêtées (tous les tests échouent)
Fiable et valide Fiable et valide Crible dont les trous sont tous assez petits pour arrêter les toutes les billes Et si aucun trou…ce serait le test exhaustif ! Mais on veut un test léger. Définir automatiquement un algo de découverte de critère fiable et valide est indécidable
Critère adéquat On fait une hypothèse sur la taille ou la forme des différentes billes pour construire des trous en mesure de les arrêter.
Types de test 1) Fonctionnels ou boîte noire 2) Structurels ou boîte blanche 1) statiques (on observe le code) 2) dynamiques (on exécute le code) Grises
Stratégie de test - ressources mises en œuvre (équipes, testeurs, outils, etc) - mécanismes du processus de test (gestion des configurations, réunions pendant lesquelles on évaluera le processus de test et ses résultats, etc.)
Catégories d ’anomalies Mineures (ex. impression) Bloquantes Majeures et graves : ex. production d ’information de navigation erronées
Intégration 1) Intégration massive (big bang) 2) au fil de l ’eau 3) par incréments (agrégats) Lanceurs : composants sollicitant le composant à tester Bouchons : composants sollicités par le composant à tester
Intégration : Variantes - descendante - ascendante - guidée par le risque - mixte
Les outils De préparation des tests Générateurs de jeux d ’essais Formateurs de données Analyseurs statiques : produisent le graphe de contrôle Instrumenteurs Gestionnaires de test
Les outils Outils d ’exécution des tests Procédures d ’exécution Jeux de tests Procédures de commande Lanceurs et bouchons Analyseurs, émulateurs, etc.
Les outils Outils de traitement de résultats Comparateurs de résultats Gestionnaires de résultats Oracles de test
Les outils Outils d ’observation et de mesure Analyseurs de performance De couverture de tests
Avantages/inconvénients function sum (x, y: integer) : integer; begin if (x = 600) and (y = 500) then sum := x - y else sum := x + y; end Doit calculer la somme de 2 entiers Approche fonctionnelle Impossible de trouver le défaut Approche structurelle : couverture de toutes les instructions DT = {x = 600, y = 500} , résultat = 100 et non 1100
Test fonctionnel/analyse partitionnelle Choix d ’un représentant de chaque classe Ex 1: Calcul de la fonction f(x) = racine carrée de 1/x avec x réel 3 classes d ’équivalence (2 invalides, 1 valide) 1ere classe : les réels négatifs 2ème classe : x = 0 3ème classe ; les réels positifs (seule classe valide)
Test fonctionnel/analyse partitionnelle Ex. 2 Le prog lit 3 réels (longueur des côtés d ’un triangle). Si ces 3 nombres ne sont pas ceux d ’un triangle, imprime message. Si triangle, isocèle, équilatéral ou scalaire et si son plus grand angle est aigu, droit ou obtus. 10 cas : 1 + 3 * 3 Angle Triangle aigu obtus droit scalaire 6, 5, 3 5, 6, 10 3, 4, 5 isocèle 6, 1, 6 7, 4, 4 Rac 2, 2, rac 2 équilatéral 4, 4, 4 impossible impossible
L’ex. du triangle Cas d ’un non triangle : 1, 2, 8
Test aux limites Ex. Si a : 1..100, on définit 6 DT où a prend les valeurs : 0, 1, 2 99, 100, 101
Test aux limites 1, 1, 2 non triangle 0, 0, 0 un seul point 4, 0, 3 une longueur vide 1, 2, 3.00001 presque triangle 0.0001 0.0001 0.0001 très petit triangle 88888, 88888, 88888 très grand triangle 3.00001, 3, 3 presqu’équilatéral 2.99999, 3, 4 presqu’isocèle 3, 4 5.00001 presque droit 3, 4, 5, 6 4 données 3 1 seule donnée 3, 4, , 5 2 virgules -3, -3, 5 valeurs négatives
Graphes cause-effet « Le caractère dans la colonne 1 doit être un « A » ou un « B ». Dans la colonne 2, il doit y avoir un chiffre. Dans ce cas, nous considérons que le fichier est mis à jour. Si le premier caractère est erroné, le message d ’erreur X12 doit être imprimé. Si dans la deuxième colonne nous n ’avons pas de chiffre, il faut alors que le message X13 soit imprimé. »
Graphes cause-effet Causes : A1 caractère de la première colonne est « A » A2 caractère de la première colonne est « B » A3 caractère de la deuxième colonne est un chiffre Effets : M1 fichier mis à jour M2 message X12 M3 message X13
Graphe cause- effet A1 M2 \/ E Faire la tdd /\ M1 A2 M3 A3
Tests syntaxiques Par exemple, grammaire de commandes On construit l ’arbre de dérivation de la grammaire Puis on construit - les commandes valides, - les commandes invalides en suivant les nœuds et les niveaux dans le graphe
Tests aléatoires - échantillonnage uniforme des domaines de définition
Analyse transactionnelle
Test structurel Chemin c1 = [u3, u1, u4, u5, u6] ou = [b, c, a, c, d, d] a u6 u1 u2 u4 d c2 = [a, b, c, d], c3 = [b, c] c2 couvre c3 b u7 Mais ne couvre ni [b, c, b] ni [a, c] u5 u3 c
Circuits linéairt indépendants c1 = [u3, u1, u4, u5, u6] a c6 = [u2, u3] u6 u1 c4 = [u7, u3, u7, u3, u1] u2 u4 d c5 = [u3, u1, u4, u7] u1 u2 u3 u4 u5 u6 u7 0 1 1 0 0 0 0 1 0 2 0 0 0 2 1 0 1 1 0 0 1 c6 c4 c5 b u7 u5 u3 c
Circuits linéairt- indépendants Circuit 1 = [u1, u4] = (1, 0, 0, 1, 0, 0, 0) Circuit 2 = [u3, u7] = (0, 0, 1, 0, 0, 0, 1) (1, 0, 1, 1, 0, 0, 1) L ’addition donne le circuit circuit 5 qui se définit à l ’aide des circuits circuit 1 et circuit 2 circuit 5 = circuit 1 + circuit 2 ou circuit 2 = circuit 5 - circuit 1 Circuit 7 = [u2, u3, u1] (1, 1, 1, 0, 0, 0, 0)est indépendant de circuit 1 et 2 puisque n ’en constitue pas une combinaison linéaire
Critique N.B. : le fait qu ’un chemin M s ’exprime à l ’aide d ’un chemin B ne signifie pas que M couvre B u1 u2 a b c u3 L ’expression vectorielle ne tient pas compte de l ’ordre de visite des nœuds ! B =[u3, u1, u2] M= [u1, u2, u3] (1, 1, 1) (1, 1, 1)
Nb cyclomatique Il existe un chemin connectant deux nœuds quelconques du graphe Il existe un ensemble de circuits indépendants CI, de sorte que de n ’importe quel circuit du graphe puisse se traduire à l ’aide des CI Base : ensemble des circuits indépendants Nbre max de circuits indépendants pour former la base, dans un graphe fortement connexe = nb d ’arcs - nb de nœuds + 1
Le nombre cyclomatique 5-4+1 = 2 B1 = [u1, u2, u3] c u2 u4 B2 = [u4, u5, u2] b B = [u5, u2, u3, u1, u2, u4] = B1 + b2 d u3 u1 u1 u2 u3 u4 u5 B1 1 1 1 0 0 B2 0 1 0 1 1 B 1 2 1 1 1 a u5
Nbre cyclomatique Pour vérifier l ’indépendance entre chemin : calculer leur vecteur voir si chaque chemin comprend au moins un arc n ’existant pas chez les autres
Graphe de contrôle i:=1 i:= 1; while i <= Max do begin if a[i] = s then pos := i; i := i + 1; end; i<=Max pos := i; a[i] /= s i := i + 1
Revues de code Exemples : - commentaires utiles ? i := i + 1 (* incrémentation du compteur i *) - le code a-t-il des points et utilitaires de traçabilité - le nommage des identificateurs est-il compréhensible ? - code lisible (structuré) ? - grand nombre de littéraux dans le code ? if (choices = 2) then answer := ‘ a ’ ; Le jour où on veut remplacer 2 par 3 il faut parcourir l ’ensemble du code pour détecter les apparitions de 2, puis se demander si il est bien la valeur d ’une variable que l ’on veut modifier : nb de jours ? de mois ?
Les constantes Solution : utiliser une « constante », regrouper les constantes dans un fichier. - taille des routines acceptables ?
Détection de défauts ou configurations douteuses : - boucles utilisant <> (différent) comme condition terminale ou comparaison de 2 réels (erreur de précisions conduisant à mauvais choix) Eviter d ’écrire : while i <> Max do … au lieu de while ( i < Max) do ou encore : while (r1 <> r2) do … au lieu de while (trunc(r1) < trunc(r2)) do ...
Revues de code (suite) - Emploi d ’instruction de la forme : if ( x = F(a) + a ) then … ou if (F or P) then … où F est une fonction booléenne ayant des effets de bord sur le paramètre a (i.e. que le par. est modifié à l ’intérieur de F) Il se peut que la valeur de a que le prog a voulu ajouter à F(a) ne soit pas la même que celle passée à la fonction P va-t-il être effectivement calculé dans le cas où F a retourné vrai (selon la stratégie d ’évaluation de la sémantique) ?
Revues de code (suite) - mauvais emploi d ’indices d ’un tableau - tentative de division par zéro ou défauts dus à des arrondis - mauvais emploi des instructions d ’E/S - mauvaises manipulations des fichiers (fermés après ouverture ?)
Complexité : nb de croisements GOTO L1 GOTO L2 L1:
Profondeur max d ’imbrication If a > 0 then while a < i do begin while j > i do if j = 2 * x then y := j else j := j + 1 ; a := a + 1 ; end; = 4
Exécution symbolique a = A, b = B, x = X a := a * a x := a + b ; a = A * A, b = B, x = X A = A*A, b = B, x = A*A+B a := a * a x := a + b ; if x = 0 then x := 0 else x := 1; A*A+B =0 A*A+B /= 0 a = A*A b = B x = 1 a = A*A b = B x = 0
Exécution symbolique
Exécution symbolique (suite) Nécessité d ’évaluations intermédiaires a = A*A + A*A + 5 + 2 simplifiée en a = 2*A*A + 7 Difficulté : tableaux La valeur de x peut tout aussi bien être 5 (si i <>j) que 3 (si i = j) a[i] := 5; : a[j] := 3; x := a[i];
if x < 0 then a := - x else a := x; b := - a if -2*b < 0 then writeln (‘ Error1 ’) else writeln (‘ Error2 ’)
Interprétation abstraite On attribue à chaque variable des Intervalles (réels ou entiers) if a = b then b := a + 3 else b := a - 3 I(a) = 10.. 20 I(b) = 10..15 Avant exécution : a : 10..15 & b : 10..15 Après exécution 1ere branche : a : 10..15 & b : 13..18 Après exécution 2e branche : a :10..15 & b : 7..17 Par union des 2 I de b, on peut prédire que la valeur finale de b : 7..18
Interprétation abstraite while (i<=n) do begin s := s + i ; i := i + 1; if s> c then n := i end; But du programmeur : calculer la somme s de tous les entiers inférieurs à n Il a imposé une autre condition : cette somme ne doit pas dépasser c Il a cru « forcer » l ’arrêt de la boucle en affectant à n la valeur i
Interprétation abstraite while (i<=n) do begin s := s + i ; i := i + 1; if s> c then n := i end; En supposant I(n) = 0..20 et I(c) = 300..1000, l ’exécution semi-symbolique met en évidence que s n ’appartiendra jamais à 300..1000 Quelles que que soient les valeurs de n et c appartenant à I(n) et I(c) respectivement, l ’instruction d ’affectation n := i est non exécutable Code mort
Interprétation abstraite Agrandissons I(c) pour satisfaire s > c I(c ) = 200..1000 s := 0; i := 1; while (i<=n) do begin s := s + i ; i := i + 1; if s> c then n := i end; n := i est symboliquement exécutée, mais peut provoquer bouclage infini Valeur initiale de i = 1 n : 0..20 1e exécution du while, I(n) : 1..20 2e exécution du while, I(n) : 2..20, etc. Au fur et à mesure où i augmente, l ’intervalle se réduit
Interprétation abstraite while (i<=n) do begin s := s + i ; i := i + 1; if s> c then n := i end; La méthode semi-symbolique permet de détecter le non rétrécissement Lorsque n dépassera c, n prendra la valeur de i I(c) = I(n) et condition du while toujours vraie. Rétrécissement des intervalles des variables intervenant dans la condition d ’itération caractérise la convergence de l ’algo
Analyse du flot des données x est définie et a et b référencées x := a + b ; read(x) x est définie write (x) x est référencée if (x=1) then x := 7 x est référencée, puis définie a [i] := x a est définie, i et x référencées x := x + 1 x est référencée, puis définie dr-chaîne
dr-chaînes Variable dr-chaîne program p (input, output) ; x drrrr y drr z r a rr b d c ddr program p (input, output) ; var x, y, z, a, b, c : integer ; begin read (c); x := 7; y := x + a; b := x+y+a; c := y + 2*x + z; write (x, c) end. z et a : référencées et non définies c définies 2 fois de suite définition de b inutile
dr-chaînes r.. Variable a une valeur indéfinie lors de sa 1e utilisation …dd… 2 définitions consécutives, la 1e est inutile …d dernière définition inutile
Analyse des domaines finis read (x, y) ; if (y >= x) then begin if (y > 5) then d := 1 else c := 2; end else c := 3; if (x + y < 4) then c := c + 10 else c := c + 20; writeln (c ); y >= x y > 5 x + y < 4 D1 y=x D2 y=5 D3 y=4 - x y TTF D1 D2 FF TFF TFT TTF: (x, y) qui satisfont la 1e Condition, non la 2e, non la 3e D3 FT x
Analyse des domaines finis Certaines régions n ’existent pas : FFT la non satisfaction de la 1e condition ne permet pas d ’atteindre la 2e ou TTT car D2 et D3 ne se croisent pas et ne permettent aucune intersection de leur domaine TTF D1 D2 FF TFF TFT D3 FT x
Analyse des domaines finis TTF Points les plus sensibles à toute transformation (rotation ou translation) que les différentes droites (conditions du programme) risquent d ’avoir subie à cause d ’erreurs du programmeur D1 D2 FF TFF TFT D3 FT x
Analyse des domaines finis Si DT (intersection) DT1 = {x=0, y=0} DT2 = {x=0, y= 10} DT3 = {x=10, y=0} DT4 = {x=10, y=10} DT5 ={x=0, y=4} DT6 = {x=4, y=0} DT7 = {x=5, y=5} DT8 = {x=2, y=2} TTF D1 D2 FF TFF TFT D3 FT x
Analyse des domaines finis - il est très difficile de représenter des conditions de plus de 2 variables (ex : x + y < z), des tableaux, variables liées par une fonction non linéaire (x * x <= y) - dans les conditions on trouve non des entrées mais des variables intermédiaires
Analyse dynamique (test structurel) 1 - Techniques de couverture du graphe de contrôle 2 - Techniques des tests mutationnels
Techniques de couverture 1 - Chemins dans un graphe de contrôle 2 - Couvertures basées sur le flot de contrôle 3 - Couvertures basées sur le flot des données
Structures élémentaires séquence while if then else repeat case
Réduction de graphe de F de C
Graphe de contrôle a If x <= 0 then x := -x else x := 1 - x; writeln (x); x<=0 x >0 x:= -x b x:=1-x c d Chemin de contrôle : [a, c, d, e, g] x /= -1 x = -1 f e x:=x+1 Non chemin de contrôle : [b, d, f, g] g writeln(x)
Expression de chemin a x<=0 x >0 Expression de chemin : a (b + c) d (e + f) g x:= -x b x:=1-x c d x /= -1 x = -1 f e x:=x+1 g writeln(x)
Expression de chemin a b c d ab (cb)* d Expression régulière
Sensibilisation de chemin x<=0 x >0 {x = 2} sensibilise le chemin [a, c, d, f, g] {x = 0} sensibilise le chemin [a, b, d, e, g] {x = 1} sensibilise le chemin [a, c, d, e, g] b x:=1-x c d x /= -1 x = -1 f e x:=x+1 g writeln(x)
Chemins exécutables/non exécutables Aucune valeur de x n ’est capable de sensibiliser [a, b, d, f, g] si on passe b, on ne passera jamais f x<=0 x >0 b x:=1-x c d Chemins exécutables : il existe des DT qui les sensibilisent x /= -1 x = -1 f e x:=x+1 Chemins inexécutables : infaisable g writeln(x)
Chemin non exécutable read(choice); 1 read (choice); if choice = 1 then x := x + 1; if choice = 2 then x := x-1; 2 choice=1 [1, 2, 3, 4, 5, 6] non exécutable car choix ne peut avoir en même temps la valeur 1 et la valeur 2 3 x:=x+1; 4 choice=2 Restructuration en: read (choice); if (choice = 1) then x := x +1 else if (choice = 2) then x := x - 1; 5 x:=x-1; 6
Chemin non exécutable 1 read(choice); Restructuration en: if (choice = 1) then x := x +1 else if (choice = 2) then x := x - 1; 2 choice=1 3 X:= x+1; 5 Choice=2 X:=x+1; 4 6
Sensible/révélateur d ’erreur Read (x) if x > 7 then y := 0 else y := x; writeln (1/y); 1 Read (x); 2 x <= 7 x > 7 y := 0; 3 4 4 writeln(1/y); [1, 2, 4, 5 ] est sensible aux erreurs [1, 2, 3, 5] est révélateur d ’erreur car erreur qqs la valeur init de x et y
Flot de ctrl vs flot de données Read (x, y); if (x mod 2 = 0) then y := y + x/2; if (x <0) then begin y := - x; writeln (y) ; end else writeln (y); read(x, y); 1 x pair 2 x impair y:=y+x/2 3 X >= 0 X < 0 4 5 y :=- x; writeln(y); writeln (y);
Flot d ’exécution read(x, y); 1 Couverture des arcs : DT1 = {x=-2, y= 0} DT2 = {x= 1, y = 0} x pair 2 x impair y:=y+x/2 DT1 exécute [1, 2, 3, 4] DT2 [1, 3, 5] 3 x >= 0 X < 0 Si 2 erroné, l ’erreur ne sera pas détectée par les DT1, 2 car la valeur de y est masquée par une nouvelle affectation y := - x 4 5 y :=- x; writeln(y); writeln (y);
read(x, y); 1 Pour tester l ’affectation du nœud 2, il faut exécuter [1, 2, 3, 5] avec la DT3 = [{x=2, y=0} pour avoir x paire et positive x pair 2 x impair y:=y+x/2 3 x >= 0 X < 0 4 5 writeln (y);
Flot d ’exécution/flot de donnée Pour trouver la nécessité de DT3, 2 raisonnements possibles : 1) on suit le flot d ’exécution Voir que flot d ’exécution [1, 2, 3, 5] n ’est pas pris en compte et créer une DT supplémentaire 2) on suit le flot de données Voir que l ’affectation de y au nœud 2 n ’a pas été ‘ utilisée ’ par les deux premières DT car elle a été « couverte » par celle du nœud 4 et créer une DT supplémentaire pour la « rendre utile »
Couverture basée sur le flot de contrôle x=0 x/=0 b c sum:=x+y; Fonction censée calculer la somme de 2 entiers : function sum (x,y: integer) : integer; begin if (x=0) then sum := x else sum := x + y; end; c Si test boîte noire, on ne découvrira pas l ’erreur Elle n ’est découverte que par DT = {x=0, y /=0}
Taux de couverture a function sum (x,y: integer) : integer; x=0 x/=0 begin if (x=0) then sum := x else sum := x + y; end; x=0 x/=0 b c sum:=x+y; sum:=x; c La plupart des DT ne couvrent que [a, b, d]. Ce n ’est qu ’en essayant de passer par c que nous exécutons le chemin sensible à l ’erreur [a, c, d]
Taux de couverture a x=0 x/=0 1e critère : sensibiliser les chemins b de contrôle qui nous permettront de visiter tous les nœuds. Critère « tous-les-nœuds » b c sum:=x+y; c A chaque critère de couverture est associé le taux de couverture ou mesure de complétude (d° de satisfaction du critère) Si on soumet uniquement DT1 = {x=2, y=5}, on exécute [a, b, d] i.e. 3 nœuds sur 4 Test Effectiveness Ratio (TER) : nb de nœuds couverts/ nb total de nœuds = 3/4 = 0, 75 = 75 %
Critère TER a x=0 x/=0 b c sum:=x+y; TER = 1 <=> C1 = 1 <=> tous-les-nœuds est satisfait <=> tous les nœuds du graphe de contrôle ont été couverts <=> toutes les instructions ont été exécutées (au moins une fois) c
couverture de tous les actes 2e critère : couverture de tous les actes read(x); a x/=0 Tous-les-nœuds est insuffisant. X=0 b read(x); : if x < > 0 then x := 1; y:= 1/x; x:=1; c d y:=1/x; DT = {x=2} sensibilise [a, b, c, d], couvre tous les nœuds sans faire apparaître l ’anomalie qui se produira si b n ’est pas exécuté
Critère tous-les-arcs Le pb vient que nous n ’avons pas couvert tous les arcs read(x); Couvrir tous les chemins de contrôle composés uniquement d ’un arc. a x/=0 X=0 b x:=1; c B1 = [a, b] B2 = [b, c] B3 = [a, c] B4 = [c, d] d y:=1/x;
TER2 ou C2 read(x); a Pour la DT {x=2} qui couvre x/=0 les chemins B1, B2 et B4 (3 sur 4) TER2 = nb des arcs couverts / nb total d ’arcs = 3/4= 0,75 = 75% x/=0 X=0 b x:=1; c B1 = [a, b] B2 = [b, c] B3 = [a, c] B4 = [c, d] Alors que la même DT donne TER1 = 4/4 = 1 = 100% d y:=1/x;
TER2 La couverture de tous les arcs équivaut à la couverture de toutes les valeurs de vérité pour chaque nœud de décision. En satisfaisant ce critère, nous couvrons tous les chemins reliant un nœud de décision à un autre (appelé aussi dd-chemins ou branches) TER2 = 1 <=> C2 = 1 <=> tous-les-arcs est satisfait <=> tous les arcs du graphe sont couverts <=> toutes les décisions ont été couvertes (V et F) <=> tous le dd-chemins ont été couverts <=> toutes les branches ont été couvertes <=> tous-les-nœuds est satisfait => C1 = 1
Décision composée If (( a < 2) and (b = a)) then x := 2 - a else x := a - 2 DT1 = {a=b=1} DT2 = {a=3} couvrent tous les arcs mais non les deux conditions composant la décision. 1 (a<=2) or (b/=a) (a<2) and (b=a) 2 3 x:=2-a; x=a-2 4
Mesure C3 1 1 (a<=2) or (b/=a) a>=2 (a<2) and (b=a) a<2 4 x:=2-a; 2 b/=a b=a b=a b/=a 4 x:=2-a 3 Pour couvrir tous les arcs il faut 2 * 2 = 4 DT 5 x:=a-2 DT1 = {a=b=1} DT2 = {a=1, b=0} DT3 = {a=3, b=2} DT4 = {a=b=3} 6
1 a>=2 1 a<2 a<2 a>=2 4 2 b/=a b=a 2 b=a b=a b/=a b/=a x:=2-a 3 5 x:=2-a 3 4 x:=a-2 x:=a-2 6 5
1 a<2 DT1 = {a=b=1} DT2 = {a=1, b=0} DT3 = {a=3, b=2} a>=2 2 b=a b/=a Couvrent tous les arcs x:=2-a 3 4 x:=a-2 5
Couverture de tous les chemins linéairement indépendants read(inf,sup); i:=inf; sum:=0; b read (inf, sup) i := inf; sum := 0; while (i <= sup) do begin sum := sum + a[i]; i := i + 1; end; u1 writeln(1/sum); u4 c e u2 i<=sup u3 sum:= sum + a[i]; i:=i+1; d Évalue l ’inverse de la somme des éléments compris entre la place inf et sup d ’un tableau a comprenant des entiers positifs.
DT1= {a[1]=50, a[2] =60, a[3]=80, inf =1, sup=3} read(inf,sup); i:=inf; sum:=0; b u1 u4 c e u2 i<=sup u3 sum:= sum + a[i]; i:=i+1; d B1=[b,c,d,c,d,c,d,c,e]=[u1,u2,u3,u2,u3,u2,u3,u4] couvre tous les arcs (et nœuds) sans détecter l ’erreur
Erreur qui se manifeste lorsque inf > sup. En effet, si la boucle n ’est pas exécutée, sum reste à 0 et l ’évaluation de son inverse est impossible. b Il faut ajouter le chemin B2 =[u1, u4] sensibilisé par ex. par DT2={inf=2, sup=1} u1 u4 c e u2 i<=sup u3 sum:= sum + a[i]; i:=i+1; d
Nombre de McCabe u1 u2 u3 u4 B1 1 3 3 1 B2 1 0 0 1 B3 1 1 1 1 B3 combinaison linéaire de B1 et B2 B3 = 1/3 (B1 + 2B2) v(G) = 4 - 4 + 2 = 2 v(G) = nb de nœuds de décision + 1= 1 + 1 B1 et B2 étant deux ch. Linéairement indépendants, ils constituent une base. N ’importe quel chemin peut s ’exprimer à l ’aide de B1 et B2
Limites Si le critère tous-les-chemins-indépendants est satisfait, alors les critères tous-les-arcs et tous-les-nœuds le sont aussi. Mais la propriété d ’indépendance n ’est ni nécessaire ni suffisante pour détecter le défauts Si nous avions choisi B3 (qui est aussi indép. De B1) au lieu de B2, on aurait obtenu une base sans détecter l ’erreur.
Couverture « vectorielle » ! 1 Program p (input, output); var a : array[1..2] of integer; E, i : integer; found : boolean; begin read(i, E, a[1], a[2]); found := false; while i <= 2 do if (a[i] = E) then found := true else found := false ; i := i + 1; end; writeln(found); end. u1 i>2 2 7 u2 u3 u8 3 a[i]/=E a[i]=E u4 u5 4 5 u6 u7 6 i:= i+1
1 u1 i>2 DT1 ={i=3} sensibilise B1=[u1, u2] 2 7 u2 u3 u8 Modifions la condition du nœud 2 : DT2 ={i=1, E=10, a[1]=20, a[2]=10} qui sensibilise B2 =[u1, u3, u4, u6, u8, u3, u5, u7, u8, u2] 3 a[i]/=E a[i]=E u4 u5 4 5 u6 u7 6 i:= i+1
1 En modifiant la condition du nœud 3 DT3 = {i=1, E=10, a[1]=10, a[2] = 20] sensibilise : B3 =[u1,u3,u5,u7,u8,u3,u6,u8,u2] u1 i>2 2 7 u2 u3 u8 3 Mais B3 n ’est pas indépendant de B2, car admettent le même vecteur v = [1, 1, 2, 1, 1, 1, 1, 2] a[i]/=E a[i]=E u4 u5 4 5 u6 u7 6 i:= i+1
B2 et B3 ont les mêmes arcs le même nb de fois. On produit 2 autres chemins : B4 et B5 DT4 ={i=1, E=10, a[1]=10, a[2]=10} sensibilisant B4 =[u1, u3, u5, u7, u8, u3, u5, u7, u8,u2] DT5 ={i=1, E=10, a[1]=20, a[2]=30} B5 =[u1, u3, u4, u6, u8, u3, u4, u6, u8, u2] B1, B4 et B5 sont vectoriellement indépendants
Limites Résultats exacts DT1 found = false car boucle non exécutée DT4 found = true DT5 found est affecté deux fois à false Or ne détectent pas l ’erreur (la valeur de found ne dépend en définitive que de la dernière valeur de l ’élément du tableau. Le seul chemin parmi ceux proposés, qui détectait l ’erreur était B3 (celui que l ’on a rejeté à cause de sa dépendance avec B2 !)
Couverture des PLCS Portion Linéaire de Code Suivie de Saut LCSAJ (Linear Code Subpath And Jump) 005 INPUT A, C 010 B= 2*A 020 A=A+1 030 IF A<0 THEN GOTO 60 040 B= -A 050 PRINT A+B 060 IF B=2*C THEN GOTO 80 070 A=1:GOTO 90 080 A=-2:GOTO 20 090 PRINT A 100 END En gras et vert les « sauts » PLCS : chemin partant d ’un nœud saut et aboutissant à un nœud saut. L ’avant dernier et dernier nœud sont le seul saut du chemin
5 PLCS 20 30 1) [5, 20, 30, 60] 2) [5, 20, 30, 40, 60, 80] 3) [5, 20, 30, 40, 60, 70, 90] 4) [20, 30, 60] 5) [20, 30, 40, 60, 80] 6) [20, 30, 40, 60, 70, 90] 7) [60, 80] 8) [60, 70, 90] 9) [80, 20] 10) [90, 100] 40 60 70 80 90 4 incluse dans 1, 5 dans 2, 6 dans 3 7 dans 2, 8 dans 3 100
TER3 Si on exécute les 3 chemins : B1 = [5, 20, 30, 60, 70, 90, 100] on couvre la totalité des PLCS TER3 = nb de PLCS couvertes / nb total de PLCS
Tests des chemins de boucle Limites et intérieurs à une boucle (boundary interior path test) Limites : traversent la boucle mais ne l ’itèrent pas Intérieurs : itèrent la boucle une seule fois. [1,2,3] chemin limite 1 1 [1,2,4,2,3] [1,2,1,2,3] 2 2 sont intérieurs 3 4 3
1 Chemins limites L1=[1,2,3,5,6] L2 =[1,2,4,5,6] 2 v f 4 3 Chemins intérieurs vv=[1,2,3,5,2,3,5,6] vf=[1,2,3,5,2,4,5,6] fv=[1,2,4,5,2,3,5,6] ff=[1,2,4,5,2,4,5,6] 5 6
On peut compléter par : 1) chemins exécutant deux fois la boucle 2) chemins exécutant le nombre max d ’itérations
Couvertures basées sur le flot de données d variable définie r variable référencée (utilisée) p-utilisation dans le prédicat d ’une instruction de décision c-utilisation dans un calcul while (i < N) do i et N sont p-utilisées et à la dernière exécution, i esst c-utilisée et ensuite définie begin s := s + i; s et i sont c-utilisées, puis s est définie i := i + 1; end; writeln (s); s est utilisée
C-utilisation et p-utilisation Chemin d ’utilisation (c-utilisation ou p-utilisation) : chemin reliant l ’instruction de définition d ’une variable à une instruction utilisatrice. [1,2,3,2,4] couvre tous les arcs read(x,y); 1 L ’arc (2, 4) est p-utilisateur de x par rapport au nœud 1 Or le chemin de p-utilisation [1,2,4] qui relie la définition de x au nœud 1 avec son arc p-utilisateur (2,4) n ’est pas inclus dans le chemin de test initial. 2 x >=100 x<100 3 4 x:=x*y; y:=y+1;
P-utilisation et c-utilisation Le critère tous-les-p-utilisateurs nécessite que tous les arcs p-utilisateurs correspondant à toutes les définitions du graphe (affectation, read, etc.) soient couvertes, par un chemin de p-utilisation. Nous ne développerons pas plus
Complémentarité des méthodes If a > b then x := a - b else x := b - a Si le programmeur avait dû écrire y := b - a au lieu de x : b - a, alors une couverture basée sur le flot de données sera sans doute en mesure de détecter l ’erreur Si le programmeur a commis une erreur de branchement (a < b au lieu de a > b) une couverture basée sur les chemins sera plus pertinente.
Test mutationnels Sélection du meilleur mécanicien parmi 10 candidats On présente à chaque candidat (DT) différentes pannes (mutants) que l ’on a volontairement provoquées sur la voiture (programme initial) Le candidat détectant le plus de pannes sera le plus compétent On va tester si la DT est appropriée en comparant les résultats qu ’elle donnera lorsqu ’elle est soumise aux différents mutants
Mutants Principaux : Mutants qui, pour au moins une DT, donnent des résultats différents du programme initial Prog. Principal : a := b + c ; writeln (a) ; Mutants : i) a := b + 1; writeln (a); ii) a := b - c; writeln (a); iii) a := b + c; writeln (b) ;
Mutants (suite) Mutants secondaires Donnent pour toutes les DT les mêmes résultats que ceux du programme initial var a, b, c : interger; begin if a < b then c := a; end i) if a <= b - 1 then c := a; ii) if a + 1 <= b then c := a; iii) if a < b then c := a + 0;
Mutants principaux Plus une DT tue de mutants principaux, plus elle est appropriée Cas idéal : une DT qui tue tous les mutants principaux. Si le programme initial est erroné et que sa bonne version constitue un mutant de celui-ci : 1) soit ces programmes donnent toujours le même résultat (ils sont équivalents) 2) soit donnent des résultats différents. La DT peut les distinguer. Elle est appropriée
Exemple Prog. : ajouter 2 à un nombre lu en entrée Erreur : confusion entre ^ et + et * On choisit l ’entrée de 2. Si le résultat est 5, prog. erroné Si le résultat est 4, sûr d ’avoir bien testé ? Non ! x:= x *2 , x := x ^2
Mutants On produit DT {x=3} pour tuer les deux mutants x := x*2 et x := x2
Tolérance aux erreurs Manip(a, b) permet d ’observer et modifier les valeurs de a et b en cours d ’exécution. read (a, b); while b > 0 do begin a := a + 1; b := b - 1; manip (a, b) ; end; Soit étant initial S0 =<3,5> 1e observation état S1 = <4,4> 2e état S2 = <5,3> 3e état S3 = <6,2> 4e état S4 = <7,1> 5e état S5 = <8,0> S5 = <a + b, 0>
Tolérance aux erreurs read (a, b); while b > 0 do begin a := a + 1; b := b - 1; manip (a, b) ; end; 1e observation état S1 = <4,4> On modifie S2 en S2 ’=<17, -1> le bouclage s ’arrête et l ’état final est : <17, -1> L ’algo n ’a pas toléré la « déformation » On recherche des transformations, qui tout en modifiant un état intermédiaire, peuvent se compenser par le comportement ultérieur et donner le résultat attendu
P-correct, s-correct 1e observation état S1 = <4,4> Modification de S2 S2 ’ = <7,1> S3 = <8, 0> S est faiblement correct (f-correct) à un certain endroit par rapport à un état initial S0, ssi, en reprenant à l ’algo à partir de cet état, on retrouve le résultat attendu. <7,1> est f-correct par rapport à <3,5> mais aussi à <1,7> , <4,4> etc. S2 = <5, 3> issu d ’un fonctionnement normal, est strictement correct
Sp-correct 2e observation modifions S2=<5,3> en <8,-2> On obtient l ’état final <8,-2>, qui n ’est pas le résultat attendu mais on a bien la bonne valeur de a <8,-2> est correct d ’après les spécifications (sp-correct) par rapport à S0 et à l ’endroit de la 2e observation.
Les états intermédiaires 1) modifications donnant le résultat final attendu. Etat intermédiaire f-correct 2) modifications qui ne donnent pas le résultat final attendu, mais celui prévu par les spécifications. Etat intermédiaire sp-correct 3) modifications altérant irrémédiablement l ’état intermédiaire. Etat non-sp correct
F-récupérable Si l ’état S courant n ’est plus f-correct. Si on peut le transformer par une routine de récupération locale, en un état f-correct, l ’état est f-récupérable.
Fin