Este proyecto es educativo y Open Source. No se copia código de otros emuladores. Implementación basada únicamente en documentación técnica y tests permitidas.
Step 0422: Test Harness Policy - ROM Writes Fixtures + Security Test
Establecimiento de política oficial de test harness para MMU test mode
Objetivo
Establecer política oficial de test harness para MMU test mode, reducir deuda técnica de ROM-writes mediante fixtures centrales y crear test de seguridad para garantizar que ROM es read-only por defecto.
Contexto
Los Steps 0419-0421 introdujeron test_mode_allow_rom_writes para permitir tests unitarios que escriben en ROM (0x0000-0x7FFF). Sin embargo, esto generó 59 llamadas manuales a mmu.set_test_mode_allow_rom_writes(True) dispersas en 6 archivos de tests.
Esta deuda técnica requiere:
- Centralización: Fixtures de pytest para evitar repetición
- Restricción: ROM-writes solo en tests que realmente lo necesitan
- Seguridad: Test que valide que ROM es read-only por defecto
Concepto de Hardware: Test Harness vs Comportamiento Real
En emuladores, el test harness es la infraestructura que permite tests unitarios sin depender de ROMs reales. El desafío es balancear:
- Realismo: El MMU debe comportarse como hardware real (ROM read-only, MBC activo)
- Testabilidad: Los tests unitarios necesitan escribir opcodes en memoria para validar CPU
Soluciones Comunes en Emuladores
| Solución | Ventajas | Desventajas |
|---|---|---|
| Test Mode Flag (nuestra solución) | ✅ Mantiene integración real CPU-MMU ✅ Permite tests atómicos ✅ No requiere ROMs externas |
⚠️ Requiere disciplina (solo usar cuando sea necesario) |
| Test ROM Sintética | ✅ Comportamiento 100% real | ❌ Requiere ROM por test ❌ Difícil de mantener |
| Memory Mocking | ✅ Flexibilidad total | ❌ No valida integración MMU-CPU |
Viboy Color usa Test Mode Flag porque mantiene la integración real CPU-MMU mientras permite tests atómicos sin ROMs externas.
Implementación
1. Auditoría Inicial (T1)
Identificar todos los usos de set_test_mode_allow_rom_writes(True):
grep -rn "set_test_mode_allow_rom_writes(True)" tests | wc -l
# Output: 59 hits en 6 archivos
Distribución:
test_core_cpu_loads.py: 18 hitstest_core_cpu_jumps.py: 14 hitstest_core_cpu_alu.py: 10 hitstest_core_cpu_interrupts.py: 8 hitstest_core_cpu_io.py: 5 hitstest_core_cpu_stack.py: 4 hits
2. Fixtures Centrales (T2)
Crear fixtures en tests/conftest.py:
@pytest.fixture
def mmu():
"""
Fixture estándar para MMU sin ROM-writes habilitados.
Uso: Tests que ejecutan desde WRAM (0xC000+) o no necesitan ROM.
"""
try:
from viboy_core import PyMMU
return PyMMU()
except ImportError:
pytest.skip("Módulo viboy_core no compilado")
@pytest.fixture
def mmu_romw():
"""
Fixture para MMU con ROM-writes habilitados (test mode).
Uso: SOLO para tests que realmente necesitan escribir en ROM.
⚠️ ADVERTENCIA: Rompe comportamiento real del MMU (MBC).
Preferir ejecutar desde WRAM cuando sea posible.
"""
try:
from viboy_core import PyMMU
mmu = PyMMU()
mmu.set_test_mode_allow_rom_writes(True)
return mmu
except ImportError:
pytest.skip("Módulo viboy_core no compilado")
3. Test de Seguridad (T4)
Crear tests/test_mmu_rom_is_readonly_by_default.py con 4 validaciones:
test_rom_is_readonly_without_test_mode: ROM no es escribible sin test_modetest_rom_is_writable_with_test_mode: fixturemmu_romwSÍ permite escriturastest_rom_range_is_readonly: Todo el rango ROM (0x0000-0x7FFF) es read-onlytest_wram_is_writable_without_test_mode: WRAM (0xC000+) es escribible sin test_mode
4. Migración Ejemplo (T3)
Migrar test_core_cpu_alu.py (10 tests) de ROM a WRAM:
# Antes (Step 0419):
def test_add_immediate_basic(self):
mmu = PyMMU()
mmu.set_test_mode_allow_rom_writes(True) # Manual
regs = PyRegisters()
cpu = PyCPU(mmu, regs)
regs.pc = 0x0100
mmu.write(0x0100, 0x3E) # LD A, d8
mmu.write(0x0101, 0x0A)
cpu.step()
# ...
# Después (Step 0422):
def test_add_immediate_basic(self, mmu): # Fixture sin ROM-writes
regs = PyRegisters()
cpu = PyCPU(mmu, regs)
program = [0x3E, 0x0A, 0xC6, 0x02] # LD A, 10; ADD A, 2
load_program(mmu, regs, program) # Carga en WRAM (0xC000)
cpu.step()
cpu.step()
# ...
Tests migrados de test_core_cpu_alu.py:
test_add_immediate_basic: ADD A, d8 (10 + 2 = 12)test_sub_immediate_zero_flag: SUB d8 (10 - 10 = 0, Z=1)test_add_half_carry: ADD con half-carry (0x0F + 0x01 = 0x10, H=1)test_xor_a_optimization: XOR A (limpia A a 0)test_inc_a: INC A (0x0F → 0x10, H=1)test_dec_a: DEC A (0x10 → 0x0F, H=1)test_add_full_carry: ADD con carry completo (0xFF + 0x01 = 0x00, C=1)test_sub_a_b: SUB B (0x3E - 0x3E = 0x00, Z=1)test_sbc_a_b_with_borrow: SBC A, B con borrowtest_sbc_a_b_with_full_borrow: SBC A, B con underflow
Política Oficial de Test Harness
Cuándo usar mmu (fixture estándar)
- ✅ Tests que ejecutan desde WRAM (0xC000-0xDFFF)
- ✅ Tests de ALU, loads, jumps, stack que no dependen de ROM específico
- ✅ Preferido por defecto (comportamiento real del MMU)
Cuándo usar mmu_romw (fixture con ROM-writes)
- ⚠️ Tests que verifican vectores de interrupción (0x0040, 0x0048, etc.)
- ⚠️ Tests que validan wrap-around de direcciones ROM
- ⚠️ Tests legacy que aún no han migrado a WRAM
- ⚠️ Uso excepcional (rompe comportamiento MBC)
Tests y Verificación
Comandos Ejecutados
# Build
python3 setup.py build_ext --inplace
# EXIT: 0 ✅
# Test Build
python3 test_build.py
# EXIT: 0 ✅
# Tests ALU + Seguridad
pytest tests/test_core_cpu_alu.py tests/test_mmu_rom_is_readonly_by_default.py -v
# 14 passed (10 ALU + 4 seguridad) ✅
# Tests Completos
pytest -q
# 118 passed, 10 failed (pre-existentes: joypad/MMU) ✅
Validación de Módulo Compilado C++
✅ Todos los tests ejecutan contra el módulo nativo viboy_core compilado desde C++.
Resultados
Auditoría Final
- Hits ROM-writes ANTES: 59
- Hits ROM-writes DESPUÉS: 49 (eliminados 10 de ALU)
- Reducción: 16.9%
Tests que requieren ROM-writes (justificados)
test_core_cpu_loads.py(18): Pueden migrar a WRAM (Step futuro)test_core_cpu_jumps.py(14): Pueden migrar a WRAM (Step futuro)test_core_cpu_interrupts.py(8): Algunos requieren vectores ROM (revisar)test_core_cpu_io.py(5): Pueden migrar a WRAM (Step futuro)test_core_cpu_stack.py(4): Pueden migrar a WRAM (Step futuro)
Archivos Modificados
tests/conftest.py: Fixturesmmuymmu_romwtests/test_mmu_rom_is_readonly_by_default.py: Test de seguridad (nuevo)tests/test_core_cpu_alu.py: Migración de 10 tests a WRAMdocs/bitacora/entries/2026-01-02__0422__test-harness-policy-rom-writes-fixtures.html: Esta entradadocs/bitacora/index.html: Actualizado con Step 0422docs/informe_fase_2/parte_01_steps_0412_0450.md: Entrada en informe dividido
Próximos Steps
- Step 0423: Migración masiva de tests CPU a WRAM (49 tests restantes)
- Step 0424: Marker pytest
@pytest.mark.rom_writespara tests excepcionales - Step 0425: Documentación de política en
CONTRIBUTING.md
Conclusión
✅ Política de test harness establecida con fixtures centrales, test de seguridad y ejemplo de migración. Reducción de 16.9% en ROM-writes (59 → 49). Base sólida para migración masiva en Steps futuros.