4. Uma fórmula temporal L especificando a condição de progresso do programa.
Depois que se prepara uma especificação de sistema usando TLA+, pode-se checá-la usando a ferramenta TLC. Quando se executa o TLC no modo de checagem de modelo, ele tenta encontrar todos os estados alcançáveis (todos os estados que podem ocorrer em comportamentos satisfazendo uma dada fórmula na forma Init ^rNextsvars). Quando ele
é executado no modo de simulação, ele randomicamente gera comportamentos (behaviors), sem tentar checar todos os estados alcançáveis [58].
Especificações TLA+ são particionadas em módulos. A seguir são apresentados três módulos que foram criados para este trabalho: TestExecution, MonitoredTestExecution e TestExecutionCheckingAssertionInvariant.
7.2
A Especificação Test Execution usando TLA+
No Apêndice A é apresentada uma especificação em TLA+ de uma execução de teste sem usar a abordagem Thread Control for Tests. Essa especificação foi escrita em um arquivo texto chamadoTestExecution.tlae então formatada para impressão usando a ferra- menta TLATeX. A sintaxe dessa especificação foi checada usando a ferramenta verificadora de sintaxetlasany.
Para que fosse possível checar essa especificação utilizando TLC, teve-se de definir no modelo os possíveis estados de threads sendo considerados e o número de threads a serem consideradas no sistema sob teste (SUT). O número de threads foi 3 para o modelo mos- trado no Apêndice A. Cada um dos estados possíveis (e.g. running ) foi definido como uma constante, e definiu-se também uma outra constante para representar o conjunto dos esta- dos possíveis, denominada ThreadsPossibleStates. A definição das constantes do Módulo
TestExecution foi feita da seguinte forma:
CONSTANTThreadsPossibleStates, unstarted ,
started , waiting, running, finished , verifying
Especificou-se que o estado do sistema a ser checado seria o estado das threads que são criadas quando um teste é executado, incluindo a thread de teste. Esse estado é representado na especificação pela variável threadsStates, que é um registro (record). Registros em TLA+ são uma forma de substituir algumas variáveis por uma única variável. A definição dessa
7.2 A Especificação Test Execution usando TLA+ 108
variável foi feita na especificação da seguinte forma:
VARIABLEthreadsStates
Os estados possíveis considerados para as threads do sistema (representando os valores assumidos por tState1, tState2 e tState3 da tupla threadsStates) são: tunstarted , started ,
running, waiting and finishedu e esse conjunto de estados possíveis é representado pela
constante ThreadsPossibleStates, cujo valor é definido em um arquivo de configuração chamado TestExecution.cfg. Os estados possíveis que foram definidos para a th- read de teste (representando os valores assumidos por tState0) são: tunstarted , running,
verifyingu. Na especificação TLA+, a definição dos estados possíveis para as três threads do
sistema e para a thread do teste estão descritos na definição da seguinte invariante: TypeInvariant ∆
^threadsStates P rtState0 : tunstarted , running, verifyingu,
tState1 : ThreadsPossibleStates, tState2 : ThreadsPossibleStates, tState3 : ThreadsPossibleStatess
A idéia básica da especificação denominada Execução de Teste (Test Execution) é que uma execução de teste (TE ) é representada pela fórmula:
TE ∆ TEini^2rTEnxts
xthreadsStatesy
Essa fórmula é apresentada na parte final da especificação e a sintetiza.
A fórmula temporal TE especifica a condição de progresso da execução do teste, a qual indica que o estado inicial deve ser verdadeiro e afirma (asserts) que TEnxt é verdadeiro para qualquer passo no comportamento (que representa uma sequência infinita de estados).
Nessa fórmula, TEini é o predicado de estado que especifica o estado inicial do pro- grama. Esse estado indica que todas as threads, incluindo a thread de teste, devem estar no estado unstarted . Este predicado é definido no arquivoTestExecution.tlada seguinte forma:
TEini ∆
^threadsStates P rtState0 :tunstartedu,
tState1 : tunstartedu, tState2 :tunstartedu,
tState3 : tunstartedus
A ação TEnxt representa todas as transições permitidas pela especificação. Ela é cha- mada de relação de próximo estado (next state relation) e especifica como o valor da variável
7.2 A Especificação Test Execution usando TLA+ 109
threadsStates pode mudar em cada passo (sendo um passo um par de estados sucessivos). A especificação dessa ação é feita da seguinte forma:
TEnxt ∆ _TestStarts _Thread 1Starts _Thread 1Runs _Thread 1FinishesRunning _Thread 1StartsToWait _Thread 2Starts _Thread 2Runs _Thread 2FinishesRunning _Thread 2StartsToWait _Thread 3Starts _Thread 3Runs _Thread 3FinishesRunning _Thread 3StartsToWait _NothingHappens _AssertionPerformed _StartAssertionsBlock _FinishAssertionsBlock
A idéia básica da ação TEnxt é que os passos permitidos em uma execução de teste (es- pecificados na definição dessa ação) são os seguintes: o teste inicia (TestStarts) ou a thread1 começa (Thread 1Starts) ou a thread1 roda (Thread 1Runs), ou a thread1 para de rodar (Thread 1FinishesRunning ) ou a thread1 começa a esperar (Thread 1StartsToWait) ou a thread2 começa (Thread 2Starts) ou a thread2 roda (Thread 2Runs) ou a thread2 termina de rodar (Thread 2FinishesRunning ), ou a thread2 começa a esperar (Thread 2StartsToWait) ou a thread3 começa (Thread 3Starts), ou a thread3 roda (Thread 3Runs) ou a th- read3 termina de rodar (Thread 3FinishesRunning ) ou a thread3 começa a esperar (Thread 3StartsToWait) ou nada acontece (NothingHappens), ou seja, passos do tipo stut-
tering) são permitidos, ou ainda uma asserção é executada (AssertionPerformed ), ou ainda
7.2 A Especificação Test Execution usando TLA+ 110
minado (FinishAssertionsBlock ). Todas essas ações possíveis são definidas no arquivo
TestExecution.tlailustrado no Apêndice A, indicando as pré-condições para que es- ses passos sejam habilitados e os novos valores de variáveis após a ocorrência desses passos, os quais são representados pela variável primed chamada threadsStates1
.
Por exemplo, a ação indicando quando um teste inicia (TestStarts) é definida da seguinte forma:
TestStarts ∆
^threadsStates P rtState0 :tunstartedu,
tState1 : tunstartedu,
tState2 : tunstartedu,tState3 : tunstartedus
^threadsStates 1
rthreadsStates EXCEPT !.tState0runnings
Essa definição indica que esse passo só é habilitado quando todas as threads estão no estado unstarted , e depois que ele ocorre, o novo estado do sistema threadsStates1
indica que todas as threads permanecem no mesmo estado, exceto a thread do teste, cujo estado, representado por tState0 muda para running .
A mesma idéia é seguida pelas outras ações definidas nessa especificação em TLA+. Algumas das ações são definidas como locais (LOCAL) para indicar que em Módulos que estendem o módulo TestExecution elas podem ser redefinidas. Um exemplo de ação nesse estilo é a que indica que a thread1 roda (Thread 1Runs). Esta ação é definida da seguinte forma:
LOCALThread 1Runs ∆
^pthreadsStates.tState1started _threadsStates.tState1waitingq ^threadsStates
1
rthreadsStates EXCEPT !.tState1runnings
Esta ação indica que o passo de fazer a thread1 rodar só é habilitado quando esta thread estava no estado started ou waiting . Além disso, uma vez que esse passo ocorre, o novo estado da thread1 passa a ser running e os estados das demais threads se mantêm.
Em TLA+, uma fórmula temporal satisfeita por todo comportamento é chamada um teo- rema. Na especificação Test Execution, o seguinte teorema é estabelecido:
THEOREMTE ñ2TypeInvariant
Esse teorema indica que a fórmula temporal TE implica que a definição de invariante TypeInvariant é sempre verdadeira. Essa invariante indica que para cada estado possível
7.2 A Especificação Test Execution usando TLA+ 111
de uma execução de teste, o estado de cada thread do sistema (SUT thread) é o conjunto ThreadsPossibleStates tunstarted , started , running, waiting and finishedue que o es-
tado da thread do teste está no conjuntotunstarted , running, verifyingu.
Um outro invariante é definido no arquivo TestExecution.tla, o NotEarlyOrLateAssertionInvariant. No entanto, a especificaçãoTestExecution.tla
não contém o teorema indicando que essa invariante deve ser sempre verdadeira. Isso é feito apenas na segunda e na terceira especificações, mostradas nos Apêndices B e C e descritas nas próximas seções. Esse invariante representa a não ocorrência de asserções antecipadas e tardias e sua especificação indica que quando a thread do teste está no estado verifying (executando alguma asserção), todas as threads do SUT devem estar waiting ou finished . A definição desse invariante é feita da seguinte forma:
NotEarlyOrLateAssertion ∆
_threadsStates P rtState0 : tverifyingu,
tState1 : twaiting, finishedu, tState2 :twaiting, finishedu,
tState3 : twaiting, finishedus
_threadsStates P rtState0 : trunningu,
tState1 : ThreadsPossibleStates, tState2 : ThreadsPossibleStates, tState3 : ThreadsPossibleStatess
_threadsStates P rtState0 : tunstartedu,
tState1 : tunstartedu,tState2 : tunstartedu,
tState3 : tunstartedus
É importante perceber que a execução de teste descrita pela especificação do arquivo
TestExecution.tlailustra uma execução de teste em que as threads não são monitora- das. Isso pode ser observado na definição da ação StartAssertionsBlock , que está ilustrada a seguir:
LOCALStartAssertionsBlock ∆
^pthreadsStates.tState0runningq ^threadsStates
1
rthreadsStates EXCEPT !.tState0verifyings
A única pré-condição especificada nessa ação para que um bloco de asserções se inicie é que a thread do teste esteja no estado running . Depois que essa ação acontece, o novo estado da thread de teste tState0 será verifying .