forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			395 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			395 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# coding: utf-8
 | 
						|
# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
 | 
						|
 | 
						|
"""Helper for building, testing, and linting coverage.py.
 | 
						|
 | 
						|
To get portability, all these operations are written in Python here instead
 | 
						|
of in shell scripts, batch files, or Makefiles.
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
import contextlib
 | 
						|
import fnmatch
 | 
						|
import glob
 | 
						|
import inspect
 | 
						|
import os
 | 
						|
import platform
 | 
						|
import sys
 | 
						|
import textwrap
 | 
						|
import warnings
 | 
						|
import zipfile
 | 
						|
 | 
						|
import pytest
 | 
						|
 | 
						|
 | 
						|
@contextlib.contextmanager
 | 
						|
def ignore_warnings():
 | 
						|
    """Context manager to ignore warning within the with statement."""
 | 
						|
    with warnings.catch_warnings():
 | 
						|
        warnings.simplefilter("ignore")
 | 
						|
        yield
 | 
						|
 | 
						|
 | 
						|
# Functions named do_* are executable from the command line: do_blah is run
 | 
						|
# by "python igor.py blah".
 | 
						|
 | 
						|
 | 
						|
def do_show_env():
 | 
						|
    """Show the environment variables."""
 | 
						|
    print("Environment:")
 | 
						|
    for env in sorted(os.environ):
 | 
						|
        print("  %s = %r" % (env, os.environ[env]))
 | 
						|
 | 
						|
 | 
						|
def do_remove_extension():
 | 
						|
    """Remove the compiled C extension, no matter what its name."""
 | 
						|
 | 
						|
    so_patterns = """
 | 
						|
        tracer.so
 | 
						|
        tracer.*.so
 | 
						|
        tracer.pyd
 | 
						|
        tracer.*.pyd
 | 
						|
        """.split()
 | 
						|
 | 
						|
    for pattern in so_patterns:
 | 
						|
        pattern = os.path.join("coverage", pattern)
 | 
						|
        for filename in glob.glob(pattern):
 | 
						|
            try:
 | 
						|
                os.remove(filename)
 | 
						|
            except OSError:
 | 
						|
                pass
 | 
						|
 | 
						|
 | 
						|
def label_for_tracer(tracer):
 | 
						|
    """Get the label for these tests."""
 | 
						|
    if tracer == "py":
 | 
						|
        label = "with Python tracer"
 | 
						|
    else:
 | 
						|
        label = "with C tracer"
 | 
						|
 | 
						|
    return label
 | 
						|
 | 
						|
 | 
						|
def should_skip(tracer):
 | 
						|
    """Is there a reason to skip these tests?"""
 | 
						|
    if tracer == "py":
 | 
						|
        # $set_env.py: COVERAGE_NO_PYTRACER - Don't run the tests under the Python tracer.
 | 
						|
        skipper = os.environ.get("COVERAGE_NO_PYTRACER")
 | 
						|
    else:
 | 
						|
        # $set_env.py: COVERAGE_NO_CTRACER - Don't run the tests under the C tracer.
 | 
						|
        skipper = os.environ.get("COVERAGE_NO_CTRACER")
 | 
						|
 | 
						|
    if skipper:
 | 
						|
        msg = "Skipping tests " + label_for_tracer(tracer)
 | 
						|
        if len(skipper) > 1:
 | 
						|
            msg += ": " + skipper
 | 
						|
    else:
 | 
						|
        msg = ""
 | 
						|
 | 
						|
    return msg
 | 
						|
 | 
						|
 | 
						|
def make_env_id(tracer):
 | 
						|
    """An environment id that will keep all the test runs distinct."""
 | 
						|
    impl = platform.python_implementation().lower()
 | 
						|
    version = "%s%s" % sys.version_info[:2]
 | 
						|
    if '__pypy__' in sys.builtin_module_names:
 | 
						|
        version += "_%s%s" % sys.pypy_version_info[:2]
 | 
						|
    env_id = "%s%s_%s" % (impl, version, tracer)
 | 
						|
    return env_id
 | 
						|
 | 
						|
 | 
						|
def run_tests(tracer, *runner_args):
 | 
						|
    """The actual running of tests."""
 | 
						|
    if 'COVERAGE_TESTING' not in os.environ:
 | 
						|
        os.environ['COVERAGE_TESTING'] = "True"
 | 
						|
    # $set_env.py: COVERAGE_ENV_ID - Use environment-specific test directories.
 | 
						|
    if 'COVERAGE_ENV_ID' in os.environ:
 | 
						|
        os.environ['COVERAGE_ENV_ID'] = make_env_id(tracer)
 | 
						|
    print_banner(label_for_tracer(tracer))
 | 
						|
    return pytest.main(list(runner_args))
 | 
						|
 | 
						|
 | 
						|
def run_tests_with_coverage(tracer, *runner_args):
 | 
						|
    """Run tests, but with coverage."""
 | 
						|
    # Need to define this early enough that the first import of env.py sees it.
 | 
						|
    os.environ['COVERAGE_TESTING'] = "True"
 | 
						|
    os.environ['COVERAGE_PROCESS_START'] = os.path.abspath('metacov.ini')
 | 
						|
    os.environ['COVERAGE_HOME'] = os.getcwd()
 | 
						|
 | 
						|
    # Create the .pth file that will let us measure coverage in sub-processes.
 | 
						|
    # The .pth file seems to have to be alphabetically after easy-install.pth
 | 
						|
    # or the sys.path entries aren't created right?
 | 
						|
    # There's an entry in "make clean" to get rid of this file.
 | 
						|
    pth_dir = os.path.dirname(pytest.__file__)
 | 
						|
    pth_path = os.path.join(pth_dir, "zzz_metacov.pth")
 | 
						|
    with open(pth_path, "w") as pth_file:
 | 
						|
        pth_file.write("import coverage; coverage.process_startup()\n")
 | 
						|
 | 
						|
    suffix = "%s_%s" % (make_env_id(tracer), platform.platform())
 | 
						|
    os.environ['COVERAGE_METAFILE'] = os.path.abspath(".metacov."+suffix)
 | 
						|
 | 
						|
    import coverage
 | 
						|
    cov = coverage.Coverage(config_file="metacov.ini")
 | 
						|
    cov._warn_unimported_source = False
 | 
						|
    cov._warn_preimported_source = False
 | 
						|
    cov.start()
 | 
						|
 | 
						|
    try:
 | 
						|
        # Re-import coverage to get it coverage tested!  I don't understand all
 | 
						|
        # the mechanics here, but if I don't carry over the imported modules
 | 
						|
        # (in covmods), then things go haywire (os == None, eventually).
 | 
						|
        covmods = {}
 | 
						|
        covdir = os.path.split(coverage.__file__)[0]
 | 
						|
        # We have to make a list since we'll be deleting in the loop.
 | 
						|
        modules = list(sys.modules.items())
 | 
						|
        for name, mod in modules:
 | 
						|
            if name.startswith('coverage'):
 | 
						|
                if getattr(mod, '__file__', "??").startswith(covdir):
 | 
						|
                    covmods[name] = mod
 | 
						|
                    del sys.modules[name]
 | 
						|
        import coverage                         # pylint: disable=reimported
 | 
						|
        sys.modules.update(covmods)
 | 
						|
 | 
						|
        # Run tests, with the arguments from our command line.
 | 
						|
        status = run_tests(tracer, *runner_args)
 | 
						|
 | 
						|
    finally:
 | 
						|
        cov.stop()
 | 
						|
        os.remove(pth_path)
 | 
						|
 | 
						|
    cov.combine()
 | 
						|
    cov.save()
 | 
						|
 | 
						|
    return status
 | 
						|
 | 
						|
 | 
						|
def do_combine_html():
 | 
						|
    """Combine data from a meta-coverage run, and make the HTML and XML reports."""
 | 
						|
    import coverage
 | 
						|
    os.environ['COVERAGE_HOME'] = os.getcwd()
 | 
						|
    os.environ['COVERAGE_METAFILE'] = os.path.abspath(".metacov")
 | 
						|
    cov = coverage.Coverage(config_file="metacov.ini")
 | 
						|
    cov.load()
 | 
						|
    cov.combine()
 | 
						|
    cov.save()
 | 
						|
    show_contexts = bool(os.environ.get('COVERAGE_CONTEXT'))
 | 
						|
    cov.html_report(show_contexts=show_contexts)
 | 
						|
    cov.xml_report()
 | 
						|
 | 
						|
 | 
						|
def do_test_with_tracer(tracer, *runner_args):
 | 
						|
    """Run tests with a particular tracer."""
 | 
						|
    # If we should skip these tests, skip them.
 | 
						|
    skip_msg = should_skip(tracer)
 | 
						|
    if skip_msg:
 | 
						|
        print(skip_msg)
 | 
						|
        return None
 | 
						|
 | 
						|
    os.environ["COVERAGE_TEST_TRACER"] = tracer
 | 
						|
    if os.environ.get("COVERAGE_COVERAGE", "no") == "yes":
 | 
						|
        return run_tests_with_coverage(tracer, *runner_args)
 | 
						|
    else:
 | 
						|
        return run_tests(tracer, *runner_args)
 | 
						|
 | 
						|
 | 
						|
def do_zip_mods():
 | 
						|
    """Build the zipmods.zip file."""
 | 
						|
    zf = zipfile.ZipFile("tests/zipmods.zip", "w")
 | 
						|
 | 
						|
    # Take one file from disk.
 | 
						|
    zf.write("tests/covmodzip1.py", "covmodzip1.py")
 | 
						|
 | 
						|
    # The others will be various encodings.
 | 
						|
    source = textwrap.dedent(u"""\
 | 
						|
        # coding: {encoding}
 | 
						|
        text = u"{text}"
 | 
						|
        ords = {ords}
 | 
						|
        assert [ord(c) for c in text] == ords
 | 
						|
        print(u"All OK with {encoding}")
 | 
						|
        """)
 | 
						|
    # These encodings should match the list in tests/test_python.py
 | 
						|
    details = [
 | 
						|
        (u'utf8', u'ⓗⓔⓛⓛⓞ, ⓦⓞⓡⓛⓓ'),
 | 
						|
        (u'gb2312', u'你好,世界'),
 | 
						|
        (u'hebrew', u'שלום, עולם'),
 | 
						|
        (u'shift_jis', u'こんにちは世界'),
 | 
						|
        (u'cp1252', u'“hi”'),
 | 
						|
    ]
 | 
						|
    for encoding, text in details:
 | 
						|
        filename = 'encoded_{}.py'.format(encoding)
 | 
						|
        ords = [ord(c) for c in text]
 | 
						|
        source_text = source.format(encoding=encoding, text=text, ords=ords)
 | 
						|
        zf.writestr(filename, source_text.encode(encoding))
 | 
						|
 | 
						|
    zf.close()
 | 
						|
 | 
						|
    zf = zipfile.ZipFile("tests/covmain.zip", "w")
 | 
						|
    zf.write("coverage/__main__.py", "__main__.py")
 | 
						|
    zf.close()
 | 
						|
 | 
						|
 | 
						|
def do_install_egg():
 | 
						|
    """Install the egg1 egg for tests."""
 | 
						|
    # I am pretty certain there are easier ways to install eggs...
 | 
						|
    cur_dir = os.getcwd()
 | 
						|
    os.chdir("tests/eggsrc")
 | 
						|
    with ignore_warnings():
 | 
						|
        import distutils.core
 | 
						|
        distutils.core.run_setup("setup.py", ["--quiet", "bdist_egg"])
 | 
						|
        egg = glob.glob("dist/*.egg")[0]
 | 
						|
        distutils.core.run_setup(
 | 
						|
            "setup.py", ["--quiet", "easy_install", "--no-deps", "--zip-ok", egg]
 | 
						|
        )
 | 
						|
    os.chdir(cur_dir)
 | 
						|
 | 
						|
 | 
						|
def do_check_eol():
 | 
						|
    """Check files for incorrect newlines and trailing whitespace."""
 | 
						|
 | 
						|
    ignore_dirs = [
 | 
						|
        '.svn', '.hg', '.git',
 | 
						|
        '.tox*',
 | 
						|
        '*.egg-info',
 | 
						|
        '_build',
 | 
						|
        '_spell',
 | 
						|
    ]
 | 
						|
    checked = set()
 | 
						|
 | 
						|
    def check_file(fname, crlf=True, trail_white=True):
 | 
						|
        """Check a single file for whitespace abuse."""
 | 
						|
        fname = os.path.relpath(fname)
 | 
						|
        if fname in checked:
 | 
						|
            return
 | 
						|
        checked.add(fname)
 | 
						|
 | 
						|
        line = None
 | 
						|
        with open(fname, "rb") as f:
 | 
						|
            for n, line in enumerate(f, start=1):
 | 
						|
                if crlf:
 | 
						|
                    if b"\r" in line:
 | 
						|
                        print("%s@%d: CR found" % (fname, n))
 | 
						|
                        return
 | 
						|
                if trail_white:
 | 
						|
                    line = line[:-1]
 | 
						|
                    if not crlf:
 | 
						|
                        line = line.rstrip(b'\r')
 | 
						|
                    if line.rstrip() != line:
 | 
						|
                        print("%s@%d: trailing whitespace found" % (fname, n))
 | 
						|
                        return
 | 
						|
 | 
						|
        if line is not None and not line.strip():
 | 
						|
            print("%s: final blank line" % (fname,))
 | 
						|
 | 
						|
    def check_files(root, patterns, **kwargs):
 | 
						|
        """Check a number of files for whitespace abuse."""
 | 
						|
        for where, dirs, files in os.walk(root):
 | 
						|
            for f in files:
 | 
						|
                fname = os.path.join(where, f)
 | 
						|
                for p in patterns:
 | 
						|
                    if fnmatch.fnmatch(fname, p):
 | 
						|
                        check_file(fname, **kwargs)
 | 
						|
                        break
 | 
						|
            for ignore_dir in ignore_dirs:
 | 
						|
                ignored = []
 | 
						|
                for dir_name in dirs:
 | 
						|
                    if fnmatch.fnmatch(dir_name, ignore_dir):
 | 
						|
                        ignored.append(dir_name)
 | 
						|
                for dir_name in ignored:
 | 
						|
                    dirs.remove(dir_name)
 | 
						|
 | 
						|
    check_files("coverage", ["*.py"])
 | 
						|
    check_files("coverage/ctracer", ["*.c", "*.h"])
 | 
						|
    check_files("coverage/htmlfiles", ["*.html", "*.scss", "*.css", "*.js"])
 | 
						|
    check_files("tests", ["*.py"])
 | 
						|
    check_files("tests", ["*,cover"], trail_white=False)
 | 
						|
    check_files("tests/js", ["*.js", "*.html"])
 | 
						|
    check_file("setup.py")
 | 
						|
    check_file("igor.py")
 | 
						|
    check_file("Makefile")
 | 
						|
    check_file(".travis.yml")
 | 
						|
    check_files(".", ["*.rst", "*.txt"])
 | 
						|
    check_files(".", ["*.pip"])
 | 
						|
 | 
						|
 | 
						|
def print_banner(label):
 | 
						|
    """Print the version of Python."""
 | 
						|
    try:
 | 
						|
        impl = platform.python_implementation()
 | 
						|
    except AttributeError:
 | 
						|
        impl = "Python"
 | 
						|
 | 
						|
    version = platform.python_version()
 | 
						|
 | 
						|
    if '__pypy__' in sys.builtin_module_names:
 | 
						|
        version += " (pypy %s)" % ".".join(str(v) for v in sys.pypy_version_info)
 | 
						|
 | 
						|
    try:
 | 
						|
        which_python = os.path.relpath(sys.executable)
 | 
						|
    except ValueError:
 | 
						|
        # On Windows having a python executable on a different drive
 | 
						|
        # than the sources cannot be relative.
 | 
						|
        which_python = sys.executable
 | 
						|
    print('=== %s %s %s (%s) ===' % (impl, version, label, which_python))
 | 
						|
    sys.stdout.flush()
 | 
						|
 | 
						|
 | 
						|
def do_help():
 | 
						|
    """List the available commands"""
 | 
						|
    items = list(globals().items())
 | 
						|
    items.sort()
 | 
						|
    for name, value in items:
 | 
						|
        if name.startswith('do_'):
 | 
						|
            print("%-20s%s" % (name[3:], value.__doc__))
 | 
						|
 | 
						|
 | 
						|
def analyze_args(function):
 | 
						|
    """What kind of args does `function` expect?
 | 
						|
 | 
						|
    Returns:
 | 
						|
        star, num_pos:
 | 
						|
            star(boolean): Does `function` accept *args?
 | 
						|
            num_args(int): How many positional arguments does `function` have?
 | 
						|
    """
 | 
						|
    try:
 | 
						|
        getargspec = inspect.getfullargspec
 | 
						|
    except AttributeError:
 | 
						|
        getargspec = inspect.getargspec
 | 
						|
    with ignore_warnings():
 | 
						|
        # DeprecationWarning: Use inspect.signature() instead of inspect.getfullargspec()
 | 
						|
        argspec = getargspec(function)
 | 
						|
    return bool(argspec[1]), len(argspec[0])
 | 
						|
 | 
						|
 | 
						|
def main(args):
 | 
						|
    """Main command-line execution for igor.
 | 
						|
 | 
						|
    Verbs are taken from the command line, and extra words taken as directed
 | 
						|
    by the arguments needed by the handler.
 | 
						|
 | 
						|
    """
 | 
						|
    while args:
 | 
						|
        verb = args.pop(0)
 | 
						|
        handler = globals().get('do_'+verb)
 | 
						|
        if handler is None:
 | 
						|
            print("*** No handler for %r" % verb)
 | 
						|
            return 1
 | 
						|
        star, num_args = analyze_args(handler)
 | 
						|
        if star:
 | 
						|
            # Handler has *args, give it all the rest of the command line.
 | 
						|
            handler_args = args
 | 
						|
            args = []
 | 
						|
        else:
 | 
						|
            # Handler has specific arguments, give it only what it needs.
 | 
						|
            handler_args = args[:num_args]
 | 
						|
            args = args[num_args:]
 | 
						|
        ret = handler(*handler_args)
 | 
						|
        # If a handler returns a failure-like value, stop.
 | 
						|
        if ret:
 | 
						|
            return ret
 | 
						|
    return 0
 | 
						|
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    sys.exit(main(sys.argv[1:]))
 |