Conceitos:
|
| -table |
| -trace |
| -nolink |
Uma vez localizada a opção na linha de comando, ela era pesquisada na tabela. Havia correspondência se ela fosse o prefixo de qualquer entrada na tabela (ou seja, "-t" correspondia a "-table"). Uma vez localizada uma correspondência, o restante da tabela era pesquisado em busca de outra correspondência. Caso outra correspondência fosse localizada, isso indicaria ambigüidade e, conseqüentemente, um erro.
O código que fazia a pesquisa tinha a seguinte aparência:
for (first=0; first < size; first++) {
if (matches(entry[first], thing_sought)) {
/* at least one match */
for(dup=first+1; dup < size; dup++)
/* search for another */
if (matches(entry[dup], thing_sought))
/* extra match */
break; /* error out */
return first;
}
}
return -1; /* Not found or ambiguity */
Você consegue ver o problema? (É bem sutil.)
O problema é a instrução de divisão. Ela se destina a dividir o loop incluso mais externo quando uma correspondência duplicada é localizada, mas, na realidade, divide o interno. Esse efeito equivale a não localizar uma segunda correspondência: o índice da primeira correspondência é retornado.
Observe que só será possível localizar essa falha se a opção localizar duas correspondências na tabela, como aconteceria com "-t".
Agora, vamos observar uma segunda falha totalmente diferente.
O código utiliza uma seqüência de caracteres. Ele deve substituir o último '=' na seqüência de caracteres por um '+'. Se não houver nenhum '=', nada será feito. O código usa a rotina de biblioteca C padrão strchr para encontrar a localização de '='. Este é o código:
ptr = strchr(string, '='); /* Find last = */
if (ptr != NULL_CHAR)
*ptr = '+';
Esse problema também é um pouco sutil.
A função strchr retorna a primeira correspondência na seqüência de caracteres, e não a última. A função correta é strrchr. O problema era provavelmente um erro de digitação. (Na verdade, o problema básico é que não é muito inteligente incluir em uma biblioteca padrão duas funções cuja diferença seja apenas uma letra.)
Essa falha só pode ser localizada quando há dois ou mais sinais de igual na entrada. Ou seja:
Nesse caso, o interessante é que há duas falhas com causas originais totalmente diferentes (erro de digitação, má compreensão de uma construção C) e manifestações distintas no código (chamada de uma função incorreta, uso impróprio da instrução de divisão), que podem ser localizadas na mesma idéia de teste (procure uma situação que ocorra duas vezes).
O que torna um catálogo adequado?
Considerando essas regras, é melhor que exista mais de um catálogo. Como alguns dados e operações são comuns a toda a programação, as respectivas idéias de teste podem ser incluídas em um catálogo para que todos os programadores possam usá-las. Outras são específicas de um determinado domínio; por isso, as idéias de teste correspondentes podem ser incluídas em um catálogo de idéias de teste específico do domínio.
O catálogo simples mostrado aqui representa um bom início. E eis outro exemplo: Idéias de Teste para Combinações de ANDs e ORs
Suponha que você esteja implementando este método:
void applyToCommonFiles(Directory d1,
Directory d2,
Operation op);
applyToCommonFiles utiliza dois diretórios como argumentos. Quando um arquivo do primeiro diretório tem o mesmo nome que o arquivo do segundo, applyToCommonFiles executa uma operação nesses dois arquivos: gera subdiretórios.
Eis como você pode usar o mesmo catálogo. O método consiste em examinar o catálogo, procurando os principais títulos correspondentes, depois analisar as idéias de teste abaixo do título para ver se são relevantes e, por fim, incluir as idéias relevantes em uma Lista de Idéias de Teste.
(Observação: essa descrição passo a passo pode dar a impressão de que o uso do catálogo é trabalhoso. Ler sobre a criação da lista de verificação demora mais que realmente criá-la.)
A primeira entrada é para Qualquer Objeto. Algum desses argumentos poderia ser um ponteiro nulo? A questão é o acordo entre applyToCommonFiles e seus chamadores. O acordo pode determinar que os chamadores não passarão por um ponteiro nulo. Se passarem, todas as apostas serão inválidas. applyToCommonFiles pode executar qualquer ação. Nesse caso, nenhum teste será apropriado, já que nenhuma ação realizada por applyToCommonFiles pode ser incorreta. Entretanto, se applyToCommonFiles precisar verificar ponteiros nulos, a idéia de teste será útil. Vamos admitir a última hipótese, que fornece a primeira Lista de Idéias de Teste:
A próxima entrada do catálogo é Seqüências de Caracteres. Os nomes dos arquivos são seqüências de caracteres e são comparados para ver se há correspondências. A idéia de testar com a seqüência de caracteres vazia ("") não parece útil: presume-se que algumas rotinas padrão de comparação de seqüências de caracteres serão usadas e que elas lidarão corretamente com as seqüências de caracteres vazias.
Mas espere... Se seqüências de caracteres estão sendo comparadas, há distinção entre maiúsculas e minúsculas? Suponha que d1 contenha um arquivo chamado "Arquivo" e d2 contenha um arquivo chamado "arquivo". Deve haver correspondência entre esses arquivos? No Unix, é claro que não. No Windows, certamente haverá. Esta é outra idéia de teste:
Observe que essa idéia de teste não é diretamente proveniente do catálogo. Contudo, o catálogo desviou nossa atenção para um aspecto específico do programa (nomes de arquivo como seqüências de caracteres), e nossa criatividade nos propiciou outra idéia. É importante não usar o catálogo de forma muito restritiva; use-o como uma técnica geradora de debates, uma maneira de inspirar novas idéias.
A próxima entrada é Conjuntos. Um diretório é um conjunto de arquivos. Muitos programas que lidam com conjuntos falham no conjunto vazio. Alguns que lidam com o conjunto vazio (ou conjuntos com vários elementos) falham em conjuntos com exatamente um elemento. Portanto, estas idéias são úteis:
A próxima idéia consiste em usar um conjunto do maior tamanho possível. Isso é útil porque programas como applyToCommonFiles normalmente são testados com pequenos diretórios triviais. Em seguida, um usuário os aplica a duas imensas árvores de diretórios com milhares de arquivos, apenas para descobrir que o programa não dispõe de memória suficiente para lidar com esse caso real.
Nesse momento, o importante não é testar o tamanho máximo absoluto de um diretório; ele só precisa ter o tamanho suficiente para atender às necessidades do usuário. Mas, pelo menos, deve haver algum teste com mais de três arquivos em um diretório:
A idéia de teste final (duplicar elementos) não se aplica a diretórios de arquivos. Isto é, se houver um diretório que possua dois arquivos com o mesmo nome, você terá um problema independentemente de applyToCommonFiles: o sistema de arquivos estará corrompido.
A próxima entrada do catálogo é Pesquisando. Essas idéias podem ser convertidas em termos do applyToCommonFiles como:
A idéia de teste final verifica se applyToCommonFiles terminou cedo demais. Ele retorna logo que localiza a primeira correspondência? O comentário entre parênteses na penúltima idéia de teste pressupõe que o programa buscará os arquivos de um diretório usando alguma rotina de biblioteca que os retorne classificados em ordem alfabética. Caso contrário, será melhor descobrir qual é realmente o último arquivo (o criado mais recentemente?) e fazer com que ele seja a correspondência. No entanto, antes de gastar muito tempo tentando descobrir como os arquivos estão ordenados, você deve considerar qual é a probabilidade de localizar defeitos colocando o elemento correspondente por último. Colocar um elemento por último em um conjunto será mais útil se o código percorrer explicitamente o conjunto usando um índice. Se estiver usando um iterador, é muito improvável que a ordem seja importante.
Vamos observar mais uma entrada do catálogo. A entrada Estruturas vinculadas lembra que estamos comparando árvores de diretório, e não apenas simples conjuntos de arquivos. Seria uma pena se applyToCommonFiles trabalhasse apenas nos diretórios de nível superior, e não nos de nível inferior. Mas decidir como testar se applyToCommonFiles trabalha em diretórios de nível inferior faz com que tenhamos de enfrentar a deficiência em sua descrição.
Primeiro, quando applyToCommonFiles gera subdiretórios? Se a estrutura de diretórios tiver esta aparência:

Fig. 1: Uma estrutura de diretórios
applyToCommonFiles gera o subdiretório Cdir? Isso não faz muito sentido. Não pode haver correspondência com nenhum outro item da outra árvore de diretórios. Na verdade, aparentemente só pode haver correspondência entre os arquivos dos subdiretórios se o mesmo ocorrer entre os nomes de subdiretório. Ou seja, suponha que exista esta estrutura de diretórios:

Fig. 2: Uma segunda estrutura de diretórios
Os arquivos denominados "Arquivo" não correspondem porque estão em subdiretórios distintos. Os subdiretórios só devem ser gerados se tiverem o mesmo nome em d1 e d2. Isso leva a estas idéias de teste:
Contudo, isso levanta outras questões. A operação (op) deve ser aplicada a subdiretórios correspondentes ou apenas a arquivos correspondentes? Se for aplicada aos subdiretórios, deverá ser aplicada antes ou após a geração? Isso fará diferença se, por exemplo, a operação excluir o arquivo ou diretório correspondente. Por esse motivo, a operação deve poder modificar a estrutura de diretórios? Mais especificamente: qual será o comportamento correto de applyToCommonFiles se ela o fizer? (Essa questão também surge com iteradores.)
Essas questões normalmente surgem quando você faz uma "leitura detalhada" da descrição de um método no processo de criação de idéias de teste. Mas vamos deixá-las de lado por enquanto. Sejam quais forem as respostas, precisará haver idéias de teste correspondentes, que verifiquem se o código implementa as respostas corretamente.
Em vez disso, vamos retornar ao catálogo. Ainda não consideramos todas as idéias de teste. A primeira "vazio (nada na estrutura) " solicita um diretório vazio. Isso também aconteceu com a entrada Conjuntos. Já obtivemos a "estrutura mínima não-vazia", que é um diretório com um único elemento. Esse tipo de redundância não é incomum, mas é fácil de ser ignorada.
Que tal "uma estrutura circular"? As estruturas de diretório não podem ser circulares um diretório não pode estar dentro de um de seus descendentes ou dentro de si mesmo. Ou pode? E os atalhos (no Windows) ou links simbólicos (no UNIX)? Se houver um atalho na árvore de diretórios de d1 que aponte de volta para d1, applyToCommonFiles deve continuar a gerar ininterruptamente? A resposta pode levar a uma ou mais novas idéias de teste:
(Dependendo do comportamento correto, deve haver mais idéias de teste além dessa.)
Por fim, que tal um "nível de profundidade maior que um"? Idéias de teste anteriores garantirão que o teste seja realizado gerando um nível de subdiretório, mas devemos verificar se applyToCommonFiles continua a gerar:
Como mencionamos anteriormente, o catálogo genérico não conterá todas as idéias de teste necessárias. Contudo, catálogos específicos de domínio não foram publicados fora das empresas que os criaram. Se você os quiser, terá de criá-los. Eis algumas sugestões.
Evite a tentação de preencher um catálogo com suas especulações sobre quais seriam as idéias apropriadas para a localização de falhas. Lembre-se que cada idéia de teste incluída no catálogo custa tempo e dinheiro: seu tempo gasto para manter o catálogo, o tempo de outros programadores para pensar sobre a idéia de teste e, possivelmente, o tempo de outros programadores para implementar um teste. Adicione apenas as idéias que tenham um registro de controle demonstrado. Você deve ser capaz de apontar pelo menos uma falha real que teria sido capturada pela idéia de teste. O ideal é que essa falha não tenha sido identificada por outro teste (ou seja, uma falha relatada pelo campo). Uma boa maneira de criar catálogos consiste em percorrer o banco de dados de erros de sua empresa e perguntar como cada falha poderia ter sido detectada anteriormente.
Se a criação e a manutenção de um Catálogo de Idéias de Teste são tarefas realizadas em seu tempo livre, isso provavelmente não vai funcionar. Você precisará de tempo especialmente alocado para essa tarefa, como para qualquer outra importante. É recomendável criar e manter o Catálogo de Idéias de Teste com base em Detalhamento do Fluxo de Trabalho: Aprimorar Vantagens do Teste.
|
Rational Unified Process
|