japanese/tests/test_learner.py
panli 2e4dcf8980 feat: 新增 jlearn 学习资料生成器 + 日语音变标注体系
新增学习资料生成器模块(learner),从权威库生成多维日语学习资料:
- 拼音/假名/汉字/熟字训四类索引,带拼音↔假名↔汉字交叉跳转
- 逐字音训分类(KANJIDIC2 精确查表 + 启发式回退 + 排序键)
- 音变标注体系:浊化(連濁)、半浊化、促音变(促音便)、连声(れんじょう)
  独立配色 + 合并逻辑 + 音变规律说明
- 显式标注表:rendaku_marks(连用形连浊)、renjou_marks(连声)
- 每索引独立例词数配置(jlearn.toml + --config)
- HTML 单页应用 + 静态 HTML + PDF(playwright)

清洗工具增强:
- 拼音校验器(pinyin_checker)集成到 jclean
- 多音字拼音校正、ます形サ変動詞转原型

数据:
- 权威库补充连声词(反応/天皇/陰陽/観音/因縁/三位/輪廻/安穏)
- KANJIDIC2 音训分类表、拼音校正字典

整理 .gitignore:忽略生成产物(output/study_materials)、词典数据库、
任务运行日志、备份文件
2026-09-09 15:44:50 +08:00

91 lines
2.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""learner 子包测试StudyDataBuilder 与各生成器。"""
import pytest
from pl_japanese.learner.builder import StudyDataBuilder
from pl_japanese.learner import generators
# 小型样本词表:覆盖单字多音、熟字训、空拼音等情况
SAMPLE = """\
中|国:ちゅう|ごく:zhong|guo
中|国|人:ちゅう|ごく|じん:zhong|guo|ren
生:せい:sheng
生:なま:sheng
学|生:がく|せい:xue|sheng
今日:きょう:jinri
一|人:ひと|り:yi|ren
"""
@pytest.fixture
def sample_vocab(tmp_path):
p = tmp_path / "vocab.txt"
p.write_text(SAMPLE, encoding="utf-8", newline="\n")
return p
@pytest.fixture
def builder(sample_vocab):
return StudyDataBuilder(str(sample_vocab))
def test_builder_loads(builder):
st = builder.stats()
assert st["loaded"] > 0
assert st["skipped"] == 0
def test_pinyin_reverse_lookup(builder):
# 单字 中 读 ちゅう,拼音应为 zhong
assert builder.pinyin_for("", "ちゅう") == "zhong"
assert builder.pinyin_for("", "せい") == "sheng"
assert builder.pinyin_for("", "なま") == "sheng"
def test_single_only_excludes_multichar(builder):
single = builder.kanji_readings(single_only=True)
# 熟字训 今日 不应出现在单字里
assert "今日" not in single
assert "" in single
assert "" in single
def test_jukujikun_captures_multichar(builder):
juku = builder.jukujikun()
assert "今日" in juku
assert "きょう" in juku["今日"]
# 单字不应出现在熟字训里
assert "" not in juku
def test_multi_reading_kanji(builder):
single = builder.kanji_readings(single_only=True)
# 生 有两种读音 せい / なま
assert set(single[""].keys()) == {"せい", "なま"}
def test_all_generators_produce_files(builder, tmp_path):
out = tmp_path / "out"
out.mkdir()
for name, (func, _desc) in generators.GENERATORS.items():
path = func(builder, out)
assert path.exists(), f"{name} 未生成文件"
assert path.stat().st_size > 0, f"{name} 生成了空文件"
def test_anki_deck_is_tsv(builder, tmp_path):
out = tmp_path / "out"
out.mkdir()
path = generators.gen_anki_deck(builder, out)
lines = path.read_text(encoding="utf-8").strip().split("\n")
assert len(lines) > 0
for line in lines:
assert "\t" in line, "Anki 卡片应为 TAB 分隔"
front, back = line.split("\t", 1)
assert front # 正面非空
def test_missing_vocab_raises(tmp_path):
with pytest.raises(FileNotFoundError):
StudyDataBuilder(str(tmp_path / "nonexistent.txt"))