Testes de data em um pipeline
A janela serve para explorar. Esta é a outra metade: uma versão de linha de comando de cerca de 2,4 MB, sem ambiente de execução para instalar, cujo código de saída é o veredito de cobertura e não o do próprio alvo.
O código de saída é o veredito
Esta é a decisão de projeto que torna a ferramenta programável por script. Quando uma sessão termina, o código de saída responde a “a substituição do tempo teve efeito?”, e não a “o que o aplicativo retornou?”. Um pipeline pode, portanto, falhar diante de uma substituição que silenciosamente não aconteceu - que é a falha pela qual vale falhar, porque tudo o que vem depois é um teste que não provou nada.
O código de saída do próprio alvo continua sendo informado, nos dados da sessão.
| Código | Significado |
|---|---|
| 0 | funciona - todo processo que leu a hora viu o relógio da sessão |
| 10 | funciona parcialmente - alguns canais cobertos, outros não |
| 11 | não funciona - a substituição não chegou aos canais principais |
| 4 | indeterminado - não foi possível estabelecer a cobertura |
| 1 | erro de uso - uma opção ou expressão incorreta |
| 2 | o alvo não pôde ser iniciado nem injetado |
| 12 | o alvo desapareceu, sugerindo um aplicativo de instância única |
| 3 | erro interno - o motor não conseguiu iniciar, chegou a um estado inesperado ou parou antes de encerrar a sessão (qualquer que fosse o seu próprio código de saída). Uma execução assim não traz veredito |
| 5 | a operação não está incluída nesta versão |
| 6 | a execução foi interrompida - --timeout expirou, ou o motor parou de responder por 15 segundos e foi parado. Isto não é um veredito: a sessão foi interrompida, então não prova nada sobre o alvo, em nenhum dos sentidos |
| 7 | outra sessão já está em execução - uma sessão por vez. Basta tentar de novo quando ela terminar |
| 8 | a sessão chegou a um veredito, mas a evidência não está no arquivo indicado por --report - o disco estava cheio, ou outro programa mantinha o arquivo antigo aberto, que então fica como estava. O veredito continua no relatório que o comando exibe |
Todas as opções dos dois comandos, e a mesma tabela para a calculadora, estão na referência da linha de comando.
Execuções limitadas
Um pipeline não pode esperar que uma pessoa feche um aplicativo de desktop.
--ticks N encerra a sessão após N batimentos - de um segundo real cada - e relata o
que encontrou até ali. A cobertura é estabelecida nos primeiros instantes do processo de qualquer
forma, então um limite curto costuma bastar.
# Dez segundos, depois parar e relatar chrono run ".\build\test.exe" --preset year-rollover --ticks 10 --report evidence.txt
Saída para máquinas e evidências
--jsonemite a sessão como registros legíveis por máquina em vez do relatório para pessoas - os mesmos fatos, inclusive a cobertura por canal.--report <path>grava o relatório para pessoas em um arquivo, que serve como artefato de build. Uma sessão cujo veredito não seja funciona abre esse arquivo com um aviso de evidência não confiável, em vez de enterrar a ressalva no fim.- A calculadora também entende
--json, então um pipeline pode calcular a data de que precisa e devolvê-la.
# Calcular a data e depois executar nela chrono calc --base today --snap eom --json chrono run ".\build\test.exe" --preset month-end --ticks 10 --json
Um passo, mais ou menos
- name: Month-end behaviour
shell: pwsh
run: |
.\chrono.exe run ".\build\test.exe" --preset month-end --ticks 10 --report evidence.txt
# um código de saída diferente de zero aqui significa que a substituição não pegou, então o
# teste seguinte não teria provado nada
- name: Keep the evidence
uses: actions/upload-artifact@v7
with:
name: chrono-evidence
path: evidence.txt
Duas ressalvas honestas sobre os runners
- É preciso um runner Windows. Não há versão para Linux nem macOS, e não haverá - no Linux, o libfaketime já cobre esse caso.
- O antivírus de um runner hospedado pode bloquear a injeção. Deslocar o tempo significa injetar uma biblioteca no processo alvo, uma técnica documentada do Windows que o malware também usa. Um runner que a bloqueie produz um código de saída dizendo isso, em vez de um falso sucesso - mas convém descobrir antes de depender disso.
Pelo mesmo motivo, a suíte de testes de ponta a ponta deste projeto roda em máquinas reais e não em um runner hospedado. A integração contínua dele compila e testa tudo com testes unitários, e a substituição em si é exercitada manualmente em aplicativos reais, tanto em builds de 32 quanto de 64 bits, antes de cada versão.