Skip to content

Commit d7ada30

Browse files
authored
Merge pull request #564 from espressif/ci/parallel_runner
ci(runner): Process runner in parallel
2 parents d0e3b07 + cb8e577 commit d7ada30

File tree

9 files changed

+139
-101
lines changed

9 files changed

+139
-101
lines changed

.github/workflows/build-run-applications.yml

Lines changed: 18 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ jobs:
7171
- idf_ver: "release-v5.4"
7272
parallel_count: 2
7373
parallel_index: 2
74+
- idf_ver: "release-v5.5"
75+
parallel_count: 2
76+
parallel_index: 1
77+
- idf_ver: "release-v5.5"
78+
parallel_count: 2
79+
parallel_index: 2
7480
runs-on: ubuntu-latest
7581
container: espressif/idf:${{ matrix.idf_ver }}
7682
steps:
@@ -121,67 +127,15 @@ jobs:
121127
idf_ver:
122128
- "latest"
123129
runner:
124-
- runs-on: "esp-box-3"
125-
marker: "esp_box_3"
126-
target: "esp32s3"
127-
- runs-on: "esp32_c3_lcdkit"
128-
marker: "esp32_c3_lcdkit"
129-
target: "esp32c3"
130-
- runs-on: "esp32_p4_box"
131-
marker: "esp32_p4_box"
132-
target: "esp32p4"
133-
- runs-on: "esp32_p4_function_ev_board"
134-
marker: "esp32_p4_function_ev_board"
135-
target: "esp32p4"
136-
- runs-on: "esp32_s3_eye"
137-
marker: "esp32_s3_eye"
138-
target: "esp32s3"
139-
- runs-on: "esp32_s3_lcd_ev_board"
140-
marker: "esp32_s3_lcd_ev_board"
141-
target: "esp32s3"
142-
- runs-on: "esp32_s3_lcd_ev_board"
143-
marker: "esp32_s3_lcd_ev_board_2"
144-
target: "esp32s3"
145-
- runs-on: "esp32_s3_usb_otg"
146-
marker: "esp32_s3_usb_otg"
147-
target: "esp32s3"
148-
- runs-on: "esp_wrover_kit"
149-
marker: "esp_wrover_kit"
150-
target: "esp32"
151-
- runs-on: "m5dial"
152-
marker: "m5dial"
153-
target: "esp32s3"
154-
- runs-on: "m5stack_core"
155-
marker: "m5stack_core"
156-
target: "esp32"
157-
- runs-on: "m5stack_core_2"
158-
marker: "m5stack_core_2"
159-
target: "esp32"
160-
- runs-on: "m5stack_core_s3"
161-
marker: "m5stack_core_s3"
162-
target: "esp32s3"
163-
- runs-on: "m5stack_core_s3"
164-
marker: "m5stack_core_s3_se"
165-
target: "esp32s3"
166-
- runs-on: "esp32_azure_iot_kit"
167-
marker: "esp32_azure_iot_kit"
168-
target: "esp32"
169-
- runs-on: "esp_bsp_devkit"
170-
marker: "esp_bsp_devkit"
171-
target: "esp32s3"
172-
- runs-on: "esp_bsp_generic"
173-
marker: "esp_bsp_generic"
174-
target: "esp32s3"
175-
- runs-on: "esp32_s3_korvo_2"
176-
marker: "esp32_s3_korvo_2"
177-
target: "esp32s3"
178-
- runs-on: "m5_atom_s3"
179-
marker: "m5_atom_s3"
180-
target: "esp32s3"
130+
- example: "test_example_display"
131+
- example: "test_example_lvgl_demos"
132+
- example: "test_example_display_sensors"
133+
- example: "test_example_generic_button_led"
134+
- example: "test_example_lvgl_benchmark"
181135
env:
182-
TEST_RESULT_NAME: test_results_${{ matrix.runner.target }}_${{ matrix.runner.marker }}_${{ matrix.idf_ver }}
183-
BENCHMARK_RESULT_NAME: benchmark_${{ matrix.runner.target }}_${{ matrix.runner.marker }}_${{ matrix.idf_ver }}
184-
TEST_RESULT_FILE: test_results_${{ matrix.runner.target }}_${{ matrix.runner.marker }}_${{ matrix.idf_ver }}.xml
136+
TEST_RESULT_NAME: test_results_${{ matrix.runner.example }}_${{ matrix.idf_ver }}
137+
BENCHMARK_RESULT_NAME: benchmark_${{ matrix.runner.example }}_${{ matrix.idf_ver }}
138+
TEST_RESULT_FILE: test_results_${{ matrix.runner.example }}_${{ matrix.idf_ver }}.xml
185139
PYTEST_BENCHMARK_IGNORE: ${{ (contains(github.event.pull_request.labels.*.name, 'Run benchmark') || contains(inputs.WFType, 'Build + Tests + Benchmark') || github.ref_name == 'master') && format(' ') || format('--ignore=examples/display_lvgl_benchmark') }}
186140
runs-on: [self-hosted, Linux, bspwall]
187141
container:
@@ -197,15 +151,17 @@ jobs:
197151
env:
198152
PIP_EXTRA_INDEX_URL: "https://dl.espressif.com/pypi/"
199153
run: |
200-
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code
154+
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code pytest-xdist
201155
- name: Download latest results
202156
uses: actions/download-artifact@v4
203157
with:
204158
pattern: benchmark_*
205159
path: benchmark/
206160
- name: Run apps
207161
run: |
208-
pytest --suppress-no-test-exit-code --ignore-glob '*/managed_components/*' --ignore=.github --junit-xml=${{ env.TEST_RESULT_FILE }} --target=${{ matrix.runner.target }} -m ${{ matrix.runner.marker }} --build-dir=build_${{ matrix.runner.runs-on }} ${{ env.PYTEST_BENCHMARK_IGNORE }}
162+
export PYTEST_EMBEDDED_CACHE_DIR=/tmp/pytest-embedded-cache-dummy
163+
mkdir -p /tmp/pytest-embedded-cache-dummy
164+
pytest --suppress-no-test-exit-code --ignore-glob '*/managed_components/*' --ignore=.github --junit-xml=${{ env.TEST_RESULT_FILE }} -k ${{ matrix.runner.example }} -n auto ${{ env.PYTEST_BENCHMARK_IGNORE }}
209165
- name: Upload test results
210166
uses: actions/upload-artifact@v4
211167
if: always()

conftest.py

Lines changed: 113 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,123 @@
1-
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
1+
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
22
# SPDX-License-Identifier: Apache-2.0
3+
import os
4+
import uuid
35
import pytest
46

57

68
def pytest_generate_tests(metafunc):
7-
# If test expects param 'port' or 'flash_port', add parametrize
9+
allowed_ids = set()
10+
11+
# get markers from test case (e.g. 'esp_box_3')
12+
for marker in metafunc.definition.iter_markers():
13+
allowed_ids.add(marker.name)
14+
15+
all_params = [
16+
pytest.param('/dev/boards/esp-box-3',
17+
'/dev/boards/esp-box-3',
18+
'build_esp-box-3',
19+
id='esp_box_3'),
20+
pytest.param('/dev/boards/esp32_c3_lcdkit',
21+
'/dev/boards/esp32_c3_lcdkit',
22+
'build_esp32_c3_lcdkit',
23+
id='esp32_c3_lcdkit'),
24+
pytest.param('/dev/boards/esp32_p4_box',
25+
'/dev/boards/esp32_p4_box',
26+
'build_esp32_p4_box',
27+
id='esp32_p4_box'),
28+
pytest.param('/dev/boards/esp32_p4_function_ev_board',
29+
'/dev/boards/esp32_p4_function_ev_board',
30+
'build_esp32_p4_function_ev_board',
31+
id='esp32_p4_function_ev_board'),
32+
pytest.param('/dev/boards/esp32_s3_eye',
33+
'/dev/boards/esp32_s3_eye',
34+
'build_esp32_s3_eye',
35+
id='esp32_s3_eye'),
36+
pytest.param('/dev/boards/esp32_s3_lcd_ev_board',
37+
'/dev/boards/esp32_s3_lcd_ev_board',
38+
'build_esp32_s3_lcd_ev_board',
39+
id='esp32_s3_lcd_ev_board'),
40+
pytest.param('/dev/boards/esp32_s3_lcd_ev_board-2',
41+
'/dev/boards/esp32_s3_lcd_ev_board-2',
42+
'build_esp32_s3_lcd_ev_board',
43+
id='esp32_s3_lcd_ev_board_2'),
44+
pytest.param('/dev/boards/esp32_s3_usb_otg',
45+
'/dev/boards/esp32_s3_usb_otg',
46+
'build_esp32_s3_usb_otg',
47+
id='esp32_s3_usb_otg'),
48+
pytest.param('/dev/boards/esp_wrover_kit',
49+
'/dev/boards/esp_wrover_kit',
50+
'build_esp_wrover_kit',
51+
id='esp_wrover_kit'),
52+
pytest.param('/dev/boards/esp32_azure_iot_kit',
53+
'/dev/boards/esp32_azure_iot_kit',
54+
'build_esp32_azure_iot_kit',
55+
id='esp32_azure_iot_kit'),
56+
pytest.param('/dev/boards/m5dial',
57+
'/dev/boards/m5dial',
58+
'build_m5dial',
59+
id='m5dial'),
60+
pytest.param('/dev/boards/m5stack_core',
61+
'/dev/boards/m5stack_core',
62+
'build_m5stack_core',
63+
id='m5stack_core'),
64+
pytest.param('/dev/boards/m5stack_core_2',
65+
'/dev/boards/m5stack_core_2',
66+
'build_m5stack_core',
67+
id='m5stack_core_2'),
68+
pytest.param('/dev/boards/m5stack_core_s3',
69+
'/dev/boards/m5stack_core_s3',
70+
'build_m5stack_core_s3',
71+
id='m5stack_core_s3'),
72+
pytest.param('/dev/boards/m5stack_core_s3_se',
73+
'/dev/boards/m5stack_core_s3_se',
74+
'build_m5stack_core_s3',
75+
id='m5stack_core_s3_se'),
76+
pytest.param('/dev/boards/m5_atom_s3',
77+
'/dev/boards/m5_atom_s3',
78+
'build_m5_atom_s3',
79+
id='m5_atom_s3'),
80+
pytest.param('/dev/boards/esp32_s3_devkitc_1_1',
81+
'/dev/boards/esp32_s3_devkitc_1_1',
82+
'build_esp_bsp_devkit',
83+
id='esp_bsp_devkit'),
84+
pytest.param('/dev/boards/esp32_s2_devkitc_1',
85+
'/dev/boards/esp32_s2_devkitc_1',
86+
'build_esp_bsp_generic',
87+
id='esp_bsp_generic'),
88+
pytest.param('/dev/boards/esp32_s3_korvo_2',
89+
'/dev/boards/esp32_s3_korvo_2',
90+
'build_esp32_s3_korvo_2',
91+
id='esp32_s3_korvo_2'),
92+
]
93+
94+
# filter by markers
95+
selected_params = [
96+
p for p in all_params if p.id in allowed_ids
97+
]
98+
99+
# if not markers, use all
100+
if not selected_params:
101+
selected_params = all_params
102+
8103
if 'port' in metafunc.fixturenames and 'flash_port' in metafunc.fixturenames:
9104
metafunc.parametrize(
10-
'port, flash_port',
11-
[
12-
pytest.param('/dev/boards/esp-box-3', '/dev/boards/esp-box-3', id='esp_box_3'),
13-
pytest.param('/dev/boards/esp32_c3_lcdkit', '/dev/boards/esp32_c3_lcdkit', id='esp32_c3_lcdkit'),
14-
pytest.param('/dev/boards/esp32_p4_box', '/dev/boards/esp32_p4_box', id='esp32_p4_box'),
15-
pytest.param('/dev/boards/esp32_p4_function_ev_board', '/dev/boards/esp32_p4_function_ev_board', id='esp32_p4_function_ev_board'),
16-
pytest.param('/dev/boards/esp32_s3_eye', '/dev/boards/esp32_s3_eye', id='esp32_s3_eye'),
17-
pytest.param('/dev/boards/esp32_s3_lcd_ev_board', '/dev/boards/esp32_s3_lcd_ev_board', id='esp32_s3_lcd_ev_board'),
18-
pytest.param('/dev/boards/esp32_s3_lcd_ev_board-2', '/dev/boards/esp32_s3_lcd_ev_board-2', id='esp32_s3_lcd_ev_board_2'),
19-
pytest.param('/dev/boards/esp32_s3_usb_otg', '/dev/boards/esp32_s3_usb_otg', id='esp32_s3_usb_otg'),
20-
pytest.param('/dev/boards/esp_wrover_kit', '/dev/boards/esp_wrover_kit', id='esp_wrover_kit'),
21-
pytest.param('/dev/boards/esp32_azure_iot_kit', '/dev/boards/esp32_azure_iot_kit', id='esp32_azure_iot_kit'),
22-
pytest.param('/dev/boards/m5dial', '/dev/boards/m5dial', id='m5dial'),
23-
pytest.param('/dev/boards/m5stack_core', '/dev/boards/m5stack_core', id='m5stack_core'),
24-
pytest.param('/dev/boards/m5stack_core_2', '/dev/boards/m5stack_core_2', id='m5stack_core_2'),
25-
pytest.param('/dev/boards/m5stack_core_s3', '/dev/boards/m5stack_core_s3', id='m5stack_core_s3'),
26-
pytest.param('/dev/boards/m5stack_core_s3_se', '/dev/boards/m5stack_core_s3_se', id='m5stack_core_s3_se'),
27-
pytest.param('/dev/boards/m5_atom_s3', '/dev/boards/m5_atom_s3', id='m5_atom_s3'),
28-
pytest.param('/dev/boards/esp32_s3_devkitc_1_1', '/dev/boards/esp32_s3_devkitc_1_1', id='esp_bsp_devkit'),
29-
pytest.param('/dev/boards/esp32_s3_devkitc_1_1', '/dev/boards/esp32_s3_devkitc_1_1', id='esp_bsp_generic'),
30-
pytest.param('/dev/boards/esp32_s3_korvo_2', '/dev/boards/esp32_s3_korvo_2', id='esp32_s3_korvo_2'),
31-
]
105+
'port, flash_port, build_dir',
106+
selected_params
32107
)
33108

34109

35-
# Check marker with params and test ID
36-
def pytest_collection_modifyitems(config, items):
37-
for item in items:
38-
marker_option = "[" + config.getoption("-m") + "]"
39-
if marker_option not in item.nodeid:
40-
item.add_marker(pytest.mark.skip(reason="Not for selected params"))
110+
@pytest.fixture
111+
def build_dir(build_dir: str) -> str:
112+
return f'{build_dir}'
113+
114+
115+
# This fixing using cache when used "-n auto" (parallel)
116+
def pytest_configure(config):
117+
# If run pytest-xdist (parallel), set unique cache dir
118+
worker_id = os.getenv("PYTEST_XDIST_WORKER", None)
119+
if worker_id:
120+
cache_dir = f"/tmp/pytest-embedded-cache-{uuid.uuid4()}"
121+
os.environ["PYTEST_EMBEDDED_CACHE_DIR"] = cache_dir
122+
os.makedirs(cache_dir, exist_ok=True)
123+
print(f"Using embedded cache dir: {cache_dir}")

examples/display/pytest_display.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@
2020
@pytest.mark.m5stack_core_s3
2121
@pytest.mark.m5stack_core_s3_se
2222
@pytest.mark.m5_atom_s3
23-
def test_display_example(dut: Dut) -> None:
23+
def test_example_display(dut: Dut) -> None:
2424
dut.expect_exact('example: Display LVGL animation')
2525
dut.expect_exact('main_task: Returned from app_main()')

examples/display_lvgl_benchmark/main/main.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ static char *TAG = "app_main";
2424
void app_main(void)
2525
{
2626
/* Initialize display and LVGL */
27-
2827
#if defined(BSP_BOARD_ESP32_S3_LCD_EV_BOARD)
2928
/* Only for esp32_s3_lcd_ev_board */
3029
bsp_display_cfg_t cfg = {

examples/display_lvgl_benchmark/pytest_display_lvgl_benchmark.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def get_test_diff(test1, test2, name, positive):
6262
@pytest.mark.m5dial
6363
@pytest.mark.m5stack_core_s3
6464
@pytest.mark.m5stack_core_s3_se
65-
def test_example(dut: Dut, request) -> None:
65+
def test_example_lvgl_benchmark(dut: Dut, request) -> None:
6666
date = datetime.datetime.now()
6767
board = request.node.callspec.id
6868

examples/display_lvgl_demos/pytest_display_lvgl_demos.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@
1313
@pytest.mark.m5dial
1414
@pytest.mark.m5stack_core_s3
1515
@pytest.mark.m5stack_core_s3_se
16-
def test_display_example(dut: Dut) -> None:
16+
def test_example_lvgl_demos(dut: Dut) -> None:
1717
dut.expect_exact('app_main: Display LVGL demo')
1818
dut.expect_exact('main_task: Returned from app_main()')

examples/display_sensors/pytest_display_display_sensors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66

77

88
@pytest.mark.esp32_azure_iot_kit
9-
def test_display_example(dut: Dut) -> None:
9+
def test_example_display_sensors(dut: Dut) -> None:
1010
dut.expect_exact('main_task: Returned from app_main()')
1111
dut.expect(r'example: temperature: (\d+[.]\d+), humidity: (\d+[.]\d+), luminance: (\d+[.]\d+)')

examples/generic_button_led/pytest_generic_button_led.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77

88
@pytest.mark.esp_bsp_devkit
99
@pytest.mark.esp_bsp_generic
10-
def test_generic_button_led(dut: Dut) -> None:
10+
def test_example_generic_button_led(dut: Dut) -> None:
1111
dut.expect_exact('led_indicator: Indicator create successfully.')
1212
dut.expect_exact('main_task: Returned from app_main()')
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# This file was generated using idf.py save-defconfig. It can be edited manually.
22
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
33
#
4-
CONFIG_IDF_TARGET="esp32s3"
4+
CONFIG_IDF_TARGET="esp32s2"
55
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
66
CONFIG_PARTITION_TABLE_CUSTOM=y
77

8-
# ESP32-S3-DevKitC-1.1 Settings
8+
# ESP32-S2-DevKitC-1 Settings
99
# Buttons
1010
CONFIG_BSP_BUTTONS_NUM=1
1111
CONFIG_BSP_BUTTON_1_TYPE_GPIO=y
@@ -14,5 +14,5 @@ CONFIG_BSP_BUTTON_1_LEVEL=0
1414
# LEDs
1515
CONFIG_BSP_LEDS_NUM=1
1616
CONFIG_BSP_LED_TYPE_RGB=y
17-
CONFIG_BSP_LED_RGB_GPIO=38
17+
CONFIG_BSP_LED_RGB_GPIO=18
1818
CONFIG_BSP_LED_RGB_BACKEND_RMT=y

0 commit comments

Comments
 (0)