from bs4 import BeautifulSoup from unittest.mock import MagicMock from src.bilingual_builder import BilingualEPUBBuilder from src.chinese_builder import ChineseEPUBBuilder # Mock classes to avoid full EPUB dependencies class MockItem: def __init__(self, name, content): self.name = name self.content = content self.title = "Mock Title" self.id = "item_1" def get_name(self): return self.name def get_content(self): return self.content.encode('utf-8') def get_type(self): return 9 # ITEM_DOCUMENT def test_bilingual_builder_misalignment(): """ Reproduces the off-by-one misalignment bug. Scenario: 1. HTML contains: [Para1], [Nav], [Para2] 2. Ordered IDs passed to builder: [ID1, ID2] (assuming Nav is validly ignored by extractor logic but maybe ID list is different? Actually, let's trace the bug logic: Extractor: - Para1 -> Clean -> Valid -> Added to Manifest (Status: Pending) -> ID1 - Nav -> Clean -> Valid -> Added to Manifest (Status: Ignored) -> ID2 - Para2 -> Clean -> Valid -> Added to Manifest (Status: Pending) -> ID3 Builder Input: - translation_map: {ID1: "Trans1", ID3: "Trans3"} (Nav ignored so no trans) - paragraph_map: {ID1: ..., ID2: ..., ID3: ...} - ordered_ids: [ID1, ID2, ID3] (All items in file) Builder Loop (Current Broken Logic): - Scans Para1: Valid, Not Nav. - Match with ordered_ids[0] (ID1). OK. - Incr index -> 1. - Scans Nav: is_navigation_element() == True -> CONTINUE - Index remains 1. - Scans Para2: Valid, Not Nav. - Match with ordered_ids[1] (ID2). - ID2 is the Nav item! - translation_map.get(ID2) -> None (or wrong if ID2 had a translation). - Result: Para2 gets NO translation or WRONG translation. - Expected: Para2 should match ID3. """ # Setup html_content = """
Paragraph 1
Paragraph 2
""" mock_item = MockItem("test.xhtml", html_content) # IDs corresponding to the elements as they would be in Manifest # ID1: Para1, ID2: Nav, ID3: Para2 ordered_ids = ["p_001", "p_002", "p_003"] translation_map = { "p_001": "翻译1", "p_003": "翻译2" # p_002 is ignored, so no translation } # Config config = {'output': {}} mock_book = MagicMock() mock_book.get_metadata.return_value = None builder = BilingualEPUBBuilder(mock_book, config) # Execute private method directly for testing # We mock _add_style_link to do nothing builder._add_style_link = MagicMock() new_item = builder._create_bilingual_document(mock_item, ordered_ids, translation_map) new_content = new_item.get_content().decode('utf-8') soup = BeautifulSoup(new_content, 'html.parser') # Analyze results paragraphs = soup.find_all('p', class_='translation-text') print(f"Generated Paragraphs: {len(paragraphs)}") for p in paragraphs: print(f" - {p.get_text()}") # Assertions # We expect 2 translated paragraphs. # Current BUG: Likely only 1 found (Para1), and Para2 missed because it matched with p_002 which has no translation. assert len(paragraphs) == 2, f"Expected 2 translated paragraphs, found {len(paragraphs)}" assert paragraphs[0].get_text() == "翻译1" assert paragraphs[1].get_text() == "翻译2" if __name__ == "__main__": try: test_bilingual_builder_misalignment() print("Test PASSED") except AssertionError as e: print(f"Test FAILED: {e}") except Exception as e: print(f"Test ERROR: {e}")