コード例 #1
0
ファイル: recipe.py プロジェクト: pombredanne/trachacks
 def report_file(self, category=None, file_=None):
     """Read report data from a file and record it.
     
     :param category: the name of the category of the report
     :param file\_: the path to the file containing the report data, relative
                    to the base directory
     """
     filename = self.resolve(file_)
     try:
         fileobj = file(filename, 'r')
         try:
             xml_elem = xmlio.Fragment()
             for child in xmlio.parse(fileobj).children():
                 child_elem = xmlio.Element(
                     child.name,
                     **dict([(name, value)
                             for name, value in child.attr.items()
                             if value is not None]))
                 xml_elem.append(child_elem[[
                     xmlio.Element(grandchild.name)[grandchild.gettext()]
                     for grandchild in child.children()
                 ]])
             self.output.append((Recipe.REPORT, category, None, xml_elem))
         finally:
             fileobj.close()
     except xmlio.ParseError, e:
         self.error('Failed to parse %s report at %s: %s' %
                    (category, filename, e))
コード例 #2
0
    def _create_build(self, url):
        xml = xmlio.Element('slave', name=self.name, version=PROTOCOL_VERSION)[
            xmlio.Element('platform', processor=self.config['processor']
                          )[self.config['machine']],
            xmlio.Element('os',
                          family=self.config['family'],
                          version=self.config['version'])[self.config['os']], ]

        log.debug('Configured packages: %s', self.config.packages)
        for package, properties in self.config.packages.items():
            xml.append(xmlio.Element('package', name=package, **properties))

        body = str(xml)
        log.debug('Sending slave configuration: %s', body)
        resp = self.request(
            'POST', url, body, {
                'Content-Length': str(len(body)),
                'Content-Type': 'application/x-bitten+xml'
            })

        if resp.code == 201:
            self._initiate_build(resp.info().get('location'))
            return True
        elif resp.code == 204:
            log.info('No pending builds')
            return False
        else:
            log.error('Unexpected response (%d %s)', resp.code, resp.msg)
            raise ExitSlave(EX_PROTOCOL)
コード例 #3
0
ファイル: phptools.py プロジェクト: pombredanne/trachacks
 def _process_testsuite(testsuite, results, parent_file=''):
     for testcase in testsuite.children():
         if testcase.name == 'testsuite':
             _process_testsuite(testcase,
                                results,
                                parent_file=testcase.attr.get(
                                    'file', parent_file))
             continue
         test = xmlio.Element('test')
         test.attr['fixture'] = testsuite.attr['name']
         test.attr['name'] = testcase.attr['name']
         test.attr['duration'] = testcase.attr['time']
         result = list(testcase.children())
         if result:
             test.append(xmlio.Element('traceback')[result[0].gettext()])
             test.attr['status'] = result[0].name
         else:
             test.attr['status'] = 'success'
         if 'file' in testsuite.attr or parent_file:
             testfile = os.path.realpath(
                 testsuite.attr.get('file', parent_file))
             if testfile.startswith(ctxt.basedir):
                 testfile = testfile[len(ctxt.basedir) + 1:]
             testfile = testfile.replace(os.sep, '/')
             test.attr['file'] = testfile
         results.append(test)
コード例 #4
0
ファイル: javatools.py プロジェクト: pombredanne/trachacks
def junit(ctxt, file_=None, srcdir=None):
    """Extract test results from a JUnit XML report.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param file\_: path to the JUnit XML test results; may contain globbing
                  wildcards for matching multiple results files
    :param srcdir: name of the directory containing the test sources, used to
                   link test results to the corresponding source files
    """
    assert file_, 'Missing required attribute "file"'
    try:
        total, failed = 0, 0
        results = xmlio.Fragment()
        for path in glob(ctxt.resolve(file_)):
            fileobj = file(path, 'r')
            try:
                for testcase in xmlio.parse(fileobj).children('testcase'):
                    test = xmlio.Element('test')
                    test.attr['fixture'] = testcase.attr['classname']
                    test.attr['name'] = testcase.attr['name']
                    if 'time' in testcase.attr:
                        test.attr['duration'] = testcase.attr['time']
                    if srcdir is not None:
                        cls = testcase.attr['classname'].split('.')
                        test.attr['file'] = posixpath.join(srcdir, *cls) + \
                                            '.java'

                    result = list(testcase.children())
                    if result:
                        test.attr['status'] = result[0].name
                        # Sometimes the traceback isn't prefixed with the
                        # exception type and message, so add it in if needed
                        tracebackprefix = "%s: %s" % (
                            result[0].attr['type'], result[0].attr['message'])
                        if result[0].gettext().startswith(tracebackprefix):
                            test.append(
                                xmlio.Element('traceback')[
                                    result[0].gettext()])
                        else:
                            test.append(
                                xmlio.Element('traceback')["\n".join(
                                    (tracebackprefix, result[0].gettext()))])
                        failed += 1
                    else:
                        test.attr['status'] = 'success'

                    results.append(test)
                    total += 1
            finally:
                fileobj.close()
        if failed:
            ctxt.error('%d of %d test%s failed' %
                       (failed, total, total != 1 and 's' or ''))
        ctxt.report('test', results)
    except IOError, e:
        log.warning('Error opening JUnit results file (%s)', e)
コード例 #5
0
def distutils(ctxt,
              file_='setup.py',
              command='build',
              options=None,
              timeout=None):
    """Execute a ``distutils`` command.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param file\_: name of the file defining the distutils setup
    :param command: the setup command to execute
    :param options: additional options to pass to the command
    :param timeout: the number of seconds before the external process should
                    be aborted (has same constraints as CommandLine)
    """
    if options:
        if isinstance(options, basestring):
            options = shlex.split(options)
    else:
        options = []

    if timeout:
        timeout = int(timeout)

    cmdline = CommandLine(_python_path(ctxt),
                          [ctxt.resolve(file_), command] + options,
                          cwd=ctxt.basedir)
    log_elem = xmlio.Fragment()
    error_logged = False
    for out, err in cmdline.execute(timeout):
        if out is not None:
            log.info(out)
            log_elem.append(xmlio.Element('message', level='info')[out])
        if err is not None:
            level = 'error'
            if err.startswith('warning: '):
                err = err[9:]
                level = 'warning'
                log.warning(err)
            elif err.startswith('error: '):
                ctxt.error(err[7:])
                error_logged = True
            else:
                log.error(err)
            log_elem.append(xmlio.Element('message', level=level)[err])
    ctxt.log(log_elem)

    if not error_logged and cmdline.returncode != 0:
        ctxt.error('distutils failed (%s)' % cmdline.returncode)
コード例 #6
0
def cobertura(ctxt, file_=None):
    """Extract test coverage information from a Cobertura XML report.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param file\_: path to the Cobertura XML output
    """
    assert file_, 'Missing required attribute "file"'

    coverage = xmlio.Fragment()
    doc = xmlio.parse(open(ctxt.resolve(file_)))
    srcdir = [
        s.gettext().strip() for ss in doc.children('sources')
        for s in ss.children('source')
    ][0]

    classes = [
        cls for pkgs in doc.children('packages')
        for pkg in pkgs.children('package') for clss in pkg.children('classes')
        for cls in clss.children('class')
    ]

    counters = {}
    class_names = {}

    for cls in classes:
        filename = cls.attr['filename'].replace(os.sep, '/')
        name = cls.attr['name']
        if not '$' in name:  # ignore internal classes
            class_names[filename] = name
        counter = counters.get(filename)
        if counter is None:
            counter = counters[filename] = _LineCounter()
        lines = [
            l for ls in cls.children('lines') for l in ls.children('line')
        ]
        for line in lines:
            counter[line.attr['number']] = line.attr['hits']

    for filename, name in class_names.iteritems():
        counter = counters[filename]
        module = xmlio.Element('coverage',
                               name=name,
                               file=posixpath.join(srcdir, filename),
                               lines=counter.num_lines,
                               percentage=counter.percentage)
        module.append(xmlio.Element('line_hits')[counter.line_hits])
        coverage.append(module)
    ctxt.report('coverage', coverage)
コード例 #7
0
ファイル: phptools.py プロジェクト: pombredanne/trachacks
    def _process_phpunit_coverage(ctxt, element, coverage):
        for cls in element._node.getElementsByTagName('class'):
            sourcefile = cls.parentNode.getAttribute('name')
            if not os.path.isabs(sourcefile):
                sourcefile = os.path.join(ctxt.basedir, sourcefile)
            if sourcefile.startswith(ctxt.basedir):
                loc, ncloc = 0, 0.0
                for line in cls.parentNode.getElementsByTagName('line'):
                    if str(line.getAttribute('type')) == 'stmt':
                        loc += 1
                        if int(line.getAttribute('count')) == 0:
                            ncloc += 1
                if loc > 0:
                    percentage = 100 - (ncloc / loc * 100)
                else:
                    percentage = 100

                if sourcefile.startswith(ctxt.basedir):
                    sourcefile = sourcefile[len(ctxt.basedir) + 1:]
                class_coverage = xmlio.Element('coverage',
                                               name=cls.getAttribute('name'),
                                               lines=int(loc),
                                               percentage=int(percentage),
                                               file=sourcefile.replace(
                                                   os.sep, '/'))
                coverage.append(class_coverage)
コード例 #8
0
ファイル: monotools.py プロジェクト: kroman0/bitten
def nunit(ctxt, file_=None):
    """Extract test results from a NUnit XML report.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param file\_: path to the NUnit XML test results; may contain globbing
                  wildcards for matching multiple results files
    """
    assert file_, 'Missing required attribute "file"'
    try:
        total, failed = 0, 0
        results = xmlio.Fragment()
        for path in glob(ctxt.resolve(file_)):
            fileobj = file(path, 'r')
            try:
                for suite, testcases in _get_cases(fileobj):
                    for testcase in testcases:
                        test = xmlio.Element('test')
                        test.attr['fixture'] = suite.attr['name']
                        if 'time' in testcase.attr:
                            test.attr['duration'] = testcase.attr['time']
                        if testcase.attr['executed'] == 'True':
                            if testcase.attr['success'] != 'True':
                                test.attr['status'] = 'failure'
                                failure = list(testcase.children('failure'))
                                if failure:
                                    stacktraceNode = list(failure[0].children('stack-trace'))
                                    if stacktraceNode:
                                        test.append(xmlio.Element('traceback')[
                                            stacktraceNode[0].gettext()
                                        ])
                                    failed += 1
                            else:
                                test.attr['status'] = 'success'
                        else:
                            test.attr['status'] = 'ignore'

                        results.append(test)
                        total += 1
            finally:
                fileobj.close()
        if failed:
            ctxt.error('%d of %d test%s failed' % (failed, total,
                       total != 1 and 's' or ''))
        ctxt.report('test', results)
    except IOError, e:
        log.warning('Error opening NUnit results file (%s)', e)
コード例 #9
0
def pylint(ctxt, file_=None):
    """Extract data from a ``pylint`` run written to a file.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param file\_: name of the file containing the Pylint output
    """
    assert file_, 'Missing required attribute "file"'
    msg_re = re.compile(r'^(?P<file>.+):(?P<line>\d+): '
                        r'\[(?P<type>[A-Z]\d*)(?:, (?P<tag>[\w\.]+))?\] '
                        r'(?P<msg>.*)$')
    msg_categories = dict(W='warning', E='error', C='convention', R='refactor')

    problems = xmlio.Fragment()
    try:
        fd = open(ctxt.resolve(file_), 'r')
        try:
            for line in fd:
                match = msg_re.search(line)
                if match:
                    msg_type = match.group('type')
                    category = msg_categories.get(msg_type[0])
                    if len(msg_type) == 1:
                        msg_type = None
                    filename = match.group('file')
                    if os.path.isabs(filename) \
                            and filename.startswith(ctxt.basedir):
                        filename = filename[len(ctxt.basedir) + 1:]
                    filename = filename.replace('\\', '/')
                    lineno = int(match.group('line'))
                    tag = match.group('tag')
                    problems.append(
                        xmlio.Element('problem',
                                      category=category,
                                      type=msg_type,
                                      tag=tag,
                                      line=lineno,
                                      file=filename)[xmlio.Element('msg')[
                                          match.group('msg') or '']])
            ctxt.report('lint', problems)
        finally:
            fd.close()
    except IOError, e:
        log.warning('Error opening pylint results file (%s)', e)
コード例 #10
0
def phpcs(ctxt, file_=None):
    """Extract test results from a PHP Code Sniffer full report."""
    assert file_, 'Missing required attribute "file"'
    msg_categories = dict(WARNING='warning', ERROR='error')

    problems = xmlio.Fragment()
    try:
        fd = open(ctxt.resolve(file_), 'r')
        try:
            filename = None
            for line in fd:
                line = line.strip()
                if (not line or line.startswith("-")
                        or line.startswith("FOUND")):
                    continue
                if line.startswith("FILE:"):
                    filename = line.split(":", 1)[1].strip()
                    if (os.path.isabs(filename)
                            and filename.startswith(ctxt.basedir)):
                        filename = filename[len(ctxt.basedir) + 1:]
                    filename = filename.replace('\\', '/')
                    continue
                parts = line.split("|")
                if len(parts) != 3 or not parts[0]:
                    continue

                lineno = int(parts[0].strip())
                type = msg_categories[parts[1].strip()]
                tag = type
                category = type
                msg = parts[2].strip()
                problems.append(
                    xmlio.Element('problem',
                                  category=category,
                                  type=type,
                                  tag=tag,
                                  line=lineno,
                                  file=filename)[xmlio.Element('msg')[msg]])
            ctxt.report('lint', problems)
        except Exception, e:
            log.warning('Error parsing phpcs report file (%s)', e)
        finally:
            fd.close()
コード例 #11
0
def unittest(ctxt, file_=None):
    """Extract data from a unittest results file in XML format.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param file\_: name of the file containing the test results
    """
    assert file_, 'Missing required attribute "file"'

    try:
        fileobj = file(ctxt.resolve(file_), 'r')
        try:
            total, failed = 0, 0
            results = xmlio.Fragment()
            for child in xmlio.parse(fileobj).children():
                test = xmlio.Element('test')
                for name, value in child.attr.items():
                    if name == 'file':
                        value = os.path.realpath(value)
                        if value.startswith(ctxt.basedir):
                            value = value[len(ctxt.basedir) + 1:]
                            value = value.replace(os.sep, '/')
                        else:
                            continue
                    test.attr[name] = value
                    if name == 'status' and value in ('error', 'failure'):
                        failed += 1
                for grandchild in child.children():
                    test.append(xmlio.Element(grandchild.name)[
                        grandchild.gettext()
                    ])
                results.append(test)
                total += 1
            if failed:
                ctxt.error('%d of %d test%s failed' % (failed, total,
                           total != 1 and 's' or ''))
            ctxt.report('test', results)
        finally:
            fileobj.close()
    except IOError, e:
        log.warning('Error opening unittest results file (%s)', e)
コード例 #12
0
 def _execute_step(self, build_url, recipe, step):
     failed = False
     started = datetime.utcnow()
     xml = xmlio.Element('result', step=step.id, time=started.isoformat())
     try:
         for type, category, generator, output in \
                 step.execute(recipe.ctxt):
             if type == Recipe.ERROR:
                 failed = True
             if type == Recipe.REPORT and self.dump_reports:
                 print output
             xml.append(xmlio.Element(type, category=category,
                                      generator=generator)[
                 output
             ])
     except KeyboardInterrupt:
         log.warning('Build interrupted')
         self._cancel_build(build_url)
     except BuildError, e:
         log.error('Build step %r failed', step.id)
         failed = True
コード例 #13
0
 def collect_log_messages(node):
     for child in node.children():
         if child.name == 'message':
             if child.attr['priority'] == 'debug':
                 continue
             log_elem.append(
                 xmlio.Element('message', level=child.attr['priority'])
                 [child.gettext().replace(ctxt.basedir + os.sep,
                                          '').replace(ctxt.basedir,
                                                      '')])
         else:
             collect_log_messages(child)
コード例 #14
0
 def _execute_step(self, build_url, recipe, step):
     failed = False
     started = int(time.time())
     xml = xmlio.Element('result', step=step.id)
     try:
         for type, category, generator, output in \
                 step.execute(recipe.ctxt):
             if type == Recipe.ERROR:
                 failed = True
             if type == Recipe.REPORT and self.dump_reports:
                 print output
             if type == Recipe.ATTACH:
                 # Attachments are added out-of-band due to major
                 # performance issues with inlined base64 xml content
                 self._attach_file(build_url, recipe, output)
             xml.append(
                 xmlio.Element(type, category=category,
                               generator=generator)[output])
     except KeyboardInterrupt:
         log.warning('Build interrupted')
         self._cancel_build(build_url)
     except BuildError, e:
         log.error('Build step %r failed', step.id)
         failed = True
コード例 #15
0
    def run(self, test):
        """
        Monkey patch improving the way bitten extracts test and fixture names from result of TestCase.__str__. It fixes
        the case when test name contains a dot as in failure in module load (unittest.loader.LoadTestsFailure) (not a test
        really, but should be reported any way.

        Works with Bitten 0.7dev-r1026.
        """
        result = TextTestRunner.run(self, test)
        if not self.xml_stream:
            return result

        root = xmlio.Element('unittest-results')
        for testcase, filename, timetaken, stdout, stderr in result.tests:
            status = 'success'
            tb = None

            if testcase in [e[0] for e in result.errors]:
                status = 'error'
                tb = [e[1] for e in result.errors if e[0] is testcase][0]
            elif testcase in [f[0] for f in result.failures]:
                status = 'failure'
                tb = [f[1] for f in result.failures if f[0] is testcase][0]

            name = str(testcase)
            fixture = None
            description = testcase.shortDescription() or ''
            if description.startswith('doctest of '):
                name = 'doctest'
                fixture = description[11:]
                description = None
            else:
                match = re.match(r'(\S+)\s+\(([\w.]+)\)', name)  # this regexp is changed from '(\w+)\s+\(([\w.]+)\)'
                if match:
                    name = match.group(1)
                    fixture = match.group(2)

            test_elem = xmlio.Element('test', file=filename, name=name,
                                      fixture=fixture, status=status,
                                      duration=timetaken)
            if description:
                test_elem.append(xmlio.Element('description')[description])
            if stdout:
                test_elem.append(xmlio.Element('stdout')[stdout])
            if stderr:
                test_elem.append(xmlio.Element('stdout')[stderr])
            if tb:
                test_elem.append(xmlio.Element('traceback')[tb])
            root.append(test_elem)

        root.write(self.xml_stream, newlines=True)
        return result
コード例 #16
0
ファイル: recipe.py プロジェクト: hefloryd/bitten
 def attach(self, file_=None, description=None, resource=None):
     """Attach a file to the build or build configuration.
     
     :param file\_: the path to the file to attach, relative to
                    base directory.
     :param description: description saved with attachment
     :param resource: which resource to attach the file to,
                either 'build' (default) or 'config'
     """
     # Attachments are not added as inline xml, so only adding
     # the details for later processing.
     if not file_:
         self.error('No attachment file specified.')
         return
     xml_elem = xmlio.Element('file',
                              filename=file_,
                              description=description or '',
                              resource=resource or 'build')
     self.output.append((Recipe.ATTACH, None, None, xml_elem))
コード例 #17
0
ファイル: phptools.py プロジェクト: pombredanne/trachacks
 def _process_phing_coverage(ctxt, element, coverage):
     for cls in element.children('class'):
         statements = float(cls.attr['statementcount'])
         covered = float(cls.attr['statementscovered'])
         if statements:
             percentage = covered / statements * 100
         else:
             percentage = 100
         class_coverage = xmlio.Element('coverage',
                                        name=cls.attr['name'],
                                        lines=int(statements),
                                        percentage=percentage)
         source = list(cls.children())[0]
         if 'sourcefile' in source.attr:
             sourcefile = os.path.realpath(source.attr['sourcefile'])
             if sourcefile.startswith(ctxt.basedir):
                 sourcefile = sourcefile[len(ctxt.basedir) + 1:]
             sourcefile = sourcefile.replace(os.sep, '/')
             class_coverage.attr['file'] = sourcefile
         coverage.append(class_coverage)
コード例 #18
0
ファイル: testrunner.py プロジェクト: pombredanne/trachacks
    def run(self, test):
        result = TextTestRunner.run(self, test)
        if not self.xml_stream:
            return result

        root = xmlio.Element('unittest-results')
        for testcase, filename, timetaken, stdout, stderr in result.tests:
            status = 'success'
            tb = None

            if testcase in [e[0] for e in result.errors]:
                status = 'error'
                tb = [e[1] for e in result.errors if e[0] is testcase][0]
            elif testcase in [f[0] for f in result.failures]:
                status = 'failure'
                tb = [f[1] for f in result.failures if f[0] is testcase][0]

            name = str(testcase)
            fixture = None
            description = testcase.shortDescription() or ''
            if description.startswith('doctest of '):
                name = 'doctest'
                fixture = description[11:]
                description = None
            else:
                match = re.match('(\w+)\s+\(([\w.]+)\)', name)
                if match:
                    name = match.group(1)
                    fixture = match.group(2)

            test_elem = xmlio.Element('test',
                                      file=filename,
                                      name=name,
                                      fixture=fixture,
                                      status=status,
                                      duration=timetaken)
            if description:
                test_elem.append(xmlio.Element('description')[description])
            if stdout:
                test_elem.append(xmlio.Element('stdout')[stdout])
            if stderr:
                test_elem.append(xmlio.Element('stdout')[stderr])
            if tb:
                test_elem.append(xmlio.Element('traceback')[tb])
            root.append(test_elem)

        root.write(self.xml_stream, newlines=True)
        return result
コード例 #19
0
    def handle_file(elem, sourcefile, coverfile=None):
        code_lines = set()
        for lineno, linetype, line in loc.count(sourcefile):
            if linetype == loc.CODE:
                code_lines.add(lineno)
        num_covered = 0
        lines = []

        if coverfile:
            prev_hits = '0'
            for idx, coverline in enumerate(coverfile):
                match = coverage_line_re.search(coverline)
                if match:
                    hits = match.group(1)
                    if hits: # Line covered
                        if hits != '0':
                            num_covered += 1
                        lines.append(hits)
                        prev_hits = hits
                    elif coverline.startswith('>'): # Line not covered
                        lines.append('0')
                        prev_hits = '0'
                    elif idx not in code_lines: # Not a code line
                        lines.append('-')
                        prev_hits = '0'
                    else: # A code line not flagged by trace.py
                        if prev_hits != '0':
                            num_covered += 1
                        lines.append(prev_hits)

            elem.append(xmlio.Element('line_hits')[' '.join(lines)])

        num_lines = not lines and len(code_lines) or \
                len([l for l in lines if l != '-'])
        if num_lines:
            percentage = int(round(num_covered * 100 / num_lines))
        else:
            percentage = 0
        elem.attr['percentage'] = percentage
        elem.attr['lines'] = num_lines
コード例 #20
0
 def handle_file(element, modname):
     if not coverdir:
         return
     fp = ctxt.resolve(
         os.path.join(coverdir,
                      modname.replace(".", "_") + ".py,cover"))
     if not os.path.exists(fp):
         log.info("No line by line coverage available for %s", modname)
         return
     try:
         with open(fp) as f:
             lines = []
             for line in f:
                 if line.startswith(">"):
                     lines.append("1")
                 elif line.startswith("!"):
                     lines.append("0")
                 else:
                     lines.append("-")
             element.append(xmlio.Element('line_hits')[' '.join(lines)])
     except Exception, e:
         log.info("Error while processing line by line coverage: %s", e)
コード例 #21
0
ファイル: recipe.py プロジェクト: pombredanne/trachacks
 def attach(self, file_=None, description=None, resource=None):
     """Attach a file to the build or build configuration.
     
     :param file\_: the path to the file to attach, relative to
                    base directory.
     :param description: description saved with attachment
     :param resource: which resource to attach the file to,
                either 'build' (default) or 'config'
     """
     filename = self.resolve(file_)
     try:
         fileobj = open(filename, 'rb')
         try:
             xml_elem = xmlio.Element('file',
                                      filename=os.path.basename(filename),
                                      description=description,
                                      resource=resource or 'build')
             xml_elem.append(fileobj.read().encode('base64'))
             self.output.append((Recipe.ATTACH, None, None, xml_elem))
         finally:
             fileobj.close()
     except IOError, e:
         self.error('Failed to read file %s as attachment' % file_)
コード例 #22
0
def trace(ctxt, summary=None, coverdir=None, include=None, exclude=None):
    """Extract data from a ``trace.py`` run.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param summary: path to the file containing the coverage summary
    :param coverdir: name of the directory containing the per-module coverage
                     details
    :param include: patterns of files or directories to include in the report
    :param exclude: patterns of files or directories to exclude from the report
    """
    assert summary, 'Missing required attribute "summary"'
    assert coverdir, 'Missing required attribute "coverdir"'

    summary_line_re = re.compile(r'^\s*(?P<lines>\d+)\s+(?P<cov>\d+)%\s+'
                                 r'(?P<module>.*?)\s+\((?P<filename>.*?)\)')
    coverage_line_re = re.compile(r'\s*(?:(?P<hits>\d+): )?(?P<line>.*)')

    fileset = FileSet(ctxt.basedir, include, exclude)
    missing_files = []
    for filename in fileset:
        if os.path.splitext(filename)[1] != '.py':
            continue
        missing_files.append(filename)
    covered_modules = set()

    def handle_file(elem, sourcefile, coverfile=None):
        code_lines = set()
        for lineno, linetype, line in loc.count(sourcefile):
            if linetype == loc.CODE:
                code_lines.add(lineno)
        num_covered = 0
        lines = []

        if coverfile:
            prev_hits = '0'
            for idx, coverline in enumerate(coverfile):
                match = coverage_line_re.search(coverline)
                if match:
                    hits = match.group(1)
                    if hits: # Line covered
                        if hits != '0':
                            num_covered += 1
                        lines.append(hits)
                        prev_hits = hits
                    elif coverline.startswith('>'): # Line not covered
                        lines.append('0')
                        prev_hits = '0'
                    elif idx not in code_lines: # Not a code line
                        lines.append('-')
                        prev_hits = '0'
                    else: # A code line not flagged by trace.py
                        if prev_hits != '0':
                            num_covered += 1
                        lines.append(prev_hits)

            elem.append(xmlio.Element('line_hits')[' '.join(lines)])

        num_lines = not lines and len(code_lines) or \
                len([l for l in lines if l != '-'])
        if num_lines:
            percentage = int(round(num_covered * 100 / num_lines))
        else:
            percentage = 0
        elem.attr['percentage'] = percentage
        elem.attr['lines'] = num_lines

    try:
        summary_file = open(ctxt.resolve(summary), 'r')
        try:
            coverage = xmlio.Fragment()
            for summary_line in summary_file:
                match = summary_line_re.search(summary_line)
                if match:
                    modname = match.group(3)
                    filename = match.group(4)
                    if not os.path.isabs(filename):
                        filename = os.path.normpath(os.path.join(ctxt.basedir,
                                                                 filename))
                    else:
                        filename = os.path.realpath(filename)
                    if not filename.startswith(ctxt.basedir):
                        continue
                    filename = filename[len(ctxt.basedir) + 1:]
                    if not filename in fileset:
                        continue

                    missing_files.remove(filename)
                    covered_modules.add(modname)
                    module = xmlio.Element('coverage', name=modname,
                                           file=filename.replace(os.sep, '/'))
                    sourcefile = file(ctxt.resolve(filename))
                    try:
                        coverpath = ctxt.resolve(coverdir, modname + '.cover')
                        if os.path.isfile(coverpath):
                            coverfile = file(coverpath, 'r')
                        else:
                            log.warning('No coverage file for module %s at %s',
                                        modname, coverpath)
                            coverfile = None
                        try:
                            handle_file(module, sourcefile, coverfile)
                        finally:
                            if coverfile:
                                coverfile.close()
                    finally:
                        sourcefile.close()
                    coverage.append(module)

            for filename in missing_files:
                modname = os.path.splitext(filename.replace(os.sep, '.'))[0]
                if modname in covered_modules:
                    continue
                covered_modules.add(modname)
                module = xmlio.Element('coverage', name=modname,
                                       file=filename.replace(os.sep, '/'),
                                       percentage=0)
                filepath = ctxt.resolve(filename)
                fileobj = file(filepath, 'r')
                try:
                    handle_file(module, fileobj)
                finally:
                    fileobj.close()
                coverage.append(module)

            ctxt.report('coverage', coverage)
        finally:
            summary_file.close()
    except IOError, e:
        log.warning('Error opening coverage summary file (%s)', e)
コード例 #23
0
def coverage(ctxt, summary=None, coverdir=None, include=None, exclude=None):
    """Extract data from a ``coverage.py`` run.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param summary: path to the file containing the coverage summary
    :param coverdir: name of the directory containing the per-module coverage
                     details
    :param include: patterns of files or directories to include in the report
    :param exclude: patterns of files or directories to exclude from the report
    """
    assert summary, 'Missing required attribute "summary"'

    summary_line_re = re.compile(r'^(?P<module>.*?)\s+(?P<stmts>\d+)\s+'
                                 r'(?P<exec>\d+)\s+(?P<cov>\d+)%\s+'
                                 r'(?:(?P<missing>(?:\d+(?:-\d+)?(?:, )?)*)\s+)?'
                                 r'(?P<file>.+)$')

    fileset = FileSet(ctxt.basedir, include, exclude)
    missing_files = []
    for filename in fileset:
        if os.path.splitext(filename)[1] != '.py':
            continue
        missing_files.append(filename)
    covered_modules = set()

    try:
        summary_file = open(ctxt.resolve(summary), 'r')
        try:
            coverage = xmlio.Fragment()
            for summary_line in summary_file:
                match = summary_line_re.search(summary_line)
                if match:
                    modname = match.group(1)
                    filename = match.group(6)
                    if not os.path.isabs(filename):
                        filename = os.path.normpath(os.path.join(ctxt.basedir,
                                                                 filename))
                    else:
                        filename = os.path.realpath(filename)
                    if not filename.startswith(ctxt.basedir):
                        continue
                    filename = filename[len(ctxt.basedir) + 1:]
                    if not filename in fileset:
                        continue

                    percentage = int(match.group(4).rstrip('%'))
                    num_lines = int(match.group(2))

                    missing_files.remove(filename)
                    covered_modules.add(modname)
                    module = xmlio.Element('coverage', name=modname,
                                           file=filename.replace(os.sep, '/'),
                                           percentage=percentage,
                                           lines=num_lines)
                    coverage.append(module)

            for filename in missing_files:
                modname = os.path.splitext(filename.replace(os.sep, '.'))[0]
                if modname in covered_modules:
                    continue
                covered_modules.add(modname)
                module = xmlio.Element('coverage', name=modname,
                                       file=filename.replace(os.sep, '/'),
                                       percentage=0)
                coverage.append(module)

            ctxt.report('coverage', coverage)
        finally:
            summary_file.close()
    except IOError, e:
        log.warning('Error opening coverage summary file (%s)', e)
コード例 #24
0
        modname = base.replace(os.path.sep, '.')
        realfilename = ctxt.resolve(filename)
        interesting_lines = get_lines(open(realfilename))
        covered_lines = coverage_data.get(realfilename, set())
        percentage = int(round(len(covered_lines) * 100 / len(interesting_lines)))
        line_hits = []
        for lineno in xrange(1, max(interesting_lines)+1):
            if lineno not in interesting_lines:
                line_hits.append('-')
            elif lineno in covered_lines:
                line_hits.append('1')
            else:
                line_hits.append('0')
        module = xmlio.Element('coverage', name=modname,
                               file=filename.replace(os.sep, '/'),
                               percentage=percentage,
                               lines=len(interesting_lines),
                               line_hits=' '.join(line_hits))
        coverage.append(module)
    ctxt.report('coverage', coverage)

def _normalize_filenames(ctxt, filenames, fileset):
    for filename in filenames:
        if not os.path.isabs(filename):
            filename = os.path.normpath(os.path.join(ctxt.basedir,
                                                     filename))
        else:
            filename = os.path.realpath(filename)
        if not filename.startswith(ctxt.basedir):
            continue
        filename = filename[len(ctxt.basedir) + 1:]
コード例 #25
0
def junit(ctxt, file_=None, srcdir=None):
    """Extract test results from a JUnit XML report.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param file\_: path to the JUnit XML test results; may contain globbing
                  wildcards for matching multiple results files
    :param srcdir: name of the directory containing the test sources, used to
                   link test results to the corresponding source files
    """
    assert file_, 'Missing required attribute "file"'
    try:
        total, failed = 0, 0
        results = xmlio.Fragment()
        for path in glob(ctxt.resolve(file_)):
            fileobj = file(path, 'r')
            try:
                output = xmlio.parse(fileobj)
            finally:
                fileobj.close()
            if output.name == 'testsuites':
                # top level wrapper for testsuites
                testcases = []
                for t_suite in output.children('testsuite'):
                    testcases.extend(
                        [t_case for t_case in t_suite.children('testcase')])
            else:
                testcases = [t_case for t_case in output.children('testcase')]
            for testcase in testcases:
                test = xmlio.Element('test')
                test.attr['fixture'] = testcase.attr['classname']
                test.attr['name'] = testcase.attr['name']
                if 'time' in testcase.attr:
                    test.attr['duration'] = testcase.attr['time']
                if srcdir is not None:
                    cls = testcase.attr['classname'].split('.')
                    test.attr['file'] = posixpath.join(srcdir, *cls) + \
                                        '.java'

                result = list(testcase.children())
                if result:
                    junit_status = result[0].name
                    test.append(
                        xmlio.Element('traceback')[_fix_traceback(result)])
                    if junit_status == 'skipped':
                        test.attr['status'] = 'ignore'
                    elif junit_status == 'error':
                        test.attr['status'] = 'error'
                        failed += 1
                    else:
                        test.attr['status'] = 'failure'
                        failed += 1
                else:
                    test.attr['status'] = 'success'

                results.append(test)
                total += 1
        if failed:
            ctxt.error('%d of %d test%s failed' %
                       (failed, total, total != 1 and 's' or ''))
        ctxt.report('test', results)
    except IOError, e:
        log.warning('Error opening JUnit results file (%s)', e)
コード例 #26
0
 def info (msg):
     log.info (msg)
     log_elem.append (xmlio.Element ('message', level='info')[msg])
コード例 #27
0
ファイル: xmlio.py プロジェクト: pombredanne/trachacks
 def test_Element_encoding(self):
     self.assertEquals(
         '<\xc3\xb8\xc3\xbc arg="\xc3\xa9\xe2\x82\xac"/>',
         str(xmlio.Element(u'\xf8\xfc', arg=u'\xe9\u20ac'.encode('utf-8'))))
コード例 #28
0
 def warning (msg):
     log.warning (msg)
     log_elem.append (xmlio.Element ('message', level='warning')[msg])
コード例 #29
0
 def error (msg):
     log.error (msg)
     log_elem.append (xmlio.Element ('message', level='error')[msg])
コード例 #30
0
def gcov(ctxt, include=None, exclude=None, prefix=None, root=""):
    """Run ``gcov`` to extract coverage data where available.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param include: patterns of files and directories to include
    :param exclude: patterns of files and directories that should be excluded
    :param prefix: optional prefix name that is added to object files by the
                   build system
    :param root: optional root path in which the build system puts the object
                 files
    """
    file_re = re.compile(r'^File (?:\'|\`)(?P<file>[^\']+)\'\s*$')
    lines_re = re.compile(r'^Lines executed:(?P<cov>\d+\.\d+)\% of (?P<num>\d+)\s*$')

    files = []
    for filename in FileSet(ctxt.basedir, include, exclude):
        if os.path.splitext(filename)[1] in ('.c', '.cpp', '.cc', '.cxx'):
            files.append(filename)

    coverage = xmlio.Fragment()
    log_elem = xmlio.Fragment()
    def info (msg):
        log.info (msg)
        log_elem.append (xmlio.Element ('message', level='info')[msg])
    def warning (msg):
        log.warning (msg)
        log_elem.append (xmlio.Element ('message', level='warning')[msg])
    def error (msg):
        log.error (msg)
        log_elem.append (xmlio.Element ('message', level='error')[msg])

    for srcfile in files:
        # Determine the coverage for each source file by looking for a .gcno
        # and .gcda pair
        info ("Getting coverage info for %s" % srcfile)
        filepath, filename = os.path.split(srcfile)
        stem = os.path.splitext(filename)[0]
        if prefix is not None:
            stem = prefix + '-' + stem

        objfile = os.path.join (root, filepath, stem + '.o')
        if not os.path.isfile(ctxt.resolve(objfile)):
            warning ('No object file found for %s at %s' % (srcfile, objfile))
            continue
        if not os.path.isfile (ctxt.resolve (os.path.join (root, filepath, stem + '.gcno'))):
            warning ('No .gcno file found for %s at %s' % (srcfile, os.path.join (root, filepath, stem + '.gcno')))
            continue
        if not os.path.isfile (ctxt.resolve (os.path.join (root, filepath, stem + '.gcda'))):
            warning ('No .gcda file found for %s at %s' % (srcfile, os.path.join (root, filepath, stem + '.gcda')))
            continue

        num_lines, num_covered = 0, 0
        skip_block = False
        cmd = CommandLine('gcov', ['-b', '-n', '-o', objfile, srcfile],
                          cwd=ctxt.basedir)
        for out, err in cmd.execute():
            if out == '': # catch blank lines, reset the block state...
                skip_block = False
            elif out and not skip_block:
                # Check for a file name
                match = file_re.match(out)
                if match:
                    if os.path.isabs(match.group('file')):
                        skip_block = True
                        continue
                else:
                    # check for a "Lines executed" message
                    match = lines_re.match(out)
                    if match:
                        lines = float(match.group('num'))
                        cov = float(match.group('cov'))
                        num_covered += int(lines * cov / 100)
                        num_lines += int(lines)
        if cmd.returncode != 0:
            continue

        module = xmlio.Element('coverage', name=os.path.basename(srcfile),
                                file=srcfile.replace(os.sep, '/'),
                                lines=num_lines, percentage=0)
        if num_lines:
            percent = int(round(num_covered * 100 / num_lines))
            module.attr['percentage'] = percent
        coverage.append(module)

    ctxt.report('coverage', coverage)
    ctxt.log (log_elem)