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

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

    if report == "all":
        with open("%s/coverage.txt" % reports_dir, "w") as cov_rep:
            coverage_obj.report(file=cov_rep, show_missing=True)
            print("Generated Code-Coverage text report at %s/coverage.txt\n" %
                  reports_dir)
        coverage_obj.html_report(directory=reports_dir)
        print("Generated Code-Coverage HTML report at %s\n" % reports_dir)
        coverage_obj.xml_report(outfile='%s/coverage.xml' % reports_dir)
        print("Generated Code-Coverage XML report at %s/coverage.xml\n" %
              reports_dir)
    elif report == "text":
        with open("%s/coverage.txt" % reports_dir, "w") as cov_rep:
            coverage_obj.report(file=cov_rep, show_missing=True)
            print("Generated Code-Coverage text report at %s/coverage.txt\n" %
                  reports_dir)
    elif report == "html":
        coverage_obj.html_report(directory=reports_dir)
        print("Generated Code-Coverage HTML report at %s\n" % reports_dir)
    elif report == "xml":
        coverage_obj.xml_report(outfile='%s/coverage.xml' % reports_dir)
        print("Generated Code-Coverage XML report at %s/coverage.xml\n" %
              reports_dir)
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'))
Exemple #5
0
    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()
Exemple #6
0
    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()
Exemple #7
0
    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)
Exemple #8
0
def coverage(packages,
             output_stream=sys.stdout,
             coverage_file=None,
             xml_enabled=None,
             xml_file=None):
    """
    Context manager that activates coverage on the specified packages.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            total_percentage = int(match.groups()[0].split()[-1][:-1])
            if total_percentage < int(world.config.cover_min_percentage):
                raise RadishError('Failed to reach minimum expected coverage of {0}% (reached: {1}%)'.format(
                    world.config.cover_min_percentage, total_percentage))
Exemple #12
0
    # 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)
Exemple #13
0
# usage:
#   run_tests.py [PYTEST_ARGS]

import os
import subprocess
import sys

from coverage import Coverage


SHELL = sys.platform == 'win32'


if __name__ == '__main__':
    command = ['py.test']
    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)
Exemple #14
0
class Coverage:
    """
    Extension for Python Code Coverage
    """

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

import os
import subprocess
import sys

from coverage import Coverage

SHELL = sys.platform == 'win32'

if __name__ == '__main__':
    command = ['py.test', '-n', 'auto']
    if os.environ.get('WITH_COVERAGE') == '1':
        command.extend(['--cov=rinoh', '--cov-report='])
    if os.environ.get('BASETEMP'):
        command.append('--basetemp={}'.format(os.environ['BASETEMP']))
    command.extend(sys.argv[1:])
    rc = subprocess.call(command, shell=SHELL)
    if os.environ.get('WITH_COVERAGE') == '1':
        cov = Coverage()
        cov.load()
        cov.combine()
        cov.report(skip_covered=True)
        cov.xml_report()
    raise SystemExit(rc)
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

Exemple #17
0
def coverage_combine(data_files, output_path, source, process=None):
    """
    Merges multiples reports.

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

    The function *process* should have the signature:

    ::

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

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

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

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

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

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

    inter = []

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return cov
Exemple #18
0
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"]))
Exemple #19
0
class CoverageScript(object):
    """The command-line interface to coverage.py."""
    def __init__(self):
        self.global_option = False
        self.coverage = None

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

        `argv` is the argument list to process.

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.coverage.load()

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

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

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

        return OK

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

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

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

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

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

        return False

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

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

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

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

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

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

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

        return OK

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

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

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

        return OK
Exemple #20
0
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)
Exemple #21
0
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
Exemple #22
0
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()
Exemple #23
0
class CoverageScript:
    """The command-line interface to coverage.py."""
    def __init__(self):
        self.global_option = False
        self.coverage = None

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

        `argv` is the argument list to process.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.coverage.load()

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

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

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

        return OK

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

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

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

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

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

        return False

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

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

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

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

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

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

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

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

        return OK

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

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

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

        return OK
Exemple #24
0
def coverage_combine(data_files, output_path, source, process=None, absolute_path=True):
    """
    Merges multiples reports.

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

    The function *process* should have the signature:

    ::

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

        from coverage.data import CoverageData

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        `argv` is the argument list to process.

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.coverage.load()

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

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

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

        return OK

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

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

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

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

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

        return False

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

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

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

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

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

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

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

        return OK

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

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

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

        return OK