diff options
-rw-r--r-- | api/apidoc/xtesting.core.pytest.rst | 7 | ||||
-rw-r--r-- | api/apidoc/xtesting.core.rst | 1 | ||||
-rw-r--r-- | docker/core/testcases.yaml | 11 | ||||
-rw-r--r-- | requirements.txt | 2 | ||||
-rw-r--r-- | setup.cfg | 1 | ||||
-rw-r--r-- | test-requirements.txt | 2 | ||||
-rw-r--r-- | upper-constraints.txt | 3 | ||||
-rw-r--r-- | xtesting/ci/testcases.yaml | 11 | ||||
-rw-r--r-- | xtesting/core/pytest.py | 101 |
9 files changed, 134 insertions, 5 deletions
diff --git a/api/apidoc/xtesting.core.pytest.rst b/api/apidoc/xtesting.core.pytest.rst new file mode 100644 index 00000000..fcc649f7 --- /dev/null +++ b/api/apidoc/xtesting.core.pytest.rst @@ -0,0 +1,7 @@ +xtesting\.core\.pytest module +============================= + +.. automodule:: xtesting.core.pytest + :members: + :undoc-members: + :show-inheritance: diff --git a/api/apidoc/xtesting.core.rst b/api/apidoc/xtesting.core.rst index 5707a28c..30276712 100644 --- a/api/apidoc/xtesting.core.rst +++ b/api/apidoc/xtesting.core.rst @@ -15,6 +15,7 @@ Submodules xtesting.core.behaveframework xtesting.core.campaign xtesting.core.feature + xtesting.core.pytest xtesting.core.robotframework xtesting.core.testcase xtesting.core.unit diff --git a/docker/core/testcases.yaml b/docker/core/testcases.yaml index 360869d4..deee73b6 100644 --- a/docker/core/testcases.yaml +++ b/docker/core/testcases.yaml @@ -78,3 +78,14 @@ tiers: args: private_data_dir: /usr/lib/python3.10/site-packages/xtesting/samples playbook: helloworld.yml + - case_name: nineth + project_name: xtesting + enabled: true + criteria: 100 + blocking: true + clean_flag: false + description: '' + run: + name: pytest + args: + dir: /usr/lib/python3.10/site-packages/xtesting/samples/fourth.py diff --git a/requirements.txt b/requirements.txt index 71596528..1db79684 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,3 +17,5 @@ junitxml boto3 # Apache-2.0 lxml!=3.7.0 # BSD ansible-runner!=1.3.5 # Apache 2.0 +pytest # MIT +pytest-html #MPL-2.0 @@ -31,6 +31,7 @@ xtesting.testcase = second = xtesting.samples.second:Test mts = xtesting.core.mts:MTSLauncher ansible = xtesting.core.ansible:Ansible + pytest = xtesting.core.pytest:Pytest [build_sphinx] all_files = 1 diff --git a/test-requirements.txt b/test-requirements.txt index 4be1d670..22457d0f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,8 +3,6 @@ # process, which may cause wedges in the gate later. coverage!=4.4 # Apache-2.0 mock!=4.0.0,!=4.0.1 # BSD -pytest # MIT -pytest-html #MPL-2.0 pytest-cov flake8 # MIT pylint # GPLv2 diff --git a/upper-constraints.txt b/upper-constraints.txt index 59d9f4d5..e15894aa 100644 --- a/upper-constraints.txt +++ b/upper-constraints.txt @@ -5,7 +5,4 @@ behave-html-formatter===0.9.8 pylint===2.11.1 flake8===4.0.1 ansible-lint===5.2.1 -pytest===7.1.2 -pytest-cov===3.0.0 -pytest-html===3.1.1 pre-commit===3.1.1 diff --git a/xtesting/ci/testcases.yaml b/xtesting/ci/testcases.yaml index ead3441c..488bf953 100644 --- a/xtesting/ci/testcases.yaml +++ b/xtesting/ci/testcases.yaml @@ -99,3 +99,14 @@ tiers: args: private_data_dir: /usr/lib/python3.10/site-packages/xtesting/samples playbook: helloworld.yml + - case_name: nineth + project_name: xtesting + enabled: true + criteria: 100 + blocking: true + clean_flag: false + description: '' + run: + name: pytest + args: + dir: /usr/lib/python3.10/site-packages/xtesting/samples/fourth.py diff --git a/xtesting/core/pytest.py b/xtesting/core/pytest.py new file mode 100644 index 00000000..1dadf300 --- /dev/null +++ b/xtesting/core/pytest.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python + +# Copyright (c) 2023 Orange and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +"""Define classes required to run any Pytest suites.""" + +import contextlib +import io +import logging +import time + +import pytest + +from xtesting.core import testcase + + +@pytest.hookimpl(hookwrapper=True) +def pytest_runtest_makereport(item, call): + """Collect all Pytest results""" + # pylint: disable=unused-argument + yreport = yield + report = yreport.get_result() + if report.when == 'call': + test = {"name": report.nodeid, "status": report.outcome.upper()} + if report.passed: + Pytest.passed += 1 + elif report.failed: + Pytest.failed += 1 + test['failure'] = report.longreprtext + Pytest.tests.append(test) + + +class Pytest(testcase.TestCase): + + """Pytest driver + + The pytest driver can be used on every test set written for pytest. + Given a pytest package that is launched with the command: + + .. code-block:: shell + + pytest --opt1 arg1 --opt2 testdir + + it can be executed by xtesting with the following testcase.yaml: + + .. code-block:: yaml + + run: + name: pytest + args: + opt1: arg1 + opt2: arg2 + """ + + __logger = logging.getLogger(__name__) + tests = [] + passed = 0 + failed = 0 + + def run(self, **kwargs): + # parsing args + # - 'dir' is mandatory + # - 'options' is an optional list or a dict flatten to a list + status = self.EX_RUN_ERROR + self.start_time = time.time() + try: + pydir = kwargs.pop('dir') + options = kwargs.pop('options', {}) + options['html'] = f'{self.res_dir}/results.html' + options['junitxml'] = f'{self.res_dir}/results.xml' + if 'tb' not in options: + options['tb'] = 'no' + options = [ + str(item) for opt in zip( + [f'--{k}' if len(str(k)) > 1 else + f'-{k}' for k in options.keys()], + options.values()) + for item in opt if item is not None] + with contextlib.redirect_stdout(io.StringIO()) as output: + pytest.main( + args=[pydir] + ['-p', __name__] + options) + with open(f'{self.res_dir}/stdout.log', + 'w', encoding='utf-8') as output_file: + output_file.write(output.getvalue()) + self.__logger.info( + "\n\n %s \n", + output.getvalue().splitlines()[-1].replace('=', '')) + self.details = Pytest.tests + if Pytest.passed + Pytest.failed: + self.result = Pytest.passed / ( + Pytest.passed + Pytest.failed) * 100 + status = self.EX_OK + except Exception: # pylint: # pylint: disable=broad-except + self.__logger.exception("Cannot execute pytest") + self.stop_time = time.time() + return status |