Testing eric7

Tue, 12 Dec 2023 16:43:51 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Tue, 12 Dec 2023 16:43:51 +0100
branch
eric7
changeset 10404
f7d9c31f0c38
parent 10403
ea3320d5e8e9
child 10405
df7e1694d0eb

Testing
- Extended the testing dialog to allow filtering of the result list based on the result status.

docs/changelog.md file | annotate | diff | comparison | revisions
src/eric7/Testing/Interfaces/PytestExecutor.py file | annotate | diff | comparison | revisions
src/eric7/Testing/Interfaces/PytestRunner.py file | annotate | diff | comparison | revisions
src/eric7/Testing/Interfaces/TestExecutorBase.py file | annotate | diff | comparison | revisions
src/eric7/Testing/Interfaces/UnittestExecutor.py file | annotate | diff | comparison | revisions
src/eric7/Testing/Interfaces/UnittestRunner.py file | annotate | diff | comparison | revisions
src/eric7/Testing/TestResultsTree.py file | annotate | diff | comparison | revisions
src/eric7/Testing/TestingWidget.py file | annotate | diff | comparison | revisions
src/eric7/Testing/TestingWidget.ui file | annotate | diff | comparison | revisions
--- a/docs/changelog.md	Tue Dec 12 09:35:39 2023 +0100
+++ b/docs/changelog.md	Tue Dec 12 16:43:51 2023 +0100
@@ -22,6 +22,9 @@
       external file manager.
     - Added an entry to the background context menu to show the project
       directory in an external file manager.
+- Testing
+    - Extended the testing dialog to allow filtering of the result list
+      based on the result status.
 - Viewmanager
     - Added `Close Tabs to the Left` and `Close Tabs to the Right` context
       menu entries to the tabview view manager.
--- a/src/eric7/Testing/Interfaces/PytestExecutor.py	Tue Dec 12 09:35:39 2023 +0100
+++ b/src/eric7/Testing/Interfaces/PytestExecutor.py	Tue Dec 12 16:43:51 2023 +0100
@@ -179,6 +179,8 @@
         if config.testNamePattern:
             args.append("-k")
             args.append(config.testNamePattern)
+##
+        ##args.append("--collect-only")
 
         if config.testFilename:
             if config.testName:
@@ -257,7 +259,15 @@
         # tests collected
         elif data["event"] == "collected":
             self.collected.emit(
-                [(data["nodeid"], self.__nodeid2testname(data["nodeid"]), "")]
+                [
+                    (
+                        data["nodeid"],
+                        self.__nodeid2testname(data["nodeid"]),
+                        "",
+                        data["filename"],
+                        data["linenumber"],
+                    )
+                ]
             )
 
         # test started
--- a/src/eric7/Testing/Interfaces/PytestRunner.py	Tue Dec 12 09:35:39 2023 +0100
+++ b/src/eric7/Testing/Interfaces/PytestRunner.py	Tue Dec 12 16:43:51 2023 +0100
@@ -173,6 +173,8 @@
                 "event": "collected",
                 "nodeid": item.nodeid,
                 "name": item.name,
+                "filename": item.location[0],
+                "linenumber": item.location[1],
             }
         )
 
--- a/src/eric7/Testing/Interfaces/TestExecutorBase.py	Tue Dec 12 09:35:39 2023 +0100
+++ b/src/eric7/Testing/Interfaces/TestExecutorBase.py	Tue Dec 12 16:43:51 2023 +0100
@@ -44,8 +44,8 @@
     message: str = ""  # short result message
     extra: list = None  # additional information text
     duration: float = None  # test duration
-    filename: str = None  # file name of a failed test
-    lineno: int = None  # line number of a failed test
+    filename: str = None  # file name of a (failed) test
+    lineno: int = None  # line number of a (failed) test
     subtestResult: bool = False  # flag indicating the result of a subtest
 
 
@@ -73,9 +73,10 @@
     """
     Base class for test framework specific implementations.
 
-    @signal collected(list of tuple of (str, str, str)) emitted after all tests
-        have been collected. Tuple elements are the test id, the test name and
-        a short description of the test.
+    @signal collected(list of tuple of (str, str, str, str, int)) emitted after all
+        tests have been collected. Tuple elements are the test id, the test name,
+        a short description of the test, the test file name and the line number of
+        the test.
     @signal collectError(list of tuple of (str, str)) emitted when errors
         are encountered during test collection. Tuple elements are the
         test name and the error message.
--- a/src/eric7/Testing/Interfaces/UnittestExecutor.py	Tue Dec 12 09:35:39 2023 +0100
+++ b/src/eric7/Testing/Interfaces/UnittestExecutor.py	Tue Dec 12 16:43:51 2023 +0100
@@ -197,7 +197,7 @@
         # tests collected
         elif data["event"] == "collected":
             self.collected.emit(
-                [(t["id"], t["name"], t["description"]) for t in data["tests"]]
+                [(t["id"], t["name"], t["description"], "", 0) for t in data["tests"]]
             )
 
         # test started
--- a/src/eric7/Testing/Interfaces/UnittestRunner.py	Tue Dec 12 09:35:39 2023 +0100
+++ b/src/eric7/Testing/Interfaces/UnittestRunner.py	Tue Dec 12 16:43:51 2023 +0100
@@ -255,7 +255,7 @@
     @type unittest.TestSuite
     @return list of tuples containing the test case ID, the string
         representation and the short description
-    @rtype list of tuples of (str, str)
+    @rtype list of tuples of (str, str, str)
     """
     testCases = []
     for test in suite:
--- a/src/eric7/Testing/TestResultsTree.py	Tue Dec 12 09:35:39 2023 +0100
+++ b/src/eric7/Testing/TestResultsTree.py	Tue Dec 12 16:43:51 2023 +0100
@@ -20,6 +20,7 @@
     QCoreApplication,
     QModelIndex,
     QPoint,
+    QSortFilterProxyModel,
     Qt,
     pyqtSignal,
     pyqtSlot,
@@ -422,6 +423,60 @@
             counts[TestResultCategory.PENDING],
         )
 
+    def getStatusFilterList(self):
+        """
+        Public method to get a list of the unique test result status.
+
+        @return test result status
+        @rtype set of str
+        """
+        return {t.status for t in self.__testResults}
+
+
+class TestResultsFilterModel(QSortFilterProxyModel):
+    """
+    Class implementing a filter model to filter the test results by status.
+    """
+
+    def __init__(self, parent=None):
+        """
+        Constructor
+
+        @param parent reference to the parent object (QObject)
+        """
+        super().__init__(parent)
+
+        self.__statusFilterString = ""
+
+    def filterAcceptsRow(self, sourceRow, sourceParent):
+        """
+        Public method to determine, if the row is acceptable.
+
+        @param sourceRow row number in the source model
+        @type int
+        @param sourceParent index of the source item
+        @type QModelIndex
+        @return flag indicating acceptance
+        @rtype bool
+        """
+        sm = self.sourceModel()
+        idx = sm.index(sourceRow, 0, sourceParent)
+        status = sm.data(idx, Qt.ItemDataRole.DisplayRole)
+        return (
+            sourceParent.isValid()
+            or self.__statusFilterString == ""
+            or status == self.__statusFilterString
+        )
+
+    def setStatusFilterString(self, filterString):
+        """
+        Public method to set the status filter string.
+
+        @param filterString status filter string
+        @type str
+        """
+        self.__statusFilterString = filterString
+        self.invalidateRowsFilter()
 
 class TestResultsTreeView(QTreeView):
     """
@@ -452,7 +507,7 @@
         self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
 
         # connect signals and slots
-        self.doubleClicked.connect(self.__gotoTestDefinition)
+        self.activated.connect(self.__gotoTestDefinition)
         self.customContextMenuRequested.connect(self.__showContextMenu)
 
         self.header().sortIndicatorChanged.connect(self.sortByColumn)
@@ -559,7 +614,7 @@
         """
         cindex = self.__canonicalIndex(index)
         filename, lineno = self.model().data(cindex, Qt.ItemDataRole.UserRole)
-        if filename is not None:
+        if filename:
             if lineno is None:
                 lineno = 1
             self.goto.emit(filename, lineno)
--- a/src/eric7/Testing/TestingWidget.py	Tue Dec 12 09:35:39 2023 +0100
+++ b/src/eric7/Testing/TestingWidget.py	Tue Dec 12 16:43:51 2023 +0100
@@ -33,7 +33,7 @@
 from .Interfaces import Frameworks
 from .Interfaces.TestExecutorBase import TestConfig, TestResult, TestResultCategory
 from .Interfaces.TestFrameworkRegistry import TestFrameworkRegistry
-from .TestResultsTree import TestResultsModel, TestResultsTreeView
+from .TestResultsTree import TestResultsFilterModel, TestResultsModel, TestResultsTreeView
 from .Ui_TestingWidget import Ui_TestingWidget
 
 
@@ -73,8 +73,10 @@
 
         self.__resultsModel = TestResultsModel(self)
         self.__resultsModel.summary.connect(self.__setStatusLabel)
+        self.__resultFilterModel = TestResultsFilterModel(self)
+        self.__resultFilterModel.setSourceModel(self.__resultsModel)
         self.__resultsTree = TestResultsTreeView(self)
-        self.__resultsTree.setModel(self.__resultsModel)
+        self.__resultsTree.setModel(self.__resultFilterModel)
         self.__resultsTree.goto.connect(self.__showSource)
         self.resultsGroupBox.layout().addWidget(self.__resultsTree)
 
@@ -99,6 +101,8 @@
         )
         self.testComboBox.lineEdit().setClearButtonEnabled(True)
 
+        self.__allFilter = self.tr("<all>")
+
         # create some more dialog buttons for orchestration
         self.__showLogButton = self.buttonBox.addButton(
             self.tr("Show Output..."), QDialogButtonBox.ButtonRole.ActionRole
@@ -932,8 +936,10 @@
                 name=name,
                 id=id,
                 message=desc,
+                filename=filename,
+                lineno=lineno,
             )
-            for id, name, desc in testNames
+            for id, name, desc, filename, lineno in testNames
         ]
         self.__resultsModel.addTestResults(testResults)
         self.__resultsTree.resizeColumns()
@@ -1029,6 +1035,7 @@
         self.__testExecutor = None
 
         self.__adjustPendingState()
+        self.__updateStatusFilterComboBox()
 
     @pyqtSlot(int, float)
     def __testRunFinished(self, noTests, duration):
@@ -1064,6 +1071,7 @@
         executor.
         """
         self.__resultsModel.clear()
+        self.statusFilterComboBox.clear()
 
     def __adjustPendingState(self):
         """
@@ -1202,6 +1210,26 @@
             with contextlib.suppress(RuntimeError):
                 editor.close()
 
+    @pyqtSlot(str)
+    def on_statusFilterComboBox_currentTextChanged(self, status):
+        """
+        Private slot handling the selection of a status for items to be shown.
+
+        @param status selected status
+        @type str
+        """
+        # TODO: not yet implemented
+        if status == self.__allFilter:
+            status = ""
+
+        self.__resultFilterModel.setStatusFilterString(status)
+
+    def __updateStatusFilterComboBox(self):
+        statusFilters = self.__resultsModel.getStatusFilterList()
+        self.statusFilterComboBox.clear()
+        self.statusFilterComboBox.addItem(self.__allFilter)
+        self.statusFilterComboBox.addItems(sorted(statusFilters))
+
 
 class TestingWindow(EricMainWindow):
     """
--- a/src/eric7/Testing/TestingWidget.ui	Tue Dec 12 09:35:39 2023 +0100
+++ b/src/eric7/Testing/TestingWidget.ui	Tue Dec 12 16:43:51 2023 +0100
@@ -402,6 +402,46 @@
          </property>
          <layout class="QVBoxLayout" name="verticalLayout_5">
           <item>
+           <layout class="QHBoxLayout" name="horizontalLayout">
+            <item>
+             <spacer name="horizontalSpacer_3">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item>
+             <widget class="QLabel" name="label_6">
+              <property name="text">
+               <string>Status Filter:</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QComboBox" name="statusFilterComboBox">
+              <property name="minimumSize">
+               <size>
+                <width>150</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="toolTip">
+               <string>Select the status of items to be shown (empty for all).</string>
+              </property>
+              <property name="sizeAdjustPolicy">
+               <enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+          <item>
            <widget class="QLabel" name="statusLabel">
             <property name="text">
              <string/>
@@ -484,6 +524,7 @@
   <tabstop>coverageCheckBox</tabstop>
   <tabstop>coverageEraseCheckBox</tabstop>
   <tabstop>failfastCheckBox</tabstop>
+  <tabstop>statusFilterComboBox</tabstop>
  </tabstops>
  <resources/>
  <connections>

eric ide

mercurial