PyUnit/UnittestDialog.py

changeset 1166
a94b0a2fafd7
parent 1131
7781e396c903
child 1481
eebaadaee21e
equal deleted inserted replaced
1163:0155e7aa345d 1166:a94b0a2fafd7
7 Module implementing the UI to the pyunit package. 7 Module implementing the UI to the pyunit package.
8 """ 8 """
9 9
10 import unittest 10 import unittest
11 import sys 11 import sys
12 import traceback
13 import time 12 import time
14 import re 13 import re
15 import os 14 import os
16 15
17 from PyQt4.QtCore import pyqtSignal, QEvent, Qt, pyqtSlot 16 from PyQt4.QtCore import pyqtSignal, QEvent, Qt, pyqtSlot
18 from PyQt4.QtGui import QWidget, QColor, QDialog, QApplication, QDialogButtonBox, \ 17 from PyQt4.QtGui import QWidget, QColor, QDialog, QApplication, QDialogButtonBox, \
19 QMainWindow 18 QMainWindow, QListWidgetItem
20 19
21 from E5Gui.E5Application import e5App 20 from E5Gui.E5Application import e5App
22 from E5Gui.E5Completers import E5FileCompleter 21 from E5Gui.E5Completers import E5FileCompleter
23 from E5Gui import E5MessageBox, E5FileDialog 22 from E5Gui import E5MessageBox, E5FileDialog
24 23
28 from DebugClients.Python3.coverage import coverage 27 from DebugClients.Python3.coverage import coverage
29 28
30 import UI.PixmapCache 29 import UI.PixmapCache
31 30
32 import Utilities 31 import Utilities
32 import Preferences
33 33
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.
93 self.savedModulelist = None 93 self.savedModulelist = None
94 self.savedSysPath = sys.path 94 self.savedSysPath = sys.path
95 if prog: 95 if prog:
96 self.insertProg(prog) 96 self.insertProg(prog)
97 97
98 self.rx1 = self.trUtf8("^Failure: ") 98 self.rxPatterns = [
99 self.rx2 = self.trUtf8("^Error: ") 99 self.trUtf8("^Failure: "),
100 self.trUtf8("^Error: "),
101 ]
100 102
101 # now connect the debug server signals if called from the eric5 IDE 103 # now connect the debug server signals if called from the eric5 IDE
102 if self.dbs: 104 if self.dbs:
103 self.dbs.utPrepared.connect(self.__UTPrepared) 105 self.dbs.utPrepared.connect(self.__UTPrepared)
104 self.dbs.utFinished.connect(self.__setStoppedMode) 106 self.dbs.utFinished.connect(self.__setStoppedMode)
105 self.dbs.utStartTest.connect(self.testStarted) 107 self.dbs.utStartTest.connect(self.testStarted)
106 self.dbs.utStopTest.connect(self.testFinished) 108 self.dbs.utStopTest.connect(self.testFinished)
107 self.dbs.utTestFailed.connect(self.testFailed) 109 self.dbs.utTestFailed.connect(self.testFailed)
108 self.dbs.utTestErrored.connect(self.testErrored) 110 self.dbs.utTestErrored.connect(self.testErrored)
111 self.dbs.utTestSkipped.connect(self.testSkipped)
112 self.dbs.utTestFailedExpected.connect(self.testFailedExpected)
113 self.dbs.utTestSucceededUnexpected.connect(self.testSucceededUnexpected)
109 114
110 def __setProgressColor(self, color): 115 def __setProgressColor(self, color):
111 """ 116 """
112 Private methode to set the color of the progress color label. 117 Private methode to set the color of the progress color label.
113 118
235 if self.dbs and not self.localCheckBox.isChecked(): 240 if self.dbs and not self.localCheckBox.isChecked():
236 # we are cooperating with the eric5 IDE 241 # we are cooperating with the eric5 IDE
237 project = e5App().getObject("Project") 242 project = e5App().getObject("Project")
238 if project.isOpen() and project.isProjectSource(prog): 243 if project.isOpen() and project.isProjectSource(prog):
239 mainScript = project.getMainScript(True) 244 mainScript = project.getMainScript(True)
245 clientType = self.project.getProjectLanguage()
240 else: 246 else:
241 mainScript = os.path.abspath(prog) 247 mainScript = os.path.abspath(prog)
248 flags = Utilities.extractFlagsFromFile(mainScript)
249 if mainScript.endswith(
250 tuple(Preferences.getPython("PythonExtensions"))) or \
251 ("FileType" in flags and
252 flags["FileType"] in ["Python", "Python2"]):
253 clientType = "Python2"
254 else:
255 clientType = ""
242 self.dbs.remoteUTPrepare(prog, self.testName, testFunctionName, 256 self.dbs.remoteUTPrepare(prog, self.testName, testFunctionName,
243 self.coverageCheckBox.isChecked(), mainScript, 257 self.coverageCheckBox.isChecked(), mainScript,
244 self.coverageEraseCheckBox.isChecked()) 258 self.coverageEraseCheckBox.isChecked(), clientType=clientType)
245 else: 259 else:
246 # we are running as an application or in local mode 260 # we are running as an application or in local mode
247 sys.path = [os.path.dirname(os.path.abspath(prog))] + self.savedSysPath 261 sys.path = [os.path.dirname(os.path.abspath(prog))] + self.savedSysPath
248 262
249 # clean up list of imported modules to force a reimport upon running the test 263 # clean up list of imported modules to force a reimport upon running the test
338 Private slot to handle the highlighted signal. 352 Private slot to handle the highlighted signal.
339 353
340 @param txt current text (string) 354 @param txt current text (string)
341 """ 355 """
342 if text: 356 if text:
343 text = re.sub(self.rx1, "", text) 357 for pattern in self.rxPatterns:
344 text = re.sub(self.rx2, "", text) 358 text = re.sub(pattern, "", text)
345 itm = self.testsListWidget.findItems(text, Qt.MatchFlags(Qt.MatchExactly))[0] 359 itm = self.testsListWidget.findItems(text, Qt.MatchFlags(Qt.MatchExactly))[0]
346 self.testsListWidget.setCurrentItem(itm) 360 self.testsListWidget.setCurrentItem(itm)
347 self.testsListWidget.scrollToItem(itm) 361 self.testsListWidget.scrollToItem(itm)
348 362
349 def __setRunningMode(self): 363 def __setRunningMode(self):
354 368
355 # reset counters and error infos 369 # reset counters and error infos
356 self.runCount = 0 370 self.runCount = 0
357 self.failCount = 0 371 self.failCount = 0
358 self.errorCount = 0 372 self.errorCount = 0
373 self.skippedCount = 0
374 self.expectedFailureCount = 0
375 self.unexpectedSuccessCount = 0
359 self.remainingCount = self.totalTests 376 self.remainingCount = self.totalTests
360 self.errorInfo = []
361 377
362 # reset the GUI 378 # reset the GUI
363 self.progressCounterRunCount.setText(str(self.runCount)) 379 self.progressCounterRunCount.setText(str(self.runCount))
380 self.progressCounterRemCount.setText(str(self.remainingCount))
364 self.progressCounterFailureCount.setText(str(self.failCount)) 381 self.progressCounterFailureCount.setText(str(self.failCount))
365 self.progressCounterErrorCount.setText(str(self.errorCount)) 382 self.progressCounterErrorCount.setText(str(self.errorCount))
366 self.progressCounterRemCount.setText(str(self.remainingCount)) 383 self.progressCounterSkippedCount.setText(str(self.skippedCount))
384 self.progressCounterExpectedFailureCount.setText(str(self.expectedFailureCount))
385 self.progressCounterUnexpectedSuccessCount.setText(
386 str(self.unexpectedSuccessCount))
367 self.errorsListWidget.clear() 387 self.errorsListWidget.clear()
368 self.testsListWidget.clear() 388 self.testsListWidget.clear()
369 self.progressProgressBar.setRange(0, self.totalTests) 389 self.progressProgressBar.setRange(0, self.totalTests)
370 self.__setProgressColor("green") 390 self.__setProgressColor("green")
371 self.progressProgressBar.reset() 391 self.progressProgressBar.reset()
400 def testFailed(self, test, exc): 420 def testFailed(self, test, exc):
401 """ 421 """
402 Public method called if a test fails. 422 Public method called if a test fails.
403 423
404 @param test name of the failed test (string) 424 @param test name of the failed test (string)
405 @param exc string representation of the exception (list of strings) 425 @param exc string representation of the exception (string)
406 """ 426 """
407 self.failCount += 1 427 self.failCount += 1
408 self.progressCounterFailureCount.setText(str(self.failCount)) 428 self.progressCounterFailureCount.setText(str(self.failCount))
409 self.errorsListWidget.insertItem(0, self.trUtf8("Failure: {0}").format(test)) 429 itm = QListWidgetItem(self.trUtf8("Failure: {0}").format(test))
410 self.errorInfo.insert(0, (test, exc)) 430 itm.setData(Qt.UserRole, (test, exc))
431 self.errorsListWidget.insertItem(0, itm)
411 432
412 def testErrored(self, test, exc): 433 def testErrored(self, test, exc):
413 """ 434 """
414 Public method called if a test errors. 435 Public method called if a test errors.
415 436
416 @param test name of the failed test (string) 437 @param test name of the failed test (string)
417 @param exc string representation of the exception (list of strings) 438 @param exc string representation of the exception (string)
418 """ 439 """
419 self.errorCount += 1 440 self.errorCount += 1
420 self.progressCounterErrorCount.setText(str(self.errorCount)) 441 self.progressCounterErrorCount.setText(str(self.errorCount))
421 self.errorsListWidget.insertItem(0, self.trUtf8("Error: {0}").format(test)) 442 itm = QListWidgetItem(self.trUtf8("Error: {0}").format(test))
422 self.errorInfo.insert(0, (test, exc)) 443 itm.setData(Qt.UserRole, (test, exc))
444 self.errorsListWidget.insertItem(0, itm)
445
446 def testSkipped(self, test, reason):
447 """
448 Public method called if a test was skipped.
449
450 @param test name of the failed test (string)
451 @param reason reason for skipping the test (string)
452 """
453 self.skippedCount += 1
454 self.progressCounterSkippedCount.setText(str(self.skippedCount))
455 itm = QListWidgetItem(self.trUtf8(" Skipped: {0}").format(reason))
456 itm.setForeground(Qt.blue)
457 self.testsListWidget.insertItem(1, itm)
458
459 def testFailedExpected(self, test, exc):
460 """
461 Public method called if a test fails expected.
462
463 @param test name of the failed test (string)
464 @param exc string representation of the exception (string)
465 """
466 self.expectedFailureCount += 1
467 self.progressCounterExpectedFailureCount.setText(str(self.expectedFailureCount))
468 itm = QListWidgetItem(self.trUtf8(" Expected Failure"))
469 itm.setForeground(Qt.blue)
470 self.testsListWidget.insertItem(1, itm)
471
472 def testSucceededUnexpected(self, test):
473 """
474 Public method called if a test succeeds unexpectedly.
475
476 @param test name of the failed test (string)
477 """
478 self.unexpectedSuccessCount += 1
479 self.progressCounterUnexpectedSuccessCount.setText(
480 str(self.unexpectedSuccessCount))
481 itm = QListWidgetItem(self.trUtf8(" Unexpected Success"))
482 itm.setForeground(Qt.red)
483 self.testsListWidget.insertItem(1, itm)
423 484
424 def testStarted(self, test, doc): 485 def testStarted(self, test, doc):
425 """ 486 """
426 Public method called if a test is about to be run. 487 Public method called if a test is about to be run.
427 488
464 525
465 @param lbitem the listbox item that was double clicked 526 @param lbitem the listbox item that was double clicked
466 """ 527 """
467 self.errListIndex = self.errorsListWidget.row(lbitem) 528 self.errListIndex = self.errorsListWidget.row(lbitem)
468 text = lbitem.text() 529 text = lbitem.text()
530 self.on_errorsListWidget_currentTextChanged(text)
469 531
470 # get the error info 532 # get the error info
471 test, tracebackLines = self.errorInfo[self.errListIndex] 533 test, tracebackText = lbitem.data(Qt.UserRole)
472 tracebackText = "".join(tracebackLines)
473 534
474 # now build the dialog 535 # now build the dialog
475 self.dlg = QDialog() 536 self.dlg = QDialog()
476 ui = Ui_UnittestStacktraceDialog() 537 ui = Ui_UnittestStacktraceDialog()
477 ui.setupUi(self.dlg) 538 ui.setupUi(self.dlg)
539 self.dlg.traceback = ui.traceback
478 540
479 ui.showButton = ui.buttonBox.addButton( 541 ui.showButton = ui.buttonBox.addButton(
480 self.trUtf8("Show Source"), QDialogButtonBox.ActionRole) 542 self.trUtf8("Show Source"), QDialogButtonBox.ActionRole)
481 ui.buttonBox.button(QDialogButtonBox.Close).setDefault(True) 543 ui.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
482 544
500 """ 562 """
501 if not self.dbs: 563 if not self.dbs:
502 return 564 return
503 565
504 # get the error info 566 # get the error info
505 test, tracebackLines = self.errorInfo[self.errListIndex] 567 tracebackLines = self.dlg.traceback.toPlainText().splitlines()
506 # find the last entry matching the pattern 568 # find the last entry matching the pattern
507 for index in range(len(tracebackLines) - 1, -1, -1): 569 for index in range(len(tracebackLines) - 1, -1, -1):
508 fmatch = re.search(r'File "(.*?)", line (\d*?),.*', tracebackLines[index]) 570 fmatch = re.search(r'File "(.*?)", line (\d*?),.*', tracebackLines[index])
509 if fmatch: 571 if fmatch:
510 break 572 break
523 """ 585 """
524 Constructor 586 Constructor
525 587
526 @param parent The parent widget. 588 @param parent The parent widget.
527 """ 589 """
528 unittest.TestResult.__init__(self) 590 super().__init__()
529 self.parent = parent 591 self.parent = parent
530 592
531 def addFailure(self, test, err): 593 def addFailure(self, test, err):
532 """ 594 """
533 Method called if a test failed. 595 Method called if a test failed.
534 596
597 @param test reference to the test object
598 @param err error traceback
599 """
600 super().addFailure(test, err)
601 tracebackLines = self._exc_info_to_string(err, test)
602 self.parent.testFailed(str(test), tracebackLines)
603
604 def addError(self, test, err):
605 """
606 Method called if a test errored.
607
608 @param test reference to the test object
609 @param err error traceback
610 """
611 super().addError(test, err)
612 tracebackLines = self._exc_info_to_string(err, test)
613 self.parent.testErrored(str(test), tracebackLines)
614
615 def addSkip(self, test, reason):
616 """
617 Method called if a test was skipped.
618
619 @param test reference to the test object
620 @param reason reason for skipping the test (string)
621 """
622 super().addSkip(test, reason)
623 self.parent.testSkipped(str(test), reason)
624
625 def addExpectedFailure(self, test, err):
626 """
627 Method called if a test failed expected.
628
629 @param test reference to the test object
630 @param err error traceback
631 """
632 super().addExpectedFailure(test, err)
633 tracebackLines = self._exc_info_to_string(err, test)
634 self.parent.testFailedExpected(str(test), tracebackLines)
635
636 def addUnexpectedSuccess(self, test):
637 """
638 Method called if a test succeeded expectedly.
639
640 @param test reference to the test object
641 """
642 super().addUnexpectedSuccess(test)
643 self.parent.testSucceededUnexpected(str(test))
644
645 def startTest(self, test):
646 """
647 Method called at the start of a test.
648
535 @param test Reference to the test object 649 @param test Reference to the test object
536 @param err The error traceback 650 """
537 """ 651 super().startTest(test)
538 unittest.TestResult.addFailure(self, test, err) 652 self.parent.testStarted(str(test), test.shortDescription())
539 tracebackLines = traceback.format_exception(*err + (10,)) 653
540 self.parent.testFailed(str(test), tracebackLines) 654 def stopTest(self, test):
541 655 """
542 def addError(self, test, err): 656 Method called at the end of a test.
543 """
544 Method called if a test errored.
545 657
546 @param test Reference to the test object 658 @param test Reference to the test object
547 @param err The error traceback 659 """
548 """ 660 super().stopTest(test)
549 unittest.TestResult.addError(self, test, err)
550 tracebackLines = traceback.format_exception(*err + (10,))
551 self.parent.testErrored(str(test), tracebackLines)
552
553 def startTest(self, test):
554 """
555 Method called at the start of a test.
556
557 @param test Reference to the test object
558 """
559 unittest.TestResult.startTest(self, test)
560 self.parent.testStarted(str(test), test.shortDescription())
561
562 def stopTest(self, test):
563 """
564 Method called at the end of a test.
565
566 @param test Reference to the test object
567 """
568 unittest.TestResult.stopTest(self, test)
569 self.parent.testFinished() 661 self.parent.testFinished()
570 662
571 663
572 class UnittestWindow(QMainWindow): 664 class UnittestWindow(QMainWindow):
573 """ 665 """

eric ide

mercurial