Beispiel #1
0
def generate_coverage_reports(reports_dir=COVERAGE_DIR, report="all"):
    '''
    This task combines all the coverage data files generated during all the tests and generates the coverage reports.
    :param report: type of the coverage report to be generated. Options: text|html|xml. Defaults to 'html'
    :param reports_dir: directory under which all coverage files to be found and reports to be generated. Defaults to reports/coverage
    '''

    print("\nInitiating code coverage analysis ...")
    coverage_obj = Coverage(data_file="%s/coverage" % reports_dir)
    coverage_obj.combine()
    coverage_obj.save()

    if report == "all":
        with open("%s/coverage.txt" % reports_dir, "w") as cov_rep:
            coverage_obj.report(file=cov_rep, show_missing=True)
            print("Generated Code-Coverage text report at %s/coverage.txt\n" %
                  reports_dir)
        coverage_obj.html_report(directory=reports_dir)
        print("Generated Code-Coverage HTML report at %s\n" % reports_dir)
        coverage_obj.xml_report(outfile='%s/coverage.xml' % reports_dir)
        print("Generated Code-Coverage XML report at %s/coverage.xml\n" %
              reports_dir)
    elif report == "text":
        with open("%s/coverage.txt" % reports_dir, "w") as cov_rep:
            coverage_obj.report(file=cov_rep, show_missing=True)
            print("Generated Code-Coverage text report at %s/coverage.txt\n" %
                  reports_dir)
    elif report == "html":
        coverage_obj.html_report(directory=reports_dir)
        print("Generated Code-Coverage HTML report at %s\n" % reports_dir)
    elif report == "xml":
        coverage_obj.xml_report(outfile='%s/coverage.xml' % reports_dir)
        print("Generated Code-Coverage XML report at %s/coverage.xml\n" %
              reports_dir)
Beispiel #2
0
def main():
    options = parse_args()
    test_directory = os.path.dirname(os.path.abspath(__file__))
    selected_tests = get_selected_tests(options)

    if options.verbose:
        print_to_stderr('Selected tests: {}'.format(', '.join(selected_tests)))

    if options.coverage and not PYTORCH_COLLECT_COVERAGE:
        shell(['coverage', 'erase'])

    if options.jit:
        selected_tests = filter(lambda test_name: "jit" in test_name, TESTS)

    if options.determine_from is not None and os.path.exists(options.determine_from):
        with open(options.determine_from, 'r') as fh:
            touched_files = [
                os.path.normpath(name.strip()) for name in fh.read().split('\n')
                if len(name.strip()) > 0
            ]
        # HACK: Ensure the 'test' paths can be traversed by Modulefinder
        sys.path.append('test')
        selected_tests = [
            test for test in selected_tests
            if determine_target(test, touched_files, options)
        ]
        sys.path.remove('test')

    has_failed = False
    failure_messages = []
    try:
        for test in selected_tests:
            options_clone = copy.deepcopy(options)
            if test in USE_PYTEST_LIST:
                options_clone.pytest = True
            err_message = run_test_module(test, test_directory, options_clone)
            if err_message is None:
                continue
            has_failed = True
            failure_messages.append(err_message)
            if not options_clone.continue_through_error:
                raise RuntimeError(err_message)
            print_to_stderr(err_message)
    finally:
        if options.coverage:
            from coverage import Coverage
            test_dir = os.path.dirname(os.path.abspath(__file__))
            with set_cwd(test_dir):
                cov = Coverage()
                if PYTORCH_COLLECT_COVERAGE:
                    cov.load()
                cov.combine(strict=False)
                cov.save()
                if not PYTORCH_COLLECT_COVERAGE:
                    cov.html_report()

    if options.continue_through_error and has_failed:
        for err in failure_messages:
            print_to_stderr(err)
        sys.exit(1)
Beispiel #3
0
def main():
    options = parse_args()

    test_directory = str(REPO_ROOT / "test")
    selected_tests = get_selected_tests(options)

    if options.verbose:
        print_to_stderr("Selected tests:\n {}".format(
            "\n ".join(selected_tests)))

    if options.dry_run:
        return

    if options.coverage and not PYTORCH_COLLECT_COVERAGE:
        shell(["coverage", "erase"])

    if IS_CI:
        selected_tests = get_reordered_tests(selected_tests)
        # downloading test cases configuration to local environment
        get_test_case_configs(dirpath=test_directory)

    has_failed = False
    failure_messages = []
    try:
        for test in selected_tests:
            options_clone = copy.deepcopy(options)
            if test in USE_PYTEST_LIST:
                options_clone.pytest = True
            err_message = run_test_module(test, test_directory, options_clone)
            if err_message is None:
                continue
            has_failed = True
            failure_messages.append(err_message)
            if not options_clone.continue_through_error:
                raise RuntimeError(err_message)
            print_to_stderr(err_message)
    finally:
        if options.coverage:
            from coverage import Coverage

            with set_cwd(test_directory):
                cov = Coverage()
                if PYTORCH_COLLECT_COVERAGE:
                    cov.load()
                cov.combine(strict=False)
                cov.save()
                if not PYTORCH_COLLECT_COVERAGE:
                    cov.html_report()

    if options.continue_through_error and has_failed:
        for err in failure_messages:
            print_to_stderr(err)
        sys.exit(1)
Beispiel #4
0
class Testmon(object):
    coverage_stack = []

    def __init__(self, rootdir="", testmon_labels=None, cov_plugin=None):
        if testmon_labels is None:
            testmon_labels = set(["singleprocess"])
        self.rootdir = rootdir
        self.testmon_labels = testmon_labels
        self.cov = None
        self.setup_coverage(not ("singleprocess" in testmon_labels), cov_plugin)

    def setup_coverage(self, subprocess, cov_plugin=None):
        params = {
            "include": [os.path.join(self.rootdir, "*")],
            "omit": _get_python_lib_paths(),
        }



        self.cov = Coverage(
            data_file=getattr(self, "sub_cov_file", None), config_file=False, **params
        )
        self.cov._warn_no_data = False


    def start(self):

        Testmon.coverage_stack.append(self.cov)
        self.cov.erase()
        self.cov.start()


    def stop(self):
        self.cov.stop()
        Testmon.coverage_stack.pop()

    def stop_and_save(self, testmon_data: TestmonData, rootdir, nodeid, result):
        self.stop()
        if hasattr(self, "sub_cov_file"):
            self.cov.combine()
        node_data = testmon_data.node_data_from_cov(self.cov, nodeid)
        testmon_data.write_node_data(nodeid, node_data, result)

    def close(self):
        if hasattr(self, "sub_cov_file"):
            os.remove(self.sub_cov_file + "_rc")
        os.environ.pop("COVERAGE_PROCESS_START", None)
Beispiel #5
0
def coverage(packages,
             output_stream=sys.stdout,
             coverage_file=None,
             xml_enabled=None,
             xml_file=None):
    """
    Context manager that activates coverage on the specified packages.

    Modules in the packages that are already loaded are removed and nosetest will load
    them again with coverage instrumentation when they are needed.

    :param packages: list of packages
    :param output_stream: the stream to write the results to
    :param coverage_file: the coverage file. If not specified coverage will use the default .coverage file
    :param xml_enabled: enable XML coverage report
    :param xml_file: the XML coverage report file. If not specified coverage will use the defualt coverage.xml file
    :return: instance of coverage.Coverage
    """
    source = {}
    for module_name, module in [
        (module_name, module)
            for module_name, module in list(sys.modules.items())
            if in_any_package(module_name, packages)
    ]:
        source[module_name] = module
        del sys.modules[module_name]

    cover = Coverage(data_file=coverage_file, auto_data=False, branch=False)
    cover.combine()
    cover.erase()
    cover.start()
    yield cover

    for module_name, module in [
        (module_name, module)
            for module_name, module in list(sys.modules.items())
            if in_any_package(module_name, packages)
    ]:
        source[module_name] = module

    cover.stop()
    cover.combine()
    cover.save()
    cover.report(list(source.values()), file=output_stream, show_missing=True)

    if xml_enabled:
        cover.xml_report(list(source.values()), xml_file)
Beispiel #6
0
def coverageReportHelper(config, dataPaths):
    """
    Small utility function to generate coverage reports.

    This was created to side-step the difficulties in submitting multi-line python
    commands on-the-fly.

    This combines data paths and then makes html and xml reports for the
    fully-combined result.
    """
    from coverage import Coverage
    import coverage

    try:
        cov = Coverage(config_file=config)
        if dataPaths:
            # fun fact: if you combine when there's only one file, it gets deleted.
            cov.combine(data_paths=dataPaths)
            cov.save()
        else:
            cov.load()
        cov.html_report()
        cov.xml_report()
    except PermissionError:
        # Albert has some issues with filename that start with a '.', such as the
        # .coverage files. If a permissions error is raised, it likely has something to
        # do with that. We changed the COVERAGE_RESULTS_FILE in cases.py for this reason.
        #
        # We print here, since this is used to run a one-off command, so runLog isn't
        # really appropriate.
        print(
            "There was an issue in generating coverage reports. Probably related to "
            "Albert hidden file issues."
        )
        # disabled until we figure out the problem.
        # raise
    except coverage.misc.CoverageException as e:
        # This is happening when forming the unit test coverage report. This may be
        # caused by the TestFixture coverage report gobbling up all of the coverage
        # files before the UnitTests.cov_report task gets a chance to see them. It may
        # simply be that we dont want a coverage report generated for the TestFixture.
        # Something to think about. Either way, we do not want to fail the job just
        # because of this
        print(
            "There was an issue generating coverage reports "
            "({}):\n{}".format(type(e), e.args)
        )
Beispiel #7
0
def produce_coverage_reports(coverdir):
    cov = Coverage(data_file='coverage')
    cov.combine(data_paths=[os.path.join(coverdir, 'data')])

    pydirs = [os.path.join(ROOT, d) for d in PYTHON_COVERAGE_DIRS]
    omit = [os.path.join(ROOT, d) for d in COVERAGE_OMIT]

    # Ensure all .py files show up in coverage report.
    for d in pydirs:
        for root, dirs, files in os.walk(d):
            for f in files:
                if f.endswith('.py'):
                    cov.data.touch_file(os.path.join(root, f))

    cov.html_report(directory=os.path.join(coverdir, 'html'),
                    ignore_errors=True, omit=omit)
    cov.xml_report(outfile=os.path.join(coverdir, 'coverage.xml'),
                   ignore_errors=True, omit=omit)
Beispiel #8
0
    def run_tests(self, *args, **kwargs):
        suite_result = super().run_tests(*args, **kwargs)

        cov = getattr(process_startup, "coverage")
        cov.stop()
        cov.save()

        print()
        print("Generating Coverage Report...")

        combined_cov = Coverage()
        combined_cov.load()
        combined_cov.combine()
        combined_cov.report()
        combined_cov.html_report()

        print()
        print("Linting files...")
        subprocess.call(["flake8"])

        return suite_result
Beispiel #9
0
class CodeCoverage(object):
    """
        Code Coverage radish extension
    """
    OPTIONS = [
        ('--with-coverage', 'enable code coverage'),
        ('--cover-packages=<cover_packages>', 'specify source code package'),
        ('--cover-append', 'append coverage data to previous collected data'),
        ('--cover-config-file=<cover_config_file>', 'specify coverage config file [default: .coveragerc]'),
        ('--cover-branches', 'include branch coverage in report'),
        ('--cover-erase', 'erase previously collected coverage data'),
        ('--cover-min-percentage=<cover_min_percentage>', 'fail if the given minimum coverage percentage is not reached'),
        ('--cover-html=<cover_html_dir>', 'specify a directory where to store HTML coverage report'),
        ('--cover-xml=<cover_xml_file>', 'specify a file where to store XML coverage report')
    ]
    LOAD_IF = staticmethod(lambda config: config.with_coverage)
    LOAD_PRIORITY = 70

    def __init__(self):
        before.all(self.coverage_start)
        after.all(self.coverage_stop)

        if world.config.cover_packages:
            self.cover_packages = world.config.cover_packages.split(",")
        else:
            self.cover_packages = []

        self.coverage = None
        self.modules_on_init = set(sys.modules.keys())

    def coverage_start(self, features, marker):
        """
        Start the coverage measurement
        """
        # if no explicit modules are specified we just
        # use the ones loaded from radish's basedir.
        # During the plugin init the basedir modules are
        # not loaded yet, but they are during the start method.
        # Thus, we are safe to consider the difference between the
        # two for coverage measurement.
        if not self.cover_packages:
            source = list(set(sys.modules.keys()).difference(self.modules_on_init))
        else:
            source = self.cover_packages

        self.coverage = Coverage(source=source,
                                 config_file=world.config.cover_config_file,
                                 branch=world.config.cover_branches)
        if world.config.cover_erase:
            self.coverage.combine()
            self.coverage.erase()

        if world.config.cover_append:
            self.coverage.load()
        self.coverage.start()

    def coverage_stop(self, features, marker):
        """
        Stop the coverage measurement
        and create report
        """
        self.coverage.stop()
        self.coverage.combine()
        self.coverage.save()
        self.coverage.report(file=sys.stdout)

        if world.config.cover_html:
            self.coverage.html_report(directory=world.config.cover_html)

        if world.config.cover_xml:
            self.coverage.xml_report(outfile=world.config.cover_xml)

        if world.config.cover_min_percentage:
            report = StringIO()
            self.coverage.report(file=report)
            match = re.search(r'^TOTAL\s+(.*)$', report.getvalue(), re.MULTILINE)
            if not match:
                raise RadishError('Failed to find total percentage in coverage report')

            total_percentage = int(match.groups()[0].split()[-1][:-1])
            if total_percentage < int(world.config.cover_min_percentage):
                raise RadishError('Failed to reach minimum expected coverage of {0}% (reached: {1}%)'.format(
                    world.config.cover_min_percentage, total_percentage))
def test_generate_coverage_report(coverage_report_directory):
    cov = Coverage()
    cov.combine(coverage_files)
    cov.html_report(directory=coverage_report_directory)
Beispiel #11
0
# usage:
#   run_tests.py [PYTEST_ARGS]

import os
import subprocess
import sys

from coverage import Coverage


SHELL = sys.platform == 'win32'


if __name__ == '__main__':
    command = ['py.test']
    if os.environ.get('WITH_COVERAGE') == '1':
        command.extend(['--cov=rinoh', '--cov-report='])
    if os.environ.get('BASETEMP'):
        command.append('--basetemp={}'.format(os.environ['BASETEMP']))
    command.extend(sys.argv[1:])
    rc = subprocess.call(command, shell=SHELL)
    if os.environ.get('WITH_COVERAGE') == '1':
        cov = Coverage()
        cov.load()
        cov.combine()
        cov.report(skip_covered=True)
        cov.xml_report()
    raise SystemExit(rc)
Beispiel #12
0
class Coverage:
    """
    Extension for Python Code Coverage
    """

    OPTIONS = [
        click.Option(
            param_decls=("--with-coverage", "with_coverage"),
            is_flag=True,
            help="Enable Code Coverage",
        ),
        click.Option(
            param_decls=("--cover-package", "cover_packages"),
            multiple=True,
            help=
            "Python Package name for which the coverage is measured and reported",
        ),
        click.Option(
            param_decls=("--cover-append", "cover_append"),
            is_flag=True,
            help="Append measured coverage data to previous collected data",
        ),
        click.Option(
            param_decls=("--cover-config-file", "cover_config_file"),
            default=".coveragerc",
            help="Path to a custom coverage configuration file",
        ),
        click.Option(
            param_decls=("--cover-branches", "cover_branches"),
            is_flag=True,
            help="Include branch coverage in the report",
        ),
        click.Option(
            param_decls=("--cover-erase", "cover_erase"),
            is_flag=True,
            help="Erase previously collected data",
        ),
        click.Option(
            param_decls=("--cover-min-percentage", "cover_min_percentage"),
            type=click.IntRange(0, 100),
            help=
            "Fail if the provided minimum coverage percentage is not reached",
        ),
        click.Option(
            param_decls=("--cover-html", "cover_html"),
            help=
            "Path to the directory where to store the HTML coverage report",
        ),
        click.Option(
            param_decls=("--cover-xml", "cover_xml"),
            help="Path to the directory where to store the XML coverage report",
        ),
    ]

    @classmethod
    def load(cls, config):
        if config.with_coverage:
            return cls(
                config.cover_packages,
                config.cover_append,
                config.cover_config_file,
                config.cover_branches,
                config.cover_erase,
                config.cover_min_percentage,
                config.cover_html,
                config.cover_xml,
            )
        else:
            return None

    def __init__(
        self,
        cover_packages,
        cover_append,
        cover_config_file,
        cover_branches,
        cover_erase,
        cover_min_percentage,
        cover_html,
        cover_xml,
    ):
        try:
            from coverage import Coverage  # noqa
        except ImportError:
            raise RadishError(
                "if you want to use the code coverage extension you have to "
                "'pip install radish-bdd[coverage]'")

        self.cover_packages = cover_packages
        self.cover_append = cover_append
        self.cover_config_file = cover_config_file
        self.cover_branches = cover_branches
        self.cover_erase = cover_erase
        self.cover_min_percentage = cover_min_percentage
        self.cover_html = cover_html
        self.cover_xml = cover_xml

        before.all()(self.coverage_start)
        after.all()(self.coverage_stop)

        self.coverage = None
        self.modules_on_init = set(sys.modules.keys())

    def coverage_start(self, features):
        """
        Hook to start the coverage measurement
        """
        from coverage import Coverage

        # if no explicit modules are specified we just
        # use the ones loaded from radish's basedir.
        # During the plugin init the basedir modules are
        # not loaded yet, but they are during the start method.
        # Thus, we are safe to consider the difference between the
        # two for coverage measurement.
        if not self.cover_packages:
            source = list(
                set(sys.modules.keys()).difference(self.modules_on_init))
        else:
            source = self.cover_packages

        self.coverage = Coverage(
            source=source,
            config_file=self.cover_config_file,
            branch=self.cover_branches,
        )
        if self.cover_erase:
            self.coverage.combine()
            self.coverage.erase()

        if self.cover_append:
            self.coverage.load()
        self.coverage.start()

    def coverage_stop(self, features):
        """
        Stop the coverage measurement
        and create report
        """
        self.coverage.stop()
        self.coverage.combine()
        self.coverage.save()
        self.coverage.report(file=sys.stdout)

        if self.cover_html:
            self.coverage.html_report(directory=self.cover_html)

        if self.cover_xml:
            self.coverage.xml_report(outfile=self.cover_xml)

        if self.cover_min_percentage:
            report = StringIO()
            self.coverage.report(file=report)
            match = re.search(r"^TOTAL\s+(.*)$", report.getvalue(),
                              re.MULTILINE)
            if not match:
                raise RadishError(
                    "Failed to find total percentage in coverage report")

            total_percentage = int(match.groups()[0].split()[-1][:-1])
            if total_percentage < int(self.cover_min_percentage):
                raise RadishError(
                    "Failed to reach minimum expected coverage of {0}% (reached: {1}%)"
                    .format(self.cover_min_percentage, total_percentage))
Beispiel #13
0
# usage:
#   run_tests.py [PYTEST_ARGS]

import os
import subprocess
import sys

from coverage import Coverage

SHELL = sys.platform == 'win32'

if __name__ == '__main__':
    command = ['py.test', '-n', 'auto']
    if os.environ.get('WITH_COVERAGE') == '1':
        command.extend(['--cov=rinoh', '--cov-report='])
    if os.environ.get('BASETEMP'):
        command.append('--basetemp={}'.format(os.environ['BASETEMP']))
    command.extend(sys.argv[1:])
    rc = subprocess.call(command, shell=SHELL)
    if os.environ.get('WITH_COVERAGE') == '1':
        cov = Coverage()
        cov.load()
        cov.combine()
        cov.report(skip_covered=True)
        cov.xml_report()
    raise SystemExit(rc)
Beispiel #14
0
class CoverageScript:
    """The command-line interface to coverage.py."""
    def __init__(self):
        self.global_option = False
        self.coverage = None

    def command_line(self, argv):
        """The bulk of the command line interface to coverage.py.

        `argv` is the argument list to process.

        Returns 0 if all is well, 1 if something went wrong.

        """
        # Collect the command-line options.
        if not argv:
            show_help(topic='minimum_help')
            return OK

        # The command syntax we parse depends on the first argument.  Global
        # switch syntax always starts with an option.
        self.global_option = argv[0].startswith('-')
        if self.global_option:
            parser = GlobalOptionParser()
        else:
            parser = COMMANDS.get(argv[0])
            if not parser:
                show_help(f"Unknown command: {argv[0]!r}")
                return ERR
            argv = argv[1:]

        ok, options, args = parser.parse_args_ok(argv)
        if not ok:
            return ERR

        # Handle help and version.
        if self.do_help(options, args, parser):
            return OK

        # Listify the list options.
        source = unshell_list(options.source)
        omit = unshell_list(options.omit)
        include = unshell_list(options.include)
        debug = unshell_list(options.debug)
        contexts = unshell_list(options.contexts)

        if options.concurrency is not None:
            concurrency = options.concurrency.split(",")
        else:
            concurrency = None

        # Do something.
        self.coverage = Coverage(
            data_file=options.data_file or DEFAULT_DATAFILE,
            data_suffix=options.parallel_mode,
            cover_pylib=options.pylib,
            timid=options.timid,
            branch=options.branch,
            config_file=options.rcfile,
            source=source,
            omit=omit,
            include=include,
            debug=debug,
            concurrency=concurrency,
            check_preimported=True,
            context=options.context,
            messages=not options.quiet,
        )

        if options.action == "debug":
            return self.do_debug(args)

        elif options.action == "erase":
            self.coverage.erase()
            return OK

        elif options.action == "run":
            return self.do_run(options, args)

        elif options.action == "combine":
            if options.append:
                self.coverage.load()
            data_paths = args or None
            self.coverage.combine(data_paths,
                                  strict=True,
                                  keep=bool(options.keep))
            self.coverage.save()
            return OK

        # Remaining actions are reporting, with some common options.
        report_args = dict(
            morfs=unglob_args(args),
            ignore_errors=options.ignore_errors,
            omit=omit,
            include=include,
            contexts=contexts,
        )

        # We need to be able to import from the current directory, because
        # plugins may try to, for example, to read Django settings.
        sys.path.insert(0, '')

        self.coverage.load()

        total = None
        if options.action == "report":
            total = self.coverage.report(precision=options.precision,
                                         show_missing=options.show_missing,
                                         skip_covered=options.skip_covered,
                                         skip_empty=options.skip_empty,
                                         sort=options.sort,
                                         **report_args)
        elif options.action == "annotate":
            self.coverage.annotate(directory=options.directory, **report_args)
        elif options.action == "html":
            total = self.coverage.html_report(
                directory=options.directory,
                precision=options.precision,
                skip_covered=options.skip_covered,
                skip_empty=options.skip_empty,
                show_contexts=options.show_contexts,
                title=options.title,
                **report_args)
        elif options.action == "xml":
            total = self.coverage.xml_report(outfile=options.outfile,
                                             skip_empty=options.skip_empty,
                                             **report_args)
        elif options.action == "json":
            total = self.coverage.json_report(
                outfile=options.outfile,
                pretty_print=options.pretty_print,
                show_contexts=options.show_contexts,
                **report_args)
        elif options.action == "lcov":
            total = self.coverage.lcov_report(outfile=options.outfile,
                                              **report_args)
        else:
            # There are no other possible actions.
            raise AssertionError

        if total is not None:
            # Apply the command line fail-under options, and then use the config
            # value, so we can get fail_under from the config file.
            if options.fail_under is not None:
                self.coverage.set_option("report:fail_under",
                                         options.fail_under)
            if options.precision is not None:
                self.coverage.set_option("report:precision", options.precision)

            fail_under = self.coverage.get_option("report:fail_under")
            precision = self.coverage.get_option("report:precision")
            if should_fail_under(total, fail_under, precision):
                msg = "total of {total} is less than fail-under={fail_under:.{p}f}".format(
                    total=Numbers(precision=precision).display_covered(total),
                    fail_under=fail_under,
                    p=precision,
                )
                print("Coverage failure:", msg)
                return FAIL_UNDER

        return OK

    def do_help(self, options, args, parser):
        """Deal with help requests.

        Return True if it handled the request, False if not.

        """
        # Handle help.
        if options.help:
            if self.global_option:
                show_help(topic='help')
            else:
                show_help(parser=parser)
            return True

        if options.action == "help":
            if args:
                for a in args:
                    parser = COMMANDS.get(a)
                    if parser:
                        show_help(parser=parser)
                    else:
                        show_help(topic=a)
            else:
                show_help(topic='help')
            return True

        # Handle version.
        if options.version:
            show_help(topic='version')
            return True

        return False

    def do_run(self, options, args):
        """Implementation of 'coverage run'."""

        if not args:
            if options.module:
                # Specified -m with nothing else.
                show_help("No module specified for -m")
                return ERR
            command_line = self.coverage.get_option("run:command_line")
            if command_line is not None:
                args = shlex.split(command_line)
                if args and args[0] in {"-m", "--module"}:
                    options.module = True
                    args = args[1:]
        if not args:
            show_help("Nothing to do.")
            return ERR

        if options.append and self.coverage.get_option("run:parallel"):
            show_help("Can't append to data files in parallel mode.")
            return ERR

        if options.concurrency == "multiprocessing":
            # Can't set other run-affecting command line options with
            # multiprocessing.
            for opt_name in [
                    'branch', 'include', 'omit', 'pylib', 'source', 'timid'
            ]:
                # As it happens, all of these options have no default, meaning
                # they will be None if they have not been specified.
                if getattr(options, opt_name) is not None:
                    show_help(
                        "Options affecting multiprocessing must only be specified "
                        + "in a configuration file.\n" +
                        f"Remove --{opt_name} from the command line.")
                    return ERR

        os.environ["COVERAGE_RUN"] = "true"

        runner = PyRunner(args, as_module=bool(options.module))
        runner.prepare()

        if options.append:
            self.coverage.load()

        # Run the script.
        self.coverage.start()
        code_ran = True
        try:
            runner.run()
        except NoSource:
            code_ran = False
            raise
        finally:
            self.coverage.stop()
            if code_ran:
                self.coverage.save()

        return OK

    def do_debug(self, args):
        """Implementation of 'coverage debug'."""

        if not args:
            show_help(
                "What information would you like: config, data, sys, premain, pybehave?"
            )
            return ERR
        if args[1:]:
            show_help("Only one topic at a time, please")
            return ERR

        if args[0] == "sys":
            write_formatted_info(print, "sys", self.coverage.sys_info())
        elif args[0] == "data":
            print(info_header("data"))
            data_file = self.coverage.config.data_file
            debug_data_file(data_file)
            for filename in combinable_files(data_file):
                print("-----")
                debug_data_file(filename)
        elif args[0] == "config":
            write_formatted_info(print, "config",
                                 self.coverage.config.debug_info())
        elif args[0] == "premain":
            print(info_header("premain"))
            print(short_stack())
        elif args[0] == "pybehave":
            write_formatted_info(print, "pybehave", env.debug_info())
        else:
            show_help(f"Don't know what you mean by {args[0]!r}")
            return ERR

        return OK
Beispiel #15
0
def main():
    options = parse_args()

    # TODO: move this export & download function in tools/ folder
    test_times_filename = options.export_past_test_times
    if test_times_filename:
        print(
            f'Exporting past test times from S3 to {test_times_filename}, no tests will be run.'
        )
        export_S3_test_times(test_times_filename)
        return

    specified_test_cases_filename = options.run_specified_test_cases
    if specified_test_cases_filename:
        print(
            f'Loading specified test cases to run from {specified_test_cases_filename}.'
        )
        global SPECIFIED_TEST_CASES_DICT
        SPECIFIED_TEST_CASES_DICT = get_specified_test_cases(
            specified_test_cases_filename, TESTS)

    test_directory = os.path.dirname(os.path.abspath(__file__))
    selected_tests = get_selected_tests(options)

    if options.verbose:
        print_to_stderr('Selected tests: {}'.format(', '.join(selected_tests)))

    if options.coverage and not PYTORCH_COLLECT_COVERAGE:
        shell(['coverage', 'erase'])

    if options.jit:
        selected_tests = filter(lambda test_name: "jit" in test_name, TESTS)

    if options.determine_from is not None and os.path.exists(
            options.determine_from):
        slow_tests = get_slow_tests_based_on_S3(TESTS, TARGET_DET_LIST,
                                                SLOW_TEST_THRESHOLD)
        print(
            'Added the following tests to target_det tests as calculated based on S3:'
        )
        print(slow_tests)
        with open(options.determine_from, 'r') as fh:
            touched_files = [
                os.path.normpath(name.strip())
                for name in fh.read().split('\n') if len(name.strip()) > 0
            ]
        # HACK: Ensure the 'test' paths can be traversed by Modulefinder
        sys.path.append('test')
        selected_tests = [
            test for test in selected_tests
            if determine_target(TARGET_DET_LIST +
                                slow_tests, test, touched_files, options)
        ]
        sys.path.remove('test')

    if IS_IN_CI:
        selected_tests = get_reordered_tests(selected_tests,
                                             ENABLE_PR_HISTORY_REORDERING)
        # downloading test cases configuration to local environment
        get_test_case_configs(
            dirpath=os.path.dirname(os.path.abspath(__file__)))

    has_failed = False
    failure_messages = []
    try:
        for test in selected_tests:
            options_clone = copy.deepcopy(options)
            if test in USE_PYTEST_LIST:
                options_clone.pytest = True
            err_message = run_test_module(test, test_directory, options_clone)
            if err_message is None:
                continue
            has_failed = True
            failure_messages.append(err_message)
            if not options_clone.continue_through_error:
                raise RuntimeError(err_message)
            print_to_stderr(err_message)
    finally:
        if options.coverage:
            from coverage import Coverage
            test_dir = os.path.dirname(os.path.abspath(__file__))
            with set_cwd(test_dir):
                cov = Coverage()
                if PYTORCH_COLLECT_COVERAGE:
                    cov.load()
                cov.combine(strict=False)
                cov.save()
                if not PYTORCH_COLLECT_COVERAGE:
                    cov.html_report()

    if options.continue_through_error and has_failed:
        for err in failure_messages:
            print_to_stderr(err)
        sys.exit(1)
Beispiel #16
0
class CoverageScript(object):
    """The command-line interface to coverage.py."""
    def __init__(self):
        self.global_option = False
        self.coverage = None

    def command_line(self, argv):
        """The bulk of the command line interface to coverage.py.

        `argv` is the argument list to process.

        Returns 0 if all is well, 1 if something went wrong.

        """
        # Collect the command-line options.
        if not argv:
            show_help(topic='minimum_help')
            return OK

        # The command syntax we parse depends on the first argument.  Global
        # switch syntax always starts with an option.
        self.global_option = argv[0].startswith('-')
        if self.global_option:
            parser = GlobalOptionParser()
        else:
            parser = CMDS.get(argv[0])
            if not parser:
                show_help("Unknown command: '%s'" % argv[0])
                return ERR
            argv = argv[1:]

        ok, options, args = parser.parse_args_ok(argv)
        if not ok:
            return ERR

        # Handle help and version.
        if self.do_help(options, args, parser):
            return OK

        # Listify the list options.
        source = unshell_list(options.source)
        omit = unshell_list(options.omit)
        include = unshell_list(options.include)
        debug = unshell_list(options.debug)
        contexts = unshell_list(options.contexts)

        # Do something.
        self.coverage = Coverage(
            data_suffix=options.parallel_mode,
            cover_pylib=options.pylib,
            timid=options.timid,
            branch=options.branch,
            config_file=options.rcfile,
            source=source,
            omit=omit,
            include=include,
            debug=debug,
            concurrency=options.concurrency,
            check_preimported=True,
            context=options.context,
        )

        if options.action == "debug":
            return self.do_debug(args)

        elif options.action == "erase":
            self.coverage.erase()
            return OK

        elif options.action == "run":
            return self.do_run(options, args)

        elif options.action == "combine":
            if options.append:
                self.coverage.load()
            data_dirs = args or None
            self.coverage.combine(data_dirs, strict=True)
            self.coverage.save()
            return OK

        # Remaining actions are reporting, with some common options.
        report_args = dict(
            morfs=unglob_args(args),
            ignore_errors=options.ignore_errors,
            omit=omit,
            include=include,
            contexts=contexts,
        )

        # We need to be able to import from the current directory, because
        # plugins may try to, for example, to read Django settings.
        sys.path.insert(0, '')

        self.coverage.load()

        total = None
        if options.action == "report":
            total = self.coverage.report(show_missing=options.show_missing,
                                         skip_covered=options.skip_covered,
                                         **report_args)
        elif options.action == "annotate":
            self.coverage.annotate(directory=options.directory, **report_args)
        elif options.action == "html":
            total = self.coverage.html_report(
                directory=options.directory,
                title=options.title,
                skip_covered=options.skip_covered,
                show_contexts=options.show_contexts,
                **report_args)
        elif options.action == "xml":
            outfile = options.outfile
            total = self.coverage.xml_report(outfile=outfile, **report_args)

        if total is not None:
            # Apply the command line fail-under options, and then use the config
            # value, so we can get fail_under from the config file.
            if options.fail_under is not None:
                self.coverage.set_option("report:fail_under",
                                         options.fail_under)

            fail_under = self.coverage.get_option("report:fail_under")
            precision = self.coverage.get_option("report:precision")
            if should_fail_under(total, fail_under, precision):
                return FAIL_UNDER

        return OK

    def do_help(self, options, args, parser):
        """Deal with help requests.

        Return True if it handled the request, False if not.

        """
        # Handle help.
        if options.help:
            if self.global_option:
                show_help(topic='help')
            else:
                show_help(parser=parser)
            return True

        if options.action == "help":
            if args:
                for a in args:
                    parser = CMDS.get(a)
                    if parser:
                        show_help(parser=parser)
                    else:
                        show_help(topic=a)
            else:
                show_help(topic='help')
            return True

        # Handle version.
        if options.version:
            show_help(topic='version')
            return True

        return False

    def do_run(self, options, args):
        """Implementation of 'coverage run'."""

        if not args:
            if options.module:
                # Specified -m with nothing else.
                show_help("No module specified for -m")
                return ERR
            command_line = self.coverage.get_option("run:command_line")
            if command_line is not None:
                args = shlex.split(command_line)
                if args and args[0] == "-m":
                    options.module = True
                    args = args[1:]
        if not args:
            show_help("Nothing to do.")
            return ERR

        if options.append and self.coverage.get_option("run:parallel"):
            show_help("Can't append to data files in parallel mode.")
            return ERR

        if options.concurrency == "multiprocessing":
            # Can't set other run-affecting command line options with
            # multiprocessing.
            for opt_name in [
                    'branch', 'include', 'omit', 'pylib', 'source', 'timid'
            ]:
                # As it happens, all of these options have no default, meaning
                # they will be None if they have not been specified.
                if getattr(options, opt_name) is not None:
                    show_help(
                        "Options affecting multiprocessing must only be specified "
                        "in a configuration file.\n"
                        "Remove --{} from the command line.".format(opt_name))
                    return ERR

        runner = PyRunner(args, as_module=bool(options.module))
        runner.prepare()

        if options.append:
            self.coverage.load()

        # Run the script.
        self.coverage.start()
        code_ran = True
        try:
            runner.run()
        except NoSource:
            code_ran = False
            raise
        finally:
            self.coverage.stop()
            if code_ran:
                self.coverage.save()

        return OK

    def do_debug(self, args):
        """Implementation of 'coverage debug'."""

        if not args:
            show_help("What information would you like: config, data, sys?")
            return ERR

        for info in args:
            if info == 'sys':
                sys_info = self.coverage.sys_info()
                print(info_header("sys"))
                for line in info_formatter(sys_info):
                    print(" %s" % line)
            elif info == 'data':
                self.coverage.load()
                data = self.coverage.get_data()
                print(info_header("data"))
                print("path: %s" % self.coverage.get_data().data_filename())
                if data:
                    print("has_arcs: %r" % data.has_arcs())
                    summary = line_counts(data, fullpath=True)
                    filenames = sorted(summary.keys())
                    print("\n%d files:" % len(filenames))
                    for f in filenames:
                        line = "%s: %d lines" % (f, summary[f])
                        plugin = data.file_tracer(f)
                        if plugin:
                            line += " [%s]" % plugin
                        print(line)
                else:
                    print("No data collected")
            elif info == 'config':
                print(info_header("config"))
                config_info = self.coverage.config.__dict__.items()
                for line in info_formatter(config_info):
                    print(" %s" % line)
            else:
                show_help("Don't know what you mean by %r" % info)
                return ERR

        return OK
Beispiel #17
0
class Testmon(object):
    coverage_stack = []

    def __init__(self, rootdir="", testmon_labels=None, cov_plugin=None):
        if testmon_labels is None:
            testmon_labels = {"singleprocess"}
        self.rootdir = rootdir
        self.testmon_labels = testmon_labels
        self.cov = None
        self.setup_coverage(not ("singleprocess" in testmon_labels), cov_plugin)

    def setup_coverage(self, subprocess, cov_plugin=None):
        params = {
            "include": [os.path.join(self.rootdir, "*")],
            "omit": _get_python_lib_paths(),
        }

        self.cov = Coverage(
            data_file=getattr(self, "sub_cov_file", None), config_file=False, **params
        )
        self.cov._warn_no_data = False

    def start(self):

        Testmon.coverage_stack.append(self.cov)
        self.cov.erase()
        self.cov.start()

    def stop(self):
        self.cov.stop()
        Testmon.coverage_stack.pop()

    def stop_and_save(self, testmon_data: TestmonData, nodeid, result):
        self.stop()
        if hasattr(self, "sub_cov_file"):
            self.cov.combine()
        measured_files = get_measured_relfiles(
            self.rootdir, self.cov, home_file(nodeid)
        )
        node_data = testmon_data.node_data_from_cov(measured_files)
        nodes_fingerprints = testmon_data.node_data2records(node_data)
        nodes_fingerprints.append(
            {
                "filename": LIBRARIES_KEY,
                "checksum": testmon_data.libraries,
                "mtime": None,
                "fingerprint": checksums_to_blob(
                    encode_lines([testmon_data.libraries])
                ),
            }
        )
        testmon_data.db.insert_node_fingerprints(
            nodeid,
            nodes_fingerprints,
            result,
        )

    def close(self):
        if hasattr(self, "sub_cov_file"):
            os.remove(self.sub_cov_file + "_rc")
        os.environ.pop("COVERAGE_PROCESS_START", None)
Beispiel #18
0
 def _validate_coverage(self):
     """Make sure there 100% coverage between the two test groups"""
     coverage = Coverage(config_file='coverage_tests/.coveragerc', )
     coverage.load()
     coverage.combine(strict=True)
     self.__assert(coverage.report() == 100.0, '100% coverage not achieved')
Beispiel #19
0
def main():
    options = parse_args()

    # TODO: move this export & download function in tools/ folder
    test_times_filename = options.export_past_test_times
    if test_times_filename:
        print(
            f"Exporting past test times from S3 to {test_times_filename}, no tests will be run."
        )
        export_S3_test_times(test_times_filename)
        return

    specified_test_cases_filename = options.run_specified_test_cases
    if specified_test_cases_filename:
        print(
            f"Loading specified test cases to run from {specified_test_cases_filename}."
        )
        global SPECIFIED_TEST_CASES_DICT
        SPECIFIED_TEST_CASES_DICT = get_specified_test_cases(
            specified_test_cases_filename, TESTS
        )

    test_directory = str(REPO_ROOT / "test")
    selected_tests = get_selected_tests(options)

    if options.verbose:
        print_to_stderr("Selected tests:\n {}".format("\n ".join(selected_tests)))

    if options.dry_run:
        return

    if options.coverage and not PYTORCH_COLLECT_COVERAGE:
        shell(["coverage", "erase"])

    # NS: Disable target determination until it can be made more reliable
    # if options.determine_from is not None and os.path.exists(options.determine_from):
    #     slow_tests = get_slow_tests_based_on_S3(
    #         TESTS, TARGET_DET_LIST, SLOW_TEST_THRESHOLD
    #     )
    #     print_to_stderr(
    #         "Added the following tests to target_det tests as calculated based on S3:"
    #     )
    #     print_to_stderr(slow_tests)
    #     with open(options.determine_from, "r") as fh:
    #         touched_files = [
    #             os.path.normpath(name.strip())
    #             for name in fh.read().split("\n")
    #             if len(name.strip()) > 0
    #         ]
    #     # HACK: Ensure the 'test' paths can be traversed by Modulefinder
    #     sys.path.append(test_directory)
    #     selected_tests = [
    #         test
    #         for test in selected_tests
    #         if should_run_test(
    #             TARGET_DET_LIST + slow_tests, test, touched_files, options
    #         )
    #     ]
    #     sys.path.remove(test_directory)

    if IS_IN_CI:
        selected_tests = get_reordered_tests(
            selected_tests, ENABLE_PR_HISTORY_REORDERING
        )
        # downloading test cases configuration to local environment
        get_test_case_configs(dirpath=test_directory)

    has_failed = False
    failure_messages = []
    try:
        for test in selected_tests:
            options_clone = copy.deepcopy(options)
            if test in USE_PYTEST_LIST:
                options_clone.pytest = True
            err_message = run_test_module(test, test_directory, options_clone)
            if err_message is None:
                continue
            has_failed = True
            failure_messages.append(err_message)
            if not options_clone.continue_through_error:
                raise RuntimeError(err_message)
            print_to_stderr(err_message)
    finally:
        if options.coverage:
            from coverage import Coverage

            with set_cwd(test_directory):
                cov = Coverage()
                if PYTORCH_COLLECT_COVERAGE:
                    cov.load()
                cov.combine(strict=False)
                cov.save()
                if not PYTORCH_COLLECT_COVERAGE:
                    cov.html_report()

    if options.continue_through_error and has_failed:
        for err in failure_messages:
            print_to_stderr(err)
        sys.exit(1)
Beispiel #20
0
def coverage_combine(data_files, output_path, source, process=None):
    """
    Merges multiples reports.

    @param      data_files                  report files (``.coverage``)
    @param      output_path                 output path
    @param      source                      source directory
    @param      process                     function which processes the coverage report
    @return                                 coverage report

    The function *process* should have the signature:

    ::

        def process(content):
            # ...
            return content

    On :epkg:`Windows`, file name have to have the right case.
    If not, coverage reports an empty coverage and raises an exception.
    """
    def raise_exc(exc, content, ex, ex2, outfile, destcov, source, dests,
                  inter, cov, infos):  # pragma: no cover
        def shorten(t):
            if len(t) > 2000:
                return t[:2000] + "\n..."
            else:
                return t

        if len(content) > 2000:
            content = content[:2000] + '\n...'
        ex = "\n-\n".join(shorten(_) for _ in ex)
        ex2 = "\n-\n".join(shorten(_) for _ in ex2)
        rows = [
            '-----------------', "destcov='{0}'".format(destcov),
            "outfile='{0}'".format(outfile), "source='{0}'".format(source),
            "cov.source={0}".format(get_source(cov)),
            "dests='{0}'".format(';'.join(dests)), "inter={0}".format(inter)
        ]
        for ii, info in enumerate(infos):
            rows.append('----------------- {}/{}'.format(ii, len(infos)))
            for k, v in sorted(info.items()):
                rows.append("{}='{}'".format(k, v))
        rows.append('-----------------')
        if cov is not None and _attr_(cov, '_data', 'data')._lines is not None:
            rows.append("##### LINES")
            end = min(5, len(_attr_(cov, '_data', 'data')._lines))
            for k, v in list(
                    sorted(_attr_(cov, '_data', 'data')._lines.items()))[:end]:
                rows.append('   {0}:{1}'.format(k, v))
            rows.append("----- RUNS")
            end = min(5, len(_attr_(cov, '_data', 'data')._runs))
            for k in _attr_(cov, '_data', 'data')._runs[:end]:
                rows.append('   {0}'.format(k))
            rows.append("----- END")

        mes = "{5}. In '{0}'.\n{1}\n{2}\n---AFTER---\n{3}\n---BEGIN---\n{4}"
        raise RuntimeError(
            mes.format(output_path, "\n".join(rows), content, ex, ex2, exc,
                       cov)) from exc

    # We copy the origin coverage if the report is produced
    # in a folder part of the merge.
    destcov = os.path.join(output_path, '.coverage')
    if os.path.exists(destcov):
        destcov2 = destcov + '_old'
        shutil.copy(destcov, destcov2)

    # Starts merging coverage.
    from coverage import Coverage
    cov = Coverage(data_file=destcov, source=[source])
    cov._init()
    cov.get_data()
    if get_source(cov) is None or len(get_source(cov)) == 0:
        raise_exc(
            FileNotFoundError("Probably unable to find '{0}'".format(source)),
            "", [], [], "", destcov, source, [], [], cov, [])

    inter = []

    def find_longest_common_root(names, begin):
        counts = {}
        for name in names:
            spl = name.split(begin)
            for i in range(1, len(spl) + 1):
                if spl[i - 1] == 'src':
                    break
                sub = begin.join(spl[:i])
                if sub in counts:
                    counts[sub] += 1
                else:
                    counts[sub] = 1
        item = max((v, k) for k, v in counts.items())
        return item[1]

    def copy_replace(source, dest, root_source, keep_infos):
        shutil.copy(source, dest)

        co = Counter(root_source)
        slash = co.get('/', 0) >= co.get('\\', 0)
        if slash:
            begin = "/"
            root_source_dup = root_source.replace('\\', '/').replace('//', '/')
        else:
            begin = "\\"
            root_source_dup = root_source.replace("\\", "\\\\")

        keep_infos["slash"] = slash
        keep_infos["begin"] = begin
        keep_infos["root_source_dup"] = root_source_dup
        keep_infos["root_source"] = root_source
        keep_infos["source"] = source
        keep_infos["dest"] = dest

        conn = sqlite3.connect(dest)
        sql = []
        names = []
        for row in conn.execute("select * from file"):
            names.append(row[1])
            name = row[1].replace('/', begin)
            if not name.startswith(root_source):
                name = root_source + begin + name
            s = "UPDATE file SET path='{}' WHERE id={};".format(name, row[0])
            sql.append(s)

        keep_infos['root_common'] = find_longest_common_root(names, begin)

        c = conn.cursor()
        for s in sql:
            c.execute(s)
        conn.commit()
        conn.close()

    # We modify the root in every coverage file.
    dests = [
        os.path.join(output_path, '.coverage{0}'.format(i))
        for i in range(len(data_files))
    ]
    infos = []
    for fi, de in zip(data_files, dests):
        keep_infos = {}
        copy_replace(fi, de, source, keep_infos)
        infos.append(keep_infos)
        shutil.copy(de, de + "~")

    # Keeping information (for exception).
    ex = []
    for d in dests:
        with open(d, "rb") as f:
            ex.append(f.read())
    ex2 = []
    for d in data_files:
        with open(d, "rb") as f:
            ex2.append(f.read())

    # We replace destcov by destcov2 if found in dests.
    if destcov in dests:
        ind = dests.index(destcov)
        dests[ind] = destcov2

    # Let's combine.
    cov.combine(dests)  # dest
    cov.save()
    report = True

    try:
        from coverage.exceptions import CoverageException
    except ImportError:
        # older version of coverage
        from coverage.misc import CoverageException
    try:
        from coverage.exceptions import NoSource
    except ImportError:
        # older version of coverage
        from coverage.misc import NoSource
    try:
        cov.html_report(directory=output_path, ignore_errors=True)
    except NoSource as e:
        raise_exc(e, "", ex, ex2, "", destcov, source, dests, inter, cov,
                  infos)
    except CoverageException as e:
        if "No data to report" in str(e):
            # issue with path
            report = False
        else:
            msg = pprint.pformat(infos)
            raise RuntimeError(  # pragma: no cover
                "Unable to process report in '{0}'.\n----\n{1}".format(
                    output_path, msg)) from e

    if report:
        outfile = os.path.join(output_path, "coverage_report.xml")
        cov.xml_report(outfile=outfile)
        cov.save()

        # Verifications
        with open(outfile, "r", encoding="utf-8") as f:
            content = f.read()
        if len(content) == 0:
            raise RuntimeError("No report was generated.")

    return cov
Beispiel #21
0
def coverage_combine(data_files, output_path, source, process=None, absolute_path=True):
    """
    Merges multiples reports.

    @param      data_files      report files (``.coverage``)
    @param      output_path     output path
    @param      source          source directory
    @param      process         function which processes the coverage report
    @param      absolute_path   relocate sources with absolute paths
    @return                     coverage report

    The function *process* should have the signature:

    ::

        def process(content):
            # ...
            return content
    """
    def raise_exc(exc, content, ex, ex2, outfile, destcov, source, dests, inter, cov):

        from coverage.data import CoverageData

        def shorten(t):
            if len(t) > 2000:
                return t[:2000] + "\n..."
            else:
                return t
        if len(content) > 2000:
            content = content[:2000] + '\n...'
        ex = "\n-\n".join(shorten(_) for _ in ex)
        ex2 = "\n-\n".join(shorten(_) for _ in ex2)
        rows = ["destcov='{0}'".format(destcov),
                "outfile='{0}'".format(outfile),
                "source='{0}'".format(source),
                "cov.source={0}".format(cov.source),
                "dests='{0}'".format(';'.join(dests)),
                "inter={0}".format(inter)]
        if cov is not None and cov.data is not None and cov.data._lines is not None:
            rows.append("----- LINES")
            end = min(5, len(cov.data._lines))
            for k, v in list(sorted(cov.data._lines.items()))[:end]:
                rows.append('   {0}:{1}'.format(k, v))
            rows.append("----- RUNS")
            end = min(5, len(cov.data._runs))
            for k in cov.data._runs[:end]:
                rows.append('   {0}'.format(k))
            rows.append("----- END")
        for d in dests:
            dd = CoverageData()
            dd.read_file(d + "~")
            rows.append("------- LINES - '{0}'".format(d))
            end = min(5, len(dd._lines))
            for k, v in list(sorted(dd._lines.items()))[:end]:
                rows.append('   {0}:{1}'.format(k, v))
            rows.append("------- RUNS - '{0}'".format(d))
            end = min(5, len(dd._runs))
            for k in dd._runs[:end]:
                rows.append('   {0}'.format(k))
            rows.append("------- END")

        mes = "{5}. In '{0}'.\n{1}\n{2}\n---AFTER---\n{3}\n---BEGIN---\n{4}"
        raise RuntimeError(mes.format(output_path, "\n".join(
            rows), content, ex, ex2, exc, cov)) from exc

    # We copy the origin coverage if the report is produced
    # in a folder part of the merge.
    destcov = os.path.join(output_path, '.coverage')
    if os.path.exists(destcov):
        destcov2 = destcov + '_old'
        shutil.copy(destcov, destcov2)

    # Starts merging coverage.
    from coverage import Coverage
    cov = Coverage(data_file=destcov, source=[source])
    # Module coverage may modify the folder,
    # we take the one it considers.
    # On Windows, it has to have the right case.
    # If not, coverage reports an empty coverage and
    # raises an exception.
    cov._init()
    cov.get_data()
    if cov.source is None or len(cov.source) == 0:
        raise_exc(FileNotFoundError("Probably unable to find '{0}'".format(source)),
                  "", [], [], "", destcov, source, [], [], cov)
    source = cov.source[0]

    inter = []
    reg = re.compile(',\\"(.*?[.]py)\\"')

    def copy_replace(source, dest, root_source):
        with open(source, "r") as f:
            content = f.read()
        if process is not None:
            content = process(content)
        cf = reg.findall(content)
        co = Counter([_.split('src')[0] for _ in cf])
        mx = max((v, k) for k, v in co.items())
        root = mx[1].rstrip('\\/')
        if absolute_path:
            if '\\\\' in root:
                s2 = root_source.replace('\\', '\\\\').replace('/', '\\\\')
                s2 += "\\\\"
                root += "\\\\"
            elif '\\' in root:
                s2 = root_source
                s2 += "\\\\"
                root += "\\"
            else:
                s2 = root_source
                s2 += "/"
                root += "/"
        else:
            s2 = ""
            if '\\\\' in root:
                root += "\\\\"
            elif '\\' in root:
                root += "\\"
            else:
                root += "/"
        inter.append((root, root_source, s2))
        content = content.replace(root, s2)
        with open(dest, "w") as f:
            f.write(content)

    # We modify the root in every coverage file.
    dests = [os.path.join(output_path, '.coverage{0}'.format(
        i)) for i in range(len(data_files))]
    for fi, de in zip(data_files, dests):
        copy_replace(fi, de, source)
        shutil.copy(de, de + "~")

    # Keeping information (for exception).
    ex = []
    for d in dests:
        with open(d, "r") as f:
            ex.append(f.read())
    ex2 = []
    for d in data_files:
        with open(d, "r") as f:
            ex2.append(f.read())

    # We replace destcov by destcov2 if found in dests.
    if destcov in dests:
        ind = dests.index(destcov)
        dests[ind] = destcov2

    # Let's combine.
    cov.combine(dests)

    from coverage.misc import NoSource
    try:
        cov.html_report(directory=output_path)
    except NoSource as e:
        raise_exc(e, "", ex, ex2, "", destcov, source, dests, inter, cov)

    outfile = os.path.join(output_path, "coverage_report.xml")
    cov.xml_report(outfile=outfile)
    cov.save()

    # Verifications
    with open(outfile, "r", encoding="utf-8") as f:
        content = f.read()

    if 'line hits="1"' not in content:
        raise_exc(Exception("Coverage is empty"), content, ex,
                  ex2, outfile, destcov, source, dests, inter, cov)

    return cov
Beispiel #22
0
class CoverageScript(object):
    """The command-line interface to coverage.py."""

    def __init__(self):
        self.global_option = False
        self.coverage = None

    def command_line(self, argv):
        """The bulk of the command line interface to coverage.py.

        `argv` is the argument list to process.

        Returns 0 if all is well, 1 if something went wrong.

        """
        # Collect the command-line options.
        if not argv:
            show_help(topic='minimum_help')
            return OK

        # The command syntax we parse depends on the first argument.  Global
        # switch syntax always starts with an option.
        self.global_option = argv[0].startswith('-')
        if self.global_option:
            parser = GlobalOptionParser()
        else:
            parser = CMDS.get(argv[0])
            if not parser:
                show_help("Unknown command: '%s'" % argv[0])
                return ERR
            argv = argv[1:]

        ok, options, args = parser.parse_args_ok(argv)
        if not ok:
            return ERR

        # Handle help and version.
        if self.do_help(options, args, parser):
            return OK

        # Listify the list options.
        source = unshell_list(options.source)
        omit = unshell_list(options.omit)
        include = unshell_list(options.include)
        debug = unshell_list(options.debug)

        # Do something.
        self.coverage = Coverage(
            data_suffix=options.parallel_mode,
            cover_pylib=options.pylib,
            timid=options.timid,
            branch=options.branch,
            config_file=options.rcfile,
            source=source,
            omit=omit,
            include=include,
            debug=debug,
            concurrency=options.concurrency,
            check_preimported=True,
            context=options.context,
            )

        if options.action == "debug":
            return self.do_debug(args)

        elif options.action == "erase":
            self.coverage.erase()
            return OK

        elif options.action == "run":
            return self.do_run(options, args)

        elif options.action == "combine":
            if options.append:
                self.coverage.load()
            data_dirs = args or None
            self.coverage.combine(data_dirs, strict=True)
            self.coverage.save()
            return OK

        # Remaining actions are reporting, with some common options.
        report_args = dict(
            morfs=unglob_args(args),
            ignore_errors=options.ignore_errors,
            omit=omit,
            include=include,
            )

        # We need to be able to import from the current directory, because
        # plugins may try to, for example, to read Django settings.
        sys.path.insert(0, '')

        self.coverage.load()

        total = None
        if options.action == "report":
            total = self.coverage.report(
                show_missing=options.show_missing,
                skip_covered=options.skip_covered,
                **report_args
                )
        elif options.action == "annotate":
            self.coverage.annotate(directory=options.directory, **report_args)
        elif options.action == "html":
            total = self.coverage.html_report(
                directory=options.directory,
                title=options.title,
                skip_covered=options.skip_covered,
                **report_args
                )
        elif options.action == "xml":
            outfile = options.outfile
            total = self.coverage.xml_report(outfile=outfile, **report_args)

        if total is not None:
            # Apply the command line fail-under options, and then use the config
            # value, so we can get fail_under from the config file.
            if options.fail_under is not None:
                self.coverage.set_option("report:fail_under", options.fail_under)

            fail_under = self.coverage.get_option("report:fail_under")
            precision = self.coverage.get_option("report:precision")
            if should_fail_under(total, fail_under, precision):
                return FAIL_UNDER

        return OK

    def do_help(self, options, args, parser):
        """Deal with help requests.

        Return True if it handled the request, False if not.

        """
        # Handle help.
        if options.help:
            if self.global_option:
                show_help(topic='help')
            else:
                show_help(parser=parser)
            return True

        if options.action == "help":
            if args:
                for a in args:
                    parser = CMDS.get(a)
                    if parser:
                        show_help(parser=parser)
                    else:
                        show_help(topic=a)
            else:
                show_help(topic='help')
            return True

        # Handle version.
        if options.version:
            show_help(topic='version')
            return True

        return False

    def do_run(self, options, args):
        """Implementation of 'coverage run'."""

        if not args:
            if options.module:
                # Specified -m with nothing else.
                show_help("No module specified for -m")
                return ERR
            command_line = self.coverage.get_option("run:command_line")
            if command_line is not None:
                args = shlex.split(command_line)
                if args and args[0] == "-m":
                    options.module = True
                    args = args[1:]
        if not args:
            show_help("Nothing to do.")
            return ERR

        if options.append and self.coverage.get_option("run:parallel"):
            show_help("Can't append to data files in parallel mode.")
            return ERR

        if options.concurrency == "multiprocessing":
            # Can't set other run-affecting command line options with
            # multiprocessing.
            for opt_name in ['branch', 'include', 'omit', 'pylib', 'source', 'timid']:
                # As it happens, all of these options have no default, meaning
                # they will be None if they have not been specified.
                if getattr(options, opt_name) is not None:
                    show_help(
                        "Options affecting multiprocessing must only be specified "
                        "in a configuration file.\n"
                        "Remove --{} from the command line.".format(opt_name)
                    )
                    return ERR

        runner = PyRunner(args, as_module=bool(options.module))
        runner.prepare()

        if options.append:
            self.coverage.load()

        # Run the script.
        self.coverage.start()
        code_ran = True
        try:
            runner.run()
        except NoSource:
            code_ran = False
            raise
        finally:
            self.coverage.stop()
            if code_ran:
                self.coverage.save()

        return OK

    def do_debug(self, args):
        """Implementation of 'coverage debug'."""

        if not args:
            show_help("What information would you like: config, data, sys?")
            return ERR

        for info in args:
            if info == 'sys':
                sys_info = self.coverage.sys_info()
                print(info_header("sys"))
                for line in info_formatter(sys_info):
                    print(" %s" % line)
            elif info == 'data':
                self.coverage.load()
                data = self.coverage.get_data()
                print(info_header("data"))
                print("path: %s" % self.coverage.get_data().data_filename())
                if data:
                    print("has_arcs: %r" % data.has_arcs())
                    summary = line_counts(data, fullpath=True)
                    filenames = sorted(summary.keys())
                    print("\n%d files:" % len(filenames))
                    for f in filenames:
                        line = "%s: %d lines" % (f, summary[f])
                        plugin = data.file_tracer(f)
                        if plugin:
                            line += " [%s]" % plugin
                        print(line)
                else:
                    print("No data collected")
            elif info == 'config':
                print(info_header("config"))
                config_info = self.coverage.config.__dict__.items()
                for line in info_formatter(config_info):
                    print(" %s" % line)
            else:
                show_help("Don't know what you mean by %r" % info)
                return ERR

        return OK