src/eric7/Testing/TestResultsTree.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9264
18a7312cfdb3
child 9413
80c06d472826
--- a/src/eric7/Testing/TestResultsTree.py	Wed Jul 13 11:16:20 2022 +0200
+++ b/src/eric7/Testing/TestResultsTree.py	Wed Jul 13 14:55:47 2022 +0200
@@ -16,8 +16,13 @@
 from operator import attrgetter
 
 from PyQt6.QtCore import (
-    pyqtSignal, pyqtSlot, Qt, QAbstractItemModel, QCoreApplication,
-    QModelIndex, QPoint
+    pyqtSignal,
+    pyqtSlot,
+    Qt,
+    QAbstractItemModel,
+    QCoreApplication,
+    QModelIndex,
+    QPoint,
 )
 from PyQt6.QtGui import QBrush, QColor
 from PyQt6.QtWidgets import QMenu, QTreeView
@@ -28,39 +33,40 @@
 
 from .Interfaces.TestExecutorBase import TestResultCategory
 
-TopLevelId = 2 ** 32 - 1
+TopLevelId = 2**32 - 1
 
 
 class TestResultsModel(QAbstractItemModel):
     """
     Class implementing the item model containing the test data.
-    
+
     @signal summary(str) emitted whenever the model data changes. The element
         is a summary of the test results of the model.
     """
+
     summary = pyqtSignal(str)
-    
+
     Headers = [
         QCoreApplication.translate("TestResultsModel", "Status"),
         QCoreApplication.translate("TestResultsModel", "Name"),
         QCoreApplication.translate("TestResultsModel", "Message"),
         QCoreApplication.translate("TestResultsModel", "Duration [ms]"),
     ]
-    
+
     StatusColumn = 0
     NameColumn = 1
     MessageColumn = 2
     DurationColumn = 3
-    
+
     def __init__(self, parent=None):
         """
         Constructor
-        
+
         @param parent reference to the parent object (defaults to None)
         @type QObject (optional)
         """
         super().__init__(parent)
-        
+
         if ericApp().usesDarkPalette():
             self.__backgroundColors = {
                 TestResultCategory.RUNNING: None,
@@ -77,14 +83,14 @@
                 TestResultCategory.SKIP: QBrush(QColor("#c5c5c5")),
                 TestResultCategory.PENDING: QBrush(QColor("#6fbaff")),
             }
-        
+
         self.__testResults = []
-    
+
     def index(self, row, column, parent=QModelIndex()):
         """
         Public method to generate an index for the given row and column to
         identify the item.
-        
+
         @param row row for the index
         @type int
         @param column column for the index
@@ -96,18 +102,18 @@
         """
         if not self.hasIndex(row, column, parent):  # check bounds etc.
             return QModelIndex()
-        
+
         if not parent.isValid():
             # top level item
             return self.createIndex(row, column, TopLevelId)
         else:
             testResultIndex = parent.row()
             return self.createIndex(row, column, testResultIndex)
-    
+
     def data(self, index, role):
         """
         Public method to get the data for the various columns and roles.
-        
+
         @param index index of the data to be returned
         @type QModelIndex
         @param role role designating the data to return
@@ -117,11 +123,11 @@
         """
         if not index.isValid():
             return None
-        
+
         row = index.row()
         column = index.column()
         idx = index.internalId()
-        
+
         if role == Qt.ItemDataRole.DisplayRole:
             if idx != TopLevelId:
                 if bool(self.__testResults[idx].extra):
@@ -138,8 +144,8 @@
                 duration = self.__testResults[row].duration
                 return (
                     ""
-                    if duration is None else
-                    locale.format_string("%.2f", duration, grouping=True)
+                    if duration is None
+                    else locale.format_string("%.2f", duration, grouping=True)
                 )
         elif role == Qt.ItemDataRole.ToolTipRole:
             if idx == TopLevelId and column == TestResultsModel.NameColumn:
@@ -155,18 +161,17 @@
         elif role == Qt.ItemDataRole.TextAlignmentRole:
             if idx == TopLevelId and column == TestResultsModel.DurationColumn:
                 return Qt.AlignmentFlag.AlignRight.value
-        elif role == Qt.ItemDataRole.UserRole:      # __IGNORE_WARNING_Y102__
+        elif role == Qt.ItemDataRole.UserRole:  # __IGNORE_WARNING_Y102__
             if idx == TopLevelId:
                 testresult = self.__testResults[row]
                 return (testresult.filename, testresult.lineno)
-        
+
         return None
-    
-    def headerData(self, section, orientation,
-                   role=Qt.ItemDataRole.DisplayRole):
+
+    def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole):
         """
         Public method to get the header string for the various sections.
-        
+
         @param section section number
         @type int
         @param orientation orientation of the header
@@ -177,17 +182,17 @@
         @rtype str
         """
         if (
-            orientation == Qt.Orientation.Horizontal and
-            role == Qt.ItemDataRole.DisplayRole
+            orientation == Qt.Orientation.Horizontal
+            and role == Qt.ItemDataRole.DisplayRole
         ):
             return TestResultsModel.Headers[section]
         else:
             return None
-    
+
     def parent(self, index):
         """
         Public method to get the parent of the item pointed to by index.
-        
+
         @param index index of the item
         @type QModelIndex
         @return index of the parent item
@@ -195,17 +200,17 @@
         """
         if not index.isValid():
             return QModelIndex()
-        
+
         idx = index.internalId()
         if idx == TopLevelId:
             return QModelIndex()
         else:
             return self.index(idx, 0)
-    
+
     def rowCount(self, parent=QModelIndex()):
         """
         Public method to get the number of row for a given parent index.
-        
+
         @param parent index of the parent item (defaults to QModelIndex())
         @type QModelIndex (optional)
         @return number of rows
@@ -213,20 +218,20 @@
         """
         if not parent.isValid():
             return len(self.__testResults)
-        
+
         if (
-            parent.internalId() == TopLevelId and
-            parent.column() == 0 and
-            self.__testResults[parent.row()].extra is not None
+            parent.internalId() == TopLevelId
+            and parent.column() == 0
+            and self.__testResults[parent.row()].extra is not None
         ):
             return len(self.__testResults[parent.row()].extra)
-        
+
         return 0
 
     def columnCount(self, parent=QModelIndex()):
         """
         Public method to get the number of columns.
-        
+
         @param parent index of the parent item (defaults to QModelIndex())
         @type QModelIndex (optional)
         @return number of columns
@@ -236,7 +241,7 @@
             return len(TestResultsModel.Headers)
         else:
             return 1
-    
+
     def clear(self):
         """
         Public method to clear the model data.
@@ -244,22 +249,23 @@
         self.beginResetModel()
         self.__testResults.clear()
         self.endResetModel()
-        
+
         self.summary.emit("")
-    
+
     def sort(self, column, order):
         """
         Public method to sort the model data by column in order.
-        
+
         @param column sort column number
         @type int
         @param order sort order
         @type Qt.SortOrder
-        """             # __IGNORE_WARNING_D234r__
+        """  # __IGNORE_WARNING_D234r__
+
         def durationKey(result):
             """
             Function to generate a key for duration sorting
-            
+
             @param result result object
             @type TestResult
             @return sort key
@@ -270,43 +276,44 @@
         self.beginResetModel()
         reverse = order == Qt.SortOrder.DescendingOrder
         if column == TestResultsModel.StatusColumn:
-            self.__testResults.sort(key=attrgetter('category', 'status'),
-                                    reverse=reverse)
+            self.__testResults.sort(
+                key=attrgetter("category", "status"), reverse=reverse
+            )
         elif column == TestResultsModel.NameColumn:
-            self.__testResults.sort(key=attrgetter('name'), reverse=reverse)
+            self.__testResults.sort(key=attrgetter("name"), reverse=reverse)
         elif column == TestResultsModel.MessageColumn:
-            self.__testResults.sort(key=attrgetter('message'), reverse=reverse)
+            self.__testResults.sort(key=attrgetter("message"), reverse=reverse)
         elif column == TestResultsModel.DurationColumn:
             self.__testResults.sort(key=durationKey, reverse=reverse)
         self.endResetModel()
-    
+
     def getTestResults(self):
         """
         Public method to get the list of test results managed by the model.
-        
+
         @return list of test results managed by the model
         @rtype list of TestResult
         """
         return copy.deepcopy(self.__testResults)
-    
+
     def setTestResults(self, testResults):
         """
         Public method to set the list of test results of the model.
-        
+
         @param testResults test results to be managed by the model
         @type list of TestResult
         """
         self.beginResetModel()
         self.__testResults = copy.deepcopy(testResults)
         self.endResetModel()
-        
+
         self.summary.emit(self.__summary())
-    
+
     def addTestResults(self, testResults):
         """
         Public method to add test results to the ones already managed by the
         model.
-        
+
         @param testResults test results to be added to the model
         @type list of TestResult
         """
@@ -315,21 +322,21 @@
         self.beginInsertRows(QModelIndex(), firstRow, lastRow)
         self.__testResults.extend(testResults)
         self.endInsertRows()
-        
+
         self.summary.emit(self.__summary())
-    
+
     def updateTestResults(self, testResults):
         """
         Public method to update the data of managed test result items.
-        
+
         @param testResults test results to be updated
         @type list of TestResult
         """
         minIndex = None
         maxIndex = None
-        
+
         testResultsToBeAdded = []
-        
+
         for testResult in testResults:
             for (index, currentResult) in enumerate(self.__testResults):
                 if currentResult.id == testResult.id:
@@ -340,117 +347,122 @@
                     else:
                         minIndex = min(minIndex, index)
                         maxIndex = max(maxIndex, index)
-                    
+
                     break
             else:
                 # Test result with given id was not found.
                 # Just add it to the list (could be a sub test)
                 testResultsToBeAdded.append(testResult)
-        
+
         if minIndex is not None:
             self.dataChanged.emit(
                 self.index(minIndex, 0),
-                self.index(maxIndex, len(TestResultsModel.Headers) - 1)
+                self.index(maxIndex, len(TestResultsModel.Headers) - 1),
             )
-            
+
             self.summary.emit(self.__summary())
-        
+
         if testResultsToBeAdded:
             self.addTestResults(testResultsToBeAdded)
-    
+
     def getFailedTests(self):
         """
         Public method to extract the test ids of all failed tests.
-        
+
         @return test ids of all failed tests
         @rtype list of str
         """
         failedIds = [
-            res.id for res in self.__testResults if (
-                res.category == TestResultCategory.FAIL and
-                not res.subtestResult
-            )
+            res.id
+            for res in self.__testResults
+            if (res.category == TestResultCategory.FAIL and not res.subtestResult)
         ]
         return failedIds
-    
+
     def __summary(self):
         """
         Private method to generate a test results summary text.
-        
+
         @return test results summary text
         @rtype str
         """
         if len(self.__testResults) == 0:
             return self.tr("No results to show")
-        
+
         counts = Counter(res.category for res in self.__testResults)
         if all(
             counts[category] == 0
-            for category in (TestResultCategory.FAIL, TestResultCategory.OK,
-                             TestResultCategory.SKIP)
+            for category in (
+                TestResultCategory.FAIL,
+                TestResultCategory.OK,
+                TestResultCategory.SKIP,
+            )
         ):
             return self.tr("Collected %n test(s)", "", len(self.__testResults))
-        
+
         return self.tr(
             "%n test(s)/subtest(s) total, {0} failed, {1} passed,"
             " {2} skipped, {3} pending",
-            "", len(self.__testResults)
+            "",
+            len(self.__testResults),
         ).format(
             counts[TestResultCategory.FAIL],
             counts[TestResultCategory.OK],
             counts[TestResultCategory.SKIP],
-            counts[TestResultCategory.PENDING]
+            counts[TestResultCategory.PENDING],
         )
 
 
 class TestResultsTreeView(QTreeView):
     """
     Class implementing a tree view to show the test result data.
-    
+
     @signal goto(str, int) emitted to go to the position given by file name
         and line number
     """
+
     goto = pyqtSignal(str, int)
-    
+
     def __init__(self, parent=None):
         """
         Constructor
-        
+
         @param parent reference to the parent widget (defaults to None)
         @type QWidget (optional)
         """
         super().__init__(parent)
-        
+
         self.setItemsExpandable(True)
         self.setExpandsOnDoubleClick(False)
         self.setSortingEnabled(True)
-        
+
         self.header().setDefaultAlignment(Qt.AlignmentFlag.AlignCenter)
         self.header().setSortIndicatorShown(False)
-        
+
         self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
-        
+
         # connect signals and slots
         self.doubleClicked.connect(self.__gotoTestDefinition)
         self.customContextMenuRequested.connect(self.__showContextMenu)
-        
+
         self.header().sortIndicatorChanged.connect(self.sortByColumn)
         self.header().sortIndicatorChanged.connect(
-            lambda column, order: self.header().setSortIndicatorShown(True))
-    
+            lambda column, order: self.header().setSortIndicatorShown(True)
+        )
+
     def reset(self):
         """
         Public method to reset the internal state of the view.
         """
         super().reset()
-        
+
         self.resizeColumns()
         self.spanFirstColumn(0, self.model().rowCount() - 1)
-    
+
     def rowsInserted(self, parent, startRow, endRow):
         """
         Public method called when rows are inserted.
-        
+
         @param parent model index of the parent item
         @type QModelIndex
         @param startRow first row been inserted
@@ -459,14 +471,14 @@
         @type int
         """
         super().rowsInserted(parent, startRow, endRow)
-        
+
         self.resizeColumns()
         self.spanFirstColumn(startRow, endRow)
-    
+
     def dataChanged(self, topLeft, bottomRight, roles=[]):
         """
         Public method called when the model data has changed.
-        
+
         @param topLeft index of the top left element
         @type QModelIndex
         @param bottomRight index of the bottom right element
@@ -475,28 +487,28 @@
         @type list of Qt.ItemDataRole (optional)
         """
         super().dataChanged(topLeft, bottomRight, roles)
-        
+
         self.resizeColumns()
         while topLeft.parent().isValid():
             topLeft = topLeft.parent()
         while bottomRight.parent().isValid():
             bottomRight = bottomRight.parent()
         self.spanFirstColumn(topLeft.row(), bottomRight.row())
-    
+
     def resizeColumns(self):
         """
         Public method to resize the columns to their contents.
         """
         for column in range(self.model().columnCount()):
             self.resizeColumnToContents(column)
-    
+
     def spanFirstColumn(self, startRow, endRow):
         """
         Public method to make the first column span the row for second level
         items.
-        
+
         These items contain the test results.
-        
+
         @param startRow index of the first row to span
         @type QModelIndex
         @param endRow index of the last row (including) to span
@@ -507,15 +519,15 @@
             index = model.index(row, 0)
             for i in range(model.rowCount(index)):
                 self.setFirstColumnSpanned(i, index, True)
-    
+
     def __canonicalIndex(self, index):
         """
         Private method to create the canonical index for a given index.
-        
+
         The canonical index is the index of the first column of the test
         result entry (i.e. the top-level item). If the index is invalid,
         None is returned.
-        
+
         @param index index to determine the canonical index for
         @type QModelIndex
         @return index of the firt column of the associated top-level item index
@@ -523,17 +535,17 @@
         """
         if not index.isValid():
             return None
-        
+
         while index.parent().isValid():  # find the top-level node
             index = index.parent()
         index = index.sibling(index.row(), 0)  # go to first column
         return index
-    
+
     @pyqtSlot(QModelIndex)
     def __gotoTestDefinition(self, index):
         """
         Private slot to show the test definition.
-        
+
         @param index index for the double-clicked item
         @type QModelIndex
         """
@@ -543,30 +555,30 @@
             if lineno is None:
                 lineno = 1
             self.goto.emit(filename, lineno)
-    
+
     @pyqtSlot(QPoint)
     def __showContextMenu(self, pos):
         """
         Private slot to show the context menu.
-        
+
         @param pos relative position for the context menu
         @type QPoint
         """
         index = self.indexAt(pos)
         cindex = self.__canonicalIndex(index)
-        
+
         contextMenu = (
             self.__createContextMenu(cindex)
-            if cindex else
-            self.__createBackgroundContextMenu()
+            if cindex
+            else self.__createBackgroundContextMenu()
         )
         contextMenu.exec(self.mapToGlobal(pos))
-    
+
     def __createContextMenu(self, index):
         """
         Private method to create a context menu for the item pointed to by the
         given index.
-        
+
         @param index index of the item
         @type QModelIndex
         @return created context menu
@@ -574,38 +586,36 @@
         """
         menu = QMenu(self)
         if self.isExpanded(index):
-            menu.addAction(self.tr("Collapse"),
-                           lambda: self.collapse(index))
+            menu.addAction(self.tr("Collapse"), lambda: self.collapse(index))
         else:
-            act = menu.addAction(self.tr("Expand"),
-                                 lambda: self.expand(index))
+            act = menu.addAction(self.tr("Expand"), lambda: self.expand(index))
             act.setEnabled(self.model().hasChildren(index))
         menu.addSeparator()
-        
-        act = menu.addAction(self.tr("Show Source"),
-                             lambda: self.__gotoTestDefinition(index))
-        act.setEnabled(
-            self.model().data(index, Qt.ItemDataRole.UserRole) is not None
+
+        act = menu.addAction(
+            self.tr("Show Source"), lambda: self.__gotoTestDefinition(index)
         )
+        act.setEnabled(self.model().data(index, Qt.ItemDataRole.UserRole) is not None)
         menu.addSeparator()
-        
+
         menu.addAction(self.tr("Collapse All"), self.collapseAll)
         menu.addAction(self.tr("Expand All"), self.expandAll)
-        
+
         return menu
-    
+
     def __createBackgroundContextMenu(self):
         """
         Private method to create a context menu for the background.
-        
+
         @return created context menu
         @rtype QMenu
         """
         menu = QMenu(self)
         menu.addAction(self.tr("Collapse All"), self.collapseAll)
         menu.addAction(self.tr("Expand All"), self.expandAll)
-        
+
         return menu
 
+
 #
 # eflag: noqa = M821, M822

eric ide

mercurial