Skip to content

Commit 2b9fbce

Browse files
committed
tests(fuzz): switch to Pytest for running multiple fuzz tests in a test suite
1 parent 988f34d commit 2b9fbce

File tree

14 files changed

+181
-58
lines changed

14 files changed

+181
-58
lines changed

.github/workflows/ci-windows.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ jobs:
5757
5858
- name: Download ChromeDriver
5959
run: |
60-
python html2pdf4doc/html2pdf4doc.py get_driver
60+
python -m html2pdf4doc.main get_driver
6161
6262
- name: Run tests (Bash)
6363
run: |

.github/workflows/ci_fuzz_linux.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
run: |
5656
if [ "${{ github.event_name }}" = "schedule" ]; then
5757
echo "🕒 Running long fuzzing..."
58-
invoke test-fuzz --long
58+
invoke test-fuzz --total-mutations 25
5959
else
6060
echo "🚀 Running short fuzzing..."
6161
invoke test-fuzz
@@ -66,6 +66,6 @@ jobs:
6666
if: failure() || always()
6767
uses: actions/upload-artifact@v4
6868
with:
69-
name: broken-pdfs
70-
path: output/
69+
name: tests_fuzz_broken_pdfs
70+
path: build/tests_fuzz
7171
retention-days: 30

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ RUN if [ "$HTML2PDF4DOC_SOURCE" = "pypi" ]; then \
3535
pip install --no-cache-dir html2pdf4doc; \
3636
else \
3737
pip install --no-cache-dir --upgrade pip && \
38-
pip install --no-cache-dir git+https://github.com/mettta/html2pdf_python.git@${HTML2PDF4DOC_SOURCE}; \
38+
pip install --no-cache-dir git+https://github.com/strictdoc-project/html2pdf4doc_python.git@${HTML2PDF4DOC_SOURCE}; \
3939
fi; \
4040
chmod -R 777 /opt/venv;
4141

html2pdf4doc/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import os
2+
from pathlib import Path
3+
4+
__version__ = "0.0.22"
5+
6+
PATH_TO_HTML2PDF4DOC_PY = os.path.join(
7+
os.path.dirname(os.path.join(__file__)),
8+
"main.py",
9+
)
10+
PATH_TO_HTML2PDF4DOC_JS = os.path.join(
11+
os.path.dirname(os.path.join(__file__)),
12+
"html2pdf4doc_js",
13+
"html2pdf4doc.min.js",
14+
)
15+
16+
DEFAULT_CACHE_DIR = os.path.join(Path.home(), ".html2pdf4doc", "chromedriver")
17+
18+
PATH_TO_CHROME_DRIVER_DEBUG_LOG = "/tmp/chromedriver.log"

html2pdf4doc/html2pdf4doc.py renamed to html2pdf4doc/main.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,13 @@
2121
from selenium.webdriver.chrome.service import Service
2222
from webdriver_manager.core.os_manager import ChromeType, OperationSystemManager
2323

24-
__version__ = "0.0.22"
25-
26-
PATH_TO_HTML2PDF4DOC_PY = __file__
27-
PATH_TO_HTML2PDF4DOC_JS = os.path.join(
28-
os.path.dirname(os.path.join(__file__)),
29-
"html2pdf4doc_js",
30-
"html2pdf4doc.min.js",
24+
from . import (
25+
DEFAULT_CACHE_DIR,
26+
PATH_TO_CHROME_DRIVER_DEBUG_LOG,
27+
PATH_TO_HTML2PDF4DOC_JS,
28+
__version__,
3129
)
3230

33-
DEFAULT_CACHE_DIR = os.path.join(Path.home(), ".html2pdf4doc", "chromedriver")
34-
35-
PATH_TO_CHROME_DRIVER_DEBUG_LOG = "/tmp/chromedriver.log"
36-
3731
# HTML2PDF4Doc.js prints unicode symbols to console. The following makes it work on
3832
# Windows which otherwise complains:
3933
# UnicodeEncodeError: 'charmap' codec can't encode characters in position 129-130: character maps to <undefined>

html2pdf4doc/html2pdf4doc_fuzzer.py renamed to html2pdf4doc/main_fuzzer.py

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
from faker import Faker
1414
from lxml import etree, html
1515

16-
from html2pdf4doc import PATH_TO_HTML2PDF4DOC_PY
17-
1816

1917
@contextlib.contextmanager
2018
def measure_performance(title: str) -> Iterator[None]:
@@ -71,7 +69,8 @@ def mutate_and_print(path_to_input_file: str, path_to_root: str) -> bool:
7169

7270
cmd: List[str] = [
7371
sys.executable,
74-
PATH_TO_HTML2PDF4DOC_PY,
72+
"-m",
73+
"html2pdf4doc.main",
7574
"print",
7675
"--strict",
7776
]
@@ -80,7 +79,7 @@ def mutate_and_print(path_to_input_file: str, path_to_root: str) -> bool:
8079
cmd.append(path_to_print_[0])
8180
cmd.append(path_to_print_[1])
8281

83-
relative_path_to_mut_html = Path(path_to_root).relative_to(".")
82+
relative_path_to_mut_html = Path(path_to_mut_html).relative_to(path_to_root)
8483
path_to_mut_output = f"output/{relative_path_to_mut_html}"
8584

8685
def copy_files_if_needed() -> None:
@@ -143,30 +142,16 @@ def copy_mutated_file() -> None:
143142
return True
144143

145144

146-
def main() -> None:
147-
parser = argparse.ArgumentParser()
148-
149-
parser.add_argument("input_file", type=str, help="TODO")
150-
parser.add_argument("root_path", type=str, help="TODO")
151-
parser.add_argument(
152-
"--long",
153-
action="store_true",
154-
help="Run the fuzzer in long mode (more iterations).",
155-
)
156-
157-
args = parser.parse_args()
158-
159-
path_to_input_file = args.input_file
160-
path_to_root = args.root_path
161-
145+
def fuzz_test(
146+
*, path_to_input_file: str, path_to_root: str, total_mutations: int = 20
147+
) -> None:
162148
shutil.rmtree("output", ignore_errors=True)
163149
Path("output").mkdir(parents=True, exist_ok=True)
164150

165-
total_runs = 200 if args.long else 20
166151
success_count, failure_count = 0, 0
167-
for i in range(1, total_runs + 1):
152+
for i in range(1, total_mutations + 1):
168153
print( # noqa: T201
169-
f"html2pdf4doc_fuzzer print cycle #{i}/{total_runs} — "
154+
f"html2pdf4doc_fuzzer print cycle #{i}/{total_mutations} — "
170155
f"So far: 🟢{success_count} / 🔴{failure_count}",
171156
flush=True,
172157
)
@@ -176,18 +161,44 @@ def main() -> None:
176161
else:
177162
failure_count += 1
178163

179-
assert total_runs > 0
180-
success_rate_percent = (success_count / total_runs) * 100
164+
assert total_mutations > 0
165+
success_rate_percent = (success_count / total_mutations) * 100
181166

182167
print( # noqa: T201
183168
f"html2pdf4doc_fuzzer: finished {'✅' if failure_count == 0 else '❌'} — "
184-
f"Success rate: {success_count}/{total_runs} ({success_rate_percent}%)",
169+
f"Success rate: {success_count}/{total_mutations} ({success_rate_percent}%)",
185170
flush=True,
186171
)
187172

188173
if failure_count > 0:
189174
sys.exit(1)
190175

191176

177+
def main() -> None:
178+
parser = argparse.ArgumentParser()
179+
180+
parser.add_argument("input_file", type=str, help="TODO")
181+
parser.add_argument("root_path", type=str, help="TODO")
182+
parser.add_argument(
183+
"--total-mutations",
184+
type=int,
185+
choices=range(1, 1001),
186+
required=True,
187+
help="An integer between 1 and 1000",
188+
)
189+
190+
args = parser.parse_args()
191+
192+
path_to_input_file = args.input_file
193+
path_to_root = args.root_path
194+
total_mutations = args.total_mutations
195+
196+
fuzz_test(
197+
path_to_input_file=path_to_input_file,
198+
path_to_root=path_to_root,
199+
total_mutations=total_mutations,
200+
)
201+
202+
192203
if __name__ == "__main__":
193204
main()

pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ requires = ["hatchling"]
33
build-backend = "hatchling.build"
44

55
[tool.hatch.version]
6-
path = "html2pdf4doc/html2pdf4doc.py"
6+
path = "html2pdf4doc/__init__.py"
77

88
[tool.hatch.build]
99
# Currently unused:
@@ -13,7 +13,7 @@ path = "html2pdf4doc/html2pdf4doc.py"
1313
# ignore-vcs = true
1414

1515
include = [
16-
"html2pdf4doc/html2pdf4doc.py",
16+
"html2pdf4doc/*.py",
1717
"html2pdf4doc/html2pdf4doc_js/html2pdf4doc.min.js",
1818
]
1919

@@ -69,8 +69,8 @@ development = [
6969
]
7070

7171
[project.scripts]
72-
html2pdf4doc = "html2pdf4doc.html2pdf4doc:main"
73-
html2pdf4doc_fuzzer = "html2pdf4doc.html2pdf4doc_fuzzer:main"
72+
html2pdf4doc = "html2pdf4doc.main:main"
73+
html2pdf4doc_fuzzer = "html2pdf4doc.main_fuzzer:main"
7474

7575
[project.urls]
7676
Changelog = "https://github.com/mettta/html2pdf_python/releases/"

requirements.development.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ twine
1111
mypy>=0.910
1212
ruff>=0.9
1313

14+
#
15+
# Unit tests
16+
#
17+
pytest
18+
1419
#
1520
# Integration tests
1621
#

tasks.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def get_chrome_driver(
102102
run_invoke(
103103
context,
104104
"""
105-
python html2pdf4doc/html2pdf4doc.py get_driver
105+
python -m html2pdf4doc.main get_driver
106106
""",
107107
)
108108

@@ -173,9 +173,7 @@ def test_integration(
173173

174174
get_chrome_driver(context)
175175

176-
cwd = os.getcwd()
177-
178-
html2pdf_exec = f'python3 \\"{cwd}/html2pdf4doc/html2pdf4doc.py\\"'
176+
html2pdf_exec = "python3 -m html2pdf4doc.main"
179177

180178
focus_or_none = f"--filter {focus}" if focus else ""
181179
debug_opts = "-vv --show-all" if debug else ""
@@ -201,16 +199,37 @@ def test_integration(
201199

202200

203201
@task(aliases=["tf"])
204-
def test_fuzz(context, long: bool = False):
205-
arg_long = "--long" if long else ""
202+
def test_fuzz(context, focus=None, total_mutations: int = 10, output=False):
203+
"""
204+
@relation(SDOC-SRS-44, scope=function)
205+
"""
206+
207+
test_reports_dir = "build/test_reports"
208+
209+
Path(test_reports_dir).mkdir(parents=True, exist_ok=True)
210+
211+
focus_argument = f"-k {focus}" if focus is not None else ""
212+
long_argument = (
213+
f"--fuzz-total-mutations {total_mutations}" if total_mutations else ""
214+
)
215+
output_argument = "--capture=no" if output else ""
216+
217+
run_invoke(
218+
context,
219+
"""
220+
rm -rf build/tests_fuzz
221+
""",
222+
)
206223

207224
run_invoke(
208225
context,
209226
f"""
210-
python html2pdf4doc/html2pdf4doc_fuzzer.py
211-
tests/fuzz/01_strictdoc_guide_202510/strictdoc/docs/strictdoc_01_user_guide-PDF.html
212-
tests/fuzz/01_strictdoc_guide_202510/
213-
{arg_long}
227+
pytest
228+
{focus_argument}
229+
{long_argument}
230+
{output_argument}
231+
-o cache_dir=build/tests_fuzz_cache
232+
tests/fuzz/
214233
""",
215234
)
216235

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import os
2+
3+
from html2pdf4doc.main_fuzzer import fuzz_test
4+
from tests.fuzz.conftest import create_build_folder, FuzzConfig
5+
6+
PATH_TO_THIS_FOLDER = os.path.dirname(__file__)
7+
8+
def test(fuzz_config: FuzzConfig):
9+
build_folder = create_build_folder(PATH_TO_THIS_FOLDER)
10+
11+
fuzz_test(
12+
path_to_input_file=os.path.join(
13+
build_folder,
14+
"strictdoc/docs/strictdoc_01_user_guide-PDF.html"
15+
),
16+
path_to_root=build_folder,
17+
total_mutations=fuzz_config.total_mutations
18+
)

0 commit comments

Comments
 (0)