1
0
mirror of https://github.com/mnaberez/py65.git synced 2024-05-31 12:41:31 +00:00
py65/py65/tests/test_monitor.py
Mike Naberezny db247b9765 Revert "Remove support for Python 2"
This reverts commit ca02d12fc6.
2023-11-18 15:03:03 -08:00

1283 lines
40 KiB
Python

import unittest
import sys
import os
import tempfile
from py65.monitor import Monitor
try:
from StringIO import StringIO
except ImportError: # Python 3
from io import StringIO
class MonitorTests(unittest.TestCase):
# line processing
def test__preprocess_line_removes_leading_dots_after_whitespace(self):
mon = Monitor()
self.assertEqual('help', mon._preprocess_line(' ...help'))
def test__preprocess_line_removes_leading_and_trailing_whitespace(self):
mon = Monitor()
self.assertEqual('help', mon._preprocess_line(' \t help \t '))
def test__preprocess_line_rewrites_shortcut_when_alone_on_line(self):
mon = Monitor()
self.assertEqual('assemble', mon._preprocess_line(' a'))
def test__preprocess_line_rewrites_shortcut_with_arguments_on_line(self):
mon = Monitor()
self.assertEqual('assemble c000', mon._preprocess_line('a c000'))
def test__preprocess_line_removes_semicolon_comments(self):
mon = Monitor()
self.assertEqual('assemble', mon._preprocess_line('a ;comment'))
def test__preprocess_line_does_not_remove_semicolons_in_quotes(self):
mon = Monitor()
self.assertEqual('assemble lda #$";"',
mon._preprocess_line('a lda #$";" ;comment'))
def test__preprocess_line_does_not_remove_semicolons_in_apostrophes(self):
mon = Monitor()
self.assertEqual("assemble lda #$';'",
mon._preprocess_line("assemble lda #$';' ;comment"))
# add_breakpoint
def test_shortcut_for_add_breakpoint(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('ab')
out = stdout.getvalue()
self.assertTrue(out.startswith('add_breakpoint'))
def test_do_add_breakpoint_syntax_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_add_breakpoint('')
out = stdout.getvalue()
self.assertTrue(out.startswith("Syntax error:"))
def test_do_add_breakpoint_adds_number(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_add_breakpoint('ffd2')
out = stdout.getvalue()
self.assertTrue(out.startswith("Breakpoint 0 added at $FFD2"))
self.assertTrue(0xffd2 in mon._breakpoints)
def test_do_add_breakpoint_adds_label(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
address_parser = mon._address_parser
address_parser.labels['chrout'] = 0xffd2
mon.do_add_breakpoint('chrout')
out = stdout.getvalue()
self.assertTrue(out.startswith("Breakpoint 0 added at $FFD2"))
self.assertTrue(0xffd2 in mon._breakpoints)
# add_label
def test_shortcut_for_add_label(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('al')
out = stdout.getvalue()
self.assertTrue(out.startswith('add_label'))
def test_do_add_label_syntax_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_add_label('should be address space label')
out = stdout.getvalue()
err = "Syntax error: should be address space label\n"
self.assertTrue(out.startswith(err))
def test_do_add_label_overflow_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_add_label('$10000 toobig')
out = stdout.getvalue()
err = "Overflow error: $10000 toobig\n"
self.assertTrue(out.startswith(err))
def test_do_add_label_label_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_add_label('nonexistent foo')
out = stdout.getvalue()
err = "Label not found: nonexistent\n"
self.assertTrue(out.startswith(err))
def test_do_add_label_adds_label(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_add_label('$c000 foo')
address_parser = mon._address_parser
self.assertEqual(0xC000, address_parser.number('foo'))
def test_help_add_label(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_add_label()
out = stdout.getvalue()
self.assertTrue(out.startswith("add_label"))
# assemble
def test_shortcut_for_assemble(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('a')
out = stdout.getvalue()
self.assertTrue(out.startswith('assemble'))
def test_do_assemble_assembles_valid_statement(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_assemble('c000 lda #$ab')
mpu = mon._mpu
self.assertEqual(0xA9, mpu.memory[0xC000])
self.assertEqual(0xAB, mpu.memory[0xC001])
def test_do_assemble_outputs_disassembly(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_assemble('c000 lda #$ab')
out = stdout.getvalue()
self.assertEqual("$c000 a9 ab LDA #$ab\n", out)
def test_do_assemble_parses_start_address_label(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_add_label('c000 base')
mon.do_assemble('base rts')
mpu = mon._mpu
self.assertEqual(0x60, mpu.memory[0xC000])
def test_do_assemble_shows_bad_label_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_assemble('nonexistent rts')
out = stdout.getvalue()
self.assertEqual("Label not found: nonexistent\n", out)
def test_do_assemble_shows_bad_syntax_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_assemble('c000 foo')
out = stdout.getvalue()
self.assertEqual("Syntax error: foo\n", out)
def test_do_assemble_shows_overflow_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_assemble('c000 lda #$fff')
out = stdout.getvalue()
self.assertEqual("Overflow error: c000 lda #$fff\n", out)
def test_do_assemble_passes_addr_for_relative_branch_calc(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_assemble('4000 bvs $4005')
out = stdout.getvalue()
self.assertEqual("$4000 70 03 BVS $4005\n", out)
def test_do_assemble_constrains_address_to_valid_range(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_assemble("-1 lda #$ab")
out = stdout.getvalue()
self.assertEqual("Overflow error: -1 lda #$ab\n", out)
def test_help_assemble(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_assemble()
out = stdout.getvalue()
self.assertTrue("assemble <address>" in out)
# cd
def test_help_cd(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_cd()
out = stdout.getvalue()
self.assertTrue(out.startswith("cd <directory>"))
def test_do_cd_with_no_dir_shows_help(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_cd("")
out = stdout.getvalue()
self.assertTrue(out.startswith("cd <directory>"))
def test_do_cd_changes_cwd(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
here = os.path.abspath(os.path.dirname(__file__))
mon.do_cd(here)
out = stdout.getvalue()
self.assertTrue(out.startswith(here))
self.assertEqual(here, os.getcwd())
def test_do_cd_with_bad_dir_shows_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_cd("/path/to/a/nonexistent/directory")
out = stdout.getvalue()
self.assertTrue(out.startswith("Cannot change directory"))
# cycles
def test_help_cycles(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_cycles()
out = stdout.getvalue()
self.assertTrue(out.startswith("Display the total number of cycles"))
def test_do_cycles_shows_zero_initially(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_cycles("")
out = stdout.getvalue()
self.assertEqual(out, "0\n")
def test_do_cycles_shows_count_after_step(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._mpu.memory[0x0000] = 0xEA # => NOP (2 cycles)
mon._mpu.step()
mon.do_cycles("")
out = stdout.getvalue()
self.assertEqual(out, "2\n")
# delete_label
def test_shortcut_for_delete_label(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('dl')
out = stdout.getvalue()
self.assertTrue(out.startswith('delete_label'))
def test_do_delete_label_no_args_displays_help(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_delete_label('')
out = stdout.getvalue()
self.assertTrue(out.startswith('delete_label'))
def test_do_delete_label_with_bad_label_fails_silently(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_delete_label('non-existant-label')
out = stdout.getvalue()
self.assertEqual('', out)
def test_do_delete_label_with_delete_label(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._address_parser.labels['foo'] = 0xc000
mon.do_delete_label('foo')
self.assertFalse('foo' in mon._address_parser.labels)
out = stdout.getvalue()
self.assertEqual('', out)
# disassemble
def test_shortcut_for_disassemble(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('d')
out = stdout.getvalue()
self.assertTrue(out.startswith('disassemble'))
def test_help_disassemble(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_disassemble()
out = stdout.getvalue()
self.assertTrue(out.startswith('disassemble <address_range>'))
def test_disassemble_shows_help_when_given_extra_args(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_disassemble("c000 c001")
out = stdout.getvalue()
self.assertTrue(out.startswith('disassemble <address_range>'))
def test_disassemble_will_disassemble_one_address(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._mpu.memory[0xc000] = 0xEA # => NOP
mon._mpu.step()
mon.do_disassemble("c000")
out = stdout.getvalue()
disasm = "$c000 ea NOP\n"
self.assertEqual(out, disasm)
def test_disassemble_will_disassemble_an_address_range(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._mpu.memory[0xc000] = 0xEA # => NOP
mon._mpu.memory[0xc001] = 0xEA # => NOP
mon._mpu.step()
mon.do_disassemble("c000:c001")
out = stdout.getvalue()
disasm = "$c000 ea NOP\n$c001 ea NOP\n"
self.assertEqual(out, disasm)
def test_disassemble_wraps_an_instruction_around_memory(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._mpu.memory[0xffff] = 0x20 # => JSR
mon._mpu.memory[0x0000] = 0xD2 #
mon._mpu.memory[0x0001] = 0xFF # => $FFD2
mon.do_disassemble("ffff")
out = stdout.getvalue()
disasm = "$ffff 20 d2 ff JSR $ffd2\n"
self.assertEqual(out, disasm)
def test_disassemble_wraps_disassembly_list_around_memory(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._mpu.memory[0xffff] = 0x20 # => JSR
mon._mpu.memory[0x0000] = 0xD2
mon._mpu.memory[0x0001] = 0xFF # => $FFD2
mon._mpu.memory[0x0002] = 0x20 # => JSR
mon._mpu.memory[0x0003] = 0xE4
mon._mpu.memory[0x0004] = 0xFF # => $FFE4
mon._mpu.memory[0x0005] = 0xEA # => NOP
mon.do_disassemble("ffff:5")
out = stdout.getvalue()
disasm = ("$ffff 20 d2 ff JSR $ffd2\n"
"$0002 20 e4 ff JSR $ffe4\n"
"$0005 ea NOP\n")
self.assertEqual(out, disasm)
# fill
def test_shortcut_f_for_fill(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('f')
out = stdout.getvalue()
self.assertTrue(out.startswith('fill <address_range>'))
def test_shortcut_gt_for_fill(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('>')
out = stdout.getvalue()
self.assertTrue(out.startswith('fill <address_range>'))
def test_help_fill(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_fill()
out = stdout.getvalue()
self.assertTrue(out.startswith('fill <address_range>'))
def test_do_fill_with_no_args_shows_help(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_fill('')
out = stdout.getvalue()
self.assertTrue(out.startswith('fill <address_range>'))
def test_do_fill_will_fill_one_address(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._mpu.memory[0xc000] = 0x00
mon.do_fill('c000 aa')
self.assertEqual(0xAA, mon._mpu.memory[0xc000])
out = stdout.getvalue()
self.assertTrue(out.startswith('Wrote +1 bytes from $c000 to $c000'))
def test_do_fill_will_fill_an_address_range_with_a_single_byte(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._mpu.memory[0xc000] = 0x00
mon._mpu.memory[0xc001] = 0x00
mon._mpu.memory[0xc002] = 0x00
mon.do_fill('c000:c001 aa')
self.assertEqual(0xAA, mon._mpu.memory[0xc000])
self.assertEqual(0xAA, mon._mpu.memory[0xc001])
self.assertEqual(0x00, mon._mpu.memory[0xc002])
out = stdout.getvalue()
self.assertTrue(out.startswith('Wrote +2 bytes from $c000 to $c001'))
def test_do_fill_will_fill_an_address_range_with_byte_sequence(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._mpu.memory[0xc000] = 0x00
mon._mpu.memory[0xc001] = 0x00
mon._mpu.memory[0xc002] = 0x00
mon._mpu.memory[0xc003] = 0x00
mon.do_fill('c000:c003 aa bb')
self.assertEqual(0xAA, mon._mpu.memory[0xc000])
self.assertEqual(0xBB, mon._mpu.memory[0xc001])
self.assertEqual(0xAA, mon._mpu.memory[0xc002])
self.assertEqual(0xBB, mon._mpu.memory[0xc003])
out = stdout.getvalue()
self.assertTrue(out.startswith('Wrote +4 bytes from $c000 to $c003'))
def test_do_fill_bad_label_in_address_shows_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_fill('nonexistent 0')
out = stdout.getvalue()
self.assertTrue(out.startswith("Label not found: nonexistent"))
def test_do_fill_bad_label_in_value_shows_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_fill('0 nonexistent')
out = stdout.getvalue()
self.assertTrue(out.startswith("Label not found: nonexistent"))
def test_do_fill_bad_start_address(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_fill('10000 00')
out = stdout.getvalue()
self.assertTrue(out.startswith("Overflow: $10000"))
def test_do_fill_bad_end_address(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_fill('ffff:10000 00 00')
out = stdout.getvalue()
self.assertTrue(out.startswith("Overflow: $10000"))
def test_do_fill_bad_data(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_fill('0 100')
out = stdout.getvalue()
self.assertTrue(out.startswith("Overflow: $100"))
# goto
def test_shortcut_for_goto(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('g')
out = stdout.getvalue()
self.assertTrue(out.startswith('goto'))
def test_goto_without_args_shows_command_help(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.onecmd('goto')
out = stdout.getvalue()
self.assertTrue("goto <address>" in out)
def test_goto_with_breakpoints_stops_execution_at_breakpoint(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._breakpoints = [ 0x03 ]
mon._mpu.memory = [ 0xEA, 0xEA, 0xEA, 0xEA ]
mon.do_goto('0')
out = stdout.getvalue()
self.assertTrue(out.startswith("Breakpoint 0 reached"))
self.assertEqual(0x03, mon._mpu.pc)
def test_goto_with_breakpoints_stops_execution_at_brk(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._breakpoints = [ 0x02 ]
mon._mpu.memory = [ 0xEA, 0xEA, 0x00, 0xEA ]
mon.do_goto('0')
self.assertEqual(0x02, mon._mpu.pc)
def test_goto_without_breakpoints_stops_execution_at_brk(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._breakpoints = []
mon._mpu.memory = [ 0xEA, 0xEA, 0x00, 0xEA ]
mon.do_goto('0')
self.assertEqual(0x02, mon._mpu.pc)
# help
def test_help_without_args_shows_documented_commands(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.onecmd('help')
out = stdout.getvalue()
self.assertTrue("Documented commands" in out)
stdout.truncate(0)
mon.onecmd('h')
out = stdout.getvalue()
self.assertTrue("Documented commands" in out)
stdout.truncate(0)
mon.onecmd('?')
out = stdout.getvalue()
self.assertTrue("Documented commands" in out)
def test_help_with_args_shows_command_help(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.onecmd('help assemble')
out = stdout.getvalue()
self.assertTrue("assemble <address>" in out)
stdout.truncate(0)
mon.onecmd('h a')
out = stdout.getvalue()
self.assertTrue("assemble <address>" in out)
def test_help_with_invalid_args_shows_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.onecmd('help foo')
out = stdout.getvalue()
self.assertTrue(out.startswith("*** No help on foo"))
def test_shortcut_for_show_breakpoints(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('shb')
out = stdout.getvalue()
self.assertTrue(out.startswith('show_breakpoints'))
def test_show_breakpoints_shows_breakpoints(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._breakpoints = [0xffd2]
mon._address_parser.labels = {'chrout': 0xffd2}
mon.do_show_breakpoints('')
out = stdout.getvalue()
self.assertTrue(out.startswith("Breakpoint 0: $FFD2 chrout"))
def test_show_breakpoints_ignores_deleted_breakpoints(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._breakpoints = [None, 0xffd2]
mon.do_show_breakpoints('')
out = stdout.getvalue()
self.assertTrue(out.startswith("Breakpoint 1: $FFD2"))
# load
def test_shortcut_for_load(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('l')
out = stdout.getvalue()
self.assertTrue(out.startswith('load'))
def test_load_with_less_than_two_args_syntax_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_load('')
out = stdout.getvalue()
self.assertTrue(out.startswith('Syntax error'))
def test_load_with_more_than_two_args_syntax_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_load('one two three')
out = stdout.getvalue()
self.assertTrue(out.startswith('Syntax error'))
def test_load(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
filename = tempfile.mktemp()
try:
f = open(filename, 'wb')
f.write(b'\xaa\xbb\xcc')
f.close()
mon.do_load("'%s' a600" % filename)
self.assertEqual('Wrote +3 bytes from $a600 to $a602\n',
stdout.getvalue())
self.assertEqual([0xAA, 0xBB, 0xCC],
mon._mpu.memory[0xA600:0xA603])
finally:
os.unlink(filename)
def test_help_load(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_load()
out = stdout.getvalue()
self.assertTrue(out.startswith('load'))
# mem
def test_shortcut_for_mem(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('m')
out = stdout.getvalue()
self.assertTrue(out.startswith('mem <address_range>'))
def test_do_mem_shows_help_when_given_no_args(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_mem('')
out = stdout.getvalue()
self.assertTrue(out.startswith('mem <address_range>'))
def test_do_mem_shows_help_when_given_extra_args(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_mem('c000 c001')
out = stdout.getvalue()
self.assertTrue(out.startswith('mem <address_range>'))
def test_do_mem_shows_memory_for_a_single_address(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._mpu.memory[0xC000] = 0xAA
mon.do_mem('c000')
out = stdout.getvalue()
self.assertEqual('c000: aa\n', out)
def test_do_mem_shows_memory_for_an_address_range(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._mpu.memory[0xC000] = 0xAA
mon._mpu.memory[0xC001] = 0xBB
mon._mpu.memory[0xC002] = 0xCC
mon.do_mem('c000:c002')
out = stdout.getvalue()
self.assertEqual('c000: aa bb cc\n', out)
def test_do_mem_wraps_at_terminal_width(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._width = 14
mon.do_mem('c000:c003')
out = stdout.getvalue()
self.assertEqual('c000: 00 00\n'
'c002: 00 00\n', out)
# mpu
def test_mpu_with_no_args_prints_current_lists_available_mpus(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_mpu('')
lines = stdout.getvalue().splitlines()
self.assertEqual(2, len(lines))
self.assertTrue(lines[0].startswith('Current MPU is '))
self.assertTrue(lines[1].startswith('Available MPUs:'))
def test_mpu_with_bad_arg_gives_error_lists_available_mpus(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_mpu('z80')
lines = stdout.getvalue().splitlines()
self.assertEqual(2, len(lines))
self.assertEqual('Unknown MPU: z80', lines[0])
self.assertTrue(lines[1].startswith('Available MPUs:'))
def test_mpu_selects_6502(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_mpu('6502')
lines = stdout.getvalue().splitlines()
self.assertEqual(1, len(lines))
self.assertEqual('Reset with new MPU 6502', lines[0])
self.assertEqual('6502', mon._mpu.name)
def test_mpu_selects_65C02(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_mpu('65C02')
lines = stdout.getvalue().splitlines()
self.assertEqual(1, len(lines))
self.assertEqual('Reset with new MPU 65C02', lines[0])
self.assertEqual('65C02', mon._mpu.name)
def test_mpu_select_is_not_case_sensitive(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_mpu('65c02')
self.assertEqual('65C02', mon._mpu.name)
def test_help_mpu(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_mpu()
lines = stdout.getvalue().splitlines()
self.assertEqual("mpu\t\tPrint available microprocessors.",
lines[0])
self.assertEqual("mpu <type>\tSelect a new microprocessor.",
lines[1])
# quit
def test_shortcuts_for_quit(self):
for shortcut in ["exit", "x", "q", "EOF"]:
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help(shortcut)
out = stdout.getvalue()
self.assertTrue(out.startswith('To quit'))
def test_do_quit(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
exitnow = mon.do_quit('')
self.assertEqual(True, exitnow)
def test_help_quit(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_quit()
out = stdout.getvalue()
self.assertTrue(out.startswith("To quit,"))
# pwd
def test_pwd_shows_os_getcwd(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_pwd()
out = stdout.getvalue()
self.assertEqual("%s\n" % os.getcwd(), out)
def test_help_pwd(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_pwd()
out = stdout.getvalue()
self.assertTrue(out.startswith("Show the current working"))
# radix
def test_shortcut_for_radix(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('rad')
out = stdout.getvalue()
self.assertTrue(out.startswith('radix'))
def test_help_radix(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_radix()
out = stdout.getvalue()
self.assertTrue(out.startswith("radix [H|D|O|B]"))
def test_radix_no_arg_displays_radix(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_radix('')
out = stdout.getvalue()
self.assertTrue(out.startswith("Default radix is Hexadecimal"))
def test_radix_invalid_radix_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_radix('f')
out = stdout.getvalue()
self.assertTrue(out.startswith("Illegal radix: f"))
def test_radix_sets_binary(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_radix('b')
out = stdout.getvalue()
self.assertTrue(out.startswith("Default radix is Binary"))
def test_radix_sets_decimal(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_radix('d')
out = stdout.getvalue()
self.assertTrue(out.startswith("Default radix is Decimal"))
def test_radix_sets_hexadecimal(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_radix('h')
out = stdout.getvalue()
self.assertTrue(out.startswith("Default radix is Hexadecimal"))
def test_radix_sets_octal(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_radix('o')
out = stdout.getvalue()
self.assertTrue(out.startswith("Default radix is Octal"))
# registers
def test_shortcut_for_registers(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('r')
out = stdout.getvalue()
self.assertTrue(out.startswith('registers'))
def test_registers_display_returns_to_prompt(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_registers('')
out = stdout.getvalue()
self.assertEqual('', out)
def test_registers_syntax_error_bad_format(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_registers('x')
out = stdout.getvalue()
self.assertEqual("Syntax error: x\n", out)
def test_registers_label_error_bad_value(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_registers('x=pony')
out = stdout.getvalue()
self.assertEqual("Label not found: pony\n", out)
def test_registers_invalid_register_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_registers('z=3')
out = stdout.getvalue()
self.assertEqual("Invalid register: z\n", out)
def test_registers_updates_single_register(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_registers('x=42')
out = stdout.getvalue()
self.assertEqual("", out)
self.assertEqual(0x42, mon._mpu.x)
def test_registers_updates_all_registers(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_registers('a=42,x=43,y=44,p=45, sp=46, pc=4600')
out = stdout.getvalue()
self.assertEqual("", out)
self.assertEqual(0x42, mon._mpu.a)
self.assertEqual(0x43, mon._mpu.x)
self.assertEqual(0x44, mon._mpu.y)
self.assertEqual(0x45, mon._mpu.p)
self.assertEqual(0x46, mon._mpu.sp)
self.assertEqual(0x4600, mon._mpu.pc)
def test_registers_pc_overflow(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_registers('pc=10000')
out = stdout.getvalue()
expected = "Overflow: '10000' too wide for register 'pc'"
self.assertTrue(out.startswith(expected))
def test_registers_a_overflow(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_registers('a=100')
out = stdout.getvalue()
expected = "Overflow: '100' too wide for register 'a'"
self.assertTrue(out.startswith(expected))
def test_help_registers(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_registers()
out = stdout.getvalue()
self.assertTrue(out.startswith("registers[<name>"))
# return
def test_shortcut_for_return(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('ret')
out = stdout.getvalue()
self.assertTrue(out.startswith('return'))
# reset
def test_do_reset(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
old_mpu = mon._mpu
old_name = mon._mpu.name
mon.do_reset('')
self.assertNotEqual(old_mpu, mon._mpu)
self.assertEqual(old_name, mon._mpu.name)
def test_help_reset(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_reset()
out = stdout.getvalue()
self.assertTrue(out.startswith("reset\t"))
# save
def test_shortcut_for_save(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('s')
out = stdout.getvalue()
self.assertTrue(out.startswith('save'))
def test_save_with_less_than_three_args_syntax_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_save('filename start')
out = stdout.getvalue()
self.assertTrue(out.startswith('Syntax error'))
def test_save(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._mpu.memory[0:3] = [0xAA, 0xBB, 0xCC]
filename = tempfile.mktemp()
try:
mon.do_save("'%s' 0 2" % filename)
self.assertEqual('Saved +3 bytes to %s\n' % filename,
stdout.getvalue())
f = open(filename, 'rb')
contents = f.read()
f.close()
self.assertEqual(b'\xaa\xbb\xcc', contents)
finally:
os.unlink(filename)
def test_help_save(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_save()
out = stdout.getvalue()
self.assertTrue(out.startswith('save'))
# step
def test_shortcut_for_step(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('z')
out = stdout.getvalue()
self.assertTrue(out.startswith('step'))
# tilde
def test_tilde_shortcut_with_space(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.onecmd('~ $10')
out = stdout.getvalue()
expected = "+16\n$10\n0020\n00010000\n"
self.assertTrue(out.startswith(expected))
def test_tilde_shortcut_without_space_for_vice_compatibility(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.onecmd('~$10')
out = stdout.getvalue()
expected = "+16\n$10\n0020\n00010000\n"
self.assertTrue(out.startswith(expected))
def test_do_tilde(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_tilde('$10')
out = stdout.getvalue()
expected = "+16\n$10\n0020\n00010000\n"
self.assertTrue(out.startswith(expected))
def test_do_tilde_with_no_arg_shows_help(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_tilde('')
out = stdout.getvalue()
expected = "~ <number>"
self.assertTrue(out.startswith(expected))
def test_do_tilde_with_bad_label_shows_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_tilde('bad_label')
out = stdout.getvalue()
expected = "Bad label: bad_label"
self.assertTrue(out.startswith(expected))
def test_do_tilde_with_overflow_shows_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_tilde('$FFFFFFFFFFFF')
out = stdout.getvalue()
expected = "Overflow error: $FFFFFFFFFFFF"
self.assertTrue(out.startswith(expected))
def test_help_tilde(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_tilde()
out = stdout.getvalue()
expected = "~ <number>"
self.assertTrue(out.startswith(expected))
# show_labels
def test_shortcut_for_show_labels(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_help('shl')
out = stdout.getvalue()
self.assertTrue(out.startswith('show_labels'))
def test_show_labels_displays_labels(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._address_parser.labels = {'chrin': 0xffc4, 'chrout': 0xffd2}
mon.do_show_labels('')
out = stdout.getvalue()
self.assertEqual("ffc4: chrin\nffd2: chrout\n", out)
def test_help_show_labels(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon._address_parser.labels = {'chrin': 0xffc4, 'chrout': 0xffd2}
mon.do_show_labels('')
out = stdout.getvalue()
self.assertEqual("ffc4: chrin\nffd2: chrout\n", out)
# version
def test_do_version(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_version('')
out = stdout.getvalue()
self.assertTrue(out.startswith("\nPy65"))
def test_help_version(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_version()
out = stdout.getvalue()
self.assertTrue(out.startswith("version\t"))
# width
def test_do_width_with_no_args_shows_current_width(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_width('')
out = stdout.getvalue()
self.assertEqual("Terminal width is 78\n", out)
def test_do_width_with_arg_changes_width(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_width('38')
out = stdout.getvalue()
self.assertEqual("Terminal width is 38\n", out)
def test_do_width_with_less_than_min_shows_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_width('3')
out = stdout.getvalue()
expected = "Minimum terminal width is 10\nTerminal width is 78\n"
self.assertEqual(expected, out)
def test_do_width_with_bad_arg_shows_error(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.do_width('bad')
out = stdout.getvalue()
expected = "Illegal width: bad\nTerminal width is 78\n"
self.assertEqual(expected, out)
def test_help_width(self):
stdout = StringIO()
mon = Monitor(stdout=stdout)
mon.help_width()
out = stdout.getvalue()
self.assertTrue(out.startswith("width <columns>"))
def test_external_memory(self):
stdout = StringIO()
memory = bytearray(65536)
memory[10] = 0xff
mon = Monitor(memory=memory, stdout=stdout, putc_addr=None, getc_addr=None)
self.assertEqual(0xff, memory[10], "memory must remain untouched")
mon.do_mem('0008:000c')
mon.do_fill('0000:0020 ab')
self.assertEqual(0xab, memory[10], "memory must have been modified")
out = stdout.getvalue()
self.assertTrue(out.startswith('0008: 00 00 ff 00 00'), "monitor must see pre-initialized memory")
# command line options
def test_argv_mpu(self):
argv = ['py65mon', '--mpu', '65c02']
stdout = StringIO()
mon = Monitor(argv=argv, stdout=stdout)
self.assertEqual('65C02', mon._mpu.name)
def test_argv_mpu_invalid(self):
argv = ['py65mon', '--mpu', 'bad']
stdout = StringIO()
try:
Monitor(argv=argv, stdout=stdout)
except SystemExit as exc:
self.assertEqual(1, exc.code)
self.assertTrue("Fatal: no such MPU." in stdout.getvalue())
def test_argv_goto(self):
argv = ['py65mon', '--goto', 'c000']
stdout = StringIO()
memory = bytearray(0x10000)
memory[0xc000] = 0xea # c000 nop
memory[0xc001] = 0xea # c001 nop
memory[0xc002] = 0x00 # c002 brk
mon = Monitor(argv=argv, stdout=stdout, memory=memory)
self.assertEqual(0xc002, mon._mpu.pc)
def test_argv_load(self):
try:
with tempfile.NamedTemporaryFile('wb+', delete=False) as f:
data = bytearray([0xab, 0xcd])
f.write(data)
argv = ['py65mon', '--load', f.name]
stdout = StringIO()
mon = Monitor(argv=argv, stdout=stdout)
self.assertEqual(list(data), mon._mpu.memory[:len(data)])
finally:
os.unlink(f.name)
def test_argv_rom(self):
try:
with tempfile.NamedTemporaryFile('wb+', delete=False) as f:
rom = bytearray(4096)
rom[0] = 0xea # f000 nop
rom[1] = 0xea # f001 nop
rom[2] = 0x00 # f002 brk
rom[-2] = 0xf000 & 0xff # fffc reset vector low
rom[-3] = 0xf000 >> 8 # fffd reset vector high
f.write(rom)
argv = ['py65mon', '--rom', f.name]
stdout = StringIO()
mon = Monitor(argv=argv, stdout=stdout)
self.assertEqual(list(rom), mon._mpu.memory[-len(rom):])
self.assertEqual(0xf002, mon._mpu.pc)
finally:
os.unlink(f.name)
def test_argv_input(self):
argv = ['py65mon', '--input', 'abcd']
stdout = StringIO()
mon = Monitor(argv=argv, stdout=stdout)
read_subscribers = mon._mpu.memory._read_subscribers
self.assertEqual(1, len(read_subscribers))
self.assertTrue('getc' in repr(read_subscribers[0xabcd]))
def test_argv_output(self):
argv = ['py65mon', '--output', 'dcba']
stdout = StringIO()
mon = Monitor(argv=argv, stdout=stdout)
write_subscribers = mon._mpu.memory._write_subscribers
self.assertEqual(1, len(write_subscribers))
self.assertTrue('putc' in repr(write_subscribers[0xdcba]))
def test_argv_combination_rom_mpu(self):
try:
with tempfile.NamedTemporaryFile('wb+', delete=False) as f:
rom = bytearray(4096)
rom[0] = 0xea # f000 nop
rom[1] = 0xea # f001 nop
rom[2] = 0x00 # f002 brk
rom[-2] = 0xf000 & 0xff # fffc reset vector low
rom[-3] = 0xf000 >> 8 # fffd reset vector high
f.write(rom)
argv = ['py65mon', '--rom', f.name, '--mpu', '65c02',]
stdout = StringIO()
mon = Monitor(argv=argv, stdout=stdout)
self.assertEqual('65C02', mon._mpu.name)
self.assertEqual(list(rom), mon._mpu.memory[-len(rom):])
self.assertEqual(0xf002, mon._mpu.pc)
finally:
os.unlink(f.name)
def test_suite():
return unittest.findTestCases(sys.modules[__name__])
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')