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
2016-02-10 12:04:47 +07:00

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()