示例#1
0
    def get_stats(self, config, args):
        """Run tests using Pylint.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # get Pylint version
        command = ['pylint', '--version', '--rcfile=%s' % self.rcfile]
        version_info = ''.join(
            run_command(command, verbose=False)[0].split('\n')[:2])
        print 'USING              :', version_info

        # Collect python files not present in packages
        py_extra = get_source_filenames(config, 'py', unpackaged_only=True)

        # call Pylint
        command = ['pylint'] + config['py_packages'] \
                  + py_extra \
                  + ['--rcfile=%s' % self.rcfile,
                     '--ignore=%s' % (
                         ','.join(config['py_exclude'])),
                     '-j 2', ]
        output = run_command(command, has_failed=has_failed)[0]

        # parse the output of Pylint into standard return values
        lines = output.split('\n')[:-1]
        score = lines[-2].split()[6]
        print 'SCORE              :', score
        counter = Counter()
        messages = set([])
        for line in lines:
            # skip lines that don't contain error messages
            if '.py:' not in line:
                continue
            if line.startswith('Report'):
                break
            # extract error information
            msg_id, _keyword, location, msg = line.split(' ', 3)
            counter[msg_id] += 1
            filename, pos = location.split(':')
            lineno, charno = pos.split(',')
            lineno = int(lineno)
            charno = int(charno)
            if charno == 0:
                charno = None
            messages.add(
                Message(filename, lineno, charno, '%s %s' % (msg_id, msg)))
        return counter, messages
示例#2
0
    def get_stats(self, config, args):
        """Run tests using Pylint.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # get Pylint version
        command = ['pylint', '--version', '--rcfile=%s' % self.rcfile]
        version_info = ''.join(run_command(command, verbose=False)[0].split('\n')[:2])
        print 'USING              :', version_info

        # Collect python files not present in packages
        py_extra = get_source_filenames(config, 'py', unpackaged_only=True)

        # call Pylint
        command = ['pylint'] + config['py_packages'] \
                  + py_extra \
                  + ['--rcfile=%s' % self.rcfile,
                     '--ignore=%s' % (
                         ','.join(config['py_exclude'])),
                     '-j 2', ]
        output = run_command(command, has_failed=has_failed)[0]

        # parse the output of Pylint into standard return values
        lines = output.split('\n')[:-1]
        score = lines[-2].split()[6]
        print 'SCORE              :', score
        counter = Counter()
        messages = set([])
        for line in lines:
            # skip lines that don't contain error messages
            if '.py:' not in line:
                continue
            if line.startswith('Report'):
                break
            # extract error information
            msg_id, _keyword, location, msg = line.split(' ', 3)
            counter[msg_id] += 1
            filename, pos = location.split(':')
            lineno, charno = pos.split(',')
            lineno = int(lineno)
            charno = int(charno)
            if charno == 0:
                charno = None
            messages.add(Message(filename, lineno, charno, '%s %s' % (msg_id, msg)))
        return counter, messages
示例#3
0
    def get_stats(self, config, args):
        """Run tests using Cppcheck.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Look for custom cppcheck build
        fns_cppcheck = sorted(
            glob('%s/cached/cppcheck-*/cppcheck' % self.qaworkdir))
        if len(fns_cppcheck) > 0:
            binary = os.path.abspath(fns_cppcheck[-1])
        else:
            binary = 'cppcheck'
        print 'USING BINARY       :', binary

        # Get version
        command = [binary, '--version']
        print 'USING VERSION      :', run_command(command,
                                                  verbose=False)[0].strip()

        # Call Cppcheck
        command = [binary] + get_source_filenames(config, 'cpp') + \
                  ['-q', '--enable=all', '--std=c++11', '--xml',
                   '--suppress=missingIncludeSystem', '--suppress=unusedFunction']
        xml_str = run_command(command)[1]
        etree = ElementTree.fromstring(xml_str)

        # Parse the output of Cppcheck into standard return values
        counter = Counter()
        messages = set([])
        for error in etree:
            if 'file' not in error.attrib:
                continue
            key = '%15s  %40s  %30s' % (
                error.attrib['severity'],
                error.attrib['file'].ljust(40),
                error.attrib['id'].ljust(30),
            )
            counter[key] += 1
            text = '%s %s %s' % (error.attrib['severity'], error.attrib['id'],
                                 error.attrib['msg'])
            messages.add(
                Message(error.attrib['file'], int(error.attrib['line']), None,
                        text))
        return counter, messages
示例#4
0
    def get_stats(self, config, args):
        """Run tests using pydocstyle.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        command = ['pydocstyle', '--version']
        version = run_command(command, verbose=False)[0].strip()
        print 'USING              : pydocstyle', version

        # Call pydocstyle in the directories containing Python code. All files will be
        # checked, including test files. Missing docstrings are ignored because they are
        # detected by PyLint in a better way.
        command = ['pydocstyle', '--match=.*\\.py',
                   '--add-ignore=D100,D101,D102,D103,D104,D105'] + \
                  config['py_packages'] + \
                  get_source_filenames(config, 'py', unpackaged_only=True)
        output = run_command(command, has_failed=has_failed)[1]

        # Parse the standard output of pydocstyle
        counter = Counter()
        messages = set([])
        lines = output.split('\n')[:-1]
        while len(lines) > 0:
            if 'WARNING: ' in lines[0]:
                lines.pop(0)
            else:
                words = lines.pop(0).split()
                filename, lineno = words[0].split(':')
                code, description = lines.pop(0).split(':', 1)
                code = code.strip()
                description = description.strip()

                key = '%s %s' % (code, filename)
                message = Message(filename, int(lineno), None,
                                  '%s %s' % (code, description))

                counter[key] += 1
                messages.add(message)
        return counter, messages
示例#5
0
    def get_stats(self, config, args):
        """Run tests using Cppcheck.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Look for custom cppcheck build
        fns_cppcheck = sorted(glob('%s/cached/cppcheck-*/cppcheck' % self.qaworkdir))
        if len(fns_cppcheck) > 0:
            binary = os.path.abspath(fns_cppcheck[-1])
        else:
            binary = 'cppcheck'
        print 'USING BINARY       :', binary

        # Get version
        command = [binary, '--version']
        print 'USING VERSION      :', run_command(command, verbose=False)[0].strip()

        # Call Cppcheck
        command = [binary] + get_source_filenames(config, 'cpp') + \
                  ['-q', '--enable=all', '--std=c++11', '--xml',
                   '--suppress=missingIncludeSystem', '--suppress=unusedFunction']
        xml_str = run_command(command)[1]
        etree = ElementTree.fromstring(xml_str)

        # Parse the output of Cppcheck into standard return values
        counter = Counter()
        messages = set([])
        for error in etree:
            if 'file' not in error.attrib:
                continue
            key = '%15s  %40s  %30s' % (
                error.attrib['severity'],
                error.attrib['file'].ljust(40),
                error.attrib['id'].ljust(30),
            )
            counter[key] += 1
            text = '%s %s %s' % (error.attrib['severity'], error.attrib['id'], error.attrib['msg'])
            messages.add(Message(error.attrib['file'], int(error.attrib['line']),
                                 None, text))
        return counter, messages
    def get_stats(self, config, args):
        """Run tests using pydocstyle.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        command = ['pydocstyle', '--version']
        version = run_command(command, verbose=False)[0].strip()
        print 'USING              : pydocstyle', version

        # Call pydocstyle in the directories containing Python code. All files will be
        # checked, including test files. Missing docstrings are ignored because they are
        # detected by PyLint in a better way.
        command = ['pydocstyle', '--match=.*\\.py',
                   '--add-ignore=D100,D101,D102,D103,D104,D105'] + \
                  config['py_packages'] + \
                  get_source_filenames(config, 'py', unpackaged_only=True)
        output = run_command(command, has_failed=has_failed)[1]

        # Parse the standard output of pydocstyle
        counter = Counter()
        messages = set([])
        lines = output.split('\n')[:-1]
        while len(lines) > 0:
            if 'WARNING: ' in lines[0]:
                lines.pop(0)
            else:
                words = lines.pop(0).split()
                filename, lineno = words[0].split(':')
                code, description = lines.pop(0).split(':', 1)
                code = code.strip()
                description = description.strip()

                key = '%s %s' % (code, filename)
                message = Message(filename, int(lineno), None, '%s %s' % (code, description))

                counter[key] += 1
                messages.add(message)
        return counter, messages
示例#7
0
    def get_stats(self, config):
        """Run tests using pydocstyle.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        command = ["pydocstyle", "--version"]
        version = run_command(command, verbose=False)[0].strip()
        print "USING              : pydocstyle", version

        # Call pydocstyle in the directories containing Python code. All files will be
        # checked, including test files. Missing tests are ignored (D103) because they are
        # already detected by PyLint in a better way.
        command = (
            ["pydocstyle", "--match=.*\\.py", "--add-ignore=D103"]
            + config["py_packages"]
            + get_source_filenames(config, "py", unpackaged_only=True)
        )
        output = run_command(command, has_failed=has_failed)[1]

        # Parse the standard output of pydocstyle
        counter = Counter()
        messages = set([])
        lines = output.split("\n")[:-1]
        while len(lines) > 0:
            if "WARNING: " in lines[0]:
                lines.pop(0)
            else:
                words = lines.pop(0).split()
                filename, lineno = words[0].split(":")
                code, description = lines.pop(0).split(":", 1)
                code = code.strip()
                description = description.strip()

                key = "%s %s" % (code, filename)
                message = Message(filename, int(lineno), None, "%s %s" % (code, description))

                counter[key] += 1
                messages.add(message)
        return counter, messages
    def get_stats(self, config, args):
        """Run tests using doxygen.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        command = ['doxygen', '--version']
        print 'USING              : doxygen', run_command(
            command, verbose=False)[0].strip()

        # Call doxygen in the doc subdirectory, mute output because it only confuses
        command = ['doxygen', self.doxyconf_file]
        run_command(command, cwd=config['doxygen_root'])

        # Parse the file doxygen_warnings log file
        counter = Counter()
        messages = set([])
        prefix = os.getcwd() + '/'

        fn_warnings = os.path.join(config['doxygen_root'],
                                   config['doxygen_warnings'])
        with open(fn_warnings, 'r') as f:
            # Doxygen sometimes wraps lines in the warnings log. That is sad, but we
            # have to handle it.
            for line in unwrapped_iter(f):
                location, description = line.split(None, 1)
                filename, lineno = location.split(':')[:2]
                if filename.startswith(prefix):
                    filename = filename[len(prefix):]
                message = Message(filename, int(lineno), None, description)
                # Sadly, doxygen sometimes generates duplicate messages for no good
                # reason, which leads to incorrect counters and incorrectly failing tests.
                # We need to check this explicitly...
                if message not in messages:
                    counter[filename] += 1
                    messages.add(message)
        return counter, messages
    def get_stats(self, config, args):
        """Run tests using doxygen.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        command = ['doxygen', '--version']
        print 'USING              : doxygen', run_command(command, verbose=False)[0].strip()

        # Call doxygen in the doc subdirectory, mute output because it only confuses
        command = ['doxygen', self.doxyconf_file]
        run_command(command, cwd=config['doxygen_root'])

        # Parse the file doxygen_warnings log file
        counter = Counter()
        messages = set([])
        prefix = os.getcwd() + '/'

        fn_warnings = os.path.join(config['doxygen_root'], config['doxygen_warnings'])
        with open(fn_warnings, 'r') as f:
            # Doxygen sometimes wraps lines in the warnings log. That is sad, but we
            # have to handle it.
            for line in unwrapped_iter(f):
                location, description = line.split(None, 1)
                filename, lineno = location.split(':')[:2]
                if filename.startswith(prefix):
                    filename = filename[len(prefix):]
                message = Message(filename, int(lineno), None, description)
                # Sadly, doxygen sometimes generates duplicate messages for no good
                # reason, which leads to incorrect counters and incorrectly failing tests.
                # We need to check this explicitly...
                if message not in messages:
                    counter[filename] += 1
                    messages.add(message)
        return counter, messages
示例#10
0
    def get_stats(self, config, args):
        """Run tests using cpplint.py.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        print 'USING              : cpplint.py update #456'

        # Call cpplint
        command = [
            self.cpplint_file, '--linelength=100', '--filter=-runtime/int'
        ]
        command += get_source_filenames(config, 'cpp')
        output = run_command(command, has_failed=has_failed)[1]

        # Parse the output of cpplint into standard return values
        counter = Counter()
        messages = set([])
        for line in output.split('\n')[:-1]:
            if line.startswith('Done') or line.startswith('Total'):
                continue
            words = line.split()
            filename, lineno = words[0].split(':')[:2]
            description = ' '.join(words[1:-2])
            tag = words[-2]
            priority = words[-1]

            counter['%3s %-30s' % (priority, tag)] += 1
            messages.add(
                Message(filename, int(lineno), None,
                        '%s %s %s' % (priority, tag, description)))

        return counter, messages
示例#11
0
    def get_stats(self, config, args):
        """Run tests using cpplint.py.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        print 'USING              : cpplint.py update #456'

        # Call cpplint
        command = [self.cpplint_file, '--linelength=100', '--filter=-runtime/int']
        command += get_source_filenames(config, 'cpp')
        output = run_command(command, has_failed=has_failed)[1]

        # Parse the output of cpplint into standard return values
        counter = Counter()
        messages = set([])
        for line in output.split('\n')[:-1]:
            if line.startswith('Done') or line.startswith('Total'):
                continue
            words = line.split()
            filename, lineno = words[0].split(':')[:2]
            description = ' '.join(words[1:-2])
            tag = words[-2]
            priority = words[-1]

            counter['%3s %-30s' % (priority, tag)] += 1
            messages.add(Message(filename, int(lineno), None, '%s %s %s' % (
                priority, tag, description)))

        return counter, messages
示例#12
0
    def get_stats(self, config):
        """Run tests using nosetests with coverage analysis.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        command = ['nosetests', '--version']
        print 'USING              :', run_command(command, verbose=False)[0].strip()
        command = ['coverage', '--version']
        print 'USING              :', run_command(command, verbose=False)[0].split('\n')[0]

        # Results will be stored in the following variables
        counter = Counter()
        messages = set([])

        # Run fast unit tests with nosetests, with coverage
        command = ['nosetests', '-v', '-a', '!slow', '--with-coverage', '--cover-erase',
                   '--cover-branches',
                   '--cover-package=%s' % ','.join(config['py_packages'])] + \
                   config['py_directories']
        output = run_command(command)[0]
        lines = [line.strip() for line in output.split('\n')]

        # Parse the output of the unit tests
        iline = 0
        for line in lines:
            if len(line) == 0:
                break
            elif line.endswith('FAIL'):
                counter['unit_tests_failed'] += 1
                messages.add(Message(None, None, None, 'nosetests ' + line))
            elif line.endswith('ERROR'):
                counter['unit_tests_error'] += 1
                messages.add(Message(None, None, None, 'nosetests ' + line))
            iline += 1

        # Run the coverage program for a full report. This separate call is needed
        # since coverage-4.1.
        fn_coverage = '%s/coverage.xml' % self.qaworkdir
        command = ['coverage', 'xml', '-o', fn_coverage,
                   '--omit=%s' % ','.join(config['py_test_files'])]
        output = run_command(command)[0]

        # Parse coverage xml output
        et = ElementTree.parse(fn_coverage)
        for class_tag in et.getroot().iter('class'):
            filename = class_tag.attrib['filename']
            for line_tag in class_tag.iter('line'):
                if line_tag.attrib['hits'] == '0':
                    line = int(line_tag.attrib['number'])
                    branch_ends = line_tag.get('missing-branches')
                    if branch_ends is not None:
                        for branch_end in branch_ends.split(','):
                            if branch_end.isdigit():
                                delta = int(branch_end) - line
                                msg = Message(filename, line, None,
                                              'Missed branch to line %+i' % (delta))
                            else:
                                msg = Message(filename, line, None,
                                              'Missed branch to %s' % branch_end)
                            messages.add(msg)
                            counter[filename] += 1
                    messages.add(Message(filename, line, None, 'Missed line'))
                    counter[filename] += 1
        return counter, messages
示例#13
0
    def get_stats(self, config, args):
        """Run tests using pydocstyle.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        command = ['pydocstyle', '--version']
        version = run_command(command, verbose=False)[0].strip()
        print 'USING              : pydocstyle', version

        default_match = '({0})'.format(config['default_match'])
        output = ''
        # apply trapdoor with custom configurations first
        for custom_config in config['custom'].values():
            match = custom_config['match']
            ignore = custom_config['ignore']
            # keep track of files that have been selected already
            # (this will be used to select files that have not been included in the configurations)
            default_match = '(?!{0}){1}'.format(match, default_match)
            # Call pydocstyle in the directories containing Python code. All files will be
            # checked, including test files. Missing docstrings are ignored because they are
            # detected by PyLint in a better way.
            output += run_command(
                [
                    'pydocstyle', '--match={0}'.format(match),
                    '--add-ignore={0}'.format(ignore)
                ] + config['py_packages'] +
                get_source_filenames(config, 'py', unpackaged_only=True),
                has_failed=has_failed)[1]
        # run trapdoor with default configuration on all the files that have not been tested yet
        output += run_command(
            [
                'pydocstyle', '--match={0}'.format(default_match),
                '--add-ignore={0}'.format(config['default_ignore'])
            ] + config['py_packages'] +
            get_source_filenames(config, 'py', unpackaged_only=True),
            has_failed=has_failed)[1]

        # Parse the standard output of pydocstyle
        counter = Counter()
        messages = set([])
        lines = output.split('\n')[:-1]
        while len(lines) > 0:
            if 'WARNING: ' in lines[0]:
                lines.pop(0)
            else:
                words = lines.pop(0).split()
                filename, lineno = words[0].split(':')
                code, description = lines.pop(0).split(':', 1)
                code = code.strip()
                description = description.strip()

                key = '%s %s' % (code, filename)
                message = Message(filename, int(lineno), None,
                                  '%s %s' % (code, description))

                counter[key] += 1
                messages.add(message)
        return counter, messages
    def get_stats(self, config, args):
        """Run tests using nosetests with coverage analysis.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        command = ['nosetests', '--version']
        print 'USING              :', run_command(command,
                                                  verbose=False)[0].strip()
        command = ['coverage', '--version']
        print 'USING              :', run_command(
            command, verbose=False)[0].split('\n')[0]

        # Results will be stored in the following variables
        counter = Counter()
        messages = set([])

        # Run fast unit tests with nosetests, with coverage
        command = ['nosetests', '-v', '-a', '!slow',
                   '--with-coverage',
                   '--cover-erase',
                   '--cover-branches',
                   '--cover-package=%s' % ','.join(config['py_packages'])] + \
                   config['py_directories']
        if args.nproc > 1:
            command.extend(
                ['--processes=%s' % args.nproc, '--process-timeout=600'])
        output = run_command(command)[0]
        lines = [line.strip() for line in output.split('\n')]

        # Parse the output of the unit tests
        iline = 0
        for line in lines:
            if len(line) == 0:
                break
            elif line.endswith('FAIL'):
                counter['unit_tests_failed'] += 1
                messages.add(Message(None, None, None, 'nosetests ' + line))
            elif line.endswith('ERROR'):
                counter['unit_tests_error'] += 1
                messages.add(Message(None, None, None, 'nosetests ' + line))
            iline += 1

        # Run the coverage program for a full report. This separate call is needed
        # since coverage-4.1.
        fn_coverage = '%s/coverage.xml' % self.qaworkdir
        command = [
            'coverage', 'xml', '-o', fn_coverage,
            '--omit=%s' % ','.join(config['py_test_files'])
        ]
        output = run_command(command)[0]

        # Parse coverage xml output
        et = ElementTree.parse(fn_coverage)
        for class_tag in et.getroot().iter('class'):
            filename = class_tag.attrib['filename']
            with open(filename) as fsource:
                source_lines = fsource.readlines()
            for line_tag in class_tag.iter('line'):
                if line_tag.attrib['hits'] == '0':
                    line = int(line_tag.attrib['number'])
                    if excluded_from_coverage(source_lines[line - 1]):
                        continue
                    branch_ends = line_tag.get('missing-branches')
                    if branch_ends is not None:
                        for branch_end in branch_ends.split(','):
                            if branch_end.isdigit():
                                delta = int(branch_end) - line
                                msg = Message(
                                    filename, line, None,
                                    'Missed branch to line %+i' % (delta))
                            else:
                                msg = Message(
                                    filename, line, None,
                                    'Missed branch to %s' % branch_end)
                            messages.add(msg)
                            counter[filename] += 1
                    messages.add(Message(filename, line, None, 'Missed line'))
                    counter[filename] += 1
        return counter, messages
示例#15
0
    def get_stats(self, config, args):
        """Run tests using nosetests with coverage analysis.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        command = ["nosetests", "--version"]
        print "USING              :", run_command(command, verbose=False)[0].strip()
        command = ["coverage", "--version"]
        print "USING              :", run_command(command, verbose=False)[0].split("\n")[0]

        # Results will be stored in the following variables
        counter = Counter()
        messages = set([])

        # Run fast unit tests with nosetests, with coverage
        command = [
            "nosetests",
            "-v",
            "-A",
            "not (slow or rt)",
            "--with-coverage",
            "--cover-erase",
            "--cover-branches",
            "--cover-package=%s" % ",".join(config["py_packages"]),
        ] + config["py_directories"]
        if args.nproc > 1:
            command.extend(["--processes=%s" % args.nproc, "--process-timeout=600"])
        output = run_command(command)[0]
        lines = [line.strip() for line in output.split("\n")]

        # Parse the output of the unit tests
        iline = 0
        for line in lines:
            if len(line) == 0:
                break
            elif line.endswith("FAIL"):
                counter["unit_tests_failed"] += 1
                messages.add(Message(None, None, None, "nosetests " + line))
            elif line.endswith("ERROR"):
                counter["unit_tests_error"] += 1
                messages.add(Message(None, None, None, "nosetests " + line))
            iline += 1

        # Run the coverage program for a full report. This separate call is needed
        # since coverage-4.1.
        fn_coverage = "%s/coverage.xml" % self.qaworkdir
        command = ["coverage", "xml", "-o", fn_coverage, "--omit=%s" % ",".join(config["py_test_files"])]
        output = run_command(command)[0]

        # Parse coverage xml output
        et = ElementTree.parse(fn_coverage)
        for class_tag in et.getroot().iter("class"):
            filename = class_tag.attrib["filename"]
            with open(filename) as fsource:
                source_lines = fsource.readlines()
            for line_tag in class_tag.iter("line"):
                if line_tag.attrib["hits"] == "0":
                    line = int(line_tag.attrib["number"])
                    if excluded_from_coverage(source_lines[line - 1]):
                        continue
                    branch_ends = line_tag.get("missing-branches")
                    if branch_ends is not None:
                        for branch_end in branch_ends.split(","):
                            if branch_end.isdigit():
                                delta = int(branch_end) - line
                                msg = Message(filename, line, None, "Missed branch to line %+i" % (delta))
                            else:
                                msg = Message(filename, line, None, "Missed branch to %s" % branch_end)
                            messages.add(msg)
                            counter[filename] += 1
                    messages.add(Message(filename, line, None, "Missed line"))
                    counter[filename] += 1
        return counter, messages
示例#16
0
    def get_stats(self, config, args):
        """Run tests using Pylint.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # get default rcfile
        qatooldir = os.path.dirname(os.path.abspath(__file__))
        default_rc_file = os.path.join(self.qaworkdir, config['default_rc'])
        # FIXME: not too sure if this should be in prepare
        shutil.copy(os.path.join(qatooldir, os.path.basename(default_rc_file)), default_rc_file)

        # get Pylint version
        command = ['pylint', '--version', '--rcfile={0}'.format(default_rc_file)]
        version_info = ''.join(run_command(command, verbose=False)[0].split('\n')[:2])
        print 'USING              :', version_info

        # Collect python files (pylint ignore is quite bad.. need to ignore manually)
        py_extra = get_source_filenames(config, 'py', unpackaged_only=True)

        def get_filenames(file_or_dir, exclude=tuple()):
            """Recursively finds all of the files within the given file or directory.

            Avoids the files and directories specified in exclude.

            Parameters
            ----------
            file_or_dir : str
                File or directory
            exclude : tuple
                Files or directories to ignore

            Returns
            -------
            list of files as a relative path
            """
            output = []
            if os.path.isfile(file_or_dir) and file_or_dir not in exclude:
                output.append(file_or_dir)
            elif os.path.isdir(file_or_dir):
                for dirpath, _dirnames, filenames in os.walk(file_or_dir):
                    # check if directory is allowed
                    if any(os.path.samefile(dirpath, i) for i in exclude):
                        continue
                    for filename in filenames:
                        # check if filename is allowed
                        if (os.path.splitext(filename)[1] != '.py' or
                                filename in exclude or
                                any(os.path.samefile(os.path.join(dirpath, filename), i)
                                    for i in exclude)):
                            continue
                        output.append(os.path.join(dirpath, filename))
            return output

        output = ''
        exclude_files = []
        # run pylint test using each configuration
        for custom_config in config['custom'].values():
            rc_file = os.path.join(self.qaworkdir, custom_config['rc'])
            shutil.copy(os.path.join(qatooldir, os.path.basename(rc_file)), rc_file)

            # collect files
            py_files = []
            for custom_file in custom_config['files']:
                py_files.extend(get_filenames(custom_file))

            # call Pylint
            output += run_command(['pylint'] +
                                  py_files +
                                  ['--rcfile={0}'.format(rc_file),
                                   '-j 2', ],
                                  has_failed=has_failed)[0]
            # exclude directories/files
            exclude_files.extend(py_files)
        # get files that have not been run
        py_files = []
        for py_file in config['py_packages'] + py_extra:
            py_files.extend(get_filenames(py_file, exclude=exclude_files + config['py_exclude']))
        # call Pylint
        output += run_command(['pylint'] +
                              py_files +
                              ['--rcfile={0}'.format(default_rc_file),
                               '-j 2', ],
                              has_failed=has_failed)[0]

        # parse the output of Pylint into standard return values
        lines = output.split('\n')[:-1]
        score = lines[-2].split()[6]
        print 'SCORE              :', score
        counter = Counter()
        messages = set([])
        for line in lines:
            # skip lines that don't contain error messages
            if '.py:' not in line:
                continue
            if line.startswith('Report'):
                break
            # extract error information
            msg_id, _keyword, location, msg = line.split(' ', 3)
            counter[msg_id] += 1
            filename, pos = location.split(':')
            lineno, charno = pos.split(',')
            lineno = int(lineno)
            charno = int(charno)
            if charno == 0:
                charno = None
            messages.add(Message(filename, lineno, charno, '%s %s' % (msg_id, msg)))
        return counter, messages