• Aucun résultat trouvé

2.2 Résolution d’un système de contraintes sur des domaines finis

2.2.2 Énumération des variables

Quand la propagation de contraintes s’achève, c’est-à-dire quand tous les domaines des variables sont stables, l’énumération des valeurs possibles pour les variables est géné- ralement requise pour obtenir une solution. La procédure d’énumération essaie d’affecter une valeur à chacune des variables, l’une après l’autre. Quand une valeur est choisie dans son domaine pour une variable, la propagation de contraintes est relancée pour réduire le domaine des autres variables sous l’hypothèse courante. Si une contradiction apparaît au cours du processus de résolution (si le système devient insatisfiable), la procédure revient en arrière (backtrack) pour tester d’autres valeurs. Le processus s’arrête quand la file de contraintes est vide et qu’une valeur est assignée à chaque variable ce qui forme une solu- tion. Si aucune solution n’est trouvée après combinaison de toutes les valeurs des domaines des variables, le système de contraintes n’a pas de solution : il est insatisfiable.

Différentes stratégies d’énumération sont possibles dans le but d’obtenir une solution rapidement. En effet, l’ordre dans lequel sont considérées les variables pour leur affecter une valeur, ainsi que l’ordre dans lequel sont affectées les valeurs de son domaine à une va- riable, peuvent influencer le temps de résolution. Certaines stratégies d’énumération sont indépendantes du système de contraintes à résoudre, comme la stratégie qui consiste à considérer d’abord les variables ayant le plus petit domaine. Des stratégies d’énumération propres au problème à résoudre peuvent aussi s’avérer bénéfiques.

Exemple 3. Soit le système de contraintes utilisé dans l’exemple 2. A l’issue de la propagation, dom(X) = {3, 4}, dom(Y ) = {2, 3} et dom(Z) = {1}. L’énumération a pour but de fixer des valeurs à X et Y (Z étant déjà instancié) telles que les contraintes X > Y et Y > Z soient satisfaites. Supposons que l’énumération considère d’abord X puis Y , et que la première valeur affectée à X soit 3. En ce cas, la propagation de contraintes est relancée comme illustré sur la figure 2.4. La contrainte X > Y est la seule restant en file donc la seule à considérer, elle permet de supprimer la valeur 3 du domaine de Y (étape

38 chapitre2

Etape File Contraintes Contraintes Domaines des variables suspendues impliquées

0 X > Y Y > Z X ∈ {3} , Y ∈ {2, 3} , Z ∈ {1} 1 X>Y Y > Z X ∈ {3} , Y ∈ {2} , Z ∈ {1} 2 Y > Z,X > Y X ∈ {3} , Y ∈ {2} , Z ∈ {1}

Fig. 2.4 – Illustration de l’énumération

1) puis passe dans l’état impliquée (étape 2). Les domaines des trois variables sont réduits à des singletons et plus aucune contrainte ne reste à considérer, X = 3, Y = 2, Z = 1 est donc une solution du système.

Deuxième partie

Modélisation par contraintes du

bytecode Java pour la génération de

données de test

Chapitre 3

Intérêt et aperçu du modèle à

contraintes d’une JVM

Dans le but de contribuer à l’automatisation de la génération de données de test pour couvrir un programme en bytecode Java, cette thèse propose un modèle à contraintes d’une JVM (Java Virtual Machine, permettant d’exécuter le bytecode). Après un rappel des contributions de cette thèse, l’intérêt d’un tel modèle à contraintes pour attaquer cette problématique de test est présenté. Un aperçu des déductions que le modèle à contraintes proposé permet d’obtenir sur les données de test à soumettre à un programme est ensuite montré, suivi d’un aperçu plus détaillé des contraintes menant à ces déductions. Enfin les difficultés principales pour la conception du modèle à contraintes sont exposées.

3.1

Rappel des contributions

Voici les principales contributions de cette thèse.

La première contribution est la définition d’un modèle à contraintes de la sé- mantique du bytecode Java. Étant donnée une séquence d’instructions, le modèle a pour but de permettre la détermination d’un état de la mémoire en début de séquence, s’il en existe, tel que celle-ci soit exécutée. Il ne nécessite pas de faire d’hypothèse a priori sur la forme de la mémoire et les valeurs numériques, mais les infère à partir des instructions conditionnelles de la séquence d’instructions. La modélisation par contraintes du bytecode Java a nécessité d’une part la définition d’un modèle de la mémoire, décrit à la section 4.2, et de nouvelles contraintes, décrites au chapitre 5, traduisant le lien entre les états de la mémoire avant et après l’exécution de chacune des instructions. L’utilisation de variables pour modéliser le type des objets permet de prendre en compte l’héritage et le polymor- phisme. Comme cela sera montré dans ce manuscrit, notre modèle est très déductif car il permet d’inférer des formes de mémoire complexes pour satisfaire les contraintes posées. Il ne fait pas de sous-approximations, y compris en présence d’expressions non linéaires et de déréférencements multiples (par exemple p.left.parent). Les états de la mémoire obtenus à l’issue de la propagation de contraintes sont une sur-approximation des états de la mémoire possibles. Ainsi, aucun état de la mémoire en entrée qui permettrait l’exécution de la séquence d’instructions choisie n’est indûment écarté.

La deuxième contribution est une méthode de génération automatique de don- nées de testpour le bytecode Java„ détaillée au chapitre 6. Cette méthode vise à couvrir des instructions particulières non couvertes par d’autres méthodes de test. La méthode

42 chapitre 3

de génération proposée raisonne dans le sens inverse à celui de l’exécution : en partant d’un objectif (une instruction à couvrir) elle essaie de trouver un chemin menant vers le point d’entrée du programme en explorant progressivement et à l’envers le graphe de flot de contrôle, puis de déterminer une donnée d’entrée qui active un tel chemin. Pour ce faire, elle exploite le modèle à contraintes du bytecode. Le sens de parcours "en arrière" du graphe de flot de contrôle est original, et favorise la couverture d’instructions non couvertes par d’autres outils de test qui utilisent une exploration en avant.

La troisième contribution est un prototype, JAUT, mettant en application le modèle et la méthode de génération proposés et permettant ainsi une validation expérimentale. L’outil est décrit au chapitre 8. Les expériences, décrites au chapitre 9, montrent que JAUT permet d’augmenter la couverture des instructions obtenue avec les autres outils disponibles [CG10], en particulier Pex qui est une référence dans le domaine.