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)
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)
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)
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)
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)
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) )
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)
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
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)
# 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)
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))
# 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)
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
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)
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
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)
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')
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)
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
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
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