summaryrefslogtreecommitdiffstatshomepage
path: root/regtests/unidasm/unidasm.py
blob: 7e9cbc58a76d16a48c1740ed1ffb5696d9809477 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#!/usr/bin/python
##
## license:BSD-3-Clause
## copyright-holders:Angelo Salese
##
import os
import sys
import logging
import subprocess
from typing import Callable

def __assert_valid_filename(filehandle: str, target_arch: str, expected_extension: str):
    """Checkout if derived file handle input is the expected one

    Args:
        filehandle (str): file handle
        target_arch (str): the expected architecture base name
        expected_extension (str): the expected extension that the file handle should have
    """

    assert filehandle.startswith(target_arch), f"{filehandle} {expected_extension} mismatch with arch {target_arch}"
    assert filehandle.endswith(expected_extension), f"{filehandle} mismatch {expected_extension}"

def run_process():
    """Process our static directory, derive and execute tests 

    For unidasm the strategy is to point at the static folder and
    derive unit tests based off what's in static directory itself.
    To compose a new test for a given arch:
    
    1. craft a new binary file and save it in static subfolder, name it <test_arch>.bin,
       where <test_arch> is the name as it is displayed by running unidasm.exe
       with no additional params. Keep it simple & short, more sofisticated programs aren't really 
       the scope of this (we're just testing if the arch disassembles here).
    2. run unidasm $(thisfolder)/static/<test_arch>.bin -arch <test_arch> > $(thisfolder)/static/<test_arch>.asm
       On windows the resulting asm file newlines must be converted to LF with this.
    3. now run make tests and check if the new test gets captured. 

    """
    unidasm_exe = os.path.join(os.getcwd(), "unidasm{0}".format(".exe" if os.name == 'nt' else ""))
    static_tests_folder = os.path.join(os.path.dirname(os.path.realpath(__file__)), "static")
    static_tests = os.listdir(static_tests_folder)
    
    test_pool = {
        asm_filename.split(".")[0]: [asm_filename, bin_filename] for asm_filename, bin_filename in zip(static_tests[0::2], static_tests[1::2])
    }

    logging.info("Detected %s tests", len(test_pool))

    for target_arch, params in test_pool.items():
        assert len(params) == 2, f"{target_arch} Unexpected input test structure {params}"
        asm_filename, bin_filename = params
        __assert_valid_filename(asm_filename, target_arch, ".asm")
        __assert_valid_filename(bin_filename, target_arch, ".bin")

        logging.debug("Test arch %s passed internal validation, launching unidasm", target_arch)

        with open(os.path.join(static_tests_folder, asm_filename), "r", encoding="utf-8", newline="\n") as txt_h:
            expected_asm = txt_h.read()

        # TODO: checkout how unidasm copes with endianness on 16/32-bit based CPUs
        launch_process = subprocess.run(
            [unidasm_exe] + [os.path.join(static_tests_folder, bin_filename), "-arch", target_arch], 
            encoding="utf-8", 
            capture_output=True,
            check=True,
            text=True,
            shell=False
        )
        assert launch_process.returncode == 0, f"{target_arch} return code == {launch_process.returncode}"
        # TODO: eventually use difflib
        assert launch_process.stdout == expected_asm, "{1} expected:{0}{2}{0}actual:{0}{3}".format(
            os.linesep,
            target_arch,
            repr(expected_asm),
            repr(launch_process.stdout)
        )
        logging.info("Target arch test %s passed", target_arch)

if __name__ == "__main__":
    logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
    try:
        run_process()
    except (subprocess.CalledProcessError, AssertionError) as ex:
        logging.error(str(ex))
        sys.exit(1)
        raise
    logging.info("unidasm test successful")
    sys.exit(0)