"""Monkey patch lame-o vanilla unittest with test skip feature.

From the patch that was never applied (shameful!):
http://bugs.python.org/issue1034053
"""

import time
import unittest


class SkipException(Exception):
    pass


def TestResult__init__(self):
    self.failures = []
    self.errors = []
    self.skipped = []
    self.testsRun = 0
    self.shouldStop = 0


unittest.TestResult.__init__ = TestResult__init__


def TestResult_addSkipped(self, test, err):
    """Called when a test is skipped.

    'err' is a tuple of values as returned by sys.exc_info().
    """
    self.skipped.append((test, str(err[1])))


unittest.TestResult.addSkipped = TestResult_addSkipped


def TestResult__repr__(self):
    return "<%s run=%i errors=%i failures=%i skipped=%i>" % (
        unittest._strclass(self.__class__), self.testsRun,
        len(self.errors), len(self.failures), len(self.skipped))


unittest.TestResult.__repr__ = TestResult__repr__


class TestCase(unittest.TestCase):
    # Yuck, all of run has to be copied for this.
    # I don't care about wrapping setUp atm.

    def run(self, result=None):
        if result is None:
            result = self.defaultTestResult()
        result.startTest(self)
        # Support variable naming differences between 2.4 and 2.6
        # Yay for silly variable hiding
        try:
            testMethodName = self.__testMethodName
            exc_info = self.__exc_info
        except AttributeError:
            testMethodName = self._testMethodName
            exc_info = self._exc_info

        testMethod = getattr(self, testMethodName)

        try:
            try:
                self.setUp()
            except KeyboardInterrupt:
                raise
            except Exception:
                result.addError(self, exc_info())
                return

            ok = False
            try:
                testMethod()
                ok = True
            except self.failureException:
                result.addFailure(self, exc_info())
            except SkipException:
                result.addSkipped(self, exc_info())
            except KeyboardInterrupt:
                raise
            except Exception:
                result.addError(self, exc_info())

            try:
                self.tearDown()
            except KeyboardInterrupt:
                raise
            except Exception:
                result.addError(self, exc_info())
                ok = False
            if ok:
                result.addSuccess(self)
        finally:
            result.stopTest(self)

    def skip(self, msg=None):
        """Skip the test, with the given message."""
        raise SkipException(msg)

    def skipIf(self, expr, msg=None):
        """Skip the test if the expression is true."""
        if expr:
            raise SkipException(msg)


def _TextTestResult_addSkipped(self, test, err):
    unittest.TestResult.addSkipped(self, test, err)
    if self.showAll:
        msg = str(err[1])
        if msg:
            msg = " (" + msg + ")"
        self.stream.writeln("SKIPPED" + msg)
    elif self.dots:
        self.stream.write('S')


unittest._TextTestResult.addSkipped = _TextTestResult_addSkipped


# Bah
def TextTestRunner_run(self, test):
    "Run the given test case or test suite."
    result = self._makeResult()
    startTime = time.time()
    test(result)
    stopTime = time.time()
    timeTaken = stopTime - startTime
    result.printErrors()
    self.stream.writeln(result.separator2)
    run = result.testsRun
    self.stream.writeln("Ran %d test%s in %.3fs" %
                        (run, run != 1 and "s" or "", timeTaken))
    self.stream.writeln()
    if not result.wasSuccessful():
        self.stream.write("FAILED (")
        failed, errored, skipped = map(
            len, (result.failures, result.errors, result.skipped))
        if failed:
            self.stream.write("failures=%d" % failed)
        if errored:
            if failed:
                self.stream.write(", ")
            self.stream.write("errors=%d" % errored)
        if skipped:
            self.stream.write(", skipped=%d" % skipped)
        self.stream.writeln(")")
    else:
        if result.skipped:
            self.stream.writeln(
                "OK (skipped=%d)" % len(result.skipped))
        else:
            self.stream.writeln("OK")
    return result


unittest.TextTestRunner.run = TextTestRunner_run
