14 import time |
14 import time |
15 import re |
15 import re |
16 import os |
16 import os |
17 |
17 |
18 from PyQt4.QtCore import pyqtSignal, QEvent, Qt, pyqtSlot |
18 from PyQt4.QtCore import pyqtSignal, QEvent, Qt, pyqtSlot |
19 from PyQt4.QtGui import QWidget, QColor, QDialog, QApplication, QDialogButtonBox, \ |
19 from PyQt4.QtGui import QWidget, QColor, QDialog, QApplication, \ |
20 QListWidgetItem |
20 QDialogButtonBox, QListWidgetItem |
21 |
21 |
22 from E5Gui.E5Application import e5App |
22 from E5Gui.E5Application import e5App |
23 from E5Gui.E5Completers import E5FileCompleter |
23 from E5Gui.E5Completers import E5FileCompleter |
24 from E5Gui import E5MessageBox, E5FileDialog |
24 from E5Gui import E5MessageBox, E5FileDialog |
25 from E5Gui.E5MainWindow import E5MainWindow |
25 from E5Gui.E5MainWindow import E5MainWindow |
34 |
34 |
35 class UnittestDialog(QWidget, Ui_UnittestDialog): |
35 class UnittestDialog(QWidget, Ui_UnittestDialog): |
36 """ |
36 """ |
37 Class implementing the UI to the pyunit package. |
37 Class implementing the UI to the pyunit package. |
38 |
38 |
39 @signal unittestFile(str, int, int) emitted to show the source of a unittest file |
39 @signal unittestFile(str, int, int) emitted to show the source of a |
|
40 unittest file |
40 @signal unittestStopped() emitted after a unit test was run |
41 @signal unittestStopped() emitted after a unit test was run |
41 """ |
42 """ |
42 unittestFile = pyqtSignal(str, int, int) |
43 unittestFile = pyqtSignal(str, int, int) |
43 unittestStopped = pyqtSignal() |
44 unittestStopped = pyqtSignal() |
44 |
45 |
45 def __init__(self, prog=None, dbs=None, ui=None, fromEric=False, parent=None, |
46 def __init__(self, prog=None, dbs=None, ui=None, fromEric=False, |
46 name=None): |
47 parent=None, name=None): |
47 """ |
48 """ |
48 Constructor |
49 Constructor |
49 |
50 |
50 @param prog filename of the program to open |
51 @param prog filename of the program to open |
51 @param dbs reference to the debug server object. It is an indication |
52 @param dbs reference to the debug server object. It is an indication |
61 self.setObjectName(name) |
62 self.setObjectName(name) |
62 self.setupUi(self) |
63 self.setupUi(self) |
63 |
64 |
64 self.startButton = self.buttonBox.addButton( |
65 self.startButton = self.buttonBox.addButton( |
65 self.trUtf8("Start"), QDialogButtonBox.ActionRole) |
66 self.trUtf8("Start"), QDialogButtonBox.ActionRole) |
66 self.startButton.setToolTip(self.trUtf8("Start the selected testsuite")) |
67 self.startButton.setToolTip(self.trUtf8( |
|
68 "Start the selected testsuite")) |
67 self.startButton.setWhatsThis(self.trUtf8( |
69 self.startButton.setWhatsThis(self.trUtf8( |
68 """<b>Start Test</b>""" |
70 """<b>Start Test</b>""" |
69 """<p>This button starts the selected testsuite.</p>""")) |
71 """<p>This button starts the selected testsuite.</p>""")) |
70 self.startFailedButton = self.buttonBox.addButton( |
72 self.startFailedButton = self.buttonBox.addButton( |
71 self.trUtf8("Rerun Failed"), QDialogButtonBox.ActionRole) |
73 self.trUtf8("Rerun Failed"), QDialogButtonBox.ActionRole) |
72 self.startFailedButton.setToolTip( |
74 self.startFailedButton.setToolTip( |
73 self.trUtf8("Reruns failed tests of the selected testsuite")) |
75 self.trUtf8("Reruns failed tests of the selected testsuite")) |
74 self.startFailedButton.setWhatsThis(self.trUtf8( |
76 self.startFailedButton.setWhatsThis(self.trUtf8( |
75 """<b>Rerun Failed</b>""" |
77 """<b>Rerun Failed</b>""" |
76 """<p>This button reruns all failed tests of the selected testsuite.</p>""")) |
78 """<p>This button reruns all failed tests of the selected""" |
|
79 """ testsuite.</p>""")) |
77 self.stopButton = self.buttonBox.addButton( |
80 self.stopButton = self.buttonBox.addButton( |
78 self.trUtf8("Stop"), QDialogButtonBox.ActionRole) |
81 self.trUtf8("Stop"), QDialogButtonBox.ActionRole) |
79 self.stopButton.setToolTip(self.trUtf8("Stop the running unittest")) |
82 self.stopButton.setToolTip(self.trUtf8("Stop the running unittest")) |
80 self.stopButton.setWhatsThis(self.trUtf8( |
83 self.stopButton.setWhatsThis(self.trUtf8( |
81 """<b>Stop Test</b>""" |
84 """<b>Stop Test</b>""" |
86 |
89 |
87 self.dbs = dbs |
90 self.dbs = dbs |
88 self.__fromEric = fromEric |
91 self.__fromEric = fromEric |
89 |
92 |
90 self.setWindowFlags( |
93 self.setWindowFlags( |
91 self.windowFlags() | Qt.WindowFlags(Qt.WindowContextHelpButtonHint)) |
94 self.windowFlags() | Qt.WindowFlags( |
|
95 Qt.WindowContextHelpButtonHint)) |
92 self.setWindowIcon(UI.PixmapCache.getIcon("eric.png")) |
96 self.setWindowIcon(UI.PixmapCache.getIcon("eric.png")) |
93 self.setWindowTitle(self.trUtf8("Unittest")) |
97 self.setWindowTitle(self.trUtf8("Unittest")) |
94 if dbs: |
98 if dbs: |
95 self.ui = ui |
99 self.ui = ui |
96 else: |
100 else: |
124 self.dbs.utStopTest.connect(self.testFinished) |
128 self.dbs.utStopTest.connect(self.testFinished) |
125 self.dbs.utTestFailed.connect(self.testFailed) |
129 self.dbs.utTestFailed.connect(self.testFailed) |
126 self.dbs.utTestErrored.connect(self.testErrored) |
130 self.dbs.utTestErrored.connect(self.testErrored) |
127 self.dbs.utTestSkipped.connect(self.testSkipped) |
131 self.dbs.utTestSkipped.connect(self.testSkipped) |
128 self.dbs.utTestFailedExpected.connect(self.testFailedExpected) |
132 self.dbs.utTestFailedExpected.connect(self.testFailedExpected) |
129 self.dbs.utTestSucceededUnexpected.connect(self.testSucceededUnexpected) |
133 self.dbs.utTestSucceededUnexpected.connect( |
|
134 self.testSucceededUnexpected) |
130 |
135 |
131 def keyPressEvent(self, evt): |
136 def keyPressEvent(self, evt): |
132 """ |
137 """ |
133 Protected slot to handle key press events. |
138 Protected slot to handle key press events. |
134 |
139 |
145 """ |
150 """ |
146 self.progressLed.setColor(QColor(color)) |
151 self.progressLed.setColor(QColor(color)) |
147 |
152 |
148 def insertProg(self, prog): |
153 def insertProg(self, prog): |
149 """ |
154 """ |
150 Public slot to insert the filename prog into the testsuiteComboBox object. |
155 Public slot to insert the filename prog into the testsuiteComboBox |
|
156 object. |
151 |
157 |
152 @param prog filename to be inserted (string) |
158 @param prog filename to be inserted (string) |
153 """ |
159 """ |
154 # prepend the selected file to the testsuite combobox |
160 # prepend the selected file to the testsuite combobox |
155 if prog is None: |
161 if prog is None: |
294 failed, |
300 failed, |
295 self.coverageCheckBox.isChecked(), mainScript, |
301 self.coverageCheckBox.isChecked(), mainScript, |
296 self.coverageEraseCheckBox.isChecked(), clientType=clientType) |
302 self.coverageEraseCheckBox.isChecked(), clientType=clientType) |
297 else: |
303 else: |
298 # we are running as an application or in local mode |
304 # we are running as an application or in local mode |
299 sys.path = [os.path.dirname(os.path.abspath(prog))] + self.savedSysPath |
305 sys.path = [os.path.dirname(os.path.abspath(prog))] + \ |
|
306 self.savedSysPath |
300 |
307 |
301 # clean up list of imported modules to force a reimport upon running the test |
308 # clean up list of imported modules to force a reimport upon |
|
309 # running the test |
302 if self.savedModulelist: |
310 if self.savedModulelist: |
303 for modname in list(sys.modules.keys()): |
311 for modname in list(sys.modules.keys()): |
304 if modname not in self.savedModulelist: |
312 if modname not in self.savedModulelist: |
305 # delete it |
313 # delete it |
306 del(sys.modules[modname]) |
314 del(sys.modules[modname]) |
316 module) |
324 module) |
317 else: |
325 else: |
318 test = unittest.defaultTestLoader.loadTestsFromName( |
326 test = unittest.defaultTestLoader.loadTestsFromName( |
319 testFunctionName, module) |
327 testFunctionName, module) |
320 except AttributeError: |
328 except AttributeError: |
321 test = unittest.defaultTestLoader.loadTestsFromModule(module) |
329 test = unittest.defaultTestLoader.loadTestsFromModule( |
|
330 module) |
322 except: |
331 except: |
323 exc_type, exc_value, exc_tb = sys.exc_info() |
332 exc_type, exc_value, exc_tb = sys.exc_info() |
324 E5MessageBox.critical(self, |
333 E5MessageBox.critical(self, |
325 self.trUtf8("Unittest"), |
334 self.trUtf8("Unittest"), |
326 self.trUtf8("<p>Unable to run test <b>{0}</b>.<br>{1}<br>{2}</p>") |
335 self.trUtf8( |
327 .format(self.testName, str(exc_type), str(exc_value))) |
336 "<p>Unable to run test <b>{0}</b>.<br>" |
|
337 "{1}<br>{2}</p>") |
|
338 .format(self.testName, str(exc_type), |
|
339 str(exc_value))) |
328 return |
340 return |
329 |
341 |
330 # now set up the coverage stuff |
342 # now set up the coverage stuff |
331 if self.coverageCheckBox.isChecked(): |
343 if self.coverageCheckBox.isChecked(): |
332 if self.dbs: |
344 if self.dbs: |
341 else: |
353 else: |
342 mainScript = os.path.abspath(prog) |
354 mainScript = os.path.abspath(prog) |
343 |
355 |
344 from DebugClients.Python3.coverage import coverage |
356 from DebugClients.Python3.coverage import coverage |
345 cover = coverage( |
357 cover = coverage( |
346 data_file="{0}.coverage".format(os.path.splitext(mainScript)[0])) |
358 data_file="{0}.coverage".format( |
|
359 os.path.splitext(mainScript)[0])) |
347 cover.use_cache(True) |
360 cover.use_cache(True) |
348 if self.coverageEraseCheckBox.isChecked(): |
361 if self.coverageEraseCheckBox.isChecked(): |
349 cover.erase() |
362 cover.erase() |
350 else: |
363 else: |
351 cover = None |
364 cover = None |
375 @param exc_value value of exception occured during preparation (string) |
388 @param exc_value value of exception occured during preparation (string) |
376 """ |
389 """ |
377 if nrTests == 0: |
390 if nrTests == 0: |
378 E5MessageBox.critical(self, |
391 E5MessageBox.critical(self, |
379 self.trUtf8("Unittest"), |
392 self.trUtf8("Unittest"), |
380 self.trUtf8("<p>Unable to run test <b>{0}</b>.<br>{1}<br>{2}</p>") |
393 self.trUtf8( |
|
394 "<p>Unable to run test <b>{0}</b>.<br>{1}<br>{2}</p>") |
381 .format(self.testName, exc_type, exc_value)) |
395 .format(self.testName, exc_type, exc_value)) |
382 return |
396 return |
383 |
397 |
384 self.totalTests = nrTests |
398 self.totalTests = nrTests |
385 self.__setRunningMode() |
399 self.__setRunningMode() |
397 |
411 |
398 def on_errorsListWidget_currentTextChanged(self, text): |
412 def on_errorsListWidget_currentTextChanged(self, text): |
399 """ |
413 """ |
400 Private slot to handle the highlighted signal. |
414 Private slot to handle the highlighted signal. |
401 |
415 |
402 @param txt current text (string) |
416 @param text current text (string) |
403 """ |
417 """ |
404 if text: |
418 if text: |
405 for pattern in self.rxPatterns: |
419 for pattern in self.rxPatterns: |
406 text = re.sub(pattern, "", text) |
420 text = re.sub(pattern, "", text) |
407 itm = self.testsListWidget.findItems(text, Qt.MatchFlags(Qt.MatchExactly))[0] |
421 itm = self.testsListWidget.findItems( |
|
422 text, Qt.MatchFlags(Qt.MatchExactly))[0] |
408 self.testsListWidget.setCurrentItem(itm) |
423 self.testsListWidget.setCurrentItem(itm) |
409 self.testsListWidget.scrollToItem(itm) |
424 self.testsListWidget.scrollToItem(itm) |
410 |
425 |
411 def __setRunningMode(self): |
426 def __setRunningMode(self): |
412 """ |
427 """ |
427 self.progressCounterRunCount.setText(str(self.runCount)) |
442 self.progressCounterRunCount.setText(str(self.runCount)) |
428 self.progressCounterRemCount.setText(str(self.remainingCount)) |
443 self.progressCounterRemCount.setText(str(self.remainingCount)) |
429 self.progressCounterFailureCount.setText(str(self.failCount)) |
444 self.progressCounterFailureCount.setText(str(self.failCount)) |
430 self.progressCounterErrorCount.setText(str(self.errorCount)) |
445 self.progressCounterErrorCount.setText(str(self.errorCount)) |
431 self.progressCounterSkippedCount.setText(str(self.skippedCount)) |
446 self.progressCounterSkippedCount.setText(str(self.skippedCount)) |
432 self.progressCounterExpectedFailureCount.setText(str(self.expectedFailureCount)) |
447 self.progressCounterExpectedFailureCount.setText( |
|
448 str(self.expectedFailureCount)) |
433 self.progressCounterUnexpectedSuccessCount.setText( |
449 self.progressCounterUnexpectedSuccessCount.setText( |
434 str(self.unexpectedSuccessCount)) |
450 str(self.unexpectedSuccessCount)) |
435 self.errorsListWidget.clear() |
451 self.errorsListWidget.clear() |
436 self.testsListWidget.clear() |
452 self.testsListWidget.clear() |
437 self.progressProgressBar.setRange(0, self.totalTests) |
453 self.progressProgressBar.setRange(0, self.totalTests) |
524 @param test name of the test (string) |
540 @param test name of the test (string) |
525 @param exc string representation of the exception (string) |
541 @param exc string representation of the exception (string) |
526 @param id id of the test (string) |
542 @param id id of the test (string) |
527 """ |
543 """ |
528 self.expectedFailureCount += 1 |
544 self.expectedFailureCount += 1 |
529 self.progressCounterExpectedFailureCount.setText(str(self.expectedFailureCount)) |
545 self.progressCounterExpectedFailureCount.setText( |
|
546 str(self.expectedFailureCount)) |
530 itm = QListWidgetItem(self.trUtf8(" Expected Failure")) |
547 itm = QListWidgetItem(self.trUtf8(" Expected Failure")) |
531 itm.setForeground(Qt.blue) |
548 itm.setForeground(Qt.blue) |
532 self.testsListWidget.insertItem(1, itm) |
549 self.testsListWidget.insertItem(1, itm) |
533 |
550 |
534 def testSucceededUnexpected(self, test, id): |
551 def testSucceededUnexpected(self, test, id): |
629 |
646 |
630 # get the error info |
647 # get the error info |
631 tracebackLines = self.dlg.traceback.toPlainText().splitlines() |
648 tracebackLines = self.dlg.traceback.toPlainText().splitlines() |
632 # find the last entry matching the pattern |
649 # find the last entry matching the pattern |
633 for index in range(len(tracebackLines) - 1, -1, -1): |
650 for index in range(len(tracebackLines) - 1, -1, -1): |
634 fmatch = re.search(r'File "(.*?)", line (\d*?),.*', tracebackLines[index]) |
651 fmatch = re.search(r'File "(.*?)", line (\d*?),.*', |
|
652 tracebackLines[index]) |
635 if fmatch: |
653 if fmatch: |
636 break |
654 break |
637 if fmatch: |
655 if fmatch: |
638 fn, ln = fmatch.group(1, 2) |
656 fn, ln = fmatch.group(1, 2) |
639 self.unittestFile.emit(fn, int(ln), 1) |
657 self.unittestFile.emit(fn, int(ln), 1) |
749 self.cw.installEventFilter(self) |
767 self.cw.installEventFilter(self) |
750 size = self.cw.size() |
768 size = self.cw.size() |
751 self.setCentralWidget(self.cw) |
769 self.setCentralWidget(self.cw) |
752 self.resize(size) |
770 self.resize(size) |
753 |
771 |
754 self.setStyle(Preferences.getUI("Style"), Preferences.getUI("StyleSheet")) |
772 self.setStyle(Preferences.getUI("Style"), |
|
773 Preferences.getUI("StyleSheet")) |
755 |
774 |
756 def eventFilter(self, obj, event): |
775 def eventFilter(self, obj, event): |
757 """ |
776 """ |
758 Public method to filter events. |
777 Public method to filter events. |
759 |
778 |