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 = "
Introduction
Welcome to the book.
"
c2 = epub.EpubHtml(title="Chapter 1", file_name="chap1.xhtml", lang="en")
c2.content = "Chapter 1
- Item One
- Item Two
"
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)
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)
def test_markdown_parser_content(self):
"""Test MarkdownParser splitting logic."""
parser = get_book_parser(self.sample_md_path)
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"])
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 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 = 'Start
Heading
End
'
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('>", 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()