| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- # SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
- # SPDX-License-Identifier: Apache-2.0
- import datetime
- import logging
- import os
- import shutil
- import subprocess
- import typing
- from pathlib import Path
- from tempfile import mkdtemp
- import pytest
- from _pytest.fixtures import FixtureRequest
- from test_build_system_helpers import EXT_IDF_PATH, EnvDict, IdfPyFunc, get_idf_build_env, run_idf_py
- # Pytest hook used to check if the test has passed or failed, from a fixture.
- # Based on https://docs.pytest.org/en/latest/example/simple.html#making-test-result-information-available-in-fixtures
- @pytest.hookimpl(tryfirst=True, hookwrapper=True)
- def pytest_runtest_makereport(item: typing.Any, call: typing.Any) -> typing.Generator[None, pytest.TestReport, None]: # pylint: disable=unused-argument
- outcome = yield # Execute all other hooks to obtain the report object
- report = outcome.get_result()
- if report.when == 'call' and report.passed:
- # set an attribute which can be checked using 'should_clean_test_dir' function below
- setattr(item, 'passed', True)
- def should_clean_test_dir(request: FixtureRequest) -> bool:
- # Only remove the test directory if the test has passed
- return getattr(request.node, 'passed', False)
- def pytest_addoption(parser: pytest.Parser) -> None:
- parser.addoption(
- '--work-dir', action='store', default=None,
- help='Directory for temporary files. If not specified, an OS-specific '
- 'temporary directory will be used.'
- )
- @pytest.fixture(name='session_work_dir', scope='session', autouse=True)
- def fixture_session_work_dir(request: FixtureRequest) -> typing.Generator[Path, None, None]:
- work_dir = request.config.getoption('--work-dir')
- if work_dir:
- work_dir = os.path.join(work_dir, datetime.datetime.utcnow().strftime('%Y-%m-%d_%H-%M-%S'))
- logging.debug(f'using work directory: {work_dir}')
- os.makedirs(work_dir, exist_ok=True)
- clean_dir = None
- else:
- work_dir = mkdtemp()
- logging.debug(f'created temporary work directory: {work_dir}')
- clean_dir = work_dir
- # resolve allows to use relative paths with --work-dir option
- yield Path(work_dir).resolve()
- if clean_dir:
- logging.debug(f'cleaning up {clean_dir}')
- shutil.rmtree(clean_dir, ignore_errors=True)
- @pytest.fixture
- def test_app_copy(session_work_dir: Path, request: FixtureRequest) -> typing.Generator[Path, None, None]:
- # by default, use hello_world app and copy it to a temporary directory with
- # the name resembling that of the test
- copy_from = 'tools/test_build_system/build_test_app'
- copy_to = request.node.name + '_app'
- # allow overriding source and destination via pytest.mark.test_app_copy()
- mark = request.node.get_closest_marker('test_app_copy')
- if mark:
- copy_from = mark.args[0]
- if len(mark.args) > 1:
- copy_to = mark.args[1]
- path_from = Path(os.environ['IDF_PATH']) / copy_from
- path_to = session_work_dir / copy_to
- # if the new directory inside the original directory,
- # make sure not to go into recursion.
- ignore = shutil.ignore_patterns(
- path_to.name,
- # also ignore files which may be present in the work directory
- 'build', 'sdkconfig')
- logging.debug(f'copying {path_from} to {path_to}')
- shutil.copytree(path_from, path_to, ignore=ignore, symlinks=True)
- old_cwd = Path.cwd()
- os.chdir(path_to)
- yield Path(path_to)
- os.chdir(old_cwd)
- if should_clean_test_dir(request):
- logging.debug('cleaning up work directory after a successful test: {}'.format(path_to))
- shutil.rmtree(path_to, ignore_errors=True)
- @pytest.fixture
- def test_git_template_app(session_work_dir: Path, request: FixtureRequest) -> typing.Generator[Path, None, None]:
- copy_to = request.node.name + '_app'
- path_to = session_work_dir / copy_to
- logging.debug(f'clonning git-teplate app to {path_to}')
- path_to.mkdir()
- # No need to clone full repository, just single master branch
- subprocess.run(['git', 'clone', '--single-branch', '-b', 'master', '--depth', '1', 'https://github.com/espressif/esp-idf-template.git', '.'],
- cwd=path_to, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- old_cwd = Path.cwd()
- os.chdir(path_to)
- yield Path(path_to)
- os.chdir(old_cwd)
- if should_clean_test_dir(request):
- logging.debug('cleaning up work directory after a successful test: {}'.format(path_to))
- shutil.rmtree(path_to, ignore_errors=True)
- @pytest.fixture
- def idf_copy(session_work_dir: Path, request: FixtureRequest) -> typing.Generator[Path, None, None]:
- copy_to = request.node.name + '_idf'
- # allow overriding the destination via pytest.mark.idf_copy()
- mark = request.node.get_closest_marker('idf_copy')
- if mark:
- copy_to = mark.args[0]
- path_from = EXT_IDF_PATH
- path_to = session_work_dir / copy_to
- # if the new directory inside the original directory,
- # make sure not to go into recursion.
- ignore = shutil.ignore_patterns(
- path_to.name,
- # also ignore the build directories which may be quite large
- '**/build')
- logging.debug(f'copying {path_from} to {path_to}')
- shutil.copytree(path_from, path_to, ignore=ignore, symlinks=True)
- orig_idf_path = os.environ['IDF_PATH']
- os.environ['IDF_PATH'] = str(path_to)
- yield Path(path_to)
- os.environ['IDF_PATH'] = orig_idf_path
- if should_clean_test_dir(request):
- logging.debug('cleaning up work directory after a successful test: {}'.format(path_to))
- shutil.rmtree(path_to, ignore_errors=True)
- @pytest.fixture(name='default_idf_env')
- def fixture_default_idf_env() -> EnvDict:
- return get_idf_build_env(os.environ['IDF_PATH']) # type: ignore
- @pytest.fixture
- def idf_py(default_idf_env: EnvDict) -> IdfPyFunc:
- def result(*args: str, check: bool = True, input_str: typing.Optional[str] = None) -> subprocess.CompletedProcess:
- return run_idf_py(*args, env=default_idf_env, workdir=os.getcwd(), check=check, input_str=input_str) # type: ignore
- return result
|