Pular para o conteúdo

Relatórios, benchmark e CI

Quatro formatos, e a medição que mostra o p95 em vez de só a média.

Os formatos#

bash
dataforge crucible                        # texto, colorido
dataforge crucible --formato=junit --out=r.xml
dataforge crucible --formato=json --out=r.json
dataforge crucible --formato=tap
FormatoPara
textoler no terminal
JUnit XMLGitHub Actions, GitLab, Jenkins — todos leem
JSONprocessar por programa
TAP 13o formato mais simples e portável que existe

O JUnit existe porque integrar com cada CI exigiria que cada um aprendesse o formato do Crucible — e nenhum vai aprender.

A ordem do relatório#

Primeiro o mapa, uma linha por suíte. Depois cada falha com espaço para respirar. Quem roda a suíte quer saber se está verde; quando não está, quer o detalhe de cada uma — e não rolar a tela procurando o vermelho no meio do verde.

Benchmark#

dataforge
crucible "Desempenho":
    bench "soma de mil" times 100:
        total := 0
        cycle i from 1 to 1000:
            total += i
text
    ⏱  soma de mil: 0.0821ms media, 0.0798ms mediana,
       p95 0.0954ms, 12180 ops/s

Pela biblioteca, com mais controle:

dataforge
adopt Crucible

m := Crucible.benchmark("soma", lambda => sum(range(1000)), 500)
out m["media_ms"], m["mediana_ms"], m["p95_ms"], m["ops_por_s"]

Ordem aleatória#

Um teste que só passa porque outro rodou antes é uma bomba-relógio. --aleatorio a detona cedo:

bash
dataforge crucible --aleatorio
# ordem aleatoria, semente 1738 (--semente=1738 repete)

dataforge crucible --aleatorio --semente=1738   # reproduz exatamente

A semente aparece no relatório justamente para a falha ser reproduzível — uma ordem aleatória que não se repete é impossível de depurar.

Instabilidade#

bash
dataforge crucible --repetir=20

Roda cada trial vinte vezes. Um teste que passa às vezes depende de tempo, de ordem, ou de estado que sobrou — e é melhor descobrir isso agora que numa madrugada de plantão.

No CI#

text
name: testes
on: [push, pull_request]

jobs:
  crucible:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: curl -fsSL https://dataforge-lang.vercel.app/instalar.sh | sh
      - run: dataforge check src/ --strict
      - run: dataforge big-o src/ --strict
      - run: dataforge crucible --formato=junit --out=resultados.xml
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: resultados
          path: resultados.xml

dataforge crucible sai com código 1 quando algo falha, então o passo reprova sozinho.

Lendo o resultado por programa#

dataforge
adopt Crucible

Crucible.suite("Exemplo", lambda => [
    Crucible.trial("passa", lambda => Crucible.expect(1).to_be(1))
])

resumo := Crucible.run()
out resumo["passou"], resumo["falhou"], resumo["verde"]

cycle r in Crucible.results():
    out r["nome"], r["estado"], r["duracao"]