From 021246f97fbe3b445619b8144c602d236441dae1 Mon Sep 17 00:00:00 2001 From: Enrico Minack Date: Mon, 23 May 2022 22:05:35 +0200 Subject: [PATCH 01/10] Add xunit support --- .github/workflows/ci-cd.yml | 2 +- README.md | 12 +-- action.yml | 3 + composite/action.yml | 4 + python/publish/junit.py | 1 + python/publish/publisher.py | 1 + python/publish/trx.py | 1 + python/publish/xslt/xunit-to-junit.xslt | 66 ++++++++++++++++ python/publish/xunit.py | 29 +++++++ python/publish_unit_test_results.py | 9 ++- python/test/files/xunit/mstest/pickles.xml | 92 ++++++++++++++++++++++ python/test/test_action_script.py | 42 +++++++--- python/test/test_publisher.py | 1 + python/test/test_xunit.py | 88 +++++++++++++++++++++ 14 files changed, 333 insertions(+), 18 deletions(-) create mode 100644 python/publish/xslt/xunit-to-junit.xslt create mode 100644 python/publish/xunit.py create mode 100644 python/test/files/xunit/mstest/pickles.xml create mode 100644 python/test/test_xunit.py diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 3fc2ab3b..b7088436 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) 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..c23e145e 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): 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..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..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/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 @@ + + + + + + + + + + Given I have entered 50 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(50) (0.0s) + And I have entered 70 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(70) (0.0s) + When I press add + -> done: Steps.WhenIPressAdd() (0.0s) + Then the result should be 120 on the screen + -> done: Steps.ThenTheResultShouldBePass(120) (0.0s) + + + + + + + + + Given I have entered 40 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(40) (0.0s) + And I have entered 50 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(50) (0.0s) + When I press add + -> done: Steps.WhenIPressAdd() (0.0s) + Then the result should be 90 on the screen + -> done: Steps.ThenTheResultShouldBePass(90) (0.0s) + + + + + + + + + Given I have entered 60 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(60) (0.0s) + And I have entered 70 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(70) (0.0s) + When I press add + -> done: Steps.WhenIPressAdd() (0.0s) + Then the result should be 130 on the screen + -> done: Steps.ThenTheResultShouldBePass(130) (0.0s) + + + + + + + + + Given I have entered 50 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(50) (0.0s) + And I have entered -1 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(-1) (0.0s) + When I press add + -> done: Steps.WhenIPressAdd() (0.0s) + Then the result should be -50 on the screen + -> error: This is a fake failure message + + + + 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..f9bcf5b1 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,6 +815,7 @@ 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) @@ -818,13 +837,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 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..002024c0 --- /dev/null +++ b/python/test/test_xunit.py @@ -0,0 +1,88 @@ +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 / 'mstest' / 'pickles.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) + 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 + ) + ] + )) From 207304cfc14fa6044503b0f0b3aefad495a9d73f Mon Sep 17 00:00:00 2001 From: Enrico Minack Date: Tue, 24 May 2022 14:19:01 +0200 Subject: [PATCH 02/10] Add transform test --- python/test/test_xunit.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/python/test/test_xunit.py b/python/test/test_xunit.py index 002024c0..a0a83d18 100644 --- a/python/test/test_xunit.py +++ b/python/test/test_xunit.py @@ -1,13 +1,27 @@ import pathlib import unittest +from lxml import etree + from publish.junit import process_junit_xml_elems, ParsedUnitTestResults, UnitTestCase -from publish.xunit import parse_xunit_files +from publish.xunit import parse_xunit_files, transform_xunit_to_junit test_files_path = pathlib.Path(__file__).parent / 'files' / 'xunit' class TestXunit(unittest.TestCase): + def test_transform(self): + result_file = str(test_files_path / 'mstest' / 'pickles.xml') + trx = etree.parse(str(result_file)) + junit = transform_xunit_to_junit(trx) + + self.assertEqual( + str(junit), + '\n' + ' \n' + '\n' + ) + def test_process_parse_xunit_files_with_time_factor(self): result_file = str(test_files_path / 'mstest' / 'pickles.xml') for time_factor in [1.0, 10.0, 60.0, 0.1, 0.001]: From 11b5ec786b2ce51489b729b81a3995af605dfcec Mon Sep 17 00:00:00 2001 From: Enrico Minack Date: Tue, 24 May 2022 14:40:53 +0200 Subject: [PATCH 03/10] Fix xunit test --- python/publish/xslt/xunit-to-junit.xslt | 2 +- python/test/test_action_script.py | 10 ++-- python/test/test_xunit.py | 80 +++++++++++++++---------- 3 files changed, 54 insertions(+), 38 deletions(-) diff --git a/python/publish/xslt/xunit-to-junit.xslt b/python/publish/xslt/xunit-to-junit.xslt index 71fd5109..294a6309 100644 --- a/python/publish/xslt/xunit-to-junit.xslt +++ b/python/publish/xslt/xunit-to-junit.xslt @@ -16,7 +16,7 @@ T - + diff --git a/python/test/test_action_script.py b/python/test/test_action_script.py index f9bcf5b1..54c81238 100644 --- a/python/test/test_action_script.py +++ b/python/test/test_action_script.py @@ -822,15 +822,15 @@ def test_parse_files(self): gha.warning.assert_not_called() gha.error.assert_not_called() - self.assertEqual(35, actual.files) + self.assertEqual(36, actual.files) self.assertEqual(4, len(actual.errors)) - self.assertEqual(32, actual.suites) - self.assertEqual(1397, actual.suite_tests) + self.assertEqual(33, actual.suites) + self.assertEqual(1401, actual.suite_tests) self.assertEqual(96, actual.suite_skipped) - self.assertEqual(48, actual.suite_failures) + self.assertEqual(49, actual.suite_failures) self.assertEqual(7, actual.suite_errors) self.assertEqual(2392, actual.suite_time) - self.assertEqual(1387, len(actual.cases)) + self.assertEqual(1391, len(actual.cases)) self.assertEqual('commit', actual.commit) def test_parse_files_no_matches(self): diff --git a/python/test/test_xunit.py b/python/test/test_xunit.py index a0a83d18..2c8ef4da 100644 --- a/python/test/test_xunit.py +++ b/python/test/test_xunit.py @@ -18,7 +18,27 @@ def test_transform(self): self.assertEqual( str(junit), '\n' - ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' '\n' ) @@ -36,67 +56,63 @@ def test_process_parse_xunit_files_with_time_factor(self): suite_skipped=0, suite_failures=1, suite_errors=0, - suite_time=int(0.867 * time_factor), + suite_time=int(0.185 * time_factor), cases=[ UnitTestCase( - class_name='Pickles.TestHarness.AdditionFeature', + class_name='Pickles.TestHarness.xUnit.AdditionFeature', result_file=result_file, test_file=None, line=None, - test_name='AddingSeveralNumbers("60","70","130",System.String[])', + test_name='AddTwoNumbers', result='success', content=None, message=None, - time=0.137 * time_factor + time=0.153 * time_factor ), UnitTestCase( - class_name='Pickles.TestHarness.AdditionFeature', + class_name='Pickles.TestHarness.xUnit.AdditionFeature', result_file=result_file, test_file=None, line=None, - test_name='AddingSeveralNumbers("40","50","90",System.String[])', + test_name='AddingSeveralNumbers', result='success', content=None, message=None, - time=0.009 * time_factor + time=0.006 * time_factor ), UnitTestCase( - class_name='Pickles.TestHarness', + class_name='Pickles.TestHarness.xUnit.AdditionFeature', result_file=result_file, test_file=None, line=None, - test_name='AdditionFeature.AddTwoNumbers', + test_name='AddingSeveralNumbers', result='success', content=None, message=None, - time=0.004 * time_factor + time=0.003 * time_factor ), UnitTestCase( - class_name='Pickles.TestHarness', + class_name='Pickles.TestHarness.xUnit.AdditionFeature', result_file=result_file, test_file=None, line=None, - test_name='AdditionFeature.FailToAddTwoNumbers', + test_name='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 + content='', + message='\n' + ' System.InvalidOperationException : This is a fake failure message\n ', + time=0.023 * time_factor ) ] )) From e3b8d7f602cccd9b1fa9347893084822ad9bde49 Mon Sep 17 00:00:00 2001 From: Enrico Minack Date: Thu, 26 May 2022 21:44:33 +0200 Subject: [PATCH 04/10] Automated example file test supersedes transformation test --- python/test/files/update_expectations.sh | 1 + .../test/files/xunit/mstest/pickles.junit-xml | 24 +++ .../test/files/xunit/mstest/pickles.results | 81 +++++++++++ python/test/test_xunit.py | 137 ++++-------------- 4 files changed, 135 insertions(+), 108 deletions(-) create mode 100644 python/test/files/xunit/mstest/pickles.junit-xml create mode 100644 python/test/files/xunit/mstest/pickles.results 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/mstest/pickles.junit-xml b/python/test/files/xunit/mstest/pickles.junit-xml new file mode 100644 index 00000000..f44bfee9 --- /dev/null +++ b/python/test/files/xunit/mstest/pickles.junit-xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + diff --git a/python/test/files/xunit/mstest/pickles.results b/python/test/files/xunit/mstest/pickles.results new file mode 100644 index 00000000..09b87165 --- /dev/null +++ b/python/test/files/xunit/mstest/pickles.results @@ -0,0 +1,81 @@ +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='', + time=0.023 + ) + ] +) \ No newline at end of file diff --git a/python/test/test_xunit.py b/python/test/test_xunit.py index 2c8ef4da..0124bb8a 100644 --- a/python/test/test_xunit.py +++ b/python/test/test_xunit.py @@ -1,118 +1,39 @@ import pathlib +import sys import unittest +from glob import glob +from typing import List, Union -from lxml import etree +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 -from publish.junit import process_junit_xml_elems, ParsedUnitTestResults, UnitTestCase -from publish.xunit import parse_xunit_files, transform_xunit_to_junit test_files_path = pathlib.Path(__file__).parent / 'files' / 'xunit' -class TestXunit(unittest.TestCase): - def test_transform(self): - result_file = str(test_files_path / 'mstest' / 'pickles.xml') - trx = etree.parse(str(result_file)) - junit = transform_xunit_to_junit(trx) +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] - self.assertEqual( - str(junit), - '\n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - ' \n' - '\n' - ) - def test_process_parse_xunit_files_with_time_factor(self): - result_file = str(test_files_path / 'mstest' / 'pickles.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) - 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.185 * time_factor), - cases=[ - UnitTestCase( - class_name='Pickles.TestHarness.xUnit.AdditionFeature', - result_file=result_file, - test_file=None, - line=None, - test_name='AddTwoNumbers', - result='success', - content=None, - message=None, - time=0.153 * time_factor - ), - UnitTestCase( - class_name='Pickles.TestHarness.xUnit.AdditionFeature', - result_file=result_file, - test_file=None, - line=None, - test_name='AddingSeveralNumbers', - result='success', - content=None, - message=None, - time=0.006 * time_factor - ), - UnitTestCase( - class_name='Pickles.TestHarness.xUnit.AdditionFeature', - result_file=result_file, - test_file=None, - line=None, - test_name='AddingSeveralNumbers', - result='success', - content=None, - message=None, - time=0.003 * time_factor - ), - UnitTestCase( - class_name='Pickles.TestHarness.xUnit.AdditionFeature', - result_file=result_file, - test_file=None, - line=None, - test_name='FailToAddTwoNumbers', - result='failure', - content='', - message='\n' - ' System.InvalidOperationException : This is a fake failure message\n ', - time=0.023 * time_factor - ) - ] - )) +if __name__ == "__main__": + TestXunit.update_expectations() From 95612ab15baf729c2a684e1f3686b65bd5d369ee Mon Sep 17 00:00:00 2001 From: Enrico Minack Date: Fri, 27 May 2022 00:04:50 +0200 Subject: [PATCH 05/10] Make test_files_path private so it is not considered a test --- python/test/test_xunit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/test/test_xunit.py b/python/test/test_xunit.py index 0124bb8a..82047f07 100644 --- a/python/test/test_xunit.py +++ b/python/test/test_xunit.py @@ -23,7 +23,7 @@ def test(self): return self @staticmethod - def test_files_path(): + def _test_files_path(): return test_files_path @staticmethod From 989d46a772ec58dedaef0cefd6564f89f0b7279c Mon Sep 17 00:00:00 2001 From: Enrico Minack Date: Fri, 27 May 2022 13:39:39 +0200 Subject: [PATCH 06/10] Remove CData from content --- python/publish/xslt/xunit-to-junit.xslt | 2 -- python/test/files/xunit/mstest/pickles.junit-xml | 4 ++-- python/test/files/xunit/mstest/pickles.results | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/python/publish/xslt/xunit-to-junit.xslt b/python/publish/xslt/xunit-to-junit.xslt index 294a6309..9c074dba 100644 --- a/python/publish/xslt/xunit-to-junit.xslt +++ b/python/publish/xslt/xunit-to-junit.xslt @@ -57,9 +57,7 @@ - <![CDATA[ - ]]> diff --git a/python/test/files/xunit/mstest/pickles.junit-xml b/python/test/files/xunit/mstest/pickles.junit-xml index f44bfee9..bd4ac432 100644 --- a/python/test/files/xunit/mstest/pickles.junit-xml +++ b/python/test/files/xunit/mstest/pickles.junit-xml @@ -6,7 +6,7 @@ - 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) @@ -17,7 +17,7 @@ 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 index 09b87165..4a8db8c1 100644 --- a/python/test/files/xunit/mstest/pickles.results +++ b/python/test/files/xunit/mstest/pickles.results @@ -50,7 +50,7 @@ publish.unittestresults.ParsedUnitTestResults( result='failure', message='\n System.InvalidOperationException : This is a fake ' 'failure message\n ', - content='', + 's.xUnit\\Addition.feature:line 18\n ', time=0.023 ) ] From 64f1d85fade1086ccf7a7a9b484f25ea50631769 Mon Sep 17 00:00:00 2001 From: Enrico Minack Date: Mon, 6 Jun 2022 22:48:43 +0200 Subject: [PATCH 07/10] Make xunit xlst work --- python/publish/junit.py | 11 +-- python/publish/xslt/xunit-to-junit.xslt | 7 +- .../test/files/junit-xml/tst/disabled.results | 2 +- .../mstest/jenkinsci/testcase1.junit-xml | 14 ++++ .../xunit/mstest/jenkinsci/testcase1.results | 48 +++++++++++++ .../xunit/mstest/jenkinsci/testcase1.xml | 18 +++++ .../mstest/jenkinsci/testcase2.junit-xml | 8 +++ .../xunit/mstest/jenkinsci/testcase2.results | 23 +++++++ .../xunit/mstest/jenkinsci/testcase2.xml | 14 ++++ .../mstest/jenkinsci/testcase3.junit-xml | 14 ++++ .../xunit/mstest/jenkinsci/testcase3.results | 48 +++++++++++++ .../xunit/mstest/jenkinsci/testcase3.xml | 18 +++++ .../mstest/jenkinsci/testcase4.junit-xml | 4 ++ .../xunit/mstest/jenkinsci/testcase4.results | 11 +++ .../xunit/mstest/jenkinsci/testcase4.xml | 8 +++ .../mstest/jenkinsci/testcase5.junit-xml | 12 ++++ .../xunit/mstest/jenkinsci/testcase5.results | 67 +++++++++++++++++++ .../xunit/mstest/jenkinsci/testcase5.xml | 44 ++++++++++++ .../mstest/jenkinsci/testcase6.junit-xml | 15 +++++ .../xunit/mstest/jenkinsci/testcase6.results | 27 ++++++++ .../xunit/mstest/jenkinsci/testcase6.xml | 17 +++++ .../test/files/xunit/mstest/pickles.junit-xml | 2 + .../test/files/xunit/mstest/pickles.results | 3 +- 23 files changed, 424 insertions(+), 11 deletions(-) create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase1.junit-xml create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase1.results create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase1.xml create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase2.junit-xml create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase2.results create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase2.xml create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase3.junit-xml create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase3.results create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase3.xml create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase4.junit-xml create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase4.results create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase4.xml create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase5.junit-xml create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase5.results create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase5.xml create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase6.junit-xml create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase6.results create mode 100644 python/test/files/xunit/mstest/jenkinsci/testcase6.xml diff --git a/python/publish/junit.py b/python/publish/junit.py index c23e145e..b1d016ff 100644 --- a/python/publish/junit.py +++ b/python/publish/junit.py @@ -173,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/xslt/xunit-to-junit.xslt b/python/publish/xslt/xunit-to-junit.xslt index 9c074dba..6ee68d64 100644 --- a/python/publish/xslt/xunit-to-junit.xslt +++ b/python/publish/xslt/xunit-to-junit.xslt @@ -1,7 +1,7 @@ - + @@ -16,8 +16,7 @@ T - - + @@ -29,7 +28,6 @@ - @@ -57,6 +55,7 @@ + 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/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 index bd4ac432..28faba47 100644 --- a/python/test/files/xunit/mstest/pickles.junit-xml +++ b/python/test/files/xunit/mstest/pickles.junit-xml @@ -7,6 +7,8 @@ + 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) diff --git a/python/test/files/xunit/mstest/pickles.results b/python/test/files/xunit/mstest/pickles.results index 4a8db8c1..775cbf02 100644 --- a/python/test/files/xunit/mstest/pickles.results +++ b/python/test/files/xunit/mstest/pickles.results @@ -50,7 +50,8 @@ publish.unittestresults.ParsedUnitTestResults( result='failure', message='\n System.InvalidOperationException : This is a fake ' 'failure message\n ', - content='\n at ' + 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' From f14fc8b4b5830cacb62601ec3f8ee441b7c9a1d3 Mon Sep 17 00:00:00 2001 From: Enrico Minack Date: Mon, 6 Jun 2022 23:10:59 +0200 Subject: [PATCH 08/10] Add one more xunit example file --- python/test/files/xunit/README.md | 1 + .../test/files/xunit/mstest/fixie.junit-xml | 26 ++++++ python/test/files/xunit/mstest/fixie.results | 93 +++++++++++++++++++ python/test/files/xunit/mstest/fixie.xml | 35 +++++++ 4 files changed, 155 insertions(+) create mode 100644 python/test/files/xunit/README.md create mode 100644 python/test/files/xunit/mstest/fixie.junit-xml create mode 100644 python/test/files/xunit/mstest/fixie.results create mode 100644 python/test/files/xunit/mstest/fixie.xml 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 From d09fcf736b24572431854e966f9c87239834a5fc Mon Sep 17 00:00:00 2001 From: Enrico Minack Date: Mon, 6 Jun 2022 23:12:24 +0200 Subject: [PATCH 09/10] Update example file stats --- python/test/test_action_script.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/python/test/test_action_script.py b/python/test/test_action_script.py index 54c81238..9d0f9ffc 100644 --- a/python/test/test_action_script.py +++ b/python/test/test_action_script.py @@ -822,15 +822,15 @@ def test_parse_files(self): gha.warning.assert_not_called() gha.error.assert_not_called() - self.assertEqual(36, actual.files) + self.assertEqual(43, actual.files) self.assertEqual(4, len(actual.errors)) - self.assertEqual(33, actual.suites) - self.assertEqual(1401, actual.suite_tests) - self.assertEqual(96, actual.suite_skipped) - self.assertEqual(49, 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(1391, 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): From 1ae95bbe0bf3dcf2c3ee076fcbb9e0b4b6ab5a4c Mon Sep 17 00:00:00 2001 From: Enrico Minack Date: Tue, 14 Jun 2022 21:02:03 +0200 Subject: [PATCH 10/10] Add xunit example files to ci --- .github/workflows/ci-cd.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index b7088436..5ef02391 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -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