mirror of
https://github.com/espressif/esp-idf.git
synced 2024-09-20 00:36:01 -04:00
Merge branch 'ci/known_generate_target_test_issues' into 'master'
ci: add known warnings while generating the target test jobs Closes IDFCI-1941 See merge request espressif/esp-idf!28551
This commit is contained in:
commit
7913c42996
@ -287,6 +287,7 @@ generate_build_child_pipeline:
|
|||||||
dependencies: # set dependencies to null to avoid missing artifacts issue
|
dependencies: # set dependencies to null to avoid missing artifacts issue
|
||||||
needs:
|
needs:
|
||||||
- pipeline_variables
|
- pipeline_variables
|
||||||
|
- check_test_cases_env_markers_and_required_runners
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build_child_pipeline.yml
|
- build_child_pipeline.yml
|
||||||
|
@ -170,3 +170,9 @@ pipeline_variables:
|
|||||||
artifacts:
|
artifacts:
|
||||||
reports:
|
reports:
|
||||||
dotenv: pipeline.env
|
dotenv: pipeline.env
|
||||||
|
|
||||||
|
check_test_cases_env_markers_and_required_runners:
|
||||||
|
extends:
|
||||||
|
- .pre_check_template
|
||||||
|
script:
|
||||||
|
- python tools/ci/dynamic_pipelines/scripts/generate_target_test_child_pipeline.py --check
|
||||||
|
@ -157,13 +157,19 @@ repos:
|
|||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- PyYAML == 5.3.1
|
- PyYAML == 5.3.1
|
||||||
- idf-build-apps~=2.0
|
- idf-build-apps~=2.0
|
||||||
- id: sort-build-test-rules-ymls
|
- id: sort-yaml-files
|
||||||
name: sort .build-test-rules.yml files
|
name: sort yaml files
|
||||||
entry: tools/ci/check_build_test_rules.py sort-yaml
|
entry: tools/ci/sort_yaml.py
|
||||||
language: python
|
language: python
|
||||||
files: '\.build-test-rules\.yml'
|
files: '\.build-test-rules\.yml$|known_generate_test_child_pipeline_warnings\.yml$'
|
||||||
|
additional_dependencies:
|
||||||
|
- ruamel.yaml
|
||||||
|
- id: sort-yaml-test
|
||||||
|
name: sort yaml test
|
||||||
|
entry: python -m unittest tools/ci/sort_yaml.py
|
||||||
|
language: python
|
||||||
|
files: 'tools/ci/sort_yaml\.py$'
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- PyYAML == 5.3.1
|
|
||||||
- ruamel.yaml
|
- ruamel.yaml
|
||||||
- id: check-build-test-rules-path-exists
|
- id: check-build-test-rules-path-exists
|
||||||
name: check path in .build-test-rules.yml exists
|
name: check path in .build-test-rules.yml exists
|
||||||
|
18
conftest.py
18
conftest.py
@ -11,8 +11,6 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import gitlab
|
|
||||||
|
|
||||||
if os.path.join(os.path.dirname(__file__), 'tools', 'ci') not in sys.path:
|
if os.path.join(os.path.dirname(__file__), 'tools', 'ci') not in sys.path:
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'tools', 'ci'))
|
sys.path.append(os.path.join(os.path.dirname(__file__), 'tools', 'ci'))
|
||||||
|
|
||||||
@ -161,22 +159,8 @@ def app_downloader(pipeline_id: t.Optional[str]) -> t.Optional[AppDownloader]:
|
|||||||
|
|
||||||
logging.info('Downloading build report from the build pipeline %s', pipeline_id)
|
logging.info('Downloading build report from the build pipeline %s', pipeline_id)
|
||||||
test_app_presigned_urls_file = None
|
test_app_presigned_urls_file = None
|
||||||
try:
|
|
||||||
gl = gitlab_api.Gitlab(os.getenv('CI_PROJECT_ID', 'espressif/esp-idf'))
|
|
||||||
except gitlab.exceptions.GitlabAuthenticationError:
|
|
||||||
msg = """To download artifacts from gitlab, please create ~/.python-gitlab.cfg with the following content:
|
|
||||||
|
|
||||||
[global]
|
gl = gitlab_api.Gitlab(os.getenv('CI_PROJECT_ID', 'espressif/esp-idf'))
|
||||||
default = internal
|
|
||||||
ssl_verify = true
|
|
||||||
timeout = 5
|
|
||||||
|
|
||||||
[internal]
|
|
||||||
url = <OUR INTERNAL HTTPS SERVER URL>
|
|
||||||
private_token = <YOUR PERSONAL ACCESS TOKEN>
|
|
||||||
api_version = 4
|
|
||||||
"""
|
|
||||||
raise SystemExit(msg)
|
|
||||||
|
|
||||||
for child_pipeline in gl.project.pipelines.get(pipeline_id, lazy=True).bridges.list(iterator=True):
|
for child_pipeline in gl.project.pipelines.get(pipeline_id, lazy=True).bridges.list(iterator=True):
|
||||||
if child_pipeline.name == 'build_child_pipeline':
|
if child_pipeline.name == 'build_child_pipeline':
|
||||||
|
@ -6,7 +6,6 @@ import inspect
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from io import StringIO
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import List
|
from typing import List
|
||||||
@ -345,38 +344,6 @@ def check_test_scripts(
|
|||||||
sys.exit(exit_code)
|
sys.exit(exit_code)
|
||||||
|
|
||||||
|
|
||||||
def sort_yaml(files: List[str]) -> None:
|
|
||||||
from ruamel.yaml import YAML, CommentedMap
|
|
||||||
|
|
||||||
yaml = YAML()
|
|
||||||
yaml.indent(mapping=2, sequence=4, offset=2)
|
|
||||||
yaml.width = 4096 # avoid wrap lines
|
|
||||||
|
|
||||||
exit_code = 0
|
|
||||||
for f in files:
|
|
||||||
with open(f) as fr:
|
|
||||||
file_s = fr.read()
|
|
||||||
fr.seek(0)
|
|
||||||
file_d: CommentedMap = yaml.load(fr)
|
|
||||||
|
|
||||||
sorted_yaml = CommentedMap(dict(sorted(file_d.items())))
|
|
||||||
file_d.copy_attributes(sorted_yaml)
|
|
||||||
|
|
||||||
with StringIO() as s:
|
|
||||||
yaml.dump(sorted_yaml, s)
|
|
||||||
|
|
||||||
string = s.getvalue()
|
|
||||||
if string != file_s:
|
|
||||||
with open(f, 'w') as fw:
|
|
||||||
fw.write(string)
|
|
||||||
print(
|
|
||||||
f'Sorted yaml file {f}. Please take a look. sometimes the format is a bit messy'
|
|
||||||
)
|
|
||||||
exit_code = 1
|
|
||||||
|
|
||||||
sys.exit(exit_code)
|
|
||||||
|
|
||||||
|
|
||||||
def check_exist() -> None:
|
def check_exist() -> None:
|
||||||
exit_code = 0
|
exit_code = 0
|
||||||
|
|
||||||
@ -422,9 +389,6 @@ if __name__ == '__main__':
|
|||||||
help='default build test rules config file',
|
help='default build test rules config file',
|
||||||
)
|
)
|
||||||
|
|
||||||
_sort_yaml = action.add_parser('sort-yaml')
|
|
||||||
_sort_yaml.add_argument('files', nargs='+', help='all specified yaml files')
|
|
||||||
|
|
||||||
_check_exist = action.add_parser('check-exist')
|
_check_exist = action.add_parser('check-exist')
|
||||||
|
|
||||||
arg = parser.parse_args()
|
arg = parser.parse_args()
|
||||||
@ -434,9 +398,7 @@ if __name__ == '__main__':
|
|||||||
os.path.join(os.path.dirname(__file__), '..', '..')
|
os.path.join(os.path.dirname(__file__), '..', '..')
|
||||||
)
|
)
|
||||||
|
|
||||||
if arg.action == 'sort-yaml':
|
if arg.action == 'check-exist':
|
||||||
sort_yaml(arg.files)
|
|
||||||
elif arg.action == 'check-exist':
|
|
||||||
check_exist()
|
check_exist()
|
||||||
else:
|
else:
|
||||||
check_dirs = set()
|
check_dirs = set()
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from idf_ci_utils import IDF_PATH
|
from idf_ci_utils import IDF_PATH
|
||||||
@ -31,3 +30,7 @@ REPORT_TEMPLATE_FILEPATH = os.path.join(
|
|||||||
)
|
)
|
||||||
|
|
||||||
BUILD_ONLY_LABEL = 'For Maintainers: Only Build Tests'
|
BUILD_ONLY_LABEL = 'For Maintainers: Only Build Tests'
|
||||||
|
|
||||||
|
KNOWN_GENERATE_TEST_CHILD_PIPELINE_WARNINGS_FILEPATH = os.path.join(
|
||||||
|
IDF_PATH, 'tools', 'ci', 'dynamic_pipelines', 'templates', 'known_generate_test_child_pipeline_warnings.yml'
|
||||||
|
)
|
||||||
|
@ -1,28 +1,36 @@
|
|||||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
"""This file is used for generating the child pipeline for target test jobs.
|
"""This file is used for generating the child pipeline for target test jobs.
|
||||||
|
|
||||||
1. Check the build jobs' artifacts to get the built apps' information.
|
1. Check the build jobs' artifacts to get the built apps' information.
|
||||||
2. Post the Build Report if it's running in an MR pipeline.
|
2. Post the Build Report if it's running in an MR pipeline.
|
||||||
3. Generate the child pipeline for target test jobs.
|
3. Generate the child pipeline for target test jobs.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import glob
|
import glob
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import typing as t
|
import typing as t
|
||||||
from collections import Counter, defaultdict
|
from collections import Counter
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
import __init__ # noqa: F401 # inject the system path
|
import __init__ # noqa: F401 # inject the system path
|
||||||
from dynamic_pipelines.constants import (BUILD_ONLY_LABEL, DEFAULT_CASES_TEST_PER_JOB,
|
import yaml
|
||||||
DEFAULT_TARGET_TEST_CHILD_PIPELINE_FILEPATH, DEFAULT_TEST_PATHS)
|
from dynamic_pipelines.constants import BUILD_ONLY_LABEL
|
||||||
from dynamic_pipelines.models import EmptyJob, Job, TargetTestJob
|
from dynamic_pipelines.constants import DEFAULT_CASES_TEST_PER_JOB
|
||||||
|
from dynamic_pipelines.constants import DEFAULT_TARGET_TEST_CHILD_PIPELINE_FILEPATH
|
||||||
|
from dynamic_pipelines.constants import DEFAULT_TEST_PATHS
|
||||||
|
from dynamic_pipelines.constants import KNOWN_GENERATE_TEST_CHILD_PIPELINE_WARNINGS_FILEPATH
|
||||||
|
from dynamic_pipelines.models import EmptyJob
|
||||||
|
from dynamic_pipelines.models import Job
|
||||||
|
from dynamic_pipelines.models import TargetTestJob
|
||||||
from dynamic_pipelines.utils import dump_jobs_to_yaml
|
from dynamic_pipelines.utils import dump_jobs_to_yaml
|
||||||
from gitlab.v4.objects import Project
|
from gitlab.v4.objects import Project
|
||||||
from gitlab_api import Gitlab
|
from gitlab_api import Gitlab
|
||||||
from idf_build_apps import App
|
from idf_build_apps import App
|
||||||
|
from idf_build_apps.constants import BuildStatus
|
||||||
from idf_ci.app import import_apps_from_txt
|
from idf_ci.app import import_apps_from_txt
|
||||||
|
from idf_pytest.script import get_all_apps
|
||||||
from idf_pytest.script import get_pytest_cases
|
from idf_pytest.script import get_pytest_cases
|
||||||
|
|
||||||
|
|
||||||
@ -41,16 +49,23 @@ def get_tags_with_amount(s: str) -> t.List[str]:
|
|||||||
return sorted(res)
|
return sorted(res)
|
||||||
|
|
||||||
|
|
||||||
def get_target_test_jobs(project: Project, paths: str, apps: t.List[App]) -> t.Tuple[t.List[Job], t.List[str]]:
|
def get_target_test_jobs(
|
||||||
|
project: Project, paths: str, apps: t.List[App]
|
||||||
|
) -> t.Tuple[t.List[Job], t.List[str], t.Dict[str, t.List[str]]]:
|
||||||
"""
|
"""
|
||||||
Return the target test jobs and the extra yaml files to include
|
Return the target test jobs and the extra yaml files to include
|
||||||
"""
|
"""
|
||||||
|
issues: t.Dict[str, t.List[str]] = {
|
||||||
|
'no_env_marker_test_cases': [],
|
||||||
|
'no_runner_tags': [],
|
||||||
|
}
|
||||||
|
|
||||||
if mr_labels := os.getenv('CI_MERGE_REQUEST_LABELS'):
|
if mr_labels := os.getenv('CI_MERGE_REQUEST_LABELS'):
|
||||||
print(f'MR labels: {mr_labels}')
|
print(f'MR labels: {mr_labels}')
|
||||||
|
|
||||||
if BUILD_ONLY_LABEL in mr_labels.split(','):
|
if BUILD_ONLY_LABEL in mr_labels.split(','):
|
||||||
print('MR has build only label, skip generating target test child pipeline')
|
print('MR has build only label, skip generating target test child pipeline')
|
||||||
return [EmptyJob()], []
|
return [EmptyJob()], [], issues
|
||||||
|
|
||||||
pytest_cases = get_pytest_cases(
|
pytest_cases = get_pytest_cases(
|
||||||
paths,
|
paths,
|
||||||
@ -61,7 +76,7 @@ def get_target_test_jobs(project: Project, paths: str, apps: t.List[App]) -> t.T
|
|||||||
res = defaultdict(list)
|
res = defaultdict(list)
|
||||||
for case in pytest_cases:
|
for case in pytest_cases:
|
||||||
if not case.env_markers:
|
if not case.env_markers:
|
||||||
print(f'No env markers found for {case.item.originalname} in {case.path}. Ignoring...')
|
issues['no_env_marker_test_cases'].append(case.item.nodeid)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
res[(case.target_selector, tuple(sorted(case.env_markers)))].append(case)
|
res[(case.target_selector, tuple(sorted(case.env_markers)))].append(case)
|
||||||
@ -72,9 +87,10 @@ def get_target_test_jobs(project: Project, paths: str, apps: t.List[App]) -> t.T
|
|||||||
# we don't need to get all runner, as long as we get one runner, it's fine
|
# we don't need to get all runner, as long as we get one runner, it's fine
|
||||||
runner_list = project.runners.list(status='online', tag_list=','.join(runner_tags), get_all=False)
|
runner_list = project.runners.list(status='online', tag_list=','.join(runner_tags), get_all=False)
|
||||||
if not runner_list:
|
if not runner_list:
|
||||||
print(f'WARNING: No runner found with tag {",".join(runner_tags)}, ignoring the following test cases:')
|
issues['no_runner_tags'].append(','.join(runner_tags))
|
||||||
|
logging.warning(f'No runner found for {",".join(runner_tags)}, required by cases:')
|
||||||
for case in cases:
|
for case in cases:
|
||||||
print(f' - {case.name}')
|
logging.warning(f' - {case.item.nodeid}')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
target_test_job = TargetTestJob(
|
target_test_job = TargetTestJob(
|
||||||
@ -95,11 +111,48 @@ def get_target_test_jobs(project: Project, paths: str, apps: t.List[App]) -> t.T
|
|||||||
else:
|
else:
|
||||||
extra_include_yml = ['tools/ci/dynamic_pipelines/templates/generate_target_test_report.yml']
|
extra_include_yml = ['tools/ci/dynamic_pipelines/templates/generate_target_test_report.yml']
|
||||||
|
|
||||||
return target_test_jobs, extra_include_yml
|
issues['no_env_marker_test_cases'] = sorted(issues['no_env_marker_test_cases'])
|
||||||
|
issues['no_runner_tags'] = sorted(issues['no_runner_tags'])
|
||||||
|
|
||||||
|
return target_test_jobs, extra_include_yml, issues
|
||||||
|
|
||||||
|
|
||||||
def generate_target_test_child_pipeline(project: Project, paths: str, apps: t.List[App], output_filepath: str) -> None:
|
def generate_target_test_child_pipeline(
|
||||||
target_test_jobs, extra_include_yml = get_target_test_jobs(project, paths, apps)
|
project: Project,
|
||||||
|
paths: str,
|
||||||
|
apps: t.List[App],
|
||||||
|
output_filepath: str,
|
||||||
|
) -> None:
|
||||||
|
target_test_jobs, extra_include_yml, issues = get_target_test_jobs(project, paths, apps)
|
||||||
|
|
||||||
|
with open(KNOWN_GENERATE_TEST_CHILD_PIPELINE_WARNINGS_FILEPATH) as fr:
|
||||||
|
known_warnings_dict = yaml.safe_load(fr) or dict()
|
||||||
|
|
||||||
|
failed = False
|
||||||
|
known_no_env_marker_test_cases = set(known_warnings_dict.get('no_env_marker_test_cases', []))
|
||||||
|
no_env_marker_test_cases = set(issues['no_env_marker_test_cases'])
|
||||||
|
|
||||||
|
if no_env_marker_test_cases - known_no_env_marker_test_cases:
|
||||||
|
print('ERROR: NEW "no_env_marker_test_cases" detected:')
|
||||||
|
for case in no_env_marker_test_cases - known_no_env_marker_test_cases:
|
||||||
|
print(f' - {case}')
|
||||||
|
failed = True
|
||||||
|
|
||||||
|
known_no_runner_tags = set(known_warnings_dict.get('no_runner_tags', []))
|
||||||
|
no_runner_tags = set(issues['no_runner_tags'])
|
||||||
|
|
||||||
|
if no_runner_tags - known_no_runner_tags:
|
||||||
|
print('ERROR: NEW "no_runner_tags" detected:')
|
||||||
|
for tag in no_runner_tags - known_no_runner_tags:
|
||||||
|
print(f' - {tag}')
|
||||||
|
failed = True
|
||||||
|
|
||||||
|
if failed:
|
||||||
|
raise SystemExit(
|
||||||
|
f'Please fix the issue, '
|
||||||
|
f'or update the known warnings file: {KNOWN_GENERATE_TEST_CHILD_PIPELINE_WARNINGS_FILEPATH}'
|
||||||
|
)
|
||||||
|
|
||||||
dump_jobs_to_yaml(target_test_jobs, output_filepath, extra_include_yml)
|
dump_jobs_to_yaml(target_test_jobs, output_filepath, extra_include_yml)
|
||||||
print(f'Generate child pipeline yaml file {output_filepath} with {sum(j.parallel for j in target_test_jobs)} jobs')
|
print(f'Generate child pipeline yaml file {output_filepath} with {sum(j.parallel for j in target_test_jobs)} jobs')
|
||||||
|
|
||||||
@ -134,13 +187,37 @@ if __name__ == '__main__':
|
|||||||
default=DEFAULT_TARGET_TEST_CHILD_PIPELINE_FILEPATH,
|
default=DEFAULT_TARGET_TEST_CHILD_PIPELINE_FILEPATH,
|
||||||
help='Output child pipeline file path',
|
help='Output child pipeline file path',
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--check',
|
||||||
|
action='store_true',
|
||||||
|
help='Check if the child pipeline could be generated successfully. '
|
||||||
|
'test cases without env marker or required unset runner will be printed out. '
|
||||||
|
'(Note: All apps and test cases will be checked)',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--app-info-filepattern',
|
||||||
|
default='list_job_*.txt',
|
||||||
|
help='glob pattern to specify the files that include built app info generated by '
|
||||||
|
'`idf-build-apps --collect-app-info ...`. will not raise ValueError when binary '
|
||||||
|
'paths not exist in local file system if not listed recorded in the app info.',
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
app_list_filepattern = 'list_job_*.txt'
|
|
||||||
apps = []
|
|
||||||
for f in glob.glob(app_list_filepattern):
|
|
||||||
apps.extend(import_apps_from_txt(f))
|
|
||||||
|
|
||||||
gl_project = Gitlab(args.project_id).project
|
gl_project = Gitlab(args.project_id).project
|
||||||
generate_target_test_child_pipeline(gl_project, args.paths, apps, args.output)
|
|
||||||
|
if args.check:
|
||||||
|
apps = list(get_all_apps(args.paths)[0]) # test related apps only
|
||||||
|
for app in apps:
|
||||||
|
app.build_status = BuildStatus.SUCCESS # pretend they are built successfully
|
||||||
|
else:
|
||||||
|
apps = []
|
||||||
|
for f in glob.glob(args.app_info_filepattern):
|
||||||
|
apps.extend(import_apps_from_txt(f))
|
||||||
|
|
||||||
|
generate_target_test_child_pipeline(
|
||||||
|
gl_project,
|
||||||
|
args.paths,
|
||||||
|
apps,
|
||||||
|
args.output,
|
||||||
|
)
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
no_env_marker_test_cases:
|
||||||
|
- components/fatfs/test_apps/flash_ro/pytest_fatfs_flash_ro.py::test_fatfs_flash_ro
|
||||||
|
- components/fatfs/test_apps/flash_wl/pytest_fatfs_flash_wl.py::test_fatfs_flash_wl_generic[default]
|
||||||
|
- components/fatfs/test_apps/flash_wl/pytest_fatfs_flash_wl.py::test_fatfs_flash_wl_generic[fastseek]
|
||||||
|
- components/fatfs/test_apps/flash_wl/pytest_fatfs_flash_wl.py::test_fatfs_flash_wl_generic[release]
|
||||||
|
- components/nvs_flash/test_apps/pytest_nvs_flash.py::test_nvs_flash[default]
|
||||||
|
- components/pthread/test_apps/pthread_psram_tests/pytest_pthread_psram_tests.py::test_pthread_psram
|
||||||
|
- components/vfs/test_apps/pytest_vfs.py::test_vfs_ccomp[ccomp]
|
||||||
|
- components/vfs/test_apps/pytest_vfs.py::test_vfs_default[default]
|
||||||
|
- components/vfs/test_apps/pytest_vfs.py::test_vfs_default[iram]
|
||||||
|
- examples/protocols/http_server/file_serving/pytest_http_server_file_serving.py::test_examples_protocol_http_server_file_serving[spiffs]
|
||||||
|
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_only_partition_gen]
|
||||||
|
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_only_partition_gen_default_dt]
|
||||||
|
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_only_partition_gen_ln]
|
||||||
|
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_only_partition_gen_ln_default_dt]
|
||||||
|
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_write_partition_gen]
|
||||||
|
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_write_partition_gen_default_dt]
|
||||||
|
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_write_partition_gen_ln]
|
||||||
|
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_write_partition_gen_ln_default_dt]
|
||||||
|
- examples/storage/nvs_rw_blob/pytest_nvs_rw_blob.py::test_examples_nvs_rw_blob
|
||||||
|
- examples/storage/nvs_rw_value/pytest_nvs_rw_value.py::test_examples_nvs_rw_value
|
||||||
|
- examples/storage/nvs_rw_value_cxx/pytest_nvs_rw_value_cxx.py::test_examples_nvs_rw_value_cxx
|
||||||
|
- examples/storage/nvsgen/pytest_nvsgen_example.py::test_nvsgen_example
|
||||||
|
- examples/storage/partition_api/partition_find/pytest_partition_find_example.py::test_partition_find_example
|
||||||
|
- examples/storage/partition_api/partition_mmap/pytest_partition_mmap_example.py::test_partition_mmap_example
|
||||||
|
- examples/storage/partition_api/partition_ops/pytest_partition_ops_example.py::test_partition_ops_example
|
||||||
|
- examples/storage/parttool/pytest_parttool_example.py::test_examples_parttool
|
||||||
|
- examples/storage/spiffs/pytest_spiffs_example.py::test_examples_spiffs
|
||||||
|
- examples/storage/spiffsgen/pytest_spiffsgen_example.py::test_spiffsgen_example
|
||||||
|
- examples/storage/wear_levelling/pytest_wear_levelling_example.py::test_wear_levelling_example
|
||||||
|
- tools/test_apps/system/panic/pytest_panic.py::test_gdbstub_coredump[gdbstub_coredump]
|
||||||
|
- tools/test_apps/system/panic/pytest_panic.py::test_panic_delay[panic_delay]
|
||||||
|
no_runner_tags:
|
||||||
|
- esp32,ip101
|
||||||
|
- esp32,psram,quad_psram
|
||||||
|
- esp32,quad_psram
|
||||||
|
- esp32c2,ethernet,xtal_40mhz
|
||||||
|
- esp32c2,ethernet_ota,xtal_40mhz
|
||||||
|
- esp32c2,jtag,xtal_40mhz
|
||||||
|
- esp32c3,Example_ShieldBox_Basic
|
||||||
|
- esp32c3,ethernet_flash_8m
|
||||||
|
- esp32c3,ethernet_ota
|
||||||
|
- esp32c3,sdcard_sdmode
|
||||||
|
- esp32c6,jtag
|
||||||
|
- esp32h2,jtag
|
||||||
|
- esp32s2,Example_ShieldBox_Basic
|
||||||
|
- esp32s2,ethernet_flash_8m
|
||||||
|
- esp32s2,ethernet_ota
|
||||||
|
- esp32s2,usb_host_flash_disk
|
||||||
|
- esp32s2,wifi_high_traffic
|
||||||
|
- esp32s3,Example_ShieldBox_Basic
|
||||||
|
- esp32s3,ethernet_flash_8m
|
||||||
|
- esp32s3,ethernet_ota
|
@ -50,3 +50,4 @@ tools/ci/python_packages/gitlab_api.py
|
|||||||
tools/ci/python_packages/idf_http_server_test/**/*
|
tools/ci/python_packages/idf_http_server_test/**/*
|
||||||
tools/ci/python_packages/idf_iperf_test_util/**/*
|
tools/ci/python_packages/idf_iperf_test_util/**/*
|
||||||
tools/esp_prov/**/*
|
tools/esp_prov/**/*
|
||||||
|
tools/ci/sort_yaml.py
|
||||||
|
@ -77,6 +77,7 @@ tools/ci/gitlab_yaml_linter.py
|
|||||||
tools/ci/mirror-submodule-update.sh
|
tools/ci/mirror-submodule-update.sh
|
||||||
tools/ci/multirun_with_pyenv.sh
|
tools/ci/multirun_with_pyenv.sh
|
||||||
tools/ci/push_to_github.sh
|
tools/ci/push_to_github.sh
|
||||||
|
tools/ci/sort_yaml.py
|
||||||
tools/ci/test_autocomplete/test_autocomplete.py
|
tools/ci/test_autocomplete/test_autocomplete.py
|
||||||
tools/ci/test_configure_ci_environment.sh
|
tools/ci/test_configure_ci_environment.sh
|
||||||
tools/ci/test_reproducible_build.sh
|
tools/ci/test_reproducible_build.sh
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -11,7 +10,12 @@ import tempfile
|
|||||||
import time
|
import time
|
||||||
import zipfile
|
import zipfile
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Any, Callable, Dict, List, Optional, Union
|
from typing import Any
|
||||||
|
from typing import Callable
|
||||||
|
from typing import Dict
|
||||||
|
from typing import List
|
||||||
|
from typing import Optional
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
import gitlab
|
import gitlab
|
||||||
|
|
||||||
@ -80,7 +84,24 @@ class Gitlab(object):
|
|||||||
def _init_gitlab_inst(self, project_id: Optional[int], config_files: Optional[List[str]]) -> None:
|
def _init_gitlab_inst(self, project_id: Optional[int], config_files: Optional[List[str]]) -> None:
|
||||||
gitlab_id = os.getenv('LOCAL_GITLAB_HTTPS_HOST') # if None, will use the default gitlab server
|
gitlab_id = os.getenv('LOCAL_GITLAB_HTTPS_HOST') # if None, will use the default gitlab server
|
||||||
self.gitlab_inst = gitlab.Gitlab.from_config(gitlab_id=gitlab_id, config_files=config_files)
|
self.gitlab_inst = gitlab.Gitlab.from_config(gitlab_id=gitlab_id, config_files=config_files)
|
||||||
self.gitlab_inst.auth()
|
|
||||||
|
try:
|
||||||
|
self.gitlab_inst.auth()
|
||||||
|
except gitlab.exceptions.GitlabAuthenticationError:
|
||||||
|
msg = """To call gitlab apis locally, please create ~/.python-gitlab.cfg with the following content:
|
||||||
|
|
||||||
|
[global]
|
||||||
|
default = internal
|
||||||
|
ssl_verify = true
|
||||||
|
timeout = 5
|
||||||
|
|
||||||
|
[internal]
|
||||||
|
url = <OUR INTERNAL HTTPS SERVER URL>
|
||||||
|
private_token = <YOUR PERSONAL ACCESS TOKEN>
|
||||||
|
api_version = 4
|
||||||
|
"""
|
||||||
|
raise SystemExit(msg)
|
||||||
|
|
||||||
if project_id:
|
if project_id:
|
||||||
self.project = self.gitlab_inst.projects.get(project_id, lazy=True)
|
self.project = self.gitlab_inst.projects.get(project_id, lazy=True)
|
||||||
else:
|
else:
|
||||||
|
94
tools/ci/sort_yaml.py
Executable file
94
tools/ci/sort_yaml.py
Executable file
@ -0,0 +1,94 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
"""
|
||||||
|
Sort yaml file
|
||||||
|
|
||||||
|
Exit non-zero if any file is modified
|
||||||
|
"""
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from ruamel.yaml import CommentedMap
|
||||||
|
from ruamel.yaml import YAML
|
||||||
|
|
||||||
|
|
||||||
|
def sort_yaml(f: str) -> int:
|
||||||
|
yaml = YAML()
|
||||||
|
yaml.indent(mapping=2, sequence=4, offset=2)
|
||||||
|
yaml.width = 4096 # avoid wrap lines``
|
||||||
|
|
||||||
|
exit_code = 0
|
||||||
|
with open(f) as fr:
|
||||||
|
file_s = fr.read()
|
||||||
|
fr.seek(0)
|
||||||
|
|
||||||
|
try:
|
||||||
|
file_d: CommentedMap = yaml.load(fr)
|
||||||
|
except Exception as e:
|
||||||
|
print(f'Failed to load yaml file {f}: {e}')
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# sort dict keys
|
||||||
|
sorted_yaml = CommentedMap(dict(sorted(file_d.items())))
|
||||||
|
file_d.copy_attributes(sorted_yaml)
|
||||||
|
|
||||||
|
# sort item
|
||||||
|
for k, v in sorted_yaml.items():
|
||||||
|
if isinstance(v, list):
|
||||||
|
sorted_yaml[k].sort()
|
||||||
|
|
||||||
|
with io.StringIO() as s:
|
||||||
|
yaml.dump(sorted_yaml, s)
|
||||||
|
|
||||||
|
string = s.getvalue()
|
||||||
|
if string != file_s:
|
||||||
|
with open(f, 'w') as fw:
|
||||||
|
fw.write(string)
|
||||||
|
print(f'Sorted yaml file {f}. Please take a look. sometimes the format is a bit messy')
|
||||||
|
exit_code = 1
|
||||||
|
|
||||||
|
return exit_code
|
||||||
|
|
||||||
|
|
||||||
|
class TestSortYaml(unittest.TestCase):
|
||||||
|
def test_sort_yaml(self) -> None:
|
||||||
|
_, test_yaml = tempfile.mkstemp()
|
||||||
|
with open(test_yaml, 'w') as fw:
|
||||||
|
fw.write(
|
||||||
|
'''no_runner: []
|
||||||
|
no_env_marker:
|
||||||
|
- 1
|
||||||
|
- 3 # foo
|
||||||
|
- 2 # bar'''
|
||||||
|
)
|
||||||
|
|
||||||
|
sort_yaml(fw.name)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(test_yaml) as fr:
|
||||||
|
self.assertEqual(
|
||||||
|
fr.read(),
|
||||||
|
'''no_env_marker:
|
||||||
|
- 1
|
||||||
|
- 2 # bard
|
||||||
|
- 3 # foo
|
||||||
|
no_runner: []''',
|
||||||
|
)
|
||||||
|
except AssertionError:
|
||||||
|
print(f'Please check the sorted yaml file {test_yaml}')
|
||||||
|
else:
|
||||||
|
os.remove(test_yaml)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
ret = 0
|
||||||
|
for _f in sys.argv[1:]:
|
||||||
|
exit_code = sort_yaml(_f)
|
||||||
|
if exit_code != 0 and ret == 0:
|
||||||
|
ret = exit_code
|
||||||
|
|
||||||
|
sys.exit(ret)
|
@ -1,6 +1,5 @@
|
|||||||
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
# SPDX-License-Identifier: CC0-1.0
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -11,6 +10,7 @@ PROMPT = 'test_intr_dump>'
|
|||||||
|
|
||||||
@pytest.mark.esp32
|
@pytest.mark.esp32
|
||||||
@pytest.mark.qemu
|
@pytest.mark.qemu
|
||||||
|
@pytest.mark.host_test
|
||||||
def test_esp_intr_dump_nonshared(dut: Dut) -> None:
|
def test_esp_intr_dump_nonshared(dut: Dut) -> None:
|
||||||
dut.expect_exact(PROMPT, timeout=10)
|
dut.expect_exact(PROMPT, timeout=10)
|
||||||
|
|
||||||
@ -24,6 +24,7 @@ def test_esp_intr_dump_nonshared(dut: Dut) -> None:
|
|||||||
|
|
||||||
@pytest.mark.esp32
|
@pytest.mark.esp32
|
||||||
@pytest.mark.qemu
|
@pytest.mark.qemu
|
||||||
|
@pytest.mark.host_test
|
||||||
def test_esp_intr_dump_shared(dut: Dut) -> None:
|
def test_esp_intr_dump_shared(dut: Dut) -> None:
|
||||||
dut.expect_exact(PROMPT, timeout=10)
|
dut.expect_exact(PROMPT, timeout=10)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user