mirror of
https://github.com/rzzzwilson/pymlac.git
synced 2025-06-10 09:32:41 +00:00
Implemented 'expected fail' in tests
This commit is contained in:
@@ -21,16 +21,16 @@ define tests to be performed. For example:
|
||||
law 1
|
||||
hlt
|
||||
end
|
||||
;! 0100 004001
|
||||
;! 0101 000000
|
||||
;|0100 004001
|
||||
;|0101 000000
|
||||
|
||||
The special test comments have **';!'** in column 1. Normal comments may also
|
||||
The special test comments have **';|** in column 1. Normal comments may also
|
||||
exist in the file. Here the test comments are:
|
||||
|
||||
::
|
||||
|
||||
;! 0100 004001
|
||||
;! 0101 000000
|
||||
;|0100 004001
|
||||
;|0101 000000
|
||||
|
||||
which tells **test_harness** to check that the PTP file generated by the
|
||||
assembler does load *004001* at address *0100*, etc.
|
||||
@@ -38,6 +38,37 @@ assembler does load *004001* at address *0100*, etc.
|
||||
Test comments may appear anywhere in a test file, and are executed in the order
|
||||
they appear in the file.
|
||||
|
||||
Failing tests
|
||||
-------------
|
||||
|
||||
Sometime we might write a test that we expect to fail. A normal test that
|
||||
succeeds produces no error output, but a failing test does. These *expected*
|
||||
failures make it harder for the user to check that all is well.
|
||||
|
||||
The **unittest** module allows failures that are expected to be considered as a
|
||||
test pass. We want to do the same with **test_harness**.
|
||||
|
||||
Tests that we expect to fail are written just the same as *normal* tests except
|
||||
that we start the test comment with '**;!**'.
|
||||
|
||||
For example, if we want to make sure that a **BSS** generates the right number
|
||||
of zero bytes we could do this:
|
||||
|
||||
::
|
||||
|
||||
; test operation of BSS
|
||||
org 0100
|
||||
law 1 ; 0100
|
||||
bss 3 ; 0101
|
||||
law 2 ; 0104
|
||||
end
|
||||
;|$1 04000+1
|
||||
;| 0
|
||||
;| 0
|
||||
;| 0
|
||||
;! 0 ; tests that 0104 is NOT a zero byte
|
||||
;|0104 04000+2 ; retest 0104 for "law 2"
|
||||
|
||||
test_harness Usage
|
||||
==================
|
||||
|
||||
|
||||
@@ -168,6 +168,9 @@ def get_memory(ptp):
|
||||
def test_file(filename):
|
||||
"""Test one ASM test file."""
|
||||
|
||||
# counter for number of errors
|
||||
errors = 0
|
||||
|
||||
# assemble file into known listing file and PTP file
|
||||
lst_file = '%s.lst' % TempPrefix
|
||||
ptp_file = '%s.ptp' % TempPrefix
|
||||
@@ -175,8 +178,9 @@ def test_file(filename):
|
||||
% (ptp_file, lst_file, filename))
|
||||
status = os.system(cmd)
|
||||
if status:
|
||||
errors += 1
|
||||
warn("Error assembling file '%s'" % filename)
|
||||
return
|
||||
return errors # must end testing
|
||||
os.remove(lst_file)
|
||||
|
||||
# get the test instructions from the ASM file
|
||||
@@ -185,27 +189,29 @@ def test_file(filename):
|
||||
lines = [line.rstrip() for line in lines]
|
||||
tests = []
|
||||
for (lnum, line) in enumerate(lines):
|
||||
if line.startswith(';|'):
|
||||
if line.startswith(';|') or line.startswith(';!'):
|
||||
test_pass = line.startswith(';|')
|
||||
|
||||
test = line[2:]
|
||||
test = test.replace('\t', ' ')
|
||||
if ';' in test:
|
||||
ndx = test.index(';')
|
||||
test = test[:ndx].rstrip()
|
||||
tests.append((lnum+1, test))
|
||||
tests.append((lnum+1, test_pass, test))
|
||||
|
||||
# make sure we have some tests
|
||||
if not tests:
|
||||
errors += 1
|
||||
print("No tests found in file '%s'" % filename)
|
||||
return
|
||||
return errors # must end testing
|
||||
|
||||
# get the generated code and ORG block info from the PTP file
|
||||
# memory locations are in a dictionary: {addr: contents, ...}
|
||||
(orgs, memory) = get_memory(ptp_file)
|
||||
|
||||
# interpret the test instructions and check generated code
|
||||
errors = False
|
||||
dot = None
|
||||
for (lnum, test) in tests:
|
||||
for (lnum, must_pass, test) in tests:
|
||||
# check for a label
|
||||
if test[0] == ' ':
|
||||
# no label
|
||||
@@ -221,9 +227,11 @@ def test_file(filename):
|
||||
try:
|
||||
dot = orgs[org_num-1]
|
||||
except IndexError:
|
||||
warn("File '%s' has label '%s' with bad ORG number"
|
||||
% (filename, label))
|
||||
return
|
||||
if must_pass:
|
||||
errors += 1
|
||||
warn("File '%s' has label '%s' with bad ORG number: %s"
|
||||
% (filename, label, test))
|
||||
return errors # must end testing
|
||||
address = dot
|
||||
else:
|
||||
address = oct_dec_int(label)
|
||||
@@ -233,27 +241,34 @@ def test_file(filename):
|
||||
value = eval_expr(dot, value)
|
||||
|
||||
if address is None:
|
||||
warn("File '%s' has label '%s' with bad ORG number"
|
||||
% (filename, label))
|
||||
return
|
||||
if must_pass:
|
||||
errors += 1
|
||||
warn("File '%s' has label '%s' with bad ORG number: %s"
|
||||
% (filename, label, test))
|
||||
return errors # must end testing
|
||||
|
||||
try:
|
||||
mem_value = memory[address]
|
||||
except KeyError:
|
||||
warn("%s\n"
|
||||
"%2d: %s\n"
|
||||
"Test comment has check for address %04o which isn't in block"
|
||||
% (filename, lnum, test, address))
|
||||
else:
|
||||
if mem_value != value:
|
||||
errors = True
|
||||
if must_pass:
|
||||
errors += 1
|
||||
warn("%s\n"
|
||||
"%2d: %s\n"
|
||||
"Memory at address %04o should be %06o, got %06o"
|
||||
% (filename, lnum, test, address, value, mem_value))
|
||||
"Test comment has check for address %04o which isn't in block"
|
||||
% (filename, lnum, test, address))
|
||||
else:
|
||||
if mem_value != value:
|
||||
if must_pass:
|
||||
errors += 1
|
||||
warn("%s\n"
|
||||
"%2d: %s\n"
|
||||
"Memory at address %04o should be %06o, got %06o"
|
||||
% (filename, lnum, test, address, value, mem_value))
|
||||
|
||||
dot += 1
|
||||
|
||||
return errors
|
||||
|
||||
def warn(msg):
|
||||
"""Print error message and continue."""
|
||||
|
||||
@@ -288,9 +303,16 @@ def run_tests(directory, prefix):
|
||||
error("No test files found in directory '%s'" % directory)
|
||||
|
||||
# test each found file
|
||||
errors = 0
|
||||
files.sort()
|
||||
for filename in files:
|
||||
test_file(filename)
|
||||
errors += test_file(filename)
|
||||
|
||||
if errors:
|
||||
if errors == 1:
|
||||
warn('There was %d error!' % errors)
|
||||
else:
|
||||
warn('There were %d errors!' % errors)
|
||||
|
||||
# remove temporary files
|
||||
for f in glob.glob('%s.*' % TempPrefix):
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
hlt
|
||||
end
|
||||
|
||||
;|0100 004002 ; is actually 004001
|
||||
;!0100 004002 ; is actually 004001
|
||||
;|0101 000000
|
||||
;|0102 000001 ; address outside block
|
||||
;!0102 000001 ; address outside block
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
hlt
|
||||
end
|
||||
|
||||
;|$1 004000 + 2 ; is actually 004001
|
||||
;!$1 004000 + 2 ; is actually 004001
|
||||
;| 000000
|
||||
;| 000001 ; address outside block
|
||||
;! 000001 ; address outside block
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
hlt
|
||||
end
|
||||
|
||||
;|$3 004001 ; bad ORG number
|
||||
;| 000000
|
||||
;!$3 004001 ; bad ORG number
|
||||
;|0100 004000 + 1
|
||||
;|0101 000000
|
||||
|
||||
47
pyasm/pyasm
47
pyasm/pyasm
@@ -573,7 +573,7 @@ def pass_1(lines):
|
||||
elif opcode == 'DATA':
|
||||
# a single data word
|
||||
if not addr or eval_expr(addr) is None:
|
||||
error("BSS pseudo-op has a bad value")
|
||||
error("DATA pseudo-op has a bad value")
|
||||
return False
|
||||
if label:
|
||||
define_label(label, Dot, lnum)
|
||||
@@ -598,6 +598,25 @@ def pass_1(lines):
|
||||
define_label(label, Dot, lnum)
|
||||
Dot += ascii_words
|
||||
|
||||
elif opcode == 'ASCIIZ':
|
||||
# ASCII string, pack two bytes/word, ensure zero byte fill at end
|
||||
if not addr:
|
||||
error("ASCIIZ pseudo-op must have a data field")
|
||||
if addr[0] not in "'\"":
|
||||
error("ASCIIZ pseudo-op must data field must be a delimited string")
|
||||
delim = addr[0]
|
||||
if addr[-1] != delim:
|
||||
error("ASCIIZ pseudo-op has a badly delimited delimited string")
|
||||
addr = addr[1:-1]
|
||||
|
||||
ascii_len = len(addr) + 1
|
||||
ascii_words = ascii_len / 2
|
||||
if ascii_len % 2:
|
||||
ascii_words += 1
|
||||
if label:
|
||||
define_label(label, Dot, lnum)
|
||||
Dot += ascii_words
|
||||
|
||||
elif opcode == 'INC':
|
||||
# start of short vector mode
|
||||
if not addr:
|
||||
@@ -760,6 +779,32 @@ def pass_2(lines):
|
||||
write_list(word_value, Dot, list_lnum, list_line)
|
||||
Dot += 1
|
||||
|
||||
elif opcode == 'ASCIIZ':
|
||||
# 'addr' must exist and be a quote-delimited string
|
||||
if not addr:
|
||||
error("ASCIIZ pseudo-op must have a data field")
|
||||
if addr[0] not in "'\"":
|
||||
error("ASCIIZ pseudo-op must data field must be a delimited string")
|
||||
delim = addr[0]
|
||||
if addr[-1] != delim:
|
||||
error("ASCIIZ pseudo-op has a badly delimited delimited string")
|
||||
addr = addr[1:-1]
|
||||
len_addr = len(addr)
|
||||
list_lnum = lnum
|
||||
list_line = line
|
||||
for i in range(0, len_addr-1, 2):
|
||||
word_value = (ord(addr[i]) << 8) + ord(addr[i+1])
|
||||
emit_word(word_value)
|
||||
write_list(word_value, Dot, list_lnum, list_line)
|
||||
list_lnum = ''
|
||||
list_line = ''
|
||||
Dot += 1
|
||||
if len_addr % 2:
|
||||
word_value = (ord(addr[-1]) << 8)
|
||||
emit_word(word_value)
|
||||
write_list(word_value, Dot, list_lnum, list_line)
|
||||
Dot += 1
|
||||
|
||||
elif opcode == 'INC':
|
||||
if not addr:
|
||||
error("INC pseudo-op must have a data field")
|
||||
|
||||
Reference in New Issue
Block a user