1
0
mirror of https://github.com/rzzzwilson/pymlac.git synced 2025-06-10 09:32:41 +00:00
Files
rzzzwilson.pymlac/pyasm/asm_tests/test_harness
Ross Wilson fd03f7576e Added labels
2016-02-13 12:30:42 +07:00

308 lines
8.0 KiB
Python
Executable File

#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
A test harness for code to test 'pyasm'.
Usage: test_harness [ -h ] [ -d <directory> ] [ -p <prefix> ]
Where <prefix> is the test filename prefix of files to test,
<directory> is a directory of test files.
If <directory> is specified run tests in that directory. If not specified
the test directory is assumed to be './tests'.
If <prefix> is specified, run tests on files in the test subdirectory with
a filename of <prefix>* . If <prefix> is not specified all files are tested.
"""
import sys
import os
import glob
import getopt
import tempfile
import shutil
import string
import struct
TestDirectory = './tests'
TempPrefix = '_#TEMP#_'
BlockloaderSize = 8 * 16 # 8 lines of 16 bytes
HighBitMask = 0100000
def oct_dec_int(s):
"""Convert string 's' to binary integer."""
if s[0] == '0':
result = int(s, base=8)
else:
result = int(s)
return result
def pyword(word):
"""Convert a 16bit value to a real python integer.
That is, convert 0xFFFF to -1.
"""
byte = (word >> 8) & 0xff
byte2 = word & 0xff
bstr = chr(byte) + chr(byte2)
return struct.unpack(">h", bstr)[0]
def get_byte(handle):
"""Read one byte from the input file."""
byte = handle.read(1)
val = struct.unpack("B", byte)[0]
return val
def get_word(handle):
"""Read one word from input file."""
val = (get_byte(handle) << 8) + get_byte(handle)
return val
def read_block(handle, memory):
"""Read data blocks from file, update memory dict.
Returns None if the end block was read otherwise returns a tuple
(org, length) where 'org' is the start address of the block and 'length'
is the number of words in the block.
"""
# read load address, num data words and checksum
base_address = get_word(handle)
if base_address & HighBitMask:
return None
dot = base_address
neg_size = get_word(handle)
data_size = -pyword(neg_size)
checksum = get_word(handle)
# read data words, placing into dictionary
for _ in range(data_size):
data_word = get_word(handle)
memory[dot] = data_word
dot += 1
return (base_address, data_size)
def get_memory(ptp):
"""Read a PTP file, return memory contents as a dictionary.
Do not return the blockloader contents.
We don't check the checksum.
Returns a tuple (orgs, memory) where:
'orgs' is a list of ORG block addresses like [0100, 0200]
'memory' is a dictionary of memory contents: {addr: contents}
"""
orgs = []
memory = {}
last_org = None
last_length = 0
with open(ptp, 'rb') as handle:
# skip leading zeros
while get_byte(handle) == 0:
pass
# then skip block loader
for _ in range(BlockloaderSize - 1):
get_byte(handle)
# now read blocks until finished
while True:
result = read_block(handle, memory)
if result is None:
break
(org, length) = result
# this block may continue the last
if last_org and last_org + last_length == org:
last_length += length
else:
orgs.append(org)
last_org = org
last_length = length
return (orgs, memory)
def test_file(filename):
"""Test one ASM test file."""
# assemble file into known listing file and PTP file
lst_file = '%s.lst' % TempPrefix
ptp_file = '%s.ptp' % TempPrefix
cmd = ('../pyasm -o %s -l %s %s >/dev/null 2>&1'
% (ptp_file, lst_file, filename))
status = os.system(cmd)
if status:
warn("Error assembling file '%s'" % filename)
return
os.remove(lst_file)
# get the test instructions from the ASM file
with open(filename, 'rb') as handle:
lines = handle.readlines()
lines = [line.rstrip() for line in lines]
tests = []
for (lnum, line) in enumerate(lines):
if line.startswith(';|'):
test = line[2:]
test = test.replace('\t', ' ')
if ';' in test:
ndx = test.index(';')
test = test[:ndx].rstrip()
tests.append((lnum+1, test))
# make sure we have some tests
if not tests:
print("No tests found in file '%s'" % filename)
return
# 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:
# check for a label
if test[0] == ' ':
# no label
label = None
address = dot
value = test
else:
# have label, $n or octal/decimal?
# set 'dot' either way
(label, value) = test.split()
if label[0] == '$':
org_num = int(label[1:])
try:
dot = orgs[org_num-1]
except IndexError:
warn("File '%s' has label '%s' with bad ORG number"
% (filename, label))
return
address = dot
else:
address = oct_dec_int(label)
dot = address
value = oct_dec_int(value)
if address is None:
warn("File '%s' has label '%s' with bad ORG number"
% (filename, label))
return
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
warn("%s\n"
"%2d: %s\n"
"Memory at address %04o should be %06o, got %06o"
% (filename, lnum, test, address, value, mem_value))
dot += 1
# if errors:
# print("File '%s' had errors" % filename)
def warn(msg):
"""Print error message and continue."""
print('=================================\n'
'%s\n'
'---------------------------------' % msg)
def error(msg):
"""Print error message and stop."""
warn(msg)
sys.exit(10)
def run_tests(directory, prefix):
"""Run all appropriate test cases.
directory path to directory holding test cases
prefix filename prefix of tests to run (may be None).
"""
# handle 'all' case
if prefix is None:
prefix = ''
# get list of all test cases we are going to run
files = glob.glob('%s/%s*' % (directory, prefix))
if not files:
if prefix:
error("No test files found in directory '%s' with prefix '%s'"
% (directory, prefix))
else:
error("No test files found in directory '%s'" % directory)
# test each found file
files.sort()
for filename in files:
test_file(filename)
# remove temporary files
for f in glob.glob('%s.*' % TempPrefix):
os.remove(f)
def usage(msg=None):
"""Print usage and optional error message."""
if msg is not None:
print('*'*60)
print(msg)
print('*'*60)
print(__doc__)
def main():
"""The test harness."""
# handle the options
try:
(opts, args) = getopt.gnu_getopt(sys.argv, 'd:hp:',
['directory=', 'help', 'prefix='])
except getopt.GetoptError:
usage()
sys.exit(10)
prefix = None
directory = TestDirectory
for opt, arg in opts:
if opt in ('-d', '--directory'):
directory = arg
elif opt in ('-h', '--help'):
usage()
sys.exit(0)
elif opt in ('-p', '--prefix'):
prefix = arg
run_tests(directory, prefix)
if __name__ == '__main__':
main()