انتقل إلى المحتوى

اختبار المرجع

يخضع كل قرار تنفيذي في Phasis لسؤال واحد: هل ينتج V8 المخرجات نفسها؟

لدى JavaScript دلالات دقيقة — NaN !== NaN، -0 === 0، [1] + 1 === "11"، والترتيب الدقيق لتعداد الخصائص، وتوقيت المهام الدقيقة في Promise. وإعادة اشتقاقها بقراءة المواصفات بطيئة ومعرّضة للأخطاء. يتجاوز Phasis ذلك بالتعامل مع Node.js (V8) بوصفه مرجعًا: اكتب سيناريو JS، وشغّله عبر V8، وسجّل المخرجات كحقيقة مرجعية؛ ثم شغّله عبر Phasis، وسجّل المخرجات مجددًا؛ وقارن بينهما.

الكود
1. SETUP    → a JavaScript source file or snippet
2. ORACLE   → Node.js executes it, output captured as truth
3. ACTUAL   → Phasis executes it, output captured
4. COMPARE  → oracle vs actual, diff measures the gap

مستويان للاختبار

المستوى 1 — سيناريوهات مخصصة

كل سيناريو في scenarios/ هو برنامج JS صغير ذو مخرجات معروفة. شغّل سيناريو واحدًا:

سطر الأوامر
./bin/test-scenario operators/arithmetic

شغّلها كلها، أو فئة واحدة فقط، أو بالتوازي:

سطر الأوامر
./bin/test-regression
./bin/test-regression --jobs 4
./bin/test-regression --category expressions

حدّث المرجع (e.g، بعد تحديث إصدار Node.js):

سطر الأوامر
./bin/oracle --refresh operators/arithmetic

نُظّمت السيناريوهات بحسب فصول ECMAScript: الثوابت، والمعاملات، والمتغيرات، وتدفق التحكم، والدوال، والكائنات، والمصفوفات، والأصناف، والمكوّنات المضمّنة، والأخطاء، والتشغيل البيني، والحالات الطرفية.

المستوى 2 — test262

تُسحب مجموعة المطابقة الرسمية لـ TC39 كوحدة فرعية في git ضمن test262/. كل اختبار يتحقق من نفسه: ينجح إذا لم يُطلق استثناءً، ويفشل إذا أطلقه (أو العكس في اختبارات negative). يحمّل مشغّل الاختبارات assert.js وsta.js قبل كل اختبار.

سطر الأوامر
./bin/test262                                    # full suite
./bin/test262 --category built-ins/Array         # subset
./bin/test262 --jobs 4                           # parallel
./bin/test262 --failures                         # one line per failing test
./bin/compat-report                              # full COMPAT.md + compat.json

توجد لقطة المجموعة الكاملة في COMPAT.md، ويُعاد إنشاؤها بواسطة bin/compat-report. وفي CI، يشغّل سير عمل compat-matrix.yml الاختبارات على دفعات ويحفظ النتيجة؛ ويُشغّل هذا السير يدويًا، لا مع كل دفعة تغييرات.

فحص محلي قبل الدفع

يشغّل ./bin/verify-all بوابة الجودة التي يفرضها CI:

الكود
=== PHPStan ===                  level 6, zero errors
=== Code Standards ===           PHPCS, zero warnings
=== PHPUnit ===                  unit suite passes
=== Oracle Regression ===        scenarios under `scenarios/` pass

لا يكفي هذا بمفرده — يجب أيضًا إجراء اختبارات عيّنية من test262 لأي تغيير يمس المحلّل أو المفسّر أو المكوّنات المضمّنة. وفي كل طلب سحب، يشغّل CI ‏PHPStan وPHPCS ومجموعة اختبارات الوحدة واختبار تراجع المرجع؛ كما يشغّل مجموعتي WPT والحزم الشائعة عندما يغيّر طلب السحب src/ أو نماذجهما. مصفوفة test262 الكاملة ليست ضمن CI لطلبات السحب، لذا اختبر محليًا الفئات المتأثرة.

لماذا ينجح هذا

إن اعتماد V8 مصدرًا للحقيقة المرجعية يزيل تقريبًا كل الحاجة إلى قراءة المواصفات خلال دورة التطوير. وأي اختلاف عن V8 يشير دائمًا إلى وجود خطأ في Phasis — وليس إلى غموض في المواصفات يمكن الجدال بشأنه. وبعد إدخال الإصلاح، تصبح اختبارات test262 المقابلة اختبارات تراجع دائمة.

معدل النجاح البالغ 100 % نتيجة لهذا الانضباط: يُقيّم كل طلب سحب بمقارنته مع V8، ويرفض CI التراجعات في مجموعات اختبارات المرجع وWPT والحزم الشائعة، وتؤكد مصفوفة test262 نتيجة المجموعة الكاملة عند تشغيلها.