190 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			190 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Python
		
	
	
	
"""
 | 
						|
   xmlresult plugin for machine-readable logging of test results. 
 | 
						|
   Useful for cruisecontrol integration code.
 | 
						|
   
 | 
						|
   An adaptation of pytest_resultlog.py
 | 
						|
"""
 | 
						|
 | 
						|
import time
 | 
						|
 | 
						|
def pytest_addoption(parser):
 | 
						|
    group = parser.addgroup("xmlresult", "xmlresult plugin options")
 | 
						|
    group.addoption('--xmlresult', action="store", dest="xmlresult", metavar="path", default=None,
 | 
						|
           help="path for machine-readable xml result log.")
 | 
						|
 | 
						|
def pytest_configure(config):
 | 
						|
    xmlresult = config.option.xmlresult
 | 
						|
    if xmlresult:
 | 
						|
        logfile = open(xmlresult, 'w', 1) # line buffered
 | 
						|
        config._xmlresult = XMLResult(logfile) 
 | 
						|
        config.pluginmanager.register(config._xmlresult)
 | 
						|
 | 
						|
def pytest_unconfigure(config):
 | 
						|
    xmlresult = getattr(config, '_xmlresult', None)
 | 
						|
    if xmlresult:
 | 
						|
        xmlresult.logfile.close()
 | 
						|
        del config._xmlresult 
 | 
						|
        config.pluginmanager.unregister(xmlresult)
 | 
						|
 | 
						|
def generic_path(item):
 | 
						|
    chain = item.listchain()
 | 
						|
    gpath = [chain[0].name]
 | 
						|
    fspath = chain[0].fspath
 | 
						|
    fspart = False
 | 
						|
    for node in chain[1:]:
 | 
						|
        newfspath = node.fspath
 | 
						|
        if newfspath == fspath:
 | 
						|
            if fspart:
 | 
						|
                gpath.append(':')
 | 
						|
                fspart = False
 | 
						|
            else:
 | 
						|
                gpath.append('.')
 | 
						|
        else:
 | 
						|
            gpath.append('/')
 | 
						|
            fspart = True
 | 
						|
        name = node.name
 | 
						|
        if name[0] in '([':
 | 
						|
            gpath.pop()
 | 
						|
        gpath.append(name)
 | 
						|
        fspath = newfspath
 | 
						|
    return ''.join(gpath)
 | 
						|
        
 | 
						|
class XMLResult(object):
 | 
						|
    test_start_time = 0.0
 | 
						|
    test_taken_time = 0.0
 | 
						|
    test_count = 0
 | 
						|
    error_count = 0
 | 
						|
    failure_count = 0
 | 
						|
    skip_count = 0
 | 
						|
    
 | 
						|
    def __init__(self, logfile):
 | 
						|
        self.logfile = logfile
 | 
						|
        self.test_logs = []
 | 
						|
    
 | 
						|
    def write_log_entry(self, testpath, shortrepr, longrepr):
 | 
						|
        self.test_count += 1
 | 
						|
        # Create an xml log entry for the tests
 | 
						|
        self.test_logs.append('<testcase test_method="%s" name="%s" time="%.3f">' % (testpath.split(':')[-1], testpath, self.test_taken_time))
 | 
						|
        
 | 
						|
        # Do we have any other data to capture for Errors, Fails and Skips
 | 
						|
        if shortrepr in ['E', 'F', 'S']:
 | 
						|
            
 | 
						|
            if shortrepr == 'E':
 | 
						|
                self.error_count += 1
 | 
						|
            elif shortrepr == 'F':
 | 
						|
                self.failure_count += 1
 | 
						|
            elif shortrepr == 'S':
 | 
						|
                self.skip_count += 1
 | 
						|
            
 | 
						|
            tag_map = {'E': 'error', 'F': 'failure', 'S': 'skipped'}
 | 
						|
            self.test_logs.append("<%s>" % tag_map[shortrepr])
 | 
						|
            
 | 
						|
            # Output any more information
 | 
						|
            for line in longrepr.splitlines():
 | 
						|
                self.test_logs.append("<![CDATA[%s\n]]>" % line)
 | 
						|
            self.test_logs.append("</%s>" % tag_map[shortrepr])
 | 
						|
        self.test_logs.append("</testcase>")
 | 
						|
 | 
						|
    def log_outcome(self, node, shortrepr, longrepr):
 | 
						|
        self.write_log_entry(node.name, shortrepr, longrepr)
 | 
						|
 | 
						|
    def pytest_runtest_logreport(self, report):
 | 
						|
        code = report.shortrepr 
 | 
						|
        if report.passed:
 | 
						|
            longrepr = ""
 | 
						|
            code = "."
 | 
						|
        elif report.failed:
 | 
						|
            longrepr = str(report.longrepr)
 | 
						|
            code = "F"
 | 
						|
        elif report.skipped:
 | 
						|
            code = "S"
 | 
						|
            longrepr = str(report.longrepr.reprcrash.message)
 | 
						|
        self.log_outcome(report.item, code, longrepr)
 | 
						|
        
 | 
						|
    def pytest_runtest_setup(self, item):
 | 
						|
        self.test_start_time = time.time()
 | 
						|
    
 | 
						|
    def pytest_runtest_teardown(self, item):
 | 
						|
        self.test_taken_time = time.time() - self.test_start_time
 | 
						|
        
 | 
						|
    def pytest_collectreport(self, report):
 | 
						|
        if not report.passed:
 | 
						|
            if report.failed:
 | 
						|
                code = "F"
 | 
						|
            else:
 | 
						|
                assert report.skipped
 | 
						|
                code = "S"
 | 
						|
            longrepr = str(report.longrepr.reprcrash)
 | 
						|
            self.log_outcome(report.collector, code, longrepr)
 | 
						|
 | 
						|
    def pytest_internalerror(self, excrepr):
 | 
						|
        path = excrepr.reprcrash.path
 | 
						|
        self.errors += 1
 | 
						|
        self.write_log_entry(path, '!', str(excrepr))
 | 
						|
 | 
						|
    def pytest_sessionstart(self, session):
 | 
						|
        self.suite_start_time = time.time()
 | 
						|
 | 
						|
    def pytest_sessionfinish(self, session, exitstatus):
 | 
						|
        """
 | 
						|
        Write the xml output
 | 
						|
        """
 | 
						|
        suite_stop_time = time.time()
 | 
						|
        suite_time_delta = suite_stop_time - self.suite_start_time
 | 
						|
        self.logfile.write('<testsuite ')
 | 
						|
        self.logfile.write('errors="%i" ' % self.error_count)
 | 
						|
        self.logfile.write('failures="%i" ' % self.failure_count)
 | 
						|
        self.logfile.write('skips="%i" ' % self.skip_count)
 | 
						|
        self.logfile.write('name="" ')
 | 
						|
        self.logfile.write('tests="%i" ' % self.test_count)
 | 
						|
        self.logfile.write('time="%.3f"' % suite_time_delta)
 | 
						|
        self.logfile.write(' >')
 | 
						|
        self.logfile.writelines(self.test_logs)
 | 
						|
        self.logfile.write('</testsuite>')
 | 
						|
        self.logfile.close()
 | 
						|
 | 
						|
 | 
						|
# Tests
 | 
						|
def test_generic(testdir, LineMatcher):
 | 
						|
    testdir.plugins.append("resultlog")
 | 
						|
    testdir.makepyfile("""
 | 
						|
        import py
 | 
						|
        def test_pass():
 | 
						|
            pass
 | 
						|
        def test_fail():
 | 
						|
            assert 0
 | 
						|
        def test_skip():
 | 
						|
            py.test.skip("")
 | 
						|
    """)
 | 
						|
    testdir.runpytest("--xmlresult=result.xml")
 | 
						|
    lines = testdir.tmpdir.join("result.xml").readlines(cr=0)
 | 
						|
    LineMatcher(lines).fnmatch_lines([
 | 
						|
        '*testsuite errors="0" failures="1" skips="1" name="" tests="3"*'
 | 
						|
    ])
 | 
						|
    LineMatcher(lines).fnmatch_lines([
 | 
						|
        '*<failure><![CDATA[def test_fail():*'
 | 
						|
    ])
 | 
						|
    LineMatcher(lines).fnmatch_lines([
 | 
						|
        '*<skipped><![CDATA[Skipped: <Skipped instance>*'
 | 
						|
    ])
 | 
						|
 | 
						|
def test_generic_path():
 | 
						|
    from py.__.test.collect import Node, Item, FSCollector
 | 
						|
    p1 = Node('a')
 | 
						|
    assert p1.fspath is None
 | 
						|
    p2 = Node('B', parent=p1)
 | 
						|
    p3 = Node('()', parent = p2)
 | 
						|
    item = Item('c', parent = p3)
 | 
						|
    res = generic_path(item)
 | 
						|
    assert res == 'a.B().c'
 | 
						|
 | 
						|
    p0 = FSCollector('proj/test')
 | 
						|
    p1 = FSCollector('proj/test/a', parent=p0)
 | 
						|
    p2 = Node('B', parent=p1)
 | 
						|
    p3 = Node('()', parent = p2)
 | 
						|
    p4 = Node('c', parent=p3)
 | 
						|
    item = Item('[1]', parent = p4)
 | 
						|
 | 
						|
    res = generic_path(item)
 | 
						|
    assert res == 'test/a:B().c[1]'
 |