Pre-publication review draft
日常訳NICHIJOYAKU日本語 ↗

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

software/tests/test_furigana.py299 linesDownload this file ↓

This is the same source included in the download. Each line number is a link you can save or share.

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