Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1709)

Unified Diff: tools/telemetry/third_party/coverage/coverage/test_helpers.py

Issue 1366913004: Add coverage Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/telemetry/third_party/coverage/coverage/test_helpers.py
diff --git a/tools/telemetry/third_party/coverage/coverage/test_helpers.py b/tools/telemetry/third_party/coverage/coverage/test_helpers.py
new file mode 100644
index 0000000000000000000000000000000000000000..50cc3298fbdcd188c23b0980aa369f8a2c8aa6c4
--- /dev/null
+++ b/tools/telemetry/third_party/coverage/coverage/test_helpers.py
@@ -0,0 +1,337 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Mixin classes to help make good tests."""
+
+import atexit
+import collections
+import contextlib
+import os
+import random
+import shutil
+import sys
+import tempfile
+import textwrap
+
+from coverage.backunittest import TestCase
+from coverage.backward import StringIO, to_bytes
+
+
+class Tee(object):
+ """A file-like that writes to all the file-likes it has."""
+
+ def __init__(self, *files):
+ """Make a Tee that writes to all the files in `files.`"""
+ self._files = files
+ if hasattr(files[0], "encoding"):
+ self.encoding = files[0].encoding
+
+ def write(self, data):
+ """Write `data` to all the files."""
+ for f in self._files:
+ f.write(data)
+
+ def flush(self):
+ """Flush the data on all the files."""
+ for f in self._files:
+ f.flush()
+
+ if 0:
+ # Use this if you need to use a debugger, though it makes some tests
+ # fail, I'm not sure why...
+ def __getattr__(self, name):
+ return getattr(self._files[0], name)
+
+
+@contextlib.contextmanager
+def change_dir(new_dir):
+ """Change directory, and then change back.
+
+ Use as a context manager, it will give you the new directory, and later
+ restore the old one.
+
+ """
+ old_dir = os.getcwd()
+ os.chdir(new_dir)
+ try:
+ yield os.getcwd()
+ finally:
+ os.chdir(old_dir)
+
+
+@contextlib.contextmanager
+def saved_sys_path():
+ """Save sys.path, and restore it later."""
+ old_syspath = sys.path[:]
+ try:
+ yield
+ finally:
+ sys.path = old_syspath
+
+
+def setup_with_context_manager(testcase, cm):
+ """Use a contextmanager to setUp a test case.
+
+ If you have a context manager you like::
+
+ with ctxmgr(a, b, c) as v:
+ # do something with v
+
+ and you want to have that effect for a test case, call this function from
+ your setUp, and it will start the context manager for your test, and end it
+ when the test is done::
+
+ def setUp(self):
+ self.v = setup_with_context_manager(self, ctxmgr(a, b, c))
+
+ def test_foo(self):
+ # do something with self.v
+
+ """
+ val = cm.__enter__()
+ testcase.addCleanup(cm.__exit__, None, None, None)
+ return val
+
+
+class ModuleAwareMixin(TestCase):
+ """A test case mixin that isolates changes to sys.modules."""
+
+ def setUp(self):
+ super(ModuleAwareMixin, self).setUp()
+
+ # Record sys.modules here so we can restore it in cleanup_modules.
+ self.old_modules = list(sys.modules)
+ self.addCleanup(self.cleanup_modules)
+
+ def cleanup_modules(self):
+ """Remove any new modules imported during the test run.
+
+ This lets us import the same source files for more than one test.
+
+ """
+ for m in [m for m in sys.modules if m not in self.old_modules]:
+ del sys.modules[m]
+
+
+class SysPathAwareMixin(TestCase):
+ """A test case mixin that isolates changes to sys.path."""
+
+ def setUp(self):
+ super(SysPathAwareMixin, self).setUp()
+ setup_with_context_manager(self, saved_sys_path())
+
+
+class EnvironmentAwareMixin(TestCase):
+ """A test case mixin that isolates changes to the environment."""
+
+ def setUp(self):
+ super(EnvironmentAwareMixin, self).setUp()
+
+ # Record environment variables that we changed with set_environ.
+ self.environ_undos = {}
+
+ self.addCleanup(self.cleanup_environ)
+
+ def set_environ(self, name, value):
+ """Set an environment variable `name` to be `value`.
+
+ The environment variable is set, and record is kept that it was set,
+ so that `cleanup_environ` can restore its original value.
+
+ """
+ if name not in self.environ_undos:
+ self.environ_undos[name] = os.environ.get(name)
+ os.environ[name] = value
+
+ def cleanup_environ(self):
+ """Undo all the changes made by `set_environ`."""
+ for name, value in self.environ_undos.items():
+ if value is None:
+ del os.environ[name]
+ else:
+ os.environ[name] = value
+
+
+class StdStreamCapturingMixin(TestCase):
+ """A test case mixin that captures stdout and stderr."""
+
+ def setUp(self):
+ super(StdStreamCapturingMixin, self).setUp()
+
+ # Capture stdout and stderr so we can examine them in tests.
+ # nose keeps stdout from littering the screen, so we can safely Tee it,
+ # but it doesn't capture stderr, so we don't want to Tee stderr to the
+ # real stderr, since it will interfere with our nice field of dots.
+ self.old_stdout = sys.stdout
+ self.captured_stdout = StringIO()
+ sys.stdout = Tee(sys.stdout, self.captured_stdout)
+
+ self.old_stderr = sys.stderr
+ self.captured_stderr = StringIO()
+ sys.stderr = self.captured_stderr
+
+ self.addCleanup(self.cleanup_std_streams)
+
+ def cleanup_std_streams(self):
+ """Restore stdout and stderr."""
+ sys.stdout = self.old_stdout
+ sys.stderr = self.old_stderr
+
+ def stdout(self):
+ """Return the data written to stdout during the test."""
+ return self.captured_stdout.getvalue()
+
+ def stderr(self):
+ """Return the data written to stderr during the test."""
+ return self.captured_stderr.getvalue()
+
+
+class TempDirMixin(SysPathAwareMixin, ModuleAwareMixin, TestCase):
+ """A test case mixin that creates a temp directory and files in it.
+
+ Includes SysPathAwareMixin and ModuleAwareMixin, because making and using
+ temp directories like this will also need that kind of isolation.
+
+ """
+
+ # Our own setting: most of these tests run in their own temp directory.
+ # Set this to False in your subclass if you don't want a temp directory
+ # created.
+ run_in_temp_dir = True
+
+ # Set this if you aren't creating any files with make_file, but still want
+ # the temp directory. This will stop the test behavior checker from
+ # complaining.
+ no_files_in_temp_dir = False
+
+ def setUp(self):
+ super(TempDirMixin, self).setUp()
+
+ if self.run_in_temp_dir:
+ # Create a temporary directory.
+ self.temp_dir = self.make_temp_dir("test_cover")
+ self.chdir(self.temp_dir)
+
+ # Modules should be importable from this temp directory. We don't
+ # use '' because we make lots of different temp directories and
+ # nose's caching importer can get confused. The full path prevents
+ # problems.
+ sys.path.insert(0, os.getcwd())
+
+ class_behavior = self.class_behavior()
+ class_behavior.tests += 1
+ class_behavior.temp_dir = self.run_in_temp_dir
+ class_behavior.no_files_ok = self.no_files_in_temp_dir
+
+ self.addCleanup(self.check_behavior)
+
+ def make_temp_dir(self, slug="test_cover"):
+ """Make a temp directory that is cleaned up when the test is done."""
+ name = "%s_%08d" % (slug, random.randint(0, 99999999))
+ temp_dir = os.path.join(tempfile.gettempdir(), name)
+ os.makedirs(temp_dir)
+ self.addCleanup(shutil.rmtree, temp_dir)
+ return temp_dir
+
+ def chdir(self, new_dir):
+ """Change directory, and change back when the test is done."""
+ old_dir = os.getcwd()
+ os.chdir(new_dir)
+ self.addCleanup(os.chdir, old_dir)
+
+ def check_behavior(self):
+ """Check that we did the right things."""
+
+ class_behavior = self.class_behavior()
+ if class_behavior.test_method_made_any_files:
+ class_behavior.tests_making_files += 1
+
+ def make_file(self, filename, text="", newline=None):
+ """Create a file for testing.
+
+ `filename` is the relative path to the file, including directories if
+ desired, which will be created if need be.
+
+ `text` is the content to create in the file, a native string (bytes in
+ Python 2, unicode in Python 3).
+
+ If `newline` is provided, it is a string that will be used as the line
+ endings in the created file, otherwise the line endings are as provided
+ in `text`.
+
+ Returns `filename`.
+
+ """
+ # Tests that call `make_file` should be run in a temp environment.
+ assert self.run_in_temp_dir
+ self.class_behavior().test_method_made_any_files = True
+
+ text = textwrap.dedent(text)
+ if newline:
+ text = text.replace("\n", newline)
+
+ # Make sure the directories are available.
+ dirs, _ = os.path.split(filename)
+ if dirs and not os.path.exists(dirs):
+ os.makedirs(dirs)
+
+ # Create the file.
+ with open(filename, 'wb') as f:
+ f.write(to_bytes(text))
+
+ return filename
+
+ # We run some tests in temporary directories, because they may need to make
+ # files for the tests. But this is expensive, so we can change per-class
+ # whether a temp directory is used or not. It's easy to forget to set that
+ # option properly, so we track information about what the tests did, and
+ # then report at the end of the process on test classes that were set
+ # wrong.
+
+ class ClassBehavior(object):
+ """A value object to store per-class."""
+ def __init__(self):
+ self.tests = 0
+ self.skipped = 0
+ self.temp_dir = True
+ self.no_files_ok = False
+ self.tests_making_files = 0
+ self.test_method_made_any_files = False
+
+ # Map from class to info about how it ran.
+ class_behaviors = collections.defaultdict(ClassBehavior)
+
+ @classmethod
+ def report_on_class_behavior(cls):
+ """Called at process exit to report on class behavior."""
+ for test_class, behavior in cls.class_behaviors.items():
+ bad = ""
+ if behavior.tests <= behavior.skipped:
+ bad = ""
+ elif behavior.temp_dir and behavior.tests_making_files == 0:
+ if not behavior.no_files_ok:
+ bad = "Inefficient"
+ elif not behavior.temp_dir and behavior.tests_making_files > 0:
+ bad = "Unsafe"
+
+ if bad:
+ if behavior.temp_dir:
+ where = "in a temp directory"
+ else:
+ where = "without a temp directory"
+ print(
+ "%s: %s ran %d tests, %d made files %s" % (
+ bad,
+ test_class.__name__,
+ behavior.tests,
+ behavior.tests_making_files,
+ where,
+ )
+ )
+
+ def class_behavior(self):
+ """Get the ClassBehavior instance for this test."""
+ return self.class_behaviors[self.__class__]
+
+# When the process ends, find out about bad classes.
+atexit.register(TempDirMixin.report_on_class_behavior)
« no previous file with comments | « tools/telemetry/third_party/coverage/coverage/templite.py ('k') | tools/telemetry/third_party/coverage/coverage/version.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698