src/eric7/DataViews/PyCoverageDialog.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9238
a7cbf3d61498
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
11 import time 11 import time
12 12
13 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl 13 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl
14 from PyQt6.QtGui import QDesktopServices 14 from PyQt6.QtGui import QDesktopServices
15 from PyQt6.QtWidgets import ( 15 from PyQt6.QtWidgets import (
16 QDialog, QDialogButtonBox, QMenu, QHeaderView, QTreeWidgetItem, 16 QDialog,
17 QApplication 17 QDialogButtonBox,
18 QMenu,
19 QHeaderView,
20 QTreeWidgetItem,
21 QApplication,
18 ) 22 )
19 23
20 from EricWidgets import EricMessageBox 24 from EricWidgets import EricMessageBox
21 from EricWidgets.EricApplication import ericApp 25 from EricWidgets.EricApplication import ericApp
22 26
28 32
29 33
30 class PyCoverageDialog(QDialog, Ui_PyCoverageDialog): 34 class PyCoverageDialog(QDialog, Ui_PyCoverageDialog):
31 """ 35 """
32 Class implementing a dialog to display the collected code coverage data. 36 Class implementing a dialog to display the collected code coverage data.
33 37
34 @signal openFile(str) emitted to open the given file in an editor 38 @signal openFile(str) emitted to open the given file in an editor
35 """ 39 """
40
36 openFile = pyqtSignal(str) 41 openFile = pyqtSignal(str)
37 42
38 def __init__(self, parent=None): 43 def __init__(self, parent=None):
39 """ 44 """
40 Constructor 45 Constructor
41 46
42 @param parent parent widget 47 @param parent parent widget
43 @type QWidget 48 @type QWidget
44 """ 49 """
45 super().__init__(parent) 50 super().__init__(parent)
46 self.setupUi(self) 51 self.setupUi(self)
47 self.setWindowFlags(Qt.WindowType.Window) 52 self.setWindowFlags(Qt.WindowType.Window)
48 53
49 self.buttonBox.button( 54 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False)
50 QDialogButtonBox.StandardButton.Close).setEnabled(False) 55 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True)
51 self.buttonBox.button( 56
52 QDialogButtonBox.StandardButton.Cancel).setDefault(True) 57 self.summaryList.headerItem().setText(self.summaryList.columnCount(), "")
53
54 self.summaryList.headerItem().setText(
55 self.summaryList.columnCount(), "")
56 self.resultList.headerItem().setText(self.resultList.columnCount(), "") 58 self.resultList.headerItem().setText(self.resultList.columnCount(), "")
57 59
58 self.cancelled = False 60 self.cancelled = False
59 self.path = '.' 61 self.path = "."
60 self.reload = False 62 self.reload = False
61 63
62 self.excludeList = ['# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]'] 64 self.excludeList = ["# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]"]
63 65
64 self.__reportsMenu = QMenu(self.tr("Create Report"), self) 66 self.__reportsMenu = QMenu(self.tr("Create Report"), self)
65 self.__reportsMenu.addAction(self.tr("HTML Report"), self.__htmlReport) 67 self.__reportsMenu.addAction(self.tr("HTML Report"), self.__htmlReport)
66 self.__reportsMenu.addSeparator() 68 self.__reportsMenu.addSeparator()
67 self.__reportsMenu.addAction(self.tr("JSON Report"), self.__jsonReport) 69 self.__reportsMenu.addAction(self.tr("JSON Report"), self.__jsonReport)
68 self.__reportsMenu.addAction(self.tr("LCOV Report"), self.__lcovReport) 70 self.__reportsMenu.addAction(self.tr("LCOV Report"), self.__lcovReport)
69 71
70 self.__menu = QMenu(self) 72 self.__menu = QMenu(self)
71 self.__menu.addSeparator() 73 self.__menu.addSeparator()
72 self.openAct = self.__menu.addAction( 74 self.openAct = self.__menu.addAction(self.tr("Open"), self.__openFile)
73 self.tr("Open"), self.__openFile)
74 self.__menu.addSeparator() 75 self.__menu.addSeparator()
75 self.__menu.addMenu(self.__reportsMenu) 76 self.__menu.addMenu(self.__reportsMenu)
76 self.__menu.addSeparator() 77 self.__menu.addSeparator()
77 self.__menu.addAction(self.tr('Erase Coverage Info'), self.__erase) 78 self.__menu.addAction(self.tr("Erase Coverage Info"), self.__erase)
78 self.resultList.setContextMenuPolicy( 79 self.resultList.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
79 Qt.ContextMenuPolicy.CustomContextMenu) 80 self.resultList.customContextMenuRequested.connect(self.__showContextMenu)
80 self.resultList.customContextMenuRequested.connect( 81
81 self.__showContextMenu)
82
83 def __format_lines(self, lines): 82 def __format_lines(self, lines):
84 """ 83 """
85 Private method to format a list of integers into string by coalescing 84 Private method to format a list of integers into string by coalescing
86 groups. 85 groups.
87 86
88 @param lines list of integers 87 @param lines list of integers
89 @type list of int 88 @type list of int
90 @return string representing the list 89 @return string representing the list
91 @rtype str 90 @rtype str
92 """ 91 """
93 pairs = [] 92 pairs = []
94 lines.sort() 93 lines.sort()
95 maxValue = lines[-1] 94 maxValue = lines[-1]
96 start = None 95 start = None
97 96
98 i = lines[0] 97 i = lines[0]
99 while i <= maxValue: 98 while i <= maxValue:
100 try: 99 try:
101 if start is None: 100 if start is None:
102 start = i 101 start = i
109 if ind + 1 >= len(lines): 108 if ind + 1 >= len(lines):
110 break 109 break
111 i = lines[ind + 1] 110 i = lines[ind + 1]
112 if start: 111 if start:
113 pairs.append((start, end)) 112 pairs.append((start, end))
114 113
115 def stringify(pair): 114 def stringify(pair):
116 """ 115 """
117 Private helper function to generate a string representation of a 116 Private helper function to generate a string representation of a
118 pair. 117 pair.
119 118
120 @param pair pair of integers 119 @param pair pair of integers
121 @type tuple of (int, int 120 @type tuple of (int, int
122 @return representation of the pair 121 @return representation of the pair
123 @rtype str 122 @rtype str
124 """ 123 """
125 start, end = pair 124 start, end = pair
126 if start == end: 125 if start == end:
127 return "{0:d}".format(start) 126 return "{0:d}".format(start)
128 else: 127 else:
129 return "{0:d}-{1:d}".format(start, end) 128 return "{0:d}-{1:d}".format(start, end)
130 129
131 return ", ".join(map(stringify, pairs)) 130 return ", ".join(map(stringify, pairs))
132 131
133 def __createResultItem(self, file, statements, executed, coverage, 132 def __createResultItem(
134 excluded, missing): 133 self, file, statements, executed, coverage, excluded, missing
134 ):
135 """ 135 """
136 Private method to create an entry in the result list. 136 Private method to create an entry in the result list.
137 137
138 @param file filename of file 138 @param file filename of file
139 @type str 139 @type str
140 @param statements number of statements 140 @param statements number of statements
141 @type int 141 @type int
142 @param executed number of executed statements 142 @param executed number of executed statements
146 @param excluded list of excluded lines 146 @param excluded list of excluded lines
147 @type str 147 @type str
148 @param missing list of lines without coverage 148 @param missing list of lines without coverage
149 @type str 149 @type str
150 """ 150 """
151 itm = QTreeWidgetItem(self.resultList, [ 151 itm = QTreeWidgetItem(
152 file, 152 self.resultList,
153 str(statements), 153 [
154 str(executed), 154 file,
155 "{0:.0f}%".format(coverage), 155 str(statements),
156 excluded, 156 str(executed),
157 missing 157 "{0:.0f}%".format(coverage),
158 ]) 158 excluded,
159 missing,
160 ],
161 )
159 for col in range(1, 4): 162 for col in range(1, 4):
160 itm.setTextAlignment(col, Qt.AlignmentFlag.AlignRight) 163 itm.setTextAlignment(col, Qt.AlignmentFlag.AlignRight)
161 if statements != executed: 164 if statements != executed:
162 font = itm.font(0) 165 font = itm.font(0)
163 font.setBold(True) 166 font.setBold(True)
164 for col in range(itm.columnCount()): 167 for col in range(itm.columnCount()):
165 itm.setFont(col, font) 168 itm.setFont(col, font)
166 169
167 def start(self, cfn, fn): 170 def start(self, cfn, fn):
168 """ 171 """
169 Public slot to start the coverage data evaluation. 172 Public slot to start the coverage data evaluation.
170 173
171 @param cfn basename of the coverage file 174 @param cfn basename of the coverage file
172 @type str 175 @type str
173 @param fn file or list of files or directory to be checked 176 @param fn file or list of files or directory to be checked
174 @type str or list of str 177 @type str or list of str
175 """ 178 """
176 # initialize the dialog 179 # initialize the dialog
177 self.resultList.clear() 180 self.resultList.clear()
178 self.summaryList.clear() 181 self.summaryList.clear()
179 self.cancelled = False 182 self.cancelled = False
180 self.buttonBox.button( 183 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False)
181 QDialogButtonBox.StandardButton.Close).setEnabled(False) 184 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(True)
182 self.buttonBox.button( 185 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True)
183 QDialogButtonBox.StandardButton.Cancel).setEnabled(True) 186
184 self.buttonBox.button(
185 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
186
187 self.__cfn = cfn 187 self.__cfn = cfn
188 self.__fn = fn 188 self.__fn = fn
189 189
190 self.cfn = ( 190 self.cfn = (
191 cfn 191 cfn
192 if cfn.endswith(".coverage") else 192 if cfn.endswith(".coverage")
193 "{0}.coverage".format(os.path.splitext(cfn)[0]) 193 else "{0}.coverage".format(os.path.splitext(cfn)[0])
194 ) 194 )
195 195
196 if isinstance(fn, list): 196 if isinstance(fn, list):
197 files = fn 197 files = fn
198 self.path = os.path.dirname(cfn) 198 self.path = os.path.dirname(cfn)
199 elif os.path.isdir(fn): 199 elif os.path.isdir(fn):
200 files = Utilities.direntries(fn, True, '*.py', False) 200 files = Utilities.direntries(fn, True, "*.py", False)
201 self.path = fn 201 self.path = fn
202 else: 202 else:
203 files = [fn] 203 files = [fn]
204 self.path = os.path.dirname(cfn) 204 self.path = os.path.dirname(cfn)
205 files.sort() 205 files.sort()
206 206
207 cover = Coverage(data_file=self.cfn) 207 cover = Coverage(data_file=self.cfn)
208 cover.load() 208 cover.load()
209 209
210 # set the exclude pattern 210 # set the exclude pattern
211 self.excludeCombo.clear() 211 self.excludeCombo.clear()
212 self.excludeCombo.addItems(self.excludeList) 212 self.excludeCombo.addItems(self.excludeList)
213 213
214 self.checkProgress.setMaximum(len(files)) 214 self.checkProgress.setMaximum(len(files))
215 QApplication.processEvents() 215 QApplication.processEvents()
216 216
217 total_statements = 0 217 total_statements = 0
218 total_executed = 0 218 total_executed = 0
219 total_exceptions = 0 219 total_exceptions = 0
220 220
221 cover.exclude(self.excludeList[0]) 221 cover.exclude(self.excludeList[0])
222 222
223 try: 223 try:
224 # disable updates of the list for speed 224 # disable updates of the list for speed
225 self.resultList.setUpdatesEnabled(False) 225 self.resultList.setUpdatesEnabled(False)
226 self.resultList.setSortingEnabled(False) 226 self.resultList.setSortingEnabled(False)
227 227
228 # now go through all the files 228 # now go through all the files
229 now = time.monotonic() 229 now = time.monotonic()
230 for progress, file in enumerate(files, start=1): 230 for progress, file in enumerate(files, start=1):
231 if self.cancelled: 231 if self.cancelled:
232 return 232 return
233 233
234 try: 234 try:
235 statements, excluded, missing, readable = ( 235 statements, excluded, missing, readable = cover.analysis2(file)[1:]
236 cover.analysis2(file)[1:]) 236 readableEx = excluded and self.__format_lines(excluded) or ""
237 readableEx = (excluded and self.__format_lines(excluded) or
238 '')
239 n = len(statements) 237 n = len(statements)
240 m = n - len(missing) 238 m = n - len(missing)
241 pc = 100.0 * m / n if n > 0 else 100.0 239 pc = 100.0 * m / n if n > 0 else 100.0
242 self.__createResultItem( 240 self.__createResultItem(
243 file, str(n), str(m), pc, readableEx, readable) 241 file, str(n), str(m), pc, readableEx, readable
244 242 )
243
245 total_statements += n 244 total_statements += n
246 total_executed += m 245 total_executed += m
247 except CoverageException: 246 except CoverageException:
248 total_exceptions += 1 247 total_exceptions += 1
249 248
250 self.checkProgress.setValue(progress) 249 self.checkProgress.setValue(progress)
251 if time.monotonic() - now > 0.01: 250 if time.monotonic() - now > 0.01:
252 QApplication.processEvents() 251 QApplication.processEvents()
253 now = time.monotonic() 252 now = time.monotonic()
254 finally: 253 finally:
255 # reenable updates of the list 254 # reenable updates of the list
256 self.resultList.setSortingEnabled(True) 255 self.resultList.setSortingEnabled(True)
257 self.resultList.setUpdatesEnabled(True) 256 self.resultList.setUpdatesEnabled(True)
258 self.checkProgress.reset() 257 self.checkProgress.reset()
259 258
260 # show summary info 259 # show summary info
261 if len(files) > 1: 260 if len(files) > 1:
262 if total_statements > 0: 261 if total_statements > 0:
263 pc = 100.0 * total_executed / total_statements 262 pc = 100.0 * total_executed / total_statements
264 else: 263 else:
265 pc = 100.0 264 pc = 100.0
266 itm = QTreeWidgetItem(self.summaryList, [ 265 itm = QTreeWidgetItem(
267 str(total_statements), 266 self.summaryList,
268 str(total_executed), 267 [str(total_statements), str(total_executed), "{0:.0f}%".format(pc)],
269 "{0:.0f}%".format(pc) 268 )
270 ])
271 for col in range(0, 3): 269 for col in range(0, 3):
272 itm.setTextAlignment(col, Qt.AlignmentFlag.AlignRight) 270 itm.setTextAlignment(col, Qt.AlignmentFlag.AlignRight)
273 else: 271 else:
274 self.summaryGroup.hide() 272 self.summaryGroup.hide()
275 273
276 if total_exceptions: 274 if total_exceptions:
277 EricMessageBox.warning( 275 EricMessageBox.warning(
278 self, 276 self,
279 self.tr("Parse Error"), 277 self.tr("Parse Error"),
280 self.tr("""%n file(s) could not be parsed. Coverage""" 278 self.tr(
281 """ info for these is not available.""", "", 279 """%n file(s) could not be parsed. Coverage"""
282 total_exceptions)) 280 """ info for these is not available.""",
283 281 "",
282 total_exceptions,
283 ),
284 )
285
284 self.__finish() 286 self.__finish()
285 287
286 def __finish(self): 288 def __finish(self):
287 """ 289 """
288 Private slot called when the action finished or the user pressed the 290 Private slot called when the action finished or the user pressed the
289 button. 291 button.
290 """ 292 """
291 self.cancelled = True 293 self.cancelled = True
292 self.buttonBox.button( 294 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True)
293 QDialogButtonBox.StandardButton.Close).setEnabled(True) 295 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False)
294 self.buttonBox.button( 296 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True)
295 QDialogButtonBox.StandardButton.Cancel).setEnabled(False)
296 self.buttonBox.button(
297 QDialogButtonBox.StandardButton.Close).setDefault(True)
298 QApplication.processEvents() 297 QApplication.processEvents()
299 self.resultList.header().resizeSections( 298 self.resultList.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents)
300 QHeaderView.ResizeMode.ResizeToContents)
301 self.resultList.header().setStretchLastSection(True) 299 self.resultList.header().setStretchLastSection(True)
302 self.summaryList.header().resizeSections( 300 self.summaryList.header().resizeSections(
303 QHeaderView.ResizeMode.ResizeToContents) 301 QHeaderView.ResizeMode.ResizeToContents
302 )
304 self.summaryList.header().setStretchLastSection(True) 303 self.summaryList.header().setStretchLastSection(True)
305 304
306 def on_buttonBox_clicked(self, button): 305 def on_buttonBox_clicked(self, button):
307 """ 306 """
308 Private slot called by a button of the button box clicked. 307 Private slot called by a button of the button box clicked.
309 308
310 @param button button that was clicked 309 @param button button that was clicked
311 @type QAbstractButton 310 @type QAbstractButton
312 """ 311 """
313 if button == self.buttonBox.button( 312 if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close):
314 QDialogButtonBox.StandardButton.Close
315 ):
316 self.close() 313 self.close()
317 elif button == self.buttonBox.button( 314 elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel):
318 QDialogButtonBox.StandardButton.Cancel
319 ):
320 self.__finish() 315 self.__finish()
321 316
322 def __showContextMenu(self, coord): 317 def __showContextMenu(self, coord):
323 """ 318 """
324 Private slot to show the context menu of the listview. 319 Private slot to show the context menu of the listview.
325 320
326 @param coord position of the mouse pointer 321 @param coord position of the mouse pointer
327 @type QPoint 322 @type QPoint
328 """ 323 """
329 itm = self.resultList.itemAt(coord) 324 itm = self.resultList.itemAt(coord)
330 if itm: 325 if itm:
331 self.openAct.setEnabled(True) 326 self.openAct.setEnabled(True)
332 else: 327 else:
333 self.openAct.setEnabled(False) 328 self.openAct.setEnabled(False)
334 self.__reportsMenu.setEnabled( 329 self.__reportsMenu.setEnabled(bool(self.resultList.topLevelItemCount()))
335 bool(self.resultList.topLevelItemCount()))
336 self.__menu.popup(self.mapToGlobal(coord)) 330 self.__menu.popup(self.mapToGlobal(coord))
337 331
338 def __openFile(self, itm=None): 332 def __openFile(self, itm=None):
339 """ 333 """
340 Private slot to open the selected file. 334 Private slot to open the selected file.
341 335
342 @param itm reference to the item to be opened 336 @param itm reference to the item to be opened
343 @type QTreeWidgetItem 337 @type QTreeWidgetItem
344 """ 338 """
345 if itm is None: 339 if itm is None:
346 itm = self.resultList.currentItem() 340 itm = self.resultList.currentItem()
347 fn = itm.text(0) 341 fn = itm.text(0)
348 342
349 try: 343 try:
350 vm = ericApp().getObject("ViewManager") 344 vm = ericApp().getObject("ViewManager")
351 vm.openSourceFile(fn) 345 vm.openSourceFile(fn)
352 editor = vm.getOpenEditor(fn) 346 editor = vm.getOpenEditor(fn)
353 editor.codeCoverageShowAnnotations(coverageFile=self.cfn) 347 editor.codeCoverageShowAnnotations(coverageFile=self.cfn)
354 except KeyError: 348 except KeyError:
355 self.openFile.emit(fn) 349 self.openFile.emit(fn)
356 350
357 def __prepareReportGeneration(self): 351 def __prepareReportGeneration(self):
358 """ 352 """
359 Private method to prepare a report generation. 353 Private method to prepare a report generation.
360 354
361 @return tuple containing a reference to the Coverage object and the 355 @return tuple containing a reference to the Coverage object and the
362 list of files to report 356 list of files to report
363 @rtype tuple of (Coverage, list of str) 357 @rtype tuple of (Coverage, list of str)
364 """ 358 """
365 count = self.resultList.topLevelItemCount() 359 count = self.resultList.topLevelItemCount()
366 if count == 0: 360 if count == 0:
367 return None, [] 361 return None, []
368 362
369 # get list of all filenames 363 # get list of all filenames
370 files = [ 364 files = [self.resultList.topLevelItem(index).text(0) for index in range(count)]
371 self.resultList.topLevelItem(index).text(0) 365
372 for index in range(count)
373 ]
374
375 cover = Coverage(data_file=self.cfn) 366 cover = Coverage(data_file=self.cfn)
376 cover.exclude(self.excludeList[0]) 367 cover.exclude(self.excludeList[0])
377 cover.load() 368 cover.load()
378 369
379 return cover, files 370 return cover, files
380 371
381 @pyqtSlot() 372 @pyqtSlot()
382 def __htmlReport(self): 373 def __htmlReport(self):
383 """ 374 """
384 Private slot to generate a HTML report of the shown data. 375 Private slot to generate a HTML report of the shown data.
385 """ 376 """
386 from .PyCoverageHtmlReportDialog import PyCoverageHtmlReportDialog 377 from .PyCoverageHtmlReportDialog import PyCoverageHtmlReportDialog
387 378
388 dlg = PyCoverageHtmlReportDialog(os.path.dirname(self.cfn), self) 379 dlg = PyCoverageHtmlReportDialog(os.path.dirname(self.cfn), self)
389 if dlg.exec() == QDialog.DialogCode.Accepted: 380 if dlg.exec() == QDialog.DialogCode.Accepted:
390 title, outputDirectory, extraCSS, openReport = dlg.getData() 381 title, outputDirectory, extraCSS, openReport = dlg.getData()
391 382
392 cover, files = self.__prepareReportGeneration() 383 cover, files = self.__prepareReportGeneration()
393 cover.html_report(morfs=files, directory=outputDirectory, 384 cover.html_report(
394 ignore_errors=True, extra_css=extraCSS, 385 morfs=files,
395 title=title) 386 directory=outputDirectory,
396 387 ignore_errors=True,
388 extra_css=extraCSS,
389 title=title,
390 )
391
397 if openReport: 392 if openReport:
398 QDesktopServices.openUrl(QUrl.fromLocalFile(os.path.join( 393 QDesktopServices.openUrl(
399 outputDirectory, "index.html"))) 394 QUrl.fromLocalFile(os.path.join(outputDirectory, "index.html"))
400 395 )
396
401 @pyqtSlot() 397 @pyqtSlot()
402 def __jsonReport(self): 398 def __jsonReport(self):
403 """ 399 """
404 Private slot to generate a JSON report of the shown data. 400 Private slot to generate a JSON report of the shown data.
405 """ 401 """
406 from .PyCoverageJsonReportDialog import PyCoverageJsonReportDialog 402 from .PyCoverageJsonReportDialog import PyCoverageJsonReportDialog
407 403
408 dlg = PyCoverageJsonReportDialog(os.path.dirname(self.cfn), self) 404 dlg = PyCoverageJsonReportDialog(os.path.dirname(self.cfn), self)
409 if dlg.exec() == QDialog.DialogCode.Accepted: 405 if dlg.exec() == QDialog.DialogCode.Accepted:
410 filename, compact = dlg.getData() 406 filename, compact = dlg.getData()
411 cover, files = self.__prepareReportGeneration() 407 cover, files = self.__prepareReportGeneration()
412 cover.json_report(morfs=files, outfile=filename, 408 cover.json_report(
413 ignore_errors=True, pretty_print=not compact) 409 morfs=files,
414 410 outfile=filename,
411 ignore_errors=True,
412 pretty_print=not compact,
413 )
414
415 @pyqtSlot() 415 @pyqtSlot()
416 def __lcovReport(self): 416 def __lcovReport(self):
417 """ 417 """
418 Private slot to generate a LCOV report of the shown data. 418 Private slot to generate a LCOV report of the shown data.
419 """ 419 """
420 from EricWidgets import EricPathPickerDialog 420 from EricWidgets import EricPathPickerDialog
421 from EricWidgets.EricPathPicker import EricPathPickerModes 421 from EricWidgets.EricPathPicker import EricPathPickerModes
422 422
423 filename, ok = EricPathPickerDialog.getPath( 423 filename, ok = EricPathPickerDialog.getPath(
424 self, 424 self,
425 self.tr("LCOV Report"), 425 self.tr("LCOV Report"),
426 self.tr("Enter the path of the output file:"), 426 self.tr("Enter the path of the output file:"),
427 mode=EricPathPickerModes.SAVE_FILE_ENSURE_EXTENSION_MODE, 427 mode=EricPathPickerModes.SAVE_FILE_ENSURE_EXTENSION_MODE,
428 path=os.path.join(os.path.dirname(self.cfn), "coverage.lcov"), 428 path=os.path.join(os.path.dirname(self.cfn), "coverage.lcov"),
429 defaultDirectory=os.path.dirname(self.cfn), 429 defaultDirectory=os.path.dirname(self.cfn),
430 filters=self.tr("LCOV Files (*.lcov);;All Files (*)") 430 filters=self.tr("LCOV Files (*.lcov);;All Files (*)"),
431 ) 431 )
432 if ok: 432 if ok:
433 cover, files = self.__prepareReportGeneration() 433 cover, files = self.__prepareReportGeneration()
434 cover.lcov_report(morfs=files, outfile=filename, 434 cover.lcov_report(morfs=files, outfile=filename, ignore_errors=True)
435 ignore_errors=True) 435
436
437 def __erase(self): 436 def __erase(self):
438 """ 437 """
439 Private slot to handle the erase context menu action. 438 Private slot to handle the erase context menu action.
440 439
441 This method erases the collected coverage data that is 440 This method erases the collected coverage data that is
442 stored in the .coverage file. 441 stored in the .coverage file.
443 """ 442 """
444 cover = Coverage(data_file=self.cfn) 443 cover = Coverage(data_file=self.cfn)
445 cover.load() 444 cover.load()
446 cover.erase() 445 cover.erase()
447 446
448 self.reloadButton.setEnabled(False) 447 self.reloadButton.setEnabled(False)
449 self.resultList.clear() 448 self.resultList.clear()
450 self.summaryList.clear() 449 self.summaryList.clear()
451 450
452 @pyqtSlot() 451 @pyqtSlot()
453 def on_reloadButton_clicked(self): 452 def on_reloadButton_clicked(self):
454 """ 453 """
455 Private slot to reload the coverage info. 454 Private slot to reload the coverage info.
456 """ 455 """
458 excludePattern = self.excludeCombo.currentText() 457 excludePattern = self.excludeCombo.currentText()
459 if excludePattern in self.excludeList: 458 if excludePattern in self.excludeList:
460 self.excludeList.remove(excludePattern) 459 self.excludeList.remove(excludePattern)
461 self.excludeList.insert(0, excludePattern) 460 self.excludeList.insert(0, excludePattern)
462 self.start(self.__cfn, self.__fn) 461 self.start(self.__cfn, self.__fn)
463 462
464 @pyqtSlot(QTreeWidgetItem, int) 463 @pyqtSlot(QTreeWidgetItem, int)
465 def on_resultList_itemActivated(self, item, column): 464 def on_resultList_itemActivated(self, item, column):
466 """ 465 """
467 Private slot to handle the activation of an item. 466 Private slot to handle the activation of an item.
468 467
469 @param item reference to the activated item (QTreeWidgetItem) 468 @param item reference to the activated item (QTreeWidgetItem)
470 @param column column the item was activated in (integer) 469 @param column column the item was activated in (integer)
471 """ 470 """
472 self.__openFile(item) 471 self.__openFile(item)

eric ide

mercurial