Files
fn_registry/dev/issues/completed/0048-cpp-visual-tests-ci-gate.md
2026-04-29 00:18:58 +02:00

4.2 KiB

0048 — Visual tests via primitives_gallery (golden screenshots) + CI gate para nuevas funciones

Metadata

Campo Valor
ID 0048
Estado pendiente
Prioridad media
Tipo feature — testing infra + CI

Dependencias

Bloquea-por: 0047 (Catch2 montado).


Objetivo

  1. Anadir captura automatica de screenshots por demo en primitives_gallery, comparados pixel-a-pixel contra goldens en cpp/tests/golden/. Cada PR que toque un primitivo de UI muestra el diff.
  2. CI gate: PR que anada funcion nueva en cpp/functions/ exige tested: true en el .md.

Contexto

primitives_gallery ya renderiza cada primitivo del registry con datos sinteticos. Si capturamos PNGs por demo y los comparamos con goldens commiteados, tenemos test visual automatico para cualquier cambio que afecte a render.

Arquitectura

cpp/
├── apps/primitives_gallery/
│   ├── main.cpp                          # MOD — flag --capture <out_dir>
│   └── capture.{h,cpp}                   # NEW — render headless por demo, dump PNG
├── tests/
│   ├── golden/                           # NEW
│   │   ├── button.png
│   │   ├── select.png
│   │   ├── kpi_card.png
│   │   └── ... (1 png por demo del gallery)
│   ├── test_visual.cpp                   # NEW — corre gallery con --capture, compara con golden
│   └── CMakeLists.txt                    # MOD
└── scripts/
    ├── run_tests.sh                      # MOD — incluye visual
    └── update_goldens.sh                 # NEW — regenera goldens

Tareas

1.1 Anadir flag --capture <output_dir>. Cuando esta activo:

  • Inicializa GLFW en modo offscreen (glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE)) o usa headless via EGL si esta disponible.
  • Para cada demo en k_demos[]: setea g_selected_id, hace 3 frames de warmup (para tooltips/animaciones estables), captura framebuffer con glReadPixels, guarda como PNG via stb_image_write.
  • Output: <output_dir>/<demo_id>.png.
  • Sale tras procesar todas. 1.2 Si --capture no esta presente: comportamiento normal (interactivo).

Fase 2 — Goldens iniciales

2.1 scripts/update_goldens.sh:

#!/usr/bin/env bash
mkdir -p cpp/tests/golden
./cpp/build/apps/primitives_gallery/primitives_gallery --capture cpp/tests/golden

2.2 Generar y commitear los PNGs como linea base.

Fase 3 — Test visual

3.1 test_visual.cpp (Catch2): para cada demo, lanzar gallery con --capture a un dir temporal, comparar contra cpp/tests/golden/<demo>.png con tolerancia configurable (por defecto: 1% pixels distintos, threshold por canal 5/255). 3.2 Si difiere, fallar con info: expected golden/X.png, actual /tmp/.../X.png, diff Y%.

Fase 4 — CI gate para tested:true

4.1 Crear cpp/scripts/check_tested.sh que valida:

sqlite3 registry.db "SELECT id FROM functions
  WHERE lang='cpp' AND tested=0 AND created_at > date('now','-30 days')"

Si hay alguno → exit 1 con mensaje "anade test antes de mergear". 4.2 Ganchar en cpp/scripts/run_tests.sh (post-ctest).

Fase 5 — Documentar en PATTERNS

5.1 En cpp/PATTERNS.md (de 0041) anadir seccion "Tests visuales":

  • Como capturar nuevo golden cuando se anade un primitivo (update_goldens.sh).
  • Como diagnosticar diff (renderiza el png actual vs golden lado a lado).

Decisiones

  • Headless via GLFW invisible es lo simple. Si no funciona en algun entorno, usar EGL/swiftshader como fallback.
  • Tolerancia 1% para evitar flakes por antialiasing/font rendering distinto. Ajustable.
  • Goldens en repo (PNGs binarios) — pesa poco si los demos son pequenos (200x200 max).

Riesgos

  • Diferencias de rendering entre Linux dev y CI: usar mesa software rendering (LIBGL_ALWAYS_SOFTWARE=1) en ambos lados para consistencia.
  • Cambios cosmeticos legitimos en primitivos requieren regenerar goldens — flujo: update_goldens.sh && git diff cpp/tests/golden/ para revisar visualmente.

Validacion

cmake --build cpp/build -j
ctest --test-dir cpp/build -R visual --output-on-failure
# Cambia un color en tokens, run otra vez, debe FALLAR.
# update_goldens.sh + git diff muestra los pngs cambiados.