diff --git "a/text/0000-\330\247\330\256\330\252\330\250\330\247\330\261-\330\260\331\207\330\250\331\212\331\221-\331\204\330\267\330\250\331\202\330\251-\330\261\330\263\331\205-\330\247\331\204\331\210\330\247\330\254\331\207\330\251.md" "b/text/0000-\330\247\330\256\330\252\330\250\330\247\330\261-\330\260\331\207\330\250\331\212\331\221-\331\204\330\267\330\250\331\202\330\251-\330\261\330\263\331\205-\330\247\331\204\331\210\330\247\330\254\331\207\330\251.md" new file mode 100644 index 0000000..8518b6d --- /dev/null +++ "b/text/0000-\330\247\330\256\330\252\330\250\330\247\330\261-\330\260\331\207\330\250\331\212\331\221-\331\204\330\267\330\250\331\202\330\251-\330\261\330\263\331\205-\330\247\331\204\331\210\330\247\330\254\331\207\330\251.md" @@ -0,0 +1,66 @@ +- **عنوان المقترح:** اختبار ذهبيّ حتميّ لطبقة التخطيط والرسم في SadUI (RecordingRenderer) +- **النطاق:** لغة `text/` (بنية اختبار للقلب الرسوميّ `features/graphics`) +- **تاريخ البدء:** 2026-07-02 +- **رقم الـ RFC:** (يُترك فارغاً حتى الدمج) +- **الحالة:** مقترَح (PR مفتوح) +- **Issue التتبُّع:** (يُترك فارغاً حتى القبول) + +# ملخّص + +إضافة اختبار ذهبيّ (golden) headless حتميّ يغطّي **طبقة التخطيط (`LayoutEngine::layout()`) والرسم (`PlatformRenderer::renderNode()`)** المشتركة أسفل شجرة IR في نظام رسومات لغة ص. يُنجَز عبر `RecordingRenderer` — تطبيقٌ لواجهة `PlatformRenderer` المجرّدة يُسجّل كلّ نداء رسم أساسيّ كسطر نصّيّ حتميّ («قائمة رسم») بدل الرسم على بكسل — ثمّ يقارن القائمة بذهبيّ مثبَّت. لا يتطلّب SDL2 ولا خطوطًا ولا نافذة، ويعمل في CI بحتميّة تامّة. + +# الدافع (Motivation) + +آليّة التحقّق الحاليّة (`طباعة_شجرة` + اختبارات `ui_cov_*.ص` بـ`@expected`) تثبت تطابق **شجرة IR** بين المحرّكين بايتيًّا، لكنّها تتوقّف عند شجرة IR: لا تمسّ حساب التخطيط (مواضع/أحجام العناصر) ولا منطق `renderNode` (تحويل كلّ عقدة إلى نداءات رسم: ظلّ/خلفية/محاذاة نصّ RTL/تدرّج/قصّ). خطأ في رياضيات التخطيط أو ترتيب الرسم يمرّ صامتًا اليوم. الرسم البكسليّ الفعليّ محكوم بـ`SAD_UI_USE_SDL2` وخطوط `SDL2_ttf` (غير حتميّ، لا يصلح لبوّابة CI). هذا المقترح يسدّ الطبقة غير المغطّاة بأرخص وأحتم صورة. + +# الشرح التوجيهي (Guide-level explanation) + +المطوّر يبني شجرة IR تمثيليّة (زرّ، عمود بأبناء، نصّ عربيّ…)، يشغّل التخطيط ثمّ الرسم عبر `RecordingRenderer`، فيحصل على قائمة رسم نصّيّة مثل: + +``` +rect (1.00,1.20) 120.00x48.00 #00000033 +rrect (0.00,0.00) 120.00x48.00 r=8.00 #1E88E5 +text "OK" (52.00,14.40) size=16.00 #FFFFFF +``` + +تُقارَن هذه القائمة بذهبيّ مثبَّت في الاختبار. أيّ تغيّر في التخطيط/الرسم يكسر المطابقة فيُكشَف فورًا؛ وعند التغيير المقصود يُحدَّث الذهبيّ بتشغيل الاختبار بمتغيّر البيئة `SAD_UI_DUMP_GOLDEN=1` ونسخ المطبوع. + +# الشرح المرجعي (Reference-level explanation) + +- **مصدر الحقيقة (`language-truth/`):** لا تغيير — المقترح **لا يوسّع SoT إطلاقًا** (يلتزم قاعدة «الأدوات/الاختبارات لا توسّع SoT»). +- **المكتبة (`features/graphics`):** لا تغيير في منطق الرسم؛ الاختبار يستهلك الواجهة المجرّدة القائمة `PlatformRenderer`/`LayoutEngine` كما هي. +- **المحرّكان (المفسّر/المترجم):** لا تغيير. طبقة `render()` مشتركة (تطبيق واحد `renderNode` يُستدعى من `render()` بلا تفريع لكلّ منصّة/محرّك)، فالتباعد بينهما على هذه الطبقة مستحيلٌ بالبناء — لذا يكفي اختبارها مرّةً بحتميّة، ولا حاجة لتكافؤ طرفٍ لطرف عبر SoT. +- **بنية الاختبار:** ملفّ `tests/unit/comprehensive/recording_renderer.h` (RecordingRenderer، header-only)، وملفّ `test_ui_render_displaylist.cpp`، وكتلة `add_comprehensive_test` في `cmake/tests_comprehensive.cmake` تربط `sad_graphics`. القياس النصّيّ تركيبيّ (تقدّم=حجم×0.5، ارتفاع=حجم×1.2) ⇒ مستقلّ عن الخطّ وحتميّ. +- **نظام الأخطاء/الأدوات/التوافق الخلفي:** لا أثر. + +# السلبيات (Drawbacks) + +- لا يتحقّق من **صحّة البكسل الفعليّة** (تنعيم، rasterization الخطّ، دقّة الدائرة/التدرّج) — فقط هندسة النداءات وألوانها وترتيبها. +- القائمة الذهبيّة تحتاج تحديثًا عند أيّ تغيير مقصود في التخطيط/الرسم (طبيعة اختبارات golden). +- حساسيّة محتملة لتنسيق العائم عبر المنصّات (يُخفَّف بتنسيق `%.2f` وتطبيع الصفر السالب). + +# المبرّرات والبدائل (Rationale and alternatives) + +- **بديل: لقطة بكسل (SDL)** — يكشف أخطاء بصريّة حقيقيّة لكنّه غير حتميّ (خطوط/تعريب/تنعيم)، هشّ، ولا يصلح لبوّابة CI. يُقترَح لاحقًا كطبقة ثانية منفصلة بخطّ مثبَّت وعتبة تسامح، خارج البوّابة الحتميّة. +- **بديل: تكافؤ قائمة الرسم طرفًا لطرف عبر مدمَجة في SoT** — قيمته الإضافيّة محدودة (طبقة الرسم مشتركة لا تتباعد) ويخالف قاعدة عدم توسيع SoT للاختبار. +- عدم فعل شيء يُبقي طبقة التخطيط+الرسم بلا أيّ اختبار. + +# أعمال سابقة (Prior art) + +نمط الطبقتين سائد: Flutter (`matchesGoldenFile` + اختبار شجرة render-object)، React (`react-test-renderer` snapshot)، Android Paparazzi (رسم بلا جهاز)، Point-Free swift-snapshot-testing (`.image` بكسل + `.recursiveDescription` نصّ حتميّ جنبًا لجنب)، وSkia `SkPicture` (تسجيل قائمة أوامر رسم قابلة للتسلسل كبديل حتميّ للبكسل، تستخدمه WebKit). RecordingRenderer هو تطبيق «قائمة الأوامر الحتميّة» على معمار ص. + +# أسئلة غير محسومة (Unresolved questions) + +- موضع `RecordingRenderer` نهائيًّا: أداة اختبار في `tests/` (الحاليّ، وهو المُوصى به بمراجعة) أم خلفيّة مكتبيّة قابلة لإعادة الاستخدام (`features/graphics/backends/recording` مثل freestanding)؟ الترقية توسّع سطح الإنتاج وبوّابات Boundary/Layering دون مستهلك إنتاجيّ ⇒ أُبقي أداةَ اختبار حتى يظهر مستهلك ثانٍ. +- توسيع الحالات لتغطية مسارات renderNode إضافيّة (شبكة/دوران/تحريك بزمن مثبَّت). +- **خارج النطاق:** طبقة لقطة البكسل، وأيّ توحيد لسلوك رسم قائم. + +## ثغرة رسم كشفها الاختبار — وأُصلحت في هذا الـPR + +**رسم خلفية الحاوية مرّتين (مُصلَح).** كانت خطوة الخلفية العامّة في `renderNode` (`features/graphics/core/src/platform_renderer.cpp`، تُنفَّذ لكلّ عقدة قبل الـswitch، تدعم `لون_خلفية`/`خلفية` + الزوايا + التحويم) تُتبَع بفرع الحاويات الذي يستدعي `drawFilledRect` **ثانيةً** بنفس المستطيل واللون — رسمٌ مزدوج (هدر + تراكم ألفا بصريّ على خلفيّة شبه شفّافة). كشفته حالتا `column_children` و`opacity_half` (سطرا `rect` متطابقان). **الإصلاح:** حُذف الرسم المكرّر من فرع الحاويات (خطوة الخلفية العامّة مجموعة فائقة له، أكمل وأدقّ)؛ بقي القصّ. تأكّد بلا انحدار عبر 309 اختبار رسومات + 18/18، وحُدِّثت القائمتان الذهبيّتان إلى `rect` واحد. هذا مثالٌ حيّ على قيمة الاختبار: كشف ثغرة رسم ثمّ ثبّت إصلاحها. + +# إمكانات مستقبلية (Future possibilities) + +- **طبقة لقطة بكسل (مؤجَّلة بقرار — الطبقة الثانية):** تكشف ما لا تكشفه الطبقة أ (تنعيم، rasterization الحرف، دقّة المنحنيات). **التأجيل مبرَّر:** قيمتها ضيّقة (تختبر غالبًا SDL_ttf/FreeType الطرف الثالث لا منطق ص)، وهشّة (لاحتميّة FreeType ⇒ baselines لكلّ منصّة)، ولا بيئة رسوميّة في CI اليوم (لا Xvfb/dummy driver). **تصحيح:** خطّ Ahem يثبّت المقاييس لا شكل الحرف (تغطّيه الطبقة أ)، ففحص البكسل يحتاج خطًّا حقيقيًّا مثبَّتًا + عتبة. **ممكِّناتها:** (1) مسار رسم headless برمجيّ عبر `SDL_CreateSoftwareRenderer` محقون في `DesktopRenderer` (ممكن — المُصيّر مفصول عن النافذة)؛ (2) مهمّة CI منفصلة على Linux بـ`SDL_VIDEODRIVER=dummy`، إعلاميّة غير حاجبة؛ (3) خطّ + إصدار SDL_ttf مثبَّتان + عتبة؛ (4) محفّز قيمة فعليّ (انحدار بصريّ لا تكشفه الطبقة أ). **MVP:** مشهد بلا نصّ (مستطيل+دائرة+تدرّج+زوايا) مقابل baseline واحدة؛ النصّ يُؤجَّل. +- توسيع القوائم الذهبيّة لكامل كتالوج المصانع (42) بالتوازي مع `ui_cov`. +- إتاحة RecordingRenderer كخلفيّة headless عامّة (توليد لقطات وثائقيّة، تشخيص).