File: conftest.py

package info (click to toggle)
python-simpy 2.3.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: buster, stretch
  • size: 11,868 kB
  • ctags: 2,403
  • sloc: python: 11,172; makefile: 145
file content (187 lines) | stat: -rw-r--r-- 6,954 bytes parent folder | download | duplicates (4)
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# coding=utf-8
"""
This module scans all ``*.rst`` files below ``docs/`` for example code. Example
code is discoved by checking for lines containing the ``.. literalinclude:: ``
directives.

An example consists of two consecutive literalinclude directives. The first
must include a ``*.py`` file and the second a ``*.out`` file. The ``*.py`` file
consists of the example code which is executed in a separate process. The
output of this process is compared to the contents of the ``*.out`` file.

"""
import os.path
import subprocess
import errno
import random

import pytest
from py._code.code import TerminalRepr
from _pytest.assertion.util import _diff_text


blacklist = ['SimRTManual.rst']
"""A list of strings with rst-files to skip."""


def pytest_collect_file(path, parent):
    """Checks if the file is a rst file and creates an :class:`ExampleFile`
    instance."""
    if path.ext != '.rst':
        return
    for item in blacklist:
        if str(path).endswith(item):
            return
    return ExampleFile(path, parent)


class ExampleFile(pytest.File):
    """Collects all examples contained in a rst-file."""

    def collect(self):
        # Collect all literal includes.
        literalincludes = []
        with self.fspath.open() as data:
            for lineno, line in enumerate(data):
                if 'literalinclude' not in line:
                    continue
                filename = line.split('::')[-1].strip()
                filepath = os.path.join(self.fspath.dirname, filename)
                literalincludes.append((lineno, filepath))

        # Check for directly following output specification.
        for idx in range(len(literalincludes) - 1):
            example_lineno, example = literalincludes[idx]
            output_lineno, output = literalincludes[idx + 1]
            if not example.endswith('.py'):
                continue
            if not output.endswith('.out'):
                continue
            yield ExampleItem(output_lineno, example, output, self)


class ExampleItem(pytest.Item):
    """Executes an example found in a rst-file."""

    def __init__(self, lineno, example, output, parent):
        pytest.Item.__init__(self, example, parent)
        self.lineno = lineno
        self.example = example
        self.output = output
        self.examplefile = os.path.join(self.fspath.dirname, self.example)
        self.outputfile = os.path.join(self.fspath.dirname, self.output)

    def runtest(self):
        # Skip if random.expovariate with the old implementation is used.
        with open(self.examplefile) as f:
            src = f.read()
        if 'expovariate' in src:
            random.seed(0)
            if int(random.expovariate(0.2)) == 0:
                pytest.skip('Old exovariate implementation.')

        # Read expected output.
        with open(self.outputfile) as f:
            expected = f.read()

        # Execute the example.
        if not hasattr(subprocess, 'check_output'):  # The case on Python 2.6
            pytest.skip('subprocess has no check_output() method.')

        output = subprocess.check_output(['python', self.examplefile],
                stderr=subprocess.STDOUT)

        if isinstance(output, bytes):  # The case on Python 3
            output = output.decode('utf8')

        if output != expected:
            # Hijack the ValueError exception to identify mismatching output.
            raise ValueError(expected, output)

    def repr_failure(self, exc_info):
        if exc_info.errisinstance((ValueError,)):
            # Output is mismatching. Create a nice diff as failure description.
            expected, output = exc_info.value.args

            message = _diff_text(expected, output)
            return ReprFailExample(self.fspath.basename, self.lineno,
                    self.outputfile, message)
        elif exc_info.errisinstance((IOError,)):
            # Something wrent wrong causing an IOError.
            if exc_info.value.errno != errno.ENOENT:
                # This isn't a file not found error so bail out and report the
                # error in detail.
                return pytest.Item.repr_failure(self, exc_info)
            # Otherwise provide a concise error description.
            return ReprFileNotFoundExample(self.fspath.basename, self.lineno,
                    self.examplefile, exc_info)
        elif exc_info.errisinstance((subprocess.CalledProcessError,)):
            # Something wrent wrong while executing the example. Provide a
            # concise error description.
            return ReprErrorExample(self.fspath.basename, self.lineno,
                    self.examplefile, exc_info)
        else:
            # Something went terribly wrong :(
            return pytest.Item.repr_failure(self, exc_info)

    def reportinfo(self):
        """Returns a description of the example."""
        return self.fspath, None, '[example %s]' % (
                os.path.relpath(self.examplefile))


class ReprFailExample(TerminalRepr):
    """Reports output mismatches in a nice and informative representation."""

    Markup = {
            '+': dict(green=True),
            '-': dict(red=True),
            '?': dict(bold=True),
    }
    """Colorize codes for the diff markup."""

    def __init__(self, filename, lineno, outputfile, message):
        self.filename = filename
        self.lineno = lineno
        self.outputfile = outputfile
        self.message = message

    def toterminal(self, tw):
        for line in self.message:
            markup = ReprFailExample.Markup.get(line[0], {})
            tw.line(line, **markup)
        tw.line('%s:%d (in %s): Unexpected output' % (self.filename,
                self.lineno, os.path.relpath(self.outputfile)))


class ReprErrorExample(TerminalRepr):
    """Reports failures in the execution of an example."""

    def __init__(self, filename, lineno, examplefile, exc_info):
        self.filename = filename
        self.lineno = lineno
        self.examplefile = examplefile
        self.exc_info = exc_info

    def toterminal(self, tw):
        tw.line('Execution failed! Captured output:', bold=True)
        tw.sep('-')
        tw.line(self.exc_info.value.output, red=True, bold=True)
        tw.line('%s:%d (%s) Example failed (exitcode=%d)' % (self.filename,
                self.lineno, os.path.relpath(self.examplefile),
                self.exc_info.value.returncode))


class ReprFileNotFoundExample(TerminalRepr):
    """Reports concise error information in the case of a file not found."""

    def __init__(self, filename, lineno, examplefile, exc_info):
        self.filename = filename
        self.lineno = lineno
        self.examplefile = examplefile
        self.exc_info = exc_info

    def toterminal(self, tw):
        tw.line(self.exc_info.value, red=True, bold=True)
        tw.line('%s:%d (%s) Example failed' % (self.filename,
                self.lineno, os.path.relpath(self.examplefile)))