Skip to content

Commit

Permalink
Write more tests for debugger
Browse files Browse the repository at this point in the history
  • Loading branch information
iafisher committed Dec 31, 2018
1 parent 14f551c commit fcb302d
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 19 deletions.
47 changes: 28 additions & 19 deletions hera/debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,26 +62,35 @@ def loop(self):
if not response:
continue

cmd, *args = response.split()
cmd = cmd.lower()
if "break".startswith(cmd):
self.exec_break(args)
elif "continue".startswith(cmd):
self.exec_continue(args)
elif "next".startswith(cmd):
self.exec_next(args)
elif "print".startswith(cmd):
self.exec_print(args)
elif "restart".startswith(cmd):
self.exec_restart(args)
elif "skip".startswith(cmd):
self.exec_skip(args)
elif "quit".startswith(cmd):
if not self.handle_command(response):
break
elif "help".startswith(cmd):
print(_HELP_MSG)
else:
print('Unknown command "{}"'.format(cmd))

def handle_command(self, response):
"""Parse the command and execute it. Return False if the loop should exit, and
True otherwise.
"""
cmd, *args = response.split()
cmd = cmd.lower()
if "break".startswith(cmd):
self.exec_break(args)
elif "continue".startswith(cmd):
self.exec_continue(args)
elif "next".startswith(cmd):
self.exec_next(args)
elif "print".startswith(cmd):
self.exec_print(args)
elif "restart".startswith(cmd):
self.exec_restart(args)
elif "skip".startswith(cmd):
self.exec_skip(args)
elif "help".startswith(cmd):
print(_HELP_MSG)
elif "quit".startswith(cmd):
return False
else:
print("{} is not a known command.".format(cmd))

return True

def exec_break(self, args):
if len(args) > 1:
Expand Down
147 changes: 147 additions & 0 deletions test/test_debugger.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
from unittest.mock import patch

from hera.debugger import Debugger
from hera.parser import parse
Expand All @@ -23,6 +24,152 @@ def debugger():
SAMPLE_PROGRAM = preprocess(_tree, _symtab)


def test_print_breakpoints(debugger, capsys):
should_continue = debugger.handle_command("break")

assert should_continue
assert capsys.readouterr().out == "No breakpoints set.\n"


def test_set_breakpoint(debugger):
assert len(debugger.breakpoints) == 0

should_continue = debugger.handle_command("break 2")

assert should_continue
assert len(debugger.breakpoints) == 1
assert 0 in debugger.breakpoints
assert debugger.breakpoints[0] == "2"


def test_set_breakpoint_not_on_line_of_code(debugger, capsys):
should_continue = debugger.handle_command("break 1")

assert should_continue
assert len(debugger.breakpoints) == 0
assert capsys.readouterr().out == "Error: could not find corresponding line.\n"


def test_set_unparseable_breakpoint(debugger, capsys):
should_continue = debugger.handle_command("break $$$")

assert should_continue
assert len(debugger.breakpoints) == 0
assert capsys.readouterr().out == "Error: could not parse argument.\n"


def test_execute_break_with_too_many_args(debugger, capsys):
should_continue = debugger.handle_command("break 1 2 3")

assert should_continue
assert len(debugger.breakpoints) == 0
assert capsys.readouterr().out == "break takes zero or one arguments.\n"


def test_execute_abbreviated_break(debugger):
with patch("hera.debugger.Debugger.exec_break") as mock_exec_break:
debugger.handle_command("break 7")
assert mock_exec_break.call_count == 1

args = mock_exec_break.call_args[0]
assert len(args) == 1
assert args[0] == ["7"]

kwargs = mock_exec_break.call_args[1]
assert len(kwargs) == 0


def test_execute_next(debugger):
assert debugger.vm.registers[1] == 0
assert debugger.vm.pc == 0

should_continue = debugger.handle_command("next")

assert should_continue
assert debugger.vm.registers[1] == 10
assert debugger.vm.pc == 2


def test_execute_abbreviated_next(debugger):
with patch("hera.debugger.Debugger.exec_next") as mock_exec_next:
debugger.handle_command("n")
assert mock_exec_next.call_count == 1


def test_execute_continue_with_breakpoint(debugger):
debugger.breakpoints[4] = ""

should_continue = debugger.handle_command("continue")

assert should_continue
assert debugger.vm.registers[1] == 10
assert debugger.vm.registers[2] == 32
assert debugger.vm.registers[3] == 0
assert debugger.vm.pc == 4


def test_execute_continue_without_breakpoint(debugger):
should_continue = debugger.handle_command("continue")

assert should_continue
assert debugger.vm.registers[1] == 10
assert debugger.vm.registers[2] == 32
assert debugger.vm.registers[3] == 42
assert debugger.vm.pc == 5


def test_execute_abbreviated_continue(debugger):
with patch("hera.debugger.Debugger.exec_continue") as mock_exec_continue:
debugger.handle_command("c")
assert mock_exec_continue.call_count == 1


def test_print_register(debugger, capsys):
debugger.vm.registers[7] = 42

should_continue = debugger.handle_command("print r7")

assert should_continue
assert capsys.readouterr().out == "r7 = 0x002a = 42 = '*'\n"


def test_print_invalid_register(debugger, capsys):
should_continue = debugger.handle_command("print r17")

assert should_continue
assert capsys.readouterr().out == "r17 is not a valid register.\n"


def test_print_program_counter(debugger, capsys):
debugger.vm.pc = 7

should_continue = debugger.handle_command("print PC")

assert should_continue
assert capsys.readouterr().out == "PC = 7\n"


def test_print_memory_location(debugger, capsys):
debugger.vm.assign_memory(97, 1000)

should_continue = debugger.handle_command("print m[97]")

assert should_continue
assert capsys.readouterr().out == "M[97] = 1000\n"


def test_execute_quit(debugger):
assert debugger.handle_command("quit") is False
assert debugger.handle_command("q") is False


def test_execute_unknown_command(debugger, capsys):
should_continue = debugger.handle_command("whatever")

assert should_continue
assert capsys.readouterr().out == "whatever is not a known command.\n"


def test_resolve_location(debugger):
assert debugger.resolve_location(2) == 0

Expand Down

0 comments on commit fcb302d

Please sign in to comment.