aboutsummaryrefslogtreecommitdiffstats
path: root/xtesting/core/pytest.py
blob: a47ab551d5f1ca12ac902363081a06fa4df67b6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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["tests"] = 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