diff --git a/tools/test_build_system/test_common.py b/tools/test_build_system/test_common.py index 9da88ea787..d79ea42099 100644 --- a/tools/test_build_system/test_common.py +++ b/tools/test_build_system/test_common.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 import json import logging @@ -9,28 +9,23 @@ import subprocess import sys import textwrap from pathlib import Path -from typing import List import pytest -from test_build_system_helpers import append_to_file from test_build_system_helpers import EnvDict +from test_build_system_helpers import IdfPyFunc +from test_build_system_helpers import append_to_file 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]: +def get_subdirs_absolute_paths(path: Path) -> list[str]: """ Get a list of files with absolute path in a given `path` folder """ - return [ - '{}/{}'.format(dir_path, file_name) - for dir_path, _, file_names in os.walk(path) - for file_name in file_names - ] + return [f'{dir_path}/{file_name}' for dir_path, _, file_names in os.walk(path) for file_name in file_names] @pytest.mark.usefixtures('test_app_copy') @@ -51,10 +46,11 @@ 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""" # make the build fail in such a way that idf.py shows a hint - replace_in_file('main/build_test_app.c', '// placeholder_inside_main', - 'esp_chip_info_t chip_info; esp_chip_info(&chip_info);') + replace_in_file( + 'main/build_test_app.c', '// placeholder_inside_main', 'esp_chip_info_t chip_info; esp_chip_info(&chip_info);' + ) - with (pytest.raises(subprocess.CalledProcessError)) as exc_info: + with pytest.raises(subprocess.CalledProcessError) as exc_info: idf_py('build') # Should not actually include a color escape sequence! @@ -70,10 +66,7 @@ def test_idf_copy(idf_copy: Path, idf_py: IdfPyFunc) -> None: idf_py('build') -def test_idf_build_with_env_var_sdkconfig_defaults( - test_app_copy: Path, - default_idf_env: EnvDict -) -> None: +def test_idf_build_with_env_var_sdkconfig_defaults(test_app_copy: Path, default_idf_env: EnvDict) -> None: with open(test_app_copy / 'sdkconfig.test', 'w') as fw: fw.write('CONFIG_BT_ENABLED=y') @@ -86,9 +79,7 @@ def test_idf_build_with_env_var_sdkconfig_defaults( @pytest.mark.usefixtures('test_app_copy') @pytest.mark.test_app_copy('examples/system/efuse') -def test_efuse_summary_cmake_functions( - default_idf_env: EnvDict -) -> None: +def test_efuse_summary_cmake_functions(default_idf_env: EnvDict) -> None: default_idf_env['IDF_CI_BUILD'] = '1' output = run_idf_py('efuse-filter', env=default_idf_env) assert 'FROM_CMAKE: MAC: 00:00:00:00:00:00' in output.stdout @@ -117,16 +108,22 @@ def test_python_interpreter_unix(test_app_copy: Path) -> None: logging.info("Make sure idf.py never runs '/usr/bin/env python' or similar") env_dict = dict(**os.environ) python = find_python(env_dict['PATH']) - (test_app_copy / 'python').write_text(textwrap.dedent("""#!/bin/sh - echo "idf.py has executed '/usr/bin/env python' or similar" - exit 1 - """)) + (test_app_copy / 'python').write_text( + textwrap.dedent( + """ + #!/bin/sh + echo "idf.py has executed '/usr/bin/env python' or similar" + exit 1 + """ + ) + ) st = os.stat(test_app_copy / 'python') # equivalent to 'chmod +x ./python' os.chmod((test_app_copy / 'python'), st.st_mode | stat.S_IEXEC) env_dict['PATH'] = str(test_app_copy) + os.pathsep + env_dict['PATH'] - # python is loaded from env:$PATH, but since false interpreter is provided there, python needs to be specified as argument + # python is loaded from env:$PATH + # but since false interpreter is provided there, python needs to be specified as argument # if idf.py is reconfigured during it's execution, it would load a false interpreter run_idf_py('reconfigure', env=env_dict, python=python) @@ -140,7 +137,8 @@ def test_python_interpreter_win(test_app_copy: Path) -> None: # on windows python interpreter has compiled code '.exe' format, so this false file can be empty (test_app_copy / 'python.exe').write_text('') env_dict['PATH'] = str(test_app_copy) + os.pathsep + env_dict['PATH'] - # python is loaded from env:$PATH, but since false interpreter is provided there, python needs to be specified as argument + # python is loaded from env:$PATH + # but since false interpreter is provided there, python needs to be specified as argument # if idf.py is reconfigured during it's execution, it would load a false interpreter run_idf_py('reconfigure', env=env_dict, python=python) @@ -172,7 +170,7 @@ def test_ccache_used_to_build(test_app_copy: Path) -> None: def test_toolchain_prefix_in_description_file(idf_py: IdfPyFunc, test_app_copy: Path) -> None: logging.info('Toolchain prefix is set in project description file') idf_py('reconfigure') - data = json.load(open(test_app_copy / 'build' / 'project_description.json', 'r')) + data = json.load(open(test_app_copy / 'build' / 'project_description.json')) assert 'monitor_toolprefix' in data @@ -195,8 +193,10 @@ def test_subcommands_with_options(idf_py: IdfPyFunc, default_idf_env: EnvDict) - def test_fallback_to_build_system_target(idf_py: IdfPyFunc, test_app_copy: Path) -> None: logging.info('idf.py fallback to build system target') msg = 'Custom target is running' - append_to_file(test_app_copy / 'CMakeLists.txt', - 'add_custom_target(custom_target COMMAND ${{CMAKE_COMMAND}} -E echo "{}")'.format(msg)) + append_to_file( + test_app_copy / 'CMakeLists.txt', + f'add_custom_target(custom_target COMMAND ${{CMAKE_COMMAND}} -E echo "{msg}")', + ) ret = idf_py('custom_target') assert msg in ret.stdout, 'Custom target did not produce expected output' @@ -205,10 +205,16 @@ def test_create_component_project(idf_copy: Path) -> None: logging.info('Create project and component using idf.py and build it') run_idf_py('-C', 'projects', 'create-project', 'temp_test_project', workdir=idf_copy) run_idf_py('-C', 'components', 'create-component', 'temp_test_component', workdir=idf_copy) - replace_in_file(idf_copy / 'projects' / 'temp_test_project' / 'main' / 'temp_test_project.c', '{\n\n}', - '\n'.join(['{', '\tfunc();', '}'])) - replace_in_file(idf_copy / 'projects' / 'temp_test_project' / 'main' / 'temp_test_project.c', '#include ', - '\n'.join(['#include ', '#include "temp_test_component.h"'])) + replace_in_file( + idf_copy / 'projects' / 'temp_test_project' / 'main' / 'temp_test_project.c', + '{\n\n}', + '\n'.join(['{', '\tfunc();', '}']), + ) + replace_in_file( + idf_copy / 'projects' / 'temp_test_project' / 'main' / 'temp_test_project.c', + '#include ', + '\n'.join(['#include ', '#include "temp_test_component.h"']), + ) run_idf_py('build', workdir=(idf_copy / 'projects' / 'temp_test_project')) @@ -244,6 +250,7 @@ def test_create_project_with_idf_readonly(idf_copy: Path) -> None: if '/bin/' in path: continue # skip executables os.chmod(os.path.join(root, name), file_permission) + logging.info('Check that command for creating new project will success if the IDF itself is readonly.') change_file_permissions(idf_copy, write_permission=False) try: @@ -267,7 +274,18 @@ def test_docs_command(idf_py: IdfPyFunc) -> None: assert 'https://docs.espressif.com/projects/esp-idf/en/v4.2.1' in ret.stdout ret = idf_py('docs', '--no-browser', '--language', 'en', '--version', 'v4.2.1', '--target', 'esp32') assert 'https://docs.espressif.com/projects/esp-idf/en/v4.2.1/esp32' in ret.stdout - ret = idf_py('docs', '--no-browser', '--language', 'en', '--version', 'v4.2.1', '--target', 'esp32', '--starting-page', 'get-started') + ret = idf_py( + 'docs', + '--no-browser', + '--language', + 'en', + '--version', + 'v4.2.1', + '--target', + 'esp32', + '--starting-page', + 'get-started', + ) assert 'https://docs.espressif.com/projects/esp-idf/en/v4.2.1/esp32/get-started' in ret.stdout @@ -285,25 +303,31 @@ def test_deprecation_warning(idf_py: IdfPyFunc) -> None: def test_save_defconfig_check(idf_py: IdfPyFunc, test_app_copy: Path) -> None: logging.info('Save-defconfig checks') - (test_app_copy / 'sdkconfig').write_text('\n'.join(['CONFIG_COMPILER_OPTIMIZATION_SIZE=y', - 'CONFIG_ESPTOOLPY_FLASHFREQ_80M=y'])) + (test_app_copy / 'sdkconfig').write_text( + '\n'.join(['CONFIG_COMPILER_OPTIMIZATION_SIZE=y', 'CONFIG_ESPTOOLPY_FLASHFREQ_80M=y']) + ) idf_py('save-defconfig') - assert not file_contains(test_app_copy / 'sdkconfig.defaults', 'CONFIG_IDF_TARGET'), \ + assert not file_contains(test_app_copy / 'sdkconfig.defaults', 'CONFIG_IDF_TARGET'), ( 'CONFIG_IDF_TARGET should not be in sdkconfig.defaults' - assert file_contains(test_app_copy / 'sdkconfig.defaults', 'CONFIG_COMPILER_OPTIMIZATION_SIZE=y'), \ + ) + assert file_contains(test_app_copy / 'sdkconfig.defaults', 'CONFIG_COMPILER_OPTIMIZATION_SIZE=y'), ( 'Missing CONFIG_COMPILER_OPTIMIZATION_SIZE=y in sdkconfig.defaults' - assert file_contains(test_app_copy / 'sdkconfig.defaults', 'CONFIG_ESPTOOLPY_FLASHFREQ_80M=y'), \ + ) + assert file_contains(test_app_copy / 'sdkconfig.defaults', 'CONFIG_ESPTOOLPY_FLASHFREQ_80M=y'), ( 'Missing CONFIG_ESPTOOLPY_FLASHFREQ_80M=y in sdkconfig.defaults' + ) idf_py('fullclean') (test_app_copy / 'sdkconfig').unlink() (test_app_copy / 'sdkconfig.defaults').unlink() idf_py('set-target', 'esp32c3') (test_app_copy / 'sdkconfig').write_text('CONFIG_PARTITION_TABLE_OFFSET=0x8001') idf_py('save-defconfig') - assert file_contains(test_app_copy / 'sdkconfig.defaults', 'CONFIG_IDF_TARGET="esp32c3"'), \ + assert file_contains(test_app_copy / 'sdkconfig.defaults', 'CONFIG_IDF_TARGET="esp32c3"'), ( 'Missing CONFIG_IDF_TARGET="esp32c3" in sdkconfig.defaults' - assert file_contains(test_app_copy / 'sdkconfig.defaults', 'CONFIG_PARTITION_TABLE_OFFSET=0x8001'), \ + ) + assert file_contains(test_app_copy / 'sdkconfig.defaults', 'CONFIG_PARTITION_TABLE_OFFSET=0x8001'), ( 'Missing CONFIG_PARTITION_TABLE_OFFSET=0x8001 in sdkconfig.defaults' + ) def test_merge_bin_cmd(idf_py: IdfPyFunc, test_app_copy: Path) -> None: diff --git a/tools/test_idf_py/test_idf_py.py b/tools/test_idf_py/test_idf_py.py index b4efe0a917..fcb885b7e5 100755 --- a/tools/test_idf_py/test_idf_py.py +++ b/tools/test_idf_py/test_idf_py.py @@ -204,7 +204,8 @@ class TestDeprecations(TestWithoutExtensions): [sys.executable, idf_py_path, '-C', current_dir, 'test-2'], env=os.environ, stderr=subprocess.STDOUT ) except subprocess.CalledProcessError as e: - self.assertIn('Error: Command "test-2" is deprecated and was removed.', e.output.decode('utf-8', 'ignore')) + output = e.output.decode('utf-8', 'ignore').replace('\r\n', '\n') + self.assertIn('Error: Command "test-2" is deprecated and was removed\n', output) def test_exit_with_error_for_option(self): try: @@ -239,7 +240,6 @@ class TestDeprecations(TestWithoutExtensions): env=os.environ, stderr=subprocess.STDOUT, ).decode('utf-8', 'ignore') - self.assertIn('Warning: Option "test_sub_1" is deprecated and will be removed in future versions.', output) self.assertIn( 'Warning: Command "test-1" is deprecated and will be removed in future versions. ' @@ -374,8 +374,8 @@ class TestWrapperCommands(TestCase): class TestEFuseCommands(TestWrapperCommands): """ Test if wrapper commands for espefuse.py are working as expected. - The goal is NOT to test the functionality of espefuse.py, but to test if the wrapper commands - are working as expected. + The goal is NOT to test the functionality of espefuse.py + but to test if the wrapper commands are working as expected. """ def test_efuse_summary(self): @@ -438,8 +438,8 @@ class TestEFuseCommands(TestWrapperCommands): class TestSecureCommands(TestWrapperCommands): """ Test if wrapper commands for espsecure.py are working as expected. - The goal is NOT to test the functionality of espsecure.py, but to test if the wrapper commands are - working as expected. + The goal is NOT to test the functionality of espsecure.py + but to test if the wrapper commands are working as expected. """ @classmethod @@ -577,8 +577,8 @@ class TestSecureCommands(TestWrapperCommands): class TestMergeBinCommands(TestWrapperCommands): """ Test if merge-bin command is invoked as expected. - This test is not testing the functionality of esptool.py merge_bin command, but the invocation of - the command from idf.py. + This test is not testing the functionality of esptool.py merge_bin command + but the invocation of the command from idf.py. """ def test_merge_bin(self):