ci(tools): Avoiding full rebuilds where not needed to improve performance

This commit is contained in:
Jakub Kocka
2026-01-26 09:43:43 +01:00
parent 02c8a8b99d
commit d860da47c0
4 changed files with 63 additions and 47 deletions
+17 -8
View File
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import logging
@@ -8,7 +8,10 @@ import shutil
from pathlib import Path
import pytest
from test_build_system_helpers import EnvDict, IdfPyFunc, append_to_file, replace_in_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 replace_in_file
@pytest.mark.usefixtures('test_app_copy')
@@ -30,15 +33,21 @@ def test_partition_nearly_full_warning(idf_py: IdfPyFunc, test_app_copy: Path, d
logging.info('Warning is given if smallest partition is nearly full')
ret = idf_py('build')
# Build a first time to get the binary size and to check that no warning is issued.
assert 'partition is nearly full' not in ret.stdout, 'Warning for nearly full smallest partition was given when the condition is not fulfilled'
assert 'partition is nearly full' not in ret.stdout, (
'Warning for nearly full smallest partition was given when the condition is not fulfilled'
)
# Get the size of the binary, in KB. Convert it to next multiple of 4.
# The goal is to create an app partition which is slightly bigger than the binary itself
updated_file_size = int((os.stat(test_app_copy / 'build' / 'build_test_app.bin').st_size + 4095) / 4096) * 4
idf_path = Path(default_idf_env['IDF_PATH'])
shutil.copy2(idf_path / 'components' / 'partition_table' / 'partitions_singleapp.csv', test_app_copy / 'partitions.csv')
replace_in_file(test_app_copy / 'partitions.csv',
'factory, app, factory, , 1M',
f'factory, app, factory, , {updated_file_size}K')
(test_app_copy / 'sdkconfig').write_text('\n'.join(['CONFIG_PARTITION_TABLE_CUSTOM=y', 'CONFIG_FREERTOS_SMP=n']))
shutil.copy2(
idf_path / 'components' / 'partition_table' / 'partitions_singleapp.csv', test_app_copy / 'partitions.csv'
)
replace_in_file(
test_app_copy / 'partitions.csv',
'factory, app, factory, , 1M',
f'factory, app, factory, , {updated_file_size}K',
)
append_to_file(test_app_copy / 'sdkconfig', '\n'.join(['CONFIG_PARTITION_TABLE_CUSTOM=y', 'CONFIG_FREERTOS_SMP=n']))
ret = idf_py('build', check=False)
assert 'partition is nearly full' in ret.stdout
+1 -1
View File
@@ -82,7 +82,7 @@ def test_spaces_bundle3(idf_copy: Path) -> None:
def test_spaces_bundle4(dummy_: str, idf_py: IdfPyFunc, test_app_copy: Path) -> None:
logging.info(f'Running test spaces bundle 4 in {test_app_copy}')
(test_app_copy / 'sdkconfig').write_text('CONFIG_APP_REPRODUCIBLE_BUILD=y')
idf_py('build')
idf_py('reconfigure')
(test_app_copy / 'sdkconfig').unlink()
idf_py('set-target', 'esp32s2')
+41 -35
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
# SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import json
import os
@@ -50,48 +50,54 @@ class TestWithoutExtensions(TestCase):
class TestExtensions(TestWithoutExtensions):
def test_extension_loading(self):
@classmethod
def setUpClass(cls):
super().setUpClass()
# Create symlink once for all tests in this class
# Handle race conditions with parallel test execution (pytest-xdist)
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'
)
except FileExistsError:
# Another worker already created it - that's fine
pass
os.environ['IDF_EXTRA_ACTIONS_PATH'] = os.path.join(current_dir, 'extra_path')
self.assertIn('--test-extension-option', output)
self.assertIn('test_subcommand', output)
self.assertIn('--some-extension-option', output)
self.assertIn('extra_subcommand', output)
finally:
@classmethod
def tearDownClass(cls):
# Clean up symlink after all tests complete
# Use try/except to handle race conditions with parallel execution
try:
os.remove(link_path)
except FileNotFoundError:
# Another worker already removed it - that's fine
pass
super().tearDownClass()
def test_extension_loading(self):
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)
self.assertIn('--some-extension-option', output)
self.assertIn('extra_subcommand', output)
def test_extension_execution(self):
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, '--some-extension-option=awesome', 'test_subcommand', 'extra_subcommand'],
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)
self.assertIn('!!! From some subcommand', output)
finally:
os.remove(link_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')
self.assertIn('!!! From some global callback: awesome', output)
self.assertIn('!!! From some subcommand', output)
self.assertIn('!!! From test global callback: test', output)
self.assertIn('!!! From some subcommand', output)
def test_hidden_commands(self):
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'
)
self.assertIn('test_subcommand', output)
self.assertNotIn('hidden_one', output)
finally:
os.remove(link_path)
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)
class TestDependencyManagement(TestWithoutExtensions):