diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index 750f04f6f9..e64b421205 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -517,8 +517,12 @@ build_clang_test_apps_esp32c6: - ${IDF_PATH}/tools/ci/test_configure_ci_environment.sh - cd ${IDF_PATH}/tools/test_build_system - python ${IDF_PATH}/tools/ci/get_known_failure_cases_file.py - - pytest --parallel-count ${CI_NODE_TOTAL:-1} --parallel-index ${CI_NODE_INDEX:-1} - --work-dir ${CI_PROJECT_DIR}/test_build_system --junitxml=${CI_PROJECT_DIR}/XUNIT_RESULT.xml + - pytest + --cleanup-idf-copy + --parallel-count ${CI_NODE_TOTAL:-1} + --parallel-index ${CI_NODE_INDEX:-1} + --work-dir ${CI_PROJECT_DIR}/test_build_system + --junitxml ${CI_PROJECT_DIR}/XUNIT_RESULT.xml --ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME} pytest_build_system: @@ -537,6 +541,7 @@ pytest_build_system_macos: extends: - .test_build_system_template - .before_script:build:macos + - .after_script:build:macos:upload-when-fail - .rules:build:macos tags: - macos_shell @@ -549,6 +554,9 @@ pytest_build_system_macos: expire_in: 2 days reports: junit: XUNIT_RESULT.xml + variables: + PYENV_VERSION: "3.8" + CI_CCACHE_DISABLE: "1" # ccache: error: Read-only file system build_docker: extends: diff --git a/.gitlab/ci/common.yml b/.gitlab/ci/common.yml index a3d0591f48..9b45d0e05b 100644 --- a/.gitlab/ci/common.yml +++ b/.gitlab/ci/common.yml @@ -52,7 +52,6 @@ variables: IDF_PATH: "$CI_PROJECT_DIR" V: "0" CHECKOUT_REF_SCRIPT: "$CI_PROJECT_DIR/tools/ci/checkout_project_ref.py" - PYTHON_VER: 3.8.17 # Docker images ESP_ENV_IMAGE: "${CI_DOCKER_REGISTRY}/esp-env-v5.2:2" @@ -196,6 +195,10 @@ variables: .before_script:build:macos: before_script: + # macos is running shell executor, which means it would use + # the system installed /usr/local/bin/python3 by default. + # Ensure pyenv and PYENV_VERSION installed + - eval "$(pyenv init -)" - *common-before_scripts # On macOS, these tools need to be installed - export IDF_TOOLS_PATH="${HOME}/.espressif_runner_${CI_RUNNER_ID}_${CI_CONCURRENT_ID}" @@ -204,6 +207,14 @@ variables: - *setup_tools_and_idf_python_venv - fetch_submodules +.after_script:build:macos:upload-when-fail: + after_script: + # macos is running shell executor, which means it would use + # the system installed /usr/local/bin/python3 by default. + # Ensure pyenv and PYENV_VERSION installed + - eval "$(pyenv init -)" + - *upload_failed_job_log_artifacts + .before_script:build: before_script: - *common-before_scripts diff --git a/tools/test_build_system/conftest.py b/tools/test_build_system/conftest.py index 94e0348ffb..ed36dc8cc6 100644 --- a/tools/test_build_system/conftest.py +++ b/tools/test_build_system/conftest.py @@ -27,7 +27,7 @@ def pytest_runtest_makereport(item: typing.Any, call: typing.Any) -> typing.Gene def should_clean_test_dir(request: FixtureRequest) -> bool: # Only remove the test directory if the test has passed - return getattr(request.node, 'passed', False) + return getattr(request.node, 'passed', False) or request.config.getoption('cleanup_idf_copy', False) def pytest_addoption(parser: pytest.Parser) -> None: @@ -36,6 +36,10 @@ def pytest_addoption(parser: pytest.Parser) -> None: help='Directory for temporary files. If not specified, an OS-specific ' 'temporary directory will be used.' ) + parser.addoption( + '--cleanup-idf-copy', action='store_true', + help='Always clean up the IDF copy after the test. By default, the copy is cleaned up only if the test passes.' + ) @pytest.fixture(name='session_work_dir', scope='session', autouse=True) diff --git a/tools/test_build_system/test_build.py b/tools/test_build_system/test_build.py index 35d59bf2be..40c50357ec 100644 --- a/tools/test_build_system/test_build.py +++ b/tools/test_build_system/test_build.py @@ -1,17 +1,25 @@ -# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 - import logging import os +import shutil import stat import sys import textwrap from pathlib import Path -from typing import List, Union +from typing import List +from typing import Union import pytest -from test_build_system_helpers import (APP_BINS, BOOTLOADER_BINS, PARTITION_BIN, IdfPyFunc, append_to_file, - file_contains, get_idf_build_env, replace_in_file, run_cmake_and_build) +from test_build_system_helpers import APP_BINS +from test_build_system_helpers import append_to_file +from test_build_system_helpers import BOOTLOADER_BINS +from test_build_system_helpers import file_contains +from test_build_system_helpers import get_idf_build_env +from test_build_system_helpers import IdfPyFunc +from test_build_system_helpers import PARTITION_BIN +from test_build_system_helpers import replace_in_file +from test_build_system_helpers import run_cmake_and_build def assert_built(paths: Union[List[str], List[Path]]) -> None: @@ -22,11 +30,16 @@ def assert_built(paths: Union[List[str], List[Path]]) -> None: def test_build_alternative_directories(idf_py: IdfPyFunc, session_work_dir: Path, test_app_copy: Path) -> None: logging.info('Moving BUILD_DIR_BASE out of tree') alt_build_dir = session_work_dir / 'alt_build' - idf_py('-B', str(alt_build_dir), 'build') - assert os.listdir(alt_build_dir) != [], 'No files found in new build directory!' - default_build_dir = test_app_copy / 'build' - if default_build_dir.exists(): - assert os.listdir(default_build_dir) == [], f'Some files were incorrectly put into the default build directory: {default_build_dir}' + try: + idf_py('-B', str(alt_build_dir), 'build') + assert os.listdir(alt_build_dir) != [], 'No files found in new build directory!' + default_build_dir = test_app_copy / 'build' + if default_build_dir.exists(): + assert os.listdir(default_build_dir) == [], f'Some files were incorrectly put into the default build directory: {default_build_dir}' + except Exception: + raise + else: + shutil.rmtree(alt_build_dir) logging.info('BUILD_DIR_BASE inside default build directory') build_subdir_inside_build_dir = default_build_dir / 'subdirectory' @@ -120,7 +133,7 @@ def test_build_compiler_flag_in_source_file(idf_py: IdfPyFunc, test_app_copy: Pa @pytest.mark.usefixtures('test_app_copy') def test_build_compiler_flags_no_overwriting(idf_py: IdfPyFunc) -> None: logging.info('Compiler flags cannot be overwritten') - # If the compiler flags are overriden, the following build command will + # If the compiler flags are overridden, the following build command will # cause issues at link time. idf_py('build', '-DCMAKE_C_FLAGS=', '-DCMAKE_CXX_FLAGS=') diff --git a/tools/test_build_system/test_common.py b/tools/test_build_system/test_common.py index cc55c5df35..0665792ef8 100644 --- a/tools/test_build_system/test_common.py +++ b/tools/test_build_system/test_common.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 import json import logging @@ -12,8 +12,14 @@ from pathlib import Path from typing import List import pytest -from test_build_system_helpers import (EnvDict, IdfPyFunc, append_to_file, file_contains, find_python, get_snapshot, - replace_in_file, run_idf_py) +from test_build_system_helpers import append_to_file +from test_build_system_helpers import EnvDict +from test_build_system_helpers import file_contains +from test_build_system_helpers import find_python +from test_build_system_helpers import get_snapshot +from test_build_system_helpers import IdfPyFunc +from test_build_system_helpers import replace_in_file +from test_build_system_helpers import run_idf_py def get_subdirs_absolute_paths(path: Path) -> List[str]: @@ -40,18 +46,6 @@ def test_compile_commands_json_updated_by_reconfigure(idf_py: IdfPyFunc) -> None snapshot_3.assert_different(snapshot_2) -@pytest.mark.usefixtures('test_app_copy') -def test_of_test_app_copy(idf_py: IdfPyFunc) -> None: - p = Path('main/idf_component.yml') - p.write_text('syntax_error\n') - try: - with (pytest.raises(subprocess.CalledProcessError)) as exc_info: - idf_py('reconfigure') - assert 'ERROR: Unknown format of the manifest file:' in exc_info.value.stderr - finally: - p.unlink() - - @pytest.mark.usefixtures('test_app_copy') def test_hints_no_color_output_when_noninteractive(idf_py: IdfPyFunc) -> None: """Check that idf.py hints don't include color escape codes in non-interactive builds""" @@ -246,11 +240,20 @@ def test_create_project_with_idf_readonly(idf_copy: Path) -> None: for name in files: path = os.path.join(root, name) if '/bin/' in path: - continue # skip excutables + continue # skip executables os.chmod(os.path.join(root, name), 0o444) # readonly logging.info('Check that command for creating new project will success if the IDF itself is readonly.') change_to_readonly(idf_copy) - run_idf_py('create-project', '--path', str(idf_copy / 'example_proj'), 'temp_test_project') + try: + run_idf_py('create-project', '--path', str(idf_copy / 'example_proj'), 'temp_test_project') + except Exception: + raise + else: + def del_rw(function, path, excinfo): # type: ignore + os.chmod(path, stat.S_IWRITE) + os.remove(path) + + shutil.rmtree(idf_copy, onerror=del_rw) @pytest.mark.usefixtures('test_app_copy')