Files
fn_registry/.claude/commands/e2e-cpp.md
T
egutierrez 7913116a8e chore: auto-commit (129 archivos)
- .claude/agents/fn-analizador/SKILL.md
- .claude/agents/fn-constructor/SKILL.md
- .claude/agents/fn-executor/SKILL.md
- .claude/agents/fn-mejorador/SKILL.md
- .claude/agents/fn-orquestador/SKILL.md
- .claude/agents/fn-recopilador/SKILL.md
- .claude/commands/app.md
- .claude/commands/compile.md
- .claude/commands/cpp-app.md
- .claude/commands/create_functions.md
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 22:23:12 +02:00

8.1 KiB

/e2e-cpp — Crear/ejecutar tests e2e para apps C++

Genera y corre tests e2e con Dear ImGui Test Engine sobre las apps C++ del registry. Cada app gana un ejecutable <app>_tests que reabre la app dentro de un harness de testing y ejecuta scripts de UI (clicks, escritura, asserts) sobre los componentes ImGui.

Suite ya instalada en cpp/vendor/imgui_test_engine/. Integracion en framework: fn::run_app_test() (ver cpp/framework/app_base.h). Opt-in via -DFN_BUILD_TESTS=ON. Sin la opcion los builds normales de /compile no cambian.

Argumento

$ARGUMENTS — formato libre. Casos:

  • <app_name> — solo el nombre. Si la app ya tiene tests, los ejecuta. Si no, pide al usuario que describa el flujo a testear.
  • <app_name> <descripcion del flujo> — genera un test nuevo para ese flujo y lo ejecuta. Ej: chart_demo abrir cada tab y verificar que renderiza.
  • vacio — detectar app desde pwd (si estas en cpp/apps/<X>/ o projects/*/apps/<X>/); si no, listar apps disponibles.

Pasos

1. Resolver app y directorio

ROOT=$HOME/fn_registry
ARGS="$ARGUMENTS"
APP_ARG="${ARGS%% *}"   # primera palabra
FLOW_DESC="${ARGS#* }"  # resto (puede coincidir con APP_ARG si solo hay una palabra)
[ "$FLOW_DESC" = "$APP_ARG" ] && FLOW_DESC=""

# Detectar desde CWD si no hay arg
if [ -z "$APP_ARG" ]; then
  CWD="$(pwd)"
  case "$CWD" in
    "$ROOT"/cpp/apps/*|"$ROOT"/projects/*/apps/*)
      APP_ARG="$(basename "$CWD")" ;;
  esac
fi

if [ -z "$APP_ARG" ]; then
  echo "Apps C++ disponibles:"
  ls "$ROOT"/cpp/apps/ 2>/dev/null
  ls "$ROOT"/projects/*/apps/ 2>/dev/null
  echo "Uso: /e2e-cpp <app> [descripcion del flujo]"
  exit 1
fi

APP_DIR=""
for cand in "$ROOT/cpp/apps/$APP_ARG" "$ROOT"/projects/*/apps/"$APP_ARG"; do
  [ -d "$cand" ] && [ -f "$cand/CMakeLists.txt" ] && APP_DIR="$cand" && break
done
[ -z "$APP_DIR" ] && { echo "App C++ no encontrada: $APP_ARG"; exit 1; }
echo "App: $APP_ARG"
echo "Dir: $APP_DIR"

2. Inspeccionar la app

Lee:

  • $APP_DIR/main.cpp — identifica:
    • El nombre de la funcion principal de render (suele ser render() o static void render()).
    • El window title que aparece en ImGui::Begin("...") — sera el primer arg de ctx->SetRef("...") en los tests. Si tiene em-dash u otros UTF-8 no ASCII, anotar la secuencia de bytes (ej: \xe2\x80\x94 para ).
    • Los IDs/labels de los widgets candidatos: tabs (BeginTabItem), botones (Button), inputs (InputText), checkboxes, etc.
  • $APP_DIR/app.md — para entender el dominio y proposito.
  • $APP_DIR/CMakeLists.txt — para saber que .cpp del registry enlaza la app (los tests linkearan los mismos).

3. Decidir tests a escribir

Si $FLOW_DESC esta vacio: pregunta al usuario que flujo testear. Sugiere 2-3 candidatos basados en los widgets vistos en main.cpp. NO inventes flujos sin confirmacion.

Si $FLOW_DESC viene en el comando: convierte la descripcion en una secuencia de pasos atomicos del Test Context API. Ejemplos canonicos:

Descripcion humano Llamada Test Engine
"abrir tab X" ctx->ItemClick("##tabs/X") o el path real del TabBar
"escribir 'hola' en el input search" ctx->ItemInput("Search", "hola")
"click boton Aceptar" ctx->ItemClick("Aceptar")
"verificar que aparece el modal Y" IM_CHECK(ctx->WindowInfo("Y").ID != 0)
"checkbox Z marcado" IM_CHECK(ctx->ItemIsChecked("Z"))
"menu File > Open" ctx->MenuClick("File/Open")

Ver cpp/vendor/imgui_test_engine/imgui_te_context.h para el catalogo completo de helpers.

4. Preparar la app para tests (idempotente)

Si es la primera vez que la app gana tests, hay que:

a) Hacer la funcion render() linkable desde otra TU

// Antes:    static void render() { ... }
// Despues:  void render() { ... }

b) Excluir int main() con guarda FN_TEST_BUILD

#ifndef FN_TEST_BUILD
int main() {
    return fn::run_app({...}, render);
}
#endif

Verifica con grep -n "FN_TEST_BUILD\|^static void render" "$APP_DIR/main.cpp". Si ya esta, no toques nada.

5. Generar/extender el archivo de tests

$APP_DIR/tests/<app>_tests.cpp — un solo archivo por app, varias IM_REGISTER_TEST dentro de register_tests().

Plantilla:

// E2E tests para <app> — Dear ImGui Test Engine.
// Construido solo con -DFN_BUILD_TESTS=ON. Reusa el mismo main.cpp con
// FN_TEST_BUILD definido para excluir su int main().

#include "app_base.h"
#include "imgui.h"
#include "imgui_te_engine.h"
#include "imgui_te_context.h"

void render();   // definido en <app>/main.cpp

static void register_tests(ImGuiTestEngine* e) {
    ImGuiTest* t = nullptr;

    t = IM_REGISTER_TEST(e, "<app>", "<test_name>");
    t->TestFunc = [](ImGuiTestContext* ctx) {
        ctx->SetRef("<window_title_exacto>");
        // ... pasos del flujo
    };

    // mas tests aqui
}

int main() {
    fn::AppConfig cfg{};
    cfg.title  = "<app>_tests";
    cfg.width  = 1280;
    cfg.height = 800;
    return fn::run_app_test(cfg, render, register_tests);
}

Si el archivo ya existe: AGREGA un nuevo IM_REGISTER_TEST dentro de la funcion register_tests existente. NO sobreescribas tests previos.

6. Actualizar CMakeLists.txt (idempotente)

Si $APP_DIR/CMakeLists.txt no tiene aun el bloque de tests, agregar al final:

# --- E2E tests (opt-in via -DFN_BUILD_TESTS=ON) ---
if(FN_BUILD_TESTS)
    add_imgui_app(<app>_tests
        main.cpp
        tests/<app>_tests.cpp
        # mismos .cpp del registry que la app principal
        ${CMAKE_SOURCE_DIR}/functions/<dom>/<func>.cpp
        ...
    )
    target_compile_definitions(<app>_tests PRIVATE FN_TEST_BUILD)
endif()

Las fuentes deben replicar las del target principal (mismas funciones del registry). Si la app ya tiene un bloque if(FN_BUILD_TESTS), no lo dupliques.

7. Build

cd "$ROOT/cpp"
cmake -S . -B build/linux_tests -DFN_BUILD_TESTS=ON 2>&1 | tail -5
cmake --build build/linux_tests --target ${APP_ARG}_tests -j4 2>&1 | tail -20

Si el build falla:

  • Errores de compilacion en tests/...cpp → revisa nombres de widgets/paths con el codigo real de main.cpp.
  • "undefined reference to render" → falta quitar static o falta el #ifndef FN_TEST_BUILD en main.cpp.
  • "multiple definition of main" → falta el target_compile_definitions(... FN_TEST_BUILD) en CMakeLists.

8. Ejecutar (headless en WSL)

WSL no tiene GLX 4.3 nativo — los tests corren bajo xvfb con software renderer Mesa. Wrapper canonico:

cd "$ROOT/cpp/build/linux_tests"
TEST_BIN="$(find . -name "${APP_ARG}_tests" -type f -executable | head -1)"
[ -z "$TEST_BIN" ] && { echo "no encuentro el binario de tests"; exit 1; }

timeout 90 xvfb-run -a -s "-screen 0 1280x800x24" \
    env LIBGL_ALWAYS_SOFTWARE=1 GALLIUM_DRIVER=llvmpipe \
    "$TEST_BIN" 2>&1
EXIT=$?
echo "EXIT: $EXIT"

Si en el host el usuario tiene GL nativo y DISPLAY funciona, el wrapper xvfb-run sigue siendo seguro (ejecuta dentro de su propio display).

9. Reportar

  • Si EXIT == 0 y la salida contiene Tests Result: OK → reporta N/M tests passed con la lista de tests ejecutados.
  • Si EXIT != 0 → muestra el bloque de log del test fallido (test engine imprime el path del widget que no encontro, el archivo y la linea del IM_CHECK que fallo). Sugiere correcciones (widget renombrado, path mal escrito, race entre frames — usar ctx->Yield()).

10. Despues de añadir tests

NO ejecutes fn index automaticamente — los tests no son funciones del registry, son artefactos de la app. Si el usuario los queria persistir, ya los tiene en <app_dir>/tests/.

Si la app es un sub-repo (lo normal segun ADR 0002), recordar al usuario que los archivos nuevos viven dentro del repo de la app y necesitan un commit alli (no en fn_registry).

Referencias

  • API de Test Context: cpp/vendor/imgui_test_engine/imgui_te_context.h
  • API del engine: cpp/vendor/imgui_test_engine/imgui_te_engine.h
  • Implementacion del harness: cpp/framework/app_base.cpp (funcion fn::run_app_test)
  • Ejemplo canonico: cpp/apps/chart_demo/tests/chart_demo_tests.cpp
  • Licencia del test engine: personal/open-source gratis (cpp/vendor/imgui_test_engine/LICENSE.txt)