script.py 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. # SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
  2. # SPDX-License-Identifier: Apache-2.0
  3. import io
  4. import typing as t
  5. from contextlib import redirect_stdout
  6. from pathlib import Path
  7. import pytest
  8. from _pytest.config import ExitCode
  9. from idf_py_actions.constants import PREVIEW_TARGETS as TOOLS_PREVIEW_TARGETS
  10. from idf_py_actions.constants import SUPPORTED_TARGETS as TOOLS_SUPPORTED_TARGETS
  11. from pytest_embedded.utils import to_list
  12. from .constants import PytestCase
  13. from .plugin import IdfPytestEmbedded
  14. def get_pytest_files(paths: t.List[str]) -> t.List[str]:
  15. # this is a workaround to solve pytest collector super slow issue
  16. # benchmark with
  17. # - time pytest -m esp32 --collect-only
  18. # user=15.57s system=1.35s cpu=95% total=17.741
  19. # - time { find -name 'pytest_*.py'; } | xargs pytest -m esp32 --collect-only
  20. # user=0.11s system=0.63s cpu=36% total=2.044
  21. # user=1.76s system=0.22s cpu=43% total=4.539
  22. # use glob.glob would also save a bunch of time
  23. pytest_scripts: t.Set[str] = set()
  24. for p in paths:
  25. path = Path(p)
  26. pytest_scripts.update(str(_p) for _p in path.glob('**/pytest_*.py') if 'managed_components' not in _p.parts)
  27. return list(pytest_scripts)
  28. def get_pytest_cases(
  29. paths: t.Union[str, t.List[str]],
  30. target: str = 'all',
  31. marker_expr: t.Optional[str] = None,
  32. filter_expr: t.Optional[str] = None,
  33. ) -> t.List[PytestCase]:
  34. if target == 'all':
  35. targets = TOOLS_SUPPORTED_TARGETS + TOOLS_PREVIEW_TARGETS
  36. else:
  37. targets = [target]
  38. paths = to_list(paths)
  39. cases: t.List[PytestCase] = []
  40. pytest_scripts = get_pytest_files(paths) # type: ignore
  41. if not pytest_scripts:
  42. print(f'WARNING: no pytest scripts found for target {target} under paths {", ".join(paths)}')
  43. return cases
  44. for target in targets:
  45. collector = IdfPytestEmbedded(target)
  46. with io.StringIO() as buf:
  47. with redirect_stdout(buf):
  48. cmd = ['--collect-only', *pytest_scripts, '--target', target, '-q']
  49. if marker_expr:
  50. cmd.extend(['-m', marker_expr])
  51. if filter_expr:
  52. cmd.extend(['-k', filter_expr])
  53. res = pytest.main(cmd, plugins=[collector])
  54. if res.value != ExitCode.OK:
  55. if res.value == ExitCode.NO_TESTS_COLLECTED:
  56. print(f'WARNING: no pytest app found for target {target} under paths {", ".join(paths)}')
  57. else:
  58. print(buf.getvalue())
  59. raise RuntimeError(f'pytest collection failed at {", ".join(paths)} with command \"{" ".join(cmd)}\"')
  60. cases.extend(collector.cases)
  61. return cases