111 lines
3.8 KiB
Python
111 lines
3.8 KiB
Python
|
|
|
|
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 = """
|
|
<html>
|
|
<body>
|
|
<p>Paragraph 1</p>
|
|
<div class="nav">Navigation Content</div>
|
|
<p>Paragraph 2</p>
|
|
</body>
|
|
</html>
|
|
"""
|
|
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}")
|