src/eric7/Testing/Interfaces/PytestRunner.py

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

eric ide

mercurial