"""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"))