diff --git a/examples/build_system/.build-test-rules.yml b/examples/build_system/.build-test-rules.yml index 509db630017..3c861c0085e 100644 --- a/examples/build_system/.build-test-rules.yml +++ b/examples/build_system/.build-test-rules.yml @@ -9,3 +9,13 @@ examples/build_system/cmake/plugins: disable_test: - if: IDF_TARGET not in ["esp32", "esp32c3"] reason: Testing on two diff architectures is sufficient + +examples/build_system/cmakev2/features/import_lib: + disable_test: + - if: IDF_TARGET not in ["esp32", "esp32c3"] + reason: QEMU tests limited to esp32 and esp32c3 + +examples/build_system/cmakev2/features/plugins: + disable_test: + - if: IDF_TARGET not in ["esp32", "esp32c3"] + reason: Testing on two diff architectures is sufficient diff --git a/examples/build_system/cmakev2/features/conditional_component/CMakeLists.txt b/examples/build_system/cmakev2/features/conditional_component/CMakeLists.txt index 83ea4f670fa..e7afe397341 100644 --- a/examples/build_system/cmakev2/features/conditional_component/CMakeLists.txt +++ b/examples/build_system/cmakev2/features/conditional_component/CMakeLists.txt @@ -33,7 +33,8 @@ idf_check_binary_size(${CMAKE_PROJECT_NAME}_binary) # Create app target add_custom_target(app ALL DEPENDS ${CMAKE_PROJECT_NAME}_binary) -# Generate metadata +# Generate flasher_args.json and metadata +idf_build_generate_flasher_args() idf_build_generate_metadata(BINARY ${CMAKE_PROJECT_NAME}_binary) # Create utility targets for configuration diff --git a/examples/build_system/cmakev2/features/conditional_component/pytest_cmakev2_conditional_component.py b/examples/build_system/cmakev2/features/conditional_component/pytest_cmakev2_conditional_component.py new file mode 100644 index 00000000000..c4a840ff434 --- /dev/null +++ b/examples/build_system/cmakev2/features/conditional_component/pytest_cmakev2_conditional_component.py @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded_idf.dut import IdfDut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.generic +@idf_parametrize('target', ['supported_targets'], indirect=['target']) +def test_cmakev2_conditional_component(dut: IdfDut) -> None: + dut.expect_exact('Conditional Component Example') + + # With default config, both logging and math components are enabled + dut.expect_exact('Logging component is ENABLED') + dut.expect_exact('This is an info message from logging_util') + dut.expect_exact('This is a warning message from logging_util') + + dut.expect_exact('Math component is ENABLED') + dut.expect_exact('add(10, 5) = 15') + dut.expect_exact('subtract(10, 5) = 5') + dut.expect_exact('multiply(10, 5) = 50') diff --git a/examples/build_system/cmakev2/features/import_lib/pytest_cmakev2_import_lib.py b/examples/build_system/cmakev2/features/import_lib/pytest_cmakev2_import_lib.py new file mode 100644 index 00000000000..a1a1f710e77 --- /dev/null +++ b/examples/build_system/cmakev2/features/import_lib/pytest_cmakev2_import_lib.py @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded_idf.utils import idf_parametrize +from pytest_embedded_qemu.dut import QemuDut + + +@pytest.mark.qemu +@idf_parametrize('target', ['esp32', 'esp32c3'], indirect=['target']) +def test_cmakev2_import_lib(dut: QemuDut) -> None: + dut.expect_exact('Initializing the filesystem') + dut.expect_exact('Read XML data:') + dut.expect_exact('Parsed XML data:') + dut.expect_exact('To: Tove') + dut.expect_exact('From: Jani') + dut.expect_exact('Heading: Reminder') + dut.expect_exact("Body: Don't forget me this weekend!") + dut.expect_exact('Example end', timeout=20) diff --git a/examples/build_system/cmakev2/features/import_lib_direct/pytest_cmakev2_import_lib_direct.py b/examples/build_system/cmakev2/features/import_lib_direct/pytest_cmakev2_import_lib_direct.py new file mode 100644 index 00000000000..86e987f47ea --- /dev/null +++ b/examples/build_system/cmakev2/features/import_lib_direct/pytest_cmakev2_import_lib_direct.py @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded_idf.dut import IdfDut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.generic +@idf_parametrize('target', ['supported_targets'], indirect=['target']) +def test_cmakev2_import_lib_direct(dut: IdfDut) -> None: + dut.expect_exact('lwjson library imported directly (downloaded, built, linked) without IDF component wrapper') + dut.expect_exact('Parsing JSON string...') + dut.expect_exact('Device name: ESP32') + dut.expect_exact('Number of cores: 2') + dut.expect_exact('Features:') + dut.expect_exact('- WiFi') + dut.expect_exact('- Bluetooth') + dut.expect_exact('- GPIO') + dut.expect_exact('Specifications:') + dut.expect_exact('Flash: 4MB') + dut.expect_exact('RAM: 520KB') + dut.expect_exact('Example complete!') diff --git a/examples/build_system/cmakev2/features/import_prebuilt/pytest_cmakev2_import_prebuilt.py b/examples/build_system/cmakev2/features/import_prebuilt/pytest_cmakev2_import_prebuilt.py new file mode 100644 index 00000000000..9b262d65505 --- /dev/null +++ b/examples/build_system/cmakev2/features/import_prebuilt/pytest_cmakev2_import_prebuilt.py @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded_idf.dut import IdfDut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.generic +@idf_parametrize('target', ['supported_targets'], indirect=['target']) +def test_cmakev2_import_prebuilt(dut: IdfDut) -> None: + dut.expect_exact("The running partition is 'factory'!") diff --git a/examples/build_system/cmakev2/features/multi_binary/CMakeLists.txt b/examples/build_system/cmakev2/features/multi_binary/CMakeLists.txt index 1706a09a957..f10efa6ee52 100644 --- a/examples/build_system/cmakev2/features/multi_binary/CMakeLists.txt +++ b/examples/build_system/cmakev2/features/multi_binary/CMakeLists.txt @@ -29,7 +29,10 @@ idf_flash_binary(app1_binary NAME "app1" FLASH) -# Generate metadata only for the primary app1 binary +# Generate flasher_args.json and metadata for the primary app1 binary. +# app1 is the only app registered in the global 'flash' target (app2 omits FLASH), +# so flasher_args.json correctly contains app1 as the sole app binary. +idf_build_generate_flasher_args() idf_build_generate_metadata(BINARY app1_binary) # Create menuconfig and confserver targets for app1 binary @@ -41,10 +44,13 @@ idf_build_binary(app2.elf OUTPUT_FILE "${CMAKE_BINARY_DIR}/app2.bin" TARGET app2_binary) +# Do NOT pass FLASH here: both apps share the same 0x10000 offset, which would +# create a duplicate key in the global 'flash' target and break flasher_args.json. +# app1 (with FLASH) is used as the default app in flasher_args.json; the pytest +# test swaps in app2.bin when needed. idf_flash_binary(app2_binary TARGET app2-flash - NAME "app2" - FLASH) + NAME "app2") # Create menuconfig and confserver targets for app2 binary idf_create_menuconfig(app2.elf TARGET app2-menuconfig) diff --git a/examples/build_system/cmakev2/features/multi_binary/pytest_cmakev2_multi_binary.py b/examples/build_system/cmakev2/features/multi_binary/pytest_cmakev2_multi_binary.py new file mode 100644 index 00000000000..42e7bebd898 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/pytest_cmakev2_multi_binary.py @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import os + +import pytest +from pytest_embedded_idf.app import FlashFile +from pytest_embedded_idf.dut import IdfDut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.generic +@idf_parametrize('target', ['supported_targets'], indirect=['target']) +def test_cmakev2_multi_binary(dut: IdfDut) -> None: + # app1 is auto-flashed by the DUT fixture via flasher_args.json + dut.expect_exact('Hello from component1!') + dut.expect_exact('Hello from component2!') + + # Re-flash only the app partition with app2 + app_offset = int(dut.app.flash_args['app1']['offset'], 16) + saved = dut.app.flash_files + dut.app.flash_files = [FlashFile(app_offset, os.path.join(dut.app.binary_path, 'app2.bin'))] + dut.serial.flash() + dut.app.flash_files = saved + + dut.expect_exact('Hello from component1!') + dut.expect_exact('Hello from component2!') + dut.expect_exact('Hello from component3!') diff --git a/examples/build_system/cmakev2/features/multi_config/pytest_cmakev2_multi_config.py b/examples/build_system/cmakev2/features/multi_config/pytest_cmakev2_multi_config.py new file mode 100644 index 00000000000..4d75f1b82a9 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_config/pytest_cmakev2_multi_config.py @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded_idf.dut import IdfDut +from pytest_embedded_idf.utils import idf_parametrize + +_EXPECTED = { + 'default': ('Blinky Development Board', 'func() from func_dev.c (Development) called.'), + 'prod1': ('Blinky Smart Light', 'func() from func_prod.c (Production) called.'), + 'prod2': ('Blinky Smart Switch', 'func() from func_prod.c (Production) called.'), +} + + +@pytest.mark.generic +@pytest.mark.parametrize('config', ['default', 'prod1', 'prod2'], indirect=True) +@idf_parametrize('target', ['supported_targets'], indirect=['target']) +def test_cmakev2_multi_config(dut: IdfDut, config: str) -> None: + product_name, func_line = _EXPECTED[config] + dut.expect_exact(f'This app is built for running on: {product_name}') + dut.expect_exact(func_line) + dut.expect_exact('See README.md for building and running other app configurations.') diff --git a/examples/build_system/cmakev2/features/multi_config/sdkconfig.ci.default b/examples/build_system/cmakev2/features/multi_config/sdkconfig.ci.default new file mode 100644 index 00000000000..61749c2cf95 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_config/sdkconfig.ci.default @@ -0,0 +1 @@ +# Default (development) build configuration — settings come from sdkconfig.defaults. diff --git a/examples/build_system/cmakev2/features/multi_config/sdkconfig.ci.prod1 b/examples/build_system/cmakev2/features/multi_config/sdkconfig.ci.prod1 new file mode 100644 index 00000000000..e6e1325d96c --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_config/sdkconfig.ci.prod1 @@ -0,0 +1,2 @@ +CONFIG_EXAMPLE_PRODUCT_NAME="Blinky Smart Light" +CONFIG_EXAMPLE_FUNC_IMPL_PROD=y diff --git a/examples/build_system/cmakev2/features/multi_config/sdkconfig.ci.prod2 b/examples/build_system/cmakev2/features/multi_config/sdkconfig.ci.prod2 new file mode 100644 index 00000000000..afd2be8d618 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_config/sdkconfig.ci.prod2 @@ -0,0 +1,2 @@ +CONFIG_EXAMPLE_PRODUCT_NAME="Blinky Smart Switch" +CONFIG_EXAMPLE_FUNC_IMPL_PROD=y diff --git a/examples/build_system/cmakev2/features/multi_config/sdkconfig.defaults.prod1 b/examples/build_system/cmakev2/features/multi_config/sdkconfig.defaults.prod1 index c21593e5628..519983ac369 100644 --- a/examples/build_system/cmakev2/features/multi_config/sdkconfig.defaults.prod1 +++ b/examples/build_system/cmakev2/features/multi_config/sdkconfig.defaults.prod1 @@ -1,2 +1,3 @@ # This build configuration is for a specific product: CONFIG_EXAMPLE_PRODUCT_NAME="Blinky Smart Light" +CONFIG_EXAMPLE_FUNC_IMPL_PROD=y diff --git a/examples/build_system/cmakev2/features/multi_config/sdkconfig.defaults.prod2 b/examples/build_system/cmakev2/features/multi_config/sdkconfig.defaults.prod2 index 91d982e9309..99b1f1b8d4e 100644 --- a/examples/build_system/cmakev2/features/multi_config/sdkconfig.defaults.prod2 +++ b/examples/build_system/cmakev2/features/multi_config/sdkconfig.defaults.prod2 @@ -1,2 +1,3 @@ # This build configuration is for a specific product: CONFIG_EXAMPLE_PRODUCT_NAME="Blinky Smart Switch" +CONFIG_EXAMPLE_FUNC_IMPL_PROD=y diff --git a/examples/build_system/cmakev2/features/multi_config/sdkconfig.defaults.prod_common b/examples/build_system/cmakev2/features/multi_config/sdkconfig.defaults.prod_common new file mode 100644 index 00000000000..c785749fcd6 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_config/sdkconfig.defaults.prod_common @@ -0,0 +1,10 @@ +# Common settings for production builds. +# For product-specific settings, see sdkconfig.defaults.prod1 and sdkconfig.defaults.prod2. + +# This selects 'func_prod.c' file to be compiled and linked, see CMakeLists.txt. +CONFIG_EXAMPLE_FUNC_IMPL_PROD=y + +# Optimize the binary size of a production build, and remove all log messages. +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y +CONFIG_LOG_DEFAULT_LEVEL_NONE=y diff --git a/examples/build_system/cmakev2/features/plugins/CMakeLists.txt b/examples/build_system/cmakev2/features/plugins/CMakeLists.txt index 655c2d75c26..1df4be14c18 100644 --- a/examples/build_system/cmakev2/features/plugins/CMakeLists.txt +++ b/examples/build_system/cmakev2/features/plugins/CMakeLists.txt @@ -28,7 +28,8 @@ idf_check_binary_size(${CMAKE_PROJECT_NAME}_binary) # Create app target add_custom_target(app ALL DEPENDS ${CMAKE_PROJECT_NAME}_binary) -# Generate metadata +# Generate flasher_args.json and metadata +idf_build_generate_flasher_args() idf_build_generate_metadata(BINARY ${CMAKE_PROJECT_NAME}_binary) # Create other utility targets diff --git a/examples/build_system/cmakev2/features/plugins/pytest_cmakev2_plugins.py b/examples/build_system/cmakev2/features/plugins/pytest_cmakev2_plugins.py new file mode 100644 index 00000000000..a90749e23da --- /dev/null +++ b/examples/build_system/cmakev2/features/plugins/pytest_cmakev2_plugins.py @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import textwrap + +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.generic +@idf_parametrize('target', ['esp32', 'esp32c3'], indirect=['target']) +def test_cmakev2_plugins(dut: Dut) -> None: + log_text = textwrap.dedent(r""" + Hello plugin performing self-registration... + Successfully registered plugin 'Hello' + Nihao plugin performing self-registration... + Successfully registered plugin 'Nihao' + main_task: Calling app_main() + List of plugins: + - Plugin 'Hello' + - Plugin 'Nihao' + Calling greet function of plugin 'Hello'... + Hello, World! + Done with greet function of plugin 'Hello'. + Calling greet function of plugin 'Nihao'... + 你好 World! + Done with greet function of plugin 'Nihao'. + """).strip('\n') + + for line in log_text.split('\n'): + dut.expect_exact(line.encode('utf-8')) diff --git a/examples/build_system/cmakev2/get-started/hello_world/pytest_cmakev2_hello_world.py b/examples/build_system/cmakev2/get-started/hello_world/pytest_cmakev2_hello_world.py new file mode 100644 index 00000000000..a967e9432c3 --- /dev/null +++ b/examples/build_system/cmakev2/get-started/hello_world/pytest_cmakev2_hello_world.py @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded_idf.dut import IdfDut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.generic +@idf_parametrize('target', ['supported_targets'], indirect=['target']) +def test_cmakev2_hello_world(dut: IdfDut) -> None: + dut.expect_exact('Hello world!')