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("%sn", 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("%sn", 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.
Post Revisions:
There are no revisions for this post.
One Comment