Rodando testes antes do commit em Mercurial

Como todo mundo, entrei de cabeça na onda dos sistemas de versão distribuídos, como Git. Por várias razões, porém, meu DVCS “do coração” é Mercurial. (Razões as quais pretendo explicar em breve, por sinal).

De qualquer forma, vai aí minha primeira dica sobre o Mercurial. Frequentemente, estou consertando um bug em um projeto…

$ nano module1.c

…e, como uso TDD, rodo os testes:

$ make test
./run_all
................................................................................

OK (80 tests)

Quando os testes passam e o bug está corrigido, comito o código alterado:

$ hg commit -m "Bug #123 corrected"

Daí, passo a trabalhar em outra funcionalidade no mesmo projeto, escrevendo os testes primeiro:

$ nano test/module2.c

Novamente no espírito de TDD, vou rodar os testes, para quebrarem. Aperto então Control+P (ou ) para chegar ao comando que roda os testes novamente (make test). Infelizmente, porém, às vezes eu aperto Enter muito cedo, o que resulta em comitar minhas alterações recentes:

$ hg commit -m "Bug #123 corrected"

Isto é ruim porque cria uma versão com código quebrando no Mercurial. A solução paliativa é executar hg rollback (obrigado de novo, Stack Overflow!). Entretanto,  hg rollback é a pílula do dia seguinte do Mercurial: envolve vários riscos e deve ser usado com cuidado.

Então tive um estalo: por que não rodo os testes sempre antes do commit? A resposta é que eu comito por acidente, claro, mas posso fazer o Mercurial rodá-los antes de confirmar um commit: bastaria criar um hook. Para isto, alterei o programa que roda os testes para retornar um valor diferente de 0 (zero) quando os testes falhassem. Assim make test retorna um valor diferente de zero. Antes eu tinha algo assim:

void RunAllTests(void) {
    CuString *output = CuStringNew();
    CuSuite* suite = CuSuiteNew();
    CuSuiteAddSuite(suite, test_project_suite());
    // ... mais coisas aqui
    CuSuiteRun(suite);
    CuSuiteSummary(suite, output);
    CuSuiteDetails(suite, output);
    printf("%s\n", output->buffer);
    CuStringDelete(output);
    CuSuiteDelete(suite);
}
int main(void) {
    RunAllTests();
    return 0;
}

Agora eu tinha algo assim:

int RunAllTests(void) { // Retorna int ao invés de void
    CuString *output = CuStringNew();
    CuSuite* suite = CuSuiteNew();
    CuSuiteAddSuite(suite, test_project_suite());
    // ... mais coisas aqui
    CuSuiteRun(suite);
    CuSuiteSummary(suite, output);
    CuSuiteDetails(suite, output);
    printf("%s\n", output->buffer);
    CuStringDelete(output);
    CuSuiteDelete(suite);
    return suite->failCount; // Retorna contagem de erros
}
int main(void) {
    return RunAllTests(); // Retorna contagem
}

(Caso você esteja se perguntando, estou utiliando CuTest, o melhor e mais cacofônico framework de testes para C.)

Após fazer esta alteração, adicionei as linhas abaixo no arquivo .hg/hgrc do projeto:

[hooks]
pretxncommit.surefire = make test

O que acontece quando vou comitar erroneamente agora? Veja só:

$ hg commit -m "Bug #123 fixed"
cc -c  -Wall -std=c99 -Iinclude -Icutest src/test/util.c -o test_util.o
cc   run_all.o test_secretary.o  CuTest.o libsecretary.a   -o run_all
./run_all
...........................................................F....................

There was 1 failure:
1) test_util_beginning_of_day: src/test/util.c:34: expected 1 but was 0

!!!FAILURES!!!
Runs: 80 Passes: 79 Fails: 1

make: *** [test] Error 1
transaction abort!
rollback completed
abort: pretxncommit.surefire hook exited with status 2

Os testes falham, o que faz o programa que os roda retornar um valor diferente de zero. O programa, falhando, faz o make falhar, retornando também um valor diferente de zero. Como o make falhou, o hook falha também, impedindo que Mercurial siga em frente com o commit. Oras, o hook foi executado na fase pretxncommit (pre transaction commit), logo antes de Mercurial registrar o commit do código. Como o hook falhou, o commit não é efetivamente feito e meu histórico fica limpo.

Este exemplo utiliza testes escritos em C, mas serve para qualquer projeto. Eventualmente, não se pode alterar o programa que roda os testes, mas pode-se criar um script que lê a saída dos testes e retorna o valor correto.

Tags: , , , , , , , ,

Uma resposta to “Rodando testes antes do commit em Mercurial”

  1. narcelio Says:

    Faz que nem eu, use o iwatch pra monitorar eventos no diretório dos seus fontes e rodar os testes ao salvar um arquivo que acabou de ser editado:

    iwatch -r -e close_write -t ‘(py$|yaml$)’ -c ‘python manage.py test’ .

    Esse comando aí roda os testes do Django quando o vim termina de criar o arquivo de backup de um .py ou .yaml.

    Eu deixo uma janela no canto rodando esse comando, edito um arquivo e só dou uma olhada pra ela quando termino a edição pra ver se deu certo.

    TDD FTW! \o/

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s


%d blogueiros gostam disto: