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...).
This commit is contained in:
Ivan “CLOVIS” Canet 2022-03-22 14:15:23 +01:00
parent b9ecc43cf5
commit 29c63d29d6
3 changed files with 68 additions and 7 deletions

View file

@ -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:

View file

@ -1,3 +1,4 @@
pycparser
z3-solver==4.8.7.0
llvmlite==0.31.0
parameterized~=0.8.1

View file

@ -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")