Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# HTML2PDF4Doc JS file.
# html2pdf4doc/html2pdf4doc_js/

.venv/
.vscode
.idea/
**/.wdm/
build/
Expand Down
42 changes: 39 additions & 3 deletions html2pdf4doc/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,12 @@ def get_inches_from_millimeters(mm: float) -> float:
return mm / 25.4


def get_pdf_from_html(driver: webdriver.Chrome, url: str) -> Tuple[bytes, int]:
def get_pdf_from_html(
*,
driver: webdriver.Chrome,
url: str,
strict_mode_2: bool = False,
) -> Tuple[bytes, int]:
print(f"html2pdf4doc: Opening URL with ChromeDriver: {url}", flush=True) # noqa: T201

driver.get(url)
Expand Down Expand Up @@ -357,12 +362,29 @@ def __init__(self, page_count: int):
)
sys.exit(1)

bad_logs: List[Dict[str, str]] = []

print("html2pdf4doc: JS logs from the print session:") # noqa: T201
print('"""') # noqa: T201
for entry in logs:
print(entry) # noqa: T201

if entry["level"] not in ("INFO", "DEBUG"):
bad_logs.append(entry)

print('"""') # noqa: T201

if len(bad_logs) > 0:
bad_logs_error_message = (
"html2pdf4doc: Something went wrong: "
"Detected console error/warning messages:\n"
f"{bad_logs}"
)
if strict_mode_2:
raise RuntimeError(bad_logs_error_message)
else:
print(bad_logs_error_message) # noqa: T201

#
# Execute Print command with ChromeDriver.
#
Expand Down Expand Up @@ -530,11 +552,21 @@ def main() -> None:
action="store_true",
help=(
"Enables Strict mode. In this mode, the library always performs a "
"validation of printed pages and raise a runtime error if the "
"validation of printed pages and raises a runtime error if the "
"validation fails. Without the Strict mode enabled, only a warning "
"message is printed and the execution continues."
),
)
command_parser_print.add_argument(
"--strict2",
action="store_true",
help=(
"Enables Strict mode (level 2). In this mode, the library always performs a "
"validation of console logs and raises a runtime error if there are "
"error/warning/severe messages. Without the Strict (level 2) mode enabled, "
"only a warning message is printed and the execution continues."
),
)
command_parser_print.add_argument(
"paths", nargs="+", help="Paths to input HTML file."
)
Expand Down Expand Up @@ -587,7 +619,11 @@ def exit_handler() -> None:

url = Path(os.path.abspath(path_to_input_html)).as_uri()

pdf_bytes, page_count = get_pdf_from_html(driver, url)
pdf_bytes, page_count = get_pdf_from_html(
driver=driver,
url=url,
strict_mode_2=args.strict2,
)
with open(path_to_output_pdf, "wb") as f:
f.write(pdf_bytes)

Expand Down
71 changes: 58 additions & 13 deletions html2pdf4doc/main_fuzzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ def measure_performance(title: str) -> Iterator[None]:
print(f"{padded_name}{padded_time}s", flush=True) # noqa: T201


def mutate_and_print(path_to_input_file: str, path_to_root: str) -> bool:
def mutate_and_print(
*,
path_to_input_file: str,
path_to_root: str,
path_to_failed_mutants_dir: str,
strict_mode: bool = False,
strict_mode_2: bool = False,
) -> bool:
assert os.path.isfile(path_to_input_file), path_to_input_file
assert os.path.isdir(path_to_root), path_to_root
if not os.path.abspath(path_to_root):
Expand Down Expand Up @@ -72,25 +79,31 @@ def mutate_and_print(path_to_input_file: str, path_to_root: str) -> bool:
"-m",
"html2pdf4doc.main",
"print",
"--strict",
]
if strict_mode:
cmd.append("--strict")
if strict_mode_2:
cmd.append("--strict2")

for path_to_print_ in paths_to_print:
cmd.append(path_to_print_[0])
cmd.append(path_to_print_[1])

relative_path_to_mut_html = Path(path_to_mut_html).relative_to(path_to_root)
path_to_mut_output = f"output/{relative_path_to_mut_html}"
path_to_mut_output = os.path.join(
path_to_failed_mutants_dir, relative_path_to_mut_html
)

def copy_files_if_needed() -> None:
if os.path.isdir(path_to_mut_output):
return

shutil.rmtree("output", ignore_errors=True)
Path("output").mkdir(parents=True, exist_ok=True)
Path(path_to_failed_mutants_dir).mkdir(parents=True, exist_ok=True)

shutil.copytree(
"html2pdf4doc", "output/html2pdf4doc", dirs_exist_ok=True
"html2pdf4doc",
os.path.join(path_to_failed_mutants_dir, "html2pdf4doc"),
dirs_exist_ok=True,
)

shutil.rmtree(path_to_mut_output, ignore_errors=True)
Expand All @@ -110,6 +123,12 @@ def copy_mutated_file() -> None:
)
shutil.copy(path_to_mut_html, path_to_mut_html_out)

if not os.path.isfile(path_to_mut_pdf):
print( # noqa: T201
f"html2pdf4doc_fuzzer: warning: Mutated PDF is missing: {path_to_mut_pdf}"
)
return

path_to_mut_pdf_out = os.path.join(
path_to_mut_output,
f"{relative_path_to_mut_html}.{timestamp}.pdf",
Expand Down Expand Up @@ -143,19 +162,28 @@ def copy_mutated_file() -> None:


def fuzz_test(
*, path_to_input_file: str, path_to_root: str, total_mutations: int = 20
*,
path_to_input_file: str,
path_to_root: str,
path_to_failed_mutants_dir: str,
total_mutations: int = 20,
strict_mode: bool = False,
strict_mode_2: bool = False,
) -> None:
shutil.rmtree("output", ignore_errors=True)
Path("output").mkdir(parents=True, exist_ok=True)

success_count, failure_count = 0, 0
for i in range(1, total_mutations + 1):
print( # noqa: T201
f"html2pdf4doc_fuzzer print cycle #{i}/{total_mutations} — "
f"So far: 🟢{success_count} / 🔴{failure_count}",
flush=True,
)
success = mutate_and_print(path_to_input_file, path_to_root)
success = mutate_and_print(
path_to_input_file=path_to_input_file,
path_to_root=path_to_root,
path_to_failed_mutants_dir=path_to_failed_mutants_dir,
strict_mode=strict_mode,
strict_mode_2=strict_mode_2,
)
if success:
success_count += 1
else:
Expand All @@ -182,24 +210,41 @@ def main() -> None:

parser.add_argument("input_file", type=str, help="TODO")
parser.add_argument("root_path", type=str, help="TODO")
parser.add_argument("path_to_failed_mutants_dir", type=str, help="TODO")
parser.add_argument(
"--total-mutations",
type=int,
choices=range(1, 1001),
required=True,
help="An integer between 1 and 1000",
)

parser.add_argument(
"--strict",
action="store_true",
help="Enables Strict mode (level 1).",
)
parser.add_argument(
"--strict2",
action="store_true",
help="Enables Strict mode (level 2).",
)
args = parser.parse_args()

path_to_input_file = args.input_file
path_to_root = args.root_path
path_to_failed_mutants_dir = args.path_to_failed_mutants_dir
total_mutations = args.total_mutations
assert 1 <= total_mutations <= 1000, total_mutations

strict_mode = args.strict
strict_mode_2 = args.strict2

fuzz_test(
path_to_input_file=path_to_input_file,
path_to_root=path_to_root,
path_to_failed_mutants_dir=path_to_failed_mutants_dir,
total_mutations=total_mutations,
strict_mode=strict_mode,
strict_mode_2=strict_mode_2,
)


Expand Down
2 changes: 1 addition & 1 deletion tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def test_fuzz(context, focus=None, total_mutations: int = 10, output=False):

focus_argument = f"-k {focus}" if focus is not None else ""
long_argument = (
f"--fuzz-total-mutations {total_mutations}" if total_mutations else ""
f"--fuzz-total-mutations={total_mutations}" if total_mutations else ""
)
output_argument = "--capture=no" if output else ""

Expand Down
7 changes: 5 additions & 2 deletions tests/fuzz/01_strictdoc_guide_202510/test_case.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os

from html2pdf4doc.main_fuzzer import fuzz_test
from tests.fuzz.conftest import create_build_folder, FuzzConfig
from tests.fuzz.conftest import create_build_folder, FuzzConfig, create_failed_mutants_folder

PATH_TO_THIS_FOLDER = os.path.dirname(__file__)

Expand All @@ -14,5 +14,8 @@ def test(fuzz_config: FuzzConfig):
"strictdoc/docs/strictdoc_01_user_guide-PDF.html"
),
path_to_root=build_folder,
total_mutations=fuzz_config.total_mutations
path_to_failed_mutants_dir=create_failed_mutants_folder(PATH_TO_THIS_FOLDER),
total_mutations=fuzz_config.total_mutations,
strict_mode=True,
strict_mode_2=False,
)
17 changes: 17 additions & 0 deletions tests/fuzz/conftest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime
import os
import shutil
from dataclasses import dataclass
Expand Down Expand Up @@ -46,3 +47,19 @@ def create_build_folder(test_folder: str) -> str:
shutil.copytree(test_folder, build_folder)

return build_folder


def create_failed_mutants_folder(test_folder: str) -> str:
assert os.path.isdir(test_folder), test_folder
assert os.path.isabs(test_folder), test_folder

relative_path_to_test_folder = Path(test_folder).relative_to(PATH_TO_TESTS_FUZZ_FOLDER)

mutants_folder = os.path.join(
"build",
"tests_fuzz_failed_mutants",
datetime.datetime.now().strftime("%Y%m%d_%H%M%S"),
relative_path_to_test_folder
)

return mutants_folder
2 changes: 1 addition & 1 deletion tests/integration/20_fuzz_integration/test.itest
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
RUN: mkdir -p %project_root/build/tests_integration_fuzz/
RUN: cp -rv %project_root/tests/fuzz/01_strictdoc_guide_202510 %project_root/build/tests_integration_fuzz/

RUN: PYTHONPATH=%project_root python -m html2pdf4doc.main_fuzzer %project_root/build/tests_integration_fuzz/01_strictdoc_guide_202510/strictdoc/docs/strictdoc_01_user_guide-PDF.html %project_root/build/tests_integration_fuzz/01_strictdoc_guide_202510 --total-mutations 1 | filecheck %s --dump-input=fail
RUN: PYTHONPATH=%project_root python -m html2pdf4doc.main_fuzzer %project_root/build/tests_integration_fuzz/01_strictdoc_guide_202510/strictdoc/docs/strictdoc_01_user_guide-PDF.html %project_root/build/tests_integration_fuzz/01_strictdoc_guide_202510 %T/ --total-mutations 1 | filecheck %s --dump-input=fail
CHECK: html2pdf4doc_fuzzer: finished ✅ — Success rate: 1/1 (100.0%)
Loading