5.3 Expérimentations pour le développement d’un composant de
5.3.4 Étape 3: Améliorer les contrats
Pendant cette troisième étape, nous mettons en œuvre une méthode d’amélioration de la spécification embarquée, sous sa forme de contrats exécutables (figure 5-16). Il s’agit de mettre à l’épreuve ces contrats avec les mutants. Pour cela ils sont utilisés comme oracle de l’analyse de mutation. Comme nous savons que les modèles de test sont capables de déclencher les erreurs injectées dans les mutants (score de 100% à l’étape précédente), nous pouvons mesurer la capacité des contrats à détecter ces erreurs. L’amélioration des contrats consiste à créer des contrats qui traduisent la spécification le plus complètement possible.
Implantation
Spécification
Cas de test
Qualification AméliorationFigure 5-16 - Qualification et amélioration du troisième sommet.
Les mutants qui étaient tués avec les modèles de test doivent être tués en utilisant les contrats. En revanche, les mutants équivalents ne doivent pas être tués par les contrats. Leur utilisation permet de contrôler l’exactitude des contrats qui sont complexes à écrire. Si un nouveau contrat est violé en utilisant les mutants équivalents, une analyse est nécessaire. Le contrat peut être erroné, mais il peut aussi mettre en évidence une erreur de la transformation. Dans ce cas une nouvelle itération est réalisée en utilisant ce(s) nouveau(x) contrat(s) à l’étape 2.
a- Un ensemble initial de contrats
Initialement, les contrats peuvent provenir de deux sources: Des contrats compris dans la spécification
Les contrats disponibles dans l’implantation quand le développement a été réalisé avec une méthodologie design-by-contract
Dans notre expérience, la spécification du workshop [Bézivin'05] fournit un contrat (en OCL) qui précise le domaine d’entrée de la transformation :
131 Expérimentations pour le développement d’un composant de confiance
context Class inv:
allAttributes()->size > 0 and
allAttributes()->exists(attr | attr.is_primary = true)
Nous pouvons utiliser ce contrat directement en Kermeta. Il suffit d’implémenter la méthode
allAttributes() en Kermeta.
Les opérations de l’implantation comportent également huit contrats (deux pré-conditions et six post-conditions). A ce moment, nous considérons que nous avons des contrats basiques (niveau 1) dans notre composant. La qualité (Qcont, mesurée comme défini au 5.1.3) de ces neuf contrats est de 59%. Ce résultat est représenté dans la première colonne de la figure 5-17.
93.8% 87.1% 59.3% 86.6% 92.3% 50% 60% 70% 80% 90% 100% niveau 1 9 contrats niveau 2 17 contrats niveau 3 18 contrats niveau 4 19 contrats niveau 5 21 contrats niveau de contrat Qc o n t
Figure 5-17 – Progression du score de mutation pendant l’amélioration des contrats.
b- Amélioration des contrats: introduction de contrats de sémantique comportementale
Nous procédons à l’amélioration des contrats en deux étapes. Dans ce point b-, nous traduisons la spécification en contrats et nous écrivons des contrats de sémantique comportementale (comme définis dans la section 5.1.1). Ensuite dans le point c-, nous améliorons les contrats à l’aide des mutants.
Dans un premier temps, nous avons défini deux contrats du domaine de sortie (des post- conditions). Le méta-modèle cible (figure 2-7) n’est pas assez restrictif concernant les clés des tables: il n’impose pas que les clés primaires d’une table soient parmi ses colonnes et qu’une clé étrangère ait ses colonnes parmi celles de sa table. Nous écrivons deux contrats qui prennent en compte ces contraintes (en Kermeta) :
post pkey_is_in_the_column_of_its_table is
result.table.forAll{t|t.pkey.forAll {pk|pk.container == t}}
post colunms_of_fkey_are_among_the_ones_of_its_table is
result.table.forAll{t|t.fkeys.forAll{fk|fk.cols.forAll{c|t.cols.contains(c)}}} Nous avons également défini six contrats reliant entrée et sortie (des post-conditions). Par exemple, un de ces contrats écrit en Kermeta est donné dans la figure 5-18. Il permet de vérifier que : dans une table correspondant à une classe cl, il y a des colonnes qui correspondent aux attributs cl dont les types t sont des classes persistantes (sans parent persistant) ayant des attributs primaires de type primitif.
Ces contrats de second niveau s’ajoutent aux contrats du premier niveau. En évaluant la qualité de cet ensemble de vingt-et-un contrats, nous mesurons un Qcont de 86% (comme illustré figure 5-17), soit un gain de 27%.
c- Amélioration des contrats grâce aux opérateurs de mutation
Dans un deuxième temps, nous améliorons les contrats pour renforcer leur pouvoir de détection d’erreurs. L’ensemble amélioré des contrats doit traduire au maximum la spécification pour considérer davantage d’exigences, ou de manière plus précise : soit des contrats entièrement nouveaux sont élaborés, soit les existants sont modifiés. Dans ce dernier cas, un nouveau contrat est systématiquement créé pour deux raisons :
La complexité d’une modification ne doit pas corrompre un contrat existant. Il est plus facile de créer de nouveaux contrats, comme nous avons expliqué qu’il est plus facile de créer de nouveaux modèles de test (3.3.1a-).
Chaque contrat considère une partie donnée de la spécification. De cette façon (comme expliqué au chapitre précédent), il peut être possible de réutiliser certains contrats quand la spécification est modifiée.
Comme dans la première étape (section 5.3.1), nous utilisons notre connaissance des erreurs injectées dans les mutants vivants. Exploiter ces erreurs permet de faciliter l’amélioration des contrats. En effet, nous pouvons constater avec l’exemple de la figure 5-18 (mais également dans l’Annexe A - A.3) la complexité des contrats.
133 Expérimentations pour le développement d’un composant de confiance
1 post attribute_persistent_classes is
2 // Création des colonnes correspondant aux attributs dont le type est une 3 // classe persistante transformée en table
4 inputModel.classifier
5 .select{cr| Class.isInstance(cr)}
6 .select{cs | cs.asType(Class).is_persistent}
7 .select{csp | not csp.asType(Class).allParents.exists{p | p.is_persistent}} 8 .forAll
9 {csp | // pour toute classe persistante sans parent persistant,
10 result.table.select{t|t.name == csp.name} //dans la table correspondante, 11 .exists{tn | csp.asType(Class).attrs
12 .select{at | Class.isInstance(at.type)}
13 // les attributs dont le type est une classe 14 .select{atc|atc.type.asType(Class).is_persistent} 15 //qui est persistante
16 .select{atcp | not atcp.type.asType(Class).allParents
17 .exists{p | p.is_persistent}}
18 // mais sans parent persistant
19 .forAll
20 {atcpwp| // (atcpwp est un attribut don’t le type est une classe 21 // persistante sans parent persistant
22 atcpwp.type.asType(Class).attrs
23 .forAll
24 {atcpta| //pour tous les attributs de ce type 25 if PrimitiveDataType.isInstance(atcpta.type) 26 //qui sont de type primitif
27 and atcpta.is_primary // et qui sont primaires
28 then
29 tn.cols.select{ctn |
30 ctn.name == atcpwp.name+"_"+atcpta.name
31 // alors il y a une colonne du nom de cette
32 // attribut + « _ » + le nom de l’attribut
33 // primaire de type primitif
34 and ctn.type == atcpta.type.name
35 // et du type de ce dernier attribut,
36 //to be improved
37 }.size==1 //cette colonne est l’unique correpondante
38 else true end
39 }
40 }
41 }
42 }
Figure 5-18 - Un exemple de contrat de sémantique comportementale
Dans l’expérimentation, nous avons, par exemple, essayé de tuer un mutant qui n’associe pas les colonnes à une clé étrangère (FKey). Nous avons créé un contrat à partir de celui de la figure 5-18 en vérifiant que la colonne considérée ctn est une des colonnes d’une fkey de la table tn. C’est une amélioration qui se traduit par le code de la figure 5-19. Il remplace le commentaire « //to be improved » à la ligne 36 de la figure 5-18 pour créer un nouveau
contrat. L’opérateur de mutation supprime la création d’une relation entre une fkey et ses colonnes, donc le contrat amélioré vérifie l’existence de cette relation.
and tn.fkeys.exists{ftn | ftn.cols.contains(ctn)}
Figure 5-19 - Amélioration d'un contrat
En appliquant cette méthode, nous améliorons la valeur Qcont de l’ensemble des contrats du composant. Nous l’illustrons dans les troisième, quatrième, cinquième colonnes de la figure 5-17. Cette fois, nous obtenons un gain pour Qcont de 7%.