11 import enum |
11 import enum |
12 import locale |
12 import locale |
13 import os |
13 import os |
14 |
14 |
15 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QEvent, QCoreApplication |
15 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QEvent, QCoreApplication |
16 from PyQt6.QtWidgets import ( |
16 from PyQt6.QtWidgets import QAbstractButton, QComboBox, QDialogButtonBox, QWidget |
17 QAbstractButton, QComboBox, QDialogButtonBox, QWidget |
|
18 ) |
|
19 |
17 |
20 from EricWidgets import EricMessageBox |
18 from EricWidgets import EricMessageBox |
21 from EricWidgets.EricApplication import ericApp |
19 from EricWidgets.EricApplication import ericApp |
22 from EricWidgets.EricMainWindow import EricMainWindow |
20 from EricWidgets.EricMainWindow import EricMainWindow |
23 from EricWidgets.EricPathPicker import EricPathPickerModes |
21 from EricWidgets.EricPathPicker import EricPathPickerModes |
24 |
22 |
25 from .Ui_TestingWidget import Ui_TestingWidget |
23 from .Ui_TestingWidget import Ui_TestingWidget |
26 |
24 |
27 from .TestResultsTree import TestResultsModel, TestResultsTreeView |
25 from .TestResultsTree import TestResultsModel, TestResultsTreeView |
28 from .Interfaces import Frameworks |
26 from .Interfaces import Frameworks |
29 from .Interfaces.TestExecutorBase import ( |
27 from .Interfaces.TestExecutorBase import TestConfig, TestResult, TestResultCategory |
30 TestConfig, TestResult, TestResultCategory |
|
31 ) |
|
32 from .Interfaces.TestFrameworkRegistry import TestFrameworkRegistry |
28 from .Interfaces.TestFrameworkRegistry import TestFrameworkRegistry |
33 |
29 |
34 import Preferences |
30 import Preferences |
35 import UI.PixmapCache |
31 import UI.PixmapCache |
36 |
32 |
37 from Globals import ( |
33 from Globals import ( |
38 recentNameTestDiscoverHistory, recentNameTestFileHistory, |
34 recentNameTestDiscoverHistory, |
39 recentNameTestNameHistory, recentNameTestFramework, |
35 recentNameTestFileHistory, |
40 recentNameTestEnvironment |
36 recentNameTestNameHistory, |
|
37 recentNameTestFramework, |
|
38 recentNameTestEnvironment, |
41 ) |
39 ) |
42 |
40 |
43 |
41 |
44 class TestingWidgetModes(enum.Enum): |
42 class TestingWidgetModes(enum.Enum): |
45 """ |
43 """ |
46 Class defining the various modes of the testing widget. |
44 Class defining the various modes of the testing widget. |
47 """ |
45 """ |
48 IDLE = 0 # idle, no test were run yet |
46 |
49 RUNNING = 1 # test run being performed |
47 IDLE = 0 # idle, no test were run yet |
50 STOPPED = 2 # test run finished |
48 RUNNING = 1 # test run being performed |
|
49 STOPPED = 2 # test run finished |
51 |
50 |
52 |
51 |
53 class TestingWidget(QWidget, Ui_TestingWidget): |
52 class TestingWidget(QWidget, Ui_TestingWidget): |
54 """ |
53 """ |
55 Class implementing a widget to orchestrate unit test execution. |
54 Class implementing a widget to orchestrate unit test execution. |
56 |
55 |
57 @signal testFile(str, int, bool) emitted to show the source of a |
56 @signal testFile(str, int, bool) emitted to show the source of a |
58 test file |
57 test file |
59 @signal testRunStopped() emitted after a test run has finished |
58 @signal testRunStopped() emitted after a test run has finished |
60 """ |
59 """ |
|
60 |
61 testFile = pyqtSignal(str, int, bool) |
61 testFile = pyqtSignal(str, int, bool) |
62 testRunStopped = pyqtSignal() |
62 testRunStopped = pyqtSignal() |
63 |
63 |
64 def __init__(self, testfile=None, parent=None): |
64 def __init__(self, testfile=None, parent=None): |
65 """ |
65 """ |
66 Constructor |
66 Constructor |
67 |
67 |
68 @param testfile file name of the test to load |
68 @param testfile file name of the test to load |
69 @type str |
69 @type str |
70 @param parent reference to the parent widget (defaults to None) |
70 @param parent reference to the parent widget (defaults to None) |
71 @type QWidget (optional) |
71 @type QWidget (optional) |
72 """ |
72 """ |
73 super().__init__(parent) |
73 super().__init__(parent) |
74 self.setupUi(self) |
74 self.setupUi(self) |
75 |
75 |
76 self.__resultsModel = TestResultsModel(self) |
76 self.__resultsModel = TestResultsModel(self) |
77 self.__resultsModel.summary.connect(self.__setStatusLabel) |
77 self.__resultsModel.summary.connect(self.__setStatusLabel) |
78 self.__resultsTree = TestResultsTreeView(self) |
78 self.__resultsTree = TestResultsTreeView(self) |
79 self.__resultsTree.setModel(self.__resultsModel) |
79 self.__resultsTree.setModel(self.__resultsModel) |
80 self.__resultsTree.goto.connect(self.__showSource) |
80 self.__resultsTree.goto.connect(self.__showSource) |
81 self.resultsGroupBox.layout().addWidget(self.__resultsTree) |
81 self.resultsGroupBox.layout().addWidget(self.__resultsTree) |
82 |
82 |
83 self.versionsButton.setIcon( |
83 self.versionsButton.setIcon(UI.PixmapCache.getIcon("info")) |
84 UI.PixmapCache.getIcon("info")) |
84 self.clearHistoriesButton.setIcon(UI.PixmapCache.getIcon("clearPrivateData")) |
85 self.clearHistoriesButton.setIcon( |
85 |
86 UI.PixmapCache.getIcon("clearPrivateData")) |
|
87 |
|
88 self.testsuitePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) |
86 self.testsuitePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) |
89 self.testsuitePicker.setInsertPolicy( |
87 self.testsuitePicker.setInsertPolicy(QComboBox.InsertPolicy.InsertAtTop) |
90 QComboBox.InsertPolicy.InsertAtTop) |
|
91 self.testsuitePicker.setSizeAdjustPolicy( |
88 self.testsuitePicker.setSizeAdjustPolicy( |
92 QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon) |
89 QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon |
93 |
90 ) |
|
91 |
94 self.discoveryPicker.setMode(EricPathPickerModes.DIRECTORY_MODE) |
92 self.discoveryPicker.setMode(EricPathPickerModes.DIRECTORY_MODE) |
95 self.discoveryPicker.setInsertPolicy( |
93 self.discoveryPicker.setInsertPolicy(QComboBox.InsertPolicy.InsertAtTop) |
96 QComboBox.InsertPolicy.InsertAtTop) |
|
97 self.discoveryPicker.setSizeAdjustPolicy( |
94 self.discoveryPicker.setSizeAdjustPolicy( |
98 QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon) |
95 QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon |
99 |
96 ) |
|
97 |
100 self.testComboBox.lineEdit().setClearButtonEnabled(True) |
98 self.testComboBox.lineEdit().setClearButtonEnabled(True) |
101 |
99 |
102 # create some more dialog buttons for orchestration |
100 # create some more dialog buttons for orchestration |
103 self.__showLogButton = self.buttonBox.addButton( |
101 self.__showLogButton = self.buttonBox.addButton( |
104 self.tr("Show Output..."), |
102 self.tr("Show Output..."), QDialogButtonBox.ButtonRole.ActionRole |
105 QDialogButtonBox.ButtonRole.ActionRole) |
103 ) |
106 self.__showLogButton.setToolTip( |
104 self.__showLogButton.setToolTip( |
107 self.tr("Show the output of the test runner process")) |
105 self.tr("Show the output of the test runner process") |
108 self.__showLogButton.setWhatsThis(self.tr( |
106 ) |
109 """<b>Show Output...</b""" |
107 self.__showLogButton.setWhatsThis( |
110 """<p>This button opens a dialog containing the output of the""" |
108 self.tr( |
111 """ test runner process of the most recent run.</p>""")) |
109 """<b>Show Output...</b""" |
112 |
110 """<p>This button opens a dialog containing the output of the""" |
|
111 """ test runner process of the most recent run.</p>""" |
|
112 ) |
|
113 ) |
|
114 |
113 self.__showCoverageButton = self.buttonBox.addButton( |
115 self.__showCoverageButton = self.buttonBox.addButton( |
114 self.tr("Show Coverage..."), |
116 self.tr("Show Coverage..."), QDialogButtonBox.ButtonRole.ActionRole |
115 QDialogButtonBox.ButtonRole.ActionRole) |
117 ) |
116 self.__showCoverageButton.setToolTip( |
118 self.__showCoverageButton.setToolTip( |
117 self.tr("Show code coverage in a new dialog")) |
119 self.tr("Show code coverage in a new dialog") |
118 self.__showCoverageButton.setWhatsThis(self.tr( |
120 ) |
119 """<b>Show Coverage...</b>""" |
121 self.__showCoverageButton.setWhatsThis( |
120 """<p>This button opens a dialog containing the collected code""" |
122 self.tr( |
121 """ coverage data.</p>""")) |
123 """<b>Show Coverage...</b>""" |
122 |
124 """<p>This button opens a dialog containing the collected code""" |
|
125 """ coverage data.</p>""" |
|
126 ) |
|
127 ) |
|
128 |
123 self.__startButton = self.buttonBox.addButton( |
129 self.__startButton = self.buttonBox.addButton( |
124 self.tr("Start"), QDialogButtonBox.ButtonRole.ActionRole) |
130 self.tr("Start"), QDialogButtonBox.ButtonRole.ActionRole |
125 |
131 ) |
126 self.__startButton.setToolTip(self.tr( |
132 |
127 "Start the selected testsuite")) |
133 self.__startButton.setToolTip(self.tr("Start the selected testsuite")) |
128 self.__startButton.setWhatsThis(self.tr( |
134 self.__startButton.setWhatsThis( |
129 """<b>Start Test</b>""" |
135 self.tr( |
130 """<p>This button starts the test run.</p>""")) |
136 """<b>Start Test</b>""" """<p>This button starts the test run.</p>""" |
131 |
137 ) |
|
138 ) |
|
139 |
132 self.__startFailedButton = self.buttonBox.addButton( |
140 self.__startFailedButton = self.buttonBox.addButton( |
133 self.tr("Rerun Failed"), QDialogButtonBox.ButtonRole.ActionRole) |
141 self.tr("Rerun Failed"), QDialogButtonBox.ButtonRole.ActionRole |
|
142 ) |
134 self.__startFailedButton.setToolTip( |
143 self.__startFailedButton.setToolTip( |
135 self.tr("Reruns failed tests of the selected testsuite")) |
144 self.tr("Reruns failed tests of the selected testsuite") |
136 self.__startFailedButton.setWhatsThis(self.tr( |
145 ) |
137 """<b>Rerun Failed</b>""" |
146 self.__startFailedButton.setWhatsThis( |
138 """<p>This button reruns all failed tests of the most recent""" |
147 self.tr( |
139 """ test run.</p>""")) |
148 """<b>Rerun Failed</b>""" |
140 |
149 """<p>This button reruns all failed tests of the most recent""" |
|
150 """ test run.</p>""" |
|
151 ) |
|
152 ) |
|
153 |
141 self.__stopButton = self.buttonBox.addButton( |
154 self.__stopButton = self.buttonBox.addButton( |
142 self.tr("Stop"), QDialogButtonBox.ButtonRole.ActionRole) |
155 self.tr("Stop"), QDialogButtonBox.ButtonRole.ActionRole |
|
156 ) |
143 self.__stopButton.setToolTip(self.tr("Stop the running test")) |
157 self.__stopButton.setToolTip(self.tr("Stop the running test")) |
144 self.__stopButton.setWhatsThis(self.tr( |
158 self.__stopButton.setWhatsThis( |
145 """<b>Stop Test</b>""" |
159 self.tr( |
146 """<p>This button stops a running test.</p>""")) |
160 """<b>Stop Test</b>""" """<p>This button stops a running test.</p>""" |
147 |
161 ) |
|
162 ) |
|
163 |
148 self.setWindowFlags( |
164 self.setWindowFlags( |
149 self.windowFlags() | |
165 self.windowFlags() | Qt.WindowType.WindowContextHelpButtonHint |
150 Qt.WindowType.WindowContextHelpButtonHint |
|
151 ) |
166 ) |
152 self.setWindowIcon(UI.PixmapCache.getIcon("eric")) |
167 self.setWindowIcon(UI.PixmapCache.getIcon("eric")) |
153 self.setWindowTitle(self.tr("Testing")) |
168 self.setWindowTitle(self.tr("Testing")) |
154 |
169 |
155 try: |
170 try: |
156 # we are called from within the eric IDE |
171 # we are called from within the eric IDE |
157 self.__venvManager = ericApp().getObject("VirtualEnvManager") |
172 self.__venvManager = ericApp().getObject("VirtualEnvManager") |
158 self.__project = ericApp().getObject("Project") |
173 self.__project = ericApp().getObject("Project") |
159 self.__project.projectOpened.connect(self.__projectOpened) |
174 self.__project.projectOpened.connect(self.__projectOpened) |
160 self.__project.projectClosed.connect(self.__projectClosed) |
175 self.__project.projectClosed.connect(self.__projectClosed) |
161 except KeyError: |
176 except KeyError: |
162 # we were called as a standalone application |
177 # we were called as a standalone application |
163 from VirtualEnv.VirtualenvManager import VirtualenvManager |
178 from VirtualEnv.VirtualenvManager import VirtualenvManager |
|
179 |
164 self.__venvManager = VirtualenvManager(self) |
180 self.__venvManager = VirtualenvManager(self) |
165 self.__venvManager.virtualEnvironmentAdded.connect( |
181 self.__venvManager.virtualEnvironmentAdded.connect( |
166 self.__populateVenvComboBox) |
182 self.__populateVenvComboBox |
|
183 ) |
167 self.__venvManager.virtualEnvironmentRemoved.connect( |
184 self.__venvManager.virtualEnvironmentRemoved.connect( |
168 self.__populateVenvComboBox) |
185 self.__populateVenvComboBox |
|
186 ) |
169 self.__venvManager.virtualEnvironmentChanged.connect( |
187 self.__venvManager.virtualEnvironmentChanged.connect( |
170 self.__populateVenvComboBox) |
188 self.__populateVenvComboBox |
|
189 ) |
171 ericApp().registerObject("VirtualEnvManager", self.__venvManager) |
190 ericApp().registerObject("VirtualEnvManager", self.__venvManager) |
172 |
191 |
173 self.__project = None |
192 self.__project = None |
174 |
193 |
175 self.__discoverHistory = [] |
194 self.__discoverHistory = [] |
176 self.__fileHistory = [] |
195 self.__fileHistory = [] |
177 self.__testNameHistory = [] |
196 self.__testNameHistory = [] |
178 self.__recentFramework = "" |
197 self.__recentFramework = "" |
179 self.__recentEnvironment = "" |
198 self.__recentEnvironment = "" |
180 self.__failedTests = [] |
199 self.__failedTests = [] |
181 |
200 |
182 self.__coverageFile = "" |
201 self.__coverageFile = "" |
183 self.__coverageDialog = None |
202 self.__coverageDialog = None |
184 |
203 |
185 self.__editors = [] |
204 self.__editors = [] |
186 self.__testExecutor = None |
205 self.__testExecutor = None |
187 self.__recentLog = "" |
206 self.__recentLog = "" |
188 |
207 |
189 # connect some signals |
208 # connect some signals |
190 self.discoveryPicker.editTextChanged.connect( |
209 self.discoveryPicker.editTextChanged.connect(self.__resetResults) |
191 self.__resetResults) |
210 self.testsuitePicker.editTextChanged.connect(self.__resetResults) |
192 self.testsuitePicker.editTextChanged.connect( |
211 self.testComboBox.editTextChanged.connect(self.__resetResults) |
193 self.__resetResults) |
212 |
194 self.testComboBox.editTextChanged.connect( |
|
195 self.__resetResults) |
|
196 |
|
197 self.__frameworkRegistry = TestFrameworkRegistry() |
213 self.__frameworkRegistry = TestFrameworkRegistry() |
198 for framework in Frameworks: |
214 for framework in Frameworks: |
199 self.__frameworkRegistry.register(framework) |
215 self.__frameworkRegistry.register(framework) |
200 |
216 |
201 self.__setIdleMode() |
217 self.__setIdleMode() |
202 |
218 |
203 self.__loadRecent() |
219 self.__loadRecent() |
204 self.__populateVenvComboBox() |
220 self.__populateVenvComboBox() |
205 |
221 |
206 if self.__project and self.__project.isOpen(): |
222 if self.__project and self.__project.isOpen(): |
207 self.venvComboBox.setCurrentText(self.__project.getProjectVenv()) |
223 self.venvComboBox.setCurrentText(self.__project.getProjectVenv()) |
208 self.frameworkComboBox.setCurrentText( |
224 self.frameworkComboBox.setCurrentText( |
209 self.__project.getProjectTestingFramework()) |
225 self.__project.getProjectTestingFramework() |
|
226 ) |
210 self.__insertDiscovery(self.__project.getProjectPath()) |
227 self.__insertDiscovery(self.__project.getProjectPath()) |
211 else: |
228 else: |
212 self.__insertDiscovery("") |
229 self.__insertDiscovery("") |
213 |
230 |
214 self.__insertTestFile(testfile) |
231 self.__insertTestFile(testfile) |
215 self.__insertTestName("") |
232 self.__insertTestName("") |
216 |
233 |
217 self.clearHistoriesButton.clicked.connect(self.clearRecent) |
234 self.clearHistoriesButton.clicked.connect(self.clearRecent) |
218 |
235 |
219 self.tabWidget.setCurrentIndex(0) |
236 self.tabWidget.setCurrentIndex(0) |
220 |
237 |
221 def __populateVenvComboBox(self): |
238 def __populateVenvComboBox(self): |
222 """ |
239 """ |
223 Private method to (re-)populate the virtual environments selector. |
240 Private method to (re-)populate the virtual environments selector. |
224 """ |
241 """ |
225 currentText = self.venvComboBox.currentText() |
242 currentText = self.venvComboBox.currentText() |
226 if not currentText: |
243 if not currentText: |
227 currentText = self.__recentEnvironment |
244 currentText = self.__recentEnvironment |
228 |
245 |
229 self.venvComboBox.clear() |
246 self.venvComboBox.clear() |
230 self.venvComboBox.addItem("") |
247 self.venvComboBox.addItem("") |
231 self.venvComboBox.addItems( |
248 self.venvComboBox.addItems(sorted(self.__venvManager.getVirtualenvNames())) |
232 sorted(self.__venvManager.getVirtualenvNames())) |
|
233 self.venvComboBox.setCurrentText(currentText) |
249 self.venvComboBox.setCurrentText(currentText) |
234 |
250 |
235 def __populateTestFrameworkComboBox(self): |
251 def __populateTestFrameworkComboBox(self): |
236 """ |
252 """ |
237 Private method to (re-)populate the test framework selector. |
253 Private method to (re-)populate the test framework selector. |
238 """ |
254 """ |
239 currentText = self.frameworkComboBox.currentText() |
255 currentText = self.frameworkComboBox.currentText() |
240 if not currentText: |
256 if not currentText: |
241 currentText = self.__recentFramework |
257 currentText = self.__recentFramework |
242 |
258 |
243 self.frameworkComboBox.clear() |
259 self.frameworkComboBox.clear() |
244 |
260 |
245 if bool(self.venvComboBox.currentText()): |
261 if bool(self.venvComboBox.currentText()): |
246 interpreter = self.__venvManager.getVirtualenvInterpreter( |
262 interpreter = self.__venvManager.getVirtualenvInterpreter( |
247 self.venvComboBox.currentText()) |
263 self.venvComboBox.currentText() |
|
264 ) |
248 self.frameworkComboBox.addItem("") |
265 self.frameworkComboBox.addItem("") |
249 for index, (name, executor) in enumerate( |
266 for index, (name, executor) in enumerate( |
250 sorted(self.__frameworkRegistry.getFrameworks().items()), |
267 sorted(self.__frameworkRegistry.getFrameworks().items()), start=1 |
251 start=1 |
|
252 ): |
268 ): |
253 isInstalled = executor.isInstalled(interpreter) |
269 isInstalled = executor.isInstalled(interpreter) |
254 entry = ( |
270 entry = ( |
255 name |
271 name if isInstalled else self.tr("{0} (not available)").format(name) |
256 if isInstalled else |
|
257 self.tr("{0} (not available)").format(name) |
|
258 ) |
272 ) |
259 self.frameworkComboBox.addItem(entry) |
273 self.frameworkComboBox.addItem(entry) |
260 self.frameworkComboBox.model().item(index).setEnabled( |
274 self.frameworkComboBox.model().item(index).setEnabled(isInstalled) |
261 isInstalled) |
275 |
262 |
|
263 self.frameworkComboBox.setCurrentText(self.__recentFramework) |
276 self.frameworkComboBox.setCurrentText(self.__recentFramework) |
264 |
277 |
265 def getResultsModel(self): |
278 def getResultsModel(self): |
266 """ |
279 """ |
267 Public method to get a reference to the model containing the test |
280 Public method to get a reference to the model containing the test |
268 result data. |
281 result data. |
269 |
282 |
270 @return reference to the test results model |
283 @return reference to the test results model |
271 @rtype TestResultsModel |
284 @rtype TestResultsModel |
272 """ |
285 """ |
273 return self.__resultsModel |
286 return self.__resultsModel |
274 |
287 |
275 def hasFailedTests(self): |
288 def hasFailedTests(self): |
276 """ |
289 """ |
277 Public method to check for failed tests. |
290 Public method to check for failed tests. |
278 |
291 |
279 @return flag indicating the existence of failed tests |
292 @return flag indicating the existence of failed tests |
280 @rtype bool |
293 @rtype bool |
281 """ |
294 """ |
282 return bool(self.__resultsModel.getFailedTests()) |
295 return bool(self.__resultsModel.getFailedTests()) |
283 |
296 |
284 def getFailedTests(self): |
297 def getFailedTests(self): |
285 """ |
298 """ |
286 Public method to get the list of failed tests (if any). |
299 Public method to get the list of failed tests (if any). |
287 |
300 |
288 @return list of IDs of failed tests |
301 @return list of IDs of failed tests |
289 @rtype list of str |
302 @rtype list of str |
290 """ |
303 """ |
291 return self.__failedTests[:] |
304 return self.__failedTests[:] |
292 |
305 |
293 @pyqtSlot(str) |
306 @pyqtSlot(str) |
294 def __insertHistory(self, widget, history, item): |
307 def __insertHistory(self, widget, history, item): |
295 """ |
308 """ |
296 Private slot to insert an item into a history object. |
309 Private slot to insert an item into a history object. |
297 |
310 |
298 @param widget reference to the widget |
311 @param widget reference to the widget |
299 @type QComboBox or EricComboPathPicker |
312 @type QComboBox or EricComboPathPicker |
300 @param history array containing the history |
313 @param history array containing the history |
301 @type list of str |
314 @type list of str |
302 @param item item to be inserted |
315 @param item item to be inserted |
309 history.remove(item) |
322 history.remove(item) |
310 history.insert(0, item) |
323 history.insert(0, item) |
311 widget.clear() |
324 widget.clear() |
312 widget.addItems(history) |
325 widget.addItems(history) |
313 widget.setEditText(item) |
326 widget.setEditText(item) |
314 |
327 |
315 @pyqtSlot(str) |
328 @pyqtSlot(str) |
316 def __insertDiscovery(self, start): |
329 def __insertDiscovery(self, start): |
317 """ |
330 """ |
318 Private slot to insert the discovery start directory into the |
331 Private slot to insert the discovery start directory into the |
319 discoveryPicker object. |
332 discoveryPicker object. |
320 |
333 |
321 @param start start directory name to be inserted |
334 @param start start directory name to be inserted |
322 @type str |
335 @type str |
323 """ |
336 """ |
324 self.__insertHistory(self.discoveryPicker, self.__discoverHistory, |
337 self.__insertHistory(self.discoveryPicker, self.__discoverHistory, start) |
325 start) |
338 |
326 |
|
327 @pyqtSlot(str) |
339 @pyqtSlot(str) |
328 def setTestFile(self, testFile, forProject=False): |
340 def setTestFile(self, testFile, forProject=False): |
329 """ |
341 """ |
330 Public slot to set the given test file as the current one. |
342 Public slot to set the given test file as the current one. |
331 |
343 |
332 @param testFile path of the test file |
344 @param testFile path of the test file |
333 @type str |
345 @type str |
334 @param forProject flag indicating that this call is for a project |
346 @param forProject flag indicating that this call is for a project |
335 (defaults to False) |
347 (defaults to False) |
336 @type bool (optional) |
348 @type bool (optional) |
337 """ |
349 """ |
338 if testFile: |
350 if testFile: |
339 self.__insertTestFile(testFile) |
351 self.__insertTestFile(testFile) |
340 |
352 |
341 self.discoverCheckBox.setChecked(forProject or not bool(testFile)) |
353 self.discoverCheckBox.setChecked(forProject or not bool(testFile)) |
342 |
354 |
343 if forProject: |
355 if forProject: |
344 self.__projectOpened() |
356 self.__projectOpened() |
345 |
357 |
346 self.tabWidget.setCurrentIndex(0) |
358 self.tabWidget.setCurrentIndex(0) |
347 |
359 |
348 @pyqtSlot(str) |
360 @pyqtSlot(str) |
349 def __insertTestFile(self, prog): |
361 def __insertTestFile(self, prog): |
350 """ |
362 """ |
351 Private slot to insert a test file name into the testsuitePicker |
363 Private slot to insert a test file name into the testsuitePicker |
352 object. |
364 object. |
353 |
365 |
354 @param prog test file name to be inserted |
366 @param prog test file name to be inserted |
355 @type str |
367 @type str |
356 """ |
368 """ |
357 self.__insertHistory(self.testsuitePicker, self.__fileHistory, |
369 self.__insertHistory(self.testsuitePicker, self.__fileHistory, prog) |
358 prog) |
370 |
359 |
|
360 @pyqtSlot(str) |
371 @pyqtSlot(str) |
361 def __insertTestName(self, testName): |
372 def __insertTestName(self, testName): |
362 """ |
373 """ |
363 Private slot to insert a test name into the testComboBox object. |
374 Private slot to insert a test name into the testComboBox object. |
364 |
375 |
365 @param testName name of the test to be inserted |
376 @param testName name of the test to be inserted |
366 @type str |
377 @type str |
367 """ |
378 """ |
368 self.__insertHistory(self.testComboBox, self.__testNameHistory, |
379 self.__insertHistory(self.testComboBox, self.__testNameHistory, testName) |
369 testName) |
380 |
370 |
|
371 def __loadRecent(self): |
381 def __loadRecent(self): |
372 """ |
382 """ |
373 Private method to load the most recently used lists. |
383 Private method to load the most recently used lists. |
374 """ |
384 """ |
375 Preferences.Prefs.rsettings.sync() |
385 Preferences.Prefs.rsettings.sync() |
376 |
386 |
377 # 1. recently selected test framework and virtual environment |
387 # 1. recently selected test framework and virtual environment |
378 self.__recentEnvironment = Preferences.Prefs.rsettings.value( |
388 self.__recentEnvironment = Preferences.Prefs.rsettings.value( |
379 recentNameTestEnvironment, "") |
389 recentNameTestEnvironment, "" |
|
390 ) |
380 self.__recentFramework = Preferences.Prefs.rsettings.value( |
391 self.__recentFramework = Preferences.Prefs.rsettings.value( |
381 recentNameTestFramework, "") |
392 recentNameTestFramework, "" |
382 |
393 ) |
|
394 |
383 # 2. discovery history |
395 # 2. discovery history |
384 self.__discoverHistory = [] |
396 self.__discoverHistory = [] |
385 rs = Preferences.Prefs.rsettings.value( |
397 rs = Preferences.Prefs.rsettings.value(recentNameTestDiscoverHistory) |
386 recentNameTestDiscoverHistory) |
|
387 if rs is not None: |
398 if rs is not None: |
388 recent = [f for f in Preferences.toList(rs) if os.path.exists(f)] |
399 recent = [f for f in Preferences.toList(rs) if os.path.exists(f)] |
389 self.__discoverHistory = recent[ |
400 self.__discoverHistory = recent[: Preferences.getDebugger("RecentNumber")] |
390 :Preferences.getDebugger("RecentNumber")] |
401 |
391 |
|
392 # 3. test file history |
402 # 3. test file history |
393 self.__fileHistory = [] |
403 self.__fileHistory = [] |
394 rs = Preferences.Prefs.rsettings.value( |
404 rs = Preferences.Prefs.rsettings.value(recentNameTestFileHistory) |
395 recentNameTestFileHistory) |
|
396 if rs is not None: |
405 if rs is not None: |
397 recent = [f for f in Preferences.toList(rs) if os.path.exists(f)] |
406 recent = [f for f in Preferences.toList(rs) if os.path.exists(f)] |
398 self.__fileHistory = recent[ |
407 self.__fileHistory = recent[: Preferences.getDebugger("RecentNumber")] |
399 :Preferences.getDebugger("RecentNumber")] |
408 |
400 |
|
401 # 4. test name history |
409 # 4. test name history |
402 self.__testNameHistory = [] |
410 self.__testNameHistory = [] |
403 rs = Preferences.Prefs.rsettings.value( |
411 rs = Preferences.Prefs.rsettings.value(recentNameTestNameHistory) |
404 recentNameTestNameHistory) |
|
405 if rs is not None: |
412 if rs is not None: |
406 recent = [n for n in Preferences.toList(rs) if n] |
413 recent = [n for n in Preferences.toList(rs) if n] |
407 self.__testNameHistory = recent[ |
414 self.__testNameHistory = recent[: Preferences.getDebugger("RecentNumber")] |
408 :Preferences.getDebugger("RecentNumber")] |
415 |
409 |
|
410 def __saveRecent(self): |
416 def __saveRecent(self): |
411 """ |
417 """ |
412 Private method to save the most recently used lists. |
418 Private method to save the most recently used lists. |
413 """ |
419 """ |
414 Preferences.Prefs.rsettings.setValue( |
420 Preferences.Prefs.rsettings.setValue( |
415 recentNameTestEnvironment, self.__recentEnvironment) |
421 recentNameTestEnvironment, self.__recentEnvironment |
|
422 ) |
416 Preferences.Prefs.rsettings.setValue( |
423 Preferences.Prefs.rsettings.setValue( |
417 recentNameTestFramework, self.__recentFramework) |
424 recentNameTestFramework, self.__recentFramework |
|
425 ) |
418 Preferences.Prefs.rsettings.setValue( |
426 Preferences.Prefs.rsettings.setValue( |
419 recentNameTestDiscoverHistory, self.__discoverHistory) |
427 recentNameTestDiscoverHistory, self.__discoverHistory |
|
428 ) |
420 Preferences.Prefs.rsettings.setValue( |
429 Preferences.Prefs.rsettings.setValue( |
421 recentNameTestFileHistory, self.__fileHistory) |
430 recentNameTestFileHistory, self.__fileHistory |
|
431 ) |
422 Preferences.Prefs.rsettings.setValue( |
432 Preferences.Prefs.rsettings.setValue( |
423 recentNameTestNameHistory, self.__testNameHistory) |
433 recentNameTestNameHistory, self.__testNameHistory |
424 |
434 ) |
|
435 |
425 Preferences.Prefs.rsettings.sync() |
436 Preferences.Prefs.rsettings.sync() |
426 |
437 |
427 @pyqtSlot() |
438 @pyqtSlot() |
428 def clearRecent(self): |
439 def clearRecent(self): |
429 """ |
440 """ |
430 Public slot to clear the recently used lists. |
441 Public slot to clear the recently used lists. |
431 """ |
442 """ |
432 # clear histories |
443 # clear histories |
433 self.__discoverHistory = [] |
444 self.__discoverHistory = [] |
434 self.__fileHistory = [] |
445 self.__fileHistory = [] |
435 self.__testNameHistory = [] |
446 self.__testNameHistory = [] |
436 |
447 |
437 # clear widgets with histories |
448 # clear widgets with histories |
438 self.discoveryPicker.clear() |
449 self.discoveryPicker.clear() |
439 self.testsuitePicker.clear() |
450 self.testsuitePicker.clear() |
440 self.testComboBox.clear() |
451 self.testComboBox.clear() |
441 |
452 |
442 # sync histories |
453 # sync histories |
443 self.__saveRecent() |
454 self.__saveRecent() |
444 |
455 |
445 @pyqtSlot() |
456 @pyqtSlot() |
446 def __resetResults(self): |
457 def __resetResults(self): |
447 """ |
458 """ |
448 Private slot to reset the test results tab and data. |
459 Private slot to reset the test results tab and data. |
449 """ |
460 """ |
450 self.__totalCount = 0 |
461 self.__totalCount = 0 |
451 self.__runCount = 0 |
462 self.__runCount = 0 |
452 |
463 |
453 self.progressCounterRunCount.setText("0") |
464 self.progressCounterRunCount.setText("0") |
454 self.progressCounterRemCount.setText("0") |
465 self.progressCounterRemCount.setText("0") |
455 self.progressProgressBar.setMaximum(100) |
466 self.progressProgressBar.setMaximum(100) |
456 self.progressProgressBar.setValue(0) |
467 self.progressProgressBar.setValue(0) |
457 |
468 |
458 self.statusLabel.clear() |
469 self.statusLabel.clear() |
459 |
470 |
460 self.__resultsModel.clear() |
471 self.__resultsModel.clear() |
461 self.__updateButtonBoxButtons() |
472 self.__updateButtonBoxButtons() |
462 |
473 |
463 @pyqtSlot() |
474 @pyqtSlot() |
464 def __updateButtonBoxButtons(self): |
475 def __updateButtonBoxButtons(self): |
465 """ |
476 """ |
466 Private slot to update the state of the buttons of the button box. |
477 Private slot to update the state of the buttons of the button box. |
467 """ |
478 """ |
468 failedAvailable = bool(self.__resultsModel.getFailedTests()) |
479 failedAvailable = bool(self.__resultsModel.getFailedTests()) |
469 |
480 |
470 # Start button |
481 # Start button |
471 if self.__mode in ( |
482 if self.__mode in (TestingWidgetModes.IDLE, TestingWidgetModes.STOPPED): |
472 TestingWidgetModes.IDLE, TestingWidgetModes.STOPPED |
|
473 ): |
|
474 self.__startButton.setEnabled( |
483 self.__startButton.setEnabled( |
475 bool(self.venvComboBox.currentText()) and |
484 bool(self.venvComboBox.currentText()) |
476 bool(self.frameworkComboBox.currentText()) and |
485 and bool(self.frameworkComboBox.currentText()) |
477 ( |
486 and ( |
478 (self.discoverCheckBox.isChecked() and |
487 ( |
479 bool(self.discoveryPicker.currentText())) or |
488 self.discoverCheckBox.isChecked() |
480 bool(self.testsuitePicker.currentText()) |
489 and bool(self.discoveryPicker.currentText()) |
|
490 ) |
|
491 or bool(self.testsuitePicker.currentText()) |
481 ) |
492 ) |
482 ) |
493 ) |
483 self.__startButton.setDefault( |
494 self.__startButton.setDefault( |
484 self.__mode == TestingWidgetModes.IDLE or |
495 self.__mode == TestingWidgetModes.IDLE or not failedAvailable |
485 not failedAvailable |
|
486 ) |
496 ) |
487 else: |
497 else: |
488 self.__startButton.setEnabled(False) |
498 self.__startButton.setEnabled(False) |
489 self.__startButton.setDefault(False) |
499 self.__startButton.setDefault(False) |
490 |
500 |
491 # Start Failed button |
501 # Start Failed button |
492 self.__startFailedButton.setEnabled( |
502 self.__startFailedButton.setEnabled( |
493 self.__mode == TestingWidgetModes.STOPPED and |
503 self.__mode == TestingWidgetModes.STOPPED and failedAvailable |
494 failedAvailable |
|
495 ) |
504 ) |
496 self.__startFailedButton.setDefault( |
505 self.__startFailedButton.setDefault( |
497 self.__mode == TestingWidgetModes.STOPPED and |
506 self.__mode == TestingWidgetModes.STOPPED and failedAvailable |
498 failedAvailable |
507 ) |
499 ) |
508 |
500 |
|
501 # Stop button |
509 # Stop button |
502 self.__stopButton.setEnabled( |
510 self.__stopButton.setEnabled(self.__mode == TestingWidgetModes.RUNNING) |
503 self.__mode == TestingWidgetModes.RUNNING) |
511 self.__stopButton.setDefault(self.__mode == TestingWidgetModes.RUNNING) |
504 self.__stopButton.setDefault( |
512 |
505 self.__mode == TestingWidgetModes.RUNNING) |
|
506 |
|
507 # Code coverage button |
513 # Code coverage button |
508 self.__showCoverageButton.setEnabled( |
514 self.__showCoverageButton.setEnabled( |
509 self.__mode == TestingWidgetModes.STOPPED and |
515 self.__mode == TestingWidgetModes.STOPPED |
510 bool(self.__coverageFile) and |
516 and bool(self.__coverageFile) |
511 ( |
517 and ( |
512 (self.discoverCheckBox.isChecked() and |
518 ( |
513 bool(self.discoveryPicker.currentText())) or |
519 self.discoverCheckBox.isChecked() |
514 bool(self.testsuitePicker.currentText()) |
520 and bool(self.discoveryPicker.currentText()) |
515 ) |
521 ) |
516 ) |
522 or bool(self.testsuitePicker.currentText()) |
517 |
523 ) |
|
524 ) |
|
525 |
518 # Log output button |
526 # Log output button |
519 self.__showLogButton.setEnabled(bool(self.__recentLog)) |
527 self.__showLogButton.setEnabled(bool(self.__recentLog)) |
520 |
528 |
521 # Close button |
529 # Close button |
522 self.buttonBox.button( |
530 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled( |
523 QDialogButtonBox.StandardButton.Close |
531 self.__mode in (TestingWidgetModes.IDLE, TestingWidgetModes.STOPPED) |
524 ).setEnabled(self.__mode in ( |
532 ) |
525 TestingWidgetModes.IDLE, TestingWidgetModes.STOPPED |
533 |
526 )) |
|
527 |
|
528 @pyqtSlot() |
534 @pyqtSlot() |
529 def __updateProgress(self): |
535 def __updateProgress(self): |
530 """ |
536 """ |
531 Private slot update the progress indicators. |
537 Private slot update the progress indicators. |
532 """ |
538 """ |
533 self.progressCounterRunCount.setText( |
539 self.progressCounterRunCount.setText(str(self.__runCount)) |
534 str(self.__runCount)) |
540 self.progressCounterRemCount.setText(str(self.__totalCount - self.__runCount)) |
535 self.progressCounterRemCount.setText( |
|
536 str(self.__totalCount - self.__runCount)) |
|
537 self.progressProgressBar.setMaximum(self.__totalCount) |
541 self.progressProgressBar.setMaximum(self.__totalCount) |
538 self.progressProgressBar.setValue(self.__runCount) |
542 self.progressProgressBar.setValue(self.__runCount) |
539 |
543 |
540 @pyqtSlot() |
544 @pyqtSlot() |
541 def __setIdleMode(self): |
545 def __setIdleMode(self): |
542 """ |
546 """ |
543 Private slot to switch the widget to idle mode. |
547 Private slot to switch the widget to idle mode. |
544 """ |
548 """ |
545 self.__mode = TestingWidgetModes.IDLE |
549 self.__mode = TestingWidgetModes.IDLE |
546 self.__updateButtonBoxButtons() |
550 self.__updateButtonBoxButtons() |
547 self.progressGroupBox.hide() |
551 self.progressGroupBox.hide() |
548 self.tabWidget.setCurrentIndex(0) |
552 self.tabWidget.setCurrentIndex(0) |
549 |
553 |
550 @pyqtSlot() |
554 @pyqtSlot() |
551 def __setRunningMode(self): |
555 def __setRunningMode(self): |
552 """ |
556 """ |
553 Private slot to switch the widget to running mode. |
557 Private slot to switch the widget to running mode. |
554 """ |
558 """ |
555 self.__mode = TestingWidgetModes.RUNNING |
559 self.__mode = TestingWidgetModes.RUNNING |
556 |
560 |
557 self.__totalCount = 0 |
561 self.__totalCount = 0 |
558 self.__runCount = 0 |
562 self.__runCount = 0 |
559 |
563 |
560 self.__coverageFile = "" |
564 self.__coverageFile = "" |
561 |
565 |
562 self.sbLabel.setText(self.tr("Running")) |
566 self.sbLabel.setText(self.tr("Running")) |
563 self.tabWidget.setCurrentIndex(1) |
567 self.tabWidget.setCurrentIndex(1) |
564 self.__updateButtonBoxButtons() |
568 self.__updateButtonBoxButtons() |
565 self.__updateProgress() |
569 self.__updateProgress() |
566 |
570 |
567 self.progressGroupBox.show() |
571 self.progressGroupBox.show() |
568 |
572 |
569 @pyqtSlot() |
573 @pyqtSlot() |
570 def __setStoppedMode(self): |
574 def __setStoppedMode(self): |
571 """ |
575 """ |
572 Private slot to switch the widget to stopped mode. |
576 Private slot to switch the widget to stopped mode. |
573 """ |
577 """ |
574 self.__mode = TestingWidgetModes.STOPPED |
578 self.__mode = TestingWidgetModes.STOPPED |
575 if self.__totalCount == 0: |
579 if self.__totalCount == 0: |
576 self.progressProgressBar.setMaximum(100) |
580 self.progressProgressBar.setMaximum(100) |
577 |
581 |
578 self.progressGroupBox.hide() |
582 self.progressGroupBox.hide() |
579 |
583 |
580 self.__updateButtonBoxButtons() |
584 self.__updateButtonBoxButtons() |
581 |
585 |
582 self.testRunStopped.emit() |
586 self.testRunStopped.emit() |
583 |
587 |
584 self.raise_() |
588 self.raise_() |
585 self.activateWindow() |
589 self.activateWindow() |
586 |
590 |
587 @pyqtSlot(bool) |
591 @pyqtSlot(bool) |
588 def on_discoverCheckBox_toggled(self, checked): |
592 def on_discoverCheckBox_toggled(self, checked): |
589 """ |
593 """ |
590 Private slot handling state changes of the 'discover' checkbox. |
594 Private slot handling state changes of the 'discover' checkbox. |
591 |
595 |
592 @param checked state of the checkbox |
596 @param checked state of the checkbox |
593 @type bool |
597 @type bool |
594 """ |
598 """ |
595 if not bool(self.discoveryPicker.currentText()): |
599 if not bool(self.discoveryPicker.currentText()): |
596 if self.__project and self.__project.isOpen(): |
600 if self.__project and self.__project.isOpen(): |
597 self.__insertDiscovery(self.__project.getProjectPath()) |
601 self.__insertDiscovery(self.__project.getProjectPath()) |
598 else: |
602 else: |
599 self.__insertDiscovery( |
603 self.__insertDiscovery(Preferences.getMultiProject("Workspace")) |
600 Preferences.getMultiProject("Workspace")) |
604 |
601 |
|
602 self.__resetResults() |
605 self.__resetResults() |
603 |
606 |
604 @pyqtSlot() |
607 @pyqtSlot() |
605 def on_testsuitePicker_aboutToShowPathPickerDialog(self): |
608 def on_testsuitePicker_aboutToShowPathPickerDialog(self): |
606 """ |
609 """ |
607 Private slot called before the test file selection dialog is shown. |
610 Private slot called before the test file selection dialog is shown. |
608 """ |
611 """ |
609 if self.__project: |
612 if self.__project: |
610 # we were called from within eric |
613 # we were called from within eric |
611 py3Extensions = ' '.join([ |
614 py3Extensions = " ".join( |
612 "*{0}".format(ext) |
615 [ |
613 for ext in |
616 "*{0}".format(ext) |
614 ericApp().getObject("DebugServer").getExtensions('Python3') |
617 for ext in ericApp() |
615 ]) |
618 .getObject("DebugServer") |
616 fileFilter = self.tr( |
619 .getExtensions("Python3") |
617 "Python3 Files ({0});;All Files (*)" |
620 ] |
618 ).format(py3Extensions) |
621 ) |
|
622 fileFilter = self.tr("Python3 Files ({0});;All Files (*)").format( |
|
623 py3Extensions |
|
624 ) |
619 else: |
625 else: |
620 # standalone application |
626 # standalone application |
621 fileFilter = self.tr("Python Files (*.py);;All Files (*)") |
627 fileFilter = self.tr("Python Files (*.py);;All Files (*)") |
622 self.testsuitePicker.setFilters(fileFilter) |
628 self.testsuitePicker.setFilters(fileFilter) |
623 |
629 |
624 defaultDirectory = ( |
630 defaultDirectory = ( |
625 self.__project.getProjectPath() |
631 self.__project.getProjectPath() |
626 if self.__project and self.__project.isOpen() else |
632 if self.__project and self.__project.isOpen() |
627 Preferences.getMultiProject("Workspace") |
633 else Preferences.getMultiProject("Workspace") |
628 ) |
634 ) |
629 if not defaultDirectory: |
635 if not defaultDirectory: |
630 defaultDirectory = os.path.expanduser("~") |
636 defaultDirectory = os.path.expanduser("~") |
631 self.testsuitePicker.setDefaultDirectory(defaultDirectory) |
637 self.testsuitePicker.setDefaultDirectory(defaultDirectory) |
632 |
638 |
633 @pyqtSlot(QAbstractButton) |
639 @pyqtSlot(QAbstractButton) |
634 def on_buttonBox_clicked(self, button): |
640 def on_buttonBox_clicked(self, button): |
635 """ |
641 """ |
636 Private slot called by a button of the button box clicked. |
642 Private slot called by a button of the button box clicked. |
637 |
643 |
638 @param button button that was clicked |
644 @param button button that was clicked |
639 @type QAbstractButton |
645 @type QAbstractButton |
640 """ |
646 """ |
641 if button == self.__startButton: |
647 if button == self.__startButton: |
642 self.startTests() |
648 self.startTests() |
647 self.startTests(failedOnly=True) |
653 self.startTests(failedOnly=True) |
648 elif button == self.__showCoverageButton: |
654 elif button == self.__showCoverageButton: |
649 self.__showCoverageDialog() |
655 self.__showCoverageDialog() |
650 elif button == self.__showLogButton: |
656 elif button == self.__showLogButton: |
651 self.__showLogOutput() |
657 self.__showLogOutput() |
652 |
658 |
653 @pyqtSlot(int) |
659 @pyqtSlot(int) |
654 def on_venvComboBox_currentIndexChanged(self, index): |
660 def on_venvComboBox_currentIndexChanged(self, index): |
655 """ |
661 """ |
656 Private slot handling the selection of a virtual environment. |
662 Private slot handling the selection of a virtual environment. |
657 |
663 |
658 @param index index of the selected environment |
664 @param index index of the selected environment |
659 @type int |
665 @type int |
660 """ |
666 """ |
661 self.__populateTestFrameworkComboBox() |
667 self.__populateTestFrameworkComboBox() |
662 self.__updateButtonBoxButtons() |
668 self.__updateButtonBoxButtons() |
663 |
669 |
664 self.versionsButton.setEnabled(bool(self.venvComboBox.currentText())) |
670 self.versionsButton.setEnabled(bool(self.venvComboBox.currentText())) |
665 |
671 |
666 self.__updateCoverage() |
672 self.__updateCoverage() |
667 |
673 |
668 @pyqtSlot(int) |
674 @pyqtSlot(int) |
669 def on_frameworkComboBox_currentIndexChanged(self, index): |
675 def on_frameworkComboBox_currentIndexChanged(self, index): |
670 """ |
676 """ |
671 Private slot handling the selection of a test framework. |
677 Private slot handling the selection of a test framework. |
672 |
678 |
673 @param index index of the selected framework |
679 @param index index of the selected framework |
674 @type int |
680 @type int |
675 """ |
681 """ |
676 self.__resetResults() |
682 self.__resetResults() |
677 self.__updateCoverage() |
683 self.__updateCoverage() |
678 |
684 |
679 @pyqtSlot() |
685 @pyqtSlot() |
680 def __updateCoverage(self): |
686 def __updateCoverage(self): |
681 """ |
687 """ |
682 Private slot to update the state of the coverage checkbox depending on |
688 Private slot to update the state of the coverage checkbox depending on |
683 the selected framework's capabilities. |
689 the selected framework's capabilities. |
684 """ |
690 """ |
685 hasCoverage = False |
691 hasCoverage = False |
686 |
692 |
687 venvName = self.venvComboBox.currentText() |
693 venvName = self.venvComboBox.currentText() |
688 if venvName: |
694 if venvName: |
689 framework = self.frameworkComboBox.currentText() |
695 framework = self.frameworkComboBox.currentText() |
690 if framework: |
696 if framework: |
691 interpreter = self.__venvManager.getVirtualenvInterpreter( |
697 interpreter = self.__venvManager.getVirtualenvInterpreter(venvName) |
692 venvName) |
698 executor = self.__frameworkRegistry.createExecutor(framework, self) |
693 executor = self.__frameworkRegistry.createExecutor( |
|
694 framework, self) |
|
695 hasCoverage = executor.hasCoverage(interpreter) |
699 hasCoverage = executor.hasCoverage(interpreter) |
696 |
700 |
697 self.coverageCheckBox.setEnabled(hasCoverage) |
701 self.coverageCheckBox.setEnabled(hasCoverage) |
698 if not hasCoverage: |
702 if not hasCoverage: |
699 self.coverageCheckBox.setChecked(False) |
703 self.coverageCheckBox.setChecked(False) |
700 |
704 |
701 @pyqtSlot() |
705 @pyqtSlot() |
702 def on_versionsButton_clicked(self): |
706 def on_versionsButton_clicked(self): |
703 """ |
707 """ |
704 Private slot to show the versions of available plugins. |
708 Private slot to show the versions of available plugins. |
705 """ |
709 """ |
706 venvName = self.venvComboBox.currentText() |
710 venvName = self.venvComboBox.currentText() |
707 if venvName: |
711 if venvName: |
708 headerText = self.tr("<h3>Versions of Frameworks and their" |
712 headerText = self.tr("<h3>Versions of Frameworks and their" " Plugins</h3>") |
709 " Plugins</h3>") |
|
710 versionsText = "" |
713 versionsText = "" |
711 interpreter = self.__venvManager.getVirtualenvInterpreter(venvName) |
714 interpreter = self.__venvManager.getVirtualenvInterpreter(venvName) |
712 for framework in sorted( |
715 for framework in sorted(self.__frameworkRegistry.getFrameworks().keys()): |
713 self.__frameworkRegistry.getFrameworks().keys() |
716 executor = self.__frameworkRegistry.createExecutor(framework, self) |
714 ): |
|
715 executor = self.__frameworkRegistry.createExecutor( |
|
716 framework, self) |
|
717 versions = executor.getVersions(interpreter) |
717 versions = executor.getVersions(interpreter) |
718 if versions: |
718 if versions: |
719 txt = "<p><strong>{0} {1}</strong>".format( |
719 txt = "<p><strong>{0} {1}</strong>".format( |
720 versions["name"], versions["version"]) |
720 versions["name"], versions["version"] |
721 |
721 ) |
|
722 |
722 if versions["plugins"]: |
723 if versions["plugins"]: |
723 txt += "<table>" |
724 txt += "<table>" |
724 for pluginVersion in versions["plugins"]: |
725 for pluginVersion in versions["plugins"]: |
725 txt += self.tr( |
726 txt += self.tr("<tr><td>{0}</td><td>{1}</td></tr>").format( |
726 "<tr><td>{0}</td><td>{1}</td></tr>" |
|
727 ).format( |
|
728 pluginVersion["name"], pluginVersion["version"] |
727 pluginVersion["name"], pluginVersion["version"] |
729 ) |
728 ) |
730 txt += "</table>" |
729 txt += "</table>" |
731 txt += "</p>" |
730 txt += "</p>" |
732 |
731 |
733 versionsText += txt |
732 versionsText += txt |
734 |
733 |
735 if not versionsText: |
734 if not versionsText: |
736 versionsText = self.tr("No version information available.") |
735 versionsText = self.tr("No version information available.") |
737 |
736 |
738 EricMessageBox.information( |
737 EricMessageBox.information( |
739 self, |
738 self, self.tr("Versions"), headerText + versionsText |
740 self.tr("Versions"), |
739 ) |
741 headerText + versionsText |
740 |
742 ) |
|
743 |
|
744 @pyqtSlot() |
741 @pyqtSlot() |
745 def startTests(self, failedOnly=False): |
742 def startTests(self, failedOnly=False): |
746 """ |
743 """ |
747 Public slot to start the test run. |
744 Public slot to start the test run. |
748 |
745 |
749 @param failedOnly flag indicating to run only failed tests |
746 @param failedOnly flag indicating to run only failed tests |
750 @type bool |
747 @type bool |
751 """ |
748 """ |
752 if self.__mode == TestingWidgetModes.RUNNING: |
749 if self.__mode == TestingWidgetModes.RUNNING: |
753 return |
750 return |
754 |
751 |
755 self.__recentLog = "" |
752 self.__recentLog = "" |
756 |
753 |
757 self.__recentEnvironment = self.venvComboBox.currentText() |
754 self.__recentEnvironment = self.venvComboBox.currentText() |
758 self.__recentFramework = self.frameworkComboBox.currentText() |
755 self.__recentFramework = self.frameworkComboBox.currentText() |
759 |
756 |
760 self.__failedTests = ( |
757 self.__failedTests = self.__resultsModel.getFailedTests() if failedOnly else [] |
761 self.__resultsModel.getFailedTests() |
|
762 if failedOnly else |
|
763 [] |
|
764 ) |
|
765 discover = self.discoverCheckBox.isChecked() |
758 discover = self.discoverCheckBox.isChecked() |
766 if discover: |
759 if discover: |
767 discoveryStart = self.discoveryPicker.currentText() |
760 discoveryStart = self.discoveryPicker.currentText() |
768 testFileName = "" |
761 testFileName = "" |
769 testName = "" |
762 testName = "" |
770 |
763 |
771 if discoveryStart: |
764 if discoveryStart: |
772 self.__insertDiscovery(discoveryStart) |
765 self.__insertDiscovery(discoveryStart) |
773 else: |
766 else: |
774 discoveryStart = "" |
767 discoveryStart = "" |
775 testFileName = self.testsuitePicker.currentText() |
768 testFileName = self.testsuitePicker.currentText() |
776 if testFileName: |
769 if testFileName: |
777 self.__insertTestFile(testFileName) |
770 self.__insertTestFile(testFileName) |
778 testName = self.testComboBox.currentText() |
771 testName = self.testComboBox.currentText() |
779 if testName: |
772 if testName: |
780 self.__insertTestName(testName) |
773 self.__insertTestName(testName) |
781 |
774 |
782 self.sbLabel.setText(self.tr("Preparing Testsuite")) |
775 self.sbLabel.setText(self.tr("Preparing Testsuite")) |
783 QCoreApplication.processEvents() |
776 QCoreApplication.processEvents() |
784 |
777 |
785 if self.__project: |
778 if self.__project: |
786 mainScript = self.__project.getMainScript(True) |
779 mainScript = self.__project.getMainScript(True) |
787 coverageFile = ( |
780 coverageFile = ( |
788 os.path.splitext(mainScript)[0] + ".coverage" |
781 os.path.splitext(mainScript)[0] + ".coverage" if mainScript else "" |
789 if mainScript else |
|
790 "" |
|
791 ) |
782 ) |
792 else: |
783 else: |
793 coverageFile = "" |
784 coverageFile = "" |
794 interpreter = self.__venvManager.getVirtualenvInterpreter( |
785 interpreter = self.__venvManager.getVirtualenvInterpreter( |
795 self.__recentEnvironment) |
786 self.__recentEnvironment |
|
787 ) |
796 config = TestConfig( |
788 config = TestConfig( |
797 interpreter=interpreter, |
789 interpreter=interpreter, |
798 discover=discover, |
790 discover=discover, |
799 discoveryStart=discoveryStart, |
791 discoveryStart=discoveryStart, |
800 testFilename=testFileName, |
792 testFilename=testFileName, |
843 category=TestResultCategory.PENDING, |
837 category=TestResultCategory.PENDING, |
844 status=self.tr("pending"), |
838 status=self.tr("pending"), |
845 name=name, |
839 name=name, |
846 id=id, |
840 id=id, |
847 message=desc, |
841 message=desc, |
848 ) for id, name, desc in testNames |
842 ) |
|
843 for id, name, desc in testNames |
849 ] |
844 ] |
850 self.__resultsModel.addTestResults(testResults) |
845 self.__resultsModel.addTestResults(testResults) |
851 |
846 |
852 self.__totalCount += len(testResults) |
847 self.__totalCount += len(testResults) |
853 self.__updateProgress() |
848 self.__updateProgress() |
854 |
849 |
855 @pyqtSlot(list) |
850 @pyqtSlot(list) |
856 def __testsCollectError(self, errors): |
851 def __testsCollectError(self, errors): |
857 """ |
852 """ |
858 Private slot handling the 'collectError' signal of the executor. |
853 Private slot handling the 'collectError' signal of the executor. |
859 |
854 |
860 @param errors list of tuples containing the test name and a description |
855 @param errors list of tuples containing the test name and a description |
861 of the error |
856 of the error |
862 @type list of tuple of (str, str) |
857 @type list of tuple of (str, str) |
863 """ |
858 """ |
864 testResults = [] |
859 testResults = [] |
865 |
860 |
866 for testFile, error in errors: |
861 for testFile, error in errors: |
867 if testFile: |
862 if testFile: |
868 testResults.append(TestResult( |
863 testResults.append( |
869 category=TestResultCategory.FAIL, |
864 TestResult( |
870 status=self.tr("Failure"), |
865 category=TestResultCategory.FAIL, |
871 name=testFile, |
866 status=self.tr("Failure"), |
872 id=testFile, |
867 name=testFile, |
873 message=self.tr("Collection Error"), |
868 id=testFile, |
874 extra=error.splitlines() |
869 message=self.tr("Collection Error"), |
875 )) |
870 extra=error.splitlines(), |
|
871 ) |
|
872 ) |
876 else: |
873 else: |
877 EricMessageBox.critical( |
874 EricMessageBox.critical( |
878 self, |
875 self, |
879 self.tr("Collection Error"), |
876 self.tr("Collection Error"), |
880 self.tr( |
877 self.tr( |
881 "<p>There was an error while collecting tests." |
878 "<p>There was an error while collecting tests." "</p><p>{0}</p>" |
882 "</p><p>{0}</p>" |
879 ).format("<br/>".join(error.splitlines())), |
883 ).format("<br/>".join(error.splitlines())) |
|
884 ) |
880 ) |
885 |
881 |
886 if testResults: |
882 if testResults: |
887 self.__resultsModel.addTestResults(testResults) |
883 self.__resultsModel.addTestResults(testResults) |
888 |
884 |
889 @pyqtSlot(tuple) |
885 @pyqtSlot(tuple) |
890 def __testStarted(self, test): |
886 def __testStarted(self, test): |
891 """ |
887 """ |
892 Private slot handling the 'startTest' signal of the executor. |
888 Private slot handling the 'startTest' signal of the executor. |
893 |
889 |
894 @param test tuple containing the id, name and short description of the |
890 @param test tuple containing the id, name and short description of the |
895 tests about to be run |
891 tests about to be run |
896 @type tuple of (str, str, str) |
892 @type tuple of (str, str, str) |
897 """ |
893 """ |
898 self.__resultsModel.updateTestResults([ |
894 self.__resultsModel.updateTestResults( |
899 TestResult( |
895 [ |
900 category=TestResultCategory.RUNNING, |
896 TestResult( |
901 status=self.tr("running"), |
897 category=TestResultCategory.RUNNING, |
902 id=test[0], |
898 status=self.tr("running"), |
903 name=test[1], |
899 id=test[0], |
904 message="" if test[2] is None else test[2], |
900 name=test[1], |
905 ) |
901 message="" if test[2] is None else test[2], |
906 ]) |
902 ) |
907 |
903 ] |
|
904 ) |
|
905 |
908 @pyqtSlot(TestResult) |
906 @pyqtSlot(TestResult) |
909 def __processTestResult(self, result): |
907 def __processTestResult(self, result): |
910 """ |
908 """ |
911 Private slot to handle the receipt of a test result object. |
909 Private slot to handle the receipt of a test result object. |
912 |
910 |
913 @param result test result object |
911 @param result test result object |
914 @type TestResult |
912 @type TestResult |
915 """ |
913 """ |
916 if not result.subtestResult: |
914 if not result.subtestResult: |
917 self.__runCount += 1 |
915 self.__runCount += 1 |
918 self.__updateProgress() |
916 self.__updateProgress() |
919 |
917 |
920 self.__resultsModel.updateTestResults([result]) |
918 self.__resultsModel.updateTestResults([result]) |
921 |
919 |
922 @pyqtSlot(list, str) |
920 @pyqtSlot(list, str) |
923 def __testProcessFinished(self, results, output): |
921 def __testProcessFinished(self, results, output): |
924 """ |
922 """ |
925 Private slot to handle the 'testFinished' signal of the executor. |
923 Private slot to handle the 'testFinished' signal of the executor. |
926 |
924 |
927 @param results list of test result objects (if not sent via the |
925 @param results list of test result objects (if not sent via the |
928 'testResult' signal |
926 'testResult' signal |
929 @type list of TestResult |
927 @type list of TestResult |
930 @param output string containing the test process output (if any) |
928 @param output string containing the test process output (if any) |
931 @type str |
929 @type str |
932 """ |
930 """ |
933 self.__recentLog = output |
931 self.__recentLog = output |
934 |
932 |
935 self.__setStoppedMode() |
933 self.__setStoppedMode() |
936 self.__testExecutor = None |
934 self.__testExecutor = None |
937 |
935 |
938 self.__adjustPendingState() |
936 self.__adjustPendingState() |
939 |
937 |
940 @pyqtSlot(int, float) |
938 @pyqtSlot(int, float) |
941 def __testRunFinished(self, noTests, duration): |
939 def __testRunFinished(self, noTests, duration): |
942 """ |
940 """ |
943 Private slot to handle the 'testRunFinished' signal of the executor. |
941 Private slot to handle the 'testRunFinished' signal of the executor. |
944 |
942 |
945 @param noTests number of tests run by the executor |
943 @param noTests number of tests run by the executor |
946 @type int |
944 @type int |
947 @param duration time needed in seconds to run the tests |
945 @param duration time needed in seconds to run the tests |
948 @type float |
946 @type float |
949 """ |
947 """ |
950 self.sbLabel.setText( |
948 self.sbLabel.setText( |
951 self.tr("Ran %n test(s) in {0}s", "", noTests).format( |
949 self.tr("Ran %n test(s) in {0}s", "", noTests).format( |
952 locale.format_string("%.3f", duration, grouping=True) |
950 locale.format_string("%.3f", duration, grouping=True) |
953 ) |
951 ) |
954 ) |
952 ) |
955 |
953 |
956 self.__setStoppedMode() |
954 self.__setStoppedMode() |
957 |
955 |
958 @pyqtSlot() |
956 @pyqtSlot() |
959 def __testsStopped(self): |
957 def __testsStopped(self): |
960 """ |
958 """ |
961 Private slot to handle the 'stop' signal of the executor. |
959 Private slot to handle the 'stop' signal of the executor. |
962 """ |
960 """ |
963 self.sbLabel.setText(self.tr("Ran %n test(s)", "", self.__runCount)) |
961 self.sbLabel.setText(self.tr("Ran %n test(s)", "", self.__runCount)) |
964 |
962 |
965 self.__setStoppedMode() |
963 self.__setStoppedMode() |
966 |
964 |
967 @pyqtSlot() |
965 @pyqtSlot() |
968 def __testRunAboutToBeStarted(self): |
966 def __testRunAboutToBeStarted(self): |
969 """ |
967 """ |
970 Private slot to handle the 'testRunAboutToBeStarted' signal of the |
968 Private slot to handle the 'testRunAboutToBeStarted' signal of the |
971 executor. |
969 executor. |
972 """ |
970 """ |
973 self.__resultsModel.clear() |
971 self.__resultsModel.clear() |
974 |
972 |
975 def __adjustPendingState(self): |
973 def __adjustPendingState(self): |
976 """ |
974 """ |
977 Private method to change the status indicator of all still pending |
975 Private method to change the status indicator of all still pending |
978 tests to "not run". |
976 tests to "not run". |
979 """ |
977 """ |
981 for result in self.__resultsModel.getTestResults(): |
979 for result in self.__resultsModel.getTestResults(): |
982 if result.category == TestResultCategory.PENDING: |
980 if result.category == TestResultCategory.PENDING: |
983 result.category = TestResultCategory.SKIP |
981 result.category = TestResultCategory.SKIP |
984 result.status = self.tr("not run") |
982 result.status = self.tr("not run") |
985 newResults.append(result) |
983 newResults.append(result) |
986 |
984 |
987 if newResults: |
985 if newResults: |
988 self.__resultsModel.updateTestResults(newResults) |
986 self.__resultsModel.updateTestResults(newResults) |
989 |
987 |
990 @pyqtSlot(str) |
988 @pyqtSlot(str) |
991 def __coverageData(self, coverageFile): |
989 def __coverageData(self, coverageFile): |
992 """ |
990 """ |
993 Private slot to handle the 'coverageData' signal of the executor. |
991 Private slot to handle the 'coverageData' signal of the executor. |
994 |
992 |
995 @param coverageFile file containing the coverage data |
993 @param coverageFile file containing the coverage data |
996 @type str |
994 @type str |
997 """ |
995 """ |
998 self.__coverageFile = coverageFile |
996 self.__coverageFile = coverageFile |
999 |
997 |
1000 @pyqtSlot() |
998 @pyqtSlot() |
1001 def __showCoverageDialog(self): |
999 def __showCoverageDialog(self): |
1002 """ |
1000 """ |
1003 Private slot to show a code coverage dialog for the most recent test |
1001 Private slot to show a code coverage dialog for the most recent test |
1004 run. |
1002 run. |
1005 """ |
1003 """ |
1006 if self.__coverageDialog is None: |
1004 if self.__coverageDialog is None: |
1007 from DataViews.PyCoverageDialog import PyCoverageDialog |
1005 from DataViews.PyCoverageDialog import PyCoverageDialog |
|
1006 |
1008 self.__coverageDialog = PyCoverageDialog(self) |
1007 self.__coverageDialog = PyCoverageDialog(self) |
1009 self.__coverageDialog.openFile.connect(self.__openEditor) |
1008 self.__coverageDialog.openFile.connect(self.__openEditor) |
1010 |
1009 |
1011 testDir = ( |
1010 testDir = ( |
1012 self.discoveryPicker.currentText() |
1011 self.discoveryPicker.currentText() |
1013 if self.discoverCheckBox.isChecked() else |
1012 if self.discoverCheckBox.isChecked() |
1014 os.path.dirname(self.testsuitePicker.currentText()) |
1013 else os.path.dirname(self.testsuitePicker.currentText()) |
1015 ) |
1014 ) |
1016 if testDir: |
1015 if testDir: |
1017 self.__coverageDialog.show() |
1016 self.__coverageDialog.show() |
1018 self.__coverageDialog.start(self.__coverageFile, testDir) |
1017 self.__coverageDialog.start(self.__coverageFile, testDir) |
1019 |
1018 |
1020 @pyqtSlot() |
1019 @pyqtSlot() |
1021 def __showLogOutput(self): |
1020 def __showLogOutput(self): |
1022 """ |
1021 """ |
1023 Private slot to show the output of the most recent test run. |
1022 Private slot to show the output of the most recent test run. |
1024 """ |
1023 """ |
1025 from EricWidgets.EricPlainTextDialog import EricPlainTextDialog |
1024 from EricWidgets.EricPlainTextDialog import EricPlainTextDialog |
|
1025 |
1026 dlg = EricPlainTextDialog( |
1026 dlg = EricPlainTextDialog( |
1027 title=self.tr("Test Run Output"), |
1027 title=self.tr("Test Run Output"), text=self.__recentLog |
1028 text=self.__recentLog |
|
1029 ) |
1028 ) |
1030 dlg.exec() |
1029 dlg.exec() |
1031 |
1030 |
1032 @pyqtSlot(str) |
1031 @pyqtSlot(str) |
1033 def __setStatusLabel(self, statusText): |
1032 def __setStatusLabel(self, statusText): |
1034 """ |
1033 """ |
1035 Private slot to set the status label to the text sent by the model. |
1034 Private slot to set the status label to the text sent by the model. |
1036 |
1035 |
1037 @param statusText text to be shown |
1036 @param statusText text to be shown |
1038 @type str |
1037 @type str |
1039 """ |
1038 """ |
1040 self.statusLabel.setText(f"<b>{statusText}</b>") |
1039 self.statusLabel.setText(f"<b>{statusText}</b>") |
1041 |
1040 |
1042 @pyqtSlot() |
1041 @pyqtSlot() |
1043 def __projectOpened(self): |
1042 def __projectOpened(self): |
1044 """ |
1043 """ |
1045 Private slot to handle a project being opened. |
1044 Private slot to handle a project being opened. |
1046 """ |
1045 """ |
1047 self.venvComboBox.setCurrentText(self.__project.getProjectVenv()) |
1046 self.venvComboBox.setCurrentText(self.__project.getProjectVenv()) |
1048 self.frameworkComboBox.setCurrentText( |
1047 self.frameworkComboBox.setCurrentText( |
1049 self.__project.getProjectTestingFramework()) |
1048 self.__project.getProjectTestingFramework() |
|
1049 ) |
1050 self.__insertDiscovery(self.__project.getProjectPath()) |
1050 self.__insertDiscovery(self.__project.getProjectPath()) |
1051 |
1051 |
1052 @pyqtSlot() |
1052 @pyqtSlot() |
1053 def __projectClosed(self): |
1053 def __projectClosed(self): |
1054 """ |
1054 """ |
1055 Private slot to handle a project being closed. |
1055 Private slot to handle a project being closed. |
1056 """ |
1056 """ |
1057 self.venvComboBox.setCurrentText("") |
1057 self.venvComboBox.setCurrentText("") |
1058 self.frameworkComboBox.setCurrentText("") |
1058 self.frameworkComboBox.setCurrentText("") |
1059 self.__insertDiscovery("") |
1059 self.__insertDiscovery("") |
1060 |
1060 |
1061 @pyqtSlot(str, int) |
1061 @pyqtSlot(str, int) |
1062 def __showSource(self, filename, lineno): |
1062 def __showSource(self, filename, lineno): |
1063 """ |
1063 """ |
1064 Private slot to show the source of a traceback in an editor. |
1064 Private slot to show the source of a traceback in an editor. |
1065 |
1065 |
1066 @param filename file name of the file to be shown |
1066 @param filename file name of the file to be shown |
1067 @type str |
1067 @type str |
1068 @param lineno line number to go to in the file |
1068 @param lineno line number to go to in the file |
1069 @type int |
1069 @type int |
1070 """ |
1070 """ |
1071 if self.__project: |
1071 if self.__project: |
1072 # running as part of eric IDE |
1072 # running as part of eric IDE |
1073 self.testFile.emit(filename, lineno, True) |
1073 self.testFile.emit(filename, lineno, True) |
1074 else: |
1074 else: |
1075 self.__openEditor(filename, lineno) |
1075 self.__openEditor(filename, lineno) |
1076 |
1076 |
1077 def __openEditor(self, filename, linenumber=1): |
1077 def __openEditor(self, filename, linenumber=1): |
1078 """ |
1078 """ |
1079 Private method to open an editor window for the given file. |
1079 Private method to open an editor window for the given file. |
1080 |
1080 |
1081 Note: This method opens an editor window when the testing dialog |
1081 Note: This method opens an editor window when the testing dialog |
1082 is called as a standalone application. |
1082 is called as a standalone application. |
1083 |
1083 |
1084 @param filename path of the file to be opened |
1084 @param filename path of the file to be opened |
1085 @type str |
1085 @type str |
1086 @param linenumber line number to place the cursor at (defaults to 1) |
1086 @param linenumber line number to place the cursor at (defaults to 1) |
1087 @type int (optional) |
1087 @type int (optional) |
1088 """ |
1088 """ |
1089 from QScintilla.MiniEditor import MiniEditor |
1089 from QScintilla.MiniEditor import MiniEditor |
|
1090 |
1090 editor = MiniEditor(filename, "Python3", self) |
1091 editor = MiniEditor(filename, "Python3", self) |
1091 editor.gotoLine(linenumber) |
1092 editor.gotoLine(linenumber) |
1092 editor.show() |
1093 editor.show() |
1093 |
1094 |
1094 self.__editors.append(editor) |
1095 self.__editors.append(editor) |
1095 |
1096 |
1096 def closeEvent(self, event): |
1097 def closeEvent(self, event): |
1097 """ |
1098 """ |
1098 Protected method to handle the close event. |
1099 Protected method to handle the close event. |
1099 |
1100 |
1100 @param event close event |
1101 @param event close event |
1101 @type QCloseEvent |
1102 @type QCloseEvent |
1102 """ |
1103 """ |
1103 event.accept() |
1104 event.accept() |
1104 |
1105 |
1105 for editor in self.__editors: |
1106 for editor in self.__editors: |
1106 with contextlib.suppress(Exception): |
1107 with contextlib.suppress(Exception): |
1107 editor.close() |
1108 editor.close() |
1108 |
1109 |
1109 |
1110 |
1110 class TestingWindow(EricMainWindow): |
1111 class TestingWindow(EricMainWindow): |
1111 """ |
1112 """ |
1112 Main window class for the standalone dialog. |
1113 Main window class for the standalone dialog. |
1113 """ |
1114 """ |
|
1115 |
1114 def __init__(self, testfile=None, parent=None): |
1116 def __init__(self, testfile=None, parent=None): |
1115 """ |
1117 """ |
1116 Constructor |
1118 Constructor |
1117 |
1119 |
1118 @param testfile file name of the test script to open |
1120 @param testfile file name of the test script to open |
1119 @type str |
1121 @type str |
1120 @param parent reference to the parent widget |
1122 @param parent reference to the parent widget |
1121 @type QWidget |
1123 @type QWidget |
1122 """ |
1124 """ |