OCRmyPDF/tests/test_helpers.py
2024-10-27 11:55:22 -07:00

137 lines
4.4 KiB
Python

# SPDX-FileCopyrightText: 2022 James R. Barlow
# SPDX-License-Identifier: MPL-2.0
from __future__ import annotations
import logging
import multiprocessing
import os
from pathlib import Path
from unittest.mock import MagicMock
import pytest
from packaging.version import Version
from ocrmypdf import helpers
from ocrmypdf.helpers import running_in_docker
needs_symlink = pytest.mark.skipif(os.name == 'nt', reason='needs posix symlink')
windows_only = pytest.mark.skipif(os.name != 'nt', reason="Windows test")
class TestSafeSymlink:
def test_safe_symlink_link_self(self, tmp_path, caplog):
helpers.safe_symlink(tmp_path / 'self', tmp_path / 'self')
assert caplog.record_tuples[0][1] == logging.WARNING
def test_safe_symlink_overwrite(self, tmp_path):
(tmp_path / 'regular_file').touch()
with pytest.raises(FileExistsError):
helpers.safe_symlink(tmp_path / 'input', tmp_path / 'regular_file')
@needs_symlink
def test_safe_symlink_relink(self, tmp_path):
(tmp_path / 'regular_file_a').touch()
(tmp_path / 'regular_file_b').write_bytes(b'ABC')
(tmp_path / 'link').symlink_to(tmp_path / 'regular_file_a')
helpers.safe_symlink(tmp_path / 'regular_file_b', tmp_path / 'link')
assert (tmp_path / 'link').samefile(tmp_path / 'regular_file_b') or (
tmp_path / 'link'
).read_bytes() == b'ABC'
def test_no_cpu_count(monkeypatch):
invoked = False
def cpu_count_raises():
nonlocal invoked
invoked = True
raise NotImplementedError()
monkeypatch.setattr(multiprocessing, 'cpu_count', cpu_count_raises)
with pytest.warns(expected_warning=UserWarning):
assert helpers.available_cpu_count() == 1
assert invoked, "Patched function called during test"
skipif_docker = pytest.mark.skipif(running_in_docker(), reason="fails on Docker")
class TestFileIsWritable:
@pytest.fixture
def non_existent(self, tmp_path):
return tmp_path / 'nofile'
@pytest.fixture
def basic_file(self, tmp_path):
basic = tmp_path / 'basic'
basic.touch()
return basic
def test_plain(self, non_existent):
assert helpers.is_file_writable(non_existent)
@needs_symlink
def test_symlink_loop(self, tmp_path):
loop = tmp_path / 'loop'
loop.symlink_to(loop)
assert not helpers.is_file_writable(loop)
@skipif_docker
def test_chmod(self, basic_file):
assert helpers.is_file_writable(basic_file)
basic_file.chmod(0o400)
assert not helpers.is_file_writable(basic_file)
basic_file.chmod(0o000)
assert not helpers.is_file_writable(basic_file)
def test_permission_error(self, basic_file):
pathmock = MagicMock(spec_set=basic_file)
pathmock.is_symlink.return_value = False
pathmock.exists.return_value = True
pathmock.is_file.side_effect = PermissionError
assert not helpers.is_file_writable(pathmock)
@windows_only
def test_gs_install_locations():
# pylint: disable=import-outside-toplevel
from ocrmypdf.subprocess._windows import _gs_version_in_path_key
assert _gs_version_in_path_key(Path("C:\\Program Files\\gs\\gs9.52\\bin")) == (
'gs',
Version('9.52'),
)
@windows_only
def test_shim_paths(tmp_path):
# pylint: disable=import-outside-toplevel
from ocrmypdf.subprocess._windows import shim_env_path
progfiles = tmp_path / 'Program Files'
progfiles.mkdir()
(progfiles / 'tesseract-ocr').mkdir()
(progfiles / 'gs' / '9.51' / 'bin').mkdir(parents=True)
(progfiles / 'gs' / 'gs9.52.3' / 'bin').mkdir(parents=True)
syspath = tmp_path / 'bin'
env = {'PROGRAMFILES': str(progfiles), 'PATH': str(syspath)}
result_str = shim_env_path(env=env)
results = result_str.split(os.pathsep)
assert results[0] == str(syspath), results
assert results[-3].endswith('tesseract-ocr'), results
assert results[-2].endswith(os.path.join('gs9.52.3', 'bin')), results
assert results[-1].endswith(os.path.join('gs', '9.51', 'bin')), results
def test_resolution():
Resolution = helpers.Resolution
dpi_100 = Resolution(100, 100)
dpi_200 = Resolution(200, 200)
assert dpi_100.is_square
assert not Resolution(100, 200).is_square
assert dpi_100 == Resolution(100, 100)
assert str(dpi_100) != str(dpi_200)
assert dpi_100.take_max([200, 300], [400]) == Resolution(300, 400)