diff options
| author | Ivan “CLOVIS” Canet <ivan.canet@gmail.com> | 2022-03-22 14:15:23 +0100 |
|---|---|---|
| committer | Ivan “CLOVIS” Canet <ivan.canet@gmail.com> | 2022-03-24 12:53:41 +0100 |
| commit | 29c63d29d6ba5b80cd680e690aa36fb6281e0467 (patch) | |
| tree | 72449c96844302abad75a6fa227bfc166f018dd1 | |
| parent | b9ecc43cf5ae1583cb9a1e053bac5be2e6c68aa0 (diff) | |
| download | focaccia-miasm-29c63d29d6ba5b80cd680e690aa36fb6281e0467.tar.gz focaccia-miasm-29c63d29d6ba5b80cd680e690aa36fb6281e0467.zip | |
Compatibility of our tests with unittest
This commit introduces a compatibility layer to run the Miasm tests using Python's unittest. Due to unittest not knowing how to execute tests in parallel, this is much slower than the current alternative. Supporting unittest (which is a Python standard) as an addition to our own homegrown runner, even if slower, is useful for integration with other tools thanks to the shared format (eg. see full standard output logs for each test in PyCharm, generate XUnit test reports in CI...).
| -rw-r--r-- | README.md | 7 | ||||
| -rw-r--r-- | optional_requirements.txt | 1 | ||||
| -rwxr-xr-x | test/test_all.py | 67 |
3 files changed, 68 insertions, 7 deletions
diff --git a/README.md b/README.md index 07e1d8e6..50b8de59 100644 --- a/README.md +++ b/README.md @@ -583,7 +583,14 @@ Miasm comes with a set of regression tests. To run all of them: ```pycon cd miasm_directory/test + +# Run tests using our own test runner python test_all.py + +# Run tests using standard frameworks (slower, require 'parameterized') +python -m unittest test_all.py # sequential, requires 'unittest' +python -m pytest test_all.py # sequential, requires 'pytest' +python -m pytest -n auto test_all.py # parallel, requires 'pytest' and 'pytest-xdist' ``` Some options can be specified: diff --git a/optional_requirements.txt b/optional_requirements.txt index 39d92a93..e8e1782c 100644 --- a/optional_requirements.txt +++ b/optional_requirements.txt @@ -1,3 +1,4 @@ pycparser z3-solver==4.8.7.0 llvmlite==0.31.0 +parameterized~=0.8.1 diff --git a/test/test_all.py b/test/test_all.py index 1ec49324..2d078bf1 100755 --- a/test/test_all.py +++ b/test/test_all.py @@ -1,20 +1,23 @@ #! /usr/bin/env python2 from __future__ import print_function -from builtins import map -from builtins import range + import argparse -from distutils.spawn import find_executable import os import platform -import time +import subprocess +import sys import tempfile -import platform +import time +import unittest +from builtins import map +from builtins import range + +from parameterized import parameterized +from utils import cosmetics, multithread from utils.test import Test from utils.testset import TestSet -from utils import cosmetics, multithread -from multiprocessing import Queue is_win = platform.system() == "Windows" is_64bit = platform.architecture()[0] == "64bit" @@ -847,6 +850,56 @@ testset += RegressionTest(["launch.py"], base_dir="arch/mep/asm") testset += RegressionTest(["launch.py"], base_dir="arch/mep/ir") testset += RegressionTest(["launch.py"], base_dir="arch/mep/jit") + +# region Unittest compatibility + +class TestSequence(unittest.TestCase): + # Compatibility layer for Python's unittest module + # Instead of calling the '__main__' defined below, we parameterize a single test with all the tests selected in + # testset, and run them as we would have. + + tests = testset.tests + tests_without_shellcodes = (t for t in tests if "shellcode.py" not in t.command_line[0]) + + @staticmethod + def run_process(t): + """ + @type t: Test + """ + print("Base dir:", t.base_dir) + print("Command: ", t.command_line) + print("Depends: ", [t.command_line for t in t.depends]) + print("Tags: ", t.tags) + print("Products:", t.products) + executable = t.executable if t.executable else sys.executable + print("Exec: ", executable, "(explicit)" if t.executable else "(default)") + + for t in t.depends: + assert "shellcode.py" in t.command_line[0], "At the moment, only dependencies on 'shellcode.py' are handled" + + subprocess.check_call( + [executable] + t.command_line, + cwd=testset.base_dir + t.base_dir, + ) + + print("Done") + + @classmethod + def setUpClass(cls): + for t in testset.tests: + if "shellcode.py" in t.command_line[0]: + print("\n*** Shellcode generation ***") + cls.run_process(t) + + @parameterized.expand(("_".join(test.command_line), test) for test in tests_without_shellcodes) + def test(self, name, t): + print("***", name, "***") + TestSequence.run_process(t) + + +# endregion + + if __name__ == "__main__": # Argument parsing parser = argparse.ArgumentParser(description="Miasm2 testing tool") |