mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 13:40:27 +02:00
220 lines
7.8 KiB
Python
220 lines
7.8 KiB
Python
import unittest
|
|
import os
|
|
import sys
|
|
import shutil
|
|
import fitz # PyMuPDF
|
|
from ebooklib import epub
|
|
|
|
# Ensure we can import the module
|
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
|
|
from abogen.book_parser import get_book_parser, PdfParser, EpubParser, MarkdownParser
|
|
|
|
|
|
class TestBookParser(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
self.test_dir = "tests/test_data"
|
|
if os.path.exists(self.test_dir):
|
|
shutil.rmtree(self.test_dir)
|
|
os.makedirs(self.test_dir)
|
|
|
|
self.sample_pdf_path = os.path.join(self.test_dir, "test_book.pdf")
|
|
self.sample_epub_path = os.path.join(self.test_dir, "test_book.epub")
|
|
self.sample_md_path = os.path.join(self.test_dir, "test_book.md")
|
|
|
|
self._create_sample_pdf()
|
|
self._create_sample_epub()
|
|
self._create_sample_md()
|
|
|
|
def tearDown(self):
|
|
if os.path.exists(self.test_dir):
|
|
shutil.rmtree(self.test_dir)
|
|
|
|
def _create_sample_pdf(self):
|
|
doc = fitz.open()
|
|
|
|
# Page 1
|
|
page1 = doc.new_page()
|
|
page1.insert_text((50, 50), "Page 1 content")
|
|
# Add pattern to be cleaned
|
|
page1.insert_text((50, 100), "[12]")
|
|
page1.insert_text((50, 200), "1") # Page number at bottom
|
|
|
|
# Page 2
|
|
page2 = doc.new_page()
|
|
page2.insert_text((50, 50), "Page 2 content")
|
|
|
|
doc.save(self.sample_pdf_path)
|
|
doc.close()
|
|
|
|
def _create_sample_epub(self):
|
|
book = epub.EpubBook()
|
|
book.set_identifier("id123456")
|
|
book.set_title("Sample Book")
|
|
book.set_language("en")
|
|
book.add_author("Test Author")
|
|
|
|
c1 = epub.EpubHtml(title="Intro", file_name="intro.xhtml", lang="en")
|
|
c1.content = "<h1>Introduction</h1><p>Welcome to the book.</p>"
|
|
|
|
c2 = epub.EpubHtml(title="Chapter 1", file_name="chap1.xhtml", lang="en")
|
|
c2.content = "<h1>Chapter 1</h1><ol><li>Item One</li><li>Item Two</li></ol>"
|
|
|
|
book.add_item(c1)
|
|
book.add_item(c2)
|
|
|
|
# Basic spine and nav
|
|
book.spine = ["nav", c1, c2]
|
|
|
|
# Add NCX and NAV for compatibility
|
|
book.add_item(epub.EpubNcx())
|
|
book.add_item(epub.EpubNav())
|
|
|
|
epub.write_epub(self.sample_epub_path, book)
|
|
|
|
def _create_sample_md(self):
|
|
content = "# Chapter 1\nSome text.\n# Chapter 2\nMore text."
|
|
with open(self.sample_md_path, "w") as f:
|
|
f.write(content)
|
|
|
|
def test_factory_returns_correct_class(self):
|
|
"""Test that get_book_parser returns the correct subclass based on extension."""
|
|
parser_pdf = get_book_parser(self.sample_pdf_path)
|
|
self.assertIsInstance(parser_pdf, PdfParser)
|
|
|
|
parser_md = get_book_parser(self.sample_md_path)
|
|
self.assertIsInstance(parser_md, MarkdownParser)
|
|
|
|
parser_epub = get_book_parser(self.sample_epub_path)
|
|
self.assertIsInstance(parser_epub, EpubParser)
|
|
|
|
def test_factory_explicit_type(self):
|
|
"""Test that explicit file type argument overrides extension."""
|
|
# 1. Copy sample epub to something.pdf
|
|
wrong_ext_path = os.path.join(self.test_dir, "actually_epub.pdf")
|
|
shutil.copy(self.sample_epub_path, wrong_ext_path)
|
|
|
|
# 2. Open it telling parser it IS epub
|
|
parser = get_book_parser(wrong_ext_path, file_type="epub")
|
|
self.assertIsInstance(parser, EpubParser)
|
|
|
|
# Should load successfully
|
|
parser.load()
|
|
self.assertTrue(parser.book is not None)
|
|
|
|
def test_pdf_parser_content(self):
|
|
"""Test PdfParser content extraction."""
|
|
parser = get_book_parser(self.sample_pdf_path)
|
|
try:
|
|
parser.process_content()
|
|
|
|
self.assertIn("page_1", parser.content_texts)
|
|
self.assertIn("page_2", parser.content_texts)
|
|
|
|
text1 = parser.content_texts["page_1"]
|
|
self.assertIn("Page 1 content", text1)
|
|
self.assertNotIn("[12]", text1)
|
|
finally:
|
|
parser.close()
|
|
|
|
def test_markdown_parser_content(self):
|
|
"""Test MarkdownParser splitting logic."""
|
|
parser = get_book_parser(self.sample_md_path)
|
|
try:
|
|
parser.process_content()
|
|
|
|
# Should have Chapter 1 and Chapter 2 keys (actual keys depend on ID generation)
|
|
# Markdown extensions might slugify IDs: "chapter-1"
|
|
self.assertIn("chapter-1", parser.content_texts)
|
|
self.assertIn("chapter-2", parser.content_texts)
|
|
|
|
self.assertIn("Some text", parser.content_texts["chapter-1"])
|
|
finally:
|
|
parser.close()
|
|
|
|
def test_epub_parser_content(self):
|
|
"""Test EpubParser processing."""
|
|
parser = get_book_parser(self.sample_epub_path)
|
|
parser.process_content()
|
|
|
|
self.assertIn("intro.xhtml", parser.content_texts)
|
|
self.assertIn("chap1.xhtml", parser.content_texts)
|
|
self.assertIn("Welcome to the book", parser.content_texts["intro.xhtml"])
|
|
|
|
def test_epub_metadata_extraction(self):
|
|
"""Test metadata extraction in EpubParser."""
|
|
parser = get_book_parser(self.sample_epub_path)
|
|
# Processing content triggers metadata extraction in current implementation
|
|
parser.process_content()
|
|
|
|
metadata = parser.get_metadata()
|
|
self.assertEqual(metadata.get("title"), "Sample Book")
|
|
self.assertEqual(metadata.get("author"), "Test Author")
|
|
|
|
def test_ordered_list_handling(self):
|
|
"""Test <ol> handling in EpubParser."""
|
|
parser = get_book_parser(self.sample_epub_path)
|
|
parser.process_content()
|
|
|
|
text = parser.content_texts.get("chap1.xhtml", "")
|
|
self.assertIn("1) Item One", text)
|
|
self.assertIn("2) Item Two", text)
|
|
|
|
def test_find_position_robust_logic(self):
|
|
"""Unit test for _find_position_robust on EpubParser."""
|
|
parser = EpubParser(self.sample_epub_path) # Instantiate directly
|
|
|
|
html = '<html><body><p>Start</p><h1 id="target">Heading</h1><p>End</p></body></html>'
|
|
parser.doc_content["dummy.html"] = html
|
|
|
|
# Test finding ID
|
|
pos = parser._find_position_robust("dummy.html", "target")
|
|
self.assertGreater(pos, 0)
|
|
self.assertTrue(html[pos:].startswith('<h1 id="target"'))
|
|
|
|
# Test missing ID
|
|
pos_missing = parser._find_position_robust("dummy.html", "missing")
|
|
self.assertEqual(pos_missing, 0)
|
|
|
|
def test_get_chapters(self):
|
|
"""Test get_chapters returns correct list for different parsers."""
|
|
# PDF
|
|
parser_pdf = get_book_parser(self.sample_pdf_path)
|
|
parser_pdf.process_content()
|
|
chapters = parser_pdf.get_chapters()
|
|
self.assertEqual(len(chapters), 2)
|
|
self.assertEqual(chapters[0], ("page_1", "Page 1 - Page 1 content"))
|
|
|
|
# MD
|
|
parser_md = get_book_parser(self.sample_md_path)
|
|
parser_md.process_content() # Must process to get structure
|
|
chapters_md = parser_md.get_chapters()
|
|
# Expecting chapter-1, chapter-2
|
|
ids = [c[0] for c in chapters_md]
|
|
self.assertIn("chapter-1", ids)
|
|
|
|
def test_get_formatted_text(self):
|
|
"""Test formatting of full text via BaseBookParser method."""
|
|
parser = get_book_parser(self.sample_md_path)
|
|
parser.process_content()
|
|
text = parser.get_formatted_text()
|
|
|
|
self.assertIn("<<CHAPTER_MARKER:Chapter 1>>", text)
|
|
self.assertIn("Some text", text)
|
|
|
|
def test_file_type_property(self):
|
|
"""Test that file_type property returns correct string for each parser."""
|
|
pdf_parser = PdfParser(self.sample_pdf_path)
|
|
self.assertEqual(pdf_parser.file_type, "pdf")
|
|
|
|
epub_parser = EpubParser(self.sample_epub_path)
|
|
self.assertEqual(epub_parser.file_type, "epub")
|
|
|
|
md_parser = MarkdownParser(self.sample_md_path)
|
|
self.assertEqual(md_parser.file_type, "markdown")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|