from xml.etree import ElementTree
import unittest


class TestResult(unittest.TestResult):
    def _exc_info_to_string(self, err, test):
        return '%s: %s' % err


class TestCase(unittest.TestCase):
    TR_CLASS = TestResult

    def __init__(self, classname, methodname):
        super(TestCase, self).__init__()
        self.classname = classname
        self.methodname = methodname

    def __str__(self):
        return "%s (%s)" % (self.methodname, self.classname)

    def __repr__(self):
        return "<%s testMethod=%s>" % \
               (self.classname, self.methodname)

    def __call__(self, *args, **kwds):
        return self.run(*args, **kwds)

    def id(self):
        return "%s.%s" % (self.classname, self.methodname)

    def seed(self, result, typename=None, message=None):
        """ Provide the expected result """
        self._seed = (result, typename, message)

    def run(self, tr=None):
        """ Fake run() that produces the seeded result """
        tr = tr or self.TR_CLASS()
        result, typename, message = self._seed

        tr.startTest(self)
        if result == 'success':
            tr.addSuccess(self)
        elif result == 'skipped':
            tr.addSkip(self, '%s: %s' % (typename, message))
        elif result == 'error':
            tr.addError(self, (typename, message))
        elif result == 'failure':
            tr.addFailure(self, (typename, message))
        tr.stopTest(self)

        return tr

    def setUp(self):
        """ Dummy method so __init__ does not fail """
        pass

    def tearDown(self):
        """ Dummy method so __init__ does not fail """
        pass

    def runTest(self):
        """ Dummy method so __init__ does not fail """
        self.run()


class TestSuite(unittest.TestSuite):
    pass


class Parser(object):
    TC_CLASS = TestCase
    TS_CLASS = TestSuite
    TR_CLASS = TestResult

    def parse(self, source):
        ts = self.TS_CLASS()

        xml = ElementTree.parse(source)
        root = xml.getroot()
        assert root.tag == 'testsuite'
        for el in xml.getroot():
            if el.tag == 'testcase':
                if len(el) == 0:
                    tc = self.TC_CLASS(el.attrib['classname'], el.attrib['name'])
                    tc.seed('success')
                    ts.addTest(tc)
                for e in el:
                    if e.tag in ('failure', 'error', 'skipped'):
                        result = e.tag
                        typename = e.attrib.get('type')
                        message = e.attrib.get('message')
                        tc = self.TC_CLASS(el.attrib['classname'], el.attrib['name'])
                        tc.seed(result, typename, message)
                        ts.addTest(tc)
        tr = ts.run(self.TR_CLASS())

        # check totals if they are in the root XML element
        if 'errors' in root.attrib:
            assert len(tr.errors) == int(root.attrib['errors'])
        if 'failures' in root.attrib:
            assert len(tr.failures) == int(root.attrib['failures'])
        if 'skip' in root.attrib:
            assert len(tr.skipped) == int(root.attrib['skip'])
        if 'tests' in root.attrib:
            assert len(list(ts)) == int(root.attrib['tests'])

        return (ts, tr)


def parse(source):
    return Parser().parse(source)

@@ -859,17 +859,46 @@
 elf.
-_seed
+result, self.typename, self.message
  = 
-(
 resu
@@ -918,17 +918,16 @@
  message
-)
 %0A%0A    de
@@ -1045,55 +1045,8 @@
 SS()
-%0A        result, typename, message = self._seed
 %0A%0A  
@@ -1081,16 +1081,21 @@
      if 
+self.
 result =
@@ -1144,32 +1144,37 @@
 f)%0A        elif 
+self.
 result == 'skipp
@@ -1219,16 +1219,21 @@
  %25s' %25 (
+self.
 typename
@@ -1226,32 +1226,37 @@
 (self.typename, 
+self.
 message))%0A      
@@ -1254,32 +1254,37 @@
 ))%0A        elif 
+self.
 result == 'error
@@ -1309,32 +1309,37 @@
 addError(self, (
+self.
 typename, messag
@@ -1324,32 +1324,37 @@
 (self.typename, 
+self.
 message))%0A      
@@ -1360,16 +1360,21 @@
    elif 
+self.
 result =
@@ -1419,16 +1419,21 @@
 (self, (
+self.
 typename
@@ -1426,32 +1426,37 @@
 (self.typename, 
+self.
 message))%0A      
@@ -1776,16 +1776,555 @@
 .run()%0A%0A
+    @property%0A    def basename(self):%0A        return self.classname.rpartition('.')%5B2%5D%0A%0A    @property%0A    def success(self):%0A        return self.result == 'success'%0A%0A    @property%0A    def skipped(self):%0A        return self.result == 'skipped'%0A%0A    @property%0A    def failed(self):%0A        return self.result == 'failure'%0A%0A    @property%0A    def errored(self):%0A        return self.result == 'error'%0A%0A    @property%0A    def good(self):%0A        return self.skipped or self.success%0A%0A    @property%0A    def bad(self):%0A        return not self.good%0A%0A
 %0Aclass T

Add properties for easier inspection