dangerzone/tests/conftest.py
Alexis Métaireau acd8717839
Update container installation logic to allow in-place updates
The isolation provider `install()` method is now passed a
`should_upgrade` argument, which is read from the settings and
represents the user decision about updates.

The tests have been updated to reflect these changes.
2025-04-22 12:55:46 +02:00

198 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import platform
import sys
import typing
import zipfile
from pathlib import Path
from typing import Callable, List
import pytest
from dangerzone.document import SAFE_EXTENSION
from dangerzone.gui import Application
from dangerzone.isolation_provider import container
sys.dangerzone_dev = True # type: ignore[attr-defined]
ASSETS_PATH = Path(__file__).parent / "assets"
TEST_PUBKEY_PATH = ASSETS_PATH / "test.pub.key"
INVALID_SIGNATURES_PATH = ASSETS_PATH / "signatures" / "invalid"
VALID_SIGNATURES_PATH = ASSETS_PATH / "signatures" / "valid"
TEMPERED_SIGNATURES_PATH = ASSETS_PATH / "signatures" / "tempered"
# Use this fixture to make `pytest-qt` invoke our custom QApplication.
# See https://pytest-qt.readthedocs.io/en/latest/qapplication.html#testing-custom-qapplications
@pytest.fixture(scope="session")
def qapp_cls() -> typing.Type[Application]:
return Application
@pytest.fixture
def unreadable_pdf(tmp_path: Path) -> str:
file_path = tmp_path / "document.pdf"
file_path.touch(mode=0o000)
return str(file_path)
@pytest.fixture
def pdf_11k_pages(tmp_path: Path) -> str:
"""11K page document with pages of 1x1 px. Generated with the command:
gs -sDEVICE=pdfwrite -o sample-11k-pages.pdf -dDEVICEWIDTHPOINTS=1 -dDEVICEHEIGHTPOINTS=1 -c 11000 {showpage} repeat
"""
filename = "sample-11k-pages.pdf"
zip_path = test_docs_compressed_dir / f"{filename}.zip"
with zipfile.ZipFile(zip_path, "r") as zip_file:
zip_file.extractall(tmp_path)
return str(tmp_path / filename)
@pytest.fixture
def uncommon_text() -> str:
"""Craft a string with Unicode characters that are considered not common.
Create a string that contains the following uncommon characters:
* ANSI escape sequences: \033[31;1;4m and \033[0m
* A Unicode character that resembles an English character: greek "X" (U+03A7)
* A Unicode control character that is not part of ASCII: zero-width joiner
(U+200D)
* An emoji: Cross Mark (U+274C)
* A surrogate escape used to decode an invalid UTF-8 sequence 0xF0 (U+DCF0)
"""
return "\033[31;1;4m BaD TeΧt \u200d\udcf0 \033[0m"
@pytest.fixture
def uncommon_filename(uncommon_text: str) -> str:
"""Craft a filename with Unicode characters that are considered not common.
We reuse the same uncommon string as above, with a small exception for macOS and
Windows.
Because the NTFS filesystem in Windows and APFS filesystem in macOS accept only
UTF-8 encoded strings [1], we cannot create a filename with invalid Unicode
characters. So, in order to test the rest of the corner cases, we replace U+DCF0
with an empty string.
Windows has the extra restriction that it cannot have escape characters in
filenames, so we replace the ASCII Escape character (\033 / U+001B) as well.
[1]: https://en.wikipedia.org/wiki/Filename#Comparison_of_filename_limitations
"""
if platform.system() == "Darwin":
uncommon_text = uncommon_text.replace("\udcf0", "")
elif platform.system() == "Windows":
uncommon_text = uncommon_text.replace("\udcf0", "").replace("\033", "")
return uncommon_text + ".pdf"
@pytest.fixture
def sanitized_text() -> str:
"""Return a sanitized version of the uncommon_text.
Take the uncommon text string and replace all the control/invalid characters with
"<EFBFBD>". The rest of the characters (emojis and non-English leters) are retained as is.
"""
return "<EFBFBD>[31;1;4m BaD TeΧt <20><20> <20>[0m"
@pytest.fixture
def sample_doc() -> str:
return str(test_docs_dir.joinpath(BASIC_SAMPLE_DOC))
@pytest.fixture
def sample_bad_height() -> str:
return str(test_docs_dir.joinpath("sample_bad_max_height.pdf"))
@pytest.fixture
def sample_bad_width() -> str:
return str(test_docs_dir.joinpath("sample_bad_max_width.pdf"))
@pytest.fixture
def sample_pdf() -> str:
return str(test_docs_dir.joinpath(BASIC_SAMPLE_PDF))
SAMPLE_DIRECTORY = "test_docs"
BASIC_SAMPLE_PDF = "sample-pdf.pdf"
BASIC_SAMPLE_DOC = "sample-doc.doc"
SAMPLE_EXTERNAL_DIRECTORY = "test_docs_external"
SAMPLE_COMPRESSED_DIRECTORY = "test_docs_compressed"
test_docs_dir = Path(__file__).parent.joinpath(SAMPLE_DIRECTORY)
test_docs_compressed_dir = Path(__file__).parent.joinpath(SAMPLE_COMPRESSED_DIRECTORY)
test_docs = [
p
for p in test_docs_dir.glob("*")
if p.is_file()
and not (p.name.endswith(SAFE_EXTENSION) or p.name.startswith("sample_bad"))
]
# Pytest parameter decorators
for_each_doc = pytest.mark.parametrize(
"doc", test_docs, ids=[str(doc.name) for doc in test_docs]
)
# External Docs - base64 docs encoded for externally sourced documents
# XXX to reduce the chance of accidentally opening them
test_docs_external_dir = Path(__file__).parent.joinpath(SAMPLE_EXTERNAL_DIRECTORY)
def get_docs_external(pattern: str = "*") -> List[Path]:
if not pattern.endswith("*"):
pattern = f"{pattern}.b64"
return [
p
for p in test_docs_external_dir.rglob(pattern)
if p.is_file() and not (p.name.endswith(SAFE_EXTENSION))
]
# Pytest parameter decorators
def for_each_external_doc(glob_pattern: str = "*") -> Callable:
test_docs_external = get_docs_external(glob_pattern)
return pytest.mark.parametrize(
"doc",
test_docs_external,
ids=[str(doc.name).rstrip(".b64") for doc in test_docs_external],
)
class TestBase:
sample_doc = str(test_docs_dir.joinpath(BASIC_SAMPLE_PDF))
def pytest_configure(config: pytest.Config) -> None:
config.addinivalue_line(
"markers",
"reference_generator: Used to mark the test cases that regenerate reference documents",
)
def pytest_addoption(parser: pytest.Parser) -> None:
parser.addoption(
"--generate-reference-pdfs",
action="store_true",
default=False,
help="Regenerate reference PDFs",
)
def pytest_collection_modifyitems(
config: pytest.Config, items: List[pytest.Item]
) -> None:
if not config.getoption("--generate-reference-pdfs"):
skip_generator = pytest.mark.skip(
reason="Only run when --generate-reference-pdfs is provided"
)
for item in items:
if "reference_generator" in item.keywords:
item.add_marker(skip_generator)