diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index b75b31b8..f097f004 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -199,7 +199,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_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_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)
diff --git a/README.md b/README.md
index f1e49a37..c93ee022 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@v1
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@v1
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"
```
@@ -153,11 +155,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 `!`:
@@ -174,6 +176,7 @@ See the complete list of options below.
|Option|Default Value|Description|
|:-----|:-----:|:----------|
|`junit_files`|One of `*_files` must be set|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 `!`.|
+|`xunit_files`|One of `*_files` must be set|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 `!`.|
|`trx_files`|One of `*_files` must be set|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 `!`.|
|`time_unit`|`seconds`|Time values in the XML files have this unit. Supports `seconds` and `milliseconds`.|
|`check_name`|`"Unit Test Results"`|An alternative name for the check result.|
diff --git a/action.yml b/action.yml
index 086246d6..943f40f0 100644
--- a/action.yml
+++ b/action.yml
@@ -28,6 +28,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 b5bb117d..00472f40 100644
--- a/composite/action.yml
+++ b/composite/action.yml
@@ -28,6 +28,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
@@ -152,6 +155,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 e3745d6e..253c3ab2 100644
--- a/python/publish/junit.py
+++ b/python/publish/junit.py
@@ -119,7 +119,7 @@ def close(self) -> Element:
def parse_junit_xml_files(files: Iterable[str], drop_testcases: bool = False) -> Iterable[Tuple[str, Union[JUnitXml, BaseException]]]:
- """Parses junit xml files and returns aggregated statistics as a ParsedUnitTestResults."""
+ """Parses junit xml files."""
def parse(path: str) -> Union[JUnitXml, BaseException]:
if not os.path.exists(path):
return FileNotFoundError(f'File does not exist.')
diff --git a/python/publish/publisher.py b/python/publish/publisher.py
index a05caea9..74cb02cd 100644
--- a/python/publish/publisher.py
+++ b/python/publish/publisher.py
@@ -39,6 +39,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 035dad3f..147892bd 100644
--- a/python/publish/trx.py
+++ b/python/publish/trx.py
@@ -11,7 +11,7 @@
def parse_trx_files(files: Iterable[str]) -> Iterable[Tuple[str, Union[JUnitXml, BaseException]]]:
- """Parses trx files and returns aggregated statistics as a ParsedUnitTestResults."""
+ """Parses trx files."""
def parse(path: str) -> Union[JUnitXml, BaseException]:
if not os.path.exists(path):
return FileNotFoundError(f'File does not exist.')
diff --git a/python/publish/xslt/xunit-to-junit.xslt b/python/publish/xslt/xunit-to-junit.xslt
new file mode 100644
index 00000000..71fd5109
--- /dev/null
+++ b/python/publish/xslt/xunit-to-junit.xslt
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ T
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <![CDATA[
+
+ ]]>
+
+
+
+
diff --git a/python/publish/xunit.py b/python/publish/xunit.py
new file mode 100644
index 00000000..080b44db
--- /dev/null
+++ b/python/publish/xunit.py
@@ -0,0 +1,28 @@
+import os
+import pathlib
+from typing import Iterable, Tuple, Union
+
+from junitparser import JUnitXml
+from lxml import etree
+
+
+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]) -> Iterable[Tuple[str, Union[JUnitXml, BaseException]]]:
+ """Parses xunit files."""
+ def parse(path: str) -> Union[JUnitXml, BaseException]:
+ 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)
+ junit = transform_xunit_to_junit(trx)
+ return JUnitXml.fromelem(junit.getroot())
+ except BaseException as e:
+ return e
+
+ return [(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 2f48c975..04204fa7 100644
--- a/python/publish_unit_test_results.py
+++ b/python/publish_unit_test_results.py
@@ -75,6 +75,7 @@ def expand_glob(pattern: Optional[str], gha: GithubAction) -> List[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 = []
@@ -82,6 +83,9 @@ def parse_files(settings: Settings, gha: GithubAction) -> ParsedUnitTestResultsW
# parse files
if junit_files:
elems.extend(parse_junit_xml_files(junit_files, settings.ignore_runs))
+ if xunit_files:
+ from publish.xunit import parse_xunit_files
+ elems.extend(parse_xunit_files(xunit_files))
if trx_files:
from publish.trx import parse_trx_files
elems.extend(parse_trx_files(trx_files))
@@ -275,7 +279,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}"')
@@ -318,6 +322,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/xunit/xunit.xml b/python/test/files/xunit/xunit.xml
new file mode 100644
index 00000000..a90f339a
--- /dev/null
+++ b/python/test/files/xunit/xunit.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 62922a05..7ef58876 100644
--- a/python/test/test_action_script.py
+++ b/python/test/test_action_script.py
@@ -135,8 +135,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
@@ -152,6 +154,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',
@@ -183,6 +186,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,
@@ -247,15 +251,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'))
@@ -268,6 +275,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))
@@ -462,7 +479,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
@@ -496,6 +513,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='create new', # true unless 'false'
@@ -780,18 +798,19 @@ 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'),
+ xunit_files_glob=str(test_files_path / 'xunit' / '*.xml'),
trx_files_glob=str(test_files_path / 'mstest' / '*.trx'))
actual = parse_files(settings, gha)
gha.warning.assert_not_called()
gha.error.assert_not_called()
- self.assertEqual(27, actual.files)
+ self.assertEqual(28, actual.files)
self.assertEqual(4, len(actual.errors))
- self.assertEqual(24, actual.suites)
- self.assertEqual(454, actual.suite_tests)
+ self.assertEqual(25, actual.suites)
+ self.assertEqual(458, actual.suite_tests)
self.assertEqual(57, actual.suite_skipped)
- self.assertEqual(29, actual.suite_failures)
+ self.assertEqual(30, actual.suite_failures)
self.assertEqual(7, actual.suite_errors)
self.assertEqual(2361, actual.suite_time)
self.assertEqual(444, len(actual.cases))
@@ -801,13 +820,16 @@ 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 6e0c85ed..d6f193ab 100644
--- a/python/test/test_publisher.py
+++ b/python/test/test_publisher.py
@@ -69,6 +69,7 @@ def create_settings(comment_mode=comment_mode_create,
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_trx.py b/python/test/test_trx.py
new file mode 100644
index 00000000..931e3172
--- /dev/null
+++ b/python/test/test_trx.py
@@ -0,0 +1,91 @@
+import pathlib
+import unittest
+
+from publish.junit import process_junit_xml_elems, ParsedUnitTestResults, UnitTestCase
+from publish.trx import parse_trx_files
+
+test_files_path = pathlib.Path(__file__).parent / 'files' / 'mstest'
+
+
+class TestTrx(unittest.TestCase):
+ def test_process_parse_trx_files_with_time_factor(self):
+ result_file = str(test_files_path / 'mstest.trx')
+ for time_factor in [1.0, 10.0, 60.0, 0.1, 0.001]:
+ with self.subTest(time_factor=time_factor):
+ actual = process_junit_xml_elems(parse_trx_files([result_file]), time_factor=time_factor)
+ self.assertEqual(actual,
+ ParsedUnitTestResults(
+ files=1,
+ errors=[],
+ suites=1,
+ suite_tests=4,
+ suite_skipped=0,
+ suite_failures=1,
+ suite_errors=0,
+ suite_time=int(0.1395124 * time_factor),
+ cases=[
+ UnitTestCase(
+ class_name='Pickles.TestHarness.MSTest.AdditionFeature',
+ result_file=result_file,
+ test_file=None,
+ line=None,
+ test_name='AddingSeveralNumbers_40',
+ result='success',
+ content=None,
+ message=None,
+ time=0.076891 * time_factor
+ ),
+ UnitTestCase(
+ class_name='Pickles.TestHarness.MSTest.AdditionFeature',
+ result_file=result_file,
+ test_file=None,
+ line=None,
+ test_name='AddingSeveralNumbers_60',
+ result='success',
+ content=None,
+ message=None,
+ time=0.0111534 * time_factor
+ ),
+ UnitTestCase(
+ class_name='Pickles.TestHarness.MSTest.AdditionFeature',
+ result_file=result_file,
+ test_file=None,
+ line=None,
+ test_name='AddTwoNumbers',
+ result='success',
+ content=None,
+ message=None,
+ time=0.0055623 * time_factor
+ ),
+ UnitTestCase(
+ class_name='Pickles.TestHarness.MSTest.AdditionFeature',
+ result_file=result_file,
+ test_file=None,
+ line=None,
+ test_name='FailToAddTwoNumbers',
+ result='failure',
+ content='\n'
+ ' MESSAGE:\n'
+ ' \n'
+ ' Test method Pickles.TestHarness.MSTest.AdditionFeature.FailToAddTwoNumbers threw exception:\n'
+ ' Should.Core.Exceptions.NotEqualException: Assert.NotEqual() Failure\n'
+ ' \n'
+ ' +++++++++++++++++++\n'
+ ' STACK TRACE:\n'
+ ' \n'
+ ' at Pickles.TestHarness.MSTest.Steps.ThenTheResultShouldBePass(Int32 result) in C:\\dev\\pickles-results-harness\\Pickles.TestHarness\\Pickles.TestHarness.MSTest\\Steps.cs:line 28\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(IContextManager 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(StepArgs stepArgs)\n'
+ ' at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.OnAfterLastStep()\n'
+ ' at TechTalk.SpecFlow.TestRunner.CollectScenarioErrors()\n'
+ ' at Pickles.TestHarness.MSTest.AdditionFeature.ScenarioCleanup() in C:\\dev\\pickles-results-harness\\Pickles.TestHarness\\Pickles.TestHarness.MSTest\\Addition.feature.cs:line 0\n'
+ ' at Pickles.TestHarness.MSTest.AdditionFeature.FailToAddTwoNumbers() in c:\\dev\\pickles-results-harness\\Pickles.TestHarness\\Pickles.TestHarness.MSTest\\Addition.feature:line 18\n'
+ ' ',
+ message=None,
+ time=0.0459057 * time_factor
+ )
+ ]
+ ))
diff --git a/python/test/test_xunit.py b/python/test/test_xunit.py
new file mode 100644
index 00000000..d12c7814
--- /dev/null
+++ b/python/test/test_xunit.py
@@ -0,0 +1,89 @@
+import pathlib
+import unittest
+
+from publish.junit import process_junit_xml_elems, ParsedUnitTestResults, UnitTestCase
+from publish.xunit import parse_xunit_files
+
+test_files_path = pathlib.Path(__file__).parent / 'files' / 'xunit'
+
+
+class TestXunit(unittest.TestCase):
+ def test_process_parse_xunit_files_with_time_factor(self):
+ result_file = str(test_files_path / 'xunit.xml')
+ for time_factor in [1.0, 10.0, 60.0, 0.1, 0.001]:
+ with self.subTest(time_factor=time_factor):
+ actual = process_junit_xml_elems(parse_xunit_files([result_file]), time_factor=time_factor)
+ print(actual)
+ self.assertEqual(actual,
+ ParsedUnitTestResults(
+ files=1,
+ errors=[],
+ suites=1,
+ suite_tests=4,
+ suite_skipped=0,
+ suite_failures=1,
+ suite_errors=0,
+ suite_time=int(0.867 * time_factor),
+ cases=[
+ UnitTestCase(
+ class_name='Pickles.TestHarness.AdditionFeature',
+ result_file=result_file,
+ test_file=None,
+ line=None,
+ test_name='AddingSeveralNumbers("60","70","130",System.String[])',
+ result='success',
+ content=None,
+ message=None,
+ time=0.137 * time_factor
+ ),
+ UnitTestCase(
+ class_name='Pickles.TestHarness.AdditionFeature',
+ result_file=result_file,
+ test_file=None,
+ line=None,
+ test_name='AddingSeveralNumbers("40","50","90",System.String[])',
+ result='success',
+ content=None,
+ message=None,
+ time=0.009 * time_factor
+ ),
+ UnitTestCase(
+ class_name='Pickles.TestHarness',
+ result_file=result_file,
+ test_file=None,
+ line=None,
+ test_name='AdditionFeature.AddTwoNumbers',
+ result='success',
+ content=None,
+ message=None,
+ time=0.004 * time_factor
+ ),
+ UnitTestCase(
+ class_name='Pickles.TestHarness',
+ result_file=result_file,
+ test_file=None,
+ line=None,
+ test_name='AdditionFeature.FailToAddTwoNumbers',
+ result='failure',
+ content='\n'
+ 'MESSAGE:\n'
+ '\n'
+ '+++++++++++++++++++\n'
+ 'STACK TRACE:\n'
+ '\n'
+ ' at Pickles.TestHarness.xUnit.Steps.ThenTheResultShouldBePass(Int32 result) in C:\\dev\\pickles-results-harness\\Pickles.TestHarness\\Pickles.TestHarness.NUnit\\Steps.cs:line 26\nat 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(IContextManager 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(StepArgs stepArgs)\n'
+ 'at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.OnAfterLastStep()\n'
+ 'at TechTalk.SpecFlow.TestRunner.CollectScenarioErrors()\n'
+ 'at Pickles.TestHarness.AdditionFeature.ScenarioCleanup() in C:\\dev\\pickles-results-harness\\Pickles.TestHarness\\Pickles.TestHarness.NUnit\\Addition.feature.cs:line 0\n'
+ 'at Pickles.TestHarness.AdditionFeature.FailToAddTwoNumbers() in c:\\dev\\pickles-results-harness\\Pickles.TestHarness\\Pickles.TestHarness.NUnit\\Addition.feature:line 18\n'
+ '\n'
+ ' ',
+ message=None,
+ time=0.028 * time_factor
+ )
+ ]
+ ))