add parallel execution of test suites

This commit is contained in:
Matthieu Jolimaitre 2024-11-07 10:56:29 +01:00
parent da55d88ddf
commit 0a1fca28b0
2 changed files with 41 additions and 13 deletions

View file

@ -6,6 +6,7 @@ from traceback import print_exception
from .utils import CaptureOutput from .utils import CaptureOutput
from .colors import red, green, bold, yellow from .colors import red, green, bold, yellow
from .strategies import Sequential, Parallel
@dataclass @dataclass
@ -132,30 +133,32 @@ class Suite:
return procedure return procedure
return decorate return decorate
def run(self, filters: list[str] = []): def run(self, filters: list[str] = [], parallel = False):
""" """
Runs the test suite. Runs the test suite.
Optionnally filter to run by their name : Only keep tests which names contains all filters. Optionnally filter to run by their name : Only keep tests which names contains all filters.
""" """
if self.name is not None:
print(" ", green("Suite"), bold(self.name))
to_run = [*self.filter_tests(filters)] to_run = [*self.filter_tests(filters)]
strategy = Parallel() if parallel else Sequential()
if self.name is not None: print(" ", green("Suite"), bold(self.name))
print(yellow('Running'), bold(str(len(to_run))), yellow('/'), bold(str(len(self.tests))), yellow('tests')) print(yellow('Running'), bold(str(len(to_run))), yellow('/'), bold(str(len(self.tests))), yellow('tests'))
print() print()
failures = list[TestFailure]() failures = [fail for fail in strategy.run_all(to_run, Suite.run_one) if fail is not None]
for test in to_run: print()
print("", yellow('Failed'), bold(str(len(failures))), yellow('/'), bold(str(len(to_run))), yellow('tests'))
for (index, failure) in enumerate(failures): failure.print(index)
return failures
@staticmethod
def run_one(test: Test):
failure = test.run() failure = test.run()
if failure is None: if failure is None:
print(f" {green('Ok')} {bold(test.name)}") print(f" {green('Ok')} {bold(test.name)}")
else: else:
print(f" {red('Err')} {bold(test.name)}") print(f" {red('Err')} {bold(test.name)}")
failures.append(failure) return failure
print()
print("", yellow('Failed'), bold(str(len(failures))), yellow('/'), bold(str(len(to_run))), yellow('tests'))
for (index, failure) in enumerate(failures):
failure.print(index)
return failures
def filter_tests(self, filters: list[str]): def filter_tests(self, filters: list[str]):
for test in self.tests: for test in self.tests:

25
src/okipy/strategies.py Normal file
View file

@ -0,0 +1,25 @@
from dataclasses import dataclass
from typing import Callable, TypeVar
from multiprocessing import Pool
T = TypeVar("T")
O = TypeVar("O")
class RunStrategy:
"""
Some strategy to run an operation on a collection of items and return a collection of results from those operations.
"""
def run_all(self, items: list[T], oper: Callable[[T], O]) -> list[O]:
raise NotImplementedError("Abstract method must be overwriten")
@dataclass
class Parallel(RunStrategy):
procs: None | int = None
def run_all(self, items: list[T], oper: Callable[[T], O]) -> list[O]:
return Pool(self.procs).map(oper, items)
class Sequential(RunStrategy):
def run_all(self, items: list[T], oper: Callable[[T], O]) -> list[O]:
return [oper(item) for item in items]