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

Unified Diff: tools/telemetry/third_party/coverage/tests/test_oddball.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/tests/test_oddball.py
diff --git a/tools/telemetry/third_party/coverage/tests/test_oddball.py b/tools/telemetry/third_party/coverage/tests/test_oddball.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ca1aa7558a5f447fbae0164296c3fe1a4f57e89
--- /dev/null
+++ b/tools/telemetry/third_party/coverage/tests/test_oddball.py
@@ -0,0 +1,435 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Oddball cases for testing coverage.py"""
+
+import sys
+
+import coverage
+from coverage.files import abs_file
+
+from tests.coveragetest import CoverageTest
+from tests import osinfo
+
+
+class ThreadingTest(CoverageTest):
+ """Tests of the threading support."""
+
+ def test_threading(self):
+ self.check_coverage("""\
+ import threading
+
+ def fromMainThread():
+ return "called from main thread"
+
+ def fromOtherThread():
+ return "called from other thread"
+
+ def neverCalled():
+ return "no one calls me"
+
+ other = threading.Thread(target=fromOtherThread)
+ other.start()
+ fromMainThread()
+ other.join()
+ """,
+ [1, 3, 4, 6, 7, 9, 10, 12, 13, 14, 15], "10")
+
+ def test_thread_run(self):
+ self.check_coverage("""\
+ import threading
+
+ class TestThread(threading.Thread):
+ def run(self):
+ self.a = 5
+ self.do_work()
+ self.a = 7
+
+ def do_work(self):
+ self.a = 10
+
+ thd = TestThread()
+ thd.start()
+ thd.join()
+ """,
+ [1, 3, 4, 5, 6, 7, 9, 10, 12, 13, 14], "")
+
+
+class RecursionTest(CoverageTest):
+ """Check what happens when recursive code gets near limits."""
+
+ def test_short_recursion(self):
+ # We can definitely get close to 500 stack frames.
+ self.check_coverage("""\
+ def recur(n):
+ if n == 0:
+ return 0
+ else:
+ return recur(n-1)+1
+
+ recur(495) # We can get at least this many stack frames.
+ i = 8 # and this line will be traced
+ """,
+ [1, 2, 3, 5, 7, 8], "")
+
+ def test_long_recursion(self):
+ # We can't finish a very deep recursion, but we don't crash.
+ with self.assertRaises(RuntimeError):
+ self.check_coverage("""\
+ def recur(n):
+ if n == 0:
+ return 0
+ else:
+ return recur(n-1)+1
+
+ recur(100000) # This is definitely too many frames.
+ """,
+ [1, 2, 3, 5, 7], ""
+ )
+
+ def test_long_recursion_recovery(self):
+ # Test the core of bug 93: http://bitbucket.org/ned/coveragepy/issue/93
+ # When recovering from a stack overflow, the Python trace function is
+ # disabled, but the C trace function is not. So if we're using a
+ # Python trace function, we won't trace anything after the stack
+ # overflow, and there should be a warning about it. If we're using
+ # the C trace function, only line 3 will be missing, and all else
+ # will be traced.
+
+ self.make_file("recur.py", """\
+ def recur(n):
+ if n == 0:
+ return 0 # never hit
+ else:
+ return recur(n-1)+1
+
+ try:
+ recur(100000) # This is definitely too many frames.
+ except RuntimeError:
+ i = 10
+ i = 11
+ """)
+
+ cov = coverage.Coverage()
+ self.start_import_stop(cov, "recur")
+
+ pytrace = (cov.collector.tracer_name() == "PyTracer")
+ expected_missing = [3]
+ if pytrace:
+ expected_missing += [9, 10, 11]
+
+ _, statements, missing, _ = cov.analysis("recur.py")
+ self.assertEqual(statements, [1, 2, 3, 5, 7, 8, 9, 10, 11])
+ self.assertEqual(missing, expected_missing)
+
+ # Get a warning about the stackoverflow effect on the tracing function.
+ if pytrace:
+ self.assertEqual(cov._warnings,
+ ["Trace function changed, measurement is likely wrong: None"]
+ )
+ else:
+ self.assertEqual(cov._warnings, [])
+
+
+class MemoryLeakTest(CoverageTest):
+ """Attempt the impossible: test that memory doesn't leak.
+
+ Note: this test is truly unusual, and has had a colorful history. See
+ for example: https://bitbucket.org/ned/coveragepy/issue/186
+
+ It may still fail occasionally, especially on PyPy.
+
+ """
+ def test_for_leaks(self):
+ # Our original bad memory leak only happened on line numbers > 255, so
+ # make a code object with more lines than that. Ugly string mumbo
+ # jumbo to get 300 blank lines at the beginning..
+ code = """\
+ # blank line\n""" * 300 + """\
+ def once(x): # line 301
+ if x % 100 == 0:
+ raise Exception("100!")
+ elif x % 2:
+ return 10
+ else: # line 306
+ return 11
+ i = 0 # Portable loop without alloc'ing memory.
+ while i < ITERS:
+ try:
+ once(i)
+ except:
+ pass
+ i += 1 # line 315
+ """
+ lines = list(range(301, 315))
+ lines.remove(306) # Line 306 is the "else".
+
+ # This is a non-deterministic test, so try it a few times, and fail it
+ # only if it predominantly fails.
+ fails = 0
+ for _ in range(10):
+ ram_0 = osinfo.process_ram()
+ self.check_coverage(code.replace("ITERS", "10"), lines, "")
+ ram_10 = osinfo.process_ram()
+ self.check_coverage(code.replace("ITERS", "10000"), lines, "")
+ ram_10k = osinfo.process_ram()
+ # Running the code 10k times shouldn't grow the ram much more than
+ # running it 10 times.
+ ram_growth = (ram_10k - ram_10) - (ram_10 - ram_0)
+ if ram_growth > 100000: # pragma: only failure
+ fails += 1
+
+ if fails > 8: # pragma: only failure
+ self.fail("RAM grew by %d" % (ram_growth))
+
+
+class PyexpatTest(CoverageTest):
+ """Pyexpat screws up tracing. Make sure we've counter-defended properly."""
+
+ def test_pyexpat(self):
+ # pyexpat calls the trace function explicitly (inexplicably), and does
+ # it wrong for exceptions. Parsing a DOCTYPE for some reason throws
+ # an exception internally, and triggers its wrong behavior. This test
+ # checks that our fake PyTrace_RETURN hack in tracer.c works. It will
+ # also detect if the pyexpat bug is fixed unbeknownst to us, meaning
+ # we'd see two RETURNs where there should only be one.
+
+ self.make_file("trydom.py", """\
+ import xml.dom.minidom
+
+ XML = '''\\
+ <!DOCTYPE fooey SYSTEM "http://www.example.com/example.dtd">
+ <root><child/><child/></root>
+ '''
+
+ def foo():
+ dom = xml.dom.minidom.parseString(XML)
+ assert len(dom.getElementsByTagName('child')) == 2
+ a = 11
+
+ foo()
+ """)
+
+ self.make_file("outer.py", "\n"*100 + "import trydom\na = 102\n")
+
+ cov = coverage.Coverage()
+ cov.erase()
+
+ # Import the Python file, executing it.
+ self.start_import_stop(cov, "outer")
+
+ _, statements, missing, _ = cov.analysis("trydom.py")
+ self.assertEqual(statements, [1, 3, 8, 9, 10, 11, 13])
+ self.assertEqual(missing, [])
+
+ _, statements, missing, _ = cov.analysis("outer.py")
+ self.assertEqual(statements, [101, 102])
+ self.assertEqual(missing, [])
+
+
+class ExceptionTest(CoverageTest):
+ """I suspect different versions of Python deal with exceptions differently
+ in the trace function.
+ """
+
+ def test_exception(self):
+ # Python 2.3's trace function doesn't get called with "return" if the
+ # scope is exiting due to an exception. This confounds our trace
+ # function which relies on scope announcements to track which files to
+ # trace.
+ #
+ # This test is designed to sniff this out. Each function in the call
+ # stack is in a different file, to try to trip up the tracer. Each
+ # file has active lines in a different range so we'll see if the lines
+ # get attributed to the wrong file.
+
+ self.make_file("oops.py", """\
+ def oops(args):
+ a = 2
+ raise Exception("oops")
+ a = 4
+ """)
+
+ self.make_file("fly.py", "\n"*100 + """\
+ def fly(calls):
+ a = 2
+ calls[0](calls[1:])
+ a = 4
+ """)
+
+ self.make_file("catch.py", "\n"*200 + """\
+ def catch(calls):
+ try:
+ a = 3
+ calls[0](calls[1:])
+ a = 5
+ except:
+ a = 7
+ """)
+
+ self.make_file("doit.py", "\n"*300 + """\
+ def doit(calls):
+ try:
+ calls[0](calls[1:])
+ except:
+ a = 5
+ """)
+
+ # Import all the modules before starting coverage, so the def lines
+ # won't be in all the results.
+ for mod in "oops fly catch doit".split():
+ self.import_local_file(mod)
+
+ # Each run nests the functions differently to get different
+ # combinations of catching exceptions and letting them fly.
+ runs = [
+ ("doit fly oops", {
+ 'doit.py': [302, 303, 304, 305],
+ 'fly.py': [102, 103],
+ 'oops.py': [2, 3],
+ }),
+ ("doit catch oops", {
+ 'doit.py': [302, 303],
+ 'catch.py': [202, 203, 204, 206, 207],
+ 'oops.py': [2, 3],
+ }),
+ ("doit fly catch oops", {
+ 'doit.py': [302, 303],
+ 'fly.py': [102, 103, 104],
+ 'catch.py': [202, 203, 204, 206, 207],
+ 'oops.py': [2, 3],
+ }),
+ ("doit catch fly oops", {
+ 'doit.py': [302, 303],
+ 'catch.py': [202, 203, 204, 206, 207],
+ 'fly.py': [102, 103],
+ 'oops.py': [2, 3],
+ }),
+ ]
+
+ for callnames, lines_expected in runs:
+
+ # Make the list of functions we'll call for this test.
+ callnames = callnames.split()
+ calls = [getattr(sys.modules[cn], cn) for cn in callnames]
+
+ cov = coverage.Coverage()
+ cov.start()
+ # Call our list of functions: invoke the first, with the rest as
+ # an argument.
+ calls[0](calls[1:]) # pragma: nested
+ cov.stop() # pragma: nested
+
+ # Clean the line data and compare to expected results.
+ # The file names are absolute, so keep just the base.
+ clean_lines = {}
+ data = cov.get_data()
+ for callname in callnames:
+ filename = callname + ".py"
+ lines = data.lines(abs_file(filename))
+ clean_lines[filename] = sorted(lines)
+
+ self.assertEqual(clean_lines, lines_expected)
+
+
+class DoctestTest(CoverageTest):
+ """Tests invoked with doctest should measure properly."""
+
+ def setUp(self):
+ super(DoctestTest, self).setUp()
+
+ # Oh, the irony! This test case exists because Python 2.4's
+ # doctest module doesn't play well with coverage. But nose fixes
+ # the problem by monkeypatching doctest. I want to undo the
+ # monkeypatch to be sure I'm getting the doctest module that users
+ # of coverage will get. Deleting the imported module here is
+ # enough: when the test imports doctest again, it will get a fresh
+ # copy without the monkeypatch.
+ del sys.modules['doctest']
+
+ def test_doctest(self):
+ self.check_coverage('''\
+ def return_arg_or_void(arg):
+ """If <arg> is None, return "Void"; otherwise return <arg>
+
+ >>> return_arg_or_void(None)
+ 'Void'
+ >>> return_arg_or_void("arg")
+ 'arg'
+ >>> return_arg_or_void("None")
+ 'None'
+ """
+ if arg is None:
+ return "Void"
+ else:
+ return arg
+
+ import doctest, sys
+ doctest.testmod(sys.modules[__name__]) # we're not __main__ :(
+ ''',
+ [1, 11, 12, 14, 16, 17], "")
+
+
+class GettraceTest(CoverageTest):
+ """Tests that we work properly with `sys.gettrace()`."""
+ def test_round_trip(self):
+ self.check_coverage('''\
+ import sys
+ def foo(n):
+ return 3*n
+ def bar(n):
+ return 5*n
+ a = foo(6)
+ sys.settrace(sys.gettrace())
+ a = bar(8)
+ ''',
+ [1, 2, 3, 4, 5, 6, 7, 8], "")
+
+ def test_multi_layers(self):
+ self.check_coverage('''\
+ import sys
+ def level1():
+ a = 3
+ level2()
+ b = 5
+ def level2():
+ c = 7
+ sys.settrace(sys.gettrace())
+ d = 9
+ e = 10
+ level1()
+ f = 12
+ ''',
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], "")
+
+
+class ExecTest(CoverageTest):
+ """Tests of exec."""
+ def test_correct_filename(self):
+ # https://bitbucket.org/ned/coveragepy/issues/380/code-executed-by-exec-excluded-from
+ # Bug was that exec'd files would have their lines attributed to the
+ # calling file. Make two files, both with ~30 lines, but no lines in
+ # common. Line 30 in to_exec.py was recorded as line 30 in main.py,
+ # but now it's fixed. :)
+ self.make_file("to_exec.py", """\
+ \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
+ print("var is {0}".format(var)) # line 31
+ """)
+ self.make_file("main.py", """\
+ namespace = {'var': 17}
+ with open("to_exec.py") as to_exec_py:
+ code = compile(to_exec_py.read(), 'to_exec.py', 'exec')
+ exec(code, globals(), namespace)
+ \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
+ print("done") # line 35
+ """)
+
+ cov = coverage.Coverage()
+ self.start_import_stop(cov, "main")
+
+ _, statements, missing, _ = cov.analysis("main.py")
+ self.assertEqual(statements, [1, 2, 3, 4, 35])
+ self.assertEqual(missing, [])
+ _, statements, missing, _ = cov.analysis("to_exec.py")
+ self.assertEqual(statements, [31])
+ self.assertEqual(missing, [])
« no previous file with comments | « tools/telemetry/third_party/coverage/tests/test_misc.py ('k') | tools/telemetry/third_party/coverage/tests/test_parser.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698