text: ################################################################################
  
  if __name__.startswith('gsdl2.tests.'):
      from gsdl2.tests.test_utils import unittest, import_submodule
  else:
      from test.test_utils import unittest, import_submodule
  import re
  import time
  import sys
  try: 
      import StringIO
  except ImportError:
      import io as StringIO
  import random
  
  from inspect import getdoc
  
  # This is needed for correct tracebacks
  __unittest = 1
  
  ################################################################################
  # Redirect stdout / stderr for the tests
  
  def redirect_output():
      yield sys.stderr, sys.stdout
      sys.stderr, sys.stdout = StringIO.StringIO(), StringIO.StringIO()
      yield sys.stderr, sys.stdout
  
  def restore_output(err, out):
      sys.stderr, sys.stdout = err, out
  
  def StringIOContents(io):
      io.seek(0)
      return io.read()
  
  ################################################################################
  # TestCase patching
  #
  
  def TestCase_run(self, result=None):
      if result is None: result = self.defaultTestResult()
      result.startTest(self)
      testMethod = getattr(self, self._testMethodName)
      
      try:
  
      ########################################################################
      # Pre run:
          #TODO: only redirect output if not tagged interactive
  
          result.tests[self.dot_syntax_name()] = {
              'times' : [],
          }
          
          tests = result.tests[self.dot_syntax_name()]
          (realerr, realout), (stderr, stdout) =  redirect_output()
          test_tags = list(get_tags(self.__class__, testMethod))
  
          if 0 or 'interactive' in test_tags:       # DEBUG
              restore_output(realerr, realout)
  
      ########################################################################
  
          for i in range(self.times_run):
              t = time.time()
              
              try:
                  self.setUp()
              except KeyboardInterrupt:
                  raise
              except:
                  result.addError(self, self._exc_info())
                  return
  
              ok = False
              try:
                  testMethod()
                  ok = True
              except self.failureException:
                  result.addFailure(self, self._exc_info())
              except KeyboardInterrupt:
                  raise
              except:
                  result.addError(self, self._exc_info())
              
              try:
                  self.tearDown()
              except KeyboardInterrupt:
                  raise
              except:
                  result.addError(self, self._exc_info())
                  ok = False
              
              
              tests["times"] += [time.time() -t]
              
              if not ok: break            
          
              # if ok:
              #     if i == 0:
              #         result.addSuccess(self)
              # else: break
      
          if ok:
              result.addSuccess(self)
      ########################################################################
      # Post run
  
          restore_output(realerr, realout)
  
          tests["stdout"] = StringIOContents(stdout)
          tests["stderr"] = StringIOContents(stderr)
          tests["tags"]   = test_tags
  
      ########################################################################
  
      finally:
          result.stopTest(self)
  
  ################################################################################
  # TestResult 
  #
  
  def TestResult___init__(self):
      self.failures   = []
      self.errors     = []
      self.tests      = {}
      self.testsRun   = 0
      self.shouldStop = 0
  
  # TODO: all this is available in the traceback object err
  FILE_LINENUMBER_RE = re.compile(r'File "([^"]+)", line ([0-9]+)')
  
  def errorHandling(key):
      def handling(self, test, err):        
          traceback = self._exc_info_to_string(err, test)
          error_file, line_number = FILE_LINENUMBER_RE.search(traceback).groups()
          error =  (
              test.dot_syntax_name(),
              traceback,
              error_file,
              line_number,         #TODO: add locals etc
          )
          getattr(self, key).append(error)
  
          # Append it to individual test dict for easy access
          self.tests[test.dot_syntax_name()][key[:-1]] = error
  
      return handling
  
  ################################################################################
  
  def printErrorList(self, flavour, errors):
      for test, err in [(e[0], e[1]) for e in errors]:
          self.stream.writeln(self.separator1)
          self.stream.writeln("%s: %s" % (flavour, test))
          self.stream.writeln(self.separator2)
          self.stream.writeln("%s" % err)
  
          # DUMP REDIRECTED STDERR / STDOUT ON ERROR / FAILURE
          if self.show_redirected_on_errors:
              stderr, stdout = map(self.tests[test].get, ('stderr','stdout'))
              if stderr: self.stream.writeln("STDERR:\n%s" % stderr)
              if stdout: self.stream.writeln("STDOUT:\n%s" % stdout)
  
  ################################################################################
  # Exclude by tags
  #
  
  TAGS_RE = re.compile(r"\|[tT]ags:(-?[ a-zA-Z,0-9_\n]+)\|", re.M)
  
  class TestTags:
      def __init__(self):
          self.memoized = {}
          self.parent_modules = {}
  
      def get_parent_module(self, class_):
          if class_ not in self.parent_modules:
              self.parent_modules[class_] = import_submodule(class_.__module__)
          return self.parent_modules[class_]
  
      def __call__(self, parent_class, meth):
          key = (parent_class, meth.__name__)
          if key not in self.memoized:
              parent_module = self.get_parent_module(parent_class)
  
              module_tags = getattr(parent_module, '__tags__', [])
              class_tags  = getattr(parent_class,  '__tags__', [])
  
              tags = TAGS_RE.search(getdoc(meth) or '')
              if tags: test_tags = [t.strip() for t in tags.group(1).split(',')]
              else:    test_tags = []
          
              combined = set()
              for tags in (module_tags, class_tags, test_tags):
                  if not tags: continue
          
                  add    = set([t for t in tags if not t.startswith('-')])
                  remove = set([t[1:] for t in tags if t not in add])
          
                  if add:     combined.update(add)
                  if remove:  combined.difference_update(remove)
      
              self.memoized[key] = combined
  
          return self.memoized[key]
  
  get_tags = TestTags()
  
  ################################################################################
  # unittest.TestLoader
  #
  def CmpToKey(mycmp):
      'Convert a cmp= function into a key= function'
      class K(object):
          def __init__(self, obj, *args):
              self.obj = obj
          def __lt__(self, other):
              return mycmp(self.obj, other.obj) == -1
      return K
  
  def getTestCaseNames(self, testCaseClass):
      def test_wanted(attrname, testCaseClass=testCaseClass,
                                prefix=self.testMethodPrefix):
          if not attrname.startswith(prefix): return False
          else:
              actual_attr = getattr(testCaseClass, attrname)
              return (
                   hasattr(actual_attr, '__call__') and
                   not [t for t in  get_tags(testCaseClass, actual_attr)
                        if t in self.exclude]
              )
  
      # TODO:
  
      # Replace test_not_implemented mechanism with technique that names the tests
      # todo_test_xxxxxx, then when wanting to fail them, loads any members that
      # startswith(test_prefix)
      
      # REGEX FOR TEST_NOT_IMPLEMENTED
      # SEARCH:
      #    def (test_[^ ]+)((?:\s+#.*\n?)+\s+)self\.assert_\(test_not_implemented\(\)\)
      # REPLACE:
      #    def todo_\1\2self.fail()
  
      testFnNames = [c for c in dir(testCaseClass) if test_wanted(c)]
  
      for baseclass in testCaseClass.__bases__:
          for testFnName in self.getTestCaseNames(baseclass):
              if testFnName not in testFnNames:  # handle overridden methods
                  testFnNames.append(testFnName)
  
      if self.randomize_tests: 
          random.shuffle(testFnNames)
      elif self.sortTestMethodsUsing:
          testFnNames.sort(key=CmpToKey(self.sortTestMethodsUsing))
  
      return testFnNames
  
  ################################################################################
  
  def patch(**kwds):
      """Customize the unittest module according to the run-time options
  
      Recognized keyword arguments:
      incomplete, randomize, seed, exclude, timings and show_output
      
      """
  
      option_incomplete = kwds.get('incomplete', False)
      option_randomize = kwds.get('randomize', False)
      try:
          option_seed = kwds['seed'] is not None
      except KeyError:
          option_seed = False
      option_exclude = kwds.get('exclude', ('interactive',))
      option_timings = kwds.get('timings', 1)
      option_show_output = kwds.get('show_output', False)
  
      # Incomplete todo_xxx tests
      if option_incomplete:
          unittest.TestLoader.testMethodPrefix = (
              unittest.TestLoader.testMethodPrefix, 'todo_'
          )
      
      # Randomizing    
      unittest.TestLoader.randomize_tests = option_randomize or option_seed
  
      unittest.TestLoader.getTestCaseNames = getTestCaseNames
      unittest.TestLoader.exclude = option_exclude
  
      # Timing
      unittest.TestCase.times_run = option_timings
      unittest.TestCase.run = TestCase_run
      unittest.TestCase.dot_syntax_name = lambda self: (
          "%s.%s"% (self.__class__.__name__, self._testMethodName) )
  
      # Error logging
      unittest.TestResult.show_redirected_on_errors = option_show_output
      unittest.TestResult.__init__   = TestResult___init__
      unittest.TestResult.addError   = errorHandling('errors')
      unittest.TestResult.addFailure = errorHandling('failures')
  
      unittest._TextTestResult.printErrorList = printErrorList
      
  ################################################################################
  
  