Skip to content

Commit 9dd249f

Browse files
authored
Fix assets file naming to work across both *nix and windows (#223)
* Fix assets file naming to work across both *nix and windows Fixes #214 Fixes #213 * Better replacement strategy for test id * Update markers
1 parent 6abbbff commit 9dd249f

File tree

3 files changed

+44
-41
lines changed

3 files changed

+44
-41
lines changed

pytest_html/plugin.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
import sys
1515
import time
1616
import bisect
17-
import hashlib
1817
import warnings
18+
import re
1919

2020
try:
2121
from ansi2html import Ansi2HTMLConverter, style
@@ -167,21 +167,17 @@ def __lt__(self, other):
167167
def create_asset(
168168
self, content, extra_index, test_index, file_extension, mode="w"
169169
):
170-
171-
hash_key = "".join([self.test_id, str(extra_index), str(test_index)])
172-
hash_generator = hashlib.md5()
173-
hash_generator.update(hash_key.encode("utf-8"))
174-
hex_digest = hash_generator.hexdigest()
175-
# 255 is the common max filename length on various filesystems,
176-
# we subtract hash length, file extension length and 2 more
177-
# characters for the underscore and dot
178-
max_length = 255 - len(hex_digest) - len(file_extension) - 2
179-
asset_file_name = "{0}_{1}.{2}".format(
180-
hash_key[:max_length], hex_digest, file_extension
181-
)
170+
# 255 is the common max filename length on various filesystems
171+
asset_file_name = "{}_{}_{}.{}".format(
172+
re.sub(r"[^\w\.]", "_", self.test_id),
173+
str(extra_index),
174+
str(test_index),
175+
file_extension,
176+
)[-255:]
182177
asset_path = os.path.join(
183178
os.path.dirname(self.logfile), "assets", asset_file_name
184179
)
180+
185181
if not os.path.exists(os.path.dirname(asset_path)):
186182
os.makedirs(os.path.dirname(asset_path))
187183

@@ -217,9 +213,9 @@ def append_extra_html(self, extra, extra_index, test_index):
217213
html_div = html.img(src=src)
218214
else:
219215
if PY3:
220-
content = b64decode(content.encode("utf-8"))
221-
else:
222-
content = b64decode(content)
216+
content = content.encode("utf-8")
217+
218+
content = b64decode(content)
223219
href = src = self.create_asset(
224220
content, extra_index, test_index, extra.get("extension"), "wb"
225221
)

testing/test_pytest_html.py

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import pkg_resources
1111
import random
1212
import re
13-
import hashlib
1413

1514
import pytest
1615

@@ -468,13 +467,8 @@ def pytest_runtest_makereport(item, call):
468467
)
469468
testdir.makepyfile("def test_pass(): pass")
470469
result, html = run(testdir)
471-
hash_key = "test_extra_text_separated.py::" "test_pass00"
472-
hash_generator = hashlib.md5()
473-
hash_generator.update(hash_key.encode("utf-8"))
474470
assert result.ret == 0
475-
src = "{0}/{1}".format(
476-
"assets", "{0}_{1}.txt".format(hash_key, hash_generator.hexdigest())
477-
)
471+
src = "assets/test_extra_text_separated.py__test_pass_0_0.txt"
478472
link = '<a class="text" href="{0}" target="_blank">'.format(src)
479473
assert link in html
480474
assert os.path.exists(src)
@@ -501,13 +495,9 @@ def pytest_runtest_makereport(item, call):
501495
)
502496
testdir.makepyfile("def test_pass(): pass")
503497
result, html = run(testdir)
504-
hash_key = "test_extra_image_separated.py::test_pass00"
505-
hash_generator = hashlib.md5()
506-
hash_generator.update(hash_key.encode("utf-8"))
507498
assert result.ret == 0
508-
src = "{0}/{1}".format(
509-
"assets",
510-
"{0}_{1}.{2}".format(hash_key, hash_generator.hexdigest(), file_extension),
499+
src = "assets/test_extra_image_separated.py__test_pass_0_0.{}".format(
500+
file_extension
511501
)
512502
link = '<a class="image" href="{0}" target="_blank">'.format(src)
513503
assert link in html
@@ -543,12 +533,8 @@ def test_fail():
543533
result, html = run(testdir)
544534

545535
for i in range(1, 4):
546-
hash_key = "test_extra_image_separated_rerun.py::" "test_fail0{0}".format(i)
547-
hash_generator = hashlib.md5()
548-
hash_generator.update(hash_key.encode("utf-8"))
549-
src = "assets/{0}_{1}.{2}".format(
550-
hash_key, hash_generator.hexdigest(), file_extension
551-
)
536+
asset_name = "test_extra_image_separated_rerun.py__test_fail"
537+
src = "assets/{}_0_{}.{}".format(asset_name, i, file_extension)
552538
link = '<a class="image" href="{0}" target="_blank">'.format(src)
553539
assert result.ret
554540
assert link in html
@@ -602,16 +588,36 @@ def {0}():
602588
)
603589
)
604590
result, html = run(testdir)
605-
606-
hash_key = "test_very_long_test_name.py::{}00".format(test_name)
607-
hash_generator = hashlib.md5()
608-
hash_generator.update(hash_key.encode("utf-8"))
609-
src = "assets/{0}_{1}.png".format(hash_key[:218], hash_generator.hexdigest())
591+
file_name = "test_very_long_test_name.py__{}_0_0.png".format(test_name)[-255:]
592+
src = "assets/" + file_name
610593
link = '<a class="image" href="{0}" target="_blank">'.format(src)
611594
assert result.ret
612595
assert link in html
613596
assert os.path.exists(src)
614597

598+
def test_no_invalid_characters_in_filename(self, testdir):
599+
testdir.makeconftest(
600+
"""
601+
import pytest
602+
@pytest.hookimpl(hookwrapper=True)
603+
def pytest_runtest_makereport(item, call):
604+
outcome = yield
605+
report = outcome.get_result()
606+
if report.when == 'call':
607+
from pytest_html import extras
608+
report.extra = [extras.image('image.png')]
609+
"""
610+
)
611+
testdir.makepyfile(
612+
"""
613+
def test_fail():
614+
assert False
615+
"""
616+
)
617+
run(testdir)
618+
for filename in os.listdir("assets"):
619+
assert re.search(r'[:\\<>\*\?\|"}{}~]', filename) is None
620+
615621
def test_no_environment(self, testdir):
616622
testdir.makeconftest(
617623
"""

tox.ini

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
envlist = py{27,36,37,py,py3}{,-ansi2html}, flake8, black
88

99
[testenv]
10-
commands = pytest -v -r a {posargs}
10+
setenv = PYTHONDONTWRITEBYTECODE=1
1111
deps =
1212
pytest-xdist
1313
pytest-rerunfailures
1414
pytest-mock
1515
py{27,36,py,py3}-ansi2html: ansi2html
16+
commands = pytest -v -r a {posargs}
1617

1718
[testenv:flake8]
1819
skip_install = true

0 commit comments

Comments
 (0)