كيف تصنع ملفًا تالفًا للاختبار
المدقق الذي لم يُعرض عليه إلا ملفات سليمة لم يُختبر حقًّا. إليك طريقة الحصول على ملف أُتلف عمدًا، ويخرج بالحجم الذي تطلبه تمامًا، ويحمل بيانًا يقول ما الذي ينبغي أن يفعله نظامك به.
الجواب المختصر
tfg generate --format png --size 2mb --damage zero-head --out ./out يكتب ملف PNG حجمه
2097152 بايتًا بالضبط، وبايتاته الأولى أصفار، ويسجّل البيان المجاور له أن نظامك ينبغي أن يرفضه.
الطريقة المعتادة
لماذا يُعدّ الملف المتلف يدويًا اختبارًا رديئًا
الطرق المعتادة هي محرر سداسي عشري، أو سكربت يقلب بضع بايتات عشوائية، أو قصّ الملف بـ
head أو truncate. تنجح مرة واحدة، ثم تكلّفك:
- يختلف في كل مرة. البايت العشوائي يقع في مكان جديد عند كل تشغيل، فقد لا يعود فشل يوم الثلاثاء يوم الأربعاء.
- يغيّر الحجم. الملف المقصوص أصغر من الحد الذي كان ينبغي أن يبقى تحته، فيجيب فحص الحجم قبل فحص المحتوى، ويمرّ الاختبار لسبب خاطئ.
- كثيرًا ما يمرّ دون أن يلاحظه أحد. النص العادي يبقى مقروءًا مع تغيير بايت في الوسط، وقارئ الصور المتسامح يرسمه ببساطة، فيُقبل الملف الذي كان يُفترض أن يكون تالفًا.
- لا يقول شيئًا عمّا ينبغي أن يحدث. الملف مجرد بايتات، ومن يقرأ الاختبار لاحقًا عليه أن يخمّن هل كان المقصود القبول أم الرفض.
ما تحصل عليه
الملف التالف يبقى بالحجم الذي طلبته
يُولَّد الملف كالمعتاد ثم يُتلَف في طريقه إلى القرص. يحتفظ بالحجم الذي طلبته، ويكتب الأمر نفسه البايتات نفسها مرة أخرى.
tfg generate --format png --size 2mb --damage zero-head --out ./out
tfg generate --format pdf --size 1mb --count 5 --damage zero-head:bytes=16 --out ./broken
تُكتب الإعدادات بعد النقطتين. يمكن تكرار الخيار، وتُطبَّق أنواع التلف بالترتيب الذي تكتبه. ويعمل مع كل واحدة من 26 صيغة.
ما الذي يستطيعه
ما أنواع التلف المتاحة؟
هذه هي القائمة التي يطبعها البرنامج، وتُقرأ منه عند بناء هذه الصفحة. يطبع tfg damage
القائمة نفسها، ويبيّن tfg damage <id> ما يقبله واحد منها.
| التلف | ما يفعله بالبايتات | أصغر ملف | الإعدادات |
|---|---|---|---|
zero-head |
يكتب أصفارًا فوق أول بايتات الملف دون المساس بطوله. معظم القارئات تنظر إلى هناك أولًا، فيلاحظ هذا التلف كل شيء تقريبًا. | 8 | bytes |
يكتب zero-head أصفارًا فوق بداية الملف. معظم القارئات تنظر إلى هناك أولًا، إلى التوقيع
والترويسة اللذين يقولان ما هو الملف، فتلاحظ ذلك أي قارئة تقريبًا. والنص العادي والسجلات لا توقيع
لها وتُرفض أيضًا، لأن سلسلة من البايتات الصفرية ليست نصًّا. وتحت أربعة بايتات تخرج بعض الصيغ
بتلف لا تشتكي منه أي قارئة، ولهذا يبدأ الإعداد من أربعة.
ما يقوله البيان
بيان يقول ما ينبغي أن يحدث
كل ملف تالف يحصل على مدخل يقول إن نظامك ينبغي أن يرفضه، ويُسجَّل التلف بجانبه:
"expected": {
"outcome": "reject",
"reason": "content_malformed",
"confidence": "certain"
},
"damage": [
{
"type": "zero-head",
"settings": {
"bytes": "8"
}
}
]
يُرفض طلبان قبل أن يُكتب أي شيء، لأن كلًّا منهما سيترك على القرص ملفًا يصفه البيان وصفًا خاطئًا:
- ملف أصغر مما يحتاجه التلف، وكان سيخرج دون تغيير
-
expected: acceptبجانب تلف، لأن لا شيء يمكنه تحقيق ذلك. اكتبsanitizeإن كان نظامك مقصودًا به إصلاح الملف، أوunspecifiedإن كان هذا هو السؤال الذي تطرحه
في وصفة
ملفات سليمة وتالفة في تشغيل واحد
ضع الاثنين في وصفة واحدة، فيحمل البيان المتوقَّع لكل ملف، ولا يحتاج الاختبار إلى قائمة تقول أيها أيّ:
version: 1
targets:
- id: healthy
format: pdf
size: 1mb
expected: accept
- id: broken
format: pdf
size: 1mb
damage:
- zero-head
في اختبار
تحويله إلى اختبار
يقرأ الاختبار البيان ويتحقق من أن ما حدث هو ما أُعلن. لا يحتاج إلى قائمة بأسماء الملفات:
import json, os
directory = "healthy-and-broken"
manifest = json.load(open(os.path.join(directory, "manifest.json")))
for entry in manifest["files"]:
response = upload(os.path.join(directory, entry["path"]))
if entry["expected"]["outcome"] == "reject":
assert not response.ok
else:
assert response.ok
الرفض الجيد هو الرفض النظيف. رسالة تقول ما الخطأ هي الجواب الذي تريده. أما خطأ الخادم أو التعليق أو ملف حُفظ نصفه فهو العيب الذي وُجد هذا الاختبار لاكتشافه.
التالي
إلى أين من هنا
- الإعداد المسبق upload-validation يوجّه إلى النموذج السؤالين الآخرين، عن الحجم وعن النوع.
- ملفات الاختبار في CI تشغّل وصفة كهذه في خط البناء.
-
التوثيق يضم كل خيار من خيارات
tfg generate.