"""Testes da extração paralela de páginas. O OCR é o gargalo da indexação: ~1,2 s por página. Para 3.364 páginas, a extração sequencial levaria mais de uma hora. A paralelização usa subprocessos do Tesseract, que é seguro porque cada página é um processo independente. """ from __future__ import annotations import sys import time from pathlib import Path import pytest sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from app.extraction import PdfExtractor # noqa: E402 from app.parallel import parallel_map # noqa: E402 def test_parallel_map_preserves_order(): results = parallel_map([1, 2, 3, 4, 5], lambda value: value * 2, workers=4) assert results == [2, 4, 6, 8, 10] def test_parallel_map_handles_empty_input(): assert parallel_map([], lambda value: value, workers=4) == [] def test_parallel_map_is_faster_than_sequential_for_io_bound_work(): def slow(value: int) -> int: time.sleep(0.2) return value start = time.monotonic() sequential = [slow(value) for value in range(4)] sequential_elapsed = time.monotonic() - start start = time.monotonic() parallel = parallel_map(list(range(4)), slow, workers=4) parallel_elapsed = time.monotonic() - start assert sequential == parallel assert parallel_elapsed < sequential_elapsed def test_parallel_map_isolates_worker_failures(): def flaky(value: int) -> int: if value == 2: raise ValueError("falhou") return value results = parallel_map([1, 2, 3], flaky, workers=2, default=None) assert results[0] == 1 assert results[2] == 3 def test_parallel_map_uses_threads_by_default(): """Threads bastam quando o trabalho é predominantemente subprocesso.""" import threading barrier = threading.Barrier(2, timeout=5) seen: list[str] = [] def worker(value: int) -> int: seen.append(threading.current_thread().name) # A barreira só passa se as duas tarefas rodarem em threads distintas. barrier.wait() return value parallel_map([1, 2], worker, workers=2) assert len(set(seen)) == 2, "esperava duas threads distintas"