text: #!/usr/bin/env python
  
  # Modified from https://github.com/agramian/custom-text-test-runner
  
  # The MIT License (MIT)
  #
  # Copyright (c) 2015 Abtin Gramian
  #
  # Permission is hereby granted, free of charge, to any person obtaining a copy
  # of this software and associated documentation files (the "Software"), to deal
  # in the Software without restriction, including without limitation the rights
  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  # copies of the Software, and to permit persons to whom the Software is
  # furnished to do so, subject to the following conditions:
  #
  # The above copyright notice and this permission notice shall be included in all
  # copies or substantial portions of the Software.
  #
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  # SOFTWARE.
  
  
  import unittest
  import os, sys, traceback
  import inspect
  import json
  import time
  import re
  import operator
  from unittest.runner import result
  from unittest.runner import registerResult
  from functools import reduce
  
  class TablePrinter(object):
      # Modified from https://github.com/agramian/table-printer, same license as above
      "Print a list of dicts as a table"
      def __init__(self, fmt, sep='', ul=None, tl=None, bl=None):
          """
          @param fmt: list of tuple(heading, key, width)
                          heading: str, column label
                          key: dictionary key to value to print
                          width: int, column width in chars
          @param sep: string, separation between columns
          @param ul: string, character to underline column label, or None for no underlining
          @param tl: string, character to draw as top line over table, or None
          @param bl: string, character to draw as bottom line under table, or None
          """
          super(TablePrinter,self).__init__()
          fmt = [x + ('left',) if len(x) < 4 else x for x in fmt]
          self.fmt   = str(sep).join('{lb}{0}:{align}{1}{rb}'.format(key, width, lb='{', rb='}', align='<' if alignment == 'left' else '>') for heading,key,width,alignment in fmt)
          self.head  = {key:heading for heading,key,width,alignment in fmt}
          self.ul    = {key:str(ul)*width for heading,key,width,alignment in fmt} if ul else None
          self.width = {key:width for heading,key,width,alignment in fmt}
          self.tl    = {key:str(tl)*width for heading,key,width,alignment in fmt} if tl else None
          self.bl    = {key:str(bl)*width for heading,key,width,alignment in fmt} if bl else None
  
      def row(self, data, separation_character=False):
          if separation_character:
              return self.fmt.format(**{ k:str(data.get(k,''))[:w] for k,w in self.width.items() })
          else:
              data = { k:str(data.get(k,'')) if len(str(data.get(k,''))) <= w else '%s...' %str(data.get(k,''))[:(w-3)] for k,w in self.width.items() }
              return self.fmt.format(**data)
  
      def __call__(self, data_list, totals=None):
          _r = self.row
          res = [_r(data) for data in data_list]
          res.insert(0, _r(self.head))
          if self.ul:
              res.insert(1, _r(self.ul, True))
          if self.tl:
              res.insert(0, _r(self.tl, True))
          if totals:
              if self.ul:
                  res.insert(len(res), _r(self.ul, True))
              res.insert(len(res), _r(totals))
          if self.bl:
              res.insert(len(res), _r(self.bl, True))
          return '\n'.join(res)
  
  
  def get_function_args(func_ref):
      try:
          return [p for p in inspect.getargspec(func_ref).args if p != 'self']
      except:
          return None
  
  def store_class_fields(class_ref, args_passed):
      """ Store the passed in class fields in self
      """
      params = get_function_args(class_ref.__init__)
      for p in params: setattr(class_ref, p, args_passed[p])
  
  def sum_dict_key(d, key, cast_type=None):
      """ Sum together all values matching a key given a passed dict
      """
      return reduce( (lambda x, y: x + y), [eval("%s(x['%s'])" %(cast_type, key)) if cast_type else x[key] for x in d] )
  
  def case_name(name):
      """ Test case name decorator to override function name.
      """
      def decorator(function):
          function.__dict__['test_case_name'] = name
          return function
      return decorator
  
  def skip_device(name):
      """ Decorator to mark a test to only run on certain devices
          Takes single device name or list of names as argument
      """
      def decorator(function):
          name_list = name if type(name) == list else [name]
          function.__dict__['skip_device'] = name_list
          return function
      return decorator
  
  def _set_test_type(function, test_type):
      """ Test type setter
      """
      if 'test_type' in function.__dict__:
          function.__dict__['test_type'].append(test_type)
      else:
          function.__dict__['test_type'] = [test_type]
      return function
  
  def smoke(function):
      """ Test decorator to mark test as smoke type
      """
      return _set_test_type(function, 'smoke')
  
  def guide_discovery(function):
      """ Test decorator to mark test as guide_discovery type
      """
      return _set_test_type(function, 'guide_discovery')
  
  def focus(function):
      """ Test decorator to mark test as focus type to all rspec style debugging of cases
      """
      return _set_test_type(function, 'focus')
  
  class _WritelnDecorator(object):
      """Used to decorate file-like objects with a handy 'writeln' method"""
      def __init__(self,stream):
          self.stream = stream
  
      def __getattr__(self, attr):
          if attr in ('stream', '__getstate__'):
              raise AttributeError(attr)
          return getattr(self.stream,attr)
  
      def writeln(self, arg=None):
          if arg:
              self.write(arg)
          self.write('\n') # text-mode streams translate to \r\n if needed
  
  class CustomTextTestResult(result.TestResult):
      _num_formatting_chars = 150
      _execution_time_significant_digits = 4
      _pass_percentage_significant_digits = 2
  
      def __init__(self, stream, descriptions, verbosity, results_file_path, result_screenshots_dir, show_previous_results, config, test_types):
          super(CustomTextTestResult, self).__init__(stream, descriptions, verbosity)
          store_class_fields(self, locals())
          self.show_overall_results = verbosity > 0
          self.show_test_info = verbosity > 1
          self.show_individual_suite_results = verbosity > 2
          self.show_errors = verbosity > 3
          self.show_errors_detail = verbosity > 4
          self.show_all = verbosity > 4
          self.suite = None
          self.total_execution_time = 0
          self.separator1 = "=" * CustomTextTestResult._num_formatting_chars
          self.separator2 = "-" * CustomTextTestResult._num_formatting_chars
          self.separator3 = "_" * CustomTextTestResult._num_formatting_chars
          self.separator4 = "*" * CustomTextTestResult._num_formatting_chars
          self.separator_failure = "!" * CustomTextTestResult._num_formatting_chars
          self.separator_pre_result = '.' * CustomTextTestResult._num_formatting_chars
  
      def getDescription(self, test):
          doc_first_line = test.shortDescription()
          if self.descriptions and doc_first_line:
              return '\n'.join((str(test), doc_first_line))
          else:
              return str(test)
  
      def getSuiteDescription(self, test):
          doc = test.__class__.__doc__
          return doc and doc.split("\n")[0].strip() or None
  
      def startTestRun(self):
          self.results = None
          self.previous_suite_runs = []
          if os.path.isfile(self.results_file_path):
              with open(self.results_file_path, 'rb') as f:
                  try:
                      self.results = json.load(f)
                      # recreated results dict with int keys
                      self.results['suites'] = {int(k):v for (k,v) in list(self.results['suites'].items())}
                      self.suite_map = {v['name']:int(k) for (k,v) in list(self.results['suites'].items())}
                      self.previous_suite_runs = list(self.results['suites'].keys())
                  except:
                      pass
          if not self.results:
              self.results = {'suites': {},
                              'name': '',
                              'num_passed': 0,
                              'num_failed': 0,
                              'num_skipped': 0,
                              'num_expected_failures': 0,
                              'execution_time': None}
          self.suite_number = int(sorted(self.results['suites'].keys())[-1]) + 1 if len(self.results['suites']) else 0
          self.case_number = 0
          self.suite_map = {}
  
      def stopTestRun(self):
          # if no tests or some failure occured execution time may not have been set
          try:
              self.results['suites'][self.suite_map[self.suite]]['execution_time'] = format(self.suite_execution_time, '.%sf' %CustomTextTestResult._execution_time_significant_digits)
          except:
              pass
          self.results['execution_time'] = format(self.total_execution_time, '.%sf' %CustomTextTestResult._execution_time_significant_digits)
          self.stream.writeln(self.separator3)
          with open(self.results_file_path, 'w') as f:
              json.dump(self.results, f)
  
      def startTest(self, test):
          suite_base_category = test.__class__.base_test_category if hasattr(test.__class__, 'base_test_category') else ''
          self.next_suite = os.path.join(suite_base_category, test.__class__.name if hasattr(test.__class__, 'name') else test.__class__.__name__)
          self.case = test._testMethodName
          super(CustomTextTestResult, self).startTest(test)
          if not self.suite or self.suite != self.next_suite:
              if self.suite:
                  self.results['suites'][self.suite_map[self.suite]]['execution_time'] = format(self.suite_execution_time, '.%sf' %CustomTextTestResult._execution_time_significant_digits)
              self.suite_execution_time = 0
              self.suite = self.next_suite
              if self.show_test_info:
                  self.stream.writeln(self.separator1)
                  self.stream.writeln("TEST SUITE: %s" %self.suite)
                  self.stream.writeln("Description: %s" %self.getSuiteDescription(test))
          try:
              name_override = getattr(test, test._testMethodName).__func__.__dict__['test_case_name']
          except:
              name_override = None
          self.case = name_override if name_override else self.case
          if self.show_test_info:
              self.stream.writeln(self.separator2)
              self.stream.writeln("CASE: %s" %self.case)
              self.stream.writeln("Description: %s" %test.shortDescription())
              self.stream.writeln(self.separator2)
              self.stream.flush()
          self.current_case_number = self.case_number
          if self.suite not in self.suite_map:
              self.suite_map[self.suite] = self.suite_number
              self.results['suites'][self.suite_number] = {
                  'name': self.suite,
                  'class': test.__class__.__name__,
                  'module': re.compile('.* \((.*)\)').match(str(test)).group(1),
                  'description': self.getSuiteDescription(test),
                  'cases': {},
                  'used_case_names': {},
                  'num_passed': 0,
                  'num_failed': 0,
                  'num_skipped': 0,
                  'num_expected_failures': 0,
                  'execution_time': None}
              self.suite_number += 1
              self.num_cases = 0
              self.num_passed = 0
              self.num_failed = 0
              self.num_skipped = 0
              self.num_expected_failures = 0
          self.results['suites'][self.suite_map[self.suite]]['cases'][self.case_number] = {
              'name': self.case,
              'method': test._testMethodName,
              'result': None,
              'description': test.shortDescription(),
              'note': None,
              'errors': None,
              'failures': None,
              'screenshots': [],
              'new_version': 'No',
              'execution_time': None}
          self.start_time = time.time()
          if self.test_types:
              if ('test_type' in getattr(test, test._testMethodName).__func__.__dict__
                  and set([s.lower() for s in self.test_types]) == set([s.lower() for s in getattr(test, test._testMethodName).__func__.__dict__['test_type']])):
                  pass
              else:
                  getattr(test, test._testMethodName).__func__.__dict__['__unittest_skip_why__'] = 'Test run specified to only run tests of type "%s"' %','.join(self.test_types)
                  getattr(test, test._testMethodName).__func__.__dict__['__unittest_skip__'] = True
          if 'skip_device' in getattr(test, test._testMethodName).__func__.__dict__:
              for device in getattr(test, test._testMethodName).__func__.__dict__['skip_device']:
                  if self.config and device.lower() in self.config['device_name'].lower():
                      getattr(test, test._testMethodName).__func__.__dict__['__unittest_skip_why__'] = 'Test is marked to be skipped on %s' %device
                      getattr(test, test._testMethodName).__func__.__dict__['__unittest_skip__'] = True
                      break
  
      def stopTest(self, test):
          self.end_time = time.time()
          self.execution_time = self.end_time - self.start_time
          self.suite_execution_time += self.execution_time
          self.total_execution_time += self.execution_time
          super(CustomTextTestResult, self).stopTest(test)
          self.num_cases += 1
          self.results['suites'][self.suite_map[self.suite]]['num_passed'] = self.num_passed
          self.results['suites'][self.suite_map[self.suite]]['num_failed'] = self.num_failed
          self.results['suites'][self.suite_map[self.suite]]['num_skipped'] = self.num_skipped
          self.results['suites'][self.suite_map[self.suite]]['num_expected_failures'] = self.num_expected_failures
          self.results['suites'][self.suite_map[self.suite]]['cases'][self.current_case_number]['execution_time']= format(self.execution_time, '.%sf' %CustomTextTestResult._execution_time_significant_digits)
          self.results['num_passed'] += self.num_passed
          self.results['num_failed'] += self.num_failed
          self.results['num_skipped'] += self.num_skipped
          self.results['num_expected_failures'] += self.num_expected_failures
          self.case_number += 1
  
      def print_error_string(self, err):
          error_string = ''.join(traceback.format_exception(err[0], err[1], err[2]))
          if self.show_errors:
              self.stream.writeln(self.separator_failure)
              self.stream.write(error_string)
          return error_string
  
      def addScreenshots(self, test):
          for root, dirs, files in os.walk(self.result_screenshots_dir):
              for file in files:
                  self.results['suites'][self.suite_map[self.suite]]['cases'][self.current_case_number]['screenshots'].append(os.path.join(root, file))
  
      def addSuccess(self, test):
          super(CustomTextTestResult, self).addSuccess(test)
          if self.show_test_info:
              self.stream.writeln(self.separator_pre_result)
              self.stream.writeln("PASS")
          self.stream.flush()
          self.results['suites'][self.suite_map[self.suite]]['cases'][self.current_case_number]['result'] = 'passed'
          self.num_passed += 1
          self.addScreenshots(test)
  
      def addError(self, test, err):
          error_string = self.print_error_string(err)
          super(CustomTextTestResult, self).addError(test, err)
          if self.show_test_info:
              self.stream.writeln(self.separator_pre_result)
              self.stream.writeln("ERROR")
          self.stream.flush()
          self.results['suites'][self.suite_map[self.suite]]['cases'][self.current_case_number]['result'] = 'error'
          self.results['suites'][self.suite_map[self.suite]]['cases'][self.current_case_number]['errors'] = error_string
          self.num_failed += 1
          self.addScreenshots(test)
  
      def addFailure(self, test, err):
          error_string = self.print_error_string(err)
          super(CustomTextTestResult, self).addFailure(test, err)
          if self.show_test_info:
              self.stream.writeln(self.separator_pre_result)
              self.stream.writeln("FAIL")
          self.stream.flush()
          self.results['suites'][self.suite_map[self.suite]]['cases'][self.current_case_number]['result'] = 'failed'
          self.results['suites'][self.suite_map[self.suite]]['cases'][self.current_case_number]['failures'] = error_string
          self.num_failed += 1
          self.addScreenshots(test)
  
      def addSkip(self, test, reason):
          super(CustomTextTestResult, self).addSkip(test, reason)
          if self.show_test_info:
              self.stream.writeln(self.separator_pre_result)
              self.stream.writeln("SKIPPED {0!r}".format(reason))
          self.stream.flush()
          self.results['suites'][self.suite_map[self.suite]]['cases'][self.current_case_number]['result'] = 'skipped'
          self.results['suites'][self.suite_map[self.suite]]['cases'][self.current_case_number]['note'] = reason
          self.num_skipped += 1
  
      def addExpectedFailure(self, test, err):
          super(CustomTextTestResult, self).addExpectedFailure(test, err)
          if self.show_test_info:
              self.stream.writeln(self.separator_pre_result)
              self.stream.writeln("EXPECTED FAILURE")
          self.stream.flush()
          self.results['suites'][self.suite_map[self.suite]]['cases'][self.current_case_number]['result'] = 'expected_failure'
          self.num_expected_failures += 1
          self.addScreenshots(test)
  
      def addUnexpectedSuccess(self, test):
          super(CustomTextTestResult, self).addUnexpectedSuccess(test)
          if self.show_test_info:
              self.stream.writeln(self.separator_pre_result)
              self.stream.writeln("UNEXPECTED SUCCESS")
          self.stream.flush()
          self.num_failed += 1
          self.addScreenshots(test)
  
      def printOverallSuiteResults(self, r):
          self.stream.writeln()
          self.stream.writeln(self.separator4)
          self.stream.writeln('OVERALL SUITE RESULTS')
          fmt = [
              ('SUITE',       'suite',        50, 'left'),
              ('CASES',       'cases',        15, 'right'),
              ('PASSED',      'passed',       15, 'right'),
              ('FAILED',      'failed',       15, 'right'),
              ('SKIPPED',     'skipped',      15, 'right'),
              ('%',           'percentage',   20, 'right'),
              ('TIME (s)',    'time',         20, 'right')
          ]
          data = []
          for x in r: data.append({'suite': r[x]['name'],
                                     'cases': r[x]['num_passed'] + r[x]['num_failed'],
                                     'passed': r[x]['num_passed'],
                                     'failed': r[x]['num_failed'],
                                     'skipped': r[x]['num_skipped'],
                                     'expected_failures': r[x]['num_expected_failures'],
                                     'percentage': float(r[x]['num_passed'])/(r[x]['num_passed'] + r[x]['num_failed']) * 100 if (r[x]['num_passed'] + r[x]['num_failed']) > 0 else 0,
                                     'time': r[x]['execution_time']})
          total_suites_passed = len([x for x in data if not x['failed']])
          total_suites_passed_percentage = format(float(total_suites_passed)/len(data) * 100, '.%sf' %CustomTextTestResult._pass_percentage_significant_digits)
          totals = {'suite': 'TOTALS %s/%s (%s%%) suites passed' %(total_suites_passed, len(data), total_suites_passed_percentage),
                    'cases': sum_dict_key(data, 'cases'),
                    'passed': sum_dict_key(data, 'passed'),
                    'failed': sum_dict_key(data, 'failed'),
                    'skipped': sum_dict_key(data, 'skipped'),
                    'percentage': sum_dict_key(data, 'percentage')/len(data),
                    'time': sum_dict_key(data, 'time', 'float')}
          for x in data: operator.setitem(x, 'percentage', format(x['percentage'], '.%sf' %CustomTextTestResult._pass_percentage_significant_digits))
          totals['percentage'] = format(totals['percentage'], '.%sf' %CustomTextTestResult._pass_percentage_significant_digits)
          self.stream.writeln( TablePrinter(fmt, tl=self.separator1, ul=self.separator2, bl=self.separator3)(data, totals) )
          self.stream.writeln()
  
      def printIndividualSuiteResults(self, r):
          self.stream.writeln()
          self.stream.writeln(self.separator4)
          self.stream.writeln('INDIVIDUAL SUITE RESULTS')
          fmt = [
              ('CASE',        'case',         50, 'left'),
              ('DESCRIPTION', 'description',  50, 'right'),
              ('RESULT',      'result',       25, 'right'),
              ('TIME (s)',    'time',         25, 'right')
          ]
          for suite in r:
              self.stream.writeln(self.separator1)
              self.stream.write('{0: <50}'.format('SUITE: %s' %r[suite]['name']))
              self.stream.writeln('{0: <100}'.format('DESCRIPTION: %s' %(r[suite]['description'] if not r[suite]['description'] or len(r[suite]['description']) <= (100 - len('DESCRIPTION: '))
                                                                         else '%s...' %r[suite]['description'][:(97 - len('DESCRIPTION: '))])))
              data = []
              cases = r[suite]['cases']
              for x in cases: data.append({'case': cases[x]['name'],
                                         'description': cases[x]['description'],
                                         'result': cases[x]['result'].upper() if cases[x]['result'] else cases[x]['result'],
                                         'time': cases[x]['execution_time']})
              self.stream.writeln( TablePrinter(fmt, tl=self.separator1, ul=self.separator2)(data) )
          self.stream.writeln(self.separator3)
          self.stream.writeln()
  
      def printErrorsOverview(self, r):
          self.stream.writeln()
          self.stream.writeln(self.separator4)
          self.stream.writeln('FAILURES AND ERRORS OVERVIEW')
          fmt = [
              ('SUITE',       'suite',         50, 'left'),
              ('CASE',        'case',          50, 'left'),
              ('RESULT',      'result',        50, 'right')
          ]
          data = []
          for suite in r:
              cases = {k:v for (k,v) in list(r[suite]['cases'].items()) if v['failures'] or v['errors']}
              for x in cases: data.append({'suite': '%s%s' %(r[suite]['name'], ' (%s)' %r[suite]['module'] if r[suite]['class'] != r[suite]['name'] else ''),
                                         'case': '%s%s' %(cases[x]['name'], ' (%s)' %cases[x]['method'] if cases[x]['name'] != cases[x]['method'] else ''),
                                         'result': cases[x]['result'].upper()})
          self.stream.writeln( TablePrinter(fmt, tl=self.separator1, ul=self.separator2)(data) )
          self.stream.writeln(self.separator3)
          self.stream.writeln()
  
      def printErrorsDetail(self, r):
          self.stream.writeln()
          self.stream.writeln(self.separator4)
          self.stream.writeln('FAILURES AND ERRORS DETAIL')
          for suite in r:
              failures_and_errors = [k for (k,v) in list(r[suite]['cases'].items()) if v['failures'] or v['errors']]
              #print failures_and_errors
              suite_str = '%s%s' %(r[suite]['name'], ' (%s)' %r[suite]['module'] if r[suite]['class'] != r[suite]['name'] else '')
              for case in failures_and_errors:
                  case_ref = r[suite]['cases'][case]
                  case_str = '%s%s' %(case_ref['name'], ' (%s)' %case_ref['method'] if case_ref['name'] != case_ref['method'] else '')
                  errors = case_ref['errors']
                  failures = case_ref['failures']
                  self.stream.writeln(self.separator1)
                  if errors:
                      self.stream.writeln('ERROR: %s [%s]' %(case_str, suite_str))
                      self.stream.writeln(self.separator2)
                      self.stream.writeln(errors)
                  if failures:
                      self.stream.writeln('FAILURE: %s [%s]' %(case_str, suite_str))
                      self.stream.writeln(self.separator2)
                      self.stream.writeln(failures)
          self.stream.writeln(self.separator3)
          self.stream.writeln()
  
      def printSkippedDetail(self, r):
          self.stream.writeln()
          self.stream.writeln(self.separator4)
          self.stream.writeln('SKIPPED DETAIL')
          fmt = [
              ('SUITE',       'suite',         50, 'left'),
              ('CASE',        'case',          50, 'left'),
              ('REASON',      'reason',        50, 'right')
          ]
          data = []
          for suite in r:
              cases = {k:v for (k,v) in list(r[suite]['cases'].items()) if v['result'] == 'skipped'}
              for x in cases: data.append({'suite': '%s%s' %(r[suite]['name'], ' (%s)' %r[suite]['module'] if r[suite]['class'] != r[suite]['name'] else ''),
                                         'case': '%s%s' %(cases[x]['name'], ' (%s)' %cases[x]['method'] if cases[x]['name'] != cases[x]['method'] else ''),
                                         'reason': cases[x]['note']})
          self.stream.writeln( TablePrinter(fmt, tl=self.separator1, ul=self.separator2)(data) )
          self.stream.writeln(self.separator3)
          self.stream.writeln()
  
      def returnCode(self):
          return not self.wasSuccessful()
  
  class CustomTextTestRunner(unittest.TextTestRunner):
      """A test runner class that displays results in textual form.
      It prints out the names of tests as they are run, errors as they
      occur, and a summary of the results at the end of the test run.
      """
  
      def __init__(self,
                   stream=sys.stderr,
                   descriptions=True,
                   verbosity=1,
                   failfast=False,
                   buffer=False,
                   resultclass=CustomTextTestResult,
                   results_file_path="results.json",
                   result_screenshots_dir='',
                   show_previous_results=False,
                   test_name=None,
                   test_description=None,
                   config=None,
                   test_types=None):
          store_class_fields(self, locals())
          self.stream = _WritelnDecorator(stream)
  
      def _makeResult(self):
          return self.resultclass(self.stream, self.descriptions, self.verbosity,
                                  self.results_file_path, self.result_screenshots_dir, self.show_previous_results,
                                  self.config, self.test_types)
  
      def run(self, test):
          output = ""
          "Run the given test case or test suite."
          result = self._makeResult()
          registerResult(result)
          result.failfast = self.failfast
          result.buffer = self.buffer
          startTime = time.time()
          startTestRun = getattr(result, 'startTestRun', None)
          if startTestRun is not None:
              startTestRun()
          try:
              test(result)
          finally:
              stopTestRun = getattr(result, 'stopTestRun', None)
              if stopTestRun is not None:
                  stopTestRun()
          stopTime = time.time()
          timeTaken = stopTime - startTime
          # filter results to output
          if result.show_previous_results:
              r = result.results['suites']
          else:
              r = {k:v for (k,v) in list(result.results['suites'].items()) if k not in result.previous_suite_runs}
          # print results based on verbosity
          if result.show_all:
              result.printSkippedDetail(r)
          if result.show_errors_detail:
              result.printErrorsDetail(r)
          if result.show_individual_suite_results:
              result.printIndividualSuiteResults(r)
          if result.show_errors:
              result.printErrorsOverview(r)
          if result.show_overall_results:
              result.printOverallSuiteResults(r)
          run = result.testsRun
          self.stream.writeln("Ran %d test case%s in %.4fs" %
                              (run, run != 1 and "s" or "", timeTaken))
          self.stream.writeln()
  
          expectedFails = unexpectedSuccesses = skipped = 0
          try:
              results = map(len, (result.expectedFailures,
                                  result.unexpectedSuccesses,
                                  result.skipped))
          except AttributeError:
              pass
          else:
              expectedFails, unexpectedSuccesses, skipped = results
  
          infos = []
          if not result.wasSuccessful():
              self.stream.write("FAILED")
              failed, errored = map(len, (result.failures, result.errors))
              if failed:
                  infos.append("failures=%d" % failed)
              if errored:
                  infos.append("errors=%d" % errored)
          else:
              self.stream.write("OK")
          if skipped:
              infos.append("skipped=%d" % skipped)
          if expectedFails:
              infos.append("expected failures=%d" % expectedFails)
          if unexpectedSuccesses:
              infos.append("unexpected successes=%d" % unexpectedSuccesses)
          if infos:
              self.stream.writeln(" (%s)" % (", ".join(infos),))
          else:
              self.stream.write("\n")
          return result
  