• Aucun résultat trouvé

6.2 D´efinition de contraintes

6.2.4 Exp´erimentations

L’exemple que nous pr´esentons ci-dessous porte sur le gestionnaire de processus. Il a

pour but d’illustrer les propos mis en avant dans la section pr´ec´edente, mais ne pr´etend

pas apporter d’information sur la m´ethodologie d’utilisation du langage de sch´ema de tests.

Notre objectif est de montrer comment, de mani`ere simple, il est possible de s´electionner un

ensemble r´eduit de tests `a partir d’un ensemble plus important et ce, `a l’aide de contraintes.

Nous illustrons aussi cette approche chapitre 8, section 8.5, sur une ´etude de cas plus

im-portante.

Soit le groupe newrdyP12 = {new(x), rdy(x)} avec x = {”p1”,”p2”}. Prenons par

exemple le sch´ema suivant :

NewRdySchema = MTC!Sch1.newrdyP12^1..4

Ce sch´ema se d´ecompose en 340 s´equences de test faisant appel aux op´erations new(x)

et rdy(y). Parmi toutes ces s´equences, certaines sont invalides du point de vue de la

sp´ecification, d’autres peuvent, sous certaines hypoth`eses, ˆetre consid´er´ees comme

redon-dantes, par exemple :

– la s´equence MTC !Sch1.new("p1") ; MTC !Sch1.new("p1") est invalide et,

– si nous faisons l’hypoth`ese que l’ordre dans lequel les new sont effectu´es n’a pas

d’im-portance, des s´equences comme :

MTC !Sch1.new("p1") ; MTC !Sch1.new("p2")

MTC !Sch1.new("p2") ; MTC !Sch1.new("p1")

sont redondantes et peuvent donc ˆetre filtr´ees.

´

Evaluation de l’utilisation de s´equences

Soitxxla variable rattach´ee au param`etre denewetyyla variable rattach´ee au param`etre

rdy. Consid´erons les s´equences suivantes, g´en´er´ees par le sch´ema NewRdySchema :

seq_a = MTC!Sch1.new("p1"); MTC!Sch1.new("p1"); MTC!Sch1.rdy("p1");

MTC!Sch1.rdy("p2")

seq_b = MTC!Sch1.new("p2"); MTC!Sch1.new("p1"); MTC!Sch1.rdy("p1");

MTC!Sch1.rdy("p2")

seq_c = MTC!Sch1.new("p1"); MTC!Sch1.rdy("p1"); MTC!Sch1.new("p2")

Pour ces cas de test, nous construisons respectivement :

– pour seq a :xx = ["p1","p1"] etyy = ["p1","p2"].

– pour seq b :xx = ["p2","p1"] etyy = ["p1","p2"].

– pour seq c :xx = ["p1","p2"] etyy = ["p1"].

Soit la contrainte suivante :

C0 :card(elems(xx))=len(xx)

Cette contrainte v´erifie que le nombre d’´el´ements diff´erents de xx est ´egal `a la

lon-gueur de la s´equence contenue dans xx. De cette fa¸con, on s’assure qu’on ne cr´ee pas

deux fois le mˆeme processus. La s´equence MTC !Sch1.new("p1") ; MTC !Sch1.new("p1") ;

MTC !Sch1.rdy("p1") ; MTC !Sch1.rdy("p2") tombe dans ce cas de figure et est rejet´ee par

la contrainte. Ainsi, avec cette seule contrainte, le nombre de s´equences passe de 340 `a 190.

Trois autres contraintes peuvent ˆetre exprim´ees sur ce sch´ema :

C1 :card(elems(yy))=len(yy).

Cette contrainte, comme la pr´ec´edente, sert `a ´eliminer les cas o`u l’on essaie de mettre deux

fois le mˆeme processus dans l’´etat ready. Dans le cas pr´esent, comme nous n’effectuons pas

d’op´erationswapdans ces s´equences, la pr´econdition derdysera viol´ee entraˆınant un verdict

inconclusif. Par ailleurs, cette contrainte r´eduit le nombre de s´equences de 190 `a 64.

C2 :elems(yy) subset elems(xx).

Cette contrainte ´elimine les cas o`u l’on appelle rdy sur un processus qui n’a pas ´et´e ou ne

sera pas cr´e´e dans la s´equence. Par exemple :

MTC !Sch1.new("p2") ; MTC !Sch1.rdy("p1")).

L`a encore cette contrainte permet d’´eliminer des s´equences de test violant la pr´econdition

de rdy. Le nombre de s´equences passe maintenant de 64 `a 44.

C3 :forall i,j in set inds xx & i<j => string lt1(xx[i],xx[j]).

Cette contrainte compare deux `a deux les valeurs stock´ees dans la s´equence xx. Pour tout

couple d’indices diff´erents dans la s´equencexx, si la premi`ere est positionn´ee avant la seconde

dans la s´equence alors elle doit ˆetre plus petite, au sens lexicographique du terme, que la

seconde. Elle permet donc d’´eliminer les permutations d’appels de l’op´erationnew comme la

s´equence :

MTC !Sch1.new("p2") ; MTC !Sch1.new("p1")

ou encore

MTC !Sch1.new("p2") ; MTC !Sch1.rdy("p2") ; MTC !Sch1.new("p1") ;

MTC !Sch1.rdy("p1").

Cette contrainte exprime l’hypoth`ese de test suivante : l’ordre dans lequel les new sont

effectu´es n’a pas d’importance. Le nombre de tests passe maintenant de 44 `a 25. `A noter que

cette derni`ere contrainte inclut la contrainte C0.

Sur les 25 s´equences restantes, 11 conduisent `a un verdict pass et 14 `a un verdict

incon-clusif.

Comme les contraintes portent sur des s´equences n’enregistrant que les valeurs des

pa-ram`etres, l’ordre des appels d’op´eration n’est pas pris en compte. C’est pourquoi il reste

autant de tests conduisant `a un verdict inconclusif. Comme par exemple :

MTC !Sch1.new("p1") ; MTC !Sch1.rdy("p1") ; MTC !Sch1.rdy("p2") ;

MTC !Sch1.new("p2").

Cette s´equence respecte les quatre contraintes d´efinies ci-dessus : xx=["p1", "p2"] et

yy=["p1", "p2"]. Nous pouvons facilement v´erifier C0 et C1. De mˆeme, yy est bien un

sous ensemble dexx, doncC2est vraie. Pour finir, l’ordre lexicographique est respect´e, donc

la contrainte C3 est aussi vraie.

1

string lt est une fonction qui prend en entr´ee deux s´equences de caract`eres et qui renvoie vrai si la

premi`ere est plus petite que la seconde dans l’ordre lexicographique.

´

Evaluation de l’utilisation de fonctions

Reprenons l’exemple expos´e dans la section pr´ec´edente o`u maintenant xx et yy ne sont

plus de type seq of PID mais de type map nat to PID. Les contraintes pr´ec´edentes

s’ex-priment maintenant de la fa¸con suivante :

– la contrainte C0, qui exprime que toutes les valeurs des appels de l’op´eration new sont

deux `a deux diff´erentes, s’´ecrit maintenant card(dom xx)=card(rng xx),

– la contrainte C1, qui exprime que toutes les valeurs des appels de l’op´eration rdy sont

deux `a deux diff´erentes, s’´ecrit maintenant card(dom yy)=card(rng yy),

– la contrainte C2, qui exprime querdy ne porte que sur des processus cr´e´es `a partir du

sch´ema, peut maintenant s’´ecrire en tenant compte de l’ordre des appels d’op´eration :

forall oprdy in set dom yy & exists opnew in set dom xx &

yy(oprdy) = xx(opnew) and oprdy > opnew

Pour chaque ´el´ement du domaine deyy, c’est-`a-dire pour chaque appel de l’op´eration

rdy, il doit exister un appel `a une op´erationnew, ant´erieure `a l’op´erationrdyconsid´er´ee

et avec un param`etre identique `a celui de l’op´eration rdy.

– la contrainte C3, qui exprime que l’ordre des appels de l’op´eration new n’a pas

d’im-portance, s’´ecrit maintenant :

forall i,j in set dom xx & i<j => string lt(xx(i),xx(j)).

Au final, le nombre de tests filtr´es `a l’aide de ces quatre contraintes nous permet de

suppri-mer les 14 s´equences conduisant `a un verdict inconclusif. Il ne reste que 11 s´equences.

L’utilisation de fonctions nous permet donc de prendre en compte cette dimension

sup-pl´ementaire qu’est l’ordre d’appel des op´erations. N´eanmoins, l’expression de contraintes s’en

retrouve complexifi´ee.

Bilan

Nous avons expos´e un certain nombre de pistes pouvant permettre de r´esoudre les

probl`emes expos´es en d´ebut de chapitre. L’expression de contraintes permet d’exercer un

contrˆole suppl´ementaire sur les tests qui sont g´en´er´es. Il est ainsi possible d’´eviter la g´en´eration

de tests violant la sp´ecification. C’est aussi un moyen de s´electionner des tests qui peuvent

sembler plus int´eressants que d’autres `a un moment donn´e du processus de test. Une contrainte

caract´erise alors une ou plusieurs hypoth`eses de test faites par l’ing´enieur.

Le fait de conserver la position des appels d’op´erations comme information suppl´ementaire

complique rapidement les contraintes ´ecrites par l’utilisateur. Cela permet n´eanmoins

d’exer-cer un meilleur contrˆole sur les tests g´en´er´es et permet donc une s´election plus fine. Il s’agit

alors de trouver le bon compromis entre la complexit´e de la contrainte et la finesse de la

s´election. Plus les contraintes sont complexes plus elles sont difficiles `a ´ecrire, et donc plus

elles peuvent se trouver erron´ees, et plus leur ex´ecution prend du temps.

Par ailleurs, les exp´eriences que nous avons men´ees nous am`enent aussi `a penser que

les contraintes les plus usuelles sont tr`es simples `a ´ecrire. Elles n’utilisent seulement que

quelques ´el´ements du langage et tiennent en quelques lignes. Leur ´ecriture ne n´ecessite pas

une grande connaissance de VDM. En effet, comme les contraintes portent sur des s´equences

d’´el´ements, les plus courantes sont de s’assurer qu’une s´equence est bien sous-s´equence d’une

autre, qu’elles ont le mˆeme nombre d’´el´ements, ou encore que le nombre d’´el´ements diff´erents

d’une s´equence est ´egal `a la taille de la s´equence. Il nous est donc possible de cr´eer une

bi-blioth`eque des contraintes les plus usuelles afin d’assister l’ing´enieur dans sa d´emarche. Ainsi,

l’ing´enieur, mˆeme non expert en VDM, peut, `a partir d’une connaissance partielle du

lan-gage, ´ecrire des contraintes particuli`erement utiles.

De plus, celui-ci garde `a sa disposition toute la puissance de VDM pour d´efinir des contraintes

plus complexes portant, par exemple, sur des types de donn´ees structur´es. L’utilisateur reste

donc maˆıtre de la complexit´e d’utilisation de cette fonctionnalit´e.