mirror of
https://github.com/espressif/esp-matter.git
synced 2026-04-27 19:13:13 +00:00
CI: Add pytest in CI test
This commit is contained in:
+168
-71
@@ -1,10 +1,12 @@
|
|||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
|
- target_test
|
||||||
- docs
|
- docs
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
ESP_MATTER_PATH: "$CI_PROJECT_DIR"
|
ESP_MATTER_PATH: "$CI_PROJECT_DIR"
|
||||||
IDF_PATH: "$CI_PROJECT_DIR/esp-idf"
|
IDF_PATH: "$CI_PROJECT_DIR/esp-idf"
|
||||||
|
BR_PATH: "$CI_PROJECT_DIR/esp-thread-br"
|
||||||
IDF_GITHUB_ASSETS: "dl.espressif.com/github_assets"
|
IDF_GITHUB_ASSETS: "dl.espressif.com/github_assets"
|
||||||
GIT_STRATEGY: fetch
|
GIT_STRATEGY: fetch
|
||||||
GIT_SUBMODULE_STRATEGY: none
|
GIT_SUBMODULE_STRATEGY: none
|
||||||
@@ -65,12 +67,38 @@ variables:
|
|||||||
- pip install python-gitlab
|
- pip install python-gitlab
|
||||||
- python tools/ci/ci_fetch_submodule.py
|
- python tools/ci/ci_fetch_submodule.py
|
||||||
|
|
||||||
|
.setup_ot_br: &setup_ot_br
|
||||||
|
- cd ${CI_PROJECT_DIR}
|
||||||
|
- git clone ssh://git@gitlab.espressif.cn:27227/espressif/esp-thread-br.git
|
||||||
|
- cd ${BR_PATH}/examples/basic_thread_border_router
|
||||||
|
- idf.py set-target esp32s3
|
||||||
|
- idf.py build
|
||||||
|
|
||||||
|
.setup_ot_rcp: &setup_ot_rcp
|
||||||
|
- cd ${IDF_PATH}
|
||||||
|
- ./install.sh
|
||||||
|
- . ./export.sh
|
||||||
|
# fetch submodules
|
||||||
|
- export PYTHONPATH=${IDF_PATH}/tools/ci/python_packages/:${PYTHONPATH}
|
||||||
|
- pip install python-gitlab
|
||||||
|
- python tools/ci/ci_fetch_submodule.py
|
||||||
|
- cd examples/openthread/ot_rcp
|
||||||
|
- idf.py set-target esp32h2
|
||||||
|
- idf.py build
|
||||||
|
|
||||||
.setup_matter: &setup_matter
|
.setup_matter: &setup_matter
|
||||||
- cd ${CI_PROJECT_DIR}
|
- cd ${CI_PROJECT_DIR}
|
||||||
# Setting up Python environment still spend a pretty long time (15mins -> 5mins).
|
# Setting up Python environment still spend a pretty long time (15mins -> 5mins).
|
||||||
- ./install.sh
|
- ./install.sh
|
||||||
- . ./export.sh
|
- . ./export.sh
|
||||||
|
|
||||||
|
.build_chip_tool: &build_chip_tool
|
||||||
|
- cd ${ESP_MATTER_PATH}/connectedhomeip/connectedhomeip
|
||||||
|
- source scripts/activate.sh
|
||||||
|
- cd ${ESP_MATTER_PATH}/connectedhomeip/connectedhomeip/examples/chip-tool
|
||||||
|
- gn gen out
|
||||||
|
- ninja -C out
|
||||||
|
|
||||||
.build_matter_examples: &build_matter_examples
|
.build_matter_examples: &build_matter_examples
|
||||||
- export MATTER_EXAMPLES_PATH=$ESP_MATTER_PATH/connectedhomeip/connectedhomeip/examples
|
- export MATTER_EXAMPLES_PATH=$ESP_MATTER_PATH/connectedhomeip/connectedhomeip/examples
|
||||||
- cd $MATTER_EXAMPLES_PATH/all-clusters-app/esp32
|
- cd $MATTER_EXAMPLES_PATH/all-clusters-app/esp32
|
||||||
@@ -84,32 +112,6 @@ variables:
|
|||||||
- cd $MATTER_EXAMPLES_PATH/persistent-storage/esp32
|
- cd $MATTER_EXAMPLES_PATH/persistent-storage/esp32
|
||||||
- idf.py build
|
- idf.py build
|
||||||
|
|
||||||
.build_examples: &build_examples
|
|
||||||
- cd $ESP_MATTER_PATH/examples/blemesh_bridge
|
|
||||||
- idf.py set-target esp32
|
|
||||||
- idf.py build
|
|
||||||
- idf.py set-target esp32c3
|
|
||||||
- idf.py build
|
|
||||||
- cd $ESP_MATTER_PATH/examples/zap_light
|
|
||||||
- idf.py set-target esp32
|
|
||||||
- idf.py build
|
|
||||||
- cd $ESP_MATTER_PATH/examples/light_switch
|
|
||||||
- idf.py set-target esp32
|
|
||||||
- idf.py build
|
|
||||||
- cd $ESP_MATTER_PATH/examples/light
|
|
||||||
- idf.py set-target esp32
|
|
||||||
- idf.py build
|
|
||||||
- idf.py set-target esp32c3
|
|
||||||
- idf.py build
|
|
||||||
- cd $ESP_MATTER_PATH/examples/controller
|
|
||||||
- idf.py set-target esp32
|
|
||||||
- idf.py build
|
|
||||||
- cd $ESP_MATTER_PATH/examples/esp-now_bridge_light
|
|
||||||
- idf.py set-target esp32
|
|
||||||
- idf.py build
|
|
||||||
- idf.py set-target esp32c3
|
|
||||||
- idf.py build
|
|
||||||
|
|
||||||
.build_external_platform_example: &build_external_platform_example
|
.build_external_platform_example: &build_external_platform_example
|
||||||
- rm -rf $ESP_MATTER_PATH/../platform
|
- rm -rf $ESP_MATTER_PATH/../platform
|
||||||
- mkdir $ESP_MATTER_PATH/../platform
|
- mkdir $ESP_MATTER_PATH/../platform
|
||||||
@@ -122,32 +124,6 @@ variables:
|
|||||||
- idf.py build
|
- idf.py build
|
||||||
- cp sdkconfig.defaults.backup sdkconfig.defaults
|
- cp sdkconfig.defaults.backup sdkconfig.defaults
|
||||||
|
|
||||||
.build_examples_idf_v5_1: &build_examples_idf_v5_1
|
|
||||||
- cd $ESP_MATTER_PATH/examples/zap_light
|
|
||||||
- idf.py --preview set-target esp32h2
|
|
||||||
- idf.py build
|
|
||||||
- idf.py set-target esp32c2
|
|
||||||
- idf.py build
|
|
||||||
- cd $ESP_MATTER_PATH/examples/light_switch
|
|
||||||
- idf.py --preview set-target esp32h2
|
|
||||||
- idf.py build
|
|
||||||
- idf.py --preview set-target esp32c6
|
|
||||||
- idf.py build
|
|
||||||
- idf.py set-target esp32c2
|
|
||||||
- idf.py build
|
|
||||||
- cd $ESP_MATTER_PATH/examples/light
|
|
||||||
- idf.py --preview set-target esp32h2
|
|
||||||
- idf.py build
|
|
||||||
- idf.py --preview set-target esp32c6
|
|
||||||
- idf.py build
|
|
||||||
- idf.py set-target esp32c2
|
|
||||||
- idf.py build
|
|
||||||
- idf.py set-target esp32c3
|
|
||||||
- idf.py build
|
|
||||||
- cd $ESP_MATTER_PATH/examples/zigbee_bridge
|
|
||||||
- idf.py set-target esp32
|
|
||||||
- idf.py build
|
|
||||||
|
|
||||||
.build_examples_template:
|
.build_examples_template:
|
||||||
stage: build
|
stage: build
|
||||||
image: gitlab.espressif.cn:5050/app-frameworks/esp-matter/build-env:latest
|
image: gitlab.espressif.cn:5050/app-frameworks/esp-matter/build-env:latest
|
||||||
@@ -171,28 +147,149 @@ variables:
|
|||||||
REPOS_PATH: "$CI_PROJECT_DIR/repos"
|
REPOS_PATH: "$CI_PROJECT_DIR/repos"
|
||||||
IDF_CCACHE_ENABLE: 1
|
IDF_CCACHE_ENABLE: 1
|
||||||
|
|
||||||
build_esp_matter_examples:
|
build_esp_matter_examples_pytest_C6_idf_v5_1:
|
||||||
extends:
|
|
||||||
- .build_examples_template
|
|
||||||
script:
|
|
||||||
- *build_examples
|
|
||||||
- *build_external_platform_example
|
|
||||||
|
|
||||||
build_esp_matter_examples_idf_v4_4:
|
|
||||||
extends:
|
|
||||||
- .build_examples_template
|
|
||||||
variables:
|
|
||||||
IDF_VERSION: "v4.4.3"
|
|
||||||
script:
|
|
||||||
- *build_examples
|
|
||||||
|
|
||||||
build_esp_matter_examples_idf_v5_1:
|
|
||||||
extends:
|
extends:
|
||||||
- .build_examples_template
|
- .build_examples_template
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- "examples/**/build*/size.json"
|
||||||
|
- "examples/**/build*/build_log.txt"
|
||||||
|
- "examples/**/build*/*.bin"
|
||||||
|
- "examples/**/build*/flasher_args.json"
|
||||||
|
- "examples/**/build*/config/sdkconfig.json"
|
||||||
|
- "examples/**/build*/bootloader/*.bin"
|
||||||
|
- "examples/**/build*/partition_table/*.bin"
|
||||||
|
- "connectedhomeip/connectedhomeip/examples/chip-tool/out/chip-tool"
|
||||||
|
when: always
|
||||||
|
expire_in: 4 days
|
||||||
variables:
|
variables:
|
||||||
IDF_VERSION: "ea5e0ff298e6257b31d8e0c81435e6d3937f04c7"
|
IDF_VERSION: "ea5e0ff298e6257b31d8e0c81435e6d3937f04c7"
|
||||||
script:
|
script:
|
||||||
- *build_examples_idf_v5_1
|
- cd ${ESP_MATTER_PATH}
|
||||||
|
- pip install -r tools/ci/requirements-build.txt
|
||||||
|
- python tools/ci/build_apps.py ./examples --pytest_c6
|
||||||
|
- *build_chip_tool
|
||||||
|
after_script:
|
||||||
|
- find . -name "*.bin"
|
||||||
|
|
||||||
|
build_esp_matter_examples_pytest_H2_idf_v5_1:
|
||||||
|
extends:
|
||||||
|
- .build_examples_template
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- "examples/**/build*/size.json"
|
||||||
|
- "examples/**/build*/build_log.txt"
|
||||||
|
- "examples/**/build*/*.bin"
|
||||||
|
- "examples/**/build*/flasher_args.json"
|
||||||
|
- "examples/**/build*/config/sdkconfig.json"
|
||||||
|
- "examples/**/build*/bootloader/*.bin"
|
||||||
|
- "examples/**/build*/partition_table/*.bin"
|
||||||
|
- "${IDF_PATH}/examples/openthread/ot_rcp/build/*.bin"
|
||||||
|
- "${IDF_PATH}/examples/openthread/ot_rcp/build/partition_table/*.bin"
|
||||||
|
- "${IDF_PATH}/examples/openthread/ot_rcp/build/bootloader/*.bin"
|
||||||
|
- "${IDF_PATH}/examples/openthread/ot_rcp/build/config/sdkconfig.json"
|
||||||
|
- "${IDF_PATH}/examples/openthread/ot_rcp/build/flasher_args.json"
|
||||||
|
- "${BR_PATH}/examples/basic_thread_border_router/build/*.bin"
|
||||||
|
- "${BR_PATH}/examples/basic_thread_border_router/build/partition_table/*.bin"
|
||||||
|
- "${BR_PATH}/examples/basic_thread_border_router/build/bootloader/*.bin"
|
||||||
|
- "${BR_PATH}/examples/basic_thread_border_router/build/config/sdkconfig.json"
|
||||||
|
- "${BR_PATH}/examples/basic_thread_border_router/build/flasher_args.json"
|
||||||
|
- "connectedhomeip/connectedhomeip/examples/chip-tool/out/chip-tool"
|
||||||
|
when: always
|
||||||
|
expire_in: 4 days
|
||||||
|
variables:
|
||||||
|
IDF_VERSION: "ea5e0ff298e6257b31d8e0c81435e6d3937f04c7"
|
||||||
|
script:
|
||||||
|
- *setup_ot_rcp
|
||||||
|
- *setup_ot_br
|
||||||
|
- cd ${ESP_MATTER_PATH}
|
||||||
|
- pip install -r tools/ci/requirements-build.txt
|
||||||
|
- python tools/ci/build_apps.py ./examples --pytest_h2
|
||||||
|
- *build_chip_tool
|
||||||
|
after_script:
|
||||||
|
- find . -name "*.bin"
|
||||||
|
|
||||||
|
build_esp_matter_examples_non_pytest_idf_v5_1:
|
||||||
|
extends:
|
||||||
|
- .build_examples_template
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- "examples/**/build*/size.json"
|
||||||
|
- "examples/**/build*/build_log.txt"
|
||||||
|
- "examples/**/build*/*.bin"
|
||||||
|
- "examples/**/build*/flasher_args.json"
|
||||||
|
- "examples/**/build*/config/sdkconfig.json"
|
||||||
|
- "examples/**/build*/bootloader/*.bin"
|
||||||
|
- "examples/**/build*/partition_table/*.bin"
|
||||||
|
when: always
|
||||||
|
expire_in: 4 days
|
||||||
|
variables:
|
||||||
|
IDF_VERSION: "ea5e0ff298e6257b31d8e0c81435e6d3937f04c7"
|
||||||
|
script:
|
||||||
|
- *build_external_platform_example
|
||||||
|
- cd ${ESP_MATTER_PATH}
|
||||||
|
- pip install -r tools/ci/requirements-build.txt
|
||||||
|
- python tools/ci/build_apps.py ./examples --no_pytest
|
||||||
|
after_script:
|
||||||
|
- find . -name "*.bin"
|
||||||
|
|
||||||
|
build_esp_matter_examples_pytest_C3_idf_v4_4:
|
||||||
|
extends:
|
||||||
|
- .build_examples_template
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- "examples/**/build*/size.json"
|
||||||
|
- "examples/**/build*/build_log.txt"
|
||||||
|
- "examples/**/build*/*.bin"
|
||||||
|
- "examples/**/build*/flasher_args.json"
|
||||||
|
- "examples/**/build*/config/sdkconfig.json"
|
||||||
|
- "examples/**/build*/bootloader/*.bin"
|
||||||
|
- "examples/**/build*/partition_table/*.bin"
|
||||||
|
- "connectedhomeip/connectedhomeip/examples/chip-tool/out/chip-tool"
|
||||||
|
when: always
|
||||||
|
expire_in: 4 days
|
||||||
|
variables:
|
||||||
|
IDF_VERSION: "v4.4.3"
|
||||||
|
script:
|
||||||
|
- cd ${ESP_MATTER_PATH}
|
||||||
|
- pip install -r tools/ci/requirements-build.txt
|
||||||
|
- python tools/ci/build_apps.py ./examples --pytest_c3
|
||||||
|
- *build_chip_tool
|
||||||
|
after_script:
|
||||||
|
- find . -name "*.bin"
|
||||||
|
|
||||||
|
pytest_esp32c3_esp_matter_dut:
|
||||||
|
stage: target_test
|
||||||
|
image: ${TARGET_TEST_ENV}
|
||||||
|
needs:
|
||||||
|
- build_esp_matter_examples_pytest_C3_idf_v4_4
|
||||||
|
script:
|
||||||
|
- cd ${ESP_MATTER_PATH}
|
||||||
|
- pip install -r tools/ci/requirements-pytest.txt
|
||||||
|
- pytest examples/ --target esp32c3 -m esp_matter_dut --junitxml=XUNIT_RESULT.xml
|
||||||
|
tags: ["esp32c3", "esp_matter_dut"]
|
||||||
|
|
||||||
|
pytest_esp32c6_esp_matter_dut:
|
||||||
|
stage: target_test
|
||||||
|
image: ${TARGET_TEST_ENV}
|
||||||
|
needs:
|
||||||
|
- build_esp_matter_examples_pytest_C6_idf_v5_1
|
||||||
|
script:
|
||||||
|
- cd ${ESP_MATTER_PATH}
|
||||||
|
- pip install -r tools/ci/requirements-pytest.txt
|
||||||
|
- pytest examples/ --target esp32c6 -m esp_matter_dut --junitxml=XUNIT_RESULT.xml
|
||||||
|
tags: ["esp32c6", "esp_matter_dut"]
|
||||||
|
|
||||||
|
pytest_esp32h2_esp_matter_dut:
|
||||||
|
stage: target_test
|
||||||
|
image: ${TARGET_TEST_ENV}
|
||||||
|
needs:
|
||||||
|
- build_esp_matter_examples_pytest_H2_idf_v5_1
|
||||||
|
script:
|
||||||
|
- cd ${ESP_MATTER_PATH}
|
||||||
|
- pip install -r tools/ci/requirements-pytest.txt
|
||||||
|
- pytest examples/ --target esp32h2 -m esp_matter_dut --junitxml=XUNIT_RESULT.xml
|
||||||
|
tags: ["esp32h2", "esp_matter_dut"]
|
||||||
|
|
||||||
build_upstream_examples:
|
build_upstream_examples:
|
||||||
extends:
|
extends:
|
||||||
@@ -203,7 +300,7 @@ build_upstream_examples:
|
|||||||
|
|
||||||
build_docs:
|
build_docs:
|
||||||
stage: build
|
stage: build
|
||||||
image: $CI_DOCKER_REGISTRY/esp-idf-doc-env-v5.0:2-3
|
image: $CI_DOCKER_REGISTRY/esp-idf-doc-env-v5.1:1-1
|
||||||
tags:
|
tags:
|
||||||
- build
|
- build
|
||||||
variables:
|
variables:
|
||||||
@@ -221,7 +318,7 @@ build_docs:
|
|||||||
|
|
||||||
.deploy_docs_template:
|
.deploy_docs_template:
|
||||||
stage: docs
|
stage: docs
|
||||||
image: $CI_DOCKER_REGISTRY/esp-idf-doc-env-v5.0:2-3
|
image: $CI_DOCKER_REGISTRY/esp-idf-doc-env-v5.1:1-1
|
||||||
tags:
|
tags:
|
||||||
- docs
|
- docs
|
||||||
needs:
|
needs:
|
||||||
|
|||||||
+269
@@ -0,0 +1,269 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
# pylint: disable=W0621 # redefined-outer-name
|
||||||
|
|
||||||
|
# This file is a pytest root configuration file and provide the following functionalities:
|
||||||
|
# 1. Defines a few fixtures that could be used under the whole project.
|
||||||
|
# 2. Defines a few hook functions.
|
||||||
|
#
|
||||||
|
# IDF is using [pytest](https://github.com/pytest-dev/pytest) and
|
||||||
|
# [pytest-embedded plugin](https://github.com/espressif/pytest-embedded) as its example test framework.
|
||||||
|
#
|
||||||
|
# This is an experimental feature, and if you found any bug or have any question, please report to
|
||||||
|
# https://github.com/espressif/pytest-embedded/issues
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Callable, List, Optional, Tuple
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from _pytest.config import Config, ExitCode
|
||||||
|
from _pytest.fixtures import FixtureRequest
|
||||||
|
from _pytest.main import Session
|
||||||
|
from _pytest.nodes import Item
|
||||||
|
from _pytest.python import Function
|
||||||
|
from _pytest.reports import TestReport
|
||||||
|
from _pytest.runner import CallInfo
|
||||||
|
from _pytest.terminal import TerminalReporter
|
||||||
|
from pytest_embedded.plugin import multi_dut_argument, multi_dut_fixture
|
||||||
|
from pytest_embedded.utils import find_by_suffix
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_SDKCONFIG = 'default'
|
||||||
|
|
||||||
|
|
||||||
|
##################
|
||||||
|
# Help Functions #
|
||||||
|
##################
|
||||||
|
def format_case_id(target: Optional[str], config: Optional[str], case: str) -> str:
|
||||||
|
return f'{target}.{config}.{case}'
|
||||||
|
|
||||||
|
|
||||||
|
def item_marker_names(item: Item) -> List[str]:
|
||||||
|
return [marker.name for marker in item.iter_markers()]
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# Fixtures #
|
||||||
|
############
|
||||||
|
@pytest.fixture(scope='session', autouse=True)
|
||||||
|
def session_tempdir() -> str:
|
||||||
|
_tmpdir = os.path.join(
|
||||||
|
os.path.dirname(__file__),
|
||||||
|
'pytest_embedded_log',
|
||||||
|
datetime.now().strftime('%Y-%m-%d_%H-%M-%S'),
|
||||||
|
)
|
||||||
|
os.makedirs(_tmpdir, exist_ok=True)
|
||||||
|
return _tmpdir
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
@multi_dut_argument
|
||||||
|
def config(request: FixtureRequest) -> str:
|
||||||
|
return getattr(request, 'param', None) or DEFAULT_SDKCONFIG
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def test_func_name(request: FixtureRequest) -> str:
|
||||||
|
return request.node.function.__name__ # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def test_case_name(request: FixtureRequest, target: str, config: str) -> str:
|
||||||
|
return format_case_id(target, config, request.node.originalname)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
@multi_dut_fixture
|
||||||
|
def build_dir(app_path: str, target: Optional[str], config: Optional[str]) -> str:
|
||||||
|
"""
|
||||||
|
Check local build dir with the following priority:
|
||||||
|
|
||||||
|
1. build_<target>_<config>
|
||||||
|
2. build_<target>
|
||||||
|
3. build_<config>
|
||||||
|
4. build
|
||||||
|
|
||||||
|
Args:
|
||||||
|
app_path: app path
|
||||||
|
target: target
|
||||||
|
config: config
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
valid build directory
|
||||||
|
"""
|
||||||
|
|
||||||
|
check_dirs = []
|
||||||
|
if target is not None and config is not None:
|
||||||
|
check_dirs.append(f'build_{target}_{config}')
|
||||||
|
if target is not None:
|
||||||
|
check_dirs.append(f'build_{target}')
|
||||||
|
if config is not None:
|
||||||
|
check_dirs.append(f'build_{config}')
|
||||||
|
check_dirs.append('build')
|
||||||
|
|
||||||
|
for check_dir in check_dirs:
|
||||||
|
binary_path = os.path.join(app_path, check_dir)
|
||||||
|
if os.path.isdir(binary_path):
|
||||||
|
logging.info(f'find valid binary path: {binary_path}')
|
||||||
|
return check_dir
|
||||||
|
|
||||||
|
logging.warning(
|
||||||
|
'checking binary path: %s... missing... try another place', binary_path
|
||||||
|
)
|
||||||
|
|
||||||
|
recommend_place = check_dirs[0]
|
||||||
|
raise ValueError(
|
||||||
|
f'no build dir valid. Please build the binary via "idf.py -B {recommend_place} build" and run pytest again'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
@multi_dut_fixture
|
||||||
|
def junit_properties(
|
||||||
|
test_case_name: str, record_xml_attribute: Callable[[str, object], None]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
This fixture is autoused and will modify the junit report test case name to <target>.<config>.<case_name>
|
||||||
|
"""
|
||||||
|
record_xml_attribute('name', test_case_name)
|
||||||
|
|
||||||
|
|
||||||
|
##################
|
||||||
|
# Hook functions #
|
||||||
|
##################
|
||||||
|
_idf_pytest_embedded_key = pytest.StashKey['IdfPytestEmbedded']
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_configure(config: Config) -> None:
|
||||||
|
# cli option "--target"
|
||||||
|
target = config.getoption('target') or ''
|
||||||
|
|
||||||
|
help_commands = ['--help', '--fixtures', '--markers', '--version']
|
||||||
|
for cmd in help_commands:
|
||||||
|
if cmd in config.invocation_params.args:
|
||||||
|
target = 'unneeded'
|
||||||
|
break
|
||||||
|
|
||||||
|
assert target, "Must specify target by --target"
|
||||||
|
|
||||||
|
config.stash[_idf_pytest_embedded_key] = IdfPytestEmbedded(
|
||||||
|
target=target,
|
||||||
|
)
|
||||||
|
config.pluginmanager.register(config.stash[_idf_pytest_embedded_key])
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_unconfigure(config: Config) -> None:
|
||||||
|
_pytest_embedded = config.stash.get(_idf_pytest_embedded_key, None)
|
||||||
|
if _pytest_embedded:
|
||||||
|
del config.stash[_idf_pytest_embedded_key]
|
||||||
|
config.pluginmanager.unregister(_pytest_embedded)
|
||||||
|
|
||||||
|
|
||||||
|
class IdfPytestEmbedded:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
target: Optional[str] = None,
|
||||||
|
):
|
||||||
|
# CLI options to filter the test cases
|
||||||
|
self.target = target
|
||||||
|
self._failed_cases: List[
|
||||||
|
Tuple[str, bool, bool]
|
||||||
|
] = [] # (test_case_name, is_known_failure_cases, is_xfail)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def failed_cases(self) -> List[str]:
|
||||||
|
return [
|
||||||
|
case
|
||||||
|
for case, is_xfail in self._failed_cases
|
||||||
|
if not is_xfail
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def xfail_cases(self) -> List[str]:
|
||||||
|
return [case for case, is_xfail in self._failed_cases if is_xfail]
|
||||||
|
|
||||||
|
@pytest.hookimpl(tryfirst=True)
|
||||||
|
def pytest_sessionstart(self, session: Session) -> None:
|
||||||
|
if self.target:
|
||||||
|
self.target = self.target.lower()
|
||||||
|
session.config.option.target = self.target
|
||||||
|
|
||||||
|
@pytest.hookimpl(tryfirst=True)
|
||||||
|
def pytest_collection_modifyitems(self, items: List[Function]) -> None:
|
||||||
|
# sort by file path and callspec.config
|
||||||
|
# implement like this since this is a limitation of pytest, couldn't get fixture values while collecting
|
||||||
|
# https://github.com/pytest-dev/pytest/discussions/9689
|
||||||
|
def _get_param_config(_item: Function) -> str:
|
||||||
|
if hasattr(_item, 'callspec'):
|
||||||
|
return _item.callspec.params.get('config', DEFAULT_SDKCONFIG) # type: ignore
|
||||||
|
return DEFAULT_SDKCONFIG
|
||||||
|
|
||||||
|
items.sort(key=lambda x: (os.path.dirname(x.path), _get_param_config(x)))
|
||||||
|
|
||||||
|
# set default timeout 10 minutes for each case
|
||||||
|
for item in items:
|
||||||
|
if 'timeout' not in item.keywords:
|
||||||
|
item.add_marker(pytest.mark.timeout(10 * 60))
|
||||||
|
|
||||||
|
# filter all the test cases with "--target"
|
||||||
|
if self.target:
|
||||||
|
items[:] = [
|
||||||
|
item for item in items if self.target in item_marker_names(item)
|
||||||
|
]
|
||||||
|
|
||||||
|
def pytest_runtest_makereport(
|
||||||
|
self, item: Function, call: CallInfo[None]
|
||||||
|
) -> Optional[TestReport]:
|
||||||
|
report = TestReport.from_item_and_call(item, call)
|
||||||
|
if report.outcome == 'failed':
|
||||||
|
test_case_name = item.funcargs.get('test_case_name', '')
|
||||||
|
is_xfail = report.keywords.get('xfail', False)
|
||||||
|
self._failed_cases.append((test_case_name, is_xfail))
|
||||||
|
|
||||||
|
return report
|
||||||
|
|
||||||
|
@pytest.hookimpl(trylast=True)
|
||||||
|
def pytest_runtest_teardown(self, item: Function) -> None:
|
||||||
|
"""
|
||||||
|
Format the test case generated junit reports
|
||||||
|
"""
|
||||||
|
tempdir = item.funcargs.get('test_case_tempdir')
|
||||||
|
if not tempdir:
|
||||||
|
return
|
||||||
|
|
||||||
|
junits = find_by_suffix('.xml', tempdir)
|
||||||
|
if not junits:
|
||||||
|
return
|
||||||
|
|
||||||
|
target = item.funcargs['target']
|
||||||
|
config = item.funcargs['config']
|
||||||
|
for junit in junits:
|
||||||
|
xml = ET.parse(junit)
|
||||||
|
testcases = xml.findall('.//testcase')
|
||||||
|
for case in testcases:
|
||||||
|
case.attrib['name'] = format_case_id(
|
||||||
|
target, config, case.attrib['name']
|
||||||
|
)
|
||||||
|
if 'file' in case.attrib:
|
||||||
|
case.attrib['file'] = case.attrib['file'].replace(
|
||||||
|
'/IDF/', ''
|
||||||
|
) # our unity test framework
|
||||||
|
xml.write(junit)
|
||||||
|
|
||||||
|
def pytest_sessionfinish(self, session: Session, exitstatus: int) -> None:
|
||||||
|
if exitstatus != 0:
|
||||||
|
if exitstatus == ExitCode.NO_TESTS_COLLECTED:
|
||||||
|
session.exitstatus = 0
|
||||||
|
|
||||||
|
def pytest_terminal_summary(self, terminalreporter: TerminalReporter) -> None:
|
||||||
|
if self.xfail_cases:
|
||||||
|
terminalreporter.section('xfail cases', bold=True, yellow=True)
|
||||||
|
terminalreporter.line('\n'.join(self.xfail_cases))
|
||||||
|
|
||||||
|
if self.failed_cases:
|
||||||
|
terminalreporter.section('Failed cases', bold=True, red=True)
|
||||||
|
terminalreporter.line('\n'.join(self.failed_cases))
|
||||||
@@ -5,7 +5,7 @@ endif()
|
|||||||
|
|
||||||
SET(device_type esp32c2_devkit_m)
|
SET(device_type esp32c2_devkit_m)
|
||||||
SET(led_type gpio)
|
SET(led_type gpio)
|
||||||
SET(button_type hollow_button)
|
SET(button_type iot_button)
|
||||||
|
|
||||||
SET(extra_components_dirs_append "$ENV{ESP_MATTER_DEVICE_PATH}/../../led_driver"
|
SET(extra_components_dirs_append "$ENV{ESP_MATTER_DEVICE_PATH}/../../led_driver"
|
||||||
"$ENV{ESP_MATTER_DEVICE_PATH}/../../button_driver/button")
|
"$ENV{ESP_MATTER_DEVICE_PATH}/../../button_driver/iot_button")
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
|
||||||
|
|
||||||
|
examples/blemesh_bridge:
|
||||||
|
enable:
|
||||||
|
- if: IDF_TARGET in ["esp32", "esp32c3"]
|
||||||
|
temporary: true
|
||||||
|
reason: the other targets are not tested yet
|
||||||
|
|
||||||
|
examples/zap_light:
|
||||||
|
enable:
|
||||||
|
- if: IDF_TARGET in ["esp32", "esp32h2"]
|
||||||
|
temporary: true
|
||||||
|
reason: the other targets are not tested yet
|
||||||
|
|
||||||
|
examples/light_switch:
|
||||||
|
enable:
|
||||||
|
- if: IDF_TARGET in ["esp32", "esp32c6", "esp32c2", "esp32h2"]
|
||||||
|
temporary: true
|
||||||
|
reason: the other targets are not tested yet
|
||||||
|
|
||||||
|
examples/light:
|
||||||
|
enable:
|
||||||
|
- if: IDF_TARGET in ["esp32", "esp32c3", "esp32c2", "esp32c6", "esp32h2"]
|
||||||
|
temporary: true
|
||||||
|
reason: the other targets are not tested yet
|
||||||
|
|
||||||
|
examples/generic_switch:
|
||||||
|
enable:
|
||||||
|
- if: IDF_TARGET in ["esp32c2", "esp32c6", "esp32h2"]
|
||||||
|
temporary: true
|
||||||
|
reason: the other targets are not tested yet
|
||||||
|
|
||||||
|
examples/esp-now_bridge_light:
|
||||||
|
enable:
|
||||||
|
- if: IDF_TARGET in ["esp32c3"]
|
||||||
|
temporary: true
|
||||||
|
reason: the other targets are not tested yet
|
||||||
|
|
||||||
|
examples/controller:
|
||||||
|
enable:
|
||||||
|
- if: IDF_TARGET in ["esp32"]
|
||||||
|
temporary: true
|
||||||
|
reason: the other targets are not tested yet
|
||||||
|
|
||||||
|
examples/zigbee_bridge:
|
||||||
|
enable:
|
||||||
|
- if: IDF_TARGET in ["esp32"]
|
||||||
|
temporary: true
|
||||||
|
reason: the other targets are not tested yet
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
import pytest
|
||||||
|
import time
|
||||||
|
import re
|
||||||
|
import pexpect
|
||||||
|
import subprocess
|
||||||
|
import netifaces
|
||||||
|
from typing import Tuple
|
||||||
|
from pytest_embedded import Dut
|
||||||
|
|
||||||
|
CURRENT_DIR_LIGHT = str(pathlib.Path(__file__).parent)+'/light'
|
||||||
|
CHIP_TOOL_DIR = str(pathlib.Path(__file__).parent)+'/../connectedhomeip/connectedhomeip/examples/chip-tool'
|
||||||
|
OT_BR_EXAMPLE_PATH = str(pathlib.Path(__file__).parent)+'/../esp-thread-br/examples/basic_thread_border_router'
|
||||||
|
pytest_build_dir = CURRENT_DIR_LIGHT
|
||||||
|
pytest_matter_thread_dir = CURRENT_DIR_LIGHT+'|'+OT_BR_EXAMPLE_PATH
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.esp32c3
|
||||||
|
@pytest.mark.esp_matter_dut
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
' count, app_path, target, erase_all', [
|
||||||
|
( 1, pytest_build_dir, 'esp32c3', 'y'),
|
||||||
|
],
|
||||||
|
indirect=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Matter over wifi commissioning
|
||||||
|
def test_matter_commissioning_c3(dut:Dut) -> None:
|
||||||
|
light = dut
|
||||||
|
# BLE start advertising
|
||||||
|
light.expect(r'chip\[DL\]\: Configuring CHIPoBLE advertising', timeout=20)
|
||||||
|
# Start commissioning
|
||||||
|
time.sleep(5)
|
||||||
|
command = CHIP_TOOL_DIR+'/out/chip-tool pairing ble-wifi 1 ChipTEH2 chiptest123 20202021 3840'
|
||||||
|
out_str = subprocess.getoutput(command)
|
||||||
|
print(out_str)
|
||||||
|
result = re.findall(r'Run command failure', str(out_str))
|
||||||
|
if len(result) != 0:
|
||||||
|
assert False
|
||||||
|
# Use toggle command to turn-off the light
|
||||||
|
time.sleep(3)
|
||||||
|
command = CHIP_TOOL_DIR+'/out/chip-tool onoff toggle 1 1'
|
||||||
|
out_str = subprocess.getoutput(command)
|
||||||
|
print(out_str)
|
||||||
|
result = re.findall(r'Run command failure', str(out_str))
|
||||||
|
if len(result) != 0:
|
||||||
|
assert False
|
||||||
|
# Use toggle command to turn-on the light
|
||||||
|
time.sleep(5)
|
||||||
|
command = CHIP_TOOL_DIR+'/out/chip-tool onoff toggle 1 1'
|
||||||
|
out_str = subprocess.getoutput(command)
|
||||||
|
print(out_str)
|
||||||
|
result = re.findall(r'Run command failure', str(out_str))
|
||||||
|
if len(result) != 0:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
@pytest.mark.esp32c6
|
||||||
|
@pytest.mark.esp_matter_dut
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
' count, app_path, target, erase_all', [
|
||||||
|
( 1, pytest_build_dir, 'esp32c6', 'y'),
|
||||||
|
],
|
||||||
|
indirect=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Matter over wifi commissioning
|
||||||
|
def test_matter_commissioning_c6(dut:Dut) -> None:
|
||||||
|
light = dut
|
||||||
|
# BLE start advertising
|
||||||
|
light.expect(r'chip\[DL\]\: Configuring CHIPoBLE advertising', timeout=20)
|
||||||
|
# Start commissioning
|
||||||
|
time.sleep(5)
|
||||||
|
command = CHIP_TOOL_DIR+'/out/chip-tool pairing ble-wifi 1 ChipTEH2 chiptest123 20202021 3840'
|
||||||
|
out_str = subprocess.getoutput(command)
|
||||||
|
print(out_str)
|
||||||
|
result = re.findall(r'Run command failure', str(out_str))
|
||||||
|
if len(result) != 0:
|
||||||
|
assert False
|
||||||
|
# Use toggle command to turn-off the light
|
||||||
|
time.sleep(3)
|
||||||
|
command = CHIP_TOOL_DIR+'/out/chip-tool onoff toggle 1 1'
|
||||||
|
out_str = subprocess.getoutput(command)
|
||||||
|
print(out_str)
|
||||||
|
result = re.findall(r'Run command failure', str(out_str))
|
||||||
|
if len(result) != 0:
|
||||||
|
assert False
|
||||||
|
# Use toggle command to turn-on the light
|
||||||
|
time.sleep(5)
|
||||||
|
command = CHIP_TOOL_DIR+'/out/chip-tool onoff toggle 1 1'
|
||||||
|
out_str = subprocess.getoutput(command)
|
||||||
|
print(out_str)
|
||||||
|
result = re.findall(r'Run command failure', str(out_str))
|
||||||
|
if len(result) != 0:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
|
||||||
|
# get the host interface name
|
||||||
|
def get_host_interface_name() -> str:
|
||||||
|
interfaces = netifaces.interfaces()
|
||||||
|
interface_name = [s for s in interfaces if 'wl' in s][0]
|
||||||
|
return str(interface_name)
|
||||||
|
|
||||||
|
|
||||||
|
# reset host interface
|
||||||
|
def reset_host_interface() -> None:
|
||||||
|
interface_name = get_host_interface_name()
|
||||||
|
flag = False
|
||||||
|
try:
|
||||||
|
command = 'ifconfig ' + interface_name + ' down'
|
||||||
|
subprocess.call(command, shell=True, timeout=5)
|
||||||
|
time.sleep(1)
|
||||||
|
command = 'ifconfig ' + interface_name + ' up'
|
||||||
|
subprocess.call(command, shell=True, timeout=10)
|
||||||
|
time.sleep(1)
|
||||||
|
flag = True
|
||||||
|
finally:
|
||||||
|
time.sleep(1)
|
||||||
|
assert flag
|
||||||
|
|
||||||
|
|
||||||
|
# set interface sysctl options
|
||||||
|
def set_interface_sysctl_options() -> None:
|
||||||
|
interface_name = get_host_interface_name()
|
||||||
|
flag = False
|
||||||
|
try:
|
||||||
|
command = 'sysctl -w net/ipv6/conf/' + interface_name + '/accept_ra=2'
|
||||||
|
subprocess.call(command, shell=True, timeout=5)
|
||||||
|
time.sleep(1)
|
||||||
|
command = 'sysctl -w net/ipv6/conf/' + interface_name + '/accept_ra_rt_info_max_plen=128'
|
||||||
|
subprocess.call(command, shell=True, timeout=5)
|
||||||
|
time.sleep(1)
|
||||||
|
command = 'sysctl -w net.ipv6.conf.all.forwarding=1'
|
||||||
|
subprocess.call(command, shell=True, timeout=5)
|
||||||
|
time.sleep(1)
|
||||||
|
flag = True
|
||||||
|
finally:
|
||||||
|
time.sleep(2)
|
||||||
|
assert flag
|
||||||
|
|
||||||
|
|
||||||
|
# initialize interface ipv6 address
|
||||||
|
def init_interface_ipv6_address() -> None:
|
||||||
|
interface_name = get_host_interface_name()
|
||||||
|
flag = False
|
||||||
|
try:
|
||||||
|
command = 'ip -6 route | grep ' + interface_name + " | grep ra | awk {'print $1'} | xargs -I {} ip -6 route del {}"
|
||||||
|
subprocess.call(command, shell=True, timeout=5)
|
||||||
|
time.sleep(0.5)
|
||||||
|
subprocess.call(command, shell=True, timeout=5)
|
||||||
|
time.sleep(1)
|
||||||
|
command = 'ip -6 address show dev ' + interface_name + \
|
||||||
|
" scope global | grep 'inet6' | awk {'print $2'} | xargs -I {} ip -6 addr del {} dev " + interface_name
|
||||||
|
subprocess.call(command, shell=True, timeout=5)
|
||||||
|
time.sleep(1)
|
||||||
|
flag = True
|
||||||
|
finally:
|
||||||
|
time.sleep(1)
|
||||||
|
assert flag
|
||||||
|
|
||||||
|
|
||||||
|
def fixture_Init_interface() -> bool:
|
||||||
|
print('Init interface')
|
||||||
|
init_interface_ipv6_address()
|
||||||
|
reset_host_interface()
|
||||||
|
time.sleep(30)
|
||||||
|
set_interface_sysctl_options()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.esp32h2
|
||||||
|
@pytest.mark.esp32s3
|
||||||
|
@pytest.mark.esp_matter_dut
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'count, app_path, target, erase_all', [
|
||||||
|
( 2, pytest_matter_thread_dir, 'esp32h2|esp32s3', 'y|n'),
|
||||||
|
],
|
||||||
|
indirect=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Matter over thread commissioning
|
||||||
|
def test_matter_commissioning_h2(dut:Tuple[Dut, Dut]) -> None:
|
||||||
|
ot_br = dut[1]
|
||||||
|
light = dut[0]
|
||||||
|
# For matter over thread commissioning need to reset host interface
|
||||||
|
fixture_Init_interface()
|
||||||
|
# BLE start advertising
|
||||||
|
light.expect(r'chip\[DL\]\: Configuring CHIPoBLE advertising', timeout=20)
|
||||||
|
# flash ot_br
|
||||||
|
ot_br.expect('OpenThread attached to netif', timeout=30)
|
||||||
|
time.sleep(2)
|
||||||
|
ot_br.write('factoryreset')
|
||||||
|
ot_br.expect('OpenThread attached to netif', timeout=30)
|
||||||
|
time.sleep(2)
|
||||||
|
ot_br.write('log level 3')
|
||||||
|
ot_br.expect('Done', timeout=5)
|
||||||
|
time.sleep(2)
|
||||||
|
# wifi connect -s ChipTEH2 -p chiptest123
|
||||||
|
ot_br.write('wifi connect -s ChipTEH2 -p chiptest123')
|
||||||
|
ot_br.expect('wifi sta is connected successfully', timeout=5)
|
||||||
|
time.sleep(2)
|
||||||
|
# start an ot network
|
||||||
|
ot_br.write('ifconfig up')
|
||||||
|
ot_br.expect('netif up', timeout=5)
|
||||||
|
time.sleep(2)
|
||||||
|
ot_br.write('thread start')
|
||||||
|
ot_br.expect('Role detached -> leader', timeout=20)
|
||||||
|
time.sleep(2)
|
||||||
|
# get dataset
|
||||||
|
ot_br.write('dataset active -x')
|
||||||
|
dataset=ot_br.expect(r'\n(\w{202}\r)', timeout=5)[1].decode()
|
||||||
|
print(dataset)
|
||||||
|
# Start commissioning
|
||||||
|
time.sleep(2)
|
||||||
|
command = CHIP_TOOL_DIR+"/out/chip-tool pairing ble-thread 1 hex:{} ".format(dataset.strip())+"20202021 3840"
|
||||||
|
out_str = subprocess.getoutput(command)
|
||||||
|
print(out_str)
|
||||||
|
result = re.findall(r'Run command failure', str(out_str))
|
||||||
|
if len(result) != 0:
|
||||||
|
assert False
|
||||||
|
# Use toggle command to turn-off the light
|
||||||
|
time.sleep(2)
|
||||||
|
command = CHIP_TOOL_DIR+'/out/chip-tool onoff toggle 1 1'
|
||||||
|
out_str = subprocess.getoutput(command)
|
||||||
|
print(out_str)
|
||||||
|
result = re.findall(r'Run command failure', str(out_str))
|
||||||
|
if len(result) != 0:
|
||||||
|
assert False
|
||||||
|
# Use toggle command to turn-on the light
|
||||||
|
time.sleep(2)
|
||||||
|
command = CHIP_TOOL_DIR+'/out/chip-tool onoff toggle 1 1'
|
||||||
|
out_str = subprocess.getoutput(command)
|
||||||
|
print(out_str)
|
||||||
|
result = re.findall(r'Run command failure', str(out_str))
|
||||||
|
if len(result) != 0:
|
||||||
|
assert False
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
[pytest]
|
||||||
|
|
||||||
|
python_files = pytest_*.py
|
||||||
|
|
||||||
|
# ignore PytestExperimentalApiWarning for record_xml_attribute
|
||||||
|
# set traceback to "short" to prevent the overwhelming tracebacks
|
||||||
|
addopts =
|
||||||
|
-s
|
||||||
|
--embedded-services esp,idf
|
||||||
|
--tb short
|
||||||
|
--strict-markers
|
||||||
|
--skip-check-coredump y
|
||||||
|
--logfile-extension ".txt"
|
||||||
|
|
||||||
|
# ignore DeprecationWarning
|
||||||
|
filterwarnings =
|
||||||
|
ignore::_pytest.warning_types.PytestExperimentalApiWarning
|
||||||
|
|
||||||
|
markers =
|
||||||
|
# target markers
|
||||||
|
esp32c3: support esp32c3 target
|
||||||
|
esp32c6: support esp32c6 target
|
||||||
|
esp32h2: support esp32h2 target
|
||||||
|
esp32s3: support esp32s3 target
|
||||||
|
# env markers
|
||||||
|
esp_matter_dut: esp matter runner which have single dut
|
||||||
|
|
||||||
|
# log related
|
||||||
|
log_cli = True
|
||||||
|
log_cli_level = INFO
|
||||||
|
log_cli_format = %(asctime)s %(levelname)s %(message)s
|
||||||
|
log_cli_date_format = %Y-%m-%d %H:%M:%S
|
||||||
|
|
||||||
|
# junit related
|
||||||
|
junit_family = xunit1
|
||||||
|
|
||||||
|
## log all to `system-out` when case fail
|
||||||
|
junit_logging = stdout
|
||||||
|
junit_log_passing_tests = False
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
# This file is used in CI generate binary files for different kinds of apps
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from idf_build_apps import LOGGER, App, build_apps, find_apps, setup_logging
|
||||||
|
|
||||||
|
# from idf_ci_utils import IDF_PATH, get_pytest_app_paths, get_pytest_cases, get_ttfw_app_paths
|
||||||
|
|
||||||
|
PROJECT_ROOT = Path(__file__).parent.parent.parent.absolute()
|
||||||
|
DEF_APP_PATH = PROJECT_ROOT / 'examples'
|
||||||
|
APPS_BUILD_PER_JOB = 30
|
||||||
|
PYTEST_C6_APPS = [
|
||||||
|
{"target": "esp32c6", "name": "light"},
|
||||||
|
]
|
||||||
|
MAINFEST_FILES = [
|
||||||
|
str(PROJECT_ROOT / 'examples' / '.build-rules.yml'),
|
||||||
|
]
|
||||||
|
|
||||||
|
PYTEST_H2_APPS = [
|
||||||
|
{"target": "esp32h2", "name": "light"},
|
||||||
|
]
|
||||||
|
MAINFEST_FILES = [
|
||||||
|
str(PROJECT_ROOT / 'examples' / '.build-rules.yml'),
|
||||||
|
]
|
||||||
|
|
||||||
|
PYTEST_C3_APPS = [
|
||||||
|
{"target": "esp32c3", "name": "light"},
|
||||||
|
]
|
||||||
|
MAINFEST_FILES = [
|
||||||
|
str(PROJECT_ROOT / 'examples' / '.build-rules.yml'),
|
||||||
|
]
|
||||||
|
|
||||||
|
def _is_c6_pytest_app(app: App) -> bool:
|
||||||
|
print(app.name , app.target)
|
||||||
|
for pytest_app in PYTEST_C6_APPS:
|
||||||
|
print(pytest_app["name"] , pytest_app["target"])
|
||||||
|
if app.name == pytest_app["name"] and app.target == pytest_app["target"]:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _is_h2_pytest_app(app: App) -> bool:
|
||||||
|
for pytest_app in PYTEST_H2_APPS:
|
||||||
|
if app.name == pytest_app["name"] and app.target == pytest_app["target"]:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _is_c3_pytest_app(app: App) -> bool:
|
||||||
|
for pytest_app in PYTEST_C3_APPS:
|
||||||
|
if app.name == pytest_app["name"] and app.target == pytest_app["target"]:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_cmake_apps(
|
||||||
|
paths: List[str],
|
||||||
|
target: str,
|
||||||
|
config_rules_str: List[str],
|
||||||
|
) -> List[App]:
|
||||||
|
apps = find_apps(
|
||||||
|
paths,
|
||||||
|
recursive=True,
|
||||||
|
target=target,
|
||||||
|
build_dir='build_@t_@w',
|
||||||
|
config_rules_str=config_rules_str,
|
||||||
|
build_log_path='build_log.txt',
|
||||||
|
size_json_path='size.json',
|
||||||
|
check_warnings=False,
|
||||||
|
preserve=True,
|
||||||
|
manifest_files=MAINFEST_FILES,
|
||||||
|
)
|
||||||
|
return apps
|
||||||
|
|
||||||
|
|
||||||
|
def main(args: argparse.Namespace) -> None:
|
||||||
|
apps = get_cmake_apps(args.paths, args.target, args.config)
|
||||||
|
|
||||||
|
# no_pytest and only_pytest can not be both True
|
||||||
|
assert not (args.no_pytest and args.pytest_c6 and args.pytest_h2 and args.pytest_c3)
|
||||||
|
if args.no_pytest:
|
||||||
|
apps_for_build = [app for app in apps if not (_is_c6_pytest_app(app) or _is_h2_pytest_app(app))]
|
||||||
|
elif args.pytest_c6:
|
||||||
|
apps_for_build = [app for app in apps if _is_c6_pytest_app(app)]
|
||||||
|
elif args.pytest_h2:
|
||||||
|
apps_for_build = [app for app in apps if _is_h2_pytest_app(app)]
|
||||||
|
elif args.pytest_c3:
|
||||||
|
apps_for_build = [app for app in apps if _is_c3_pytest_app(app)]
|
||||||
|
else:
|
||||||
|
apps_for_build = apps[:]
|
||||||
|
|
||||||
|
LOGGER.info('Found %d apps after filtering', len(apps_for_build))
|
||||||
|
LOGGER.info(
|
||||||
|
'Suggest setting the parallel count to %d for this build job',
|
||||||
|
len(apps_for_build) // APPS_BUILD_PER_JOB + 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
ret_code = build_apps(
|
||||||
|
apps_for_build,
|
||||||
|
parallel_count=args.parallel_count,
|
||||||
|
parallel_index=args.parallel_index,
|
||||||
|
dry_run=False,
|
||||||
|
collect_size_info=args.collect_size_info,
|
||||||
|
# build_verbose=0,
|
||||||
|
keep_going=True,
|
||||||
|
ignore_warning_strs=[r".*"],
|
||||||
|
copy_sdkconfig=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
sys.exit(ret_code)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Build all the apps for different test types. Will auto remove those non-test apps binaries',
|
||||||
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||||
|
)
|
||||||
|
parser.add_argument('paths', nargs='*', help='Paths to the apps to build.')
|
||||||
|
parser.add_argument(
|
||||||
|
'-t',
|
||||||
|
'--target',
|
||||||
|
default='all',
|
||||||
|
help='Build apps for given target. could pass "all" to get apps for all targets',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--config',
|
||||||
|
default=['sdkconfig.ci=default', 'sdkconfig.ci.*=', '=default'],
|
||||||
|
action='append',
|
||||||
|
help='Adds configurations (sdkconfig file names) to build. This can either be '
|
||||||
|
'FILENAME[=NAME] or FILEPATTERN. FILENAME is the name of the sdkconfig file, '
|
||||||
|
'relative to the project directory, to be used. Optional NAME can be specified, '
|
||||||
|
'which can be used as a name of this configuration. FILEPATTERN is the name of '
|
||||||
|
'the sdkconfig file, relative to the project directory, with at most one wildcard. '
|
||||||
|
'The part captured by the wildcard is used as the name of the configuration.',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--parallel-count', default=1, type=int, help='Number of parallel build jobs.'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--parallel-index',
|
||||||
|
default=1,
|
||||||
|
type=int,
|
||||||
|
help='Index (1-based) of the job, out of the number specified by --parallel-count.',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--no_pytest',
|
||||||
|
action="store_true",
|
||||||
|
help='Exclude pytest apps, definded in PYTEST_H2_APPS and PYTEST_C6_APPS',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--pytest_c6',
|
||||||
|
action="store_true",
|
||||||
|
help='Only build pytest apps, definded in PYTEST_C6_APPS',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--pytest_h2',
|
||||||
|
action="store_true",
|
||||||
|
help='Only build pytest apps, definded in PYTEST_H2_APPS',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--pytest_c3',
|
||||||
|
action="store_true",
|
||||||
|
help='Only build pytest apps, definded in PYTEST_C3_APPS',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--collect-size-info',
|
||||||
|
type=argparse.FileType('w'),
|
||||||
|
help='If specified, the test case name and size info json will be written to this file',
|
||||||
|
)
|
||||||
|
|
||||||
|
arguments = parser.parse_args()
|
||||||
|
if not arguments.paths:
|
||||||
|
arguments.paths = [DEF_APP_PATH]
|
||||||
|
setup_logging(verbose=1) # Info
|
||||||
|
main(arguments)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
idf_build_apps
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
pytest-embedded-serial-esp~=1.0
|
||||||
|
pytest-embedded-idf~=1.0
|
||||||
|
pytest-embedded-qemu~=1.0
|
||||||
|
pytest-timeout
|
||||||
|
netifaces
|
||||||
|
esptool>=4.5
|
||||||
Reference in New Issue
Block a user