mirror of
https://github.com/rzzzwilson/pymlac.git
synced 2025-06-10 09:32:41 +00:00
252 lines
6.3 KiB
Python
Executable File
252 lines
6.3 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 False if the end block was read.
|
|
"""
|
|
|
|
# read load address, num data words and checksum
|
|
base_address = get_word(handle)
|
|
if base_address & HighBitMask:
|
|
return False
|
|
|
|
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[base_address] = data_word
|
|
base_address += 1
|
|
|
|
return True
|
|
|
|
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.
|
|
"""
|
|
|
|
memory = {}
|
|
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 read_block(handle, memory):
|
|
pass
|
|
|
|
return 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:
|
|
print("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:].strip()
|
|
test = test.replace('\t', ' ')
|
|
if len(test.split()) < 2:
|
|
warn("%d: %s\nbad test comment in file '%s'"
|
|
% (lnum+1, line, filename))
|
|
return # finished with this file
|
|
tests.append((lnum+1, test))
|
|
|
|
# get the generated code from the PTP file
|
|
# put memory locations into a dictionary
|
|
memory = get_memory(ptp_file)
|
|
|
|
# interpret the test instructions and check generated code
|
|
errors = False
|
|
for (lnum, test) in tests:
|
|
(address, value) = test.split()
|
|
address = oct_dec_int(address)
|
|
value = oct_dec_int(value)
|
|
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))
|
|
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()
|