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.
Dans le document
Outils pour la synthèse de tests et la maîtrise de l'explosion combinatoire.
(Page 109-113)