diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 3fc2ab3b..5ef02391 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -202,7 +202,7 @@ jobs:
id: test-results
if: always()
run: |
- docker run --workdir $GITHUB_WORKSPACE --rm -e INPUT_CHECK_NAME -e INPUT_JUNIT_FILES -e INPUT_TRX_FILES -e INPUT_TIME_UNIT -e INPUT_GITHUB_TOKEN -e INPUT_GITHUB_RETRIES -e INPUT_COMMIT -e INPUT_COMMENT_TITLE -e INPUT_FAIL_ON -e INPUT_REPORT_INDIVIDUAL_RUNS -e INPUT_DEDUPLICATE_CLASSES_BY_FILE_NAME -e INPUT_IGNORE_RUNS -e INPUT_HIDE_COMMENTS -e INPUT_COMMENT_ON_PR -e INPUT_COMMENT_MODE -e INPUT_COMPARE_TO_EARLIER_COMMIT -e INPUT_PULL_REQUEST_BUILD -e INPUT_EVENT_FILE -e INPUT_EVENT_NAME -e INPUT_TEST_CHANGES_LIMIT -e INPUT_CHECK_RUN_ANNOTATIONS -e INPUT_CHECK_RUN_ANNOTATIONS_BRANCH -e INPUT_SECONDS_BETWEEN_GITHUB_READS -e INPUT_SECONDS_BETWEEN_GITHUB_WRITES -e INPUT_JSON_FILE -e INPUT_JSON_THOUSANDS_SEPARATOR -e INPUT_JOB_SUMMARY -e HOME -e GITHUB_JOB -e GITHUB_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_REPOSITORY_OWNER -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RETENTION_DAYS -e GITHUB_ACTOR -e GITHUB_WORKFLOW -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GITHUB_EVENT_NAME -e GITHUB_SERVER_URL -e GITHUB_API_URL -e GITHUB_GRAPHQL_URL -e GITHUB_WORKSPACE -e GITHUB_ACTION -e GITHUB_EVENT_PATH -e GITHUB_ACTION_REPOSITORY -e GITHUB_ACTION_REF -e GITHUB_PATH -e GITHUB_ENV -e GITHUB_STEP_SUMMARY -e RUNNER_OS -e RUNNER_TOOL_CACHE -e RUNNER_TEMP -e RUNNER_WORKSPACE -e ACTIONS_RUNTIME_URL -e ACTIONS_RUNTIME_TOKEN -e ACTIONS_CACHE_URL -e GITHUB_ACTIONS=true -e CI=true -v "/var/run/docker.sock":"/var/run/docker.sock" -v "$RUNNER_TEMP":"$RUNNER_TEMP" -v "$GITHUB_WORKSPACE":"$GITHUB_WORKSPACE" enricomi/publish-unit-test-result-action:latest
+ docker run --workdir $GITHUB_WORKSPACE --rm -e INPUT_CHECK_NAME -e INPUT_JUNIT_FILES -e INPUT_XUNIT_FILES -e INPUT_TRX_FILES -e INPUT_TIME_UNIT -e INPUT_GITHUB_TOKEN -e INPUT_GITHUB_RETRIES -e INPUT_COMMIT -e INPUT_COMMENT_TITLE -e INPUT_FAIL_ON -e INPUT_REPORT_INDIVIDUAL_RUNS -e INPUT_DEDUPLICATE_CLASSES_BY_FILE_NAME -e INPUT_IGNORE_RUNS -e INPUT_HIDE_COMMENTS -e INPUT_COMMENT_ON_PR -e INPUT_COMMENT_MODE -e INPUT_COMPARE_TO_EARLIER_COMMIT -e INPUT_PULL_REQUEST_BUILD -e INPUT_EVENT_FILE -e INPUT_EVENT_NAME -e INPUT_TEST_CHANGES_LIMIT -e INPUT_CHECK_RUN_ANNOTATIONS -e INPUT_CHECK_RUN_ANNOTATIONS_BRANCH -e INPUT_SECONDS_BETWEEN_GITHUB_READS -e INPUT_SECONDS_BETWEEN_GITHUB_WRITES -e INPUT_JSON_FILE -e INPUT_JSON_THOUSANDS_SEPARATOR -e INPUT_JOB_SUMMARY -e HOME -e GITHUB_JOB -e GITHUB_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_REPOSITORY_OWNER -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RETENTION_DAYS -e GITHUB_ACTOR -e GITHUB_WORKFLOW -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GITHUB_EVENT_NAME -e GITHUB_SERVER_URL -e GITHUB_API_URL -e GITHUB_GRAPHQL_URL -e GITHUB_WORKSPACE -e GITHUB_ACTION -e GITHUB_EVENT_PATH -e GITHUB_ACTION_REPOSITORY -e GITHUB_ACTION_REF -e GITHUB_PATH -e GITHUB_ENV -e GITHUB_STEP_SUMMARY -e RUNNER_OS -e RUNNER_TOOL_CACHE -e RUNNER_TEMP -e RUNNER_WORKSPACE -e ACTIONS_RUNTIME_URL -e ACTIONS_RUNTIME_TOKEN -e ACTIONS_CACHE_URL -e GITHUB_ACTIONS=true -e CI=true -v "/var/run/docker.sock":"/var/run/docker.sock" -v "$RUNNER_TEMP":"$RUNNER_TEMP" -v "$GITHUB_WORKSPACE":"$GITHUB_WORKSPACE" enricomi/publish-unit-test-result-action:latest
env:
INPUT_GITHUB_TOKEN: ${{ github.token }}
INPUT_CHECK_NAME: Test Results (Docker Image)
@@ -423,6 +423,7 @@ jobs:
check_name: Test Results (Test Files)
fail_on: nothing
junit_files: "test-files/junit-xml/**/*.xml"
+ xunit_files: "test-files/xunit/**/*.xml"
trx_files: "test-files/trx/**/*.trx"
json_file: "tests.json"
log_level: DEBUG
diff --git a/README.md b/README.md
index b899a5ee..96d24fc8 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,8 @@ You can add this action to your GitHub workflow for ![Ubuntu Linux](https://badg
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
- junit_files: "test-results/**/*.xml"
+ junit_files: "test-results/junit/**/*.xml"
+ xunit_files: "test-results/xunit/**/*.xml"
trx_files: "test-results/**/*.trx"
```
@@ -34,7 +35,8 @@ and ![Windows](https://badgen.net/badge/icon/Windows?icon=windows&label) (e.g. `
uses: EnricoMi/publish-unit-test-result-action/composite@v2
if: always()
with:
- junit_files: "test-results/**/*.xml"
+ junit_files: "test-results/junit/**/*.xml"
+ xunit_files: "test-results/xunit/**/*.xml"
trx_files: "test-results/**/*.trx"
```
@@ -182,11 +184,11 @@ With `comment_mode: off`, the `pull-requests: write` permission is not needed.
## Configuration
-Files can be selected via the `junit_files` and `trx_files` options.
+Files can be selected via the `junit_files`, `xunit_files`, and `trx_files` options.
They support [glob wildcards](https://docs.python.org/3/library/glob.html#glob.glob) like `*`, `**`, `?` and `[]`.
The `**` wildcard matches all files and directories recursively: `./`, `./*/`, `./*/*/`, etc.
-At least one of `junit_files` and `trx_files` options have to be set.
+At least one of `junit_files`, `xunit_files`, and `trx_files` options have to be set.
You can provide multiple file patterns, one pattern per line. Patterns starting with `!` exclude the matching files.
There have to be at least one pattern starting without a `!`:
@@ -202,7 +204,7 @@ The list of most notable options:
|Option|Default Value|Description|
|:-----|:-----:|:----------|
-|`junit_files`
`trx_files`|At least one of these `*_files` must be set.|File patterns of JUnit XML and TRX test result files, respectively. Supports `*`, `**`, `?`, and `[]`. Use multiline string for multiple patterns. Patterns starting with `!` exclude the matching files. There have to be at least one pattern starting without a `!`.|
+|`junit_files`
`xunit_files`
`trx_files`|At least one of these `*_files` must be set.|File patterns of JUnit XML, XUnit XML, and TRX test result files, respectively. Supports `*`, `**`, `?`, and `[]`. Use multiline string for multiple patterns. Patterns starting with `!` exclude the matching files. There have to be at least one pattern starting without a `!`.|
|`check_name`|`"Test Results"`|An alternative name for the check result.|
|`comment_title`|same as `check_name`|An alternative name for the pull request comment.|
|`comment_mode`|`always`|The action posts comments to pull requests that are associated with the commit. Set to:
`always` - always comment
`changes` - comment when changes w.r.t. the target branch exist
`changes in failures` - when changes in the number of failures and errors exist
`changes in errors` - when changes in the number of (only) errors exist
`failures` - when failures or errors exist
`errors` - when (only) errors exist
`off` - to not create pull request comments.|
diff --git a/action.yml b/action.yml
index 9a286de9..5aa76711 100644
--- a/action.yml
+++ b/action.yml
@@ -32,6 +32,9 @@ inputs:
junit_files:
description: 'File patterns of JUnit XML test result files. Supports *, **, ?, and []. Use multiline string for multiple patterns. Patterns starting with ! exclude the matching files. There have to be at least one pattern starting without a `!`.'
required: false
+ xunit_files:
+ description: 'File patterns of XUnit XML test result files. Supports *, **, ?, and []. Use multiline string for multiple patterns. Patterns starting with ! exclude the matching files. There have to be at least one pattern starting without a `!`.'
+ required: false
trx_files:
description: 'File patterns of TRX test result files. Supports *, **, ?, and []. Use multiline string for multiple patterns. Patterns starting with ! exclude the matching files. There have to be at least one pattern starting without a `!`.'
required: false
diff --git a/composite/action.yml b/composite/action.yml
index 38481a3e..3ce7035b 100644
--- a/composite/action.yml
+++ b/composite/action.yml
@@ -32,6 +32,9 @@ inputs:
junit_files:
description: 'File patterns of JUnit XML test result files. Supports *, **, ?, and []. Use multiline string for multiple patterns. Patterns starting with ! exclude the matching files. There have to be at least one pattern starting without a `!`.'
required: false
+ xunit_files:
+ description: 'File patterns of XUnit XML test result files. Supports *, **, ?, and []. Use multiline string for multiple patterns. Patterns starting with ! exclude the matching files. There have to be at least one pattern starting without a `!`.'
+ required: false
trx_files:
description: 'File patterns of TRX test result files. Supports *, **, ?, and []. Use multiline string for multiple patterns. Patterns starting with ! exclude the matching files. There have to be at least one pattern starting without a `!`.'
required: false
@@ -157,6 +160,7 @@ runs:
# deprecated
FILES: ${{ inputs.files }}
JUNIT_FILES: ${{ inputs.junit_files }}
+ XUNIT_FILES: ${{ inputs.xunit_files }}
TRX_FILES: ${{ inputs.trx_files }}
TIME_UNIT: ${{ inputs.time_unit }}
REPORT_INDIVIDUAL_RUNS: ${{ inputs.report_individual_runs }}
diff --git a/python/publish/junit.py b/python/publish/junit.py
index 4ae3972e..b1d016ff 100644
--- a/python/publish/junit.py
+++ b/python/publish/junit.py
@@ -127,6 +127,7 @@ def close(self) -> Element:
def parse_junit_xml_files(files: Iterable[str],
drop_testcases: bool = False,
progress: Callable[[ParsedJUnitFile], ParsedJUnitFile] = lambda x: x) -> Iterable[ParsedJUnitFile]:
+ """Parses junit xml files."""
def parse(path: str) -> JUnitTreeOrException:
"""Parses a junit xml file and returns either a JUnitTree or an Exception."""
if not os.path.exists(path):
@@ -172,11 +173,12 @@ def create_junitxml(filepath: str, tree: JUnitTree) -> Union[JUnitXml, JUnitXmlE
for result_file, junit in junits
for suite in (junit if junit._tag == "testsuites" else [junit])]
- suite_tests = sum([suite.tests for result_file, suite in suites])
- suite_skipped = sum([suite.skipped + suite.disabled for result_file, suite in suites])
- suite_failures = sum([suite.failures for result_file, suite in suites])
- suite_errors = sum([suite.errors for result_file, suite in suites])
- suite_time = int(sum([suite.time for result_file, suite in suites if not math.isnan(suite.time)]) * time_factor)
+ suite_tests = sum([suite.tests for result_file, suite in suites if suite.tests])
+ suite_skipped = sum([suite.skipped + suite.disabled for result_file, suite in suites if suite.skipped])
+ suite_failures = sum([suite.failures for result_file, suite in suites if suite.failures])
+ suite_errors = sum([suite.errors for result_file, suite in suites if suite.errors])
+ suite_time = int(sum([suite.time for result_file, suite in suites
+ if suite.time and not math.isnan(suite.time)]) * time_factor)
def int_opt(string: Optional[str]) -> Optional[int]:
try:
diff --git a/python/publish/publisher.py b/python/publish/publisher.py
index 18cb16f9..0ee8440f 100644
--- a/python/publish/publisher.py
+++ b/python/publish/publisher.py
@@ -45,6 +45,7 @@ class Settings:
fail_on_failures: bool
# one of these *_files_glob must be set
junit_files_glob: Optional[str]
+ xunit_files_glob: Optional[str]
trx_files_glob: Optional[str]
time_factor: float
check_name: str
diff --git a/python/publish/trx.py b/python/publish/trx.py
index 77e3d31e..dcabc9c2 100644
--- a/python/publish/trx.py
+++ b/python/publish/trx.py
@@ -12,6 +12,7 @@
def parse_trx_files(files: Iterable[str],
progress: Callable[[ParsedJUnitFile], ParsedJUnitFile] = lambda x: x) -> Iterable[ParsedJUnitFile]:
+ """Parses trx files."""
def parse(path: str) -> JUnitTreeOrException:
"""Parses a trx file and returns either a JUnitTree or an Exception."""
if not os.path.exists(path):
diff --git a/python/publish/xslt/xunit-to-junit.xslt b/python/publish/xslt/xunit-to-junit.xslt
new file mode 100644
index 00000000..6ee68d64
--- /dev/null
+++ b/python/publish/xslt/xunit-to-junit.xslt
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ T
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/python/publish/xunit.py b/python/publish/xunit.py
new file mode 100644
index 00000000..5874db27
--- /dev/null
+++ b/python/publish/xunit.py
@@ -0,0 +1,29 @@
+import os
+import pathlib
+from typing import Iterable, Callable
+
+from lxml import etree
+
+from publish.junit import JUnitTreeOrException, ParsedJUnitFile
+
+with (pathlib.Path(__file__).parent / 'xslt' / 'xunit-to-junit.xslt').open('r', encoding='utf-8') as r:
+ transform_xunit_to_junit = etree.XSLT(etree.parse(r))
+
+
+def parse_xunit_files(files: Iterable[str],
+ progress: Callable[[ParsedJUnitFile], ParsedJUnitFile] = lambda x: x) -> Iterable[ParsedJUnitFile]:
+ """Parses xunit files."""
+ def parse(path: str) -> JUnitTreeOrException:
+ """Parses an xunit file and returns either a JUnitTree or an Exception."""
+ if not os.path.exists(path):
+ return FileNotFoundError(f'File does not exist.')
+ if os.stat(path).st_size == 0:
+ return Exception(f'File is empty.')
+
+ try:
+ trx = etree.parse(path)
+ return transform_xunit_to_junit(trx)
+ except BaseException as e:
+ return e
+
+ return [progress((result_file, parse(result_file))) for result_file in files]
diff --git a/python/publish_unit_test_results.py b/python/publish_unit_test_results.py
index 3174a16d..ed934f33 100644
--- a/python/publish_unit_test_results.py
+++ b/python/publish_unit_test_results.py
@@ -93,13 +93,14 @@ def get_number_of_files(files: List[str]) -> str:
def parse_files(settings: Settings, gha: GithubAction) -> ParsedUnitTestResultsWithCommit:
# expand file globs
junit_files = expand_glob(settings.junit_files_glob, gha)
+ xunit_files = expand_glob(settings.xunit_files_glob, gha)
trx_files = expand_glob(settings.trx_files_glob, gha)
elems = []
# parse files, log the progress
# https://github.com/EnricoMi/publish-unit-test-result-action/issues/304
- with progress_logger(items=len(junit_files + trx_files),
+ with progress_logger(items=len(junit_files + xunit_files + trx_files),
interval_seconds=10,
progress_template='Read {progress} files in {time}',
finish_template='Finished reading {observations} files in {duration}',
@@ -107,6 +108,9 @@ def parse_files(settings: Settings, gha: GithubAction) -> ParsedUnitTestResultsW
logger=logger) as progress:
if junit_files:
elems.extend(parse_junit_xml_files(junit_files, settings.ignore_runs, progress))
+ if xunit_files:
+ from publish.xunit import parse_xunit_files
+ elems.extend(parse_xunit_files(xunit_files, progress))
if trx_files:
from publish.trx import parse_trx_files
elems.extend(parse_trx_files(trx_files, progress))
@@ -325,7 +329,7 @@ def get_settings(options: dict, gha: Optional[GithubAction] = None) -> Settings:
# replace with error when deprecated FILES is removed
default_junit_files_glob = None
if not any([get_var(f'{flavour}_FILES', options)
- for flavour in ['JUNIT', 'TRX']]):
+ for flavour in ['JUNIT', 'XUNIT', 'TRX']]):
default_junit_files_glob = '*.xml'
gha.warning(f'At least one of the *_FILES options has to be set! '
f'Falling back to deprecated default "{default_junit_files_glob}"')
@@ -369,6 +373,7 @@ def get_settings(options: dict, gha: Optional[GithubAction] = None) -> Settings:
fail_on_errors=fail_on_errors,
fail_on_failures=fail_on_failures,
junit_files_glob=get_var('JUNIT_FILES', options) or get_var('FILES', options) or default_junit_files_glob,
+ xunit_files_glob=get_var('XUNIT_FILES', options),
trx_files_glob=get_var('TRX_FILES', options),
time_factor=time_factor,
check_name=check_name,
diff --git a/python/test/files/junit-xml/tst/disabled.results b/python/test/files/junit-xml/tst/disabled.results
index c7c08bb1..cbdb1e7c 100644
--- a/python/test/files/junit-xml/tst/disabled.results
+++ b/python/test/files/junit-xml/tst/disabled.results
@@ -3,7 +3,7 @@ publish.unittestresults.ParsedUnitTestResults(
errors=[],
suites=2,
suite_tests=31,
- suite_skipped=5,
+ suite_skipped=0,
suite_failures=19,
suite_errors=1,
suite_time=0,
diff --git a/python/test/files/update_expectations.sh b/python/test/files/update_expectations.sh
index 1af705b0..8f333be2 100755
--- a/python/test/files/update_expectations.sh
+++ b/python/test/files/update_expectations.sh
@@ -3,5 +3,6 @@
base=$(dirname "$0")
python $base/../test_junit.py
+python $base/../test_xunit.py
python $base/../test_trx.py
diff --git a/python/test/files/xunit/README.md b/python/test/files/xunit/README.md
new file mode 100644
index 00000000..c83fd6a9
--- /dev/null
+++ b/python/test/files/xunit/README.md
@@ -0,0 +1 @@
+[mstest/fixie.xml](https://raw.githubusercontent.com/fixie/fixie/42b43dc6cc57476958eea8b507aa9d0d72cedae6/src/Fixie.Tests/Reports/XUnitXmlReport.xml)
diff --git a/python/test/files/xunit/mstest/fixie.junit-xml b/python/test/files/xunit/mstest/fixie.junit-xml
new file mode 100644
index 00000000..dc08dec5
--- /dev/null
+++ b/python/test/files/xunit/mstest/fixie.junit-xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+ Expected: System.String
+Actual: System.Int32 at [genericTestClassForStackTrace].ShouldBeString[T](T genericArgument) in [fileLocation]:line #
+
+
+
+
+ 'Fail' failed! at [testClassForStackTrace].Fail() in [fileLocation]:line #
+
+
+ Expected: 2
+Actual: 1 at [testClassForStackTrace].FailByAssertion() in [fileLocation]:line #
+
+
+
+
+
+
+
+
diff --git a/python/test/files/xunit/mstest/fixie.results b/python/test/files/xunit/mstest/fixie.results
new file mode 100644
index 00000000..88ab4f45
--- /dev/null
+++ b/python/test/files/xunit/mstest/fixie.results
@@ -0,0 +1,93 @@
+publish.unittestresults.ParsedUnitTestResults(
+ files=1,
+ errors=[],
+ suites=1,
+ suite_tests=7,
+ suite_skipped=1,
+ suite_failures=3,
+ suite_errors=0,
+ suite_time=8,
+ cases=[
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/fixie.xml',
+ test_file=None,
+ line=None,
+ class_name='[genericTestClass]',
+ test_name='ShouldBeString',
+ result='success',
+ message=None,
+ content=None,
+ time=1.234
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/fixie.xml',
+ test_file=None,
+ line=None,
+ class_name='[genericTestClass]',
+ test_name='ShouldBeString',
+ result='success',
+ message=None,
+ content=None,
+ time=1.234
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/fixie.xml',
+ test_file=None,
+ line=None,
+ class_name='[genericTestClass]',
+ test_name='ShouldBeString',
+ result='failure',
+ message='Expected: System.String\nActual: System.Int32',
+ content='Expected: System.String\nActual: System.Int32 at '
+ '[genericTestClassForStackTrace].ShouldBeString[T](T genericArgument) '
+ 'in [fileLocation]:line #',
+ time=1.234
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/fixie.xml',
+ test_file=None,
+ line=None,
+ class_name='[testClass]',
+ test_name='Fail',
+ result='failure',
+ message="'Fail' failed!",
+ content="'Fail' failed! at [testClassForStackTrace].Fail() in "
+ "[fileLocation]:line #",
+ time=1.234
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/fixie.xml',
+ test_file=None,
+ line=None,
+ class_name='[testClass]',
+ test_name='FailByAssertion',
+ result='failure',
+ message='Expected: 2\nActual: 1',
+ content='Expected: 2\nActual: 1 at '
+ '[testClassForStackTrace].FailByAssertion() in [fileLocation]:line #',
+ time=1.234
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/fixie.xml',
+ test_file=None,
+ line=None,
+ class_name='[testClass]',
+ test_name='Pass',
+ result='success',
+ message=None,
+ content=None,
+ time=1.234
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/fixie.xml',
+ test_file=None,
+ line=None,
+ class_name='[testClass]',
+ test_name='Skip',
+ result='skipped',
+ message='⚠ Skipped with attribute.',
+ content=None,
+ time=1.234
+ )
+ ]
+)
\ No newline at end of file
diff --git a/python/test/files/xunit/mstest/fixie.xml b/python/test/files/xunit/mstest/fixie.xml
new file mode 100644
index 00000000..77715ea6
--- /dev/null
+++ b/python/test/files/xunit/mstest/fixie.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase1.junit-xml b/python/test/files/xunit/mstest/jenkinsci/testcase1.junit-xml
new file mode 100644
index 00000000..6a0d81a4
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase1.junit-xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ Assert.True() Failure at MyProject.Tests.SampleFact.FailedTest() in c:\Jenkins\jobs\my-project\workspace\MyProject\MyProject.Tests\SampleFact.cs:line 16
+
+
+
+
+
+
+
+
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase1.results b/python/test/files/xunit/mstest/jenkinsci/testcase1.results
new file mode 100644
index 00000000..fbf10373
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase1.results
@@ -0,0 +1,48 @@
+publish.unittestresults.ParsedUnitTestResults(
+ files=1,
+ errors=[],
+ suites=1,
+ suite_tests=3,
+ suite_skipped=1,
+ suite_failures=1,
+ suite_errors=0,
+ suite_time=0,
+ cases=[
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/jenkinsci/testcase1.xml',
+ test_file=None,
+ line=None,
+ class_name='MyProject.Tests.SampleFact',
+ test_name='FailedTest',
+ result='failure',
+ message='Assert.True() Failure',
+ content='Assert.True() Failure at MyProject.Tests.SampleFact.FailedTest() '
+ 'in '
+ 'c:\\Jenkins\\jobs\\my-project\\workspace\\MyProject\\MyProject.Tests\\Sample'
+ 'Fact.cs:line 16',
+ time=0.014
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/jenkinsci/testcase1.xml',
+ test_file=None,
+ line=None,
+ class_name='MyProject.Tests.SampleFact',
+ test_name='SuccessfulTest',
+ result='success',
+ message=None,
+ content=None,
+ time=0.01
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/jenkinsci/testcase1.xml',
+ test_file=None,
+ line=None,
+ class_name='MyProject.Tests.SampleFact',
+ test_name='SkippedTest',
+ result='skipped',
+ message='On Purpose',
+ content=None,
+ time=0.0
+ )
+ ]
+)
\ No newline at end of file
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase1.xml b/python/test/files/xunit/mstest/jenkinsci/testcase1.xml
new file mode 100644
index 00000000..cc9cab4a
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase1.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase2.junit-xml b/python/test/files/xunit/mstest/jenkinsci/testcase2.junit-xml
new file mode 100644
index 00000000..7f95608b
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase2.junit-xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase2.results b/python/test/files/xunit/mstest/jenkinsci/testcase2.results
new file mode 100644
index 00000000..2a43cf8f
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase2.results
@@ -0,0 +1,23 @@
+publish.unittestresults.ParsedUnitTestResults(
+ files=1,
+ errors=[],
+ suites=1,
+ suite_tests=3,
+ suite_skipped=1,
+ suite_failures=1,
+ suite_errors=0,
+ suite_time=0,
+ cases=[
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/jenkinsci/testcase2.xml',
+ test_file=None,
+ line=None,
+ class_name='MyProject.Tests.SampleFact',
+ test_name='SuccessfulTestWithTrait',
+ result='success',
+ message=None,
+ content=None,
+ time=35.5236617
+ )
+ ]
+)
\ No newline at end of file
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase2.xml b/python/test/files/xunit/mstest/jenkinsci/testcase2.xml
new file mode 100644
index 00000000..ef720197
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase2.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase3.junit-xml b/python/test/files/xunit/mstest/jenkinsci/testcase3.junit-xml
new file mode 100644
index 00000000..d4596aca
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase3.junit-xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ Assert.True() Failure at MyProject.Tests.SampleFact.FailedTest() in c:\Jenkins\jobs\my-project\workspace\MyProject\MyProject.Tests\SampleFact.cs:line 16
+
+
+
+
+
+
+
+
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase3.results b/python/test/files/xunit/mstest/jenkinsci/testcase3.results
new file mode 100644
index 00000000..50507c1b
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase3.results
@@ -0,0 +1,48 @@
+publish.unittestresults.ParsedUnitTestResults(
+ files=1,
+ errors=[],
+ suites=1,
+ suite_tests=3,
+ suite_skipped=1,
+ suite_failures=1,
+ suite_errors=0,
+ suite_time=288,
+ cases=[
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/jenkinsci/testcase3.xml',
+ test_file=None,
+ line=None,
+ class_name='MyProject.Tests.SampleFact',
+ test_name='FailedTest',
+ result='failure',
+ message='Assert.True() Failure',
+ content='Assert.True() Failure at MyProject.Tests.SampleFact.FailedTest() '
+ 'in '
+ 'c:\\Jenkins\\jobs\\my-project\\workspace\\MyProject\\MyProject.Tests\\Sample'
+ 'Fact.cs:line 16',
+ time=14.0
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/jenkinsci/testcase3.xml',
+ test_file=None,
+ line=None,
+ class_name='MyProject.Tests.SampleFact',
+ test_name='SuccessfulTest',
+ result='success',
+ message=None,
+ content=None,
+ time=10.0
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/jenkinsci/testcase3.xml',
+ test_file=None,
+ line=None,
+ class_name='MyProject.Tests.SampleFact',
+ test_name='SkippedTest',
+ result='skipped',
+ message='On Purpose',
+ content=None,
+ time=0.0
+ )
+ ]
+)
\ No newline at end of file
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase3.xml b/python/test/files/xunit/mstest/jenkinsci/testcase3.xml
new file mode 100644
index 00000000..21a9c533
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase3.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase4.junit-xml b/python/test/files/xunit/mstest/jenkinsci/testcase4.junit-xml
new file mode 100644
index 00000000..80b072ff
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase4.junit-xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase4.results b/python/test/files/xunit/mstest/jenkinsci/testcase4.results
new file mode 100644
index 00000000..87ab69c4
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase4.results
@@ -0,0 +1,11 @@
+publish.unittestresults.ParsedUnitTestResults(
+ files=1,
+ errors=[],
+ suites=1,
+ suite_tests=0,
+ suite_skipped=0,
+ suite_failures=0,
+ suite_errors=0,
+ suite_time=0,
+ cases=[]
+)
\ No newline at end of file
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase4.xml b/python/test/files/xunit/mstest/jenkinsci/testcase4.xml
new file mode 100644
index 00000000..c43c2bc7
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase4.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase5.junit-xml b/python/test/files/xunit/mstest/jenkinsci/testcase5.junit-xml
new file mode 100644
index 00000000..d5f5b093
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase5.junit-xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase5.results b/python/test/files/xunit/mstest/jenkinsci/testcase5.results
new file mode 100644
index 00000000..53a81563
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase5.results
@@ -0,0 +1,67 @@
+publish.unittestresults.ParsedUnitTestResults(
+ files=1,
+ errors=[],
+ suites=1,
+ suite_tests=5,
+ suite_skipped=0,
+ suite_failures=0,
+ suite_errors=0,
+ suite_time=92,
+ cases=[
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/jenkinsci/testcase5.xml',
+ test_file=None,
+ line=None,
+ class_name='UnitTest.UnitTest',
+ test_name='UninstallApplicationIfInstalled',
+ result='success',
+ message=None,
+ content=None,
+ time=48.3914131
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/jenkinsci/testcase5.xml',
+ test_file=None,
+ line=None,
+ class_name='UnitTest.UnitTest',
+ test_name='InstallApplication',
+ result='success',
+ message=None,
+ content=None,
+ time=33.1446488
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/jenkinsci/testcase5.xml',
+ test_file=None,
+ line=None,
+ class_name='UnitTest.UnitTest',
+ test_name='CheckIfAllFilesAreThere',
+ result='success',
+ message=None,
+ content=None,
+ time=3.0399824
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/jenkinsci/testcase5.xml',
+ test_file=None,
+ line=None,
+ class_name='UnitTest.UnitTest',
+ test_name='CheckIfFilesWereSinged',
+ result='success',
+ message=None,
+ content=None,
+ time=4.7233194
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/jenkinsci/testcase5.xml',
+ test_file=None,
+ line=None,
+ class_name='UnitTest.UnitTest',
+ test_name='CheckIfAppIsRunning',
+ result='success',
+ message=None,
+ content=None,
+ time=3.0279026
+ )
+ ]
+)
\ No newline at end of file
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase5.xml b/python/test/files/xunit/mstest/jenkinsci/testcase5.xml
new file mode 100644
index 00000000..3ab35ec0
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase5.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase6.junit-xml b/python/test/files/xunit/mstest/jenkinsci/testcase6.junit-xml
new file mode 100644
index 00000000..a16cb199
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase6.junit-xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ OK: TestProcess.Execution.ExitCode == 0
+FAIL: Output differs from reference.
+ Left: data\ref
+ Right: results\out
+
+
+
+
+
+
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase6.results b/python/test/files/xunit/mstest/jenkinsci/testcase6.results
new file mode 100644
index 00000000..27e747b7
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase6.results
@@ -0,0 +1,27 @@
+publish.unittestresults.ParsedUnitTestResults(
+ files=1,
+ errors=[],
+ suites=1,
+ suite_tests=1,
+ suite_skipped=0,
+ suite_failures=1,
+ suite_errors=0,
+ suite_time=0,
+ cases=[
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/jenkinsci/testcase6.xml',
+ test_file=None,
+ line=None,
+ class_name='',
+ test_name='',
+ result='failure',
+ message='OK: TestProcess.Execution.ExitCode == 0\r\nFAIL: Output differs from '
+ 'reference.\r\n Left: data\\ref\r\n Right: results\\out\r\n \r\n '
+ ' ',
+ content='OK: TestProcess.Execution.ExitCode == 0\r\nFAIL: Output differs from '
+ 'reference.\r\n Left: data\\ref\r\n Right: results\\out\r\n \r\n '
+ ' ',
+ time=36.7820544
+ )
+ ]
+)
\ No newline at end of file
diff --git a/python/test/files/xunit/mstest/jenkinsci/testcase6.xml b/python/test/files/xunit/mstest/jenkinsci/testcase6.xml
new file mode 100644
index 00000000..30f598a8
--- /dev/null
+++ b/python/test/files/xunit/mstest/jenkinsci/testcase6.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ OK: TestProcess.Execution.ExitCode == 0
+FAIL: Output differs from reference.
+ Left: data\ref
+ Right: results\out
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/test/files/xunit/mstest/pickles.junit-xml b/python/test/files/xunit/mstest/pickles.junit-xml
new file mode 100644
index 00000000..28faba47
--- /dev/null
+++ b/python/test/files/xunit/mstest/pickles.junit-xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+ System.InvalidOperationException : This is a fake failure message
+
+ at Pickles.TestHarness.xUnit.Steps.ThenTheResultShouldBePass(Int32 result) in C:\dev\pickles-results-harness\Pickles.TestHarness\Pickles.TestHarness.xUnit\Steps.cs:line 26
+ at lambda_method(Closure , IContextManager , Int32 )
+ at TechTalk.SpecFlow.Bindings.MethodBinding.InvokeAction(IContextManager contextManager, Object[] arguments, ITestTracer testTracer, TimeSpan& duration)
+ at TechTalk.SpecFlow.Bindings.StepDefinitionBinding.Invoke(IContextManager contextManager, ITestTracer testTracer, Object[] arguments, TimeSpan& duration)
+ at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStepMatch(BindingMatch match, Object[] arguments)
+ at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStep(StepArgs stepArgs)
+ at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.OnAfterLastStep()
+ at TechTalk.SpecFlow.TestRunner.CollectScenarioErrors()
+ at Pickles.TestHarness.xUnit.AdditionFeature.ScenarioCleanup() in C:\dev\pickles-results-harness\Pickles.TestHarness\Pickles.TestHarness.xUnit\Addition.feature.cs:line 0
+ at Pickles.TestHarness.xUnit.AdditionFeature.FailToAddTwoNumbers() in c:\dev\pickles-results-harness\Pickles.TestHarness\Pickles.TestHarness.xUnit\Addition.feature:line 18
+
+
+
+
+
diff --git a/python/test/files/xunit/mstest/pickles.results b/python/test/files/xunit/mstest/pickles.results
new file mode 100644
index 00000000..775cbf02
--- /dev/null
+++ b/python/test/files/xunit/mstest/pickles.results
@@ -0,0 +1,82 @@
+publish.unittestresults.ParsedUnitTestResults(
+ files=1,
+ errors=[],
+ suites=1,
+ suite_tests=4,
+ suite_skipped=0,
+ suite_failures=1,
+ suite_errors=0,
+ suite_time=0,
+ cases=[
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/pickles.xml',
+ test_file=None,
+ line=None,
+ class_name='Pickles.TestHarness.xUnit.AdditionFeature',
+ test_name='AddTwoNumbers',
+ result='success',
+ message=None,
+ content=None,
+ time=0.153
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/pickles.xml',
+ test_file=None,
+ line=None,
+ class_name='Pickles.TestHarness.xUnit.AdditionFeature',
+ test_name='AddingSeveralNumbers',
+ result='success',
+ message=None,
+ content=None,
+ time=0.006
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/pickles.xml',
+ test_file=None,
+ line=None,
+ class_name='Pickles.TestHarness.xUnit.AdditionFeature',
+ test_name='AddingSeveralNumbers',
+ result='success',
+ message=None,
+ content=None,
+ time=0.003
+ ),
+ publish.unittestresults.UnitTestCase(
+ result_file='mstest/pickles.xml',
+ test_file=None,
+ line=None,
+ class_name='Pickles.TestHarness.xUnit.AdditionFeature',
+ test_name='FailToAddTwoNumbers',
+ result='failure',
+ message='\n System.InvalidOperationException : This is a fake '
+ 'failure message\n ',
+ content='\n System.InvalidOperationException : This is a fake '
+ 'failure message\n \n at '
+ 'Pickles.TestHarness.xUnit.Steps.ThenTheResultShouldBePass(Int32 '
+ 'result) in '
+ 'C:\\dev\\pickles-results-harness\\Pickles.TestHarness\\Pickles.TestHarnes'
+ 's.xUnit\\Steps.cs:line 26\n at lambda_method(Closure , '
+ 'IContextManager , Int32 )\n at '
+ 'TechTalk.SpecFlow.Bindings.MethodBinding.InvokeAction(IContextManager'
+ ' contextManager, Object[] arguments, ITestTracer testTracer, '
+ 'TimeSpan& duration)\n at '
+ 'TechTalk.SpecFlow.Bindings.StepDefinitionBinding.Invoke(IContextManag'
+ 'er contextManager, ITestTracer testTracer, Object[] arguments, '
+ 'TimeSpan& duration)\n at '
+ 'TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStepMatch'
+ '(BindingMatch match, Object[] arguments)\n at '
+ 'TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStep(Step'
+ 'Args stepArgs)\n at '
+ 'TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.OnAfterLastStep('
+ ')\n at TechTalk.SpecFlow.TestRunner.CollectScenarioErrors()\n'
+ ' at '
+ 'Pickles.TestHarness.xUnit.AdditionFeature.ScenarioCleanup() in '
+ 'C:\\dev\\pickles-results-harness\\Pickles.TestHarness\\Pickles.TestHarnes'
+ 's.xUnit\\Addition.feature.cs:line 0\n at '
+ 'Pickles.TestHarness.xUnit.AdditionFeature.FailToAddTwoNumbers() in '
+ 'c:\\dev\\pickles-results-harness\\Pickles.TestHarness\\Pickles.TestHarnes'
+ 's.xUnit\\Addition.feature:line 18\n ',
+ time=0.023
+ )
+ ]
+)
\ No newline at end of file
diff --git a/python/test/files/xunit/mstest/pickles.xml b/python/test/files/xunit/mstest/pickles.xml
new file mode 100644
index 00000000..a90f339a
--- /dev/null
+++ b/python/test/files/xunit/mstest/pickles.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ System.InvalidOperationException : This is a fake failure message
+
+
+ at Pickles.TestHarness.xUnit.Steps.ThenTheResultShouldBePass(Int32 result) in C:\dev\pickles-results-harness\Pickles.TestHarness\Pickles.TestHarness.xUnit\Steps.cs:line 26
+ at lambda_method(Closure , IContextManager , Int32 )
+ at TechTalk.SpecFlow.Bindings.MethodBinding.InvokeAction(IContextManager contextManager, Object[] arguments, ITestTracer testTracer, TimeSpan& duration)
+ at TechTalk.SpecFlow.Bindings.StepDefinitionBinding.Invoke(IContextManager contextManager, ITestTracer testTracer, Object[] arguments, TimeSpan& duration)
+ at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStepMatch(BindingMatch match, Object[] arguments)
+ at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStep(StepArgs stepArgs)
+ at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.OnAfterLastStep()
+ at TechTalk.SpecFlow.TestRunner.CollectScenarioErrors()
+ at Pickles.TestHarness.xUnit.AdditionFeature.ScenarioCleanup() in C:\dev\pickles-results-harness\Pickles.TestHarness\Pickles.TestHarness.xUnit\Addition.feature.cs:line 0
+ at Pickles.TestHarness.xUnit.AdditionFeature.FailToAddTwoNumbers() in c:\dev\pickles-results-harness\Pickles.TestHarness\Pickles.TestHarness.xUnit\Addition.feature:line 18
+
+
+
+
+
diff --git a/python/test/test_action_script.py b/python/test/test_action_script.py
index ccd5f4ae..9d0f9ffc 100644
--- a/python/test/test_action_script.py
+++ b/python/test/test_action_script.py
@@ -134,8 +134,10 @@ def test_get_var(self):
@classmethod
def get_settings_no_default_files(cls,
junit_files_glob=None,
+ xunit_files_glob=None,
trx_files_glob=None) -> Settings:
return cls.get_settings(junit_files_glob=junit_files_glob,
+ xunit_files_glob=xunit_files_glob,
trx_files_glob=trx_files_glob)
@staticmethod
@@ -151,6 +153,7 @@ def get_settings(token='token',
fail_on_errors=True,
fail_on_failures=True,
junit_files_glob='junit-files',
+ xunit_files_glob='xunit-files',
trx_files_glob='trx-files',
time_factor=1.0,
check_name='check name',
@@ -184,6 +187,7 @@ def get_settings(token='token',
fail_on_errors=fail_on_errors,
fail_on_failures=fail_on_failures,
junit_files_glob=junit_files_glob,
+ xunit_files_glob=xunit_files_glob,
trx_files_glob=trx_files_glob,
time_factor=time_factor,
check_name=check_name,
@@ -248,15 +252,18 @@ def test_get_settings_github_retries(self):
def test_get_settings_any_files(self):
for junit in [None, 'junit-file']:
- for trx in [None, 'trx-file']:
- with self.subTest(junit=junit, trx=trx):
- any_flavour_set = any([flavour is not None for flavour in [junit, trx]])
- expected = self.get_settings(junit_files_glob=junit if any_flavour_set else '*.xml',
- trx_files_glob=trx)
- warnings = None if any_flavour_set else 'At least one of the *_FILES options has to be set! ' \
- 'Falling back to deprecated default "*.xml"'
-
- self.do_test_get_settings(JUNIT_FILES=junit, TRX_FILES=trx, expected=expected, warning=warnings)
+ for xunit in [None, 'xunit-file']:
+ for trx in [None, 'trx-file']:
+ with self.subTest(junit=junit, xunit=xunit, trx=trx):
+ any_flavour_set = any([flavour is not None for flavour in [junit, xunit, trx]])
+ expected = self.get_settings(junit_files_glob=junit if any_flavour_set else '*.xml',
+ xunit_files_glob=xunit,
+ trx_files_glob=trx)
+ warnings = None if any_flavour_set else 'At least one of the *_FILES options has to be set! ' \
+ 'Falling back to deprecated default "*.xml"'
+
+ self.do_test_get_settings(JUNIT_FILES=junit, XUNIT_FILES=xunit, TRX_FILES=trx,
+ expected=expected, warning=warnings)
def test_get_settings_junit_files(self):
self.do_test_get_settings_no_default_files(JUNIT_FILES='file', expected=self.get_settings_no_default_files(junit_files_glob='file'))
@@ -269,6 +276,16 @@ def test_get_settings_junit_files(self):
self.do_test_get_settings_no_default_files(JUNIT_FILES=None, FILES='file\nfile2', expected=self.get_settings_no_default_files(junit_files_glob='file\nfile2'), warning=['Option FILES is deprecated, please use JUNIT_FILES instead!', 'At least one of the *_FILES options has to be set! Falling back to deprecated default "*.xml"'])
self.do_test_get_settings_no_default_files(JUNIT_FILES=None, FILES=None, expected=self.get_settings_no_default_files(junit_files_glob='*.xml'), warning='At least one of the *_FILES options has to be set! Falling back to deprecated default "*.xml"')
+ def test_get_settings_xunit_files(self):
+ self.do_test_get_settings_no_default_files(XUNIT_FILES='file', expected=self.get_settings_no_default_files(xunit_files_glob='file'))
+ self.do_test_get_settings_no_default_files(XUNIT_FILES='file\nfile2', expected=self.get_settings_no_default_files(xunit_files_glob='file\nfile2'))
+ self.do_test_get_settings_no_default_files(XUNIT_FILES=None, expected=self.get_settings_no_default_files(xunit_files_glob=None, junit_files_glob='*.xml'), warning='At least one of the *_FILES options has to be set! Falling back to deprecated default "*.xml"')
+
+ def test_get_settings_trx_files(self):
+ self.do_test_get_settings_no_default_files(TRX_FILES='file', expected=self.get_settings_no_default_files(trx_files_glob='file'))
+ self.do_test_get_settings_no_default_files(TRX_FILES='file\nfile2', expected=self.get_settings_no_default_files(trx_files_glob='file\nfile2'))
+ self.do_test_get_settings_no_default_files(TRX_FILES=None, expected=self.get_settings_no_default_files(trx_files_glob=None, junit_files_glob='*.xml'), warning='At least one of the *_FILES options has to be set! Falling back to deprecated default "*.xml"')
+
def test_get_settings_time_unit(self):
self.do_test_get_settings(TIME_UNIT=None, expected=self.get_settings(time_factor=1.0))
self.do_test_get_settings(TIME_UNIT='milliseconds', expected=self.get_settings(time_factor=0.001))
@@ -479,7 +496,7 @@ def do_test_get_settings_no_default_files(self,
expected: Settings = get_settings.__func__(),
**kwargs):
options = dict(**kwargs)
- for flavour in ['JUNIT', 'TRX']:
+ for flavour in ['JUNIT', 'XUNIT', 'TRX']:
if f'{flavour}_FILES' not in kwargs:
options[f'{flavour}_FILES'] = None
@@ -513,6 +530,7 @@ def do_test_get_settings(self,
GITHUB_REPOSITORY='repo',
COMMIT='commit', # defaults to get_commit_sha(event, event_name)
JUNIT_FILES='junit-files',
+ XUNIT_FILES='xunit-files',
TRX_FILES='trx-files',
COMMENT_TITLE='title', # defaults to check name
COMMENT_MODE='always',
@@ -797,34 +815,38 @@ def test_get_files_with_mock(self):
def test_parse_files(self):
gha = mock.MagicMock()
settings = self.get_settings(junit_files_glob=str(test_files_path / 'junit-xml' / '**' / '*.xml'),
+ xunit_files_glob=str(test_files_path / 'xunit' / '**' / '*.xml'),
trx_files_glob=str(test_files_path / 'trx' / '**' / '*.trx'))
actual = parse_files(settings, gha)
gha.warning.assert_not_called()
gha.error.assert_not_called()
- self.assertEqual(35, actual.files)
+ self.assertEqual(43, actual.files)
self.assertEqual(4, len(actual.errors))
- self.assertEqual(32, actual.suites)
- self.assertEqual(1397, actual.suite_tests)
- self.assertEqual(96, actual.suite_skipped)
- self.assertEqual(48, actual.suite_failures)
+ self.assertEqual(40, actual.suites)
+ self.assertEqual(1423, actual.suite_tests)
+ self.assertEqual(95, actual.suite_skipped)
+ self.assertEqual(56, actual.suite_failures)
self.assertEqual(7, actual.suite_errors)
- self.assertEqual(2392, actual.suite_time)
- self.assertEqual(1387, len(actual.cases))
+ self.assertEqual(2782, actual.suite_time)
+ self.assertEqual(1411, len(actual.cases))
self.assertEqual('commit', actual.commit)
def test_parse_files_no_matches(self):
gha = mock.MagicMock()
with tempfile.TemporaryDirectory() as path:
missing_junit = str(pathlib.Path(path) / 'junit-not-there')
+ missing_xunit = str(pathlib.Path(path) / 'xunit-not-there')
missing_trx = str(pathlib.Path(path) / 'trx-not-there')
settings = self.get_settings(junit_files_glob=missing_junit,
+ xunit_files_glob=missing_xunit,
trx_files_glob=missing_trx)
actual = parse_files(settings, gha)
gha.warning.assert_has_calls([
mock.call(f'Could not find any files for {missing_junit}'),
+ mock.call(f'Could not find any files for {missing_xunit}'),
mock.call(f'Could not find any files for {missing_trx}')
])
gha.error.assert_not_called()
diff --git a/python/test/test_publisher.py b/python/test/test_publisher.py
index a4fd3de9..a17ecc2c 100644
--- a/python/test/test_publisher.py
+++ b/python/test/test_publisher.py
@@ -106,6 +106,7 @@ def create_settings(comment_mode=comment_mode_always,
fail_on_errors=True,
fail_on_failures=True,
junit_files_glob='*.xml',
+ xunit_files_glob=None,
trx_files_glob=None,
time_factor=1.0,
check_name='Check Name',
diff --git a/python/test/test_xunit.py b/python/test/test_xunit.py
new file mode 100644
index 00000000..82047f07
--- /dev/null
+++ b/python/test/test_xunit.py
@@ -0,0 +1,39 @@
+import pathlib
+import sys
+import unittest
+from glob import glob
+from typing import List, Union
+
+sys.path.append(str(pathlib.Path(__file__).resolve().parent.parent))
+sys.path.append(str(pathlib.Path(__file__).resolve().parent.parent.parent))
+
+from publish.junit import JUnitTree
+from publish.xunit import parse_xunit_files
+from test_junit import JUnitXmlParseTest
+
+
+test_files_path = pathlib.Path(__file__).parent / 'files' / 'xunit'
+
+
+class TestXunit(unittest.TestCase, JUnitXmlParseTest):
+ maxDiff = None
+
+ @property
+ def test(self):
+ return self
+
+ @staticmethod
+ def _test_files_path():
+ return test_files_path
+
+ @staticmethod
+ def get_test_files() -> List[str]:
+ return glob(str(test_files_path / '**' / '*.xml'), recursive=True)
+
+ @staticmethod
+ def parse_file(filename) -> Union[JUnitTree, BaseException]:
+ return list(parse_xunit_files([filename]))[0][1]
+
+
+if __name__ == "__main__":
+ TestXunit.update_expectations()