From e48fc7c485d416b2566fcb10d1badf02fcd11b83 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 3 Mar 2026 16:09:06 +0100 Subject: [PATCH] test for arm thumb variants split into two tests --- tests/test_ops_armv7mthumb.py | 174 ++++++++++++++++++++++++++++++++++ tests/test_ops_armv7thumb.py | 10 +- 2 files changed, 179 insertions(+), 5 deletions(-) create mode 100644 tests/test_ops_armv7mthumb.py diff --git a/tests/test_ops_armv7mthumb.py b/tests/test_ops_armv7mthumb.py new file mode 100644 index 0000000..16058d6 --- /dev/null +++ b/tests/test_ops_armv7mthumb.py @@ -0,0 +1,174 @@ +from copapy import NumLike, iif, value +from copapy.backend import Store, compile_to_dag, add_read_value_remote +import subprocess +from copapy import _binwrite +import copapy.backend as backend +import os +import warnings +import re +import struct +import pytest +import copapy as cp + +if os.name == 'nt': + # On Windows wsl and qemu-user is required: + # sudo apt install qemu-user + qemu_command = ['wsl', 'qemu-arm'] +else: + qemu_command = ['qemu-arm'] + + +def parse_results(log_text: str) -> dict[int, bytes]: + regex = r"^READ_DATA offs=(\d*) size=(\d*) data=(.*)$" + matches = re.finditer(regex, log_text, re.MULTILINE) + var_dict: dict[int, bytes] = {} + + for match in matches: + value_str: list[str] = match.group(3).strip().split(' ') + #print('--', value_str) + value = bytes(int(v, base=16) for v in value_str) + if len(value) <= 8: + var_dict[int(match.group(1))] = value + + return var_dict + + +def run_command(command: list[str]) -> str: + result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf8', check=False) + assert result.returncode != 11, f"SIGSEGV (segmentation fault)\n -Error occurred: {result.stderr}\n -Output: {result.stdout}" + assert result.returncode == 0, f"\n -Error occurred: {result.stderr}\n -Output: {result.stdout}" + return result.stdout + + +def check_for_qemu() -> bool: + command = qemu_command + ['--version'] + try: + result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False) + except Exception: + return False + return result.returncode == 0 + + +def function1(c1: NumLike) -> list[NumLike]: + return [c1 / 4, c1 / -4, c1 // 4, c1 // -4, (c1 * -1) // 4, + c1 * 4, c1 * -4, + c1 + 4, c1 - 4, + c1 > 2, c1 > 100, c1 < 4, c1 < 100] + +def function1ex(c1: NumLike) -> list[NumLike]: + return [c1 // 4] + + +def function2(c1: NumLike) -> list[NumLike]: + return [c1 * 4.44, c1 * -4.44] + + +def function3(c1: NumLike) -> list[NumLike]: + return [c1 / 4] + + +def function4(c1: NumLike) -> list[NumLike]: + return [c1 == 9, c1 == 4, c1 != 9, c1 != 4] + + +def function5(c1: NumLike) -> list[NumLike]: + return [c1 == True, c1 == False, c1 != True, c1 != False, c1 / 2, c1 + 2] + + +def function6(c1: NumLike) -> list[NumLike]: + return [c1 == True] + + +def iiftests(c1: NumLike) -> list[NumLike]: + return [iif(c1 > 5, 8, 9), + iif(c1 < 5, 8.5, 9.5), + iif(1 > 5, 3.3, 8.8) + c1, + iif(1 < 5, c1 * 3.3, 8.8), + iif(c1 < 5, c1 * 3.3, 8.8)] + + +@pytest.mark.runner +def test_compile(): + c_i = value(9) + c_f = value(1.111) + c_b = value(True) + + ret_test = function1(c_i) + function1(c_f) + function2(c_i) + function2(c_f) + function3(c_i) + function4(c_i) + function5(c_b) + [value(9) % 2] + iiftests(c_i) + iiftests(c_f) + [cp.asin(c_i/10)] + ret_ref = function1(9) + function1(1.111) + function2(9) + function2(1.111) + function3(9) + function4(9) + function5(True) + [9 % 2] + iiftests(9) + iiftests(1.111) + [cp.asin(9/10)] + + out = [Store(r) for r in ret_test] + + sdb = backend.stencil_db_from_package('armv7mthumb') + dw, variables = compile_to_dag(out, sdb) + + #dw.write_com(_binwrite.Command.READ_DATA) + #dw.write_int(0) + #dw.write_int(28) + + du = dw.copy() + dw.write_com(_binwrite.Command.RUN_PROG) + du.write_com(_binwrite.Command.DUMP_CODE) + + for v in ret_test: + assert isinstance(v, value) + add_read_value_remote(dw, variables, v.net) + + #dw.write_com(_binwrite.Command.READ_DATA) + #dw.write_int(0) + #dw.write_int(28) + + dw.write_com(_binwrite.Command.END_COM) + du.write_com(_binwrite.Command.END_COM) + + #print('* Data to runner:') + #dw.print() + + dw.to_file('build/runner/test-armv7mthumb.copapy') + du.to_file('build/runner/test-armv7mthumb-dump.copapy') + + if not check_for_qemu(): + warnings.warn("qemu-armv7 not found, armv7 test skipped!", UserWarning) + return + if not os.path.isfile('build/runner/coparun-armv7'): + warnings.warn("armv7 runner not found, armv7 test skipped!", UserWarning) + return + + print('----- Dump code...') + command = qemu_command + ['build/runner/coparun-armv7', 'build/runner/test-armv7mthumb-dump.copapy', 'build/runner/test.copapy-armv7mthumb.bin'] + result = run_command(command) + + print('----- Run code...') + command = qemu_command + ['build/runner/coparun-armv7', 'build/runner/test-armv7mthumb.copapy'] + result = run_command(command) + + + print('* Output from runner:\n--') + print(result) + print('--') + + assert 'Return value: 1' in result + + result_data = parse_results(result) + + for test, ref in zip(ret_test, ret_ref): + assert isinstance(test, value) + address = variables[test.net][0] + data = result_data[address] + if test.dtype == 'int': + val = int.from_bytes(data, sdb.byteorder, signed=True) + elif test.dtype == 'bool': + val = bool.from_bytes(data, sdb.byteorder) + elif test.dtype == 'float': + en = {'little': '<', 'big': '>'}[sdb.byteorder] + val = struct.unpack(en + 'f', data)[0] + assert isinstance(val, float) + else: + raise Exception(f"Unknown type: {test.dtype}") + print('+', val, ref, test.dtype, f" addr={address}") + for t in (int, float, bool): + assert isinstance(val, t) == isinstance(ref, t), f"Result type does not match for {val} and {ref}" + assert val == pytest.approx(ref, 1e-5), f"Result does not match: {val} and reference: {ref}" # pyright: ignore[reportUnknownMemberType] + + +if __name__ == "__main__": + test_compile() diff --git a/tests/test_ops_armv7thumb.py b/tests/test_ops_armv7thumb.py index 983510e..997f1cd 100644 --- a/tests/test_ops_armv7thumb.py +++ b/tests/test_ops_armv7thumb.py @@ -98,7 +98,7 @@ def test_compile(): out = [Store(r) for r in ret_test] - sdb = backend.stencil_db_from_package('armv7mthumb') + sdb = backend.stencil_db_from_package('armv7thumb') dw, variables = compile_to_dag(out, sdb) #dw.write_com(_binwrite.Command.READ_DATA) @@ -129,16 +129,16 @@ def test_compile(): if not check_for_qemu(): warnings.warn("qemu-armv7 not found, armv7 test skipped!", UserWarning) return - if not os.path.isfile('build/runner/coparun-armv7thumb'): - warnings.warn("armv7thumb runner not found, armv7thumb test skipped!", UserWarning) + if not os.path.isfile('build/runner/coparun-armv7'): + warnings.warn("armv7 runner not found, armv7 test skipped!", UserWarning) return print('----- Dump code...') - command = qemu_command + ['build/runner/coparun-armv7thumb', 'build/runner/test-armv7thumb-dump.copapy', 'build/runner/test.copapy-armv7thumb.bin'] + command = qemu_command + ['build/runner/coparun-armv7', 'build/runner/test-armv7thumb-dump.copapy', 'build/runner/test.copapy-armv7thumb.bin'] result = run_command(command) print('----- Run code...') - command = qemu_command + ['build/runner/coparun-armv7thumb', 'build/runner/test-armv7thumb.copapy'] + command = qemu_command + ['build/runner/coparun-armv7', 'build/runner/test-armv7thumb.copapy'] result = run_command(command)