Ejemplo n.º 1
0
 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))
Ejemplo n.º 2
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:
                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)
Ejemplo n.º 3
0
def phpunit(ctxt, file_=None):
    """Extract test results from a PHPUnit XML report."""
    assert file_, 'Missing required attribute "file"'

    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)

    try:
        total, failed = 0, 0
        results = xmlio.Fragment()
        fileobj = file(ctxt.resolve(file_), 'r')
        try:
            for testsuite in xmlio.parse(fileobj).children('testsuite'):
                total += int(testsuite.attr['tests'])
                failed += int(testsuite.attr['failures']) + \
                            int(testsuite.attr['errors'])

                _process_testsuite(testsuite, results)
        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:
        ctxt.log('Error opening PHPUnit results file (%s)' % e)
Ejemplo n.º 4
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)
Ejemplo n.º 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)
Ejemplo n.º 6
0
def figleaf(ctxt, summary=None, include=None, exclude=None):
    """Extract data from a ``Figleaf`` run.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param summary: path to the file containing the coverage summary
    :param include: patterns of files or directories to include in the report
    :param exclude: patterns of files or directories to exclude from the report
    """
    from figleaf import get_lines
    coverage = xmlio.Fragment()
    try:
        fileobj = open(ctxt.resolve(summary))
    except IOError, e:
        log.warning('Error opening coverage summary file (%s)', e)
        return
Ejemplo n.º 7
0
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)
Ejemplo n.º 8
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)
Ejemplo n.º 9
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()
Ejemplo n.º 10
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)
Ejemplo n.º 11
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)
Ejemplo n.º 12
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)
Ejemplo n.º 13
0
def coverage(ctxt, file_=None):
    """Extract data from Phing or PHPUnit code coverage report."""
    assert file_, 'Missing required attribute "file"'

    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)

    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)

    try:
        summary_file = file(ctxt.resolve(file_), 'r')
        summary = xmlio.parse(summary_file)
        coverage = xmlio.Fragment()
        try:
            for element in summary.children():
                if element.name == 'package':
                    _process_phing_coverage(ctxt, element, coverage)
                elif element.name == 'project':
                    _process_phpunit_coverage(ctxt, element, coverage)
        finally:
            summary_file.close()
        ctxt.report('coverage', coverage)
    except IOError, e:
        ctxt.log('Error opening coverage summary file (%s)' % e)
Ejemplo n.º 14
0
def ant(ctxt, file_=None, target=None, keep_going=False, args=None):
    """Run an Ant build.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param file\_: name of the Ant build file
    :param target: name of the target that should be executed (optional)
    :param keep_going: whether Ant should keep going when errors are encountered
    :param args: additional arguments to pass to Ant
    """
    executable = 'ant'
    ant_home = ctxt.config.get_dirpath('ant.home')
    if ant_home:
        executable = os.path.join(ant_home, 'bin', 'ant')

    java_home = ctxt.config.get_dirpath('java.home')
    if java_home:
        os.environ['JAVA_HOME'] = java_home

    logfile = tempfile.NamedTemporaryFile(prefix='ant_log', suffix='.xml')
    logfile.close()
    if args:
        args = shlex.split(args)
    else:
        args = []
    args += [
        '-noinput', '-listener', 'org.apache.tools.ant.XmlLogger',
        '-Dant.XmlLogger.stylesheet.uri', '""', '-DXmlLogger.file',
        logfile.name
    ]
    if file_:
        args += ['-buildfile', ctxt.resolve(file_)]
    if keep_going:
        args.append('-keep-going')
    if target:
        args.append(target)

    shell = False
    if os.name == 'nt':
        # Need to execute ant.bat through a shell on Windows
        shell = True

    cmdline = CommandLine(executable, args, cwd=ctxt.basedir, shell=shell)
    for out, err in cmdline.execute():
        if out is not None:
            log.info(out)
        if err is not None:
            log.error(err)

    error_logged = False
    log_elem = xmlio.Fragment()
    try:
        xml_log = xmlio.parse(file(logfile.name, 'r'))

        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)

        collect_log_messages(xml_log)

        if 'error' in xml_log.attr:
            ctxt.error(xml_log.attr['error'])
            error_logged = True

    except xmlio.ParseError, e:
        log.warning('Error parsing Ant XML log file (%s)', e)
Ejemplo n.º 15
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)
Ejemplo n.º 16
0
def execute(ctxt,
            executable=None,
            file_=None,
            input_=None,
            output=None,
            args=None,
            dir_=None,
            filter_=None):
    """Generic external program execution.
    
    This function is not itself bound to a recipe command, but rather used from
    other commands.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param executable: name of the executable to run
    :param file\_: name of the script file, relative to the project directory,
                  that should be run
    :param input\_: name of the file containing the data that should be passed
                   to the shell script on its standard input stream
    :param output: name of the file to which the output of the script should be
                   written
    :param args: command-line arguments to pass to the script
    :param filter\_: function to filter out messages from the executable stdout
    """
    if args:
        if isinstance(args, basestring):
            args = shlex.split(args)
    else:
        args = []

    if dir_:

        def resolve(*args):
            return ctxt.resolve(dir_, *args)
    else:
        resolve = ctxt.resolve

    if file_ and os.path.isfile(resolve(file_)):
        file_ = resolve(file_)

    if executable is None:
        executable = file_
    elif file_:
        args[:0] = [file_]

    # Support important Windows CMD.EXE built-ins (and it does its own quoting)
    if os.name == 'nt' and executable.upper() in [
            'COPY', 'DIR', 'ECHO', 'ERASE', 'DEL', 'MKDIR', 'MD', 'MOVE',
            'RMDIR', 'RD', 'TYPE'
    ]:
        args = ['/C', executable] + [arg.strip('"') for arg in args]
        executable = os.environ['COMSPEC']

    if input_:
        input_file = file(resolve(input_), 'r')
    else:
        input_file = None

    if output:
        output_file = file(resolve(output), 'w')
    else:
        output_file = None

    if dir_ and os.path.isdir(ctxt.resolve(dir_)):
        dir_ = ctxt.resolve(dir_)
    else:
        dir_ = ctxt.basedir

    if not filter_:
        filter_ = lambda s: s

    try:
        cmdline = CommandLine(executable, args, input=input_file, cwd=dir_)
        log_elem = xmlio.Fragment()
        for out, err in cmdline.execute():
            if out is not None:
                log.info(out)
                info = filter_(out)
                if info:
                    log_elem.append(
                        xmlio.Element('message', level='info')[info.replace(
                            ctxt.basedir + os.sep,
                            '').replace(ctxt.basedir, '')])
                if output:
                    output_file.write(out + os.linesep)
            if err is not None:
                log.error(err)
                log_elem.append(
                    xmlio.Element('message', level='error')[err.replace(
                        ctxt.basedir + os.sep, '').replace(ctxt.basedir, '')])
                if output:
                    output_file.write(err + os.linesep)
        ctxt.log(log_elem)
    finally:
        if input_:
            input_file.close()
        if output:
            output_file.close()

    return cmdline.returncode
Ejemplo n.º 17
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)
Ejemplo n.º 18
0
def cppunit(ctxt, file_=None, srcdir=None):
    """Collect CppUnit XML data.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param file\_: path of the file containing the CppUnit results; may contain
                  globbing wildcards to match multiple files
    :param srcdir: name of the directory containing the source files, used to
                   link the test results to the corresponding files
    """
    assert file_, 'Missing required attribute "file"'

    try:
        fileobj = file(ctxt.resolve(file_), 'r')
        try:
            total, failed = 0, 0
            results = xmlio.Fragment()
            for group in xmlio.parse(fileobj):
                if group.name not in ('FailedTests', 'SuccessfulTests'):
                    continue
                for child in group.children():
                    test = xmlio.Element('test')
                    name = child.children('Name').next().gettext()
                    if '::' in name:
                        parts = name.split('::')
                        test.attr['fixture'] = '::'.join(parts[:-1])
                        name = parts[-1]
                    test.attr['name'] = name

                    for location in child.children('Location'):
                        for file_elem in location.children('File'):
                            filepath = file_elem.gettext()
                            if srcdir is not None:
                                filepath = posixpath.join(srcdir, filepath)
                            test.attr['file'] = filepath
                            break
                        for line_elem in location.children('Line'):
                            test.attr['line'] = line_elem.gettext()
                            break
                        break

                    if child.name == 'FailedTest':
                        for message in child.children('Message'):
                            test.append(xmlio.Element('traceback')[
                                message.gettext()
                            ])
                        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)

        finally:
            fileobj.close()

    except IOError, e:
        log.warning('Error opening CppUnit results file (%s)', e)
Ejemplo n.º 19
0
def cunit (ctxt, file_=None, srcdir=None):
    """Collect CUnit XML data.
    
    :param ctxt: the build context
    :type ctxt: `Context`
    :param file\_: path of the file containing the CUnit results; may contain
                  globbing wildcards to match multiple files
    :param srcdir: name of the directory containing the source files, used to
                   link the test results to the corresponding files
    """
    assert file_, 'Missing required attribute "file"'

    try:
        fileobj = file(ctxt.resolve(file_), 'r')
        try:
            total, failed = 0, 0
            results = 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 node in xmlio.parse(fileobj):
                if node.name != 'CUNIT_RESULT_LISTING':
                    continue
                for suiteRun in node.children ('CUNIT_RUN_SUITE'):
                    for suite in suiteRun.children():
                        if suite.name not in ('CUNIT_RUN_SUITE_SUCCESS', 'CUNIT_RUN_SUITE_FAILURE'):
                            warning ("Unknown node: %s" % suite.name)
                            continue
                        suiteName = suite.children ('SUITE_NAME').next().gettext()
                        info ("%s [%s]" % ("*" * (57 - len (suiteName)), suiteName))
                        for record in suite.children ('CUNIT_RUN_TEST_RECORD'):
                            for result in record.children():
                                if result.name not in ('CUNIT_RUN_TEST_SUCCESS', 'CUNIT_RUN_TEST_FAILURE'):
                                    continue
                                testName = result.children ('TEST_NAME').next().gettext()
                                info ("Running %s..." % testName);
                                test = xmlio.Element('test')
                                test.attr['fixture'] = suiteName
                                test.attr['name'] = testName
                                if result.name == 'CUNIT_RUN_TEST_FAILURE':
                                    error ("%s(%d): %s"
                                               % (result.children ('FILE_NAME').next().gettext(),
                                                  int (result.children ('LINE_NUMBER').next().gettext()),
                                                  result.children ('CONDITION').next().gettext()))
                                    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)
            ctxt.log (log_elem)

        finally:
            fileobj.close()

    except IOError, e:
        log.warning('Error opening CUnit results file (%s)', e)
Ejemplo n.º 20
0
                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)

    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