Skip to content

🔍 Failure Diagnosis - Flowchart de Decisión


🎯 Clasificación Rápida

┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  [Test falló] → ¿Falla consistentemente?                           │
│                      ↓           ↓                                  │
│                     SÍ          NO                                  │
│                      ↓           ↓                                  │
│               🐛 Bug real   ⚡ Flaky test                           │
│                                                                     │
│  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  │
│                                                                     │
│  Prueba de consistencia:                                            │
│  Run 10 veces → Si falla todas = Bug real                          │
│               → Si falla algunas = Flaky                            │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

🐛 Bug Real: Patrones Comunes

╔═════════════════════════════════════════════════════════════════════╗
║                                                                     ║
║  CATEGORÍAS DE BUGS REALES                                          ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  🔴 ASSERTION FAILURE                                               │
│                                                                     │
│  Síntoma:                                                           │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │ FAILED:                                                       │ │
│  │   REQUIRE( actual == expected )                               │ │
│  │ with expansion:                                               │ │
│  │   440.123 == 440.0                                            │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  Investigar:                                                        │
│  → ¿Cambió el algoritmo recientemente?                             │
│  → ¿Es regresión de un commit específico?                          │
│  → ¿Tolerancia demasiado estricta?                                 │
│  → ¿Test expectation desactualizada?                               │
│                                                                     │
│  Acción:                                                            │
│  1. git bisect para encontrar commit culpable                      │
│  2. Review cambios en ese commit                                   │
│  3. Verificar si bug o test incorrecto                             │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  💥 CRASH / SEGFAULT                                                │
│                                                                     │
│  Síntoma:                                                           │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │ Segmentation fault (core dumped)                              │ │
│  │ Signal: SIGSEGV                                               │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  Causas comunes:                                                    │
│  → Null pointer dereference                                        │
│  → Out of bounds array access                                      │
│  → Use after free                                                  │
│  → Stack overflow (infinite recursion)                             │
│                                                                     │
│  Diagnóstico:                                                       │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │ # GDB debugger                                                │ │
│  │ gdb ./test_executable core                                    │ │
│  │ (gdb) bt          # Backtrace                                 │ │
│  │ (gdb) frame 0     # Inspect crash frame                       │ │
│  │ (gdb) print var   # Inspect variables                         │ │
│  │                                                               │ │
│  │ # AddressSanitizer                                            │ │
│  │ cmake -DASAN=ON ..                                            │ │
│  │ ./test_executable  # Will pinpoint exact line                │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  ⏱️ TIMEOUT                                                         │
│                                                                     │
│  Síntoma:                                                           │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │ Test timed out after 30 seconds                               │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  Causas comunes:                                                    │
│  → Infinite loop                                                   │
│  → Deadlock (thread waiting forever)                               │
│  → Performance regression (código muy lento)                       │
│  → Blocking I/O sin timeout                                        │
│                                                                     │
│  Diagnóstico:                                                       │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │ # Attach debugger mientras corre                              │ │
│  │ gdb -p <pid>                                                  │ │
│  │ (gdb) thread apply all bt  # Backtrace de todos threads      │ │
│  │                                                               │ │
│  │ # Profile para encontrar hotspot                              │ │
│  │ perf record ./test_executable                                 │ │
│  │ perf report                                                   │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  💾 MEMORY LEAK                                                     │
│                                                                     │
│  Síntoma:                                                           │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │ ==12345== LEAK SUMMARY:                                       │ │
│  │ ==12345==    definitely lost: 1,024 bytes in 1 blocks         │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  Causas comunes:                                                    │
│  → new sin delete                                                  │
│  → malloc sin free                                                 │
│  → Smart pointer circular reference                                │
│  → Resource leak (file handles, sockets)                           │
│                                                                     │
│  Diagnóstico:                                                       │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │ # Valgrind (Linux/macOS)                                      │ │
│  │ valgrind --leak-check=full ./test_executable                  │ │
│  │                                                               │ │
│  │ # AddressSanitizer con leak detection                         │ │
│  │ ASAN_OPTIONS=detect_leaks=1 ./test_executable                 │ │
│  │                                                               │ │
│  │ # Windows: Visual Studio memory profiler                      │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

⚡ Flaky Test: Causas

╔═════════════════════════════════════════════════════════════════════╗
║                                                                     ║
║  CATEGORÍAS DE FLAKINESS                                            ║
║                                                                     ║
║  🎲 Race conditions      → Threading bug                           ║
║  ⏰ Timing dependencies   → Sleep/wait unreliable                  ║
║  🌐 External dependencies → Network, filesystem                    ║
║  🎰 Random data           → Seed not fixed                         ║
║  💻 Platform-specific     → OS/compiler differences                ║
║  📊 Floating point        → Rounding differences                   ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  🎲 RACE CONDITIONS                                                 │
│                                                                     │
│  Ejemplo problemático:                                              │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │ // Thread 1                                                   │ │
│  │ data = new AudioBuffer();                                     │ │
│  │                                                               │ │
│  │ // Thread 2                                                   │ │
│  │ process(data);  // ⚠️ Puede ejecutar antes de new             │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  Síntoma:                                                           │
│  → Falla ~20% de las veces                                         │
│  → Falla más en máquinas rápidas/multicore                         │
│  → Falla difiere entre runs                                        │
│                                                                     │
│  Solución:                                                          │
│  → NO testear multithreading directamente en unit tests            │
│  → Usar mocks para simular threading                               │
│  → Dedicated integration tests con sincronización explícita        │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  ⏰ TIMING DEPENDENCIES                                             │
│                                                                     │
│  Ejemplo problemático:                                              │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │ startAsyncOperation();                                        │ │
│  │ sleep(100);  // "Should be enough"                            │ │
│  │ REQUIRE(operation.isComplete());  // ⚠️ No garantizado        │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  Síntoma:                                                           │
│  → Falla en máquinas lentas (CI servers)                           │
│  → Falla bajo carga                                                │
│  → Timing assumptions inválidos                                    │
│                                                                     │
│  Solución:                                                          │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │ // ✅ Wait explícito con condition                            │ │
│  │ bool waitUntil(condition, timeout) {                          │ │
│  │     auto start = now();                                       │ │
│  │     while (!condition() && now() - start < timeout) {        │ │
│  │         sleep(10ms);                                          │ │
│  │     }                                                         │ │
│  │     return condition();                                       │ │
│  │ }                                                             │ │
│  │                                                               │ │
│  │ REQUIRE(waitUntil([&]{ return op.isComplete(); }, 5s));      │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  🌐 EXTERNAL DEPENDENCIES                                           │
│                                                                     │
│  Dependencias problemáticas:                                        │
│  → Network availability                                            │
│  → Specific file existing in filesystem                            │
│  → System time/date                                                │
│  → Environment variables                                           │
│  → Hardware (audio device, GPU)                                    │
│                                                                     │
│  Síntoma:                                                           │
│  → Pasa localmente, falla en CI                                    │
│  → Falla en diferentes plataformas                                 │
│  → Falla cuando ejecutas desde diferente directorio                │
│                                                                     │
│  Solución:                                                          │
│  → Mock filesystem (usar test fixtures)                            │
│  → Mock network (no hacer real HTTP calls)                         │
│  → Inject time source (no usar system clock directamente)          │
│  → Inject dependencies (dependency injection pattern)              │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  🎰 RANDOM DATA                                                     │
│                                                                     │
│  Ejemplo problemático:                                              │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │ float testValue = random();  // ⚠️ Different cada run         │ │
│  │ auto result = process(testValue);                             │ │
│  │ REQUIRE(result > 0);  // Puede fallar con ciertos valores    │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  Síntoma:                                                           │
│  → Resultados no reproducibles                                     │
│  │  Falla esporádicamente sin patrón                               │
│  → Difícil de debuggear (no puedes reproducir)                     │
│                                                                     │
│  Solución:                                                          │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │ // ✅ Fix random seed                                         │ │
│  │ srand(42);  // Same sequence every run                        │ │
│  │                                                               │ │
│  │ // O mejor: usar valores determinísticos                      │ │
│  │ float testValue = 0.5f;  // Explicit, reproducible           │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

🔧 Quick Fixes Reference

┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  Problema                │ Solución Rápida                         │
│  ════════════════════════╪═════════════════════════════════        │
│  Timing issue            │ Remove sleeps, use mocks                │
│  Race condition          │ Synchronization primitives              │
│  Random failures         │ Fix seed: srand(42)                     │
│  Platform variance       │ Adjust tolerances                       │
│  Filesystem dependency   │ Mock filesystem                         │
│  Float comparison        │ Use REQUIRE_THAT(x, Approx(y))          │
│  Network dependency      │ Mock HTTP client                        │
│  System time dependency  │ Inject clock, use fake time             │
│  Memory leak             │ Smart pointers, RAII                    │
│  Null pointer            │ Add null checks, use optional<>         │
│                          │                                         │
└─────────────────────────────────────────────────────────────────────┘

🎯 Workflow de Debug

╔═════════════════════════════════════════════════════════════════════╗
║                                                                     ║
║  FLOWCHART DE DEBUG                                                 ║
║                                                                     ║
║  1️⃣ Reproduce localmente                                           ║
║     → Run test en tu máquina                                       ║
║     → Si no reproduce: es flaky o environment-specific             ║
║                                                                     ║
║  2️⃣ Run en aislamiento                                             ║
║     → ./test_specific_case (no suite completo)                     ║
║     → Elimina interferencia de otros tests                         ║
║                                                                     ║
║  3️⃣ Increase verbosity/logging                                     ║
║     → CATCH_CONFIG_RUNNER con custom reporter                      ║
║     → Agregar debug prints en código bajo test                     ║
║                                                                     ║
║  4️⃣ Debugger breakpoint                                            ║
║     → Set breakpoint en línea de falla                             ║
║     → Inspect variables, stack, memory                             ║
║                                                                     ║
║  5️⃣ Inspect state                                                  ║
║     → ¿Qué valores tienen las variables?                           ║
║     → ¿Estado de objetos correcto?                                 ║
║     → ¿Punteros válidos?                                           ║
║                                                                     ║
║  6️⃣ Fix → Verify → Document                                        ║
║     → Apply fix                                                    ║
║     → Run test 100 veces para confirmar                            ║
║     → Document el bug y fix en commit message                      ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

💡 Prevention: Evitar Failures Futuros

┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  🛡️ DEFENSIVE TESTING PRACTICES                                    │
│                                                                     │
│  ✅ Siempre fix random seeds                                        │
│  ✅ Mock external dependencies                                      │
│  ✅ Use waitUntil() no sleep()                                      │
│  ✅ Floating point: use Approx() con tolerance                      │
│  ✅ Null checks antes de dereference                                │
│  ✅ RAII para resource management                                   │
│  ✅ Run tests con sanitizers (ASAN, TSAN, UBSAN)                    │
│  ✅ Run tests en paralelo para detectar races                       │
│  ✅ CI debe run cada test múltiples veces                           │
│  ✅ Document assumptions en comments                                │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

🔍 Tools de Diagnóstico

┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  Tool                    │ Qué detecta                             │
│  ════════════════════════╪═════════════════════════════════        │
│  GDB/LLDB                │ Crashes, inspección de estado           │
│  Valgrind                │ Memory leaks, invalid access            │
│  AddressSanitizer        │ Memory errors, leaks                    │
│  ThreadSanitizer         │ Race conditions, data races             │
│  UndefinedBehaviorSan    │ Undefined behavior (overflow, etc)      │
│  perf/VTune              │ Performance bottlenecks                 │
│  strace/ltrace           │ System calls, library calls             │
│  Catch2 reporters        │ Custom test output formatting           │
│                          │                                         │
└─────────────────────────────────────────────────────────────────────┘

📋 Checklist: Before Filing Bug

□ Reproducido localmente (no solo en CI)
□ Reproducido en aislamiento (single test)
□ Verificado que no es flaky (run 10+ veces)
□ Stack trace capturado (si crash)
□ Minimal reproducible example creado
□ git bisect ejecutado para encontrar culprit commit
□ Logs y output relevante guardados
□ Environment details documentados (OS, compiler, etc)