Test et débogage Tests unitaires. Gestion d’erreurs. Notion d’état, de pré-condition et de post-condition. Assertion. Traces de programme. Débogueur et.

Slides:



Advertisements
Présentations similaires
Premier programme en C :
Advertisements

La boucle for : init7.c et init71.c
Rappels C.
La classe String Attention ce n’est pas un type de base. Il s'agit d'une classe défini dans l’API Java (Dans le package java.lang) String s="aaa"; // s.
Synchronisation des processus père - fils
Module Systèmes dexploitation Chapitre 6 Communication Interprocessus Partie III École Normale Supérieure Tétouan Département Informatique
GEF 435 Principes des systèmes dexploitation Communication Interprocessus (CIP) III (Tanenbaum 2.3)
GEF 243B Programmation informatique appliquée
Atelier régional des Nations Unies sur le traitement des données de recensement: les technologies modernes pour la saisie et la correction des données.
C.
Tests et itérations Programmes séquentiels ne résolvent pas tous les problèmes exemple simple : calcul des racines d'un polynôme de d° 2 dans R Algorithme.
8. Les tableaux P. Costamagna – ISEN N1.
Section VI Structures répétitives (suite)
Tests Programmation par contrats
Points importants de la semaine Les types arithmétiques. Les opérateurs.
Langage C Révision.
Cours 7 - Les pointeurs, l'allocation dynamique, les listes chaînées
Récursivité.
Points importants de la semaine Les boucles. Les types arithmétiques. Les opérateurs.
Les méthodes en java Une méthode est un regroupement d’instructions ayant pour but de faire un traitement bien précis. Une méthode pour être utilisée.
Démarche de résolution de problèmes
Algorithmique et Programmation
RESUMES Module II1 SOMMAIRE CYCLE 1 : Saisir – Afficher – Données
8PRO100 Éléments de programmation Allocation dynamique de la mémoire.
Algorithmique et Programmation
Quest-ce quune classe dallocation? Une classe dallocation détermine la portée et la durée de vie dun objet ou dune fonction.
Bases de la programmation en C++ 1 Les enchaînementsdinstruction Séquentiels. Exécutions dinstructions les unes à la suite des autres. Instructions séparées.
Les pointeurs Modes d’adressage de variables. Définition d’un pointeur. Opérateurs de base. Opérations élémentaires. Pointeurs et tableaux. Pointeurs et.
Structures de données IFT Abder Alikacem Gestion des exceptions Département dinformatique et de génie logiciel Édition Septembre 2009.
Présentation Structures de Données et TDA
Points importants de la semaine Le préprocesseur. La conversion de types. Les fonctions.
Instruction sélective switch-case Instruction répétitive do-while For
Chapitre XI Gestion des erreurs et exceptions. 2 La gestion des erreurs et exceptions De nombreux problèmes peuvent survenir pendant lexécution dun programme:
Leçon 1 : notion dobjet IUP Génie Informatique Besançon Méthode et Outils pour la Programmation Françoise Greffier Université de Franche-Comté.
Programme de baccalauréat en informatique Programmation Orientée Objets IFT Thierry EUDE Module 6. Gestion des erreurs et des exceptions : Fonctionnement.
Structures de données IFT-2000 Abder Alikacem La récursivité Semaine 5 Département dinformatique et de génie logiciel Édition Septembre 2009.
Répéter dans un programme avec une Instruction itérative
Procédures et fonctions
Plan cours La notion de pointeur et d’adresse mémoire.
Structures de données IFT-2000 Abder Alikacem La récursivité Département d’informatique et de génie logiciel Édition Septembre 2009.
Introduction à la programmation orientée objets
La librairie assert.h.
Les assertions en Java.
Le langage C Rappel Pointeurs & Allocation de mémoire.
9ième Classe (Mardi, 4 novembre) CSI2572. H Nous avons vu comment utiliser les directives #define #ifndef #endif Pour s’assurer de l’inclusion unique.
GESTION ET TRAITEMENT DES ERREURS
Communication avec l’environnement
Interactions entre Processus
1 Structures des données. 2  Le tableau permettait de désigner sous un seul nom un ensemble de valeurs de même type, chacune d'entre elles étant repérée.
Programmation Système et Réseau
GF-11: Tri Interne Efficace et Tri Externe
ISBN Chapitre 10 L'implémentation des sous- programmes.
Le Préprocesseur. Sujets abordés: Préprocesseur –Qu’est ce? –Qu’est ce qu’une directive? Les macros –Les définir, les redéfinir, les dé-définir –Les macros.
Conception de Programmes - IUT de Paris - 1ère année – Cours 8 – Les entrées/sorties Comment fonctionnent les opérateurs > pour les types élémentaires.
Introduction au langage C : Structures de contrôle 1 ère année Génie Informatique Dr Daouda Traoré Université de Ségou
6ième Classe (Mercredi, 17 novembre) CSI2572
Maintenance.
Simulation de lectures d’algorithmes
CPI/BTS 2 Programmation Web Les sites dynamiques Prog Web CPI/BTS2 – M. Dravet – 02/10/2003 Dernière modification: 02/10/2003.
8PRO107 Éléments de programmation Les adresses et les pointeurs.
1 Cpt JAVA : Eclipse (bis) Debogage. 2 Code à tester public class siecle { int t; public siecle() { super(); t=1; } static public boolean vrai() { return(false);
Scripts et fonctions Instructions de contrôle
QCM VBA.
Algorithmique Tableaux de données
Exception Handling "Unfortunately, it's almost accepted practice to ignore error conditions, as if we're in a state of denial about errors." Bruce Eckel.
Master 1 SIGLIS Jave Lecteur Stéphane Tallard Chapitre 5 – Correction TD.
Algorithmique Boucles et Itérations
FACTORY systemes Module 5 Section 1 Page 5-3 Les scripts de traitement FORMATION INTOUCH 7.0.
Transcription de la présentation:

Test et débogage Tests unitaires. Gestion d’erreurs. Notion d’état, de pré-condition et de post-condition. Assertion. Traces de programme. Débogueur et ses limites. Stratégies de mise au point du programme. Normes de programmation.

La gestion des erreurs Plusieurs problèmes peuvent survenir pendant l’exécution d’un programme: - insuffisance de mémoire, disque plein, - perte d’un fichier, imprimante non branchée ou à court de papier, - saisie non valide d’une valeur, - une fonction qui fonctionne mal, - etc. Rôle des programmeurs face à ces erreurs d’exécution: - prévoir ces erreurs, - une application étant construite à partir d’un enchaînement d’appels de fonctions, toute fonction pouvant être confrontée à une erreur d’exécution, il devient nécessaire de pouvoir gérer les appels (les erreurs) en cascade de fonctions, - en informer l’utilisateur, sauver son travail et arrêter le programme de façon contrôlée, - éventuellement, de mettre en œuvre des solutions de reprises et de correction.

Mise en œuvre de la gestion des erreurs PROBLÈME RENCONTRÉ : le code qui détecte l’erreur ne peut rien faire pour sauver le travail de l’usager et quitter élégamment. Supposer que les erreurs ne se produiront pas est une mauvaise approche. Utiliser une variable globale qui sera initialisée par la fonction provoquant l’erreur d’exécution. Le contenu de cette variable sera ensuite récupéré et traité par la fonction appelante de celle qui a entraîné l’erreur d’exécution. Les variables globales n’offrent aucune garantie de sécurité. Cela signifie que l’on peut y accéder de n’importe où sans restriction. **** à éviter **** Ignorer l’erreur Programmation défensive Peut masquer des erreurs importantes. Prendre la décision de ne rien faire en cas de demande erronée. Ex. : Ne pas insérer un nouvel élément dans une pile pleine. Dans certains cas, on ne peut ignorer l’erreur parce que l’opération ne peut être complétée. Ex. : lors d’un calcul.

Mise en œuvre de la gestion des erreurs Imprimer un message d’erreur et/ou arrêter le programme Le code de traitement des erreurs est dispersé et imbriqué tout au long du code du système. Les erreurs sont gérées là où ces erreurs sont le plus susceptibles de se produire. Avantage: En lisant le code, on peut voir le traitement d’erreur dans le voisinage immédiat du code et déterminer si une vérification adéquate des erreurs a été mise en place. Désavantage: Le code est « pollué » par le traitement d’erreurs. Cela rend le code plus difficile à comprendre et à maintenir. Envisageable au cours de la phase de mise au point seulement. **** à éviter ****

Mise en œuvre de la gestion des erreurs Syntaxe : assert(expression logique); Donne lieu à aucune action si l’expression logique est vraie. Autrement, le programme termine anormalement en affichant un diagnostic. Note : Cette clause fait partie de la librairie C : assert.h #include <stdio.h> #include <assert.h> int sommation(int n) { assert(n > 0); return n * (n + 1) / 2; } void main() int N; printf("Entrez un nombre entier positif : "); scanf("%i", &N); printf("\nSommation : %i", sommation(N)); -5 Assertion failed: n > 0, file e:\nouveau_dossier\chiffres_romains.c, line 6

Mise en œuvre de la gestion des erreurs Abandonner Recommencer Ignorer Le programme prend fin Le programme prend fin avec un message qu’il termine anormalement. Envoyer le rapport d’erreurs Débogage Ne pas envoyer Mettre fin à l’exécution est souvent une méthode trop radicale pour répondre à une erreur. De nombreuses erreurs peuvent être gérées si elles sont signalées de façon à ce qu’un programmeur puisse les détecter et les analyser.

Mise en œuvre de la gestion des erreurs En C++, on utilise la librairie cassert. #include <iostream.h> #include <cassert> int sommation(int n) { assert(n > 0); return n * (n + 1) / 2; } void main() int N; cout << "Entrez un nombre entier positif : "; cin >> N; cout << "\nSommation : " << sommation(N); cout << "\nFIN\n";

Mise en œuvre de la gestion des erreurs Approche classique d’interception d’erreurs: retour de code d’erreur - des fonctions symbolisent les traitements à mettre en œuvre; - ces fonctions renvoient des valeurs qui peuvent servir à déterminer le succès ou l’échec du traitement; - on peut tester les valeurs de retour des fonctions et réagir en conséquence aux erreurs d’exécution.

Utilisation des valeurs de retour #include <iostream.h> int Fonction_1(………….) { ……... // Retourne 0 si aucune erreur n’est détectée; // un entier positif autrement. …….. } ………... int Fonction_m(………….)

Utilisation des valeurs de retour int main() { ………. // Appel de la fonction Fonction_i. // Si une erreur est détectée, alors gérer cette erreur en lien avec la fonction Fonction_i sinon appel de la fonction Fonction_j si une erreur est détectée, alors gérer cette erreur en lien avec Fonction_j sinon ……….. } Cela met en évidence l’architecture nécessaire à des appels de fonctions en cascade qui peuvent entraîner une erreur d’exécution. Architecture peu lisible et surtout très pénible à maintenir (if-else imbriqué qui alourdit l’écriture et la mise à jour du programme).

Utilisation des valeurs de retour #include <iostream.h> enum Nom_des_fonctions {Fn_A, Fn_B}; int Fonction_A(float v) { if (v < 0.0) return 1; if (v == 0.0) return 2; // Traitement. return 0; } int Fonction_B(char c) { if (c == ' ') return 1; if ((c < 'A') | (c > 'Z')) return 3; // Traitement. return 0; }

Utilisation des valeurs de retour int Gestion_des_erreurs(Nom_des_fonctions Nom, int code_erreur) { if (Nom== Fn_A) switch(code_erreur) case 0 : cout << "Parfait"; return 0; case 1 : cout<<"Erreur 1"; return 1; case 2 : cout << "Erreur 2"; return 0; default: cout << "Erreur"; return 0; } else if (Nom== Fn_B) case 3 : cout <<"Erreur 3"; return 1; // Erreur grave else cout << "incomplet "; return 0;

Utilisation des valeurs de retour int main() { char C = ' '; float U = -3.2f; if (Gestion_des_erreurs(Fn_A, Fonction_A(U))) return 1; if (Gestion_des_erreurs(Fn_B, Fonction_B(C))) return 1; return 0; }

Mécanisme des exceptions Solution fiable et standardisée de gestion d’erreurs d’exécution qui ne se fonde pas sur les valeurs de retour des fonctions. permet de capturer toutes les exceptions d’un type donné, permet de lever des exceptions et transférer le contrôle avec les informations pertinentes à une autre partie du code qui pourra gérer la situation correcte- ment, en retirant le code de traitement des erreurs du « flot principal » d’exécution d’un programme, cela permet d’améliorer la lisibilité des programmes et faciliter leur maintenance. permet de gérer nativement les appels de fonctions en cascade.

Test unitaire d’une fonction Au lieu d’assembler chaque composante d’un programme en espérant que cela fonctionne, il est préférable de les tester individuellement. Le test unitaire (ou test de la boîte noire) est un programme avec un résultat booléen qui ne se préoccupe pas du fonctionnement interne de la fonction. Voici une fonction qui calcule la racine carrée d’un nombre entier positif ou nul avec une précision EPSILON : Exemple : #include <iostream.h> const float EPSILON = 0.00001f; // Calcule la racine carrée de la valeur réelle a. // // Paramètre d 'entrée : // a : une valeur réelle // Valeur de renvoi : // la racine carrée de a.

Test unitaire d’une fonction float RacineCarree(float a) { if (a < 0.0f) throw(1); // Lancement d’une exception. if (a == 0.0f) return 0.0f; float xnouveau = a; float xvieux; do xvieux = xnouveau; xnouveau = (xvieux + a / xvieux) / 2.0f; } while ( (xnouveau - xvieux) >= EPSILON || (xnouveau - xvieux) <= - EPSILON ); return xnouveau;

Test unitaire d’une fonction void main() { float x, y, z; // Test positif où RacineCarree doit fonctionner correctement. x = 2.0f; y = RacineCarree(x); z = y * y; if ((z - x) <= EPSILON && (z - x) >= - EPSILON) cout << "succes\n"; else cout << "echec\n"; // Test frontière où RacineCarree doit fonctionner correctement. x = 0.0f; if ((z - x) <= EPSILON && (z - x) >= -EPSILON) cout << "succes\n"; x = EPSILON;

Test unitaire d’une fonction // Test négatif où la fonction RacineCarree doit échouer. try { x = -1.0f; y = RacineCarree(x); cout << "echec\n"; } catch(int i) // Prise en compte d’une exception. cout << i << " : succes\n";

Test unitaire d’une fonction #include <iostream.h> #include <string.h> /* Le président d'un syndicat local possède l'information suivante sur ses membres (dont le nombre ne dépassera jamais 100) : - le numéro d'employé (lequel est une identification unique de l'employé correspondant à un nombre de 4 chiffres dont le 1er est 6), - son nom, - son prénom, - son adresse, - son traitement annuel - sa cotisation syndicale annuelle (entre 0 et 2%). */ struct employe { int numero_employe; char nom[20+1], prenom[20+1], adresse[40+1]; float traitement_annuel; float cotisation_annuelle; } ensemble_membres[100]; int Nombre_de_membres; /* Renferme le nombre de syndiqués.*/

Test unitaire d’une fonction void syndicat() // Permet de créer un syndicat avec aucun membre. { Nombre_de_membres = 0; } int Nombre_de_membres_du_syndicat() /* Permet de connaître le nombre de membres faisant partie du syndicat. Valeur de renvoi : le nombre de membres du syndicat. */ return Nombre_de_membres;

Test unitaire d’une fonction float Calcule_cotisation_annuelle_moyenne() /* Fournit la moyenne des cotisations annuelles des membres. Le syndicat possède au moins un membre. Valeur de renvoi : la moyenne des cotisations annuelles. */ { float somme = 0.0f; if (Nombre_de_membres == 0) throw(3); for (int i = 0; i < Nombre_de_membres; i++) somme += ensemble_membres[i].cotisation_annuelle; return somme / Nombre_de_membres; }

Test unitaire d’une fonction void Inserer_nouveau_membre( int numero_employe, char * nom, char * prenom, char * adresse, float traitement_annuel, float cotisation_annuelle) /* Permet d'ajouter un nouveau membre syndiqué dont les caractéristiques sont passées en paramètres. Le nombre d'employés est moindre que 100. L'employé ayant ce numéro n'est pas syndiqué jusqu'à maintenant. Paramètres d'entrée : numero_employe : le no. de l'employé entre 6000 et 6999, nom : le nom de l'employé, prenom : le prénom de l'employé, adresse : son adresse, traitement_annuel : le traitement annuel de l'employé, cotisation_annuelle : la cotisation annuelle entre 0 et 2. */

Test unitaire d’une fonction { if (Nombre_de_membres == 100) throw(4); for (int i = 0; i < Nombre_de_membres; i++) if (ensemble_membres[i].numero_employe == numero_employe) throw(4); if ((numero_employe < 6000) | (numero_employe > 6999)) throw(4); if((cotisation_annuelle < 0) | (cotisation_annuelle > 2)) throw(4); ensemble_membres[Nombre_de_membres].numero_employe = numero_employe; strcpy(ensemble_membres[Nombre_de_membres].nom, nom); strcpy(ensemble_membres[Nombre_de_membres].prenom, prenom); strcpy(ensemble_membres[Nombre_de_membres].adresse, adresse); ensemble_membres[Nombre_de_membres].traitement_annuel = traitement_annuel; ensemble_membres[Nombre_de_membres].cotisation_annuelle = cotisation_annuelle; Nombre_de_membres += 1; }

Test unitaire d’une fonction void main() { // La fonction "syndicat". // Test positif. syndicat(); if (Nombre_de_membres_du_syndicat() == 0) cout << "Reussite.\n"; else cout << "echec\n"; // La fonction "Nombre_de_membres_du_syndicat". Inserer_nouveau_membre(6234, "Rioux", "Luc", "3456 rue Lavoie", 65000,1.50); Inserer_nouveau_membre(6500, "Roy", "Peter", "3429 rue du Lac", 35050,1.75); if (Nombre_de_membres_du_syndicat() == 2) cout << "Reussite.\n";

Test unitaire d’une fonction // La fonction "Calcule_cotisation_annuelle_moyenne". // Test positif. if (Calcule_cotisation_annuelle_moyenne() == 1.625f) cout << "Reussite.\n"; else cout << "echec\n"; // Test négatif try { syndicat(); cout << Calcule_cotisation_annuelle_moyenne(); cout << "echec\n"; } catch(int i) cout << i << " : succes\n";

Test unitaire d’une fonction // La fonction "Inserer_nouveau_membre". // Test négatif. try { Inserer_nouveau_membre(5234, "Roy", "Luc", "3456 Lavoie", 65000, 1.50); cout << "echec\n"; } catch(int i) cout << i << " : succes\n"; Inserer_nouveau_membre(6234, "Roy", "Luc", "3456 Lavoie", 65000, 2.50);

Test unitaire d’une fonction try { Inserer_nouveau_membre(6234, "Roy", "Luc", "3456 Lavoie", 65000, 1.50); cout << "echec\n"; } catch(int i) cout << i << " : succes\n";

Test de la boîte blanche Il est impossible de garantir avec certitude qu’un programme fonctionne correctement dans tous les cas possibles. Le test unitaire comprend un nombre fini de tests. Tout test ne peut prouver que la présence de bogues, non leur absence. Dijkstra Il s’agit ici de considérer le fonctionnement interne de la fonction en effectuant une trace de la fonction. La trace peut se faire via le débogueur ou à l’aide d’opérations de sortie.

Test de la boîte blanche float RacineCarree(float a) { if (a < 0.0f) throw(1); // Lancement d’une exception. if (a == 0.0f) return 0.0f; float xnouveau = a; float xvieux; do xvieux = xnouveau; xnouveau = (xvieux + a / xvieux) / 2.0f; cout << "xvieux = " << xvieux << " xnouveau = "  << xnouveau << endl; } while ( (xnouveau - xvieux) >= EPSILON || (xnouveau - xvieux) <= - EPSILON ); return xnouveau;