25 QCoreApplication.translate("TestResultsModel", "Name"), |
37 QCoreApplication.translate("TestResultsModel", "Name"), |
26 QCoreApplication.translate("TestResultsModel", "Message"), |
38 QCoreApplication.translate("TestResultsModel", "Message"), |
27 QCoreApplication.translate("TestResultsModel", "Duration (ms)"), |
39 QCoreApplication.translate("TestResultsModel", "Duration (ms)"), |
28 ] |
40 ] |
29 |
41 |
|
42 StatusColumn = 0 |
|
43 NameColumn = 1 |
|
44 MessageColumn = 2 |
|
45 DurationColumn = 3 |
|
46 |
30 def __init__(self, parent=None): |
47 def __init__(self, parent=None): |
31 """ |
48 """ |
32 Constructor |
49 Constructor |
33 |
50 |
34 @param parent reference to the parent object (defaults to None) |
51 @param parent reference to the parent object (defaults to None) |
35 @type QObject (optional) |
52 @type QObject (optional) |
36 """ |
53 """ |
37 super().__init__(parent) |
54 super().__init__(parent) |
38 |
55 |
|
56 if ericApp().usesDarkPalette(): |
|
57 self.__backgroundColors = { |
|
58 ResultCategory.RUNNING: None, |
|
59 ResultCategory.FAIL: QBrush(QColor("#880000")), |
|
60 ResultCategory.OK: QBrush(QColor("#005500")), |
|
61 ResultCategory.SKIP: QBrush(QColor("#3f3f3f")), |
|
62 ResultCategory.PENDING: QBrush(QColor("#004768")), |
|
63 } |
|
64 else: |
|
65 self.__backgroundColors = { |
|
66 ResultCategory.RUNNING: None, |
|
67 ResultCategory.FAIL: QBrush(QColor("#ff8080")), |
|
68 ResultCategory.OK: QBrush(QColor("#c1ffba")), |
|
69 ResultCategory.SKIP: QBrush(QColor("#c5c5c5")), |
|
70 ResultCategory.PENDING: QBrush(QColor("#6fbaff")), |
|
71 } |
|
72 |
39 self.__testResults = [] |
73 self.__testResults = [] |
|
74 |
|
75 def index(self, row, column, parent=QModelIndex()): |
|
76 """ |
|
77 Public method to generate an index for the given row and column to |
|
78 identify the item. |
|
79 |
|
80 @param row row for the index |
|
81 @type int |
|
82 @param column column for the index |
|
83 @type int |
|
84 @param parent index of the parent item (defaults to QModelIndex()) |
|
85 @type QModelIndex (optional) |
|
86 @return index for the item |
|
87 @rtype QModelIndex |
|
88 """ |
|
89 if not self.hasIndex(row, column, parent): # check bounds etc. |
|
90 return QModelIndex() |
|
91 |
|
92 if not parent.isValid(): |
|
93 # top level item |
|
94 return self.createIndex(row, column, TopLevelId) |
|
95 else: |
|
96 testResultIndex = parent.row() |
|
97 return self.createIndex(row, column, testResultIndex) |
|
98 |
|
99 def data(self, index, role): |
|
100 """ |
|
101 Public method to get the data for the various columns and roles. |
|
102 |
|
103 @param index index of the data to be returned |
|
104 @type QModelIndex |
|
105 @param role role designating the data to return |
|
106 @type Qt.ItemDataRole |
|
107 @return requested data item |
|
108 @rtype Any |
|
109 """ |
|
110 if not index.isValid(): |
|
111 return None |
|
112 |
|
113 row = index.row() |
|
114 column = index.column() |
|
115 idx = index.internalId() |
|
116 |
|
117 if role == Qt.ItemDataRole.DisplayRole: |
|
118 if idx != TopLevelId: |
|
119 if bool(self.__testResults[idx].extra): |
|
120 return self.__testResults[idx].extra[index.row()] |
|
121 else: |
|
122 return None |
|
123 elif column == TestResultsModel.StatusColumn: |
|
124 return self.__testResults[row].status |
|
125 elif column == TestResultsModel.NameColumn: |
|
126 return self.__testResults[row].name |
|
127 elif column == TestResultsModel.MessageColumn: |
|
128 return self.__testResults[row].message |
|
129 elif column == TestResultsModel.DurationColumn: |
|
130 duration = self.__testResults[row].duration |
|
131 return ( |
|
132 '' |
|
133 if duration is None else |
|
134 locale.format_string("%.2f", duration, grouping=True) |
|
135 ) |
|
136 elif role == Qt.ItemDataRole.ToolTipRole: |
|
137 if idx == TopLevelId and column == TestResultsModel.NameColumn: |
|
138 return self.testresults[row].name |
|
139 elif role == Qt.ItemDataRole.FontRole: |
|
140 if idx != TopLevelId: |
|
141 return Preferences.getEditorOtherFonts("MonospacedFont") |
|
142 elif role == Qt.ItemDataRole.BackgroundRole: |
|
143 if idx == TopLevelId: |
|
144 testResult = self.__testResults[row] |
|
145 with contextlib.suppress(KeyError): |
|
146 return self.__backgroundColors[testResult.category] |
|
147 elif role == Qt.ItemDataRole.TextAlignmentRole: |
|
148 if idx == TopLevelId and column == TestResultsModel.DurationColumn: |
|
149 return Qt.AlignmentFlag.AlignRight |
|
150 elif role == Qt.ItemDataRole.UserRole: # __IGNORE_WARNING_Y102__ |
|
151 if idx == TopLevelId: |
|
152 testresult = self.testresults[row] |
|
153 return (testresult.filename, testresult.lineno) |
|
154 |
|
155 return None |
40 |
156 |
41 def headerData(self, section, orientation, |
157 def headerData(self, section, orientation, |
42 role=Qt.ItemDataRole.DisplayRole): |
158 role=Qt.ItemDataRole.DisplayRole): |
43 """ |
159 """ |
44 Public method to get the header string for the various sections. |
160 Public method to get the header string for the various sections. |
96 Public method to clear the model data. |
234 Public method to clear the model data. |
97 """ |
235 """ |
98 self.beginResetModel() |
236 self.beginResetModel() |
99 self.__testResults.clear() |
237 self.__testResults.clear() |
100 self.endResetModel() |
238 self.endResetModel() |
|
239 |
|
240 def sort(self, column, order): |
|
241 """ |
|
242 Public method to sort the model data by column in order. |
|
243 |
|
244 @param column sort column number |
|
245 @type int |
|
246 @param order sort order |
|
247 @type Qt.SortOrder |
|
248 """ # __IGNORE_WARNING_D234r__ |
|
249 def durationKey(result): |
|
250 """ |
|
251 Function to generate a key for duration sorting |
|
252 |
|
253 @param result result object |
|
254 @type UTTestResult |
|
255 @return sort key |
|
256 @rtype float |
|
257 """ |
|
258 return result.duration or -1.0 |
|
259 |
|
260 self.beginResetModel() |
|
261 reverse = order == Qt.SortOrder.DescendingOrder |
|
262 if column == TestResultsModel.StatusColumn: |
|
263 self.__testResults.sort(key=attrgetter('category', 'status'), |
|
264 reverse=reverse) |
|
265 elif column == TestResultsModel.NameColumn: |
|
266 self.__testResults.sort(key=attrgetter('name'), reverse=reverse) |
|
267 elif column == TestResultsModel.MessageColumn: |
|
268 self.__testResults.sort(key=attrgetter('message'), reverse=reverse) |
|
269 elif column == TestResultsModel.DurationColumn: |
|
270 self.__testResults.sort(key=durationKey, reverse=reverse) |
|
271 self.endResetModel() |
|
272 |
|
273 def getTestResults(self): |
|
274 """ |
|
275 Public method to get the list of test results managed by the model. |
|
276 |
|
277 @return list of test results managed by the model |
|
278 @rtype list of UTTestResult |
|
279 """ |
|
280 return copy.deepcopy(self.__testResults) |
|
281 |
|
282 def setTestResults(self, testResults): |
|
283 """ |
|
284 Public method to set the list of test results of the model. |
|
285 |
|
286 @param testResults test results to be managed by the model |
|
287 @type list of UTTestResult |
|
288 """ |
|
289 self.beginResetModel() |
|
290 self.__testResults = copy.deepcopy(testResults) |
|
291 self.endResetModel() |
|
292 |
|
293 def addTestResults(self, testResults): |
|
294 """ |
|
295 Public method to add test results to the ones already managed by the |
|
296 model. |
|
297 |
|
298 @param testResults test results to be added to the model |
|
299 @type list of UTTestResult |
|
300 """ |
|
301 firstRow = len(self.__testResults) |
|
302 lastRow = firstRow + len(testResults) - 1 |
|
303 self.beginInsertRows(QModelIndex(), firstRow, lastRow) |
|
304 self.__testResults.extend(testResults) |
|
305 self.endInsertRows() |
|
306 |
|
307 def updateTestResults(self, testResults): |
|
308 """ |
|
309 Public method to update the data of managed test result items. |
|
310 |
|
311 @param testResults test results to be updated |
|
312 @type list of UTTestResult |
|
313 """ |
|
314 minIndex = None |
|
315 maxIndex = None |
|
316 |
|
317 for testResult in testResults: |
|
318 for (index, currentResult) in enumerate(self.__testResults): |
|
319 if currentResult.id == testResult.id: |
|
320 self.__testResults[index] = testResult |
|
321 if minIndex is None: |
|
322 minIndex = index |
|
323 maxIndex = index |
|
324 else: |
|
325 minIndex = min(minIndex, index) |
|
326 maxIndex = max(maxIndex, index) |
|
327 |
|
328 if minIndex is not None: |
|
329 self.dataChanged.emit( |
|
330 self.index(minIndex, 0), |
|
331 self.index(maxIndex, len(TestResultsModel.Headers) - 1) |
|
332 ) |
101 |
333 |
102 |
334 |
103 class TestResultsTreeView(QTreeView): |
335 class TestResultsTreeView(QTreeView): |
104 """ |
336 """ |
105 Class implementing a tree view to show the test result data. |
337 Class implementing a tree view to show the test result data. |
130 |
362 |
131 self.header().sortIndicatorChanged.connect(self.sortByColumn) |
363 self.header().sortIndicatorChanged.connect(self.sortByColumn) |
132 self.header().sortIndicatorChanged.connect( |
364 self.header().sortIndicatorChanged.connect( |
133 lambda column, order: self.header().setSortIndicatorShown(True)) |
365 lambda column, order: self.header().setSortIndicatorShown(True)) |
134 |
366 |
|
367 def reset(self): |
|
368 """ |
|
369 Public method to reset the internal state of the view. |
|
370 """ |
|
371 super().reset() |
|
372 |
|
373 self.resizeColumns() |
|
374 self.spanFirstColumn(0, self.model().rowCount() - 1) |
|
375 |
|
376 def rowsInserted(self, parent, startRow, endRow): |
|
377 """ |
|
378 Public method called when rows are inserted. |
|
379 |
|
380 @param parent model index of the parent item |
|
381 @type QModelIndex |
|
382 @param startRow first row been inserted |
|
383 @type int |
|
384 @param endRow last row been inserted |
|
385 @type int |
|
386 """ |
|
387 super().rowsInserted(parent, startRow, endRow) |
|
388 |
|
389 self.resizeColumns() |
|
390 self.spanFirstColumn(startRow, endRow) |
|
391 |
|
392 def dataChanged(self, topLeft, bottomRight, roles=[]): |
|
393 """ |
|
394 Public method called when the model data has changed. |
|
395 |
|
396 @param topLeft index of the top left element |
|
397 @type QModelIndex |
|
398 @param bottomRight index of the bottom right element |
|
399 @type QModelIndex |
|
400 @param roles list of roles changed (defaults to []) |
|
401 @type list of Qt.ItemDataRole (optional) |
|
402 """ |
|
403 super().dataChanged(topLeft, bottomRight, roles) |
|
404 |
|
405 self.resizeColumns() |
|
406 while topLeft.parent().isValid(): |
|
407 topLeft = topLeft.parent() |
|
408 while bottomRight.parent().isValid(): |
|
409 bottomRight = bottomRight.parent() |
|
410 self.spanFirstColumn(topLeft.row(), bottomRight.row()) |
|
411 |
135 @pyqtSlot(QModelIndex) |
412 @pyqtSlot(QModelIndex) |
136 def __gotoTestDefinition(self, index): |
413 def __gotoTestDefinition(self, index): |
137 """ |
414 """ |
138 Private slot to show the test definition. |
415 Private slot to show the test definition. |
139 |
416 |
140 @param index index for the double-clicked item |
417 @param index index for the double-clicked item |
141 @type QModelIndex |
418 @type QModelIndex |
142 """ |
419 """ |
143 # TODO: not implemented yet |
420 # TODO: not implemented yet (__gotoTestDefinition) |
144 pass |
421 pass |
|
422 |
|
423 def resizeColumns(self): |
|
424 """ |
|
425 Public method to resize the columns to their contents. |
|
426 """ |
|
427 for column in range(self.model().columnCount()): |
|
428 self.resizeColumnToContents(column) |
|
429 |
|
430 def spanFirstColumn(self, startRow, endRow): |
|
431 """ |
|
432 Public method to make the first column span the row for second level |
|
433 items. |
|
434 |
|
435 These items contain the test results. |
|
436 |
|
437 @param startRow index of the first row to span |
|
438 @type QModelIndex |
|
439 @param endRow index of the last row (including) to span |
|
440 @type QModelIndex |
|
441 """ |
|
442 model = self.model() |
|
443 for row in range(startRow, endRow + 1): |
|
444 index = model.index(row, 0) |
|
445 for i in range(model.rowCount(index)): |
|
446 self.setFirstColumnSpanned(i, index, True) |
145 |
447 |
146 # |
448 # |
147 # eflag: noqa = M822 |
449 # eflag: noqa = M821, M822 |