وحدة:ar-verb/مختبر
المظهر
(بالتحويل من وحدة:ar-verb/testcases)
| هذه صفحة مختبر للوحدة وحدة:ar-verb. نتائج هذا الاختبار. |
-- Unit tests for [[Module:ar-verb]]. Click talk page to run tests.
-- اختبارات الوحدة. انقر على صفحة النقاش لعرض النتائج
-- 🚧 Work In Progress !!
local tests = require("Module:UnitTests")
local m_ar_verb = require("Module:ar-verb")
-- ═══════════════════════════════════════════════════════════
-- Utilities required for some tests
-- ═══════════════════════════════════════════════════════════
local m_string_utilities = require("Module:string utilities")
local u = m_string_utilities.char
-- ═══════════════════════════════════════════════════════════
function tests:test_conj_form_I()
local result = m_ar_verb.do_generate_forms({["جذر"] = "كتب", ["وزن"] = "فعَل يفعُل"}, "ar-conj")
self:equals("كتب، فعَل يفعُل، هو، الماضي", result.forms.past_3ms[1].form, "كَتَبَ")
self:equals("كتب، فعَل يفعُل، هو، المضارع", result.forms.ind_3ms[1].form, "يَكْتُبُ")
self:equals("كتب، فعَل يفعُل، أنتَ، الأمر", result.forms.imp_2ms[1].form, "اُكْتُبْ")
self:equals("كتب، فعَل يفعُل، اسم الفاعل", result.forms.ap[1].form, "كَاتِب")
self:equals("كتب، فعَل يفعُل، اسم المفعول", result.forms.pp[1].form, "مَكْتُوب")
self:equals("كتب، فعَل يفعُل، له معلوم", result.has_active, true)
self:equals("كتب، فعَل يفعُل، له مجهول", result.has_passive, true)
self:equals("كتب، فعَل يفعُل، الوزن I", result.verb_forms[1], "I")
end
function tests:test_conj_form_II()
local result = m_ar_verb.do_generate_forms({["جذر"] = "درس", ["وزن"] = "فعّل"}, "ar-conj")
self:equals("درس، فعّل، هو، الماضي", result.forms.past_3ms[1].form, "دَرَّسَ")
self:equals("درس، فعّل، هو، المضارع", result.forms.ind_3ms[1].form, "يُدَرِّسُ")
self:equals("درس، فعّل، أنتَ، الأمر", result.forms.imp_2ms[1].form, "دَرِّسْ")
self:equals("درس، فعّل، اسم الفاعل", result.forms.ap[1].form, "مُدَرِّس")
self:equals("درس، فعّل، اسم المفعول", result.forms.pp[1].form, "مُدَرَّس")
self:equals("درس، فعّل، المصدر", result.forms.vn[1].form, "تَدْرِيس")
end
function tests:test_conj_form_III()
local result = m_ar_verb.do_generate_forms({["جذر"] = "قتل", ["وزن"] = "فاعل"}, "ar-conj")
self:equals("قتل، فاعل، هو، الماضي", result.forms.past_3ms[1].form, "قَاتَلَ")
self:equals("قتل، فاعل، هو، المضارع", result.forms.ind_3ms[1].form, "يُقَاتِلُ")
self:equals("قتل، فاعل، أنتَ، الأمر", result.forms.imp_2ms[1].form, "قَاتِلْ")
self:equals("قتل، فاعل، اسم الفاعل", result.forms.ap[1].form, "مُقَاتِل")
self:equals("قتل، فاعل، اسم المفعول", result.forms.pp[1].form, "مُقَاتَل")
end
function tests:test_conj_form_IV()
local result = m_ar_verb.do_generate_forms({["جذر"] = "رسل", ["وزن"] = "أفعل"}, "ar-conj")
self:equals("رسل، أفعل، هو، الماضي", result.forms.past_3ms[1].form, "أَرْسَلَ")
self:equals("رسل، أفعل، هو، المضارع", result.forms.ind_3ms[1].form, "يُرْسِلُ")
self:equals("رسل، أفعل، أنتَ، الأمر", result.forms.imp_2ms[1].form, "أَرْسِلْ")
self:equals("رسل، أفعل، اسم الفاعل", result.forms.ap[1].form, "مُرْسِل")
self:equals("رسل، أفعل، اسم المفعول", result.forms.pp[1].form, "مُرْسَل")
end
-- وزن «أفعل» من الأجوف: تُعَلّ العين ألفًا (أَفَالَ) في الواوي واليائي معًا،
-- تبعًا لإعلالها في الثلاثي المجرد (معجم متن اللغة).
function tests:test_conj_form_IV_hollow()
-- أجوف واوي
local qwl = m_ar_verb.do_generate_forms({["جذر"] = "قول", ["وزن"] = "أفعل"}, "ar-conj")
self:equals("قول، أفعل، هو، الماضي", qwl.forms.past_3ms[1].form, "أَقَالَ")
-- أجوف يائي: يُعَلّ كنظيره الواوي
local yaa_cases = {
{"تيس", "أَتَاسَ"},
{"طيب", "أَطَابَ"},
{"بين", "أَبَانَ"},
{"ضيع", "أَضَاعَ"},
{"طيح", "أَطَاحَ"},
}
for _, case in ipairs(yaa_cases) do
local root, expected = case[1], case[2]
local result = m_ar_verb.do_generate_forms({["جذر"] = root, ["وزن"] = "أفعل"}, "ar-conj")
self:equals(root .. "، أفعل، هو، الماضي", result.forms.past_3ms[1].form, expected)
end
-- الاستثناء يخصّ «أفعل» وحده؛ الأوزان الأخرى من الأجوف اليائي تبقى معلّة
local tys_x = m_ar_verb.do_generate_forms({["جذر"] = "تيس", ["وزن"] = "استفعل"}, "ar-conj")
self:equals("تيس، استفعل، هو، الماضي", tys_x.forms.past_3ms[1].form, "اِسْتَتَاسَ")
end
-- 7 جذور يائية شاذّة يبقى وزن «أفعل» منها صحيحًا (أَفْيَلَ) — أفعال مسموعة موثّقة،
-- لا الصفة المشبهة (مثل أَخْيَف/خَيْفاء). انظر SOUND_FORM_IV_ROOTS في الوحدة.
function tests:test_conj_form_IV_sound_exceptions()
local cases = {
{"خيل", "أَخْيَلَ"}, -- أخيلت السماء — المعجم الوسيط 1/266
{"غيل", "أَغْيَلَ"}, -- أغيلته سقته الغَيْل — لسان العرب 11/111
{"حيج", "أَحْيَجَ"}, -- أحيجت الأرض — تاج العروس 5/500
{"حين", "أَحْيَنَ"}, -- أحين القوم — المعجم الوسيط 1/212
{"خيف", "أَخْيَفَ"}, -- أخيف الحاج — كتاب الأفعال لابن القوطية 35
{"ريف", "أَرْيَفَ"}, -- أريفت الأرض — المحيط في اللغة 10/256
{"زين", "أَزْيَنَ"}, -- أزينت الأرض — مقاييس اللغة 3/41
}
for _, case in ipairs(cases) do
local root, expected = case[1], case[2]
local result = m_ar_verb.do_generate_forms({["جذر"] = root, ["وزن"] = "أفعل"}, "ar-conj")
self:equals(root .. "، أفعل، هو، الماضي (شاذّ صحيح)", result.forms.past_3ms[1].form, expected)
end
end
function tests:test_conj_weak()
local result = m_ar_verb.do_generate_forms({["جذر"] = "قول", ["وزن"] = "فعَل يفعُل"}, "ar-conj")
self:equals("قول، فعَل يفعُل، هو، الماضي", result.forms.past_3ms[1].form, "قَالَ")
self:equals("قول، فعَل يفعُل، هو، المضارع", result.forms.ind_3ms[1].form, "يَقُولُ")
self:equals("قول، فعَل يفعُل، أنتَ، الأمر", result.forms.imp_2ms[1].form, "قُلْ")
local result = m_ar_verb.do_generate_forms({["جذر"] = "رمي", ["وزن"] = "فعَل يفعِل"}, "ar-conj")
self:equals("رمي، فعَل يفعِل، هو، الماضي", result.forms.past_3ms[1].form, "رَمَى")
self:equals("رمي، فعَل يفعِل، هو، المضارع", result.forms.ind_3ms[1].form, "يَرْمِي")
self:equals("رمي، فعَل يفعِل، أنتَ، الأمر", result.forms.imp_2ms[1].form, "اِرْمِ")
self:equals("رمي، فعَل يفعِل، اسم الفاعل", result.forms.ap[1].form, "رَامٍ")
local result = m_ar_verb.do_generate_forms({["جذر"] = "م_د_د", ["وزن"] = "فعَل يفعُل"}, "ar-conj")
self:equals("مدد، فعَل يفعُل، مضعَّف، هو، الماضي", result.forms.past_3ms[1].form, "مَدَّ")
local result = m_ar_verb.do_generate_forms({["جذر"] = "وصل", ["وزن"] = "فعَل يفعِل"}, "ar-conj")
self:equals("وصل، فعَل يفعِل، مثال، هو، الماضي", result.forms.past_3ms[1].form, "وَصَلَ")
self:equals("وصل، فعَل يفعِل، مثال، هو، المضارع", result.forms.ind_3ms[1].form, "يَصِلُ")
self:equals("وصل، فعَل يفعِل، مثال، أنت، الأمر", result.forms.imp_2ms[1].form, "صِلْ")
end
function tests:test_conj_with_underscores()
local result = m_ar_verb.do_generate_forms({["جذر"] = "ك_ت_ب", ["وزن"] = "فعَل يفعُل"}, "ar-conj")
self:equals("كتب، فعَل يفعُل، هو، الماضي", result.forms.past_3ms[1].form, "كَتَبَ")
self:equals("كتب، فعَل يفعُل، هو، المضارع", result.forms.ind_3ms[1].form, "يَكْتُبُ")
end
function tests:test_conj_with_spaces()
local result = m_ar_verb.do_generate_forms({["جذر"] = "ك ت ب", ["وزن"] = "فعَل يفعُل"}, "ar-conj")
self:equals("كتب، فعَل يفعُل، هو، الماضي", result.forms.past_3ms[1].form, "كَتَبَ")
self:equals("كتب، فعَل يفعُل، هو، المضارع", result.forms.ind_3ms[1].form, "يَكْتُبُ")
end
function tests:test_conj_passive_types()
local result_pass = m_ar_verb.do_generate_forms({[1] = "كتب<I/a~u.pass>"}, "ar-conj")
self:equals("كتب، مبني للمجهول فقط", result_pass.passive, "pass")
local result_ipass = m_ar_verb.do_generate_forms({[1] = "كتب<I/a~u.ipass>"}, "ar-conj")
self:equals("كتب، مجهول غير شخصي", result_ipass.passive, "ipass")
local result_nopass = m_ar_verb.do_generate_forms({[1] = "كتب<I/a~u.nopass>"}, "ar-conj")
self:equals("كتب، بدون مجهول", result_nopass.passive, "nopass")
self:equals("كتب، بدون مجهول، has_passive خطأ", result_nopass.has_passive, false)
end
function tests:test_conj_overrides()
local result = m_ar_verb.do_generate_forms({[1] = "كتب<I/a~u.vn:كِتَابَة>"}, "ar-conj")
self:equals("كتب، تجاوز المصدر", result.forms.vn[1].form, "كِتَابَة")
end
function tests:test_infer_radicals()
local result = m_ar_verb.infer_radicals({
headword = "كتب",
vform = "I"
})
self:equals("كتب، نوع الضعف: صحيح", result.weakness, "sound")
self:equals("كتب، حرف الجذر الأول: ك", result.rad1, "ك")
self:equals("كتب، حرف الجذر الثاني: ت", result.rad2, "ت")
self:equals("كتب، حرف الجذر الثالث: ب", result.rad3, "ب")
local U = u(0x064F) -- ḍamma
local result = m_ar_verb.infer_radicals({
headword = "قال",
vform = "I",
nonpast_vowel = U
})
self:equals("قال، نوع الضعف: أجوف", result.weakness, "hollow")
self:equals("قال، حرف الجذر الأول: ق", result.rad1, "ق")
self:equals("قال، حرف الجذر الثاني: و (مستنتج)", result.rad2, "و")
self:equals("قال، حرف الجذر الثالث: ل", result.rad3, "ل")
local result = m_ar_verb.infer_radicals({
headword = "رمى",
vform = "I"
})
self:equals("رمى، نوع الضعف: ناقص", result.weakness, "final-weak")
self:equals("رمى، حرف الجذر الأول: ر", result.rad1, "ر")
self:equals("رمى، حرف الجذر الثاني: م", result.rad2, "م")
self:equals("رمى، حرف الجذر الثالث: ي", result.rad3, "ي")
end
function tests:test_infer_participle_vocalization()
local result = m_ar_verb.infer_participle_vocalization("كاتب", "I", "sound", true)
self:equals("كاتب، تشكيل اسم الفاعل", result, "كَاتِب")
local result = m_ar_verb.infer_participle_vocalization("مكتوب", "I", "sound", false)
self:equals("مكتوب، تشكيل اسم المفعول", result, "مَكْتُوب")
local result = m_ar_verb.infer_participle_vocalization("مدرس", "II", "sound", true)
self:equals("مدرس، فعّل، تشكيل اسم الفاعل", result, "مُدَرِّس")
end
-- ═══════════════════════════════════════════════════════════
-- Tests for classify_triliteral_verb function
-- ═══════════════════════════════════════════════════════════
-- الصحيح
function tests:test_verb_classify_sound()
self:equals("كتب", m_ar_verb.classify_triliteral_verb("ك", "ت", "ب"), "صحيح سالم")
self:equals("ذهب", m_ar_verb.classify_triliteral_verb("ذ", "ه", "ب"), "صحيح سالم")
self:equals("فعل", m_ar_verb.classify_triliteral_verb("ف", "ع", "ل"), "صحيح سالم")
self:equals("مدّ", m_ar_verb.classify_triliteral_verb("م", "د", "د"), "صحيح مُضعَّف")
self:equals("شدّ", m_ar_verb.classify_triliteral_verb("ش", "د", "د"), "صحيح مُضعَّف")
self:equals("عدّ", m_ar_verb.classify_triliteral_verb("ع", "د", "د"), "صحيح مُضعَّف")
self:equals("أخذ", m_ar_verb.classify_triliteral_verb("أ", "خ", "ذ"), "صحيح مهموز الفاء")
self:equals("أكل", m_ar_verb.classify_triliteral_verb("أ", "ك", "ل"), "صحيح مهموز الفاء")
self:equals("سأل", m_ar_verb.classify_triliteral_verb("س", "أ", "ل"), "صحيح مهموز العين")
self:equals("قرأ", m_ar_verb.classify_triliteral_verb("ق", "ر", "أ"), "صحيح مهموز اللام")
self:equals("أبأ", m_ar_verb.classify_triliteral_verb("أ", "ب", "أ"), "صحيح مهموز الفاء واللام")
end
-- المعتل
function tests:test_verb_classify_weak()
self:equals("وجد", m_ar_verb.classify_triliteral_verb("و", "ج", "د"), "معتل مثال واوي")
self:equals("وقف", m_ar_verb.classify_triliteral_verb("و", "ق", "ف"), "معتل مثال واوي")
self:equals("وضأ", m_ar_verb.classify_triliteral_verb("و", "ض", "أ"), "معتل مثال واوي مهموز اللام")
self:equals("يسر", m_ar_verb.classify_triliteral_verb("ي", "س", "ر"), "معتل مثال يائي")
self:equals("يئس", m_ar_verb.classify_triliteral_verb("ي", "أ", "س"), "معتل مثال يائي مهموز العين")
self:equals("قال", m_ar_verb.classify_triliteral_verb("ق", "و", "ل"), "معتل أجوف واوي")
self:equals("صام", m_ar_verb.classify_triliteral_verb("ص", "و", "م"), "معتل أجوف واوي")
self:equals("باع", m_ar_verb.classify_triliteral_verb("ب", "ي", "ع"), "معتل أجوف يائي")
self:equals("نام", m_ar_verb.classify_triliteral_verb("ن", "و", "م"), "معتل أجوف واوي")
self:equals("دعا", m_ar_verb.classify_triliteral_verb("د", "ع", "و"), "معتل ناقص واوي")
self:equals("سما", m_ar_verb.classify_triliteral_verb("س", "م", "و"), "معتل ناقص واوي")
self:equals("رمى", m_ar_verb.classify_triliteral_verb("ر", "م", "ي"), "معتل ناقص يائي")
self:equals("بكى", m_ar_verb.classify_triliteral_verb("ب", "ك", "ي"), "معتل ناقص يائي")
-- لفيف مقرون (adjacent weak letters)
self:equals("ويّ", m_ar_verb.classify_triliteral_verb("و", "ي", "ي"), "معتل لفيف مقرون")
self:equals("أوى", m_ar_verb.classify_triliteral_verb("أ", "و", "ي"), "معتل لفيف مقرون مهموز الفاء")
-- لفيف مفروق (separated weak letters)
self:equals("وقى", m_ar_verb.classify_triliteral_verb("و", "ق", "ي"), "معتل لفيف مفروق")
self:equals("وأى", m_ar_verb.classify_triliteral_verb("و", "أ", "ي"), "معتل لفيف مفروق مهموز العين")
-- Combined weaknesses
self:equals("أول", m_ar_verb.classify_triliteral_verb("أ", "و", "ل"), "معتل أجوف واوي مهموز الفاء")
self:equals("قوأ", m_ar_verb.classify_triliteral_verb("ق", "و", "أ"), "معتل أجوف واوي مهموز اللام")
self:equals("أيب", m_ar_verb.classify_triliteral_verb("أ", "ي", "ب"), "معتل أجوف يائي مهموز الفاء")
self:equals("جيأ", m_ar_verb.classify_triliteral_verb("ج", "ي", "أ"), "معتل أجوف يائي مهموز اللام")
self:equals("رأو", m_ar_verb.classify_triliteral_verb("ر", "أ", "و"), "معتل ناقص واوي مهموز العين")
self:equals("شأي", m_ar_verb.classify_triliteral_verb("ش", "أ", "ي"), "معتل ناقص يائي مهموز العين")
self:equals("ودد", m_ar_verb.classify_triliteral_verb("و", "د", "د"), "معتل مثال واوي مُضعَّف")
self:equals("ينن", m_ar_verb.classify_triliteral_verb("ي", "ن", "ن"), "معتل مثال يائي مُضعَّف")
self:equals("وأب", m_ar_verb.classify_triliteral_verb("و", "أ", "ب"), "معتل مثال واوي مهموز العين")
self:equals("أبب", m_ar_verb.classify_triliteral_verb("أ", "ب", "ب"), "صحيح مُضعَّف مهموز الفاء")
self:equals("أتى", m_ar_verb.classify_triliteral_verb("أ", "ت", "ي"), "معتل ناقص يائي مهموز الفاء")
self:equals("قلا", m_ar_verb.classify_triliteral_verb("ق", "ل", "و"), "معتل ناقص واوي")
self:equals("نما (و)", m_ar_verb.classify_triliteral_verb("ن", "م", "و"), "معتل ناقص واوي")
self:equals("نمى (ي)", m_ar_verb.classify_triliteral_verb("ن", "م", "ي"), "معتل ناقص يائي")
self:equals("يئس", m_ar_verb.classify_triliteral_verb("ي", "أ", "س"), "معتل مثال يائي مهموز العين")
end
-- ═══════════════════════════════════════════════════════════
-- تغطية كل الأوزان — فعل شاهد لكل وزن
-- ═══════════════════════════════════════════════════════════
-- مختصر: يولّد التصريف ويعيد صيغة الخانة المطلوبة
local function conj(root, pattern)
return m_ar_verb.do_generate_forms({["جذر"] = root, ["وزن"] = pattern}, "ar-conj")
end
local function slot(result, name)
local forms = result.forms[name]
if not forms then
return nil
end
local out = {}
for i, f in ipairs(forms) do
out[i] = f.form
end
return table.concat(out, "/")
end
-- الثلاثي المجرد: الأبواب الستة
function tests:test_conj_form_I_all_vowel_patterns()
local cases = {
-- جذر، وزن، الماضي، المضارع، الأمر
{"كتب", "فعَل يفعُل", "كَتَبَ", "يَكْتُبُ", "اُكْتُبْ"},
{"ضرب", "فعَل يفعِل", "ضَرَبَ", "يَضْرِبُ", "اِضْرِبْ"},
{"فتح", "فعَل يفعَل", "فَتَحَ", "يَفْتَحُ", "اِفْتَحْ"},
{"علم", "فعِل يفعَل", "عَلِمَ", "يَعْلَمُ", "اِعْلَمْ"},
{"كرم", "فعُل يفعُل", "كَرُمَ", "يَكْرُمُ", "اُكْرُمْ"},
{"حسب", "فعِل يفعِل", "حَسِبَ", "يَحْسِبُ", "اِحْسِبْ"},
}
for _, c in ipairs(cases) do
local root, pattern, past, pres, imp = c[1], c[2], c[3], c[4], c[5]
local r = conj(root, pattern)
self:equals(root .. "، " .. pattern .. "، الماضي", slot(r, "past_3ms"), past)
self:equals(root .. "، " .. pattern .. "، المضارع", slot(r, "ind_3ms"), pres)
self:equals(root .. "، " .. pattern .. "، الأمر", slot(r, "imp_2ms"), imp)
end
end
-- المزيد: الأوزان V–XIII
function tests:test_conj_derived_forms()
local cases = {
-- جذر، وزن، الماضي، المضارع، المصدر
{"علم", "تفعّل", "تَعَلَّمَ", "يَتَعَلَّمُ", "تَعَلُّم"},
{"عون", "تفاعل", "تَعَاوَنَ", "يَتَعَاوَنُ", "تَعَاوُن"},
{"كسر", "انفعل", "اِنْكَسَرَ", "يَنْكَسِرُ", "اِنْكِسَار"},
{"جمع", "افتعل", "اِجْتَمَعَ", "يَجْتَمِعُ", "اِجْتِمَاع"},
{"حمر", "افعلّ", "اِحْمَرَّ", "يَحْمَرُّ", "اِحْمِرَار"},
{"خرج", "استفعل", "اِسْتَخْرَجَ", "يَسْتَخْرِجُ", "اِسْتِخْرَاج"},
{"حمر", "افعالّ", "اِحْمَارَّ", "يَحْمَارُّ", "اِحْمِيرَار"},
{"عشب", "افعوعل", "اِعْشَوْشَبَ", "يَعْشَوْشِبُ", "اِعْشِيشَاب"},
{"جلذ", "افعوّل", "اِجْلَوَّذَ", "يَجْلَوِّذُ", "اِجْلِوَّاذ"},
}
for _, c in ipairs(cases) do
local root, pattern, past, pres, vn = c[1], c[2], c[3], c[4], c[5]
local r = conj(root, pattern)
self:equals(root .. "، " .. pattern .. "، الماضي", slot(r, "past_3ms"), past)
self:equals(root .. "، " .. pattern .. "، المضارع", slot(r, "ind_3ms"), pres)
self:equals(root .. "، " .. pattern .. "، المصدر", slot(r, "vn"), vn)
end
end
-- الرباعي: مجرده ومزيده
function tests:test_conj_quadriliteral()
local cases = {
{"دحرج", "فعلل", "دَحْرَجَ", "يُدَحْرِجُ", "دَحْرَجَة"},
{"دحرج", "تفعلل", "تَدَحْرَجَ", "يَتَدَحْرَجُ", "تَدَحْرُج"},
{"حرجم", "افعنلل", "اِحْرَنْجَمَ", "يَحْرَنْجِمُ", "اِحْرِنْجَام"},
{"قشعر", "افعللّ", "اِقْشَعَرَّ", "يَقْشَعِرُّ", "اِقْشِعْرَار"},
}
for _, c in ipairs(cases) do
local root, pattern, past, pres, vn = c[1], c[2], c[3], c[4], c[5]
local r = conj(root, pattern)
self:equals(root .. "، " .. pattern .. "، الماضي", slot(r, "past_3ms"), past)
self:equals(root .. "، " .. pattern .. "، المضارع", slot(r, "ind_3ms"), pres)
self:equals(root .. "، " .. pattern .. "، المصدر", slot(r, "vn"), vn)
end
end
-- الضمائر والأزمنة: نموذج كامل من «كتب»
function tests:test_conj_persons_and_moods()
local r = conj("كتب", "فعَل يفعُل")
local cases = {
{"past_1s", "كَتَبْتُ"},
{"past_2ms", "كَتَبْتَ"},
{"past_3fs", "كَتَبَتْ"},
{"past_3fp", "كَتَبْنَ"},
{"ind_1s", "أَكْتُبُ"},
{"ind_2fs", "تَكْتُبِينَ"},
{"ind_3mp", "يَكْتُبُونَ"},
{"sub_3ms", "يَكْتُبَ"},
{"juss_3ms", "يَكْتُبْ"},
{"imp_2fs", "اُكْتُبِي"},
{"imp_2mp", "اُكْتُبُوا"},
{"imp_2fp", "اُكْتُبْنَ"},
}
for _, c in ipairs(cases) do
self:equals("كتب، " .. c[1], slot(r, c[1]), c[2])
end
end
-- المبني للمجهول
function tests:test_conj_passive_forms()
local r = conj("كتب", "فعَل يفعُل")
self:equals("كتب، الماضي المجهول", slot(r, "past_pass_3ms"), "كُتِبَ")
self:equals("كتب، المضارع المجهول", slot(r, "ind_pass_3ms"), "يُكْتَبُ")
self:equals("كتب، المنصوب المجهول", slot(r, "sub_pass_3ms"), "يُكْتَبَ")
self:equals("كتب، المجزوم المجهول", slot(r, "juss_pass_3ms"), "يُكْتَبْ")
local r2 = conj("درس", "فعّل")
self:equals("درس، فعّل، الماضي المجهول", slot(r2, "past_pass_3ms"), "دُرِّسَ")
self:equals("درس، فعّل، المضارع المجهول", slot(r2, "ind_pass_3ms"), "يُدَرَّسُ")
end
-- الأفعال الشاذّة المعالَجة داخل الوحدة
function tests:test_conj_irregular_verbs()
local raa = conj("رأي", "فعَل يفعَل")
self:equals("رأى، الماضي", slot(raa, "past_3ms"), "رَأَى")
self:equals("رأى، المضارع", slot(raa, "ind_3ms"), "يَرَى")
self:equals("رأى، الأمر", slot(raa, "imp_2ms"), "رَ")
local araa = conj("رأي", "أفعل")
self:equals("أرى، الماضي", slot(araa, "past_3ms"), "أَرَى")
self:equals("أرى، المضارع", slot(araa, "ind_3ms"), "يُرِي")
local akal = conj("أكل", "فعَل يفعُل")
self:equals("أكل، الماضي", slot(akal, "past_3ms"), "أَكَلَ")
self:equals("أكل، الأمر (محذوف)", slot(akal, "imp_2ms"), "كُلْ")
local akhadh = conj("أخذ", "فعَل يفعُل")
self:equals("أخذ، الأمر (محذوف)", slot(akhadh, "imp_2ms"), "خُذْ")
-- أمر: للأمر صيغتان، المحذوفة والتامّة بعد حرف عطف
local amar = conj("أمر", "فعَل يفعُل")
self:equals("أمر، الأمر", slot(amar, "imp_2ms"), "مُرْ/اُؤْمُرْ")
-- سأل: للمجزوم والأمر صيغتان
local saal = conj("سأل", "فعَل يفعَل")
self:equals("سأل، الأمر", slot(saal, "imp_2ms"), "اِسْأَلْ/سَلْ")
self:equals("سأل، المجزوم", slot(saal, "juss_3ms"), "يَسْأَلْ/يَسَلْ")
-- حيّ/حيي: الإدغام والفكّ
local hayy = conj("حيي", "فعِل يفعَل")
self:equals("حيّ، الماضي (مدغم ومفكوك)", slot(hayy, "past_3ms"), "حَيَّ/حَيِيَ")
self:equals("حيّ، المضارع", slot(hayy, "ind_3ms"), "يَحْيَا")
end
-- الإدغام بالتاء (var:short/long) وصيغ الإدغام المختصرة (reduced)
function tests:test_conj_variants_and_reduced()
local short = m_ar_verb.do_generate_forms({[1] = "حيي<I/i~a.var:short>"}, "ar-conj")
self:equals("حيي، var:short، الماضي", slot(short, "past_3ms"), "حَيَّ")
local long = m_ar_verb.do_generate_forms({[1] = "حيي<I/i~a.var:long>"}, "ar-conj")
self:equals("حيي، var:long، الماضي", slot(long, "past_3ms"), "حَيِيَ")
-- اتّخذ: افتعل من «أخذ» بإبدال الهمزة تاءً وإدغامها
local ittakhadha = m_ar_verb.do_generate_forms({[1] = "اتخذ<VIII.reduced>"}, "ar-conj")
self:equals("اتّخذ، الماضي", slot(ittakhadha, "past_3ms"), "اِتَّخَذَ")
self:equals("اتّخذ، المضارع", slot(ittakhadha, "ind_3ms"), "يَتَّخِذُ")
self:equals("اتّخذ، المصدر", slot(ittakhadha, "vn"), "اِتِّخَاذ")
-- ادّارأ: تفاعل مدغم (اِدَّارَأَ عوض تَدَارَأَ)
local iddaaraa = m_ar_verb.do_generate_forms({[1] = "ادارأ<VI.reduced>"}, "ar-conj")
self:equals("ادّارأ، الماضي", slot(iddaaraa, "past_3ms"), "اِدَّارَأَ")
-- اسطاع: استفعل محذوف التاء
local istaa = m_ar_verb.do_generate_forms({[1] = "اسطاع<X.reduced>"}, "ar-conj")
self:equals("اسطاع، الماضي", slot(istaa, "past_3ms"), "اِسْطَاعَ")
self:equals("اسطاع، المضارع", slot(istaa, "ind_3ms"), "يَسْطِيعُ")
end
-- المعتل: المثال والناقص واللفيف
function tests:test_conj_weak_extended()
local cases = {
-- جذر، وزن، الماضي، المضارع، الأمر، اسم الفاعل
{"وعد", "فعَل يفعِل", "وَعَدَ", "يَعِدُ", "عِدْ", "وَاعِد"}, -- مثال واوي
{"دعو", "فعَل يفعُل", "دَعَا", "يَدْعُو", "اُدْعُ", "دَاعٍ"}, -- ناقص واوي
{"شوي", "فعَل يفعِل", "شَوَى", "يَشْوِي", "اِشْوِ", "شَاوٍ"}, -- ناقص يائي
{"وقي", "فعَل يفعِل", "وَقَى", "يَقِي", "قِ", "وَاقٍ"}, -- لفيف مفروق
}
for _, c in ipairs(cases) do
local root, pattern = c[1], c[2]
local r = conj(root, pattern)
self:equals(root .. "، الماضي", slot(r, "past_3ms"), c[3])
self:equals(root .. "، المضارع", slot(r, "ind_3ms"), c[4])
self:equals(root .. "، الأمر", slot(r, "imp_2ms"), c[5])
self:equals(root .. "، اسم الفاعل", slot(r, "ap"), c[6])
end
end
-- حواشي على حركات الماضي والمضارع، مثل <I/a[نادر]~u>.
-- كانت ترمي خطأ لوا: verify_vowel كانت تقارن الكائن نفسه لا قيمته.
function tests:test_conj_vowel_footnotes()
local a = m_ar_verb.do_generate_forms({[1] = "كتب<I/a[نادر]~u>"}, "ar-conj")
self:equals("كتب، حاشية على حركة الماضي", a.forms.past_3ms[1].form, "كَتَبَ")
self:equals("كتب، حاشية على حركة الماضي، المضارع", a.forms.ind_3ms[1].form, "يَكْتُبُ")
local b = m_ar_verb.do_generate_forms({[1] = "كتب<I/a~u[نادر]>"}, "ar-conj")
self:equals("كتب، حاشية على حركة المضارع", b.forms.past_3ms[1].form, "كَتَبَ")
-- أجوف: يمرّ عبر infer_radicals، وهو موضع الخطأ الأصلي
local c = m_ar_verb.do_generate_forms({[1] = "قال<I/a[نادر]~u>"}, "ar-conj")
self:equals("قال، حاشية على حركة الماضي", c.forms.past_3ms[1].form, "قَالَ")
end
-- q/qq/l/ll أُزيلت لأنها تستدعي [[وحدة:pron qualifier]] غير الموجودة في ويكاموس العربي.
-- المطلوب الآن خطأ واضح في التحليل، لا خطأ لوا.
function tests:test_conj_qualifier_modifiers_rejected()
local ok, err = pcall(m_ar_verb.do_generate_forms,
{[1] = "كتب<I/a~u.vn:كِتَابَة<q:نادر>>"}, "ar-conj")
local msg = tostring(err)
self:equals("q: مرفوضة", ok, false)
self:equals("q: رسالة خطأ واضحة", not not msg:find("Unrecognized prefix"), true)
self:equals("q: لا تستدعي pron qualifier", not not msg:find("pron qualifier"), false)
end
return tests