diff --git a/pyasm/asm_tests/README.rst b/pyasm/asm_tests/README.rst index 12928c3..c30bc43 100755 --- a/pyasm/asm_tests/README.rst +++ b/pyasm/asm_tests/README.rst @@ -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 ================== diff --git a/pyasm/asm_tests/test_harness b/pyasm/asm_tests/test_harness index fecd6cc..3c75be0 100755 --- a/pyasm/asm_tests/test_harness +++ b/pyasm/asm_tests/test_harness @@ -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): diff --git a/pyasm/asm_tests/tests/alpha b/pyasm/asm_tests/tests/alpha index 9ae1bb6..45ac7e0 100644 --- a/pyasm/asm_tests/tests/alpha +++ b/pyasm/asm_tests/tests/alpha @@ -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 diff --git a/pyasm/asm_tests/tests/alpha.NEW b/pyasm/asm_tests/tests/alpha.NEW index fe21ce6..9850254 100644 --- a/pyasm/asm_tests/tests/alpha.NEW +++ b/pyasm/asm_tests/tests/alpha.NEW @@ -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 diff --git a/pyasm/asm_tests/tests/delta.NEW b/pyasm/asm_tests/tests/delta.NEW index a9a2b78..c30402d 100644 --- a/pyasm/asm_tests/tests/delta.NEW +++ b/pyasm/asm_tests/tests/delta.NEW @@ -4,5 +4,6 @@ hlt end -;|$3 004001 ; bad ORG number -;| 000000 +;!$3 004001 ; bad ORG number +;|0100 004000 + 1 +;|0101 000000 diff --git a/pyasm/asm_tests/tests/gamma b/pyasm/asm_tests/tests/gamma deleted file mode 100644 index e69de29..0000000 diff --git a/pyasm/pyasm b/pyasm/pyasm index c30b56d..11df4c7 100755 --- a/pyasm/pyasm +++ b/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")