L’informatique a une importance grandissante dans notre vie. Tous nos équipements tendent à être de plus en plus « intelligents ». De nombreux logiciels fonctionnent sur ces équipements. Il est important de s’assurer de la correction de ces logiciels par rapport à leur spécification, pour qu’ils puissent être utilisés en toute confiance. C’est là qu’intervient le génie logiciel, l’application d’une approche systématique, disciplinée, et mesurable à la conception, au développement, à l’exploitation, à la maintenance, et à l’évaluation de logiciels.
Le test logiciel est une des activités du génie logiciel qui vise à vérifier et à valider des logiciels. Tester un programme consiste à rechercher les fautes qu’il peut contenir. Pour cela, le testeur va écrire puis exécuter des cas de test qui produiront chacun un verdict. Le verdict d’un cas de test est son résultat sous forme booléenne, un cas de test « passe » ou il « échoue ». Un cas de test passe si le résultat obtenu après exécution du programme sous test est correct par rapport à la spécification du programme, sinon il échoue. La spécification d’un programme est l’ensemble des exigences que ce programme doit satisfaire. Un cas de test est principalement composé de trois éléments : une initialisation, qui conduit le programme sous test dans un état souhaité ; des données de test qui serviront d’entrée du programme sous test ; et un oracle. L’oracle est un programme chargé de produire le verdict. Un oracle est composé d’une donnée d’oracle et d’une fonction d’oracle. La fonction d’oracle analyse le résultat obtenu par l’exécution du programme sous test et utilise la donnée d’oracle pour produire le verdict.
L’Ingénierie Dirigée par les Modèles (IDM) est une technique qui place les modèles au cœur des différentes activités du développement logiciel. Un modèle est une abstraction d’un système, il peut être utilisé pour décrire sa structure ou son comportement par exemple. Cette abstraction permet notamment aux développeurs de ne pas prendre en compte les détails techniques de la mise en œuvre. Ils peuvent ainsi se concentrer sur les aspects fonctionnels. La réutilisation de modèles existants est également facilitée par cette abstraction. Un modèle est écrit dans un langage particulier, ce langage est décrit par un méta-modèle. Un modèle est conforme à un méta-modèle. Une machine à états hiérarchique permet par exemple de modéliser le comportement d’un système. Elle décrit les différents états dans lesquels peut se trouver le système et les transitions qui entrainent un changement d’état.
Les modèles ne sont pas figés, ils évoluent au fil de leur utilisation, ils peuvent par exemple être combinés pour en obtenir de nouveaux. Le développeur peut également simplifier un modèle en enlevant certains détails. C’est le rôle des transformations de modèles, qui sont automatisées. Cette automatisation est un élément essentiel de l’IDM, pour pouvoir réutiliser ces transformations un grand nombre de fois. Une transformation de modèles peut, par exemple, réaliser la mise à plat de la machine à états hiérarchique. Le modèle d’entrée de la transformation est une machine à états hiérarchique, le modèle de sortie est une nouvelle machine à états décrivant le même comportement sans utiliser d’états composites.
Avant de pouvoir la réutiliser de multiples fois, une transformation de modèles doit être validée. Une solution pour valider une transformation est de la tester. Dans le contexte de l’IDM, la donnée de test ou modèle de test, est un modèle d’entrée de la transformation sous test. Le modèle obtenu est produit par l’exécution de la transformation sous test sur le modèle de test. L’oracle contrôle la correction de ce modèle obtenu et fournit le verdict du cas de test. Si le modèle obtenu est conforme à la spécification de la transformation, le test passe, sinon il échoue.
L’IDM est une technique de plus en plus répandue. Le besoin de disposer de transformations de modèles validées est grandissant, le test de transformations de modèles est le sujet de nombreuses études. Ces études portent sur la génération et la sélection de modèles de test, l’oracle, ou encore la mise au point de frameworks dédiés au test de transformations de modèles.
Dans cette thèse nous nous intéressons à l’étude de l’oracle. Contrairement à la génération et la sélection de modèles de test [Fleurey et al., 2009], peu d’études ont été consacrées à l’oracle. Il a pourtant été reconnu que l’oracle était une composante importante du test, et qu’il ne doit plus être négligé dans les études à venir [Bertolino, 2007, Staats et al., 2011].
Actuellement, il n’est pas possible de générer de manière automatique (sauf si le testeur dispose d’une spécification formelle de la transformation sous test) un oracle pour un cas de test. Nous pouvons envisager d’assister le testeur dans cette tâche essentiellement manuelle. Cette assistance peut être passive, et prendre la forme de fonctions d’oracles adaptées à ses besoins. Nous pouvons aussi envisager une assistance plus active en lui fournissant des indicateurs de la qualité de ses oracles ainsi que des pistes pour les améliorer. Ces pistes sont exploitées dans cette thèse.
Il existe des fonctions d’oracle spécifiques aux transformations de modèles [Mottu et al., 2008]. Plusieurs de ces fonctions d’oracles utilisent la comparaison de modèles. Une autre utilise des contrats, ces contraintes exprimées sur le modèle d’entrée, sur le modèle obtenu ou entre les modèles d’entrée et de sortie. A partir de là, nous nous posons la question A :
– Les fonctions d’oracles existantes sont-elles adaptées à tous les besoins du testeur ?
Le testeur peut choisir de ne contrôler qu’une partie du modèle obtenu ; parfois, ce choix lui est imposé. C’est par exemple le cas s’il n’est pas capable de prévoir pour un coût raisonnable la totalité du modèle attendu mais seulement une partie, c’est-à dire un modèle attendu partiel. Ce modèle attendu partiel peut être produit par une précédente version de la transformation sous test. Dans un autre cas de figure, la transformation sous test a des sorties polymorphes ; il existe plusieurs modèles de sortie valides pour un modèle d’entrée donné. Ici, le modèle attendu partiel serait la partie commune à toutes les variantes du modèle de sortie. La mise à plat d’une machine à états hiérarchique est un exemple d’une telle transformation de modèles. Suivant le modèle d’entrée fourni, il peut exister plusieurs variantes du modèle de sortie, avec un nombre d’états finaux différent. Dans ce cas le testeur peut vouloir contrôler ce modèle de sortie obtenu sans tenir compte des états finaux. Dans un dernier cas de figure, si la transformation sous test effectue un ré-usinage, alors une partie du modèle d’entrée sera à l’identique dans le modèle de sortie. Dans ce cas, le testeur peut utiliser le modèle d’entrée comme modèle attendu pour contrôler la partie du modèle obtenu non modifiée par la transformation.
La fonction d’oracle la plus simple compare un modèle attendu au modèle obtenu, pour le contrôler entièrement. Le testeur doit donc obtenir d’une manière ou d’une autre, un modèle attendu entier. Si le modèle utilisé comme modèle attendu ne contient la valeur attendue que pour une partie seulement des éléments du modèle de sortie il s’agit d’un modèle attendu partiel. Si le modèle obtenu est comparé à un modèle attendu partiel, des différences seront observées à propos de la partie non présente dans le modèle attendu partiel et le test échouera. Pour utiliser cette fonction d’oracle avec une transformation aux sorties polymorphes, le testeur devrait utiliser pour chaque cas de test autant de modèles obtenus qu’il existe de variantes valides du modèle de sortie. Cela serait trop coûteux. Les fonctions d’oracles utilisant des contrats ne sont pas non plus appropriées pour obtenir un tel verdict partiel. Écrire ces contrats est aussi complexe que de développer la transformation, et le risque d’erreur est aussi élevé que pour la transformation sous test.
Les fonctions d’oracles existantes n’étant pas adaptées pour contrôler uniquement une partie d’un modèle obtenu, nous en proposons une nouvelle. Nous utilisons une comparaison de modèles filtrée. Nous comparons les modèles puis nous filtrons le résultat de cette comparaison pour retirer une partie des différences observées. Le testeur compare le modèle obtenu avec un modèle attendu, partiel ou non. Il filtre ensuite le résultat de cette comparaison pour retirer toutes les différences observées à propos de la partie du modèle obtenu qui ne l’intéresse pas. Le test passe si aucune différence n’est présente dans le résultat filtré de cette comparaison, sinon il échoue.
1 Introduction |