Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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 عامّة (توليد لقطات وثائقيّة، تشخيص).
Loading