summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2018-01-20 10:01:28 -0500
committerNed Batchelder <ned@nedbatchelder.com>2018-01-20 10:01:28 -0500
commite6b23c689a7c5b08131aff1962cccf8a93c50f3c (patch)
tree89d3df8bee3ddde81923cc63f3bb59a29eec5967
parent2e3428fa02082c982409b1bf8db1bb9523aac7f4 (diff)
downloadpython-coveragepy-e6b23c689a7c5b08131aff1962cccf8a93c50f3c.tar.gz
Don't ever create HTML filenames longer than ~200 chars. Fixes #627.
-rw-r--r--CHANGES.rst6
-rw-r--r--CONTRIBUTORS.txt1
-rw-r--r--coverage/files.py10
-rw-r--r--tests/test_files.py24
4 files changed, 37 insertions, 4 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 97a7247..51a0019 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -13,7 +13,13 @@ Unreleased
See :ref:`api_plugin` for details. This solves the complex configuration
problem described in `issue 563`_.
+- On Windows, the HTML report could fail when source trees are deeply nested,
+ due to attempting to create HTML filenames longer than the 250-character
+ maximum. Now filenames will never exceed 200 characters, fixing `issue
+ 627`_. Thanks to Alex Sandro for helping with the fix.
+
.. _issue 563: https://bitbucket.org/ned/coveragepy/issues/563/platform-specific-configuration
+.. _issue 627: https://bitbucket.org/ned/coveragepy/issues/627/failure-generating-html-reports-when-the
.. _changes_442:
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index b6d7889..6c6bcd0 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -7,6 +7,7 @@ useful bug reports, have been made by:
Adi Roiban
Alex Gaynor
Alex Groce
+Alex Sandro
Alexander Todorov
Andrew Hoos
Anthony Sottile
diff --git a/coverage/files.py b/coverage/files.py
index 04ce9cb..12b89e8 100644
--- a/coverage/files.py
+++ b/coverage/files.py
@@ -3,6 +3,7 @@
"""File wrangling."""
+import hashlib
import fnmatch
import ntpath
import os
@@ -75,6 +76,9 @@ def canonical_filename(filename):
return CANONICAL_FILENAME_CACHE[filename]
+MAX_FLAT = 200
+
+@contract(filename='unicode', returns='unicode')
def flat_rootname(filename):
"""A base for a flat file name to correspond to this file.
@@ -86,7 +90,11 @@ def flat_rootname(filename):
"""
name = ntpath.splitdrive(filename)[1]
- return re.sub(r"[\\/.:]", "_", name)
+ name = re.sub(r"[\\/.:]", "_", name)
+ if len(name) > MAX_FLAT:
+ h = hashlib.sha1(name.encode('UTF-8')).hexdigest()
+ name = name[-(MAX_FLAT-len(h)-1):] + '_' + h
+ return name
if env.WINDOWS:
diff --git a/tests/test_files.py b/tests/test_files.py
index c76dbaf..84d84a4 100644
--- a/tests/test_files.py
+++ b/tests/test_files.py
@@ -1,3 +1,4 @@
+# coding: utf-8
# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
@@ -6,6 +7,8 @@
import os
import os.path
+import pytest
+
from coverage import files
from coverage.files import (
TreeMatcher, FnmatchMatcher, ModuleMatcher, PathAliases,
@@ -54,9 +57,24 @@ class FilesTest(CoverageTest):
rel = os.path.join('sub', trick, 'file1.py')
self.assertEqual(files.relative_filename(abs_file(rel)), rel)
- def test_flat_rootname(self):
- self.assertEqual(flat_rootname("a/b/c.py"), "a_b_c_py")
- self.assertEqual(flat_rootname(r"c:\foo\bar.html"), "_foo_bar_html")
+
+@pytest.mark.parametrize("original, flat", [
+ (u"a/b/c.py", u"a_b_c_py"),
+ (u"c:\\foo\\bar.html", u"_foo_bar_html"),
+ (u"Montréal/☺/conf.py", u"Montréal_☺_conf_py"),
+ ( # original:
+ u"c:\\lorem\\ipsum\\quia\\dolor\\sit\\amet\\consectetur\\adipisci\\velit\\sed\\quia\\non"
+ u"\\numquam\\eius\\modi\\tempora\\incidunt\\ut\\labore\\et\\dolore\\magnam\\aliquam"
+ u"\\quaerat\\voluptatem\\ut\\enim\\ad\\minima\\veniam\\quis\\nostrum\\exercitationem"
+ u"\\ullam\\corporis\\suscipit\\laboriosam\\Montréal\\☺\\my_program.py",
+ # flat:
+ u"re_et_dolore_magnam_aliquam_quaerat_voluptatem_ut_enim_ad_minima_veniam_quis_"
+ u"nostrum_exercitationem_ullam_corporis_suscipit_laboriosam_Montréal_☺_my_program_py_"
+ u"97eaca41b860faaa1a21349b1f3009bb061cf0a8"
+ ),
+])
+def test_flat_rootname(original, flat):
+ assert flat_rootname(original) == flat
class MatcherTest(CoverageTest):