48 IDLE = 0 # idle, no test were run yet |
48 IDLE = 0 # idle, no test were run yet |
49 RUNNING = 1 # test run being performed |
49 RUNNING = 1 # test run being performed |
50 STOPPED = 2 # test run finished |
50 STOPPED = 2 # test run finished |
51 |
51 |
52 |
52 |
53 # TODO: add a "Show Coverage" function using PyCoverageDialog |
|
54 |
|
55 class TestingWidget(QWidget, Ui_TestingWidget): |
53 class TestingWidget(QWidget, Ui_TestingWidget): |
56 """ |
54 """ |
57 Class implementing a widget to orchestrate unit test execution. |
55 Class implementing a widget to orchestrate unit test execution. |
58 |
56 |
59 @signal testFile(str, int, bool) emitted to show the source of a |
57 @signal testFile(str, int, bool) emitted to show the source of a |
100 QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon) |
98 QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon) |
101 |
99 |
102 self.testComboBox.lineEdit().setClearButtonEnabled(True) |
100 self.testComboBox.lineEdit().setClearButtonEnabled(True) |
103 |
101 |
104 # create some more dialog buttons for orchestration |
102 # create some more dialog buttons for orchestration |
|
103 self.__showCoverageButton = self.buttonBox.addButton( |
|
104 self.tr("Show Coverage..."), |
|
105 QDialogButtonBox.ButtonRole.ActionRole) |
|
106 self.__showCoverageButton.setToolTip( |
|
107 self.tr("Show code coverage in a new dialog")) |
|
108 self.__showCoverageButton.setWhatsThis(self.tr( |
|
109 """<b>Show Coverage...</b>""" |
|
110 """<p>This button opens a dialog containing the collected code""" |
|
111 """ coverage data.</p>""")) |
|
112 |
105 self.__startButton = self.buttonBox.addButton( |
113 self.__startButton = self.buttonBox.addButton( |
106 self.tr("Start"), QDialogButtonBox.ButtonRole.ActionRole) |
114 self.tr("Start"), QDialogButtonBox.ButtonRole.ActionRole) |
107 |
115 |
108 self.__startButton.setToolTip(self.tr( |
116 self.__startButton.setToolTip(self.tr( |
109 "Start the selected testsuite")) |
117 "Start the selected testsuite")) |
157 self.__fileHistory = [] |
165 self.__fileHistory = [] |
158 self.__testNameHistory = [] |
166 self.__testNameHistory = [] |
159 self.__recentFramework = "" |
167 self.__recentFramework = "" |
160 self.__recentEnvironment = "" |
168 self.__recentEnvironment = "" |
161 self.__failedTests = [] |
169 self.__failedTests = [] |
|
170 |
|
171 self.__coverageFile = "" |
|
172 self.__coverageDialog = None |
162 |
173 |
163 self.__editors = [] |
174 self.__editors = [] |
164 self.__testExecutor = None |
175 self.__testExecutor = None |
165 |
176 |
166 # connect some signals |
177 # connect some signals |
475 self.__stopButton.setEnabled( |
486 self.__stopButton.setEnabled( |
476 self.__mode == TestingWidgetModes.RUNNING) |
487 self.__mode == TestingWidgetModes.RUNNING) |
477 self.__stopButton.setDefault( |
488 self.__stopButton.setDefault( |
478 self.__mode == TestingWidgetModes.RUNNING) |
489 self.__mode == TestingWidgetModes.RUNNING) |
479 |
490 |
|
491 # Code coverage button |
|
492 self.__showCoverageButton.setEnabled( |
|
493 self.__mode == TestingWidgetModes.STOPPED and |
|
494 bool(self.__coverageFile) and |
|
495 ( |
|
496 (self.discoverCheckBox.isChecked() and |
|
497 bool(self.discoveryPicker.currentText())) or |
|
498 bool(self.testsuitePicker.currentText()) |
|
499 ) |
|
500 ) |
|
501 |
480 # Close button |
502 # Close button |
481 self.buttonBox.button( |
503 self.buttonBox.button( |
482 QDialogButtonBox.StandardButton.Close |
504 QDialogButtonBox.StandardButton.Close |
483 ).setEnabled(self.__mode in ( |
505 ).setEnabled(self.__mode in ( |
484 TestingWidgetModes.IDLE, TestingWidgetModes.STOPPED |
506 TestingWidgetModes.IDLE, TestingWidgetModes.STOPPED |
515 |
537 |
516 self.__totalCount = 0 |
538 self.__totalCount = 0 |
517 self.__runCount = 0 |
539 self.__runCount = 0 |
518 |
540 |
519 self.__coverageFile = "" |
541 self.__coverageFile = "" |
520 # TODO: implement the handling of the 'Show Coverage' button |
|
521 |
542 |
522 self.sbLabel.setText(self.tr("Running")) |
543 self.sbLabel.setText(self.tr("Running")) |
523 self.tabWidget.setCurrentIndex(1) |
544 self.tabWidget.setCurrentIndex(1) |
524 self.__updateButtonBoxButtons() |
545 self.__updateButtonBoxButtons() |
525 self.__updateProgress() |
546 self.__updateProgress() |
603 self.__saveRecent() |
624 self.__saveRecent() |
604 elif button == self.__stopButton: |
625 elif button == self.__stopButton: |
605 self.__stopTests() |
626 self.__stopTests() |
606 elif button == self.__startFailedButton: |
627 elif button == self.__startFailedButton: |
607 self.startTests(failedOnly=True) |
628 self.startTests(failedOnly=True) |
|
629 elif button == self.__showCoverageButton: |
|
630 self.__showCoverageDialog() |
608 |
631 |
609 @pyqtSlot(int) |
632 @pyqtSlot(int) |
610 def on_venvComboBox_currentIndexChanged(self, index): |
633 def on_venvComboBox_currentIndexChanged(self, index): |
611 """ |
634 """ |
612 Private slot handling the selection of a virtual environment. |
635 Private slot handling the selection of a virtual environment. |
701 testName = "suite" |
724 testName = "suite" |
702 |
725 |
703 self.sbLabel.setText(self.tr("Preparing Testsuite")) |
726 self.sbLabel.setText(self.tr("Preparing Testsuite")) |
704 QCoreApplication.processEvents() |
727 QCoreApplication.processEvents() |
705 |
728 |
|
729 if self.__project: |
|
730 mainScript = self.__project.getMainScript(True) |
|
731 coverageFile = os.path.splitext(mainScript)[0] + ".coverage" |
|
732 else: |
|
733 coverageFile = "" |
706 interpreter = self.__venvManager.getVirtualenvInterpreter( |
734 interpreter = self.__venvManager.getVirtualenvInterpreter( |
707 self.__recentEnvironment) |
735 self.__recentEnvironment) |
708 config = TestConfig( |
736 config = TestConfig( |
709 interpreter=interpreter, |
737 interpreter=interpreter, |
710 discover=self.discoverCheckBox.isChecked(), |
738 discover=discover, |
711 discoveryStart=discoveryStart, |
739 discoveryStart=discoveryStart, |
712 testFilename=testFileName, |
740 testFilename=testFileName, |
713 testName=testName, |
741 testName=testName, |
714 failFast=self.failfastCheckBox.isChecked(), |
742 failFast=self.failfastCheckBox.isChecked(), |
715 failedOnly=failedOnly, |
743 failedOnly=failedOnly, |
716 collectCoverage=self.coverageCheckBox.isChecked(), |
744 collectCoverage=self.coverageCheckBox.isChecked(), |
717 eraseCoverage=self.coverageEraseCheckBox.isChecked(), |
745 eraseCoverage=self.coverageEraseCheckBox.isChecked(), |
|
746 coverageFile=coverageFile, |
718 ) |
747 ) |
719 |
748 |
720 self.__testExecutor = self.__frameworkRegistry.createExecutor( |
749 self.__testExecutor = self.__frameworkRegistry.createExecutor( |
721 self.__recentFramework, self) |
750 self.__recentFramework, self) |
722 self.__testExecutor.collected.connect(self.__testsCollected) |
751 self.__testExecutor.collected.connect(self.__testsCollected) |
886 |
915 |
887 @param coverageFile file containing the coverage data |
916 @param coverageFile file containing the coverage data |
888 @type str |
917 @type str |
889 """ |
918 """ |
890 self.__coverageFile = coverageFile |
919 self.__coverageFile = coverageFile |
891 |
920 |
892 # TODO: implement the handling of the 'Show Coverage' button |
921 @pyqtSlot() |
|
922 def __showCoverageDialog(self): |
|
923 """ |
|
924 Private slot to show a code coverage dialog for the most recent test |
|
925 run. |
|
926 """ |
|
927 if self.__coverageDialog is None: |
|
928 from DataViews.PyCoverageDialog import PyCoverageDialog |
|
929 self.__coverageDialog = PyCoverageDialog(self) |
|
930 self.__coverageDialog.openFile.connect(self.__openEditor) |
|
931 |
|
932 if self.discoverCheckBox.isChecked(): |
|
933 testDir = self.discoveryPicker.currentText() |
|
934 else: |
|
935 testDir = os.path.dirname(self.testsuitePicker.currentText()) |
|
936 if testDir: |
|
937 self.__coverageDialog.show() |
|
938 self.__coverageDialog.start(self.__coverageFile, testDir) |
893 |
939 |
894 @pyqtSlot(str) |
940 @pyqtSlot(str) |
895 def __setStatusLabel(self, statusText): |
941 def __setStatusLabel(self, statusText): |
896 """ |
942 """ |
897 Private slot to set the status label to the text sent by the model. |
943 Private slot to set the status label to the text sent by the model. |
934 # running as part of eric IDE |
980 # running as part of eric IDE |
935 self.testFile.emit(filename, lineno, True) |
981 self.testFile.emit(filename, lineno, True) |
936 else: |
982 else: |
937 self.__openEditor(filename, lineno) |
983 self.__openEditor(filename, lineno) |
938 |
984 |
939 def __openEditor(self, filename, linenumber): |
985 def __openEditor(self, filename, linenumber=1): |
940 """ |
986 """ |
941 Private method to open an editor window for the given file. |
987 Private method to open an editor window for the given file. |
942 |
988 |
943 Note: This method opens an editor window when the testing dialog |
989 Note: This method opens an editor window when the testing dialog |
944 is called as a standalone application. |
990 is called as a standalone application. |
945 |
991 |
946 @param filename path of the file to be opened |
992 @param filename path of the file to be opened |
947 @type str |
993 @type str |
948 @param linenumber line number to place the cursor at |
994 @param linenumber line number to place the cursor at (defaults to 1) |
949 @type int |
995 @type int (optional) |
950 """ |
996 """ |
951 from QScintilla.MiniEditor import MiniEditor |
997 from QScintilla.MiniEditor import MiniEditor |
952 editor = MiniEditor(filename, "Python3", self) |
998 editor = MiniEditor(filename, "Python3", self) |
953 editor.gotoLine(linenumber) |
999 editor.gotoLine(linenumber) |
954 editor.show() |
1000 editor.show() |