Commits

Lucas Taylor  committed 1d7f28b

[WIP] Trial reporters to growl test results ...
Current implementation leaves reactor dirty. Reporter needs to call stopListening on the growler after each test

  • Participants
  • Parent commits f0135b4

Comments (0)

Files changed (4)

File tests/test_growl.py

 # -*- coding: utf-8 -*-
 from twisted.trial.unittest import TestCase
 from twisted.python import log
+from twisted.internet import defer
 
 from txGrowl import GrowlObserver
 
         self.growler = growler
         
         log.addObserver(self.growler)
+
+        def disconnectGrowler():
+            if self.growler.lport is not None:
+                return defer.maybeDeferred(self.growler.lport.stopListening)
+        self.addCleanup(disconnectGrowler)
         self.addCleanup(log.removeObserver, self.growler)
 
-        def disconnectGrowler():
-            if self.growler.protocol.transport is not None:
-                self.growler.protocol.transport.stopListening()
-        self.addCleanup(disconnectGrowler)
-            
-
-
-    def tearDown(self):
-        def finalize(_):
-            log.msg("Finished", growl=True)
-        del self.growler
-        
+    
     def test_growler(self):
+        """Logged messages should be added to the growled observable list"""
         log.msg("Significant Event", growl=True)
         
         growled = self.growler.observed.pop()
         self.assertEqual(message, "Significant Event")
         self.assertEqual(priority, 0)
 
-        log.err("Problem", growl=True)
         self.flushLoggedErrors()
 
+
     def test_ignore(self):
         # Ignore messages w/o required key
         self.growler.required_key = None
         log.msg("This should be ignored by Growl observer", growl=False, notification="Default")
 
         self.assertRaises(IndexError, self.growler.observed.pop)
-        self.flushLoggedErrors()        
+        self.flushLoggedErrors()
+
+
+    def test_skipped(self):
+        pass
+    test_skipped.skip = "Dummy method to test skip reporter"
+
+    
+    def test_todo_expected(self):
+        raise Exception("Intended Consequences")
+    test_todo_expected.todo = "Dummy method to show expected failures"
+
+
+    def test_todo_unexpected(self):
+        pass
+    test_todo_unexpected.todo = "Dummy method to show unexpected success"

File twisted/plugins/trial_reporters.py

+"""
+trial_reporters - Register custom trial reporter plugins to Growl test completion
+"""
+from twisted.plugins import twisted_trial
+
+        
+Growler = twisted_trial._Reporter("Growl Reporter",
+                 module="txGrowl.reporter",
+                 description="verbose color output with Growl notifications",
+                 longOpt="growl",
+                 shortOpt="g",
+                 klass="GrowlReporter")
+
+MinimalGrowler = twisted_trial._Reporter("Minimal Growl Reporter",
+                 "txGrowl.reporter",
+                 description="minimal summary output with Growl notifications",
+                 longOpt="summary-growl",
+                 shortOpt=None,
+                 klass="MinimalGrowlReporter")
+
+BWGrowler = twisted_trial._Reporter("Black-And-White Growl Reporter",
+                "txGrowl.reporter",
+                description="Colorless verbose output with Growl notifications",
+                longOpt="bwverbose-growl",
+                shortOpt=None,
+                klass="VerboseTextGrowlReporter")
+
+Classic = twisted_trial._Reporter("Classic Reporter",
+                 "txGrowl.reporter",
+                 description="terse text output with Growl notifications",
+                 longOpt="text-growl",
+                 shortOpt=None,
+                 klass="TextGrowlReporter")
+
+Timing = twisted_trial._Reporter("Timing Reporter",
+                "txGrowl.reporter",
+                description="Timing output with Growl notifications",
+                longOpt="timing-growl",
+                shortOpt=None,
+                klass="TimingTextGrowlReporter")
+
+Subunit = twisted_trial._Reporter("Subunit Reporter",
+                 "txGrowl.reporter",
+                 description="subunit output with Growl notifications",
+                 longOpt="subunit-growl",
+                 shortOpt=None,
+                 klass="SubunitGrowlReporter")

File txGrowl/observer.py

         self.addNotification("Default")
         self.addNotification("Error")
 
-
+        
     def __call__(self, event):
         if self.required_key not in event:
             return

File txGrowl/reporter.py

+import time
+
+from twisted.python import log
+from twisted.trial.reporter import (
+    TreeReporter, MinimalReporter, TextReporter, VerboseTextReporter,
+    TimingTextReporter, SubunitReporter
+)
+
+from txGrowl import GrowlObserver
+
+
+
+class GrowlReporterMixin(object):
+    """
+    Send growl notifications for key testing events.
+    
+    In practice, it only makes sense to emit growl notices when tests complete,
+    in the `done` method, and possibly during failures/errors
+    
+    __dunderscore methods below are stubbed-in for examples
+
+    See `twisted.trial.itrial.IReporter` for interface docs
+
+    TODO: Accept command-line args to set growl destination/password
+    """
+    def __init__(self, *args, **kwargs):
+        super(GrowlReporterMixin, self).__init__(*args, **kwargs)
+        self._createClient()
+        
+    def _createClient(self):
+        app_name = "txGrowlReporter"
+        password = "password"
+        destinations = ["127.0.0.1",]
+
+        growler = GrowlObserver(app_name, password, destinations)
+        growler.required_key = "growlreport"
+
+        # Register notification types that Growl should display
+        growler.addNotification("Test Result")
+        growler.addNotification("Tests Complete")
+        
+        self.growler = growler
+        
+        log.addObserver(self.growler)
+
+
+    def getDescription(self, test):
+        """
+        Return the name of the method which 'test' represents.
+
+        e.g. getDescription(TestCase('test_foo')) ==> test_foo
+        """
+        return test.id().split('.')[-1]
+
+
+    def __startTest(self, test):
+        test_name = self.getDescription(test)
+        log.msg(test_name, title="Test START", growlreport=True)
+        return super(GrowlReporterMixin, self).startTest(test)
+
+
+    def __stopTest(self, test):
+        self.growler.lport.stopListening()
+        test_name = self.getDescription(test)
+        log.msg(test_name, title="Test STOPPED", growlreport=True)
+        
+        return super(GrowlReporterMixin, self).stopTest(test)
+
+
+    def __addSuccess(self, test):
+        test_name = self.getDescription(test)
+        log.msg(test_name, title="Test SUCCESS", growlreport=True)
+        super(GrowlReporterMixin, self).addSuccess(test)
+
+
+    def __addError(self, *args):
+        test, err = args
+        test_name = self.getDescription(test)
+        log.err(err.getErrorMessage(), growlreport=True, title="Test [ERROR]: %s" % test_name)
+        super(GrowlReporterMixin, self).addError(*args)
+
+    
+    def __addFailure(self, *args):
+        test, err = args
+        test_name = self.getDescription(test)
+        log.err(err.getErrorMessage(), growlreport=True, title="Test [FAILURE]: %s" % test_name)
+        super(GrowlReporterMixin, self).addFailure(*args)
+
+    
+    def __addSkip(self, *args):
+        test, reason = args
+        test_name = self.getDescription(test)
+        log.msg(reason, growlreport=True, title="Test [SKIPPED]: %s" % test_name)
+        super(GrowlReporterMixin, self).addSkip(*args)
+
+    
+    def __addExpectedFailure(self, *args):
+        test, failure, todo = args
+        test_name = self.getDescription(test)
+        log.msg(todo.reason, growlreport=True, title="Test [TODO]: %s" % test_name)
+        super(GrowlReporterMixin, self).addExpectedFailure(*args)
+    
+    def __addUnexpectedSuccess(self, *args):
+        test, todo = args
+        test_name = self.getDescription(test)
+        log.msg(todo.reason, growlreport=True, title="Test [SUCCESS!?!]: %s" % test_name)
+        super(GrowlReporterMixin, self).addUnexpectedSuccess(*args)
+
+    
+    def done(self):
+        """Summarize, via growl"""
+        summary = self._getSummary()
+        if self._startTime is not None:
+            timing = 'Ran %d tests in %.3fs' % (self.testsRun,
+                          time.time() - self._startTime)
+        else:
+            timing = 'No Tests Were Run'
+        message = "%s\n%s" % (timing, summary)
+        if self.wasSuccessful():
+            log.msg(message, growlreport=True, title="Tests PASSED", notification="Test Completion")
+        else:
+            log.err(message, growlreport=True, title="Tests FAILED", notification="Test Completion")
+
+        super(GrowlReporterMixin, self).done()
+
+
+
+class GrowlReporter(GrowlReporterMixin, TreeReporter):
+    """
+    Print out the tests in the form of a tree.
+
+    Tests are indented according to which class and module they belong.
+    Results are printed in ANSI color.
+    """
+    pass
+
+class MinimalGrowlReporter(GrowlReporterMixin, MinimalReporter):
+    """
+    A minimalist reporter that prints only a summary of the test result, in
+    the form of (timeTaken, #tests, #tests, #errors, #failures, #skips).
+    """
+    pass
+
+class TextGrowlReporter(GrowlReporterMixin, TextReporter):
+    """
+    Simple reporter that prints a single character for each test as it runs,
+    along with the standard Trial summary text.
+    """
+    pass
+
+class VerboseTextGrowlReporter(GrowlReporterMixin, VerboseTextReporter):
+    """
+    A verbose reporter that prints the name of each test as it is running.
+
+    Each line is printed with the name of the test, followed by the result of
+    that test.
+    """
+    pass
+
+class TimingTextGrowlReporter(GrowlReporterMixin, TimingTextReporter):
+    """
+    Prints out each test as it is running, followed by the time taken for each
+    test to run.
+    """
+    pass
+
+class SubunitGrowlReporter(GrowlReporterMixin, SubunitReporter):
+    """
+    Reports test output via Subunit.
+    """
+    pass