OPEN SOURCE
software/tests/test_furigana.py
Read on this site and download the files you need.
Nichijoyaku was created using AI. A complete review by human biblical-language and biblical-studies specialists has not been established. Read alongside established translations and original-language resources.
← Back to the open-source guide
1"""Tests for furigana parsing and generation utilities."""23from __future__ import annotations45import pytest67from src.utils.furigana import (8FuriganaAnnotation,9extract_all_readings,10format_furigana_html,11format_furigana_inline,12parse_furigana,13validate_furigana_text,14)151617class TestParseFurigana:18"""Tests for parse_furigana function."""1920def test_parse_simple_furigana(self) -> None:21"""Test parsing simple text with furigana."""22text = "神[かみ]は愛[あい]です"23plain, annotations = parse_furigana(text)2425assert plain == "神は愛です"26assert len(annotations) == 22728assert annotations[0].start_offset == 029assert annotations[0].end_offset == 130assert annotations[0].base_text == "神"31assert annotations[0].reading == "かみ"3233assert annotations[1].start_offset == 234assert annotations[1].end_offset == 335assert annotations[1].base_text == "愛"36assert annotations[1].reading == "あい"3738def test_parse_no_furigana(self) -> None:39"""Test parsing text without furigana."""40text = "こんにちは"41plain, annotations = parse_furigana(text)4243assert plain == "こんにちは"44assert annotations == []4546def test_parse_multiple_kanji_compound(self) -> None:47"""Test parsing compound kanji with single reading."""48text = "聖書[せいしょ]を読[よ]む"49plain, annotations = parse_furigana(text)5051assert plain == "聖書を読む"52assert len(annotations) == 25354assert annotations[0].base_text == "聖書"55assert annotations[0].reading == "せいしょ"56assert annotations[0].start_offset == 057assert annotations[0].end_offset == 25859assert annotations[1].base_text == "読"60assert annotations[1].reading == "よ"61assert annotations[1].start_offset == 362assert annotations[1].end_offset == 46364def test_parse_consecutive_furigana(self) -> None:65"""Test parsing consecutive furigana annotations."""66text = "日本[にほん]語[ご]"67plain, annotations = parse_furigana(text)6869assert plain == "日本語"70assert len(annotations) == 27172assert annotations[0].start_offset == 073assert annotations[0].end_offset == 274assert annotations[1].start_offset == 275assert annotations[1].end_offset == 37677def test_parse_katakana_reading(self) -> None:78"""Test parsing with katakana reading."""79text = "神[カミ]"80plain, annotations = parse_furigana(text)8182assert plain == "神"83assert len(annotations) == 184assert annotations[0].reading == "カミ"8586def test_parse_empty_string(self) -> None:87"""Test parsing empty string."""88plain, annotations = parse_furigana("")8990assert plain == ""91assert annotations == []9293def test_annotation_defaults(self) -> None:94"""Test that FuriganaAnnotation has correct defaults."""95_, annotations = parse_furigana("神[かみ]")9697assert annotations[0].is_standard_reading is True98assert annotations[0].reading_source == "dictionary"99100def test_annotation_frozen(self) -> None:101"""Test that FuriganaAnnotation is immutable."""102annotation = FuriganaAnnotation(0, 1, "神", "かみ")103104with pytest.raises(AttributeError):105annotation.reading = "しん" # type: ignore[misc]106107108class TestFormatFuriganaInline:109"""Tests for format_furigana_inline function."""110111def test_format_simple(self) -> None:112"""Test formatting simple annotations."""113annotations = [114FuriganaAnnotation(0, 1, "神", "かみ"),115FuriganaAnnotation(2, 3, "愛", "あい"),116]117result = format_furigana_inline("神は愛です", annotations)118119assert result == "神[かみ]は愛[あい]です"120121def test_format_no_annotations(self) -> None:122"""Test formatting text without annotations."""123result = format_furigana_inline("こんにちは", [])124125assert result == "こんにちは"126127def test_format_unsorted_annotations(self) -> None:128"""Test that annotations are sorted by offset."""129annotations = [130FuriganaAnnotation(2, 3, "愛", "あい"), # Out of order131FuriganaAnnotation(0, 1, "神", "かみ"),132]133result = format_furigana_inline("神は愛です", annotations)134135assert result == "神[かみ]は愛[あい]です"136137def test_format_compound_kanji(self) -> None:138"""Test formatting compound kanji."""139annotations = [140FuriganaAnnotation(0, 2, "聖書", "せいしょ"),141]142result = format_furigana_inline("聖書", annotations)143144assert result == "聖書[せいしょ]"145146def test_roundtrip(self) -> None:147"""Test parse -> format roundtrip produces same output."""148original = "神[かみ]は愛[あい]です"149plain, annotations = parse_furigana(original)150reconstructed = format_furigana_inline(plain, annotations)151152assert reconstructed == original153154155class TestFormatFuriganaHtml:156"""Tests for format_furigana_html function."""157158def test_format_simple_html(self) -> None:159"""Test HTML ruby formatting."""160annotations = [161FuriganaAnnotation(0, 1, "神", "かみ"),162FuriganaAnnotation(2, 3, "愛", "あい"),163]164result = format_furigana_html("神は愛です", annotations)165166expected = "<ruby>神<rt>かみ</rt></ruby>は<ruby>愛<rt>あい</rt></ruby>です"167assert result == expected168169def test_format_no_annotations_html(self) -> None:170"""Test HTML formatting without annotations."""171result = format_furigana_html("こんにちは", [])172173assert result == "こんにちは"174175def test_format_unsorted_html(self) -> None:176"""Test that annotations are sorted for HTML output."""177annotations = [178FuriganaAnnotation(2, 3, "愛", "あい"),179FuriganaAnnotation(0, 1, "神", "かみ"),180]181result = format_furigana_html("神は愛です", annotations)182183expected = "<ruby>神<rt>かみ</rt></ruby>は<ruby>愛<rt>あい</rt></ruby>です"184assert result == expected185186187class TestValidateFuriganaText:188"""Tests for validate_furigana_text function."""189190def test_valid_text(self) -> None:191"""Test validation of valid furigana text."""192errors = validate_furigana_text("神[かみ]は愛[あい]です")193assert errors == []194195def test_unmatched_closing_bracket(self) -> None:196"""Test detection of unmatched closing bracket."""197errors = validate_furigana_text("神かみ]は")198assert len(errors) == 1199assert "Unmatched closing bracket" in errors[0]200201def test_unmatched_opening_bracket(self) -> None:202"""Test detection of unmatched opening bracket."""203errors = validate_furigana_text("神[かみは")204assert len(errors) == 1205assert "Unmatched opening bracket" in errors[0]206207def test_nested_brackets(self) -> None:208"""Test detection of nested brackets."""209errors = validate_furigana_text("神[[かみ]]")210assert any("Nested bracket" in e for e in errors)211212def test_non_kanji_with_furigana(self) -> None:213"""Test detection of non-kanji with furigana."""214errors = validate_furigana_text("あ[い]")215assert len(errors) == 1216assert "Non-kanji character" in errors[0]217218def test_empty_text(self) -> None:219"""Test validation of empty text."""220errors = validate_furigana_text("")221assert errors == []222223224class TestExtractAllReadings:225"""Tests for extract_all_readings function."""226227def test_extract_single_reading(self) -> None:228"""Test extracting single reading per kanji."""229readings = extract_all_readings("神[かみ]は愛[あい]です")230231assert readings == {"神": ["かみ"], "愛": ["あい"]}232233def test_extract_multiple_readings(self) -> None:234"""Test extracting multiple readings for same kanji."""235readings = extract_all_readings("神[かみ]は神[しん]の愛[あい]")236237assert "神" in readings238assert "かみ" in readings["神"]239assert "しん" in readings["神"]240assert len(readings["神"]) == 2241assert readings["愛"] == ["あい"]242243def test_extract_no_readings(self) -> None:244"""Test extracting from text without furigana."""245readings = extract_all_readings("こんにちは")246247assert readings == {}248249def test_no_duplicate_readings(self) -> None:250"""Test that duplicate readings are not added."""251readings = extract_all_readings("神[かみ]と神[かみ]")252253assert readings == {"神": ["かみ"]}254255256class TestFuriganaAnnotationDataclass:257"""Tests for FuriganaAnnotation dataclass."""258259def test_create_with_all_fields(self) -> None:260"""Test creating annotation with all fields."""261annotation = FuriganaAnnotation(262start_offset=0,263end_offset=1,264base_text="神",265reading="かみ",266is_standard_reading=False,267reading_source="theological",268)269270assert annotation.start_offset == 0271assert annotation.end_offset == 1272assert annotation.base_text == "神"273assert annotation.reading == "かみ"274assert annotation.is_standard_reading is False275assert annotation.reading_source == "theological"276277def test_create_with_defaults(self) -> None:278"""Test creating annotation with default values."""279annotation = FuriganaAnnotation(0, 1, "神", "かみ")280281assert annotation.is_standard_reading is True282assert annotation.reading_source == "dictionary"283284def test_annotation_equality(self) -> None:285"""Test annotation equality comparison."""286a1 = FuriganaAnnotation(0, 1, "神", "かみ")287a2 = FuriganaAnnotation(0, 1, "神", "かみ")288a3 = FuriganaAnnotation(0, 1, "神", "しん")289290assert a1 == a2291assert a1 != a3292293def test_annotation_hash(self) -> None:294"""Test that annotations are hashable (frozen dataclass)."""295annotation = FuriganaAnnotation(0, 1, "神", "かみ")296annotations_set = {annotation}297298assert annotation in annotations_set299