Skip to content

Commit fcb9210

Browse files
authored
Merge pull request #1335 from bact/more-tests
Add more tests for tone and vowel analysis
2 parents 77ebb17 + 22ead5a commit fcb9210

4 files changed

Lines changed: 303 additions & 0 deletions

File tree

tests/compact/testc_util.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,17 @@ def test_spell_word(self):
2222
self.assertEqual(
2323
spell_word("คนดี"), ["คอ", "นอ", "คน", "ดอ", "อี", "ดี", "คนดี"]
2424
)
25+
result = spell_word("คน")
26+
self.assertIsInstance(result, list)
27+
self.assertIn("คน", result)
28+
2529
# Edge cases: None and empty string
2630
self.assertEqual(spell_word(None), [])
2731
self.assertEqual(spell_word(""), [])
2832

33+
# multi-syllable: last element is the full word
34+
result_multi = spell_word("คนดี")
35+
self.assertEqual(result_multi[-1], "คนดี")
2936

3037
class UtilTestCaseC(unittest.TestCase):
3138
def test_rhyme(self):
@@ -37,6 +44,14 @@ def test_thai_word_tone_detector(self):
3744
self.assertEqual(
3845
thai_word_tone_detector("ราคา"), [("รา", "m"), ("คา", "m")]
3946
)
47+
result = thai_word_tone_detector("คนดี")
48+
self.assertIsInstance(result, list)
49+
valid_tones = {"l", "m", "h", "r", "f", ""}
50+
for syllable, tone in result:
51+
self.assertIsInstance(syllable, str)
52+
self.assertIn(tone, valid_tones)
53+
self.assertIsInstance(thai_word_tone_detector("มือถือ"), list)
54+
4055
# Edge cases: None and empty string
4156
self.assertEqual(thai_word_tone_detector(None), [])
4257
self.assertEqual(thai_word_tone_detector(""), [])
@@ -63,3 +78,5 @@ def test_check_khuap_klam(self):
6378

6479
# Edge cases: empty string returns None
6580
self.assertIsNone(check_khuap_klam(""))
81+
for word in ["กลม", "จริง", "ตา"]:
82+
self.assertIn(check_khuap_klam(word), (True, False, None))

tests/core/test_khavee.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,144 @@ def test_check_aek_too(self):
117117
self.assertTrue(
118118
kv.check_aek_too(["หนม", "หน่ม", "หน้ม"]), [False, "aek", "too"]
119119
)
120+
121+
122+
class KhaveeCheckKaruLahuTestCase(unittest.TestCase):
123+
"""Tests for KhaveeVerifier.check_karu_lahu"""
124+
125+
def setUp(self):
126+
self.kv = KhaveeVerifier()
127+
128+
def test_dead_syllable_is_karu(self):
129+
self.assertEqual(self.kv.check_karu_lahu("กด"), "karu")
130+
131+
def test_long_live_syllable_is_karu(self):
132+
self.assertEqual(self.kv.check_karu_lahu("กา"), "karu")
133+
134+
def test_live_syllable_with_final_consonant_is_karu(self):
135+
self.assertEqual(self.kv.check_karu_lahu("กาน"), "karu")
136+
137+
def test_bo_mai_ek_is_lahu(self):
138+
self.assertEqual(self.kv.check_karu_lahu("บ่"), "lahu")
139+
140+
def test_no_short_word_is_lahu(self):
141+
self.assertEqual(self.kv.check_karu_lahu("ณ"), "lahu")
142+
143+
def test_tho_short_word_is_lahu(self):
144+
self.assertEqual(self.kv.check_karu_lahu("ธ"), "lahu")
145+
146+
def test_ko_mai_is_lahu(self):
147+
self.assertEqual(self.kv.check_karu_lahu("ก็"), "lahu")
148+
149+
150+
class KhaveeHandleKarunTestCase(unittest.TestCase):
151+
"""Tests for KhaveeVerifier.handle_karun_sound_silence"""
152+
153+
def setUp(self):
154+
self.kv = KhaveeVerifier()
155+
156+
def test_word_without_karun_unchanged(self):
157+
self.assertEqual(self.kv.handle_karun_sound_silence("คน"), "คน")
158+
self.assertEqual(self.kv.handle_karun_sound_silence("กา"), "กา")
159+
160+
def test_word_ending_with_karun_stripped(self):
161+
# เกมส์ → drop ์ and the consonant before it (ส) → เกม
162+
self.assertEqual(self.kv.handle_karun_sound_silence("เกมส์"), "เกม")
163+
164+
def test_word_ending_with_karun_stripped_2(self):
165+
# รักษ์ → drop ์ + ษ → รัก
166+
self.assertEqual(self.kv.handle_karun_sound_silence("รักษ์"), "รัก")
167+
168+
def test_returns_string(self):
169+
self.assertIsInstance(self.kv.handle_karun_sound_silence("สวัสดี"), str)
170+
171+
172+
class KhaveeCheckAekTooEdgeCasesTestCase(unittest.TestCase):
173+
"""Edge-case tests for KhaveeVerifier.check_aek_too"""
174+
175+
def setUp(self):
176+
self.kv = KhaveeVerifier()
177+
178+
def test_non_string_raises_type_error(self):
179+
with self.assertRaises(TypeError):
180+
self.kv.check_aek_too(123)
181+
182+
def test_dead_syllable_as_aek_flag(self):
183+
self.assertEqual(self.kv.check_aek_too("บท", dead_syllable_as_aek=True), "aek")
184+
185+
def test_dead_syllable_without_flag_returns_false(self):
186+
self.assertFalse(self.kv.check_aek_too("บท", dead_syllable_as_aek=False))
187+
188+
def test_list_with_non_string_element_raises(self):
189+
with self.assertRaises(TypeError):
190+
self.kv.check_aek_too(["ไก่", 42])
191+
192+
def test_both_tone_marks_returns_false(self):
193+
# word with both ่ and ้ should return False
194+
self.assertFalse(self.kv.check_aek_too("ก่้"))
195+
196+
197+
class KhaveeCheckKlonExtendedTestCase(unittest.TestCase):
198+
"""Tests for check_klon k_type=8 and invalid k_type"""
199+
200+
def setUp(self):
201+
self.kv = KhaveeVerifier()
202+
203+
def test_invalid_k_type_returns_error_string(self):
204+
result = self.kv.check_klon("บทกวีทดสอบ", k_type=99)
205+
self.assertIsInstance(result, str)
206+
self.assertIn("Something went wrong", result)
207+
208+
def test_incomplete_klon4_poem(self):
209+
result = self.kv.check_klon("ฉันชื่อหมูกรอบ", k_type=4)
210+
self.assertIsInstance(result, str)
211+
self.assertIn("does not have 4 complete sentences", result)
212+
213+
def test_incomplete_klon8_poem(self):
214+
result = self.kv.check_klon("ฉันชื่อหมูกรอบ", k_type=8)
215+
self.assertIsInstance(result, str)
216+
217+
def test_check_klon8_correct_poem(self):
218+
poem = (
219+
"ฉันชื่อหมูกรอบ ฉันชอบกินไก่ แล้วก็วิ่งไล่ หมาชื่อนํ้าทอง "
220+
"ลคคนเก่ง เอ๋งเอ๋งคะนอง มีคนจับจอง เขาชื่อน้องเธียร"
221+
)
222+
self.assertIsNotNone(self.kv.check_klon(poem, k_type=8))
223+
224+
225+
class KhaveeCheckSaraEdgeCasesTestCase(unittest.TestCase):
226+
"""Edge-case tests for KhaveeVerifier.check_sara"""
227+
228+
def setUp(self):
229+
self.kv = KhaveeVerifier()
230+
231+
def test_bo_mai_ek_returns_oo(self):
232+
self.assertEqual(self.kv.check_sara("บ่"), "ออ")
233+
234+
def test_special_word_เออ(self):
235+
self.assertEqual(self.kv.check_sara("เออ"), "เออ")
236+
237+
def test_special_word_เอ(self):
238+
self.assertEqual(self.kv.check_sara("เอ"), "เอ")
239+
240+
def test_special_word_เอะ(self):
241+
self.assertEqual(self.kv.check_sara("เอะ"), "เอะ")
242+
243+
def test_special_word_เอา(self):
244+
self.assertEqual(self.kv.check_sara("เอา"), "เอา")
245+
246+
def test_special_word_เอาะ(self):
247+
self.assertEqual(self.kv.check_sara("เอาะ"), "เอาะ")
248+
249+
def test_ru_sara(self):
250+
self.assertEqual(self.kv.check_sara("ฤ"), "อึ")
251+
252+
def test_ruea_sara(self):
253+
# ฤา (ฤ + sara aa U+0E32) → อือ; note: ฤๅ uses lakkhangyao, not sara aa
254+
self.assertEqual(self.kv.check_sara("ฤา"), "อือ")
255+
256+
def test_เอือ_sara(self):
257+
self.assertEqual(self.kv.check_sara("เรือ"), "เอือ")
258+
259+
def test_returns_string(self):
260+
self.assertIsInstance(self.kv.check_sara("เริง"), str)

tests/core/test_tag.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,82 @@ def test_ner_locations(self):
228228
)
229229

230230

231+
class BlackboardPreProcessTestCase(unittest.TestCase):
232+
"""Tests for pythainlp.tag.blackboard.pre_process"""
233+
234+
def setUp(self):
235+
from pythainlp.tag.blackboard import pre_process
236+
237+
self.pre_process = pre_process
238+
239+
def test_space_is_escaped(self):
240+
self.assertEqual(self.pre_process([" "]), ["_"])
241+
242+
def test_regular_words_unchanged(self):
243+
self.assertEqual(self.pre_process(["ผม", "รัก", "คุณ"]), ["ผม", "รัก", "คุณ"])
244+
245+
def test_mixed_space_and_words(self):
246+
self.assertEqual(self.pre_process(["ผม", " ", "คุณ"]), ["ผม", "_", "คุณ"])
247+
248+
def test_multiple_spaces(self):
249+
self.assertEqual(self.pre_process([" ", " "]), ["_", "_"])
250+
251+
def test_empty_list(self):
252+
self.assertEqual(self.pre_process([]), [])
253+
254+
def test_non_space_special_chars_unchanged(self):
255+
self.assertEqual(self.pre_process(["_"]), ["_"]) # already escaped form
256+
257+
def test_single_thai_word(self):
258+
self.assertEqual(self.pre_process(["สวัสดี"]), ["สวัสดี"])
259+
260+
261+
class BlackboardPostProcessTestCase(unittest.TestCase):
262+
"""Tests for pythainlp.tag.blackboard.post_process"""
263+
264+
def setUp(self):
265+
from pythainlp.tag.blackboard import TO_UD, post_process
266+
267+
self.post_process = post_process
268+
self.TO_UD = TO_UD
269+
270+
def test_unescape_underscore_to_space(self):
271+
result = self.post_process([("_", "NN"), ("คน", "NN")])
272+
self.assertEqual(result, [(" ", "NN"), ("คน", "NN")])
273+
274+
def test_regular_words_pass_through(self):
275+
result = self.post_process([("ผม", "PPRS"), ("รัก", "VACT")])
276+
self.assertEqual(result, [("ผม", "PPRS"), ("รัก", "VACT")])
277+
278+
def test_unescape_to_space_with_ud(self):
279+
result = self.post_process([("_", "NN")], to_ud=True)
280+
self.assertEqual(result, [(" ", "NOUN")])
281+
282+
def test_regular_word_to_ud(self):
283+
result = self.post_process([("คน", "NN")], to_ud=True)
284+
self.assertEqual(result, [("คน", "NOUN")])
285+
286+
def test_all_to_ud_mappings(self):
287+
for bb_tag, ud_tag in self.TO_UD.items():
288+
if bb_tag == "":
289+
continue # skip empty-string sentinel
290+
result = self.post_process([("word", bb_tag)], to_ud=True)
291+
self.assertEqual(
292+
result[0][1],
293+
ud_tag,
294+
f"TO_UD mapping failed for tag '{bb_tag}': "
295+
f"expected '{ud_tag}', got '{result[0][1]}'",
296+
)
297+
298+
def test_empty_list(self):
299+
self.assertEqual(self.post_process([]), [])
300+
self.assertEqual(self.post_process([], to_ud=True), [])
301+
302+
def test_no_ud_default_preserves_tag(self):
303+
result = self.post_process([("คน", "VV")])
304+
self.assertEqual(result[0][1], "VV")
305+
306+
231307
class TagNNERTestCase(unittest.TestCase):
232308
"""Test pythainlp.tag.thai_nner"""
233309

tests/core/test_util.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,23 @@
4040
reign_year_to_ad,
4141
remove_dangling,
4242
remove_dup_spaces,
43+
remove_repeat_vowels,
44+
remove_spaces_before_marks,
4345
remove_tone_ipa,
4446
remove_tonemark,
4547
remove_trailing_repeat_consonants,
4648
remove_zw,
49+
reorder_vowels,
4750
sound_syllable,
51+
spell_syllable,
4852
spelling,
4953
syllable_length,
5054
syllable_open_close_detector,
5155
text_to_arabic_digit,
5256
text_to_num,
5357
text_to_thai_digit,
5458
th_zodiac,
59+
thai_consonant_to_spelling,
5560
thai_digit_to_arabic_digit,
5661
thai_keyboard_dist,
5762
thai_strftime,
@@ -65,6 +70,7 @@
6570
to_idna,
6671
to_lunar_date,
6772
tone_detector,
73+
tone_to_spelling,
6874
words_to_num,
6975
)
7076
from pythainlp.util.morse import morse_decode, morse_encode
@@ -1048,3 +1054,66 @@ def test_analyze_thai_text(self):
10481054
analyze_thai_text("เล่น"),
10491055
{'สระ เอ': 1, 'ล': 1, 'ไม้เอก': 1, 'น': 1}
10501056
)
1057+
1058+
# ### pythainlp.util.pronounce
1059+
1060+
def test_thai_consonant_to_spelling(self):
1061+
self.assertEqual(thai_consonant_to_spelling("ก"), "กอ")
1062+
self.assertEqual(thai_consonant_to_spelling("ข"), "ขอ")
1063+
self.assertEqual(thai_consonant_to_spelling("น"), "นอ")
1064+
# multi-char strings and non-consonants pass through unchanged
1065+
self.assertEqual(thai_consonant_to_spelling("กา"), "กา")
1066+
self.assertEqual(thai_consonant_to_spelling("า"), "า")
1067+
self.assertEqual(thai_consonant_to_spelling("A"), "A")
1068+
self.assertEqual(thai_consonant_to_spelling(""), "")
1069+
1070+
def test_tone_to_spelling(self):
1071+
self.assertEqual(tone_to_spelling("่"), "ไม้เอก")
1072+
self.assertEqual(tone_to_spelling("้"), "ไม้โท")
1073+
self.assertEqual(tone_to_spelling("๊"), "ไม้ตรี")
1074+
self.assertEqual(tone_to_spelling("๋"), "ไม้จัตวา")
1075+
# non-tone-mark characters pass through unchanged
1076+
self.assertEqual(tone_to_spelling("ก"), "ก")
1077+
self.assertEqual(tone_to_spelling(""), "")
1078+
1079+
# ### pythainlp.util.spell_words
1080+
1081+
def test_spell_syllable(self):
1082+
result = spell_syllable("แมว")
1083+
self.assertIsInstance(result, list)
1084+
self.assertEqual(result[-1], "แมว")
1085+
result_kon = spell_syllable("คน")
1086+
self.assertGreater(len(result_kon), 0)
1087+
self.assertIn("คน", result_kon)
1088+
1089+
# ### pythainlp.util.normalize – remove_repeat_vowels,
1090+
# remove_spaces_before_marks, reorder_vowels
1091+
1092+
def test_remove_repeat_vowels(self):
1093+
self.assertEqual(remove_repeat_vowels(""), "")
1094+
self.assertEqual(remove_repeat_vowels("สวัสดี"), "สวัสดี")
1095+
self.assertEqual(remove_repeat_vowels("นานาา"), "นานา")
1096+
self.assertEqual(remove_repeat_vowels("ดีีีี"), "ดี")
1097+
# double sara E is reordered to sara Ae before repeat-removal
1098+
self.assertEqual(remove_repeat_vowels("เเปลก"), "แปลก")
1099+
1100+
def test_remove_spaces_before_marks(self):
1101+
self.assertEqual(remove_spaces_before_marks(""), "")
1102+
self.assertEqual(remove_spaces_before_marks("กิน"), "กิน")
1103+
self.assertEqual(remove_spaces_before_marks("ก ิ"), "กิ")
1104+
self.assertEqual(remove_spaces_before_marks("ก ุ"), "กุ")
1105+
self.assertEqual(remove_spaces_before_marks("ก ่า"), "ก่า")
1106+
# spaces between regular consonants are preserved
1107+
self.assertIn(" ", remove_spaces_before_marks("ก ข"))
1108+
1109+
def test_reorder_vowels(self):
1110+
self.assertEqual(reorder_vowels(""), "")
1111+
self.assertEqual(reorder_vowels("สวัสดี"), "สวัสดี")
1112+
# two sara E → sara Ae
1113+
self.assertEqual(reorder_vowels("เเปลก"), "แปลก")
1114+
# nikhahit (ํ) + sara aa (า) → sara am (ำ)
1115+
self.assertEqual(reorder_vowels("\u0e01\u0e4d\u0e32"), "\u0e01\u0e33")
1116+
# tone mark reorder: both characters still present after reorder
1117+
result = reorder_vowels("\u0e01\u0e32\u0e48")
1118+
self.assertIn("\u0e48", result)
1119+
self.assertIn("\u0e32", result)

0 commit comments

Comments
 (0)