def run_tests(self): from illume import config from coverage import Coverage config.setenv("test") from pytest import main from illume.util import remove_or_ignore_dir from logging import basicConfig, DEBUG basicConfig(level=DEBUG, filename="illume-test.log") project_root = config.get("PROJECT_ROOT") data_dir = config.get("DATA_DIR") cov_config_dir = os.path.join(project_root, '.coveagerc') cov = Coverage(config_file=cov_config_dir) # Remvoe data directory in case tests failed to complete last time. remove_or_ignore_dir(data_dir) cov.start() exit_code = main(shlex.split(self.pytest_args or "")) cov.stop() cov.xml_report() # Remove data directory if tests passed successfully. Keep it around # if tests failed so the developer can troubleshoot the problem. if exit_code == 0: remove_or_ignore_dir(data_dir) sys.exit(exit_code)
class CodeCoverage(): def __init__(self, with_coverage, app): self.with_coverage = with_coverage self.app = app or 'frappe' def __enter__(self): if self.with_coverage: import os from coverage import Coverage from frappe.utils import get_bench_path # Generate coverage report only for app that is being tested source_path = os.path.join(get_bench_path(), 'apps', self.app) omit = STANDARD_EXCLUSIONS[:] if self.app == 'frappe': omit.extend(FRAPPE_EXCLUSIONS) self.coverage = Coverage(source=[source_path], omit=omit, include=STANDARD_INCLUSIONS) self.coverage.start() def __exit__(self, exc_type, exc_value, traceback): if self.with_coverage: self.coverage.stop() self.coverage.save() self.coverage.xml_report()
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 coverage_analysis(): cov = Coverage() cov.start() try: yield finally: cov.stop() cov.report() print("------ SAVING COVERAGE REPORTS ------ ") cov.xml_report(outfile=os.path.join(".", 'cobertura.xml'))
def run(self): from coverage import Coverage cov = Coverage(source=self.distribution.packages) cov.start() super().run() cov.stop() cov.xml_report() cov.html_report()
def handle(self, *args, **kwargs): # pragma: no cover cov = Coverage() cov.erase() cov.start() super().handle(*args, **kwargs) cov.stop() cov.save() covered = cov.report() cov.xml_report() if covered < 100: sys.exit(1)
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)
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))
# Discover all tests within the 'tests' directory testCase = defaultTestLoader.discover('./tests', 'test*.py') # Add each test case into the test suite for case in testCase: testSuite.addTests(case) # Initialize XML generating test runner xmlRunner = XMLTestRunner(output='./reports') # Start coverage analyzer coverageAnalyzer = Coverage() # Run all tests within test suite coverageAnalyzer.start() xmlRunner.run(testSuite) coverageAnalyzer.stop() # Save coverage reports coverageAnalyzer.save() coverageAnalyzer.xml_report() linterArgs = list() for relPath, dirContents, fileList in walk('.'): for file in fileList: if file.endswith('.py'): linterArgs.append(path.join(relPath, file)) resultsLint = lint.Run(linterArgs) if resultsLint.linter.stats['global_note'] < 4.0: sys.exit(1)
# 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)
paths = [] for path in sys.path: try: paths.append(str(Path(path).resolve()).lower() + os.path.sep) except FileNotFoundError: pass paths = tuple(sorted(paths, key=len, reverse=True)) unix_paths = tuple(p.replace('\\', '/') for p in paths) packages = tuple(p.replace('/', '.') for p in unix_paths) dst = Path('coverage.xml') # Generate `coverage.xml` with absolute file and package names. cov = Coverage() cov.load() cov.xml_report(outfile=str(dst)) # Load the report, remove the largest prefix in `packages` from attribute # `name` of element `package`, if any, and similarly the largest prefix in # `paths` from attribute `filename` of element `class` and from the content of # element `source`. Matching prefixes is case insensitive for case insensitive # file systems. def remove_prefix(value, prefixes): lvalue = value.lower() for prefix in prefixes: if lvalue.startswith(prefix): return value[len(prefix):] return value
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 run_test_suite(args): skip_utc = args.skip_utc enable_coverage = not args.no_coverage enable_pep8 = not args.no_pep8 if enable_coverage: cov = Coverage(config_file=True) cov.erase() cov.start() settings.configure( DJBRAINTREE_TESTS_SKIP_UTC=skip_utc, TIME_ZONE='America/Los_Angeles', DEBUG=True, USE_TZ=True, DATABASES={ "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": "djbraintree", "USER": "", "PASSWORD": "", "HOST": "", "PORT": "", }, }, ROOT_URLCONF="tests.test_urls", INSTALLED_APPS=[ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "jsonfield", "djbraintree", "tests", "tests.apps.testapp" ], MIDDLEWARE_CLASSES=( "django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware"), SITE_ID=1, BRAINTREE_PUBLIC_KEY=os.environ.get("BRAINTREE_PUBLIC_KEY", ""), BRAINTREE_PRIVATE_KEY=os.environ.get("BRAINTREE_PRIVATE_KEY", ""), BRAINTREE_MERCHANT_ID=os.environ.get("BRAINTREE_MERCHANT_ID", ""), DJBRAINTREE_PLANS={ "test0": { "braintree_plan_id": "test_id_0", "name": "Test Plan 0", "description": "A test plan", "price": 1000, # $10.00 "currency": "usd", "interval": "month" }, "test": { "braintree_plan_id": "test_id", "name": "Test Plan 1", "description": "Another test plan", "price": 2500, # $25.00 "currency": "usd", "interval": "month" }, "test2": { "braintree_plan_id": "test_id_2", "name": "Test Plan 2", "description": "Yet Another test plan", "price": 5000, # $50.00 "currency": "usd", "interval": "month" }, "test_deletion": { "braintree_plan_id": "test_id_3", "name": "Test Plan 3", "description": "Test plan for deletion.", "price": 5000, # $50.00 "currency": "usd", "interval": "month" }, "test_trial": { "braintree_plan_id": "test_id_4", "name": "Test Plan 4", "description": "Test plan for trails.", "price": 7000, # $70.00 "currency": "usd", "interval": "month", "trial_period_days": 7 }, "unidentified_test_plan": { "name": "Unidentified Test Plan", "description": "A test plan with no ID.", "price": 2500, # $25.00 "currency": "usd", "interval": "month" } }, DJBRAINTREE_PLAN_HIERARCHY={ "bronze": { "level": 1, "plans": [ "test0", "test", ] }, "silver": { "level": 2, "plans": [ "test2", "test_deletion", ] }, "gold": { "level": 3, "plans": [ "test_trial", "unidentified_test_plan", ] }, }, DJBRAINTREE_SUBSCRIPTION_REQUIRED_EXCEPTION_URLS=( "(admin)", "test_url_name", "testapp_namespaced:test_url_namespaced", "fn:/test_fnmatch*"), ) # Avoid AppRegistryNotReady exception # http://stackoverflow.com/questions/24793351/django-appregistrynotready if hasattr(django, "setup"): django.setup() # Announce the test suite sys.stdout.write( colored(text="\nWelcome to the ", color="magenta", attrs=["bold"])) sys.stdout.write( colored(text="dj-braintree", color="green", attrs=["bold"])) sys.stdout.write( colored(text=" test suite.\n\n", color="magenta", attrs=["bold"])) # Announce test run sys.stdout.write( colored(text="Step 1: Running unit tests.\n\n", color="yellow", attrs=["bold"])) # Hack to reset the global argv before nose has a chance to grab it # http://stackoverflow.com/a/1718407/1834570 args = sys.argv[1:] sys.argv = sys.argv[0:1] from django_nose import NoseTestSuiteRunner test_runner = NoseTestSuiteRunner(verbosity=1) failures = test_runner.run_tests(["."]) if failures: sys.exit(failures) if enable_coverage: # Announce coverage run sys.stdout.write( colored(text="\nStep 2: Generating coverage results.\n\n", color="yellow", attrs=["bold"])) cov.stop() percentage = round(cov.report(show_missing=True), 2) cov.html_report(directory='cover') cov.save() cov.xml_report() if percentage < TESTS_THRESHOLD: sys.stderr.write( colored( text="YOUR CHANGES HAVE CAUSED TEST COVERAGE TO DROP. " + "WAS {old}%, IS NOW {new}%.\n\n".format( old=TESTS_THRESHOLD, new=percentage), color="red", attrs=["bold"])) sys.exit(1) else: # Announce disabled coverage run sys.stdout.write( colored(text="\nStep 2: Generating coverage results [SKIPPED].", color="yellow", attrs=["bold"])) if enable_pep8: # Announce flake8 run sys.stdout.write( colored(text="\nStep 3: Checking for pep8 errors.\n\n", color="yellow", attrs=["bold"])) print("pep8 errors:") print( "----------------------------------------------------------------------" ) from subprocess import call flake_result = call(["flake8", ".", "--count"]) if flake_result != 0: sys.stderr.write("pep8 errors detected.\n") sys.stderr.write( colored(text="\nYOUR CHANGES HAVE INTRODUCED PEP8 ERRORS!\n\n", color="red", attrs=["bold"])) sys.exit(flake_result) else: print("None") else: # Announce disabled coverage run sys.stdout.write( colored(text="\nStep 3: Checking for pep8 errors [SKIPPED].\n", color="yellow", attrs=["bold"])) # Announce success if enable_coverage and enable_pep8: sys.stdout.write( colored( text= "\nTests completed successfully with no errors. Congrats!\n", color="green", attrs=["bold"])) else: sys.stdout.write( colored( text= "\nTests completed successfully, but some step(s) were skipped!\n", color="green", attrs=["bold"])) sys.stdout.write( colored(text="Don't push without running the skipped step(s).\n", color="red", attrs=["bold"]))
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
if __name__ == "__main__": try: # multiprocessing.log_to_stderr(logging.DEBUG) tasks = multiprocessing.JoinableQueue() results = multiprocessing.Queue() num_consumers = int(os.environ.get("NUM_WORKERS", 6)) consumers = [NoseWorker(tasks, results) for i in range(num_consumers)] for w in consumers: w.start() num_jobs = 0 for job in generate_tasks_input(): if job[1]: num_consumers -= 1 # take into account exclusive jobs tasks.put(NoseTask(job)) num_jobs += 1 for i in range(num_consumers): tasks.put(None) tasks.join() while num_jobs: result = results.get() num_jobs -= 1 finally: cov.stop() cov.combine() cov.xml_report(outfile="coverage.xml", ignore_errors=True)
def main(argv): """Main program.""" # Environment if sys.version_info < (2, 7): stderr('%s: need Python 2.7 or later' % argv[0]) stderr('your python is %s' % sys.version) return 1 # Defaults cfg = Options() cfg.basedir = os.path.join(os.path.dirname(argv[0]), 'src') cfg.basedir = os.path.abspath(cfg.basedir) # Figure out terminal size try: import curses except ImportError: pass else: try: curses.setupterm() cols = curses.tigetnum('cols') if cols > 0: cfg.screen_width = cols except (curses.error, TypeError): # tigetnum() is broken in PyPy3 and raises TypeError pass # Option processing opts, args = getopt.gnu_getopt(argv[1:], 'hvpqufw', [ 'list-files', 'list-tests', 'list-hooks', 'level=', 'all-levels', 'coverage' ]) for k, v in opts: if k == '-h': print(__doc__) return 0 elif k == '-v': cfg.verbosity += 1 cfg.quiet = False elif k == '-p': cfg.progress = True cfg.quiet = False elif k == '-q': cfg.verbosity = 0 cfg.progress = False cfg.quiet = True elif k == '-u': cfg.unit_tests = True elif k == '-f': cfg.functional_tests = True elif k == '-w': cfg.warn_omitted = True elif k == '--list-files': cfg.list_files = True cfg.run_tests = False elif k == '--list-tests': cfg.list_tests = True cfg.run_tests = False elif k == '--list-hooks': cfg.list_hooks = True cfg.run_tests = False elif k == '--coverage': cfg.coverage = True elif k == '--level': try: cfg.level = int(v) except ValueError: stderr('%s: invalid level: %s' % (argv[0], v)) stderr('run %s -h for help') return 1 elif k == '--all-levels': cfg.level = None else: stderr('%s: invalid option: %s' % (argv[0], k)) stderr('run %s -h for help') return 1 if args: cfg.pathname_regex = args[0] if len(args) > 1: cfg.test_regex = args[1] if len(args) > 2: stderr('%s: too many arguments: %s' % (argv[0], args[2])) stderr('run %s -h for help') return 1 if not cfg.unit_tests and not cfg.functional_tests: cfg.unit_tests = True # Set up the python path sys.path[0] = cfg.basedir # Set up tracing before we start importing things cov = None if cfg.run_tests and cfg.coverage: from coverage import Coverage cov = Coverage(omit=['test.py']) # Finding and importing test_files = get_test_files(cfg) if cov is not None: cov.start() if cfg.list_tests or cfg.run_tests: test_cases = get_test_cases(test_files, cfg, cov=cov) if cfg.list_hooks or cfg.run_tests: test_hooks = get_test_hooks(test_files, cfg, cov=cov) # Configure the logging module import logging logging.basicConfig() logging.root.setLevel(logging.CRITICAL) # Running success = True if cfg.list_files: baselen = len(cfg.basedir) + 1 print("\n".join([fn[baselen:] for fn in test_files])) if cfg.list_tests: print("\n".join([test.id() for test in test_cases])) if cfg.list_hooks: print("\n".join([str(hook) for hook in test_hooks])) if cfg.run_tests: runner = CustomTestRunner(cfg, test_hooks) suite = unittest.TestSuite() suite.addTests(test_cases) if cov is not None: cov.start() run_result = runner.run(suite) if cov is not None: cov.stop() success = run_result.wasSuccessful() del run_result if cov is not None: traced_file_types = ('.py', '.pyx', '.pxi', '.pxd') modules = [] def add_file(_, path, files): if 'tests' in os.path.relpath(path, cfg.basedir).split(os.sep): return for filename in files: if filename.endswith(traced_file_types): modules.append(os.path.join(path, filename)) if cfg.follow_symlinks: walker = walk_with_symlinks else: walker = os.path.walk walker(os.path.abspath(cfg.basedir), add_file, None) try: cov.xml_report(modules, outfile='coverage.xml') if cfg.coverdir: cov.html_report(modules, directory=cfg.coverdir) finally: # test runs can take a while, so at least try to print something cov.report() # That's all if success: return 0 else: return 1
if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sequoia.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?") from exc # Code coverage is handled here because of a bug with Django and nose that # hasn't been fixed after years. IS_TESTING = 'test' in sys.argv if IS_TESTING: from coverage import Coverage COV = Coverage() COV.erase() COV.start() execute_from_command_line(sys.argv) if IS_TESTING: COV.stop() COV.save() COV.report() COV.html_report(directory='htmlcov') COV.xml_report()
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 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