diff --git a/pytest_html/plugin.py b/pytest_html/plugin.py index d1f5f2d9..49578cba 100644 --- a/pytest_html/plugin.py +++ b/pytest_html/plugin.py @@ -14,8 +14,8 @@ import sys import time import bisect -import hashlib import warnings +import re try: from ansi2html import Ansi2HTMLConverter, style @@ -167,21 +167,17 @@ def __lt__(self, other): def create_asset( self, content, extra_index, test_index, file_extension, mode="w" ): - - hash_key = "".join([self.test_id, str(extra_index), str(test_index)]) - hash_generator = hashlib.md5() - hash_generator.update(hash_key.encode("utf-8")) - hex_digest = hash_generator.hexdigest() - # 255 is the common max filename length on various filesystems, - # we subtract hash length, file extension length and 2 more - # characters for the underscore and dot - max_length = 255 - len(hex_digest) - len(file_extension) - 2 - asset_file_name = "{0}_{1}.{2}".format( - hash_key[:max_length], hex_digest, file_extension - ) + # 255 is the common max filename length on various filesystems + asset_file_name = "{}_{}_{}.{}".format( + re.sub(r"[^\w\.]", "_", self.test_id), + str(extra_index), + str(test_index), + file_extension, + )[-255:] asset_path = os.path.join( os.path.dirname(self.logfile), "assets", asset_file_name ) + if not os.path.exists(os.path.dirname(asset_path)): os.makedirs(os.path.dirname(asset_path)) @@ -217,9 +213,9 @@ def append_extra_html(self, extra, extra_index, test_index): html_div = html.img(src=src) else: if PY3: - content = b64decode(content.encode("utf-8")) - else: - content = b64decode(content) + content = content.encode("utf-8") + + content = b64decode(content) href = src = self.create_asset( content, extra_index, test_index, extra.get("extension"), "wb" ) diff --git a/testing/test_pytest_html.py b/testing/test_pytest_html.py index 76bdc839..c7309a40 100644 --- a/testing/test_pytest_html.py +++ b/testing/test_pytest_html.py @@ -10,7 +10,6 @@ import pkg_resources import random import re -import hashlib import pytest @@ -468,13 +467,8 @@ def pytest_runtest_makereport(item, call): ) testdir.makepyfile("def test_pass(): pass") result, html = run(testdir) - hash_key = "test_extra_text_separated.py::" "test_pass00" - hash_generator = hashlib.md5() - hash_generator.update(hash_key.encode("utf-8")) assert result.ret == 0 - src = "{0}/{1}".format( - "assets", "{0}_{1}.txt".format(hash_key, hash_generator.hexdigest()) - ) + src = "assets/test_extra_text_separated.py__test_pass_0_0.txt" link = ''.format(src) assert link in html assert os.path.exists(src) @@ -501,13 +495,9 @@ def pytest_runtest_makereport(item, call): ) testdir.makepyfile("def test_pass(): pass") result, html = run(testdir) - hash_key = "test_extra_image_separated.py::test_pass00" - hash_generator = hashlib.md5() - hash_generator.update(hash_key.encode("utf-8")) assert result.ret == 0 - src = "{0}/{1}".format( - "assets", - "{0}_{1}.{2}".format(hash_key, hash_generator.hexdigest(), file_extension), + src = "assets/test_extra_image_separated.py__test_pass_0_0.{}".format( + file_extension ) link = ''.format(src) assert link in html @@ -543,12 +533,8 @@ def test_fail(): result, html = run(testdir) for i in range(1, 4): - hash_key = "test_extra_image_separated_rerun.py::" "test_fail0{0}".format(i) - hash_generator = hashlib.md5() - hash_generator.update(hash_key.encode("utf-8")) - src = "assets/{0}_{1}.{2}".format( - hash_key, hash_generator.hexdigest(), file_extension - ) + asset_name = "test_extra_image_separated_rerun.py__test_fail" + src = "assets/{}_0_{}.{}".format(asset_name, i, file_extension) link = ''.format(src) assert result.ret assert link in html @@ -602,16 +588,36 @@ def {0}(): ) ) result, html = run(testdir) - - hash_key = "test_very_long_test_name.py::{}00".format(test_name) - hash_generator = hashlib.md5() - hash_generator.update(hash_key.encode("utf-8")) - src = "assets/{0}_{1}.png".format(hash_key[:218], hash_generator.hexdigest()) + file_name = "test_very_long_test_name.py__{}_0_0.png".format(test_name)[-255:] + src = "assets/" + file_name link = ''.format(src) assert result.ret assert link in html assert os.path.exists(src) + def test_no_invalid_characters_in_filename(self, testdir): + testdir.makeconftest( + """ + import pytest + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_makereport(item, call): + outcome = yield + report = outcome.get_result() + if report.when == 'call': + from pytest_html import extras + report.extra = [extras.image('image.png')] + """ + ) + testdir.makepyfile( + """ + def test_fail(): + assert False + """ + ) + run(testdir) + for filename in os.listdir("assets"): + assert re.search(r'[:\\<>\*\?\|"}{}~]', filename) is None + def test_no_environment(self, testdir): testdir.makeconftest( """ diff --git a/tox.ini b/tox.ini index 1489252b..5c6a4fb3 100644 --- a/tox.ini +++ b/tox.ini @@ -7,12 +7,13 @@ envlist = py{27,36,37,py,py3}{,-ansi2html}, flake8, black [testenv] -commands = pytest -v -r a {posargs} +setenv = PYTHONDONTWRITEBYTECODE=1 deps = pytest-xdist pytest-rerunfailures pytest-mock py{27,36,py,py3}-ansi2html: ansi2html +commands = pytest -v -r a {posargs} [testenv:flake8] skip_install = true