diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/coveragetest.py | 15 | ||||
-rw-r--r-- | tests/farm/html/src/partial.ini | 9 | ||||
-rw-r--r-- | tests/farm/html/src/partial.py | 9 | ||||
-rw-r--r-- | tests/test_arcs.py | 60 | ||||
-rw-r--r-- | tests/test_cmdline.py | 8 | ||||
-rw-r--r-- | tests/test_config.py | 2 | ||||
-rw-r--r-- | tests/test_execfile.py | 19 | ||||
-rw-r--r-- | tests/test_html.py | 60 | ||||
-rw-r--r-- | tests/test_misc.py | 7 | ||||
-rw-r--r-- | tests/test_process.py | 42 | ||||
-rw-r--r-- | tests/test_summary.py | 41 |
11 files changed, 222 insertions, 50 deletions
diff --git a/tests/coveragetest.py b/tests/coveragetest.py index dacb9b6..539dd59 100644 --- a/tests/coveragetest.py +++ b/tests/coveragetest.py @@ -240,17 +240,17 @@ class CoverageTest( with self.delayed_assertions(): self.assert_equal_args( analysis.arc_possibilities(), arcs, - "Possible arcs differ", + "Possible arcs differ: minus is actual, plus is expected" ) self.assert_equal_args( analysis.arcs_missing(), arcs_missing, - "Missing arcs differ" + "Missing arcs differ: minus is actual, plus is expected" ) self.assert_equal_args( analysis.arcs_unpredicted(), arcs_unpredicted, - "Unpredicted arcs differ" + "Unpredicted arcs differ: minus is actual, plus is expected" ) if report: @@ -397,9 +397,8 @@ class CoverageTest( # Add our test modules directory to PYTHONPATH. I'm sure there's too # much path munging here, but... - here = os.path.dirname(self.nice_file(coverage.__file__, "..")) - testmods = self.nice_file(here, 'tests/modules') - zipfile = self.nice_file(here, 'tests/zipmods.zip') + testmods = self.nice_file(self.working_root(), 'tests/modules') + zipfile = self.nice_file(self.working_root(), 'tests/zipmods.zip') pypath = os.getenv('PYTHONPATH', '') if pypath: pypath += os.pathsep @@ -410,6 +409,10 @@ class CoverageTest( print(self.last_command_output) return self.last_command_status, self.last_command_output + def working_root(self): + """Where is the root of the coverage.py working tree?""" + return os.path.dirname(self.nice_file(coverage.__file__, "..")) + def report_from_command(self, cmd): """Return the report from the `cmd`, with some convenience added.""" report = self.run_command(cmd).replace('\\', '/') diff --git a/tests/farm/html/src/partial.ini b/tests/farm/html/src/partial.ini new file mode 100644 index 0000000..cdb241b --- /dev/null +++ b/tests/farm/html/src/partial.ini @@ -0,0 +1,9 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt + +[run] +branch = True + +[report] +exclude_lines = + raise AssertionError diff --git a/tests/farm/html/src/partial.py b/tests/farm/html/src/partial.py index 66dddac..0f8fbe3c 100644 --- a/tests/farm/html/src/partial.py +++ b/tests/farm/html/src/partial.py @@ -1,9 +1,9 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt -# partial branches +# partial branches and excluded lines -a = 3 +a = 6 while True: break @@ -18,4 +18,7 @@ if 0: never_happen() if 1: - a = 13 + a = 21 + +if a == 23: + raise AssertionError("Can't") diff --git a/tests/test_arcs.py b/tests/test_arcs.py index b03ac53..d9b77dc 100644 --- a/tests/test_arcs.py +++ b/tests/test_arcs.py @@ -252,12 +252,12 @@ class LoopArcTest(CoverageTest): """, arcz=".1 12 23 34 45 36 63 57 7.", ) - # With "while True", 2.x thinks it's computation, 3.x thinks it's - # constant. + # With "while True", 2.x thinks it's computation, + # 3.x thinks it's constant. if env.PY3: arcz = ".1 12 23 34 45 36 63 57 7." else: - arcz = ".1 12 23 27 34 45 36 62 57 7." + arcz = ".1 12 23 34 45 36 62 57 7." self.check_coverage("""\ a, i = 1, 0 while True: @@ -270,6 +270,37 @@ class LoopArcTest(CoverageTest): arcz=arcz, ) + def test_zero_coverage_while_loop(self): + # https://bitbucket.org/ned/coveragepy/issue/502 + self.make_file("main.py", "print('done')") + self.make_file("zero.py", """\ + def method(self): + while True: + return 1 + """) + out = self.run_command("coverage run --branch --source=. main.py") + self.assertEqual(out, 'done\n') + report = self.report_from_command("coverage report -m") + squeezed = self.squeezed_lines(report) + self.assertIn("zero.py 3 3 0 0 0% 1-3", squeezed[3]) + + def test_bug_496_continue_in_constant_while(self): + # https://bitbucket.org/ned/coveragepy/issue/496 + if env.PY3: + arcz = ".1 12 23 34 45 53 46 6." + else: + arcz = ".1 12 23 34 45 52 46 6." + self.check_coverage("""\ + up = iter('ta') + while True: + char = next(up) + if char == 't': + continue + break + """, + arcz=arcz + ) + def test_for_if_else_for(self): self.check_coverage("""\ def branches_2(l): @@ -762,16 +793,19 @@ class ExceptionArcTest(CoverageTest): def test_return_finally(self): self.check_coverage("""\ a = [1] - def func(): - try: - return 10 - finally: - a.append(6) - - assert func() == 10 - assert a == [1, 6] - """, - arcz=".1 12 28 89 9. -23 34 46 6-2", + def check_token(data): + if data: + try: + return 5 + finally: + a.append(7) + return 8 + assert check_token(False) == 8 + assert a == [1] + assert check_token(True) == 5 + assert a == [1, 7] + """, + arcz=".1 12 29 9A AB BC C-1 -23 34 45 57 7-2 38 8-2", ) def test_except_jump_finally(self): diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 3b982eb..45898f1 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -16,7 +16,7 @@ import coverage.cmdline from coverage import env from coverage.config import CoverageConfig from coverage.data import CoverageData, CoverageDataFiles -from coverage.misc import ExceptionDuringRun +from coverage.misc import CoverageException, ExceptionDuringRun from tests.coveragetest import CoverageTest, OK, ERR @@ -39,7 +39,7 @@ class BaseCmdLineTest(CoverageTest): ) defaults.html_report( directory=None, ignore_errors=None, include=None, omit=None, morfs=[], - title=None, + skip_covered=None, title=None ) defaults.report( ignore_errors=None, include=None, omit=None, morfs=[], @@ -459,6 +459,10 @@ class CmdLineTest(BaseCmdLineTest): .save() """) + def test_bad_run_args_with_both_source_and_include(self): + with self.assertRaisesRegex(CoverageException, 'mutually exclusive'): + self.command_line("run --include=pre1,pre2 --source=lol,wut foo.py", ret=ERR) + def test_bad_concurrency(self): self.command_line("run --concurrency=nothing", ret=ERR) out = self.stdout() diff --git a/tests/test_config.py b/tests/test_config.py index 6cb5e46..2aa592b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -239,7 +239,6 @@ class ConfigFileTest(CoverageTest): branch = 1 cover_pylib = TRUE parallel = on - include = a/ , b/ concurrency = thread source = myapp plugins = @@ -329,7 +328,6 @@ class ConfigFileTest(CoverageTest): self.assertEqual(cov.get_exclude_list(), ["if 0:", r"pragma:?\s+no cover", "another_tab"]) self.assertTrue(cov.config.ignore_errors) - self.assertEqual(cov.config.include, ["a/", "b/"]) self.assertEqual(cov.config.omit, ["one", "another", "some_more", "yet_more"]) self.assertEqual(cov.config.precision, 3) diff --git a/tests/test_execfile.py b/tests/test_execfile.py index 889d6cf..8585b16 100644 --- a/tests/test_execfile.py +++ b/tests/test_execfile.py @@ -145,6 +145,25 @@ class RunPycFileTest(CoverageTest): with self.assertRaisesRegex(NoCode, "No file to run: 'xyzzy.pyc'"): run_python_file("xyzzy.pyc", []) + def test_running_py_from_binary(self): + # Use make_file to get the bookkeeping. Ideally, it would + # be able to write binary files. + bf = self.make_file("binary") + with open(bf, "wb") as f: + f.write(b'\x7fELF\x02\x01\x01\x00\x00\x00') + + msg = ( + r"Couldn't run 'binary' as Python code: " + r"(TypeError|ValueError): " + r"(" + r"compile\(\) expected string without null bytes" # for py2 + r"|" + r"source code string cannot contain null bytes" # for py3 + r")" + ) + with self.assertRaisesRegex(Exception, msg): + run_python_file(bf, [bf]) + class RunModuleTest(CoverageTest): """Test run_python_module.""" diff --git a/tests/test_html.py b/tests/test_html.py index 1df602f..1c9fa43 100644 --- a/tests/test_html.py +++ b/tests/test_html.py @@ -6,6 +6,7 @@ import datetime import glob +import json import os import os.path import re @@ -208,6 +209,27 @@ class HtmlDeltaTest(HtmlTestHelpers, CoverageTest): fixed_index2 = index2.replace("XYZZY", self.real_coverage_version) self.assertMultiLineEqual(index1, fixed_index2) + def test_status_format_change(self): + self.create_initial_files() + self.run_coverage() + self.remove_html_files() + + with open("htmlcov/status.json") as status_json: + status_data = json.load(status_json) + + self.assertEqual(status_data['format'], 1) + status_data['format'] = 2 + with open("htmlcov/status.json", "w") as status_json: + json.dump(status_data, status_json) + + self.run_coverage() + + # All the files have been reported again. + self.assert_exists("htmlcov/index.html") + self.assert_exists("htmlcov/helper1_py.html") + self.assert_exists("htmlcov/main_file_py.html") + self.assert_exists("htmlcov/helper2_py.html") + class HtmlTitleTest(HtmlTestHelpers, CoverageTest): """Tests of the HTML title support.""" @@ -425,6 +447,40 @@ class HtmlTest(HtmlTestHelpers, CoverageTest): self.run_coverage() self.assert_exists("htmlcov/status.dat") + def test_report_skip_covered_no_branches(self): + self.make_file("main_file.py", """ + import not_covered + + def normal(): + print("z") + normal() + """) + self.make_file("not_covered.py", """ + def not_covered(): + print("n") + """) + self.run_coverage(htmlargs=dict(skip_covered=True)) + self.assert_exists("htmlcov/index.html") + self.assert_doesnt_exist("htmlcov/main_file_py.html") + self.assert_exists("htmlcov/not_covered_py.html") + + def test_report_skip_covered_branches(self): + self.make_file("main_file.py", """ + import not_covered + + def normal(): + print("z") + normal() + """) + self.make_file("not_covered.py", """ + def not_covered(): + print("n") + """) + self.run_coverage(covargs=dict(branch=True), htmlargs=dict(skip_covered=True)) + self.assert_exists("htmlcov/index.html") + self.assert_doesnt_exist("htmlcov/main_file_py.html") + self.assert_exists("htmlcov/not_covered_py.html") + class HtmlStaticFileTest(CoverageTest): """Tests of the static file copying for the HTML report.""" @@ -686,7 +742,7 @@ class HtmlGoldTests(CoverageGoldTest): with change_dir("src"): # pylint: disable=import-error - cov = coverage.Coverage(branch=True) + cov = coverage.Coverage(config_file="partial.ini") cov.start() import partial # pragma: nested cov.stop() # pragma: nested @@ -700,6 +756,8 @@ class HtmlGoldTests(CoverageGoldTest): '<p id="t14" class="stm run hide_run">', # The "if 0" and "if 1" statements are optimized away. '<p id="t17" class="pln">', + # The "raise AssertionError" is excluded by regex in the .ini. + '<p id="t24" class="exc">', ) contains( "out/partial/index.html", diff --git a/tests/test_misc.py b/tests/test_misc.py index 38be345..96b5100 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -34,6 +34,13 @@ class HasherTest(CoverageTest): h2.update(b"Goodbye!") self.assertNotEqual(h1.hexdigest(), h2.hexdigest()) + def test_unicode_hashing(self): + h1 = Hasher() + h1.update(u"Hello, world! \N{SNOWMAN}") + h2 = Hasher() + h2.update(u"Goodbye!") + self.assertNotEqual(h1.hexdigest(), h2.hexdigest()) + def test_dict_hashing(self): h1 = Hasher() h1.update({'a': 17, 'b': 23}) diff --git a/tests/test_process.py b/tests/test_process.py index dda43ba..2e94c19 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -11,6 +11,8 @@ import re import sys import textwrap +import pytest + import coverage from coverage import env, CoverageData from coverage.misc import output_encoding @@ -692,6 +694,7 @@ class ProcessTest(CoverageTest): self.assertEqual(len(infos), 1) self.assertEqual(infos[0]['note'], u"These are musical notes: ♫𝅗𝅥♩") + @pytest.mark.expensive def test_fullcoverage(self): # pragma: not covered if env.PY2: # This doesn't work on Python 2. self.skipTest("fullcoverage doesn't work on Python 2.") @@ -1139,25 +1142,38 @@ def possible_pth_dirs(): yield distutils.sysconfig.get_python_lib() +def find_writable_pth_directory(): + """Find a place to write a .pth file.""" + for pth_dir in possible_pth_dirs(): # pragma: part covered + try_it = os.path.join(pth_dir, "touch_{0}.it".format(WORKER)) + with open(try_it, "w") as f: + try: + f.write("foo") + except (IOError, OSError): # pragma: not covered + continue + + os.remove(try_it) + return pth_dir + + return None + +WORKER = os.environ.get('PYTEST_XDIST_WORKER', '') +PTH_DIR = find_writable_pth_directory() + + class ProcessCoverageMixin(object): """Set up a .pth file to coverage-measure all sub-processes.""" def setUp(self): super(ProcessCoverageMixin, self).setUp() - # Find a place to put a .pth file. + + # Create the .pth file. + self.assert_(PTH_DIR) pth_contents = "import coverage; coverage.process_startup()\n" - for pth_dir in possible_pth_dirs(): # pragma: part covered - worker = os.environ.get('PYTEST_XDIST_WORKER', '') - pth_path = os.path.join(pth_dir, "subcover_{0}.pth".format(worker)) - with open(pth_path, "w") as pth: - try: - pth.write(pth_contents) - self.pth_path = pth_path - break - except (IOError, OSError): # pragma: not covered - pass - else: # pragma: not covered - raise Exception("Couldn't find a place for the .pth file") + pth_path = os.path.join(PTH_DIR, "subcover_{0}.pth".format(WORKER)) + with open(pth_path, "w") as pth: + pth.write(pth_contents) + self.pth_path = pth_path self.addCleanup(os.remove, self.pth_path) diff --git a/tests/test_summary.py b/tests/test_summary.py index bda6568..5ba0038 100644 --- a/tests/test_summary.py +++ b/tests/test_summary.py @@ -361,6 +361,27 @@ class SummaryTest(CoverageTest): squeezed = self.squeezed_lines(report) self.assertEqual(squeezed[3], "1 file skipped due to complete coverage.") + def test_report_skip_covered_longfilename(self): + self.make_file("long_______________filename.py", """ + def foo(): + pass + foo() + """) + out = self.run_command("coverage run --branch long_______________filename.py") + self.assertEqual(out, "") + report = self.report_from_command("coverage report --skip-covered") + + # Name Stmts Miss Branch BrPart Cover + # ----------------------------------------- + # + # 1 file skipped due to complete coverage. + + self.assertEqual(self.line_count(report), 4, report) + lines = self.report_lines(report) + self.assertEqual(lines[0], "Name Stmts Miss Branch BrPart Cover") + squeezed = self.squeezed_lines(report) + self.assertEqual(squeezed[3], "1 file skipped due to complete coverage.") + def test_report_skip_covered_no_data(self): report = self.report_from_command("coverage report --skip-covered") @@ -381,18 +402,18 @@ class SummaryTest(CoverageTest): self.make_file("mycode.py", "This isn't python at all!") report = self.report_from_command("coverage report mycode.py") + # mycode NotPython: Couldn't parse '...' as Python source: 'invalid syntax' at line 1 # Name Stmts Miss Cover # ---------------------------- - # mycode NotPython: Couldn't parse '...' as Python source: 'invalid syntax' at line 1 # No data to report. - last = self.squeezed_lines(report)[-2] + errmsg = self.squeezed_lines(report)[0] # The actual file name varies run to run. - last = re.sub(r"parse '.*mycode.py", "parse 'mycode.py", last) + errmsg = re.sub(r"parse '.*mycode.py", "parse 'mycode.py", errmsg) # The actual error message varies version to version - last = re.sub(r": '.*' at", ": 'error' at", last) + errmsg = re.sub(r": '.*' at", ": 'error' at", errmsg) self.assertEqual( - last, + errmsg, "mycode.py NotPython: Couldn't parse 'mycode.py' as Python source: 'error' at line 1" ) @@ -405,16 +426,16 @@ class SummaryTest(CoverageTest): self.make_file(u"accented\xe2.py", "This isn't python at all!") report = self.report_from_command(u"coverage report accented\xe2.py") + # xxxx NotPython: Couldn't parse '...' as Python source: 'invalid syntax' at line 1 # Name Stmts Miss Cover # ---------------------------- - # xxxx NotPython: Couldn't parse '...' as Python source: 'invalid syntax' at line 1 # No data to report. - last = self.squeezed_lines(report)[-2] + errmsg = self.squeezed_lines(report)[0] # The actual file name varies run to run. - last = re.sub(r"parse '.*(accented.*?\.py)", r"parse '\1", last) + errmsg = re.sub(r"parse '.*(accented.*?\.py)", r"parse '\1", errmsg) # The actual error message varies version to version - last = re.sub(r": '.*' at", ": 'error' at", last) + errmsg = re.sub(r": '.*' at", ": 'error' at", errmsg) expected = ( u"accented\xe2.py NotPython: " u"Couldn't parse 'accented\xe2.py' as Python source: 'error' at line 1" @@ -422,7 +443,7 @@ class SummaryTest(CoverageTest): if env.PY2: # pylint: disable=redefined-variable-type expected = expected.encode(output_encoding()) - self.assertEqual(last, expected) + self.assertEqual(errmsg, expected) def test_dotpy_not_python_ignored(self): # We run a .py file, and when reporting, we can't parse it as Python, |