[tor-commits] [stem/master] Moving python testing output filters to module

atagar at torproject.org atagar at torproject.org
Fri Dec 30 18:15:42 UTC 2011


commit d02aa3e9128e92982e6f2760594523556bd45e85
Author: Damian Johnson <atagar at torproject.org>
Date:   Wed Dec 28 09:24:35 2011 -0800

    Moving python testing output filters to module
    
    Rewriting the hacks I was applying to test output for better readability as a
    proper module. Nicer code and now much easier to add more filters.
---
 run_tests.py     |   34 ++++++--------------
 test/__init__.py |    2 +-
 test/output.py   |   93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 104 insertions(+), 25 deletions(-)

diff --git a/run_tests.py b/run_tests.py
index 7247dd7..7e3dadb 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -12,6 +12,7 @@ import logging
 import unittest
 import StringIO
 
+import test.output
 import test.runner
 import test.unit.version
 import test.unit.socket.control_message
@@ -94,30 +95,10 @@ TEST_OUTPUT_ATTR = {
   "... skipped": (term.Color.BLUE,),
 }
 
-# lines that print_test_results have processed which contained a test failure
-PRINTED_ERRORS = []
-
 def print_divider(msg, is_header = False):
   attr = HEADER_ATTR if is_header else CATEGORY_ATTR
   print term.format("%s\n%s\n%s\n" % (DIVIDER, msg.center(70), DIVIDER), *attr)
 
-def print_test_results(test_results):
-  test_results.seek(0)
-  for line in test_results.readlines():
-    line_attr = DEFAULT_TEST_ATTR
-    
-    for result in TEST_OUTPUT_ATTR:
-      if result in line:
-        line_attr = TEST_OUTPUT_ATTR[result]
-        
-        if result in ("... FAIL", "... ERROR"):
-          PRINTED_ERRORS.append(line)
-        
-        break
-    
-    if line_attr: line = term.format(line, *line_attr)
-    sys.stdout.write(line)
-
 if __name__ == '__main__':
   start_time = time.time()
   run_unit_tests = False
@@ -193,6 +174,9 @@ if __name__ == '__main__':
     
     print
   
+  error_tracker = test.output.ErrorTracker()
+  output_filters = (error_tracker.get_filter(), test.output.colorize, )
+  
   if run_unit_tests:
     print_divider("UNIT TESTS", True)
     
@@ -201,7 +185,8 @@ if __name__ == '__main__':
       suite = unittest.TestLoader().loadTestsFromTestCase(test_class)
       test_results = StringIO.StringIO()
       unittest.TextTestRunner(test_results, verbosity=2).run(suite)
-      print_test_results(test_results)
+      
+      sys.stdout.write(test.output.apply_filters(test_results.getvalue(), *output_filters))
       print
     
     print
@@ -251,7 +236,8 @@ if __name__ == '__main__':
           suite = unittest.TestLoader().loadTestsFromTestCase(test_class)
           test_results = StringIO.StringIO()
           unittest.TextTestRunner(test_results, verbosity=2).run(suite)
-          print_test_results(test_results)
+          
+          sys.stdout.write(test.output.apply_filters(test_results.getvalue(), *output_filters))
           print
       except OSError:
         pass
@@ -262,10 +248,10 @@ if __name__ == '__main__':
   
   runtime_label = "(%i seconds)" % (time.time() - start_time)
   
-  if PRINTED_ERRORS:
+  if error_tracker.has_error_occured():
     print term.format("TESTING FAILED %s" % runtime_label, term.Color.RED, term.Attr.BOLD)
     
-    for line in PRINTED_ERRORS:
+    for line in error_tracker:
       print term.format("  %s" % line, term.Color.RED, term.Attr.BOLD)
   else:
     print term.format("TESTING PASSED %s" % runtime_label, term.Color.GREEN, term.Attr.BOLD)
diff --git a/test/__init__.py b/test/__init__.py
index 9afce0a..a663300 100644
--- a/test/__init__.py
+++ b/test/__init__.py
@@ -2,5 +2,5 @@
 Unit and integration tests for the stem library.
 """
 
-__all__ = ["runner"]
+__all__ = ["output", "runner"]
 
diff --git a/test/output.py b/test/output.py
new file mode 100644
index 0000000..4b53e52
--- /dev/null
+++ b/test/output.py
@@ -0,0 +1,93 @@
+"""
+Variety of filters for the python unit testing output, which can be chained
+together for improved readability.
+"""
+
+import stem.util.enum
+import stem.util.term as term
+
+LineType = stem.util.enum.Enum("OK", "FAIL", "ERROR", "SKIPPED", "CONTENT")
+
+LINE_ENDINGS = {
+  "... ok": LineType.OK,
+  "... FAIL": LineType.FAIL,
+  "... ERROR": LineType.ERROR,
+  "... skipped": LineType.SKIPPED,
+}
+
+LINE_ATTR = {
+  LineType.OK: (term.Color.GREEN,),
+  LineType.FAIL: (term.Color.RED, term.Attr.BOLD),
+  LineType.ERROR: (term.Color.RED, term.Attr.BOLD),
+  LineType.SKIPPED: (term.Color.BLUE,),
+  LineType.CONTENT: (term.Color.CYAN,),
+}
+
+def apply_filters(testing_output, *filters):
+  """
+  Gets the tests results, possably processed through a series of filters. The
+  filters are applied in order, each getting the output of the previous.
+  
+  A filter's input arguments should be the line's (type, content) and the
+  output is either a string with the new content or None if the line should be
+  omitted.
+  
+  Arguments:
+    testing_output (str) - output from the unit testing
+    filters (list) - functors to be applied to each line of the results
+  
+  Returns:
+    str with the processed test results
+  """
+  
+  results = []
+  
+  for line in testing_output.split("\n"):
+    # determine the type of the line
+    line_type = LineType.CONTENT
+    
+    for ending in LINE_ENDINGS:
+      if line.endswith(ending):
+        line_type = LINE_ENDINGS[ending]
+        break
+    
+    for result_filter in filters:
+      line = result_filter(line_type, line)
+      if line == None: break
+    
+    if line != None:
+      results.append(line)
+  
+  return "\n".join(results)
+
+def colorize(line_type, line_content):
+  """
+  Applies escape sequences so each line is colored according to its type.
+  """
+  
+  return term.format(line_content, *LINE_ATTR[line_type])
+
+class ErrorTracker:
+  """
+  Stores any failure or error results we've encountered.
+  """
+  
+  def __init__(self):
+    self._errors = []
+  
+  def has_error_occured(self):
+    return bool(self._errors)
+  
+  def get_filter(self):
+    def _error_tracker(line_type, line_content):
+      if line_type in (LineType.FAIL, LineType.ERROR):
+        self._errors.append(line_content)
+      
+      return line_content
+    
+    return _error_tracker
+  
+  def __iter__(self):
+    for error_line in self._errors:
+      yield error_line
+





More information about the tor-commits mailing list