mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
ci(pytest): Add functionality to merge JUnit files of the multidut testcases into one file with the unique testcases
This commit is contained in:
parent
5f68437c2f
commit
9f5f8fa939
82
conftest.py
82
conftest.py
@ -1,4 +1,4 @@
|
|||||||
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
# SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
# pylint: disable=W0621 # redefined-outer-name
|
# pylint: disable=W0621 # redefined-outer-name
|
||||||
@ -22,7 +22,7 @@ import sys
|
|||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from fnmatch import fnmatch
|
from fnmatch import fnmatch
|
||||||
from typing import Callable, List, Optional, Tuple
|
from typing import Callable, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.config import Config, ExitCode
|
from _pytest.config import Config, ExitCode
|
||||||
@ -139,6 +139,8 @@ ENV_MARKERS = {
|
|||||||
'sdio_master_slave': 'Test sdio multi board.',
|
'sdio_master_slave': 'Test sdio multi board.',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SUB_JUNIT_FILENAME = 'dut.xml'
|
||||||
|
|
||||||
|
|
||||||
##################
|
##################
|
||||||
# Help Functions #
|
# Help Functions #
|
||||||
@ -215,6 +217,49 @@ def get_target_marker_from_expr(markexpr: str) -> str:
|
|||||||
raise ValueError('Please specify one target marker via "--target [TARGET]" or via "-m [TARGET]"')
|
raise ValueError('Please specify one target marker via "--target [TARGET]" or via "-m [TARGET]"')
|
||||||
|
|
||||||
|
|
||||||
|
def merge_junit_files(junit_files: List[str], target_path: str) -> Optional[ET.Element]:
|
||||||
|
merged_testsuite: ET.Element = ET.Element('testsuite')
|
||||||
|
testcases: Dict[str, ET.Element] = {}
|
||||||
|
|
||||||
|
if len(junit_files) == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if len(junit_files) == 1:
|
||||||
|
return ET.parse(junit_files[0]).getroot()
|
||||||
|
|
||||||
|
for junit in junit_files:
|
||||||
|
logging.info(f'Merging {junit} to {target_path}')
|
||||||
|
tree: ET.ElementTree = ET.parse(junit)
|
||||||
|
testsuite: ET.Element = tree.getroot()
|
||||||
|
|
||||||
|
for testcase in testsuite.findall('testcase'):
|
||||||
|
name: str = testcase.get('name') if testcase.get('name') else '' # type: ignore
|
||||||
|
|
||||||
|
if name not in testcases:
|
||||||
|
testcases[name] = testcase
|
||||||
|
merged_testsuite.append(testcase)
|
||||||
|
continue
|
||||||
|
|
||||||
|
existing_testcase = testcases[name]
|
||||||
|
for element_name in ['failure', 'error']:
|
||||||
|
for element in testcase.findall(element_name):
|
||||||
|
existing_element = existing_testcase.find(element_name)
|
||||||
|
if existing_element is None:
|
||||||
|
existing_testcase.append(element)
|
||||||
|
else:
|
||||||
|
existing_element.attrib.setdefault('message', '') # type: ignore
|
||||||
|
existing_element.attrib['message'] += '. ' + element.get('message', '') # type: ignore
|
||||||
|
|
||||||
|
os.remove(junit)
|
||||||
|
|
||||||
|
merged_testsuite.set('tests', str(len(merged_testsuite.findall('testcase'))))
|
||||||
|
merged_testsuite.set('failures', str(len(merged_testsuite.findall('.//testcase/failure'))))
|
||||||
|
merged_testsuite.set('errors', str(len(merged_testsuite.findall('.//testcase/error'))))
|
||||||
|
merged_testsuite.set('skipped', str(len(merged_testsuite.findall('.//testcase/skipped'))))
|
||||||
|
|
||||||
|
return merged_testsuite
|
||||||
|
|
||||||
|
|
||||||
############
|
############
|
||||||
# Fixtures #
|
# Fixtures #
|
||||||
############
|
############
|
||||||
@ -448,13 +493,13 @@ def pytest_addoption(parser: pytest.Parser) -> None:
|
|||||||
'--app-info-basedir',
|
'--app-info-basedir',
|
||||||
default=IDF_PATH,
|
default=IDF_PATH,
|
||||||
help='app info base directory. specify this value when you\'re building under a '
|
help='app info base directory. specify this value when you\'re building under a '
|
||||||
'different IDF_PATH. (Default: $IDF_PATH)',
|
'different IDF_PATH. (Default: $IDF_PATH)',
|
||||||
)
|
)
|
||||||
idf_group.addoption(
|
idf_group.addoption(
|
||||||
'--app-info-filepattern',
|
'--app-info-filepattern',
|
||||||
help='glob pattern to specify the files that include built app info generated by '
|
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 '
|
'`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.',
|
'paths not exist in local file system if not listed recorded in the app info.',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -688,22 +733,23 @@ class IdfPytestEmbedded:
|
|||||||
failed_sub_cases = []
|
failed_sub_cases = []
|
||||||
target = item.funcargs['target']
|
target = item.funcargs['target']
|
||||||
config = item.funcargs['config']
|
config = item.funcargs['config']
|
||||||
for junit in junits:
|
merged_dut_junit_filepath = os.path.join(tempdir, SUB_JUNIT_FILENAME)
|
||||||
xml = ET.parse(junit)
|
merged_testsuite = merge_junit_files(junit_files=junits, target_path=merged_dut_junit_filepath)
|
||||||
testcases = xml.findall('.//testcase')
|
|
||||||
for case in testcases:
|
|
||||||
# modify the junit files
|
|
||||||
new_case_name = format_case_id(target, config, case.attrib['name'])
|
|
||||||
case.attrib['name'] = new_case_name
|
|
||||||
if 'file' in case.attrib:
|
|
||||||
case.attrib['file'] = case.attrib['file'].replace('/IDF/', '') # our unity test framework
|
|
||||||
|
|
||||||
# collect real failure cases
|
if merged_testsuite is None:
|
||||||
if case.find('failure') is not None:
|
return
|
||||||
failed_sub_cases.append(new_case_name)
|
|
||||||
|
|
||||||
xml.write(junit)
|
for testcase in merged_testsuite.findall('testcase'):
|
||||||
|
new_case_name: str = format_case_id(target, config, testcase.attrib['name'])
|
||||||
|
testcase.attrib['name'] = new_case_name
|
||||||
|
if 'file' in testcase.attrib:
|
||||||
|
testcase.attrib['file'] = testcase.attrib['file'].replace('/IDF/', '') # Our unity test framework
|
||||||
|
# Collect real failure cases
|
||||||
|
if testcase.find('failure') is not None:
|
||||||
|
failed_sub_cases.append(new_case_name)
|
||||||
|
|
||||||
|
merged_tree: ET.ElementTree = ET.ElementTree(merged_testsuite)
|
||||||
|
merged_tree.write(merged_dut_junit_filepath)
|
||||||
item.stash[_item_failed_cases_key] = failed_sub_cases
|
item.stash[_item_failed_cases_key] = failed_sub_cases
|
||||||
|
|
||||||
def pytest_sessionfinish(self, session: Session, exitstatus: int) -> None:
|
def pytest_sessionfinish(self, session: Session, exitstatus: int) -> None:
|
||||||
|
Loading…
Reference in New Issue
Block a user