From 86feaab0ddef6cf5da5bf660fc0a497a67b035ce Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 28 Aug 2026 17:50:57 +0200 Subject: [PATCH] test(core): add debugger and watchpoint tests --- tests/Chip8.Tests/DebuggerTests.cs | 115 +++++++++++++++++++++++++++ tests/Chip8.Tests/WatchpointTests.cs | 101 +++++++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 tests/Chip8.Tests/DebuggerTests.cs create mode 100644 tests/Chip8.Tests/WatchpointTests.cs diff --git a/tests/Chip8.Tests/DebuggerTests.cs b/tests/Chip8.Tests/DebuggerTests.cs new file mode 100644 index 0000000..0c8141d --- /dev/null +++ b/tests/Chip8.Tests/DebuggerTests.cs @@ -0,0 +1,115 @@ +using Chip8.Core; +using Xunit; + +namespace Chip8.Tests; + +public class DebuggerTests +{ + [Fact] + public void stepAdvancesProgramCounter() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0x6001); + var debugger = new Debugger(machine); + debugger.step(); + Assert.Equal(0x202, machine.pc); + Assert.Equal(1, machine.v[0]); + } + + [Fact] + public void peekAndDisassembleShowUpcomingInstruction() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0xA300); + var debugger = new Debugger(machine); + Assert.Equal(0xA300, debugger.peekOpcode()); + Assert.Equal("LD I, 0x300", debugger.disassembleNext()); + } + + [Fact] + public void breakpointStopsRunBeforeExecuting() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0x6001, 0x6102, 0x6203, 0x1200); + var debugger = new Debugger(machine); + debugger.addBreakpoint(0x204); + + var outcome = debugger.run(100); + + Assert.Equal(StopReason.Breakpoint, outcome.reason); + Assert.Equal(2, outcome.steps); + Assert.Equal(0x204, machine.pc); + Assert.Equal(1, machine.v[0]); + Assert.Equal(2, machine.v[1]); + Assert.Equal(0, machine.v[2]); + } + + [Fact] + public void runWithoutBreakpointReachesStepLimit() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0x1200); + var debugger = new Debugger(machine); + var outcome = debugger.run(50); + Assert.Equal(StopReason.StepLimit, outcome.reason); + Assert.Equal(50, outcome.steps); + } + + [Fact] + public void runStopsWhenInterpreterExits() + { + var machine = TestHarness.withProgram(Platform.SuperChip, 0x6001, 0x00FD); + var debugger = new Debugger(machine); + var outcome = debugger.run(100); + Assert.Equal(StopReason.Exited, outcome.reason); + Assert.True(machine.exited); + } + + [Fact] + public void snapshotCapturesRegistersStackAndTimers() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0x2204); + var debugger = new Debugger(machine); + machine.v[5] = 0x9A; + machine.delayTimer = 7; + + debugger.step(); + var snapshot = debugger.snapshot(); + + Assert.Equal(0x204, snapshot.pc); + Assert.Equal(1, snapshot.sp); + Assert.Single(snapshot.stack); + Assert.Equal(0x202, snapshot.stack[0]); + Assert.Equal(0x9A, snapshot.v[5]); + Assert.Equal(7, snapshot.delayTimer); + Assert.Contains("PC=0x0204", snapshot.format()); + Assert.Contains("V5=9A", snapshot.format()); + } + + [Fact] + public void snapshotIsNotAliasedToLiveState() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0x6001); + var debugger = new Debugger(machine); + var snapshot = debugger.snapshot(); + machine.v[0] = 0xFF; + Assert.Equal(0, snapshot.v[0]); + } + + [Fact] + public void readMemoryReturnsRequestedWindow() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0xABCD); + var debugger = new Debugger(machine); + var window = debugger.readMemory(0x200, 2); + Assert.Equal(0xAB, window[0]); + Assert.Equal(0xCD, window[1]); + } + + [Fact] + public void breakpointCanBeRemoved() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0x1200); + var debugger = new Debugger(machine); + debugger.addBreakpoint(0x200); + Assert.True(debugger.hasBreakpoint(0x200)); + debugger.removeBreakpoint(0x200); + Assert.False(debugger.hasBreakpoint(0x200)); + } +} diff --git a/tests/Chip8.Tests/WatchpointTests.cs b/tests/Chip8.Tests/WatchpointTests.cs new file mode 100644 index 0000000..1e10fa2 --- /dev/null +++ b/tests/Chip8.Tests/WatchpointTests.cs @@ -0,0 +1,101 @@ +using Chip8.Core; +using Xunit; + +namespace Chip8.Tests; + +public class WatchpointTests +{ + [Fact] + public void memoryWatchStopsWhenTheByteChanges() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0xA400, 0x6005, 0xF055, 0x1206); + var debugger = new Debugger(machine); + debugger.watchMemory(0x400); + + RunResult outcome = debugger.run(100); + + Assert.Equal(StopReason.Watchpoint, outcome.reason); + Assert.Equal(3, outcome.steps); + Assert.Equal(0x400, outcome.triggeredAddress); + Assert.Equal(5, machine.memory[0x400]); + Assert.Contains("memory 0x0400", debugger.lastWatchDescription); + } + + [Fact] + public void registerWatchStopsWhenTheRegisterChanges() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0x6005, 0x1202); + var debugger = new Debugger(machine); + debugger.watchRegister(0); + + RunResult outcome = debugger.run(50); + + Assert.Equal(StopReason.Watchpoint, outcome.reason); + Assert.Equal(1, outcome.steps); + Assert.Contains("V0", debugger.lastWatchDescription); + } + + [Fact] + public void unwatchedMemoryDoesNotStop() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0xA400, 0x6005, 0xF055, 0x1206); + var debugger = new Debugger(machine); + debugger.watchMemory(0x400); + debugger.clearWatches(); + + RunResult outcome = debugger.run(20); + + Assert.Equal(StopReason.StepLimit, outcome.reason); + } + + [Fact] + public void stepOverRunsThroughACall() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0x2204, 0x6042, 0x0000, 0x00EE); + var debugger = new Debugger(machine); + + RunResult outcome = debugger.stepOver(100); + + Assert.Equal(0x202, machine.pc); + Assert.Equal(0, machine.v[0]); + Assert.Equal(3, outcome.steps); + } + + [Fact] + public void stepOverIsASingleStepForNonCalls() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0x6005, 0x1202); + var debugger = new Debugger(machine); + + debugger.stepOver(100); + + Assert.Equal(0x202, machine.pc); + Assert.Equal(5, machine.v[0]); + } + + [Fact] + public void runUntilStopsAtTheTargetAddress() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0x7001, 0x1200); + var debugger = new Debugger(machine); + + RunResult outcome = debugger.runUntil(0x202, 100); + + Assert.Equal(0x202, machine.pc); + Assert.Equal(StopReason.Breakpoint, outcome.reason); + } + + [Fact] + public void disassembleWindowDecodesFromProgramCounter() + { + var machine = TestHarness.withProgram(Platform.Chip8, 0xA300, 0x6001, 0x00EE); + var debugger = new Debugger(machine); + + var lines = debugger.disassembleWindow(3); + + Assert.Equal(3, lines.Count); + Assert.Equal(0x200, lines[0].address); + Assert.Equal("LD I, 0x300", lines[0].mnemonic); + Assert.Equal("RET", lines[2].mnemonic); + } +}