OMGL2 Conception logiciel

Etape Codage
IHM Données Logiciel Tests

Annie Culet

Etape 3 Codage
La phase de codage Traduction de la conception dans un langage de programmation Exécution des tests Résultat Des sources commentés, commandes de compilation, exécution .. Résultats des tests

Etape 3 : codage - démarche
Choix du langage de programmation et de la bibliothèque graphique : Ada / GtkAda Choix de la méthode de gestion de la persistance des données : Outil de mapping typeAda/ relationnel : Mill Choix du logiciel de test : framework AUnit Réalisation : L’IHM Code des procédures "charge" et handler des modules IHM Le noyau fonctionnel Schéma relationnel des données Code des procédures de la couche application du noyau fonctionnel Tests fonctionnels unitaires

La réalisation

Annie Culet

3

Bonnes pratiques de programmation
Il est indispensable de : comprendre son propre code en cours d'écriture après un mois (!) être compris par les autres développeurs changement d'équipe, maintenance, ... Que fait ce programme ? Exemple tiré de http://www.labri.fr/~marlet
int a[1817];main(z,p,q,r){for (p=80;q+p-80;p-=2*a[p])for (z=9;z--;)q=3&(r=time(0)+r*57)/ 7,q=q?q-1?q-2?1-p%79?-1:0:p%7977?1:0:p<1659?79:0:p>158?-79:0,q?!a [p+q*2]?a[p+=a[p+=q]=q]=q:0:0;for (;q++-1817;)printf(q%79?"%c":"% c\n"," #"[!a[q-1]]);}
La réalisation Annie Culet

4

Bonnes pratiques
Nommage : choisir des noms explicites, utliser des conventions de nommage meilleure lisibilité du code, recherche facilitée, distingue l’élément désigné. Variables nomAuteur, refOuvrage et non a, x entNomPays et non entry1, nom Types tnomAuteur Modules p_ajouterOuvrage et non ch30 et non CU7 et non consult (…)

Procédures, fonctions consulterTousAuteurs (…) Exceptions ExPaysExistant
La réalisation

et non e1
Annie Culet

5

Bonnes pratiques
Structuration du code et homogénéité modules utilitaires, procédures privées, …. (pas de code redondant) homogénéité d’un style dans l’utilisation des structures de contrôles, règles de style et d’indentation, … (cf. cours algo/prog) Commentaires Les commentaires doivent être ... nombreux il n'y en a jamais assez uniques duplication → incohérence, problème de maintenance pertinents pas de commentaire « affectation de la variable x à zéro » pour « x = 0; » au plus près de la chose commentée systématiquement mis à jour

La réalisation

Annie Culet

6

Bonnes pratiques
Utilisation d’un environnement de développement IDE environnement de développement intégré gestionnaire de versions Exemples en Ada : Gnat Programming Studio (Unix) Eclipse (plug-in pour Ada), AdaGide (Windows)

La réalisation

Annie Culet

7

Réalisation de l’IHM
Notion de programmation événementielle Présentation de la bibliothèque graphique GtkAda Codage de l’interface description des fenêtres en XML désignation des composants de l’IHM dans le code codage des procédures handler conversion de types

Réalisation IHM

Annie Culet

8

Programmation événementielle
Application interactive Programmation événementielle : l’utilisateur a le contrôle une action de l'utilisateur sur un composant graphique déclenche un événement exemples d’événement : un clic sur un bouton de commande, une saisie dans une zone de texte, une sélection dans une liste, un choix dans une case à cocher… les événements sont captés par le système d'exploitation puis pris en charge par la boucle de gestion des événements si une procédure spécifique (handler, callback, procédure de rappel) est associée à un événement, elle est alors exécutée Exemple : le fait de cliquer sur le « Bouton A » va déclencher l'évènement « clicked » associé à « Bouton A ». Le programmeur définit pour chacun des composants qu'il utilise, les évènements qu’il veut gérer pour ce composant, et donc les procédures associées à la gestion de cet évènement pour ce composant.
Réalisation IHM Annie Culet

9

Utilisation de la bibliothèque graphique GtkAda
GtkAda : bibliothèque graphique permet de réaliser des interfaces graphiques en Ada. construite sur la bibliothèque graphique GTK+ (Gimp ToolKit, libre) GTK+ existe sur plusieurs plates-formes (UNIX, Windows) GTK+ est utilisable avec plusieurs langages de programmation (C, C++, Ada, Perl, Python, PHP … ) Intégrée avec un constructeur d’interface graphique Glade Glade sauvegarde un projet dans un fichier XML (.glade) contenant une description de l'interface et les procédures à appeler pour traiter les différents signaux La bibliothèque libglade permet de créer et afficher dynamiquement les composants à partir d’un fichier de définitions XML Glade.XML est le module de liaison à la bibliothèque libglade

Réalisation IHM

Annie Culet

10

XML
XML : eXtensible Markup Language (Langage à balises extensible) Norme permettant de définir un format d'échange de données structurées selon les besoins de l'utilisateur (standard du World Wide Web Consortium) Les données sont indépendantes de l'affichage. Un document XML est composé de balises et de texte libre. Un document XML peut se représenter sous la forme d'une arborescence d'éléments
<?xml version="1.0" encoding="ISO-8859-1"?> <annuaire> <personne> <nom>Strummer</nom> <prenom>Joe</prenom> </personne> <personne> <nom>Cantat</nom> <prenom>Bertrand</prenom> </personne> </annuaire>

Réalisation IHM

Annie Culet

11

Extrait d’un fichier XML généré par Glade
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <glade-interface> <widget class="GtkWindow" id="oneWindow"> balise et fermeture de balise <property name="visible">True</property> <property name="title" translatable="yes">maFenetre</property> <mot clé …> </mot clé> <child> <widget class="GtkVBox" id="vbox1"> <child> <widget class="GtkHBox" id="hbox1"> <child> <widget class="GtkLabel" id="label1"> <property name="label" translatable="yes">Nom de famille</property> </widget> </child> <child> <widget class="GtkEntry" id="entry_nomFamille"> propriété : attribut = "valeur" <property name="visible">True</property> class type du composant </widget> </child> id identificateur du composant </widget> handler identificateur de la procédure </child> <child> <widget class="GtkHBox" id="hbox2"> <child> <widget class="GtkButton" id="button_valider"> <property name="label" translatable="yes">Valider</property> <signal name="clicked" handler="on_button_valider_clicked"/> </widget> ... </child> </widget> </child> </widget> </child> </widget> </glade-interface>

Réalisation IHM

Annie Culet

12

Description de l’interface
La description d’un composant graphique est introduite par la balise <widget Lorsqu’un composant en contient d’autres, ceux-ci sont placés dans un bloc <child Chaque composant graphique est défini par : une classe (son type, par exemple GtkWindow, GtkButton, ...) un id (son identificateur, qui permet d'y accéder par la suite) <widget class="GtkEntry" id="entry_nomFamille"> un certain nombre de propriétés qui dépendent du type du composant <property name="visible">True</property> indique que la fenêtre est visible La balise <signal introduit le type du signal émis qui déclenchera la procédure dont l’identificateur est donné. <signal name="clicked" handler="on_button_valider_clicked"/>

Réalisation IHM

Annie Culet

13

usr/share/ada/adainclude/gtkada2/glade.XML.ads
-- This package is a binding to the libglade library that provides routines -- to create widgets dynamically from an XML definition file. with … ; with Gtk.Widget; use Gtk.Widget; package Glade.XML is type Glade_XML_Record is new Glib.Object.GObject_Record with private; type Glade_XML is access all Glade_XML_Record'Class; procedure Gtk_New (XML : out Glade_XML; Fname : String; Root : String := ""; Domain : String := ""); -- Create a new GladeXML object (and the corresponding widgets) from the XML file Fname. -- Optionally it will only build the interface from the widget node Root (if it is not empty) -- Domain, if not null, is the international domain to use for string translation.
Réalisation IHM Annie Culet

14

usr/share/ada/adainclude/gtkada2/glade.XML.ads
procedure Signal_Connect (XML : access Glade_XML_Record; Handlername : String; Func : System.Address; User_Data : System.Address); function Get_Widget (XML : access Glade_XML_Record; Name : String) return Gtk_Widget; -- This function is used to get the Gtk_Widget corresponding to name in the interface description. -- You would use this if you have to do anything to the widget after loading. function Get_Widget_Name (Widget : access Gtk_Widget_Record'Class) return String; ….

Réalisation IHM

Annie Culet

15

Programme principal "type"
with Gtk.Main; with Glade.XML; procedure start is xml : Glade_XML ; -- variable de type Glade_XML begin -- Initialisation de variables d’environnement Gtk.Main.Set_Locale; -- Initialisation de Gtk Gtk.Main.Init; --Chargement de la description complète de l’interface à partir du fichier XML et affichage Glade.XML.Gtk_New (xml, " monFichier.glade"); -- Lancement de la boucle évènementielle de Gtk Gtk.Main.Main; end start;

Réalisation IHM

Annie Culet

16

"Chargement" d’une description de fenêtre
"Chargement" de la description d’une fenêtre à partir de son id dans le fichier XML xml : Glade_XML ; -- variable de type Glade_XML Glade.XML.Gtk_New (xml, "fichier.glade", "oneWindow") ;
<widget class="GtkWindow" id="oneWindow"> paramètre en sortie … </widget> <widget class="GtkWindow" id=“anOtherWindow"> … </widget>

" Récupération" d’un composant de la fenêtre à partir de son id dans le fichier XML avec conversion de type entNom : Gtk_Gentry; -- variable de type Gtk_Gentry
paramètre en entrée

entNom := Gtk_Entry (Glade.XML.Get_Widget (xml, "entry_nomFamille");
<widget class="GtkEntry" id="entry_nomFamille">

Réalisation IHM

Annie Culet

17

Association d’une procédure Handler
Association d’une procédure Handler désignée par son identificateur dans la description de l’interface à une procédure dans un programme : Glade.XML.signal_connect (xml, "on_button_valider_clicked", procTraite’address, System. null_address);
<signal name="clicked" handler="on_button_valider_clicked"/>

procedure procTraite (widget : access Gtk_Widget_Record’Class) is begin -- traitement à effectuer sur émission du signal end procTraite;

Réalisation IHM

Annie Culet

18

usr/share/ada/adainclude/gtkada2/Gtk.Object.ads
with with with with Glib.Object; Glib.Properties; Glib.GSlist; Gtkada.Types;

package Gtk.Object is type Gtk_Object_Record is new Glib.Object.GObject_Record with private; type Gtk_Object is access all Gtk_Object_Record'Class; procedure Destroy (Object : access Gtk_Object_Record); ----Destroy the object. The object is then unref-ed, and the memory associated with the object is freed. In most cases, only toplevel widgets (windows) require explicit destruction because when you destroy a toplevel its children will be destroyed as well.

Réalisation IHM

Annie Culet

19

usr/share/ada/adainclude/gtkada2/Gtk.Widget.ads
…. with Gtk.Enums; with Gtk.Object; with Gtk.Style; package Gtk.Widget is type Gtk_Widget_Record is new Object.Gtk_Object_Record with private; type Gtk_Widget is access all Gtk_Widget_Record'Class; procedure Show (Widget : access Gtk_Widget_Record); -- Schedule the widget to be displayed on the screen when its parent is also shown procedure Hide (Widget : access Gtk_Widget_Record); -- Hide the widget from the screen -- If Widget was visible, it is immediately hidden. procedure Show_All (Widget : access Gtk_Widget_Record); -- Show Widget and all its children recursively. procedure Hide_All (Widget : access Gtk_Widget_Record); -- Hide Widget and all its children. procedure Set_Sensitive (Widget : access Gtk_Widget_Record; Sensitive : Boolean := True); -- Modify the sensitivity of the widget. -- An insensitive widget is generally grayed out, and can not be activated.

Réalisation IHM

Annie Culet

20

usr/share/ada/adainclude/gtkada2/Gtk.Editable.ads
with Gtk.Widget; package Gtk.Editable is type Gtk_Editable_Record is new Gtk.Widget.Gtk_Widget_Record with private; type Gtk_Editable is access all Gtk_Editable_Record'Class; -- Gtk_Editable is now an interface, not an object procedure Insert_Text (Editable : access Gtk_Editable_Record; New_Text : UTF8_String; Position : in out Gint); -- Insert the given string at the given position. -- Position is set to the new cursor position. If Position is -1, the text is appended at the end. procedure Delete_Text (Editable : access Gtk_Editable_Record; Start_Pos : Gint := 0; End_Pos : Gint := -1); -- Delete the characters from Start_Pos to End_Pos. -- If End_Pos is negative, the characters are deleted from Start_Pos to the end of the text. procedure Set_Editable (Widget : access Gtk_Editable_Record; Editable : Boolean := True);

Réalisation IHM

Annie Culet

21

usr/share/ada/adainclude/gtkada2/Gtk.Gentry.ads
with Glib.Properties; with Gtk.Editable; with Gtk.Entry_Completion; use Gtk.Entry_Completion; with Pango.Layout; package Gtk.GEntry is type Gtk_Entry_Record is new Gtk.Editable.Gtk_Editable_Record with private; -- Gtk_Entry is actually a child of Gtk_Widget, and implements the -- Gtk_Editable interface, but GtkAda does not support yet interfaces, type Gtk_Entry is access all Gtk_Entry_Record'Class; subtype Gtk_GEntry is Gtk_Entry; procedure Set_Text (The_Entry : access Gtk_Entry_Record; Text : UTF8_String); function Get_Text (The_Entry : access Gtk_Entry_Record) return UTF8_String; -- Modify the text in the entry. -- The text is cut at the maximum length that was set when the entry was created. -- The text replaces the current contents. procedure Set_Max_Length (The_Entry : access Gtk_Entry_Record; Max : Gint); -- Set the maximum length for the text. The current text is truncated if needed.
Réalisation IHM Annie Culet

22

Architecture d’un logiciel interactif

acquisition des données en provenance de l’utilisateur
données au format des composants de la fenêtre

Couche IHM
Présentation et dialogue

restitution de données vers l’utilisateur
données converties au format des composants de la fenêtre

Adaptateur des données appel des procédures du noyau fonctionnel
données converties aux données au format des types types de données du langage de données du langage

récupération des résultats

Noyau Fonctionnel Adaptateur des données Ajuste les différences de modélisation entre les données de la présentation et celles du noyau fonctionnel (conversion de type, interprétation de cases à cocher, boutons radio, …)
Conception Logiciel Annie Culet

23

Adaptateur de données
Module de conversion de type : p_conversion (fourni localement) une fonction : empty -- teste si une chaîne UTF8 est vide ou non des procédures : to_ada_type -- conversion d’une chaîne UTF8 vers un type Ada des fonctions : to_string -- conversion un type numérique non contraint et date en chaîne UTF8 + modules génériques pour les conversions concernant les numériques contraints et les énumérés avec des procédures/fonctions : to_ada_type et to_string une procédure to_ada_type : lève l’exception ExConversion si le type en sortie n’est pas conforme au type Ada attendu, affiche un message d’erreur dans une boîte de dialogue.
La réalisation Annie Culet

24

Module de conversion de type
package p_Conversion is -- teste si une chaîne est vide ou non function empty (entree : UTF8_String) return boolean; -- effectue une conversion d’une chaîne vers un type Ada (chaîne ou numérique non contraint) -- si le type en sortie n’est pas conforme au type Ada attendu, affiche un message d’erreur et lève l’exception ExConversion procedure to_ada_type (entree : UTF8_String; sortie : out integer) ; procedure to_ada_type (entree : UTF8_String; sortie : out float) ; procedure to_ada_type (entree : UTF8_String; sortie : out string ); ExConversion : exception ; -- convertit un type numérique non contraint en chaîne function to_string (item : float) return string; function to_string (item : integer) return string;

Réalisation IHM

Annie Culet

25

Module de conversion de type
-- modules génériques pour les conversions concernant les numériques contraints et les énumérés -- si le type en sortie des procédures to_ada_type n’est pas conforme au type Ada attendu, affiche un message d’erreur et lève l’exception ExConversion generic type entier is range <> ; package P_Entier is procedure to_ada_type (entree : UTF8_String; sortie : out entier ); ExConversion : exception ; function to_string (item : entier) return string; end P_Entier;

Réalisation IHM

Annie Culet

26

Module de conversion de type
generic type reel is digits <> ; package P_Reel is procedure to_ada_type (entree : UTF8_String; sortie : out reel ); ExConversion : exception ; function to_string (item : reel) return string; end P_Reel; --------------------------------------------------------------------------------------------------------------------------generic type t_enum is (<>); package P_Enum is procedure to_ada_type (entree : UTF8_String; sortie : out t_enum ); ExConversion : exception ; function to_string (item : t_enum) return string; end P_Enum;

Réalisation IHM

Annie Culet

27

Contrôles réalisés dans l’IHM
A l’issue d’une saisie, les contrôles de type et de présence de données obligatoires sont effectués dans la couche IHM avant tout appel d’une procédure du NF. Les types des paramètres d’une procédure du NF sont donc vérifiés avant son appel. Contrôle de présence pour une donnée obligatoire Ex. vérification qu’une zone d’entrée n’est pas vide Contrôle du type d’une donnée saisie si l’adaptateur n’arrive pas à convertir le contenu d’une zone d’entrée, un élément d’une liste ou d’une table au type de la donnée correspondant dans le langage de programmation (chaîne d’une longueur donnée, entier, réel, énuméré, …), il y a une erreur de type.

Conception Logiciel

Annie Culet

28

Codage de l’interface
Module fenêtre Choisir la modalité de la fenêtre Définir une variable par composant manipulé Lorsqu’une fenêtre est composée de plusieurs régions, pour focaliser le dialogue dans une région, les autres régions doivent être rendues inaccessibles
Protection contre les erreurs

Set_Editable (entNom, false)

Set_Sensitive (butEnregistrer, false)

Réalisation IHM

Annie Culet

29

Code des procédures handler
Enregistrer un adhérent

F. EnregAdh bibliothèque graphique GtkAda
p_conversion

Ok

B. Confirm

Enregistrer/générer numAdhérent et créer adhérent Annuler

Couche IHM
p_window_enregAdherent
Interface p_window_enregAdherent charge (…) enregistrerAdherent(…) { } {la boîte de dialogue de confirmation de la création de l’adhérent est affichée; la fenêtre “EnregAdh“ est fermée } annulerOperation(..) { } {la fenêtre “EnregAdh“ est fermée}

charge (…) enregistrerAdherent() …

Conversion Chaînes /types du langage Exception ExConversion

p_application
Noyau Fonctionnel Couche application

creerAdherent ( adh : tAdhérent) ;
utilise

Interface creerAdhérent (d adh : tAdhérent) ; { } { le numéro de l’adhérent est généré. L’adhérent adh est créé sans emprunt}

légende

propage exception

Réalisation IHM

Annie Culet

30

Code des procédures handler
procedure enregistreAdherent (…) is
1. – vérif. présence infos obligatoires, récupération des valeurs saisies,conversion vers types Ada du modèle de données

adh : tAdherent; if empty (get_text(entNom)) then rep := Message_Dialog (« le nom est obligatoire »); else …. p_conversion.to_ada_type(get_text(entNom), adh.nomAdherent)); …. if get_active(radiobutAdulte) then adhVal.categorie := adulte; ….
2. -- appel de la procédure du noyau fonctionnel. Le type adhVal est correct. Message de confirmation.

creerAdherent (adhVal , adhIdf) ; rep := Message_Dialog (« Confirmation enregistrement adhérent » & adhVal.numAdherent); destroy (FenEnregAdherent); -- destruction de la fenêtre
3. -- traitement des exceptions

when ExConversion => return; when ExopFatal => rep := Message_Dialog (« Problème sur la base »);

Réalisation IHM

Annie Culet

31

Code des procédures handler
Modifier coordonnées adhérent F. ModifAdh région 1 Annuler Ok Ok [non trouvé] B.Erreur B. Confirm Enregistrer / modifier adhérent Annuler Rechercher / consulter adhérent [trouvé] F. ModifAdh région 2

p_FenModifCoordAdherent

Couche IHM

charge (…) rechercherAdherent() modidierAdherent()
Interface consulterAdhérent (d n : tnumAdhérent; r adh : tAdhérent); { } {l’adhérent n est consulté et les informations relatives à l’adhérent sont retournées dans adh} Exception : AdhérentNonTrouvé {l’adhérent n n’existe pas} modifierAdhérent (d n : tnumAdhérent; d adh : tAdhérent); {} {les coordonnées de l’adhérent n sont modifiées et sont enregistrées}

Noyau Fonctionnel Couche application

p_application
consulterAdherent (n : …) : tAdherent; Exception ExAdherentNonTrouve modifierAdherent (adh : tAdherent)

légende

utilise propage exception retourne résultat

Réalisation IHM

Annie Culet

32

Code des procédures handler

Procedure on_buttonRechercher_clicked (…) is

Spécification { } {les informations relatives à l’adhérent sont affichées dans la fenêtre}

1. -- vérif. présence du numéro, récupération de la valeur saisie et conversion au type Ada du modèle de données -- les boutons Enregistrer/Annuler ne doivent pas être activables, …. 2. -- appel de la procédure du noyau fonctionnel. Le type adhIdf est correct.

consulterAdherent (adhIdf, adhVal ) ; 3. -– affichage des valeurs résultat dans la région 2 de la fenêtre
-- les boutons Rechercher/Annuler ne doivent pas être activables, …. Le nom est non modifiable.

set_text (entNom, adhVal.nomAdherent); -- pas de conversion puisque le nom est une chaîne
4. -- traitement des exceptions

when ExConversion => return; when ExAdherentNonTrouve => rep := Message_Dialog (« Adhérent non trouvé »); when ExopFatal => rep := Message_Dialog (« Problème sur la base »);
Réalisation IHM Annie Culet

33

Code des procédures handler
Spécification { } {les nouvelles informations relatives à l’adhérent sont enregistrées}

Procedure on_buttonEnregistrer_clicked (…) is
1. -- récupération de la nouvelle adresse saisie et conversion au type Ada du modèle de données 2. -- appel de la procédure du noyau fonctionnel. Message de confirmation. Destruction de la fenêtre

modifierAdherent (adhIdf, adhVal ) ; ……
3. -- traitement des exceptions

when ExConversion => return; when ExopFatal => rep := Message_Dialog (« Problème sur la base »);

Réalisation IHM

Annie Culet

34

Code des procédures handler
type tOuvrageVal is record Interface couche Application réfOuvrage : trefOuvrage; consulterOuvrage(ouvIdf : in tOuvrageIdf ; … ouvVal : out tOuvrageVal ; autVal : out tautVal); auteur : tAuteurIdf; Exception : ExOuvrageNonTrouve empruntOuvrage : tEmpruntIdf := empruntNul; Exception : ExOuvrageDisponible end record; consulterEmprunt (empIdf : in tEmpruntIdf ;empVal : out tEmpruntVal);
Spécification { } {les infos sur l’ouvrage, son auteur et éventuellement son emprunt sont affichées dans la fenêtre}

Procedure on_buttonRechercher_clicked (…) is
1. -- récupération de la valeur saisie dans la fenêtre et conversion 2. -- appel au noyau fonctionnel.

consulterOuvrage (ouvIdf, ouvVal , autVal) ; consulterEmprunt (ouvVal.empruntOuvrage, empVal); 3. -– affichage des valeurs résultat affiche_InfosOuvrageAuteur; -- procédure privée affiche_InfosEmprunt; -- procédure privée
4. -- traitement des exceptions

when ExOuvrageNonTrouve => rep := Message_Dialog (« Ouvrage non trouvé »); when ExOuvrageDisponible => affiche_InfosOuvrageAuteur; set_text (entDateEmprunt, « Ouvrage disponible » ); -- + les autres
Réalisation IHM Annie Culet

35

Conception des données
Solution retenue : les données sont stockées dans une base de données relationnelle Dans ce contexte : détermination du schéma relationnel de données

On applique les « règles » de traduction Diagrammes de classes / Schéma relationnel Le schéma relationnel obtenu doit préciser : les clés primaires les clés étrangères Il faut également préciser : le caractère « obligatoire » ou non des attributs toutes les contraintes d’intégrité du diagramme de classes non prises en compte par le schéma relationnel

La réalisation

Annie Culet

36

à partir d’un diagramme de classes …
{taille(réfOuvrage) ≤ 6 et unique}

Ouvrage réfOuvrage : Chaine titreOuvrage : Chaine genreOuvrage : Enum {roman, policier, SF, documentaire, théâtre, poésie, BD } dateParution : Date emprunt 0..4 Emprunt publication 1..* 1..1

Gestion d’une bibliothèque

Auteur nomAuteur : Chaine auteur prénomAuteur : Chaine nationalitéAuteur : Chaine
{concat(nomAuteur, prénomAuteur) unique}

dateEmprunt :Date 0..1 emprunteur

Adhérent numAdhérent : Chaine nomAdhérent : Chaine adresseAdhérent : Chaine catégorie : Enum {jeune, adulte}
Conception Données

{taille(numAdhérent) ≤ 5 et unique}

Annie Culet

37

Passage du diagramme de classes au relationnel
R0 : Chaque classe C devient une relation RC dont : les attributs sont les attributs monovalués de la classe C, la clé de RC est l’attribut (ou groupe d’attributs) marqué par la contrainte {unique} ou un nouvel attribut.
Auteur nomAuteur prénomAuteur nationalitéAuteur

AUTEUR(nomAuteur, prénomAuteur, nationalité)

{concat(nomAuteur, prénomAuteur) unique}

Retard minutesRetard raison

RETARD(cléRetard, minutesRetard, raison)

La réalisation

Annie Culet

38

Passage du diagramme de classes au relationnel
R1 : un attribut multivalué d’une classe C devient une relation dont : les attributs sont l’attribut multivalué de la classe C et la clé de la relation RC représentant la classe C qui est clé étrangère, la clé de cette relation est l’ensemble des 2 attributs.
Personne numSécu {unique} nom telPersonnel [1..2]

PERSONNE(numSécu, nom) TELPERSONNE(#numSécu, telPersonnel)

La réalisation

Annie Culet

39

Passage du diagramme de classes au relationnel
R2 : une association dont l’un des rôles est de multiplicité 1 détermine une clé étrangère dans la relation RC représentant la classe C associée au rôle inverse. Les attributs de la classe association deviennent attributs de cette relation RC.
Personne nInsee {unique} nom age employé 1..* 1 service Embauche dateEmbauche Service nomService {unique} batiment

PERSONNE(nInsee, nom, age, # nomService, dateEmbauche) SERVICE(nomService, batiment)

La réalisation

Annie Culet

40

Passage du diagramme de classes au relationnel
R3 : une association dont l’un des rôles est de multiplicité 0..1 devient une relation dont : les attributs sont les clés des relations représentant les classes associées et les attributs de la classe association, la clé est la clé de la relation RC représentant la classe C associée au rôle inverse , les clés des relations représentant les classes associées sont clés étrangères.
Ouvrage réfOuvrage {unique} titreOuvrage genreOuvrage 0..4 emprunt Emprunt dateEmprunt emprunteur 0..1 Adhérent numAdhérent {unique} nomAdhérent adresseAdhérent

OUVRAGE(réfOuvrage, titreOuvrage, genreOuvrage) ADHERENT(numAdhérent, nomAdhérent,adresseAdhérent) EMPRUNT(#réfOuvrage, #numAdhérent, dateEmprunt)

La réalisation

Annie Culet

41

Passage du diagramme de classes au relationnel
R4 : une association dont les rôles sont de multiplicité * devient une relation dont : les attributs sont les clés des relations représentant les classes associées et les attributs de la classe association, la clé est formée par les clés des relations représentant les classes associées, qui sont aussi clés étrangères.
Enseignant nom {unique} spécialité Cours enseignant 1..* 1..* coursDonné Salle numSalle intitulé {unique} matière nbHeures

ENSEIGNANT(nom, spécialité) COURS(intitulé, matière,nbHeures) ENSEIGNE(#nom, #intitulé, numSalle)

La réalisation

Annie Culet

42

Passage du diagramme de classes au relationnel
Que devient un attribut calculé d’une classe ? en général non représenté : il sera calculé dynamiquement (par une procédure au niveau application). on peut néanmoins décider (pour des raisons de performance) de représenter l'attribut dérivé mais il sera nécessaire dans ce cas d’assurer que la valeur stockée évolue en même temps que les attributs sur lesquels le calcul dérivé porte.

Compléter votre schéma en notant les attributs obligatoires avec * Exprimer toutes les contraintes d’intégrité en français

La réalisation

Annie Culet

43

… le schéma relationnel
Gestion d’une bibliothèque

OUVRAGE (réfOuvrage, titreOuvrage*, genreOuvrage*, dateParution*, #(nomAuteur, prenomAuteur)*) AUTEUR (nomAuteur, prenomAuteur, nationalité*) ADHERENT (numAdhérent, nomAdhérent*, adresseAdhérent*, categorie*) EMPRUNT (#réfOuvrage, #numAdhérent*, dateEmprunt*) contrainte d’intégrité : Dans EMPRUNT: un adhérent a au plus 4 ouvrages empruntés.

La réalisation

Annie Culet

44

Mapping vers le relationnel depuis Ada
Motivation avoir un niveau d'accès aux données qui masque la couche SGBD relationnel qui réalise la persistance =>permet de développer une couche application sans SQL

Une variable structurée permet de représenter directement un n-uplet (une entité) stocké dans une table du SGBD Le type structuré a des attributs qui correspondront aux champs (colonnes) de la table qu'elle représente On définit un type structuré pour chaque table de la BD

La réalisation

Annie Culet

45

Mapping vers le relationnel depuis Ada
Exemple : table Ouvrage contenant des ouvrages :
refOuvrage CAM645 CAM128 titreOuvrage la lune de papier la prise de Malaké genreOuvrage policier roman dateParution janvier 2008 février 2006 nomAuteur Camilleri Camilleri prenomAuteur Andrea Andrea

On définit un type structuré pour représenter un enregistrement de cette table : type tOuvrage is record refOuvrage : String; titreOuvrage : String; genreOuvrage : tgenre_enum; dateParution: Date; nomAuteur : String; prenomAuteur : String; end record; On définit également des procédures/fonctions pour manipuler les variables de ce type.

La réalisation

Annie Culet

46

Besoin d’un outil
Implémenter "à la main" un mapping est une tâche répétitive, fastidieuse et délicate (en particulier adéquation entre les types de données SQL et ceux de Ada)

Il existe des outils dans d’autres langages pour réaliser cette corvée de façon "propre" et automatique : en java : Torque, ... en PHP : Propel,...

En Ada, un outil a été développé selon les mêmes principes : Mill http://www.virtual-worlds.biz/downloads/mill/

La réalisation

Annie Culet

47

Principe de Mill (de Torque, …)

Fichier décrivant le schéma relationnel de la BD (format XML)

Un type structuré par table

Générateur Mill
Un ensemble de procédures par table

La réalisation

Annie Culet

48

Schéma XML correspondant au schéma relationnel de la BD
<database name= "Biblio" > <table name= "Ouvrage" > <column name= "refOuvrage" <column name= "titreOuvrage" <column name="genreOuvrage" <column name="dateParution" <column name= "nomAuteur" primaryKey="true" type="CHAR" size=" 6"/> required="true" required="true" required="true" required="true" type="CHAR" type="ENUM" type=" DATE" /> type="CHAR" type="CHAR" size=" 30"/> size=" 30"/> size=" 80"/>

values = "roman policier SF documentaire théâtre poésie BD" />

<column name= "prenomAuteur" required="true" <foreign-key foreignTable="Auteur" > <reference foreign="nomAuteur" </foreign-key> <foreign-key foreignTable="Auteur" > <reference foreign="prenomAuteur" </foreign-key> </table>

local= "nomAuteur "/>

local= "prenomAuteur"/>

La réalisation

Annie Culet

49

Codage du logiciel
Codage des modules de la couche application du noyau fonctionnel Génération de la couche utilitaire avec Mill Utilisation des modules fournis par la couche utilitaire Propagation des exceptions levées dans la couche application vers la couche IHM

Codage des tests fonctionnels unitaires Écrire les "test_case" avec AUnit et les exécuter

Réalisation Logiciel

Annie Culet

50

Architecture en couches du NF
couche Application
p_application ConsulterOuvrage(…) ConsulterAuteur(…) ModifierAdhérent(…) Exception
… modules réalisant les CU de l’application et les primitives sur la base

couche Utilitaire

ouvrage_io
save () retrieve () …

auteur_io
save () retrieve () …

emprunt_io
save () retrieve () …

biblio_data

Types des données de l’application

modules réalisant le mapping vers le relationnel

couche Noyau
gnade/postgres

Réalisation Logiciel

Annie Culet

51

Utilisation de Mill
Pour une BD biblio contenant les tables XXX, Mill génère : un module biblio.sql qui permet de créer les relations de la base (CREATE TABLE ouvrage …) un module biblio_data dans lequel sont déclarés pour chaque table XXX : le type structuré XXX correspondant ( type touvrage is record ….) un module XXX_list qui gère un ensemble d’éléments de ce type ( package ouvrage_list …) un module base_types qui contient en particulier les déclarations des types énumérés utilisés s’il y en a (type tgenre_enum, …) pour chaque table XXX un module XXX_io qui contient les procédures permettant de réaliser les mises à jour dans la table (ouvrage_io, …) Retrieve_By_PK, Retrieve, Save, Delete Add_nomAtt pour valoriser une clause WHERE (add_titreOuvrage, …)

La réalisation

Annie Culet

52

Création simple
Pour créer un nuplet dans une table, il suffit de donner des valeurs aux attributs de la variable structurée correspondante et de la sauvegarder dans la BD par la procédure save. Mill traduira cela par une requête SQL « insert ».

ouvrage_item : biblio_data.touvrage; ouvrage_item.refOuvrage := "CAM645" ; ouvrage_item.titreOuvrage := "la lune de papier" ; …. ouvrage_io.save (ouvrage_item, false); une exception est levée si l’ouvrage existe déjà.

La réalisation

Annie Culet

53

Rechercher un nuplet dans la base
Pour rechercher à partir de la clé primaire, il suffit de passer la valeur de la clé à la procédure retrieve_by_pk. Mill va exécuter une requête " select " ouvrage_item : biblio_data.touvrage; ref : Unbounded_String; ref := "CAM645" ; ouvrage_item := ouvrage_io.retrieve_by_pk (ref); retourne biblio_data.null_ouvrage s’il n’est pas trouvé à tester avec la fonction Is_Null

La réalisation

Annie Culet

54

Rechercher un ensemble de nuplets dans la base
Pour rechercher à partir de la valeur d’un ou plusieurs attributs, on affecte les attributs et on utilise la procédure retrieve. Mill va exécuter une requête " select " avec une clause WHERE Vector est une collection d’éléments (voir les containers en Ada) ouvrage_list_item: biblio_data.ouvrage_list.vector; criteria : db_commons.criteria; rechercher tous les ouvrages ouvrage_list_item := retrieve(criteria) rechercher tous les ouvrages du genre policier ouvrage_io.add_genreOuvrage (criteria, policier); ouvrage_list_item := retrieve(criteria); retourne biblio_data.null_ouvrage_list si le résultat est vide à tester avec la fonction Is_Empty
La réalisation Annie Culet

55

Modification, suppression
Pour modifier un nuplet idem création mais …. ouvrage_io.save (ouvrage_item, true); Pour supprimer un nuplet idem rechercher mais …. ouvrage_io.delete (criteria)

La réalisation

Annie Culet

56

Equivalence des types Ada/SQL
Type Fichier XML DATE|DATETIME|TIME CHAR INTEGER REAL BOOLEAN DECIMAL ENUM size, scale values size Attributs Type ada ada.calendar.time unbounded_string integer long_float boolean type décimal contraint type énuméré

La réalisation

Annie Culet

57

ODBC, GNADE
ODBC (Microsoft) (comme JDBC Sun Java) : API Interface de Programmation (Application Programming Interface) permet la connexion, interrogation et sa mise à jour de la base de données, traitement des erreurs indépendante de tout SGBD particulier nécessite un driver (pilote) spécifique au SGBD utilisé

GNADE (GNU Ada Database Environment) : interface SQL avec Ada 95 suivant le principe de l'Embedded SQL (SQL embarqué). Utilise l’API ODBC disponible sur Linux, Solaris et Windows/NT

La réalisation

Annie Culet

58

Traitement des exceptions
…. créerOuvrage(…) Exception when ExOuvrageExistant => … créerOuvrage(…) ExOuvrageExistant
interface corps Module

couche IHM couche Application couche Utilitaire couche Fichier

Propagation exception

ajouter (…)

ExOuvrageAjouter

noyau fonctionnel

ecrire(…)

ExFiEcrire

Toutes les exceptions sont propagées jusqu’au niveau IHM pour y être traitées dans leur contexte.
Réalisation Logiciel Annie Culet

59

Exception - Rappel
1. traitée localement
Procedure Q(…) begin si Ex est levée, elle est traitée par le Traite-Exception TE qui achève …; l’exécution de P. Q ne sait pas que son appel à P s’est "mal terminé". P(…); …; end Q;

TE

Procedure P(…) begin …; _____ …; Exception when Ex => … ; end P;

l’exception Ex est levée abandon de la suite d’instructions contrôle transféré au Traite-Exception TE

Réalisation Logiciel

Annie Culet

60

Exception - Rappel
2. propagée à l’unité appelante
Procedure Q(…) begin …; la même exception Ex (ou ExAlias si renommage) est levée (propagation) et P(…); traitée par le Traite-Exception TE qui achève l’exécution de Q _____ …; La propagation continue tant que l’unité qui la reçoit n’a Exception pas de Traite-Exception. when Ex => … ; Si la propagation arrive au programme principal, l’exécution du programme s’arrête. end Q; l’exception peut être propagée sous un autre nom ExAlias : exception renames Ex Procedure P(…) begin …; _____ …; end P;
l’exception Ex est levée abandon de la suite d’instructions pas de Traite-Exception dans P propagation dans l’unité appelante

TE

Réalisation Logiciel

Annie Culet

61

Exception - Rappel
3. redéclenchement d’une exception
Procedure Q(…) begin …; l’exception Ex2 est levée P(…); _____ …;
Ex2 est traitée par un Traite-Exception ou propagée à l’unité appelante

end Q ;

TE

Procedure P(…) begin …; _____ …; Exception when Ex => … ; raise Ex2; end P ;

l’exception Ex est levée contrôle transféré au Traite-Exception TE

Redéclencher une autre exception est une forme de propagation de l’exception initiale.
redéclenche l’exception Ex2 pour l’unité appelante
Annie Culet

Réalisation Logiciel

62

Renommage
d’une exception, d’une procédure
Utilisation des ressources d’un module :
pour établir la visibilité de son interface : clause with

Identification d’une ressource (procédure, fonction, exception, …)
par le nom complet (notation pointée) : préfixage par le nom du module

P_ ensembleOuvrage.ExEnsConsulter
visibilité directe : utilisation de la clause use mais problème de conflit de noms

ExEnsConsulter de p_ensembleOuvrage ou de p_ensembleAuteur ?
renommage (désignation sous un nouveau nom) : utilisation du renames

ExOuvrageConsulter : exception renames p_ensembleOuvrage.ExEnsConsulter;

les exceptions de la couche application sont souvent redéclenchées pour être propagées sous le nom énoncé dans la conception de l’interface de cette couche.

Réalisation Logiciel

Annie Culet

63

Exemple de propagation d’exceptions
Procedure consulterOuvrage (oIdf : in touvrageIdf ; oVal : out tOuvrageVal ; aVal : out tauteurVal); … ensembleOuvrage.consulter (oIdf, oVal); -- cas ouvrage non emprunté if oVal.emprunt = empruntNul then raise ExOuvrageDisponible; end if; ensembleAuteur.consulter (oVal.auteur, aVal); exception when ExOuvrageConsulter => raise ExOuvrageNonTrouve when ExOuvrageFatal | ExAuteurFatal => raise ExOpFatal; end consulterOuvrage;

couche Application

Procedure on_button_rechercher_clicked (…) is … consulterOuvrage (oIdf , oVal , aVal ); consulterEmprunt (oVal.emprunt , eVal); … exception when ExOuvrageNonTrouve => rep := Message_Dialog (« Référence de l’ouvrage incorrecte»); when ExopFatal => rep := Message_Dialog (« Problème sur la base »); when ExOuvrageDisponible => … when ExConversion => return; couche IHM
Réalisation Logiciel Annie Culet

64

Utilitaire pour le composant Tree_View
with Gtk.Tree_View; use Gtk.Tree_View; with Gtk.Tree_Store; use Gtk.Tree_Store; with Glib; use Glib; -- prévu uniquement pour afficher des chaînes package p_util_treeview is -- création d'une colonne dans une tree_view avec un titre. Le titre peut être visible ou non. procedure creerColonne (titre : UTF8_String ; treeview : Gtk_Tree_View; titreVisible : boolean); -- création d'un modèle pour une tree_view. Il contient les données destinées à alimenter la vue. procedure creerModele ( treeview : Gtk_Tree_View; modele : out Gtk_Tree_Store); end p_util_treeview;

Réalisation IHM

Annie Culet

65

Affichage dans un composant Tree_View
type tAuteurVal is record
nomAuteur : tnomAuteur ; prénomAuteur : tprénomAuteur ; nationalitéAuteur : tnationalitéAuteur ; publication : tEnsOuvrageIdf;

Interface couche Application consulterAuteur(autIdf :in tAuteurIdf ; listeOuvVal :out tlisteOuvrageVal); Exception : ExAuteurNonTrouvé

end record;
Spécification { } {la liste des ouvrages de l’auteur sélectionné est affichée dans la fenêtre}

Procedure on_buttonRechercher_clicked (…) is

1. -- récupération de la valeur saisie et conversion au type Ada du modèle de données -- le bouton Fermer de la région 2 ne doit pas être activable 2. -- appel de la procédure du noyau fonctionnel.

consulterAuteur (autIdf, listeOuvVal ) ; 3. -- les boutons Rechercher/Annuler de la région 1 ne doivent pas être activables, …
-- préparation de la tree_view pour afficher la liste des ouvrages

creerColonne ("référence" , treeViewOuvrage, false); -- 3 fois pour les 3 colonnes sans titre creerModele (treeViewOuvrage, modeleOuvrage) ; -- associe le modèle à la vue

-- modeleOuvrage : Gtk_Tree_Store;

utilise le module Gtk.Tree_Store

Réalisation IHM

Annie Culet

66

Affichage dans un composant Tree_View
4. -– affichage des valeurs résultat dans la région 2 de la fenêtre dans un composant tree_view -- la liste chaînée est parcourue avec le module p_list listeCourante := listeOuvVal; while not estVide (listeCourante) loop ouvVal := premier (listeCourante); -- ajout d’une ligne dans le modèle append (modeleOuvrage, rangOuvrage, null_iter); -- rang désigne une ligne -- alimente les 3 colonnes de la ligne avec une valeur de type chaîne

set(modeleOuvrage, rangOuvrage, 0, ouvVal.refOuvrage); --colonne 1 set(modeleOuvrage, rangOuvrage, 1, ouvVal.titreOuvrage); -- colonne 2 set(modeleOuvrage, rangOuvrage, 2, p_conversionGenre.to_string(ouvVal.genreOuvrage));
listeCourante := suite(listeCourante); end loop; -- une fois le modèle alimenté, la vue s’affiche automatiquement -- rangOuvrage : Gtk_Tree_Iter := Null_Iter; utilise le module Gtk.Tree_Model; -- le module p_conversionGenre instancie le module générique p_enum du module p_conversion pour le type tgenreOuvrage;
Réalisation IHM Annie Culet

67

Sélection dans un composant Tree_View
… on veut récupérer la référence de l’ouvrage dans la ligne sélectionnée de la tree_view précédente.
-- des valeurs sont affichées dans une ou plusieurs colonnes d’une Tree_View -- récupération de la ligne sélectionnée (rang) Get_Selected (Get_Selection (treeviewOuvrage), Gtk_Tree_Model(modeleOuvrage), rangOuvrage); -- récupération de la valeur de la colonne 1 dans la ligne (une chaîne) -- et conversion au type Ada to_ada_type (Get_String (modeleOuvrage, rangOuvrage, 0), ouvVal.refOuvrage); -- utlise le module Gtk.Tree_Selection

Réalisation IHM

Annie Culet

68

Test de logiciel
KOUROU, Guyane 4 Juin 1996 37 secondes après le décollage la fusée Ariane 5 explose en plein ciel à 4000 m d'altitude.
Pourquoi ? Réutilisation dans Ariane 5 d’un composant logiciel d’Ariane 4. ... une seule petite variable : l'accélération horizontale. l'accélération maximum d'Ariane 4 était d'environ 64 l'accélération maximum d'Ariane 5 était d'environ 300 la variable codée sur 8 bits a connu un dépassement de capacité …

La réalisation

Annie Culet

69

Définition du test
Ce que n’est pas le test : un moyen de s’assurer qu’un logiciel fonctionne. Ce qu’est le test : un processus - manuel ou automatique- dont le but est de mettre en évidence les défauts d’un logiciel. Défaut,anomalie l’exécution : manifestation d’une erreur lors de

Erreur : écart entre une valeur (ou condition) observée ou mesurée et la valeur (ou condition) spécifiée ou théoriquement correcte une activité destructrice : un bon test est un essai qui trouve des erreurs. une activité centrale dans le processus de développement

La réalisation

Annie Culet

70

Test et qualité du logiciel
Le test, une activité non valorisante ? La majorité des logiciels ne sont pas suffisamment testés. Le test n’est pas du debugging: le test ne doit pas être fait par les développeurs (sauf tests unitaires) son objectif n’est pas de localiser les erreurs, ni de les corriger L’objectif du test n’est pas de montrer que le logiciel ne comporte plus d’erreurs il restera toujours des erreurs ! le test est un moyen d’atteindre un niveau de qualité du logiciel. Celui-ci dépend du contexte d ’utilisation du logiciel. Le test exhaustif d’un logiciel est impossible.

La réalisation

Annie Culet

71

Coût du test
Analyse Conception 40%
Test Installation 40%

En moyenne, on trouve entre 100 et 150 erreurs pour chaque millier de lignes de code écrites à la main.

Codage 20% L’activité de test composante à part entière dans le cycle de développement d’un logiciel

Une grande partie des activités de test est effectuée en fin de développement et plus une faute est détectée tard, plus elle coûte cher… dans un contexte où les testeurs accumulent tous les retards pris par les phases précédentes.

La réalisation

Annie Culet

72

Niveaux de test et cycle de vie
Plans de tests Résultats des tests

Expression des besoins Spécification

Tests de recette Tests de validation

Conception globale

Tests d’intégration

Conception détaillée Codage
La réalisation

Tests unitaires

Annie Culet

73

Niveaux de test
Test unitaire test d’un composant du logiciel pris isolément (par ex. une procédure, un module, ...) Test d’intégration succession de tests dans laquelle les composants sont progressivement assemblés en suivant la relation d’utilisation. Vérification des spécifications fonctionnelles et techniques. Test des interfaces entre composants. Test de validation vérification de la conformité du produit final aux spécifications. Test de recette test dans les locaux de l’acquéreur pour vérifier que les dispositions contractuelles sont bien vérifiées.

La réalisation

Annie Culet

74

Test de non régression
Problématique : "Je ne comprends pas. Hier ça marchait …" Il est très souvent impossible de répondre. La non régression du code (une nouvelle version du code est au moins aussi bonne que la précédente) est la certitude que l’on avance. Test de non régression : test effectué suite à une modification afin de montrer que l’ensemble n’a pas été affecté (ex. par un effet de bord non prévu). Comment ? : tester la nouvelle version du composant ou d’un ensemble de composants avec le jeu de test précédent. Quand? : tests unitaires, test d’intégration

La réalisation

Annie Culet

75

Tests statiques / dynamiques
Statiques inspection : examen du code sans exécution par des personnes n’ayant pas participé au codage méthode efficace et peu coûteuse nécessaire mais pas suffisante ne permet pas de valider le comportement du logiciel risque de provoquer des tensions Dynamiques exécution du programme avec un jeu de test Ces 2 méthodes sont complémentaires

La réalisation

Annie Culet

76

Test dynamique : familles
Test fonctionnel (test de boite noire) à partir des spécifications (informelles, semi-formelles, formelles). L’implémentation des composants n’est pas connue (le code n’est pas utilisé pour sélectionner les données de test). objectif : mettre en évidence un écart entre le comportement réel et le comportement spécifié d’un programme ou d’un composant. Test structurel (test de boite blanche) à partir de la structure interne du programme. C’est l’implémentation qui est testée. objectif : couvrir tous les ‘chemins’ possibles dans le code afin mettre en évidence des erreurs dans l’exécution du programme. Test de performance évaluation des performances d'un système par simulation des conditions réelles d'utilisation Test IHM (interface homme-machine) test du comportement de l’interface (menu, fenêtre, navigation ...)
La réalisation Annie Culet

77

Test dynamique : étapes
Formulation d’une stratégie de test niveaux de test à appliquer et leurs objectifs méthodes, techniques, outils Définition des plans de test quels composants, dans quel ordre ? comment appliquer la stratégie ? (tests des versions successives, critères d’arrêt des tests, …) Description de fiches de test (valeurs à l’entrée du test, sorties attendues) Exécution des fiches de test spécifiées Observation des résultats

La réalisation

Annie Culet

78

Les difficultés du test
Comment déterminer les jeux de test pertinents ? sélection des tests assurant un bon échantillonnage des cas possibles (entrées valides, invalides, inattendues, aux limites) : couverture du domaine des entrées. Comment déterminer les oracles des tests ? décider si le test est un échec ou un succès (rôle de l ’oracle) comparaison des résultats observés avec ceux attendus (les résultats - valeurs, propriétés- ne sont pas forcément facilement accessibles …) Comment décider de l’arrêt des tests ? impossible de tester un système de façon exhaustive nécessité de définir des critères d’arrêt

La réalisation

Annie Culet

79

Méthode fonctionnelle (1)
Tests boite noire : tests conçus à partir de la spécification du programme (la structure du programme est inconnue) : Pour chaque fonctionnalité requise d’un programme, sélection d’un jeu de tests Objectif : tester tous les comportements du programme afin de détecter les écarts par rapport au comportement spécifié (fonctions non conformes ou manquantes) . Vérifier les critères qualité (sécurité, portabilité, …)

Difficulté de mettre en œuvre des méthodes systématiques lorsque les spécifications ne sont pas écrites dans un langage formel.

La réalisation

Annie Culet

80

Méthode fonctionnelle (2)
1. A partir de la spécification, identification des fonctionnalités élémentaires pouvant être testées séparément.

Exemple : programme réalisant les opérations courantes sur un compte bancaire Fonctionnalités : créditer (compte, montant) retourner solde débiter (compte, montant) retourner solde consulter (compte) retourner solde -- solde : solde du compte -- montant : montant de dépôt ou du retrait

La réalisation

Annie Culet

81

Méthode fonctionnelle (3)
2. Pour chaque fonctionnalité, sélectionner un jeu de tests garantissant une bonne couverture du domaine des entrées : dans les limites fonctionnelles, hors limites, aux limites. débiter (compte : COMPTE, montant : réel+ ) retourner solde : réel contraintes : solde >= solde-min du compte (découvert maximum autorisé) montant <= montant-max du compte (retrait maximum autorisé) conditions préalables : compte ∈ COMPTE ; solde initial >= solde-min précondition : montant<=montant-max traitement solde final := solde initial - montant

postcondition : solde final >= solde-min

La réalisation

Annie Culet

82

Méthode fonctionnelle (4)
Sélection du jeu de test : Découper le domaine des entrées en classes d’équivalence de façon à ce que chaque classe corresponde aux valeurs faisant l’objet d’un traitement différent dans la spécification. Pour chaque classe : sélectionner un élément déterminer la valeur des sorties attendues

valeur non valide valeur spéciale montant

valeur valide valeur non valide

x x
0

x
Annie Culet

x
montant-max

réel

Domaine du montant du retrait
La réalisation

83

Méthode fonctionnelle (5)
Rajouter les valeurs aux bornes des entrées (limites)

valeur non valide valeur spéciale montant

valeur valide extrême valeur valide valeur non valide

x x
0

x

x x
montant-max

réel

Domaine du montant du retrait

opération refusée solde initial

valeur valide extrême

valeur valide

x
solde-min

x

x
Solde initial - montant = solde-min
Annie Culet

x

réel

Domaine du solde
La réalisation

84

Méthode fonctionnelle (6)
Solde init Solde-min montant max-retrait Solde final 500 500 500 500 500 -300 -200 100 200 Cas mont < 0 mont = 0 normal limite non valide non valide non valide limite valide Résultat attendu refus refus OK OK refus refus refus OK OK

-300 -300 -300 -300 -300 -300 -300 -300 -300

-200 0 300 700 900 400 400 400 400

700 700 700 700 700 700 700 700 700

500 500 200 -200 500 -300 -200 -300 -200

La réalisation

Annie Culet

85

Méthode fonctionnelle (7)
Solde initial
500

OK refus

Domaine de valeurs valides
200 100

2 jeux de valeurs pour le même cas

-200

0

300 400

700

900

montant

-200

-300
La réalisation Annie Culet

86

Quand écrire les tests unitaires?
L'une des pratiques les plus en vogue aujourd'hui, l'eXtreme Programming, nous dit de les écrire avant même de commencer à coder. Avant d'écrire un composant avec des défauts d'abord se demander ce que le composant de programme doit faire et écrire un programme de test puis réaliser le composant et donc constater que celui-ci a des défauts parce que le test unitaire ne passe pas. Mais …. Avantages réfléchir aux pré et post conditions préciser les règles de validité de paramètres éviter d’écrire du code inutile

La réalisation

Annie Culet

87

AUnit
Aunit : Framework* de tests unitaires et de non régression ensemble de modules Ada de la famille xUnit (Junit, Cunit, ...) facilite la création et l’exécution de tests pour des applications Ada beaucoup plus qu’une alternative aux "println" ! Principe : Tests avec entrées normales, limites, hors limites et comparaison avec résultats attendus

*Framework : ossature générale d’une application offrant une partie commune de traitements et que chaque utilisateur personnalise pour son cas particulier.

La réalisation

Annie Culet

88

Aunit : la non régression
Cas de test (Test_Case) module contenant un ensemble de procédures de test + Set_Up : procédure exécutée avant l’appel d ’une procédure de test + Tear_Down : procédure exécutée après l’exécution d’une procédure de test Suite de tests (Test_Suite) empilement de Test_Case Lancement des tests (Test_Run) lancement d’une suite de tests avec édition d’un rapport de test Test_Suite Test_Case
Proc. de test Set_Up Proc. de test Proc. de test Tear_Down

T_C
P P S_U P T_D

T_C
P S_U P P

P

T_D

La réalisation

Annie Culet

89

Test_Case (1)
with AUnit.Test_Cases.Registration; use AUnit.Test_Cases.Registration; with AUnit.Assertions; use AUnit.Assertions; -- modèle pour un module cas de test package body p_XXXX is -- préparation : exécutée avant chaque procédure de test procedure Set_Up (T : in out Test_Case) is begin -- effectuer toutes les initialisations nécessaires null; end Set_Up; -- exécutée après chaque procédure de test procedure Tear_Down (T : in out Test_Case) is begin -- remettre au propre l’environnement pour la procédure de test suivante null; end Tear_Down;

La réalisation

Annie Culet

90

Test_Case (2)
-procédure de test. Autant de procédures que nécessaires procedure Test1 (R : in out AUnit.Test_Cases.Test_Case'Class) is begin -- le corps de la procédure de test null; -- teste les résultats attendus -- plusieurs assertions et actions sont possibles Assert (boolean_Expression, "indication de l’erreur"); end Test1;

Les assertions : Aunit.Assertions.Assert(boolean_Expression, description); description : chaîne éditée dans le rapport lorsque l’expression booléenne est fausse

Exemple : dans une procédure de test de l’opération addition
x:= 12; y := 14; assert (x + y = 26, "l’addition est incorrecte");

La réalisation

Annie Culet

91

Test_Case (3)
-Enregistrement des procédures de test procedure Register_Tests (T : in out Test_Case) is begin -- à répéter pour chaque procédure de test Register_Routine (T, Test1'Access,"nommage de la procédure de test"); end Register_Tests; -Identification du cas de test

function Name (T : Test_Case) return String_Access is begin return new String'("nom du cas de test"); end Name; end p_XXXX;

La réalisation

Annie Culet

92

Suite de tests
with AUnit.Test_Suites; use AUnit.Test_Suites; -- Liste des cas de test à empiler with p_XXXX; with p_YYYY; function Une_Test_Suite return Access_Test_Suite is Result : Access_Test_Suite := new Test_Suite; begin -- ajout des cas de test Add_Test (Result, new p_XXXX.Test_Case); Add_Test (Result, new p_YYYY.Test_Case); return Result; end Une_Test_Suite;

La réalisation

Annie Culet

93

Lancement d’une suite de tests
with AUnit.Test_Runner; -- Suite de tests with Une_Test_Suite; procedure Test_Run is procedure Run is new AUnit.Test_Runner (Une_Test_Suite); begin Run (Timed => False); end Test_Run ;

Note : Il est possible d ’empiler plusieurs suites de tests. Cette possibilité n ’est pas illustrée dans ce cours.

La réalisation

Annie Culet

94

Exemple
Opération de débit sur un compte bancaire-1
Les contraintes : si ces conditions ne sont pas satisfaites, le compte n’est pas débité. le découvert maximum autorisé sur le compte est de 300 € le retrait maximum autorisé est de 700 €.

subtype tcompteNumero is string (1 .. 10) ; subtype tsolde is float ; subtype tretrait is float range 0.0 .. float’last ; type tcompte is record numero : tcompteNumero ; solde : tsolde ; end ; procedure debiter(compte:in out tcompte; montant:in tretrait); -- condition préalable à l’opération : compte.solde >= -300 -- précondition de l’opération : montant <= 700 -- postcondition de l’opération : compte.solde >= -300 ExRetraitImpossible:exception ; -- levée dans tous les cas où le retrait est refusé ; le solde reste identique. procedure initialiserCompte (compte:in out tcompte; montant:in tsolde); -- initialise le solde du compte avec montant (>=-300)
La réalisation Annie Culet

95

Exemple
Opération de débit sur un compte bancaire-2
package body p_TestDebitSoldeInitConstant is leCompte : tcompte; procedure Set_Up (T : in out Test_Case) is begin initialiserCompte (leCompte, 500); end Set_Up; procedure Tear_Down (Test : in out Test_Case) is begin null; end Tear_Down; function Name (T : Test_Case) return String_Access is begin return new String'("Test de l’opération de débit en faisant varier le retrait"); end Name;

La réalisation

Annie Culet

96

Exemple
Opération de débit sur un compte bancaire-3
-- cas normal. Retrait possible procedure test_debitOk1 (T : in out AUnit.Test_Cases.Test_Case'Class) is begin debiter (leCompte, 300) ; assert (leCompte.solde=200, “débit non correctement effectué”) ; exception when ExRetraitImpossible => assert (false, “le retrait devrait être effectué”) ; end test_debitOk1; -- cas où le montant du retrait dépasse le maximum autorisé procédure test_debitRefus1 (t : in out Aunit.Test_Cases.Test_Case’Class) is begin debiter (leCompte, 900) ; assert (false, “montant retrait trop important non traité”) ; exception when ExRetraitImpossible => assert (leCompte.solde=500,“le solde devrait rester identique”); end test_debitRefus1;

La réalisation

Annie Culet

97

Exemple
Opération de débit sur un compte bancaire-4
-- cas où le montant du retrait est égal au maximum autorisé procédure test_debitLimite1 (t : in out Aunit.Test_Cases.Test_Case’Class) is begin debiter (leCompte, 700) ; assert (leCompte.solde = -200, “débit non correctement effectué”) ; exception when ExRetraitImpossible => assert (false, “le retrait devrait être effectué”) ; end test_debitLimite1; procedure Register_Tests (T : in out Test_Case) is begin Register_Routine(T,Test_DebitOk1'Access, "Test retrait cas normal"); Register_Routine(T,Test_DebitRefus1'Access, "Test retrait trop important"); Register_Routine(T,Test_DebitLimite1'Access,"Test retrait limite autorisée"); end Register_Tests;

end p_TestDebitSoldeInitConstant;
La réalisation Annie Culet

98

Exemple
Opération de débit sur un compte bancaire-5
package body p_TestDebitRetraitConstant is
leCompte : tcompte; retrait : tretrait ; procedure Set_Up (T : in out Test_Case) is begin retrait := 400; end Set_Up; procedure Tear_Down (Test : in out Test_Case) is begin null; end Tear_Down; function Name (T : Test_Case) return String_Access is begin return new String'("Test de l’opération de débit en faisant varier le solde initial"); end Name;

La réalisation

Annie Culet

99

Exemple
Opération de débit sur un compte bancaire-6
-- cas où le montant du découvert autorisé est dépassé procédure test_debitRefus2 (t : in out Aunit.Test_Cases.Test_Case’Class) is begin initialiserCompte (leCompte, -200); debiter (leCompte, retrait) ; assert (false, “ cas découvert non autorisé non traité ”) ; exception when ExRetraitImpossible => assert (leCompte.solde = -200, “ le solde devrait rester identique ”) ; end test_debitRefus2; -- cas où le montant du découvert autorisé est atteint procédure test_debitLimite2 (t : in out Aunit.Test_Cases.Test_Case’Class) is begin initialiserCompte (leCompte, 100); debiter (leCompte, retrait) ; assert (leCompte.solde = -300, “débit non correctement effectué”) ; exception when ExRetraitImpossible => assert (false, “le retrait devrait être effectué”) ; end test_debitLimite2;
La réalisation Annie Culet

100

Exemple
Opération de débit sur un compte bancaire-7
-- cas normal : le montant du découvert n’est pas dépassé procédure test_debitOk2 (t : in out Aunit.Test_Cases.Test_Case’Class) is begin initialiserCompte (leCompte, 200); debiter (leCompte, retrait) ; assert (leCompte.solde = -200, “débit non correctement effectué”) ; exception when ExRetraitImpossible => assert (false, “le retrait devrait être effectué”) ; end test_debitOk2; procedure Register_Tests (T : in out Test_Case) is begin Register_Routine(T,Test_DebitOk2'Access, "Test cas normal"); Register_Routine(T,Test_DebitRefus2'Access, "Test découvert non autorisé"); Register_Routine(T,Test_DebitLimite2'Access,"Test découvert limite "); end Register_Tests;

end p_TestDebitRetraitConstant;
La réalisation Annie Culet

101

Méthodes structurelles (1)
tests boite blanche : tests conçus à partir de la structure du code (flot de contrôle, flot de données): Objectif : détecter les fautes d’implémentation Couverture du flot de contrôle toutes les instructions, conditions, chemins exécutables, … Couverture du flot de données toutes les définitions de variables, leurs utilisations, …

La réalisation

Annie Culet

102

Méthodes structurelles (2)
But : produire des données de test qui exécuteront un ensemble de comportement du programme a Un programme => un graphe de contrôle x>0 x<=0 begin if (x<=0) then x:= -x else x:= 1-x; if (x=-1) then x:=1 else x:=x+1; end
x:= -x b c x:= 1-x

d x=-1 x:=1 f x!=-1 e x:=x+1

Graphe orienté : g un noeud : un bloc d’instructions un arc : la possibilité de transfert de l’exécution d’un noeud à un autre Une exécution possible = un chemin dans le graphe de contrôle acdeg est un chemin de contrôle bdfg n’est pas un chemin de contrôle
La réalisation Annie Culet

103

Méthodes structurelles (3)
L’ensemble des chemins de contrôle du graphe = abdfg + abdeg + acdfg + acdeg
x:= -x x<=0 b a x>0 c x:= 1-x

d

Construction de l’expression des chemins :
x:=1

x=-1 f g

x!=-1 e x:=x+1

séquentielle
a

alternative
a

itérative
a c b

b b d

c

ab
La réalisation

abd + acd

a(ba)*c
Annie Culet

104

Méthodes structurelles (4)
Chemin exécutable – non exécutable
x<=0 a x>0 c x:= 1-x

Donnée de test (DT1) : { x=2 } DT1 sensibilise le chemin acdfg : acdfg est un chemin exécutable abdfg est un chemin non exécutable : aucune DT capable de sensibiliser ce chemin sensibiliser un chemin : problème difficile intérêt des outils automatiques (mais attention problème de trouver des DT qui sensibilisent un chemin est non décidable) Existence de chemins non exécutables : souvent signe de mauvais codage

x:= -x

b

d x=-1 x:=1 f g x!=-1 e x:=x+1

La réalisation

Annie Culet

105

Méthodes structurelles (5)
Hiérarchie des techniques de tests structurels Couverture de tous les noeuds sensibiliser tous les chemins de contrôle qui nous permettent de visiter tous les nœuds du graphe de contrôle. Couverture de tous les arcs sensibiliser tous les chemins de contrôle qui nous permettent de visiter tous les arcs du graphe de contrôle. « tous les arcs» est plus fiable (plus fort) que «tous les nœuds» Couverture de tous les chemins critère le plus fort : impossible à appliquer dès que le code contient un boucle (+ problème des chemins non exécutables)

La réalisation

Annie Culet

106

Méthodes structurelles (6)
Le critère de sélection du jeu de tests repose sur le code du programme Limitations : un jeu de tests de taille raisonnable couvrant tous les chemins exécutables est utopique les oublis par rapport à la spécification ne sont pas détectés lors d’une modification du programme, difficile de réutiliser les tests précédents (problème de non-régression) Intérêt : quantifiable, automatisable nombreux outils disponibles (Logiscope Verilog, Attol, ...)

La réalisation

Annie Culet

107