diff --git a/examples/security/flash_encryption/pytest_flash_encryption.py b/examples/security/flash_encryption/pytest_flash_encryption.py index ceb2eecc32..cbb32a23f4 100644 --- a/examples/security/flash_encryption/pytest_flash_encryption.py +++ b/examples/security/flash_encryption/pytest_flash_encryption.py @@ -36,12 +36,15 @@ def _test_flash_encryption(dut: Dut) -> None: key_bytes = b'\xff' + b'\x00' * 31 aes_xts = True - # Emulate espsecure encrypt_flash_data command EncryptFlashDataArgs = namedtuple( 'EncryptFlashDataArgs', ['output', 'plaintext_file', 'address', 'keyfile', 'flash_crypt_conf', 'aes_xts'] ) args = EncryptFlashDataArgs(BytesIO(), BytesIO(plain_data), flash_addr, BytesIO(key_bytes), 0xF, aes_xts) - espsecure.encrypt_flash_data(args) + try: + # espsecure 5.0 arguments are passed one by one; the following will convert tuple to dict and unwrap it + espsecure.encrypt_flash_data(**args._asdict()) + except TypeError: + espsecure.encrypt_flash_data(args) expected_ciphertext = args.output.getvalue() hex_ciphertext = binascii.hexlify(expected_ciphertext).decode('ascii') diff --git a/examples/system/ota/otatool/get_running_partition.py b/examples/system/ota/otatool/get_running_partition.py index bfb8a0bde3..5a05fca8ac 100644 --- a/examples/system/ota/otatool/get_running_partition.py +++ b/examples/system/ota/otatool/get_running_partition.py @@ -38,7 +38,7 @@ def get_running_partition(port=None): try: # Check what esptool.py finds on what port the device is connected to output = subprocess.check_output([sys.executable, ESPTOOL_PY, 'chip_id']) # may raise CalledProcessError - pattern = r'Serial port ([\S]+)' + pattern = r'Serial port ([^:\s]+)' pattern = re.compile(pattern.encode()) port = re.search(pattern, output).group(1) # may raise AttributeError diff --git a/tools/ci/ignore_build_warnings.txt b/tools/ci/ignore_build_warnings.txt index dddea3463f..81c42ae5fb 100644 --- a/tools/ci/ignore_build_warnings.txt +++ b/tools/ci/ignore_build_warnings.txt @@ -39,3 +39,8 @@ warning: unknown kconfig symbol 'UNITY_FREERTOS_STACK_SIZE' assigned to '12288' warning: unknown kconfig symbol 'WPA3_SAE' assigned to 'y' in .*/components/wpa_supplicant/test_apps/sdkconfig.defaults ld: warning: ignoring duplicate libraries archive library: .+ the table of contents is empty +Warning: Deprecated: Option '--flash_size' is deprecated. Use '--flash-size' instead. +Warning: Deprecated: Option '--flash_mode' is deprecated. Use '--flash-mode' instead. +Warning: Deprecated: Option '--flash_freq' is deprecated. Use '--flash-freq' instead. +Warning: Deprecated: Command 'sign_data' is deprecated. Use 'sign-data' instead. +Warning: Deprecated: Command 'extract_public_key' is deprecated. Use 'extract-public-key' instead. diff --git a/tools/idf_py_actions/serial_ext.py b/tools/idf_py_actions/serial_ext.py index e2503efb26..09692ce077 100644 --- a/tools/idf_py_actions/serial_ext.py +++ b/tools/idf_py_actions/serial_ext.py @@ -7,9 +7,6 @@ import signal import sys from pathlib import Path from typing import Any -from typing import Dict -from typing import List -from typing import Optional import click @@ -46,22 +43,22 @@ PORT = { } -def yellow_print(message: str, newline: Optional[str] = '\n') -> None: +def yellow_print(message: str, newline: str | None = '\n') -> None: """Print a message to stderr with yellow highlighting""" - sys.stderr.write('%s%s%s%s' % ('\033[0;33m', message, '\033[0m', newline)) + sys.stderr.write(f'\033[0;33m{message}\033[0m{newline}') sys.stderr.flush() -def action_extensions(base_actions: Dict, project_path: str) -> Dict: +def action_extensions(base_actions: dict, project_path: str) -> dict: def _get_project_desc(ctx: click.core.Context, args: PropertyDict) -> Any: desc_path = os.path.join(args.build_dir, 'project_description.json') if not os.path.exists(desc_path): ensure_build_directory(args, ctx.info_name) - with open(desc_path, 'r', encoding='utf-8') as f: + with open(desc_path, encoding='utf-8') as f: project_desc = json.load(f) return project_desc - def _get_esptool_args(args: PropertyDict) -> List: + def _get_esptool_args(args: PropertyDict) -> list: esptool_path = os.path.join(os.environ['IDF_PATH'], 'components/esptool_py/esptool/esptool.py') esptool_wrapper_path = os.environ.get('ESPTOOL_WRAPPER', '') if args.port is None: @@ -84,7 +81,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict: result += ['--no-stub'] return result - def _get_commandline_options(ctx: click.core.Context) -> List: + def _get_commandline_options(ctx: click.core.Context) -> list: """Return all the command line options up to first action""" # This approach ignores argument parsing done Click result = [] @@ -185,7 +182,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict: monitor_args += ['--disable-auto-color'] idf_py = [PYTHON] + _get_commandline_options(ctx) # commands to re-run idf.py - monitor_args += ['-m', ' '.join("'%s'" % a for a in idf_py)] + monitor_args += ['-m', ' '.join(f"'{a}'" for a in idf_py)] hints = not args.no_hints # Temporally ignore SIGINT, which is used in idf_monitor to spawn gdb. @@ -247,7 +244,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict: esptool_args += ['erase_flash'] RunTool('esptool.py', esptool_args, args.build_dir, hints=not args.no_hints)() - def global_callback(ctx: click.core.Context, global_args: Dict, tasks: PropertyDict) -> None: + def global_callback(ctx: click.core.Context, global_args: dict, tasks: PropertyDict) -> None: encryption = any([task.name in ('encrypted-flash', 'encrypted-app-flash') for task in tasks]) if encryption: for task in tasks: @@ -503,7 +500,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict: encrypt_nvs_partition_args += [extra_args['partition_size']] RunTool('espsecure', encrypt_nvs_partition_args, args.project_dir)() - def _parse_efuse_args(ctx: click.core.Context, args: PropertyDict, extra_args: Dict) -> List: + def _parse_efuse_args(ctx: click.core.Context, args: PropertyDict, extra_args: dict) -> list: efuse_args = [] if args.port: efuse_args += ['-p', args.port] @@ -520,18 +517,20 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict: efuse_args += ['--do-not-confirm'] return efuse_args - def efuse_burn(action: str, ctx: click.core.Context, args: PropertyDict, **extra_args: Dict) -> None: + def efuse_burn(action: str, ctx: click.core.Context, args: PropertyDict, **extra_args: dict) -> None: ensure_build_directory(args, ctx.info_name) - burn_efuse_args = [PYTHON, '-mespefuse', 'burn_efuse'] + burn_efuse_args = [PYTHON, '-m', 'espefuse'] burn_efuse_args += _parse_efuse_args(ctx, args, extra_args) + burn_efuse_args.append('burn_efuse') if extra_args['efuse_positional_args']: burn_efuse_args += list(extra_args['efuse_positional_args']) RunTool('espefuse', burn_efuse_args, args.build_dir)() def efuse_burn_key(action: str, ctx: click.core.Context, args: PropertyDict, **extra_args: str) -> None: ensure_build_directory(args, ctx.info_name) - burn_key_args = [PYTHON, '-mespefuse', 'burn_key'] + burn_key_args = [PYTHON, '-m', 'espefuse'] burn_key_args += _parse_efuse_args(ctx, args, extra_args) + burn_key_args.append('burn_key') if extra_args['no_protect_key']: burn_key_args += ['--no-protect-key'] if extra_args['force_write_always']: @@ -543,19 +542,21 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict: RunTool('espefuse.py', burn_key_args, args.project_dir, build_dir=args.build_dir)() def efuse_dump( - action: str, ctx: click.core.Context, args: PropertyDict, file_name: str, **extra_args: Dict + action: str, ctx: click.core.Context, args: PropertyDict, file_name: str, **extra_args: dict ) -> None: ensure_build_directory(args, ctx.info_name) - dump_args = [PYTHON, '-mespefuse', 'dump'] + dump_args = [PYTHON, '-m', 'espefuse'] dump_args += _parse_efuse_args(ctx, args, extra_args) + dump_args.append('dump') if file_name: dump_args += ['--file_name', file_name] RunTool('espefuse', dump_args, args.build_dir)() - def efuse_read_protect(action: str, ctx: click.core.Context, args: PropertyDict, **extra_args: Dict) -> None: + def efuse_read_protect(action: str, ctx: click.core.Context, args: PropertyDict, **extra_args: dict) -> None: ensure_build_directory(args, ctx.info_name) - read_protect_args = [PYTHON, '-mespefuse', 'read_protect_efuse'] + read_protect_args = [PYTHON, '-m', 'espefuse'] read_protect_args += _parse_efuse_args(ctx, args, extra_args) + read_protect_args.append('read_protect_efuse') if extra_args['efuse_positional_args']: read_protect_args += list(extra_args['efuse_positional_args']) RunTool('espefuse', read_protect_args, args.build_dir)() @@ -565,21 +566,23 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict: ctx: click.core.Context, args: PropertyDict, format: str, # noqa: A002 - **extra_args: Dict, + **extra_args: dict, ) -> None: ensure_build_directory(args, ctx.info_name) - summary_args = [PYTHON, '-mespefuse', 'summary'] + summary_args = [PYTHON, '-m', 'espefuse'] summary_args += _parse_efuse_args(ctx, args, extra_args) + summary_args.append('summary') if format: summary_args += [f'--format={format.replace("-", "_")}'] if extra_args['efuse_name']: summary_args += [str(extra_args['efuse_name'])] RunTool('espefuse', summary_args, args.build_dir)() - def efuse_write_protect(action: str, ctx: click.core.Context, args: PropertyDict, **extra_args: Dict) -> None: + def efuse_write_protect(action: str, ctx: click.core.Context, args: PropertyDict, **extra_args: dict) -> None: ensure_build_directory(args, ctx.info_name) - write_protect_args = [PYTHON, '-mespefuse', 'write_protect_efuse'] + write_protect_args = [PYTHON, '-m', 'espefuse'] write_protect_args += _parse_efuse_args(ctx, args, extra_args) + write_protect_args.append('write_protect_efuse') if extra_args['efuse_positional_args']: write_protect_args += list(extra_args['efuse_positional_args']) RunTool('espefuse', write_protect_args, args.build_dir)() diff --git a/tools/test_apps/system/gdb_loadable_elf/test_gdb_loadable_elf_util/loadable_app_serial.py b/tools/test_apps/system/gdb_loadable_elf/test_gdb_loadable_elf_util/loadable_app_serial.py index 2d38453fbe..cc2d9e0e1f 100644 --- a/tools/test_apps/system/gdb_loadable_elf/test_gdb_loadable_elf_util/loadable_app_serial.py +++ b/tools/test_apps/system/gdb_loadable_elf/test_gdb_loadable_elf_util/loadable_app_serial.py @@ -1,24 +1,25 @@ -# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Unlicense OR CC0-1.0 import logging -from typing import Any, Optional +from collections import namedtuple +from typing import Any import esptool from pytest_embedded_idf.app import IdfApp -from pytest_embedded_serial_esp.serial import EspSerial, EsptoolArgs +from pytest_embedded_serial_esp.serial import EspSerial class LoadableAppSerial(EspSerial): def __init__( self, app: IdfApp, - target: Optional[str] = None, + target: str | None = None, **kwargs: Any, ) -> None: self.app = app if not hasattr(self.app, 'target'): - raise ValueError(f'Idf app not parsable. Please check if it\'s valid: {self.app.binary_path}') + raise ValueError(f"Idf app not parsable. Please check if it's valid: {self.app.binary_path}") if target and self.app.target and self.app.target != target: raise ValueError(f'Targets do not match. App target: {self.app.target}, Cmd target: {target}.') @@ -37,17 +38,10 @@ class LoadableAppSerial(EspSerial): logging.error('No image file detected. Skipping load ram...') return - f_bin_file = open(self.app.bin_file, 'rb') - - default_kwargs = { - 'filename': f_bin_file, - 'chip': self.esp.CHIP_NAME.lower().replace('-', ''), - } - - load_ram_args = EsptoolArgs(**default_kwargs) - - try: + with open(self.app.bin_file, 'rb') as f_bin_file: self.esp.change_baud(460800) - esptool.load_ram(self.esp, load_ram_args) - finally: - f_bin_file.close() + try: + # esptool v5.0+ + esptool.load_ram(self.esp, input=f_bin_file) + except TypeError: + esptool.load_ram(self.esp, namedtuple('args', 'filename')(f_bin_file)) diff --git a/tools/test_build_system/test_build.py b/tools/test_build_system/test_build.py index 13a8f32faf..da8d17c73c 100644 --- a/tools/test_build_system/test_build.py +++ b/tools/test_build_system/test_build.py @@ -2,13 +2,12 @@ # SPDX-License-Identifier: Apache-2.0 import logging import os +import re import shutil import stat import sys import textwrap from pathlib import Path -from typing import List -from typing import Union import pytest from test_build_system_helpers import APP_BINS @@ -22,7 +21,7 @@ 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: +def assert_built(paths: list[str] | list[Path]) -> None: for path in paths: assert os.path.exists(path) @@ -92,7 +91,7 @@ def test_build_with_cmake_and_idf_path_unset(idf_py: IdfPyFunc, test_app_copy: P logging.info('Can build with IDF_PATH set via cmake cache not environment') replace_in_file('CMakeLists.txt', 'ENV{IDF_PATH}', '{IDF_PATH}') - run_cmake_and_build('-G', 'Ninja', '..', '-DIDF_PATH={}'.format(idf_path), env=env) + run_cmake_and_build('-G', 'Ninja', '..', f'-DIDF_PATH={idf_path}', env=env) assert_built(BOOTLOADER_BINS + APP_BINS + PARTITION_BIN) idf_py('fullclean') @@ -100,7 +99,7 @@ def test_build_with_cmake_and_idf_path_unset(idf_py: IdfPyFunc, test_app_copy: P # working with already changed CMakeLists.txt kconfig_file = test_app_copy / 'main' / 'Kconfig.projbuild' kconfig_file.write_text('source "$IDF_PATH/examples/wifi/getting_started/station/main/Kconfig.projbuild"') - run_cmake_and_build('-G', 'Ninja', '..', '-DIDF_PATH={}'.format(idf_path), env=env) + run_cmake_and_build('-G', 'Ninja', '..', f'-DIDF_PATH={idf_path}', env=env) assert_built(BOOTLOADER_BINS + APP_BINS + PARTITION_BIN) kconfig_file.unlink() # remove file to not affect following sub-test idf_py('fullclean') @@ -108,7 +107,7 @@ def test_build_with_cmake_and_idf_path_unset(idf_py: IdfPyFunc, test_app_copy: P logging.info('Can build with IDF_PATH unset and inferred by build system') # replacing {IDF_PATH} not ENV{IDF_PATH} since CMakeLists.txt was already changed in this test replace_in_file('CMakeLists.txt', '{IDF_PATH}', '{ci_idf_path}') - run_cmake_and_build('-G', 'Ninja', '-D', 'ci_idf_path={}'.format(idf_path), '..', env=env) + run_cmake_and_build('-G', 'Ninja', '-D', f'ci_idf_path={idf_path}', '..', env=env) assert_built(BOOTLOADER_BINS + APP_BINS + PARTITION_BIN) @@ -184,16 +183,18 @@ def test_build_dfu(idf_py: IdfPyFunc) -> None: def test_build_uf2(idf_py: IdfPyFunc) -> None: logging.info('UF2 build works') ret = idf_py('uf2') - assert 'build/uf2.bin, ready to be flashed with any ESP USB Bridge' in ret.stdout, 'UF2 build should work for esp32' + assert re.search(r"build/uf2.bin'?, ready to be flashed with any ESP USB Bridge", ret.stdout) is not None, ( + 'UF2 build should work for esp32' + ) assert_built(BOOTLOADER_BINS + APP_BINS + PARTITION_BIN + ['build/uf2.bin']) ret = idf_py('uf2-app') - assert 'build/uf2-app.bin, ready to be flashed with any ESP USB Bridge' in ret.stdout, ( + assert re.search(r"build/uf2-app.bin'?, ready to be flashed with any ESP USB Bridge", ret.stdout) is not None, ( 'UF2 build should work for application binary' ) assert_built(['build/uf2-app.bin']) idf_py('set-target', 'esp32s2') ret = idf_py('uf2') - assert 'build/uf2.bin, ready to be flashed with any ESP USB Bridge' in ret.stdout, ( + assert re.search(r"build/uf2.bin'?, ready to be flashed with any ESP USB Bridge", ret.stdout) is not None, ( 'UF2 build should work for esp32s2' ) assert_built(BOOTLOADER_BINS + APP_BINS + PARTITION_BIN + ['build/uf2.bin']) @@ -253,7 +254,7 @@ def test_build_with_misspelled_kconfig(idf_py: IdfPyFunc, test_app_copy: Path) - ret = idf_py('build') assert " file should be named 'Kconfig.projbuild'" in ret.stderr, 'Misspelled Kconfig file should be detected' assert_built(BOOTLOADER_BINS + APP_BINS + PARTITION_BIN) - with open(test_app_copy / 'sdkconfig', 'r') as f: + with open(test_app_copy / 'sdkconfig') as f: sdkconfig = f.read() assert 'CONFIG_FROM_MISSPELLED_KCONFIG=y' in sdkconfig, ( 'There should be a config from the misspelled Kconfig file in sdkconfig' diff --git a/tools/test_idf_py/test_idf_py.py b/tools/test_idf_py/test_idf_py.py index b715a78945..b4efe0a917 100755 --- a/tools/test_idf_py/test_idf_py.py +++ b/tools/test_idf_py/test_idf_py.py @@ -8,10 +8,9 @@ import shutil import subprocess import sys from typing import Any -from typing import List +from unittest import TestCase from unittest import main from unittest import mock -from unittest import TestCase import jsonschema @@ -37,10 +36,13 @@ class TestWithoutExtensions(TestCase): @classmethod def setUpClass(cls): # Disable the component manager and extra extensions for these tests - cls.env_patcher = mock.patch.dict(os.environ, { - 'IDF_COMPONENT_MANAGER': '0', - 'IDF_EXTRA_ACTIONS_PATH': '', - }) + cls.env_patcher = mock.patch.dict( + os.environ, + { + 'IDF_COMPONENT_MANAGER': '0', + 'IDF_EXTRA_ACTIONS_PATH': '', + }, + ) cls.env_patcher.start() super().setUpClass() @@ -51,8 +53,9 @@ class TestExtensions(TestWithoutExtensions): try: os.symlink(extension_path, link_path) os.environ['IDF_EXTRA_ACTIONS_PATH'] = os.path.join(current_dir, 'extra_path') - output = subprocess.check_output([sys.executable, idf_py_path, '--help'], - env=os.environ).decode('utf-8', 'ignore') + output = subprocess.check_output([sys.executable, idf_py_path, '--help'], env=os.environ).decode( + 'utf-8', 'ignore' + ) self.assertIn('--test-extension-option', output) self.assertIn('test_subcommand', output) @@ -67,7 +70,8 @@ class TestExtensions(TestWithoutExtensions): os.environ['IDF_EXTRA_ACTIONS_PATH'] = ';'.join([os.path.join(current_dir, 'extra_path')]) output = subprocess.check_output( [sys.executable, idf_py_path, '--some-extension-option=awesome', 'test_subcommand', 'extra_subcommand'], - env=os.environ).decode('utf-8', 'ignore') + env=os.environ, + ).decode('utf-8', 'ignore') self.assertIn('!!! From some global callback: awesome', output) self.assertIn('!!! From some subcommand', output) self.assertIn('!!! From test global callback: test', output) @@ -79,8 +83,9 @@ class TestExtensions(TestWithoutExtensions): try: os.symlink(extension_path, link_path) os.environ['IDF_EXTRA_ACTIONS_PATH'] = ';'.join([os.path.join(current_dir, 'extra_path')]) - output = subprocess.check_output([sys.executable, idf_py_path, '--help'], - env=os.environ).decode('utf-8', 'ignore') + output = subprocess.check_output([sys.executable, idf_py_path, '--help'], env=os.environ).decode( + 'utf-8', 'ignore' + ) self.assertIn('test_subcommand', output) self.assertNotIn('hidden_one', output) @@ -127,7 +132,8 @@ class TestDependencyManagement(TestWithoutExtensions): sys.stderr = sys.__stderr__ self.assertIn( 'WARNING: Commands "all", "clean" are found in the list of commands more than once.', - capturedOutput.getvalue()) + capturedOutput.getvalue(), + ) sys.stderr = capturedOutput idf.init_cli()( @@ -136,7 +142,8 @@ class TestDependencyManagement(TestWithoutExtensions): ) sys.stderr = sys.__stderr__ self.assertIn( - 'WARNING: Command "clean" is found in the list of commands more than once.', capturedOutput.getvalue()) + 'WARNING: Command "clean" is found in the list of commands more than once.', capturedOutput.getvalue() + ) class TestVerboseFlag(TestWithoutExtensions): @@ -145,10 +152,13 @@ class TestVerboseFlag(TestWithoutExtensions): [ sys.executable, idf_py_path, - '-C%s' % current_dir, + '-C', + current_dir, '-v', 'test-verbose', - ], env=os.environ).decode('utf-8', 'ignore') + ], + env=os.environ, + ).decode('utf-8', 'ignore') self.assertIn('Verbose mode on', output) @@ -157,9 +167,12 @@ class TestVerboseFlag(TestWithoutExtensions): [ sys.executable, idf_py_path, - '-C%s' % current_dir, + '-C', + current_dir, 'test-verbose', - ], env=os.environ).decode('utf-8', 'ignore') + ], + env=os.environ, + ).decode('utf-8', 'ignore') self.assertIn('Output from test-verbose', output) self.assertNotIn('Verbose mode on', output) @@ -188,27 +201,31 @@ class TestDeprecations(TestWithoutExtensions): def test_exit_with_error_for_subcommand(self): try: subprocess.check_output( - [sys.executable, idf_py_path, '-C%s' % current_dir, 'test-2'], env=os.environ, stderr=subprocess.STDOUT) + [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')) def test_exit_with_error_for_option(self): try: subprocess.check_output( - [sys.executable, idf_py_path, '-C%s' % current_dir, '--test-5=asdf'], + [sys.executable, idf_py_path, '-C', current_dir, '--test-5=asdf'], env=os.environ, - stderr=subprocess.STDOUT) + stderr=subprocess.STDOUT, + ) except subprocess.CalledProcessError as e: self.assertIn( 'Error: Option "test_5" is deprecated since v2.0 and was removed in v3.0.', - e.output.decode('utf-8', 'ignore')) + e.output.decode('utf-8', 'ignore'), + ) def test_deprecation_messages(self): output = subprocess.check_output( [ sys.executable, idf_py_path, - '-C%s' % current_dir, + '-C', + current_dir, '--test-0=a', '--test-1=b', '--test-2=c', @@ -220,23 +237,28 @@ class TestDeprecations(TestWithoutExtensions): 'test-1', ], env=os.environ, - stderr=subprocess.STDOUT).decode('utf-8', 'ignore') + 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. ' - 'Please use alternative command.', output) + 'Please use alternative command.', + output, + ) self.assertIn('Warning: Option "test_1" is deprecated and will be removed in future versions.', output) self.assertIn( 'Warning: Option "test_2" is deprecated and will be removed in future versions. ' - 'Please update your parameters.', output) + 'Please update your parameters.', + output, + ) self.assertIn('Warning: Option "test_3" is deprecated and will be removed in future versions.', output) self.assertNotIn('"test-0" is deprecated', output) self.assertNotIn('"test_0" is deprecated', output) class TestHelpOutput(TestWithoutExtensions): - def action_test_idf_py(self, commands: List[str], schema: Any) -> None: + def action_test_idf_py(self, commands: list[str], schema: Any) -> None: env = dict(**os.environ) python = shutil.which('python', path=env['PATH']) if python is None: @@ -244,20 +266,17 @@ class TestHelpOutput(TestWithoutExtensions): idf_path = env.get('IDF_PATH') if idf_path is None: raise ValueError('Empty IDF_PATH') - idf_py_cmd = [ - python, - os.path.join(idf_path, 'tools', 'idf.py') - ] + idf_py_cmd = [python, os.path.join(idf_path, 'tools', 'idf.py')] commands = idf_py_cmd + commands output_file = 'idf_py_help_output.json' with open(output_file, 'w') as outfile: subprocess.run(commands, env=env, stdout=outfile) - with open(output_file, 'r') as outfile: + with open(output_file) as outfile: help_obj = json.load(outfile) self.assertIsNone(jsonschema.validate(help_obj, schema)) def test_output(self): - with open(os.path.join(current_dir, 'idf_py_help_schema.json'), 'r') as schema_file: + with open(os.path.join(current_dir, 'idf_py_help_schema.json')) as schema_file: schema_json = json.load(schema_file) self.action_test_idf_py(['help', '--json'], schema_json) self.action_test_idf_py(['help', '--json', '--add-options'], schema_json) @@ -270,7 +289,8 @@ class TestFileArgumentExpansion(TestCase): output = subprocess.check_output( [sys.executable, idf_py_path, '--version', '@file_args_expansion_inputs/args_a'], env=os.environ, - stderr=subprocess.STDOUT).decode('utf-8', 'ignore') + stderr=subprocess.STDOUT, + ).decode('utf-8', 'ignore') self.assertIn('Running: idf.py --version DAAA DBBB', output) except subprocess.CalledProcessError as e: self.fail(f'Process should have exited normally, but it exited with a return code of {e.returncode}') @@ -279,9 +299,16 @@ class TestFileArgumentExpansion(TestCase): """Test multiple @filename arguments""" try: output = subprocess.check_output( - [sys.executable, idf_py_path, '--version', '@file_args_expansion_inputs/args_a', '@file_args_expansion_inputs/args_b'], + [ + sys.executable, + idf_py_path, + '--version', + '@file_args_expansion_inputs/args_a', + '@file_args_expansion_inputs/args_b', + ], env=os.environ, - stderr=subprocess.STDOUT).decode('utf-8', 'ignore') + stderr=subprocess.STDOUT, + ).decode('utf-8', 'ignore') self.assertIn('Running: idf.py --version DAAA DBBB DCCC DDDD', output) except subprocess.CalledProcessError as e: self.fail(f'Process should have exited normally, but it exited with a return code of {e.returncode}') @@ -292,7 +319,8 @@ class TestFileArgumentExpansion(TestCase): output = subprocess.check_output( [sys.executable, idf_py_path, '--version', '@file_args_expansion_inputs/args_recursive'], env=os.environ, - stderr=subprocess.STDOUT).decode('utf-8', 'ignore') + stderr=subprocess.STDOUT, + ).decode('utf-8', 'ignore') self.assertIn('Running: idf.py --version DAAA DBBB DEEE DFFF', output) except subprocess.CalledProcessError as e: self.fail(f'Process should have exited normally, but it exited with a return code of {e.returncode}') @@ -303,7 +331,8 @@ class TestFileArgumentExpansion(TestCase): subprocess.check_output( [sys.executable, idf_py_path, '--version', '@file_args_expansion_inputs/args_circular_a'], env=os.environ, - stderr=subprocess.STDOUT).decode('utf-8', 'ignore') + stderr=subprocess.STDOUT, + ).decode('utf-8', 'ignore') self.assertIn('Circular dependency in file argument expansion', cm.exception.output.decode('utf-8', 'ignore')) def test_missing_file(self): @@ -312,8 +341,11 @@ class TestFileArgumentExpansion(TestCase): subprocess.check_output( [sys.executable, idf_py_path, '--version', '@args_non_existent'], env=os.environ, - stderr=subprocess.STDOUT).decode('utf-8', 'ignore') - self.assertIn('(expansion of @args_non_existent) could not be opened', cm.exception.output.decode('utf-8', 'ignore')) + stderr=subprocess.STDOUT, + ).decode('utf-8', 'ignore') + self.assertIn( + '(expansion of @args_non_existent) could not be opened', cm.exception.output.decode('utf-8', 'ignore') + ) class TestWrapperCommands(TestCase): @@ -323,12 +355,11 @@ class TestWrapperCommands(TestCase): os.chdir(cls.sample_project_dir) super().setUpClass() - def call_command(self, command: List[str]) -> str: + def call_command(self, command: list[str]) -> str: try: - output = subprocess.check_output( - command, - env=os.environ, - stderr=subprocess.STDOUT).decode('utf-8', 'ignore') + output = subprocess.check_output(command, env=os.environ, stderr=subprocess.STDOUT).decode( + 'utf-8', 'ignore' + ) return output except subprocess.CalledProcessError as e: self.fail(f'Process should have exited normally, but it exited with a return code of {e.returncode}') @@ -343,7 +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): @@ -351,17 +383,17 @@ class TestEFuseCommands(TestWrapperCommands): output = self.call_command(summary_command) self.assertIn('EFUSE_NAME (Block) Description = [Meaningful Value] [Readable/Writeable] (Hex Value)', output) - output = self.call_command(summary_command + ['--format','summary']) + output = self.call_command(summary_command + ['--format', 'summary']) self.assertIn('00:00:00:00:00:00', output) self.assertIn('MAC address', output) - output = self.call_command(summary_command + ['--format','value-only', 'WR_DIS']) + output = self.call_command(summary_command + ['--format', 'value-only', 'WR_DIS']) self.assertIn('0', output) def test_efuse_burn(self): burn_command = [sys.executable, idf_py_path, 'efuse-burn', '--virt', '--do-not-confirm'] output = self.call_command(burn_command + ['WR_DIS', '1']) - self.assertIn('\'WR_DIS\' (Efuse write disable mask) 0x0000 -> 0x0001', output) + self.assertIn("'WR_DIS' (Efuse write disable mask) 0x0000 -> 0x0001", output) self.assertIn('Successful', output) output = self.call_command(burn_command + ['WR_DIS', '1', 'RD_DIS', '1']) @@ -371,9 +403,14 @@ class TestEFuseCommands(TestWrapperCommands): def test_efuse_burn_key(self): key_name = 'efuse_test_key.bin' - subprocess.run([sys.executable, idf_py_path, 'secure-generate-flash-encryption-key', os.path.join(current_dir, key_name)], stdout=subprocess.DEVNULL) + subprocess.run( + [sys.executable, idf_py_path, 'secure-generate-flash-encryption-key', os.path.join(current_dir, key_name)], + stdout=subprocess.DEVNULL, + ) burn_key_command = [sys.executable, idf_py_path, 'efuse-burn-key', '--virt', '--do-not-confirm'] - output = self.call_command(burn_key_command + ['--show-sensitive-info', 'secure_boot_v1', os.path.join(current_dir, key_name)]) + output = self.call_command( + burn_key_command + ['--show-sensitive-info', 'secure_boot_v1', os.path.join(current_dir, key_name)] + ) self.assertIn('Burn keys to blocks:', output) self.assertIn('Successful', output) @@ -401,8 +438,10 @@ 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 def setUpClass(cls): super().setUpClass() @@ -412,106 +451,125 @@ class TestSecureCommands(TestWrapperCommands): cls.nvs_partition_key = 'nvs_partition_key.bin' def secure_generate_flash_encryption_key(self): - generate_key_command = [sys.executable, idf_py_path, 'secure-generate-flash-encryption-key', self.flash_encryption_key] + generate_key_command = [ + sys.executable, + idf_py_path, + 'secure-generate-flash-encryption-key', + self.flash_encryption_key, + ] output = self.call_command(generate_key_command) - self.assertIn(f'Writing 256 random bits to key file {self.flash_encryption_key}', output) + self.assertRegex(output, f'Writing 256 random bits to key file "?{self.flash_encryption_key}"?') def secure_encrypt_flash_data(self): self.secure_generate_flash_encryption_key() - encrypt_command = [sys.executable, - idf_py_path, - 'secure-encrypt-flash-data', - '--aes-xts', - '--keyfile', - f'../{self.flash_encryption_key}', - '--address', - '0x1000', - '--output', - 'bootloader-enc.bin', - 'bootloader/bootloader.bin'] + encrypt_command = [ + sys.executable, + idf_py_path, + 'secure-encrypt-flash-data', + '--aes-xts', + '--keyfile', + f'../{self.flash_encryption_key}', + '--address', + '0x1000', + '--output', + 'bootloader-enc.bin', + 'bootloader/bootloader.bin', + ] output = self.call_command(encrypt_command) self.assertIn('Using 256-bit key', output) self.assertIn('Done', output) def test_secure_decrypt_flash_data(self): self.secure_encrypt_flash_data() - decrypt_command = [sys.executable, - idf_py_path, - 'secure-decrypt-flash-data', - '--aes-xts', - '--keyfile', - f'../{self.flash_encryption_key}', - '--address', - '0x1000', - '--output', - 'bootloader-dec.bin', - 'bootloader-enc.bin'] + decrypt_command = [ + sys.executable, + idf_py_path, + 'secure-decrypt-flash-data', + '--aes-xts', + '--keyfile', + f'../{self.flash_encryption_key}', + '--address', + '0x1000', + '--output', + 'bootloader-dec.bin', + 'bootloader-enc.bin', + ] output = self.call_command(decrypt_command) self.assertIn('Using 256-bit key', output) self.assertIn('Done', output) def secure_sign_data(self): self.secure_generate_signing_key() - sign_command = [sys.executable, - idf_py_path, - 'secure-sign-data', - '--version', - '2', - '--keyfile', - f'../{self.signing_key}', - '--output', - 'bootloader-signed.bin', - 'bootloader/bootloader.bin'] + sign_command = [ + sys.executable, + idf_py_path, + 'secure-sign-data', + '--version', + '2', + '--keyfile', + f'../{self.signing_key}', + '--output', + 'bootloader-signed.bin', + 'bootloader/bootloader.bin', + ] output = self.call_command(sign_command) self.assertIn('Signed', output) def secure_verify_signature(self): self.secure_sign_data() - sign_command = [sys.executable, - idf_py_path, - 'secure-verify-signature', - '--version', - '2', - '--keyfile', - f'../{self.signing_key}', - 'bootloader-signed.bin'] + sign_command = [ + sys.executable, + idf_py_path, + 'secure-verify-signature', + '--version', + '2', + '--keyfile', + f'../{self.signing_key}', + 'bootloader-signed.bin', + ] output = self.call_command(sign_command) self.assertIn('verification successful', output) def secure_generate_signing_key(self): - generate_key_command = [sys.executable, - idf_py_path, - 'secure-generate-signing-key', - '--version', - '2', - '--scheme', - 'rsa3072', - self.signing_key] + generate_key_command = [ + sys.executable, + idf_py_path, + 'secure-generate-signing-key', + '--version', + '2', + '--scheme', + 'rsa3072', + self.signing_key, + ] output = self.call_command(generate_key_command) - self.assertIn(f'RSA 3072 private key in PEM format written to {self.signing_key}', output) + self.assertRegex(output, f'RSA 3072 private key in PEM format written to "?{self.signing_key}"?') def test_secure_generate_key_digest(self): self.secure_generate_signing_key() - digest_command = [sys.executable, - idf_py_path, - 'secure-generate-key-digest', - '--keyfile', - f'{self.signing_key}', - '--output', - 'key_digest.bin'] + digest_command = [ + sys.executable, + idf_py_path, + 'secure-generate-key-digest', + '--keyfile', + f'{self.signing_key}', + '--output', + 'key_digest.bin', + ] output = self.call_command(digest_command) - self.assertIn(f'Writing the public key digest of {self.signing_key} to key_digest.bin', output) + self.assertRegex(output, f'Writing the public key digest of "?{self.signing_key}"? to "?key_digest.bin"?.') def test_secure_generate_nvs_partition_key(self): - generate_key_command = [sys.executable, - idf_py_path, - 'secure-generate-nvs-partition-key', - '--keyfile', - f'{self.nvs_partition_key}', - '--encryption-scheme', - 'HMAC', - '--hmac-keyfile', - 'nvs_partition_key.bin'] + generate_key_command = [ + sys.executable, + idf_py_path, + 'secure-generate-nvs-partition-key', + '--keyfile', + f'{self.nvs_partition_key}', + '--encryption-scheme', + 'HMAC', + '--hmac-keyfile', + 'nvs_partition_key.bin', + ] output = self.call_command(generate_key_command) self.assertIn('Created encryption keys:', output) @@ -519,14 +577,15 @@ 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): merge_bin_command = [sys.executable, idf_py_path, 'merge-bin'] merged_binary_name = 'test-merge-binary.bin' output = self.call_command(merge_bin_command + ['--output', merged_binary_name]) - self.assertIn(f'file {merged_binary_name}, ready to flash to offset 0x0', output) + self.assertRegex(output, f"file '?{merged_binary_name}'?, ready to flash to offset 0x0") self.assertIn(f'Merged binary {merged_binary_name} will be created in the build directory...', output)