Files
wehub-resource-sync da6a09ff09
Auto Bump and Release / auto-bump-and-release (push) Has been cancelled
style-check / pre-commit (push) Has been cancelled
unit-test / unit testing with python (push) Has been cancelled
unit-test / unit testing with python 3.10 (push) Has been cancelled
unit-test / unit testing with python 3.11 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:49 +08:00

318 lines
9.6 KiB
Python

from __future__ import annotations
from pathlib import Path
from typing import TYPE_CHECKING
from unittest.mock import MagicMock, PropertyMock
import pytest
from kotaemon.base import Document
from kotaemon.loaders.paddleocr_loader.adapter import PaddleOCRResult
from .conftest import skip_when_paddleocr_not_installed
if TYPE_CHECKING:
from pytest_mock.plugin import MockerFixture
def _make_page_result(inner_dict: dict) -> MagicMock:
"""Build a mock page result with .json['res'] as used by the adapter."""
page = MagicMock()
page.json = {"res": inner_dict}
return page
# ---------------------------------------------------------------------------
# PaddleOCRResult adapter tests
# ---------------------------------------------------------------------------
def test_clean_table_html_strips_wrapper() -> None:
"""_clean_table_html removes <html><body> wrapper."""
raw = [_make_page_result({"page_index": 0, "parsing_res_list": []})]
result = PaddleOCRResult(
raw_result=raw, file_path=Path("/tmp/doc.pdf"), extra_info={}
)
out = result._clean_table_html("<html><body><table>x</table></body></html>")
assert out.strip() == "<table>x</table>"
def test_file_name_property() -> None:
"""file_name returns the path name."""
raw = [_make_page_result({"page_index": 0, "parsing_res_list": []})]
result = PaddleOCRResult(
raw_result=raw,
file_path=Path("/some/dir/doc.pdf"),
extra_info={},
)
assert result.file_name == "doc.pdf"
def test_to_documents_text_and_table() -> None:
"""to_documents returns text and table docs from parsing_res_list."""
raw = [
_make_page_result(
{
"page_index": 0,
"parsing_res_list": [
{"block_label": "paragraph_title", "block_content": "Title"},
{"block_label": "text", "block_content": "Body text."},
{
"block_label": "table",
"block_content": "<html><body><table><tr><td>A</td></tr>"
"</table></body></html>",
},
],
}
)
]
result = PaddleOCRResult(
raw_result=raw,
file_path=Path("doc.pdf"),
extra_info={"source": "test"},
)
docs = result.to_documents()
text_docs = [d for d in docs if d.metadata.get("type") != "table"]
table_docs = [d for d in docs if d.metadata.get("type") == "table"]
assert len(text_docs) == 1
assert "Title" in text_docs[0].text and "Body text." in text_docs[0].text
assert text_docs[0].metadata.get("source") == "test"
assert len(table_docs) == 1
assert "A" in table_docs[0].text
assert table_docs[0].metadata["table_origin"]
assert table_docs[0].metadata["page_label"] == 1
def test_to_documents_skips_empty_content() -> None:
"""Blocks with empty block_content are skipped."""
raw = [
_make_page_result(
{
"page_index": 0,
"parsing_res_list": [
{"block_label": "text", "block_content": ""},
{"block_label": "paragraph_title", "block_content": "Only"},
],
}
)
]
result = PaddleOCRResult(raw_result=raw, file_path=Path("x.pdf"), extra_info={})
docs = result.to_documents()
assert len(docs) == 1
assert docs[0].text.strip() == "Only"
# ---------------------------------------------------------------------------
# PPStructureV3Reader tests
# ---------------------------------------------------------------------------
@skip_when_paddleocr_not_installed
def test_ppstructure_v3_unsupported_file_type_raises() -> None:
"""load_data raises ValueError for unsupported extension."""
from kotaemon.loaders import PPStructureV3Reader
reader = PPStructureV3Reader()
with pytest.raises(ValueError, match="Unsupported file type"):
reader.load_data(Path("/tmp/file.xyz"))
@skip_when_paddleocr_not_installed
def test_ppstructure_v3_supported_file_types_attribute() -> None:
"""Reader has expected supported_file_types."""
from kotaemon.loaders import PPStructureV3Reader
reader = PPStructureV3Reader()
assert ".pdf" in reader.supported_file_types
assert ".png" in reader.supported_file_types
@skip_when_paddleocr_not_installed
def test_ppstructure_v3_load_data_with_mocked_pipeline(
mocker: MockerFixture,
) -> None:
"""load_data returns list of Documents when pipeline is mocked."""
from kotaemon.loaders.paddleocr_loader.ppstructure_v3_loader import (
PPStructureV3Reader,
)
mock_raw = [
_make_page_result(
{
"page_index": 0,
"parsing_res_list": [
{"block_label": "text", "block_content": "Hello"},
],
}
)
]
mock_pipeline = MagicMock()
mock_pipeline.predict.return_value = mock_raw
mocker.patch.object(
PPStructureV3Reader,
"pipeline_",
new_callable=PropertyMock,
return_value=mock_pipeline,
)
reader = PPStructureV3Reader()
docs = reader.load_data(Path("/tmp/doc.pdf"), extra_info={})
assert len(docs) >= 1
assert all(isinstance(d, Document) for d in docs)
mock_pipeline.predict.assert_called_once_with("/tmp/doc.pdf")
@skip_when_paddleocr_not_installed
@pytest.mark.parametrize(
"device",
("cpu", "cpu:0"),
)
def test_ppstructure_v3_cpu_disables_mkldnn(
mocker: MockerFixture,
device: str,
) -> None:
"""CPU init passes enable_mkldnn=False (Paddle#77340 workaround)."""
from kotaemon.loaders.paddleocr_loader import ppstructure_v3_loader
mock_pipeline = MagicMock()
mock_ctor = mocker.patch(
"paddleocr.PPStructureV3",
return_value=mock_pipeline,
)
reader = ppstructure_v3_loader.PPStructureV3Reader(device=device)
_ = reader.pipeline_
mock_ctor.assert_called_once()
assert mock_ctor.call_args.kwargs["enable_mkldnn"] is False
@skip_when_paddleocr_not_installed
def test_ppstructure_v3_gpu_omits_mkldnn_override(
mocker: MockerFixture,
) -> None:
"""GPU init leaves enable_mkldnn to paddleocr defaults."""
from kotaemon.loaders.paddleocr_loader import ppstructure_v3_loader
mock_pipeline = MagicMock()
mock_ctor = mocker.patch(
"paddleocr.PPStructureV3",
return_value=mock_pipeline,
)
reader = ppstructure_v3_loader.PPStructureV3Reader(device="gpu:0")
_ = reader.pipeline_
mock_ctor.assert_called_once()
assert "enable_mkldnn" not in mock_ctor.call_args.kwargs
# ---------------------------------------------------------------------------
# PaddleOCRVLReader tests
# ---------------------------------------------------------------------------
@skip_when_paddleocr_not_installed
def test_paddleocr_vl_unsupported_file_type_raises() -> None:
"""load_data raises ValueError for unsupported extension."""
from kotaemon.loaders import PaddleOCRVLReader
reader = PaddleOCRVLReader()
with pytest.raises(ValueError, match="Unsupported file type"):
reader.load_data(Path("/tmp/file.xyz"))
@skip_when_paddleocr_not_installed
def test_paddleocr_vl_supported_file_types_attribute() -> None:
"""Reader has expected supported_file_types."""
from kotaemon.loaders import PaddleOCRVLReader
reader = PaddleOCRVLReader()
assert ".pdf" in reader.supported_file_types
assert ".png" in reader.supported_file_types
@skip_when_paddleocr_not_installed
def test_paddleocr_vl_load_data_with_mocked_pipeline(
mocker: MockerFixture,
) -> None:
"""load_data returns list of Documents when pipeline is mocked."""
from kotaemon.loaders import PaddleOCRVLReader
mock_raw = [
_make_page_result(
{
"page_index": 0,
"parsing_res_list": [
{"block_label": "text", "block_content": "VL text"},
],
}
)
]
mock_pipeline = MagicMock()
mock_pipeline.predict.return_value = mock_raw
mocker.patch.object(
PaddleOCRVLReader,
"pipeline_",
new_callable=PropertyMock,
return_value=mock_pipeline,
)
reader = PaddleOCRVLReader()
docs = reader.load_data(Path("/tmp/img.png"))
assert len(docs) >= 1
assert all(isinstance(d, Document) for d in docs)
mock_pipeline.predict.assert_called_once_with("/tmp/img.png")
@skip_when_paddleocr_not_installed
@pytest.mark.parametrize(
"device",
("cpu", "cpu:0"),
)
def test_paddleocr_vl_cpu_disables_mkldnn(
mocker: MockerFixture,
device: str,
) -> None:
"""CPU init passes enable_mkldnn=False (Paddle#77340 workaround)."""
from kotaemon.loaders.paddleocr_loader import paddleocr_vl_loader
mock_pipeline = MagicMock()
mock_ctor = mocker.patch(
"paddleocr.PaddleOCRVL",
return_value=mock_pipeline,
)
reader = paddleocr_vl_loader.PaddleOCRVLReader(device=device)
_ = reader.pipeline_
mock_ctor.assert_called_once()
assert mock_ctor.call_args.kwargs["enable_mkldnn"] is False
@skip_when_paddleocr_not_installed
def test_paddleocr_vl_gpu_omits_mkldnn_override(
mocker: MockerFixture,
) -> None:
"""GPU init leaves enable_mkldnn to paddleocr defaults."""
from kotaemon.loaders.paddleocr_loader import paddleocr_vl_loader
mock_pipeline = MagicMock()
mock_ctor = mocker.patch(
"paddleocr.PaddleOCRVL",
return_value=mock_pipeline,
)
reader = paddleocr_vl_loader.PaddleOCRVLReader(device="gpu:0")
_ = reader.pipeline_
mock_ctor.assert_called_once()
assert "enable_mkldnn" not in mock_ctor.call_args.kwargs