eric7/Testing/Interfaces/PytestRunner.py

branch
unittest
changeset 9089
b48a6d0f6309
parent 9066
a219ade50f7c
child 9192
a763d57e23bc
equal deleted inserted replaced
9086:4dc05dd170a5 9089:b48a6d0f6309
6 """ 6 """
7 Module implementing the test runner script for the 'pytest' framework. 7 Module implementing the test runner script for the 'pytest' framework.
8 """ 8 """
9 9
10 import json 10 import json
11 import os
11 import sys 12 import sys
12 13 import time
13 # TODO: implement 'pytest' support in PytestRunner 14
15 sys.path.insert(
16 2,
17 os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
18 )
14 19
15 20
16 class GetPluginVersionsPlugin(): 21 class GetPluginVersionsPlugin():
17 """ 22 """
18 Class implementing a pytest plugin to extract the version info of all 23 Class implementing a pytest plugin to extract the version info of all
49 @rtype list of dict 54 @rtype list of dict
50 """ 55 """
51 return self.versions 56 return self.versions
52 57
53 58
59 class EricPlugin():
60 """
61 Class implementing a pytest plugin which reports the data in a format
62 suitable for the PytestExecutor.
63 """
64 def __init__(self, writer):
65 """
66 Constructor
67
68 @param writer reference to the object to write the results to
69 @type EricJsonWriter
70 """
71 self.__writer = writer
72
73 self.__testsRun = 0
74
75 def __initializeReportData(self):
76 """
77 Private method to initialize attributes for data collection.
78 """
79 self.__status = '---'
80 self.__duration = 0
81 self.__report = []
82 self.__reportPhase = ""
83 self.__sections = []
84 self.__hadError = False
85 self.__wasSkipped = False
86 self.__wasXfail = False
87
88 def pytest_report_header(self, config, startdir):
89 """
90 Public method called by pytest before any reporting.
91
92 @param config reference to the configuration object
93 @type Config
94 @param startdir starting directory
95 @type LocalPath
96 """
97 self.__writer.write({
98 "event": "config",
99 "root": str(config.rootdir)
100 })
101
102 def pytest_collectreport(self, report):
103 """
104 Public method called by pytest after the tests have been collected.
105
106 @param report reference to the report object
107 @type CollectReport
108 """
109 if report.outcome == "failed":
110 self.__writer.write({
111 "event": "collecterror",
112 "nodeid": report.nodeid,
113 "report": str(report.longrepr),
114 })
115
116 def pytest_itemcollected(self, item):
117 """
118 Public malled by pytest after a test item has been collected.
119
120 @param item reference to the collected test item
121 @type Item
122 """
123 self.__writer.write({
124 "event": "collected",
125 "nodeid": item.nodeid,
126 "name": item.name,
127 })
128
129 def pytest_runtest_logstart(self, nodeid, location):
130 """
131 Public method called by pytest before running a test.
132
133 @param nodeid node id of the test item
134 @type str
135 @param location tuple containing the file name, the line number and
136 the test name
137 @type tuple of (str, int, str)
138 """
139 self.__testsRun += 1
140
141 self.__writer.write({
142 "event": "starttest",
143 "nodeid": nodeid,
144 })
145
146 self.__initializeReportData()
147
148 def pytest_runtest_logreport(self, report):
149 """
150 Public method called by pytest when a test phase (setup, call and
151 teardown) has been completed.
152
153 @param report reference to the test report object
154 @type TestReport
155 """
156 if report.when == "call":
157 self.__status = report.outcome
158 self.__duration = report.duration
159 else:
160 if report.outcome == "failed":
161 self.__hadError = True
162 elif report.outcome == "skipped":
163 self.__wasSkipped = True
164
165 if hasattr(report, "wasxfail"):
166 self.__wasXfail = True
167 self.__report.append(report.wasxfail)
168 self.__reportPhase = report.when
169
170 self.__sections = report.sections
171
172 if report.longrepr:
173 self.__reportPhase = report.when
174 if (
175 hasattr(report.longrepr, "reprcrash") and
176 report.longrepr.reprcrash is not None
177 ):
178 self.__report.append(
179 report.longrepr.reprcrash.message)
180 if isinstance(report.longrepr, tuple):
181 self.__report.append(report.longrepr[2])
182 elif isinstance(report.longrepr, str):
183 self.__report.append(report.longrepr)
184 else:
185 self.__report.append(str(report.longrepr))
186
187 def pytest_runtest_logfinish(self, nodeid, location):
188 """
189 Public method called by pytest after a test has been completed.
190
191 @param nodeid node id of the test item
192 @type str
193 @param location tuple containing the file name, the line number and
194 the test name
195 @type tuple of (str, int, str)
196 """
197 if self.__wasXfail:
198 self.__status = (
199 "xpassed"
200 if self.__status == "passed" else
201 "xfailed"
202 )
203 elif self.__wasSkipped:
204 self.__status = "skipped"
205
206 data = {
207 "event": "result",
208 "status": self.__status,
209 "with_error": self.__hadError,
210 "sections": self.__sections,
211 "duration_s": self.__duration,
212 "nodeid": nodeid,
213 "filename": location[0],
214 "linenumber": location[1],
215 "report_phase": self.__reportPhase,
216 }
217 if self.__report:
218 messageLines = self.__report[0].rstrip().splitlines()
219 data["message"] = messageLines[0]
220 data["report"] = "\n".join(self.__report)
221
222 self.__writer.write(data)
223
224 def pytest_sessionstart(self, session):
225 """
226 Public method called by pytest before performing collection and
227 entering the run test loop.
228
229 @param session reference to the session object
230 @type Session
231 """
232 self.__totalStartTime = time.monotonic_ns()
233 self.__testsRun = 0
234
235 def pytest_sessionfinish(self, session, exitstatus):
236 """
237 Public method called by pytest after the whole test run finished.
238
239 @param session reference to the session object
240 @type Session
241 @param exitstatus exit status
242 @type int or ExitCode
243 """
244 stopTime = time.monotonic_ns()
245 duration = (stopTime - self.__totalStartTime) / 1_000_000_000 # s
246
247 self.__writer.write({
248 "event": "finished",
249 "duration_s": duration,
250 "tests": self.__testsRun,
251 })
252
253
54 def getVersions(): 254 def getVersions():
55 """ 255 """
56 Function to determine the framework version and versions of all available 256 Function to determine the framework version and versions of all available
57 plugins. 257 plugins.
58 """ 258 """
59 try: 259 try:
60 import pytest # __IGNORE_WARNING__ 260 import pytest
61 versions = { 261 versions = {
62 "name": "pytest", 262 "name": "pytest",
63 "version": pytest.__version__, 263 "version": pytest.__version__,
64 "plugins": [], 264 "plugins": [],
65 } 265 }
86 sys.exit(1) 286 sys.exit(1)
87 287
88 elif command == "versions": 288 elif command == "versions":
89 getVersions() 289 getVersions()
90 290
291 elif command == "runtest":
292 import pytest
293 from EricNetwork.EricJsonStreamWriter import EricJsonWriter
294 writer = EricJsonWriter(sys.argv[2], int(sys.argv[3]))
295 pytest.main(sys.argv[4:], plugins=[EricPlugin(writer)])
296 writer.close()
297 sys.exit(0)
298
91 sys.exit(42) 299 sys.exit(42)
92 300
93 # 301 #
94 # eflag: noqa = M801 302 # eflag: noqa = M801

eric ide

mercurial