أنواع الاختبارات
- الاختبارات الوظيفية - مجموعة من الاستعلامات والبرامج النصية التي تتضمن المجموعات الفرعية المتداخلة التالية
- Fast test - أصغر مجموعة فرعية
- الاختبارات عديمة الحالة التي لا تتطلب تعبئة قواعد البيانات بالبيانات
- الاختبارات المتسلسلة التي لا يمكن تشغيلها بالتوازي
- اختبارات التكامل، وتُشغَّل بواسطة
pytestعلى عنقود - اختبارات الوحدة
- اختبارات الأداء
- اختبارات البناء
- أدوات sanitizer
- Fuzzers وبعض الاختبارات الأخرى، راجع الأقسام أدناه.
الاختبارات الوظيفية
./tests/queries.
يمكن أن يكون كل اختبار أحد نوعين: .sql و .sh.
- اختبار
.sqlهو برنامج SQL نصي بسيط يُمرَّر إلىclickhouse-clientعبر pipe. - اختبار
.shهو برنامج نصي يُشغَّل مباشرةً.
.sh.
يجب ألا تستخدم اختبارات .sh إلا عند الحاجة إلى اختبار ميزة لا يمكن التحقق منها باستخدام SQL فقط، مثل تمرير بعض بيانات الإدخال إلى clickhouse-client عبر pipe أو اختبار clickhouse-local.
من الأخطاء الشائعة عند اختبار أنواع البيانات
DateTime و DateTime64 افتراض أن الخادم يستخدم منطقة زمنية محددة (مثل “UTC”). وهذا غير صحيح، إذ تُحدَّد المناطق الزمنية عشوائيًا عمدًا في تشغيلات اختبارات CI.
وأسهل حل بديل هو تحديد المنطقة الزمنية لقيم الاختبار صراحةً، مثل toDateTime64(val, 3, 'Europe/Amsterdam').تشغيل اختبار محليًا
01428_hash_set_nan_key، على سبيل المثال، انتقل إلى مجلد المستودع ونفّذ الأمر التالي:
stderr وstdout) في الملفين 01428_hash_set_nan_key.[stderr|stdout] الموجودين بجوار ملف الاختبار نفسه (بالنسبة إلى queries/0_stateless/foo.sql، سيكون الإخراج في queries/0_stateless/foo.stdout).
راجع tests/clickhouse-test --help للاطّلاع على جميع خيارات clickhouse-test.
يمكنك تشغيل جميع الاختبارات أو تشغيل مجموعة فرعية منها بتمرير عامل تصفية لأسماء الاختبارات: ./clickhouse-test substring.
توجد أيضًا خيارات لتشغيل الاختبارات بالتوازي أو بترتيب عشوائي.
تشغيل الاختبارات السريعة
t3.2xlarge يعمل بنظام Ubuntu amd64 مع سعة تخزين تبلغ 100 GB.
- ثبّت المتطلبات الأساسية ثم سجّل الدخول مرة أخرى.
- نزّل الشيفرة المصدرية.
- ابنِ الشيفرة البرمجية وشغّل “الاختبارات السريعة”.
nohup أو disown لإبقائها قيد التشغيل بعد انقطاع اتصال ssh.
تشغيل الاختبارات عديمة الحالة
m7i.8xlarge من AWS بمعمارية amd64 ونظام Ubuntu، مع سعة تخزين تبلغ 200 جيجابايت.
- ثبّت المتطلبات اللازمة ثم سجّل الدخول مجددًا.
- نزّل الشيفرة المصدرية.
- قم ببناء الشيفرة.
- شغّل الاختبارات عديمة الحالة التي يمكن تنفيذها بالتوازي.
python -m ci.praktika run مهمةً محددة للتكامل المستمر، ويمكنك قراءة المزيد عن ClickHouse CI هنا.
إضافة اختبار جديد
.sql أو .sh في الدليل queries/0_stateless.
ثم أنشئ ملف .reference المقابل باستخدام clickhouse-client < 12345_test.sql > 12345_test.reference أو ./12345_test.sh > ./12345_test.reference.
يجب أن تقتصر الاختبارات على إنشاء الجداول أو حذفها أو تنفيذ عمليات select عليها، وما إلى ذلك، ضمن قاعدة البيانات test فقط، وهي قاعدة بيانات تُنشأ تلقائيًا مسبقًا.
ولا بأس باستخدام الجداول المؤقتة.
ولإعداد البيئة نفسها المستخدمة في CI على جهازك المحلي، ثبّت إعدادات الاختبار (إذ ستستخدم تنفيذًا وهميًا لـ ZooKeeper وستعدّل بعض الإعدادات)
يجب أن تكون الاختبارات
- في أضيق حد ممكن: لا تُنشئ إلا الجداول والأعمدة وأدنى قدر لازم من التعقيد،
- سريعة: ألّا تستغرق أكثر من بضع ثوانٍ (ويُفضَّل: أقل من ثانية)،
- صحيحة وحتمية: أن تفشل إذا، وفقط إذا، كانت الميزة قيد الاختبار لا تعمل،
- معزولة/عديمة الحالة: ألا تعتمد على البيئة أو التوقيت
- شاملة: أن تغطي الحالات الطرفية مثل الأصفار، والقيم NULL، والمجموعات الفارغة، والاستثناءات (الاختبارات السلبية؛ استخدم الصياغة
-- { serverError xyz }و-- { clientError xyz }لذلك)، - أن تنظّف الجداول في نهاية الاختبار (تحسّبًا لوجود بقايا)،
- والتأكّد من أن الاختبارات الأخرى لا تختبر الشيء نفسه (أي استخدم
grepأولًا).
اختبارات القوالب باستخدام Jinja
.sql على شكل قالب Jinja2 بإضافة اللاحقة .j2 إلى اسم الملف، بحيث يصبح foo.sql هو foo.sql.j2. قبل تشغيل الاختبار، يعرض clickhouse-test القالب كنص برمجي عادي بصيغة .sql ثم ينفّذ الناتج.
يفيد ذلك عندما يكرّر الاختبار الاستعلام نفسه مع اختلافات بسيطة: إذ تُنشئ الحلقة الاستعلامات انطلاقًا من قالب موجز بدلًا من كتابة كل استعلام يدويًا. وأكثر البنى شيوعًا هي:
{% for ... %} ... {% endfor %}لتكرار كتلة،{{ expression }}لإدراج قيمة في المخرجات،-%}و{%-لحذف المسافات البيضاء المجاورة حتى يبقى النص البرمجي المُولَّد نظيفًا.
<name>.reference عادي يحتوي على النتائج الموسَّعة بالكامل، أو كقالب <name>.reference.j2، والذي يعالجه clickhouse-test بالطريقة نفسها قبل المقارنة. استخدم الصيغة المعتمدة على القوالب عندما تتبع المخرجات المتوقعة أيضًا نمطًا متكررًا. لمزيد من الأمثلة، راجع ملفات *.sql.j2 الموجودة في tests/queries/0_stateless/.
تقييد تشغيل الاختبارات
.sql، تُوضَع الوسوم في السطر الأول على شكل تعليق SQL:
.sh، تُكتب الوسوم على شكل تعليق في السطر الثاني:
| اسم الوسم | ما يفعله | مثال على الاستخدام |
|---|---|---|
disabled | لا يتم تشغيل الاختبار | |
long | تمتد مدة تنفيذ الاختبار من دقيقة واحدة إلى 10 دقائق | |
deadlock | يتم تشغيل الاختبار في حلقة لمدة طويلة | |
race | مثل deadlock. يُفضَّل استخدام deadlock | |
shard | يجب أن يستمع الخادم إلى 127.0.0.* | |
distributed | مثل shard. يُفضَّل استخدام shard | |
global | مثل shard. يُفضَّل استخدام shard | |
zookeeper | يتطلب الاختبار تشغيل Zookeeper أو ClickHouse Keeper | يستخدم الاختبار ReplicatedMergeTree |
replica | مثل zookeeper. يُفضَّل استخدام zookeeper | |
no-fasttest | لا يتم تشغيل الاختبار ضمن Fast test | يستخدم الاختبار محرك الجداول MySQL المعطَّل في Fast test |
fasttest-only | لا يتم تشغيل الاختبار إلا ضمن Fast test | |
no-[asan, tsan, msan, ubsan] | يعطّل الاختبارات في إصدارات البناء التي تستخدم sanitizers | يتم تشغيل الاختبار تحت QEMU، الذي لا يعمل مع sanitizers |
no-replicated-database | يعطّل الاختبار عندما تستخدم قاعدة البيانات الافتراضية ReplicatedDatabaseEngine | |
no-ordinary-database | يعطّل الاختبار عندما يكون محرك قاعدة البيانات الافتراضية هو Ordinary | |
no-parallel | يعطّل تشغيل اختبارات أخرى بالتوازي مع هذا الاختبار | يقرأ الاختبار من جداول system وقد تنكسر الثوابت |
no-parallel-replicas | يعطّل الاختبار عندما تكون parallel replicas مفعّلة | |
no-debug | يعطّل الاختبارات في إصدارات Debug | |
no-release | يعطّل الاختبارات في إصدارات Release | |
no-darwin | يعطّل الاختبار على macOS (Darwin) | يعتمد الاختبار على ميزات خاصة بـ Linux، مثل distributed queries أو procfs أو خادم HTTP |
no-polymorphic-parts, no-random-settings, no-random-merge-tree-settings, no-backward-compatibility-check, no-cpu-x86_64, no-cpu-aarch64, no-cpu-ppc64le, no-s3-storage.
بالإضافة إلى الإعدادات المذكورة أعلاه، يمكنك استخدام العلامات USE_* من system.build_options لتحديد استخدام ميزات معيّنة في ClickHouse.
على سبيل المثال، إذا كان اختبارك يستخدم جدول MySQL، فيجب إضافة الوسم use-mysql.
تحديد حدود الإعدادات العشوائية
.sh، تُكتب الحدود على هيئة تعليق في السطر المجاور للوسوم، أو في السطر الثاني إذا لم يتم تحديد أي وسوم:
.sql، توضع الوسوم على شكل تعليق SQL في السطر المجاور لها أو في السطر الأول:
None للحدّ الآخر.
اختيار اسم الاختبار
00422_hash_function_constexpr.sql.
ولاختيار البادئة، ابحث عن أكبر بادئة موجودة بالفعل في المجلد، ثم زِدها بمقدار واحد.
التحقق من حدوث خطأ متوقّع
x.
إذا لم يحدث أي خطأ، أو كان الخطأ مختلفًا، فسيفشل الاختبار.
إذا كنت تريد التأكد من حدوث خطأ على جانب العميل، فاستخدم الوسم clientError بدلًا من ذلك.
لا تتحقق من صياغة معيّنة لرسالة الخطأ، لأنها قد تتغير في المستقبل، وقد يؤدي ذلك إلى فشل الاختبار دون داعٍ.
تحقق فقط من رمز الخطأ.
إذا لم يكن رمز الخطأ الحالي دقيقًا بما يكفي لاحتياجاتك، ففكّر في إضافة رمز جديد.
اختبار استعلام موزّع
remote مع العناوين 127.0.0.{1..2} لكي يجري الخادم استعلامًا على نفسه؛ أو يمكنك استخدام عناقيد الاختبار المعرّفة مسبقًا في ملف إعدادات الخادم، مثل test_shard_localhost.
تذكّر إضافة الكلمتين shard أو distributed إلى اسم الاختبار، بحيث يُشغَّل في CI ضمن الإعدادات الصحيحة التي يكون فيها الخادم مهيّأً لدعم الاستعلامات الموزّعة.
العمل مع الملفات المؤقتة
$CLICKHOUSE_TEST_UNIQUE_NAME لمنح الملفات المؤقتة اسمًا فريدًا للاختبار الجاري تشغيله.
وبذلك يمكنك التأكد من أن الملف الذي تُنشئه أثناء الإعداد أو تحذفه أثناء التنظيف هو الملف المستخدم فقط من قِبل ذلك الاختبار، وليس من قِبل اختبار آخر يعمل بالتوازي.
الأخطاء المعروفة
tests/queries/bugs.
وتُنقل هذه الاختبارات إلى tests/queries/0_stateless عند إصلاح الأخطاء.
اختبارات التكامل
tests/integration/README.md لمعرفة كيفية تشغيل هذه الاختبارات.
لاحظ أن تكامل ClickHouse مع برامج تشغيل الجهات الخارجية لا يُختبَر.
كما أننا لا نملك حاليًا اختبارات تكامل لبرنامجي تشغيل JDBC وODBC الخاصين بنا.
اختبارات الوحدة
ENABLE_TESTS.
توجد اختبارات الوحدة (وبرامج الاختبار الأخرى) في الأدلة الفرعية tests في أنحاء الشيفرة البرمجية.
لتشغيل اختبارات الوحدة، اكتب ninja test.
تستخدم بعض الاختبارات gtest، بينما يكون بعضها الآخر مجرد برامج تُرجع رمز خروج غير صفري عند فشل الاختبار.
ليس من الضروري وجود اختبارات وحدة إذا كانت الشيفرة البرمجية مشمولة بالفعل بالاختبارات الوظيفية (وعادةً ما تكون الاختبارات الوظيفية أبسط بكثير في الاستخدام).
يمكنك تشغيل فحوصات gtest الفردية باستدعاء الملف التنفيذي مباشرةً، على سبيل المثال:
اختبارات الأداء
tests/performance/.
ويُمثَّل كل اختبار بملف .xml يصف حالة الاختبار.
تُشغَّل الاختبارات باستخدام الأداة docker/test/performance-comparison. راجع ملف readme لمعرفة طريقة التشغيل.
يُنفِّذ كل اختبار استعلامًا واحدًا أو عدة استعلامات (وقد تكون مع توليفات من المعلمات) ضمن حلقة.
إذا كنت تريد تحسين أداء ClickHouse في سيناريو معيّن، وكان بالإمكان ملاحظة التحسينات من خلال استعلامات بسيطة، فمن الموصى به بشدة كتابة اختبار أداء.
ويُوصى أيضًا بكتابة اختبارات أداء عند إضافة دوال SQL أو تعديلها إذا كانت معزولة نسبيًا وليست غامضة جدًا.
ومن المفيد دائمًا استخدام perf top أو أدوات perf الأخرى أثناء اختباراتك.
أدوات الاختبار والبرامج النصية
tests ليست اختبارات جاهزة، بل أدوات للاختبار.
فعلى سبيل المثال، بالنسبة إلى Lexer توجد أداة src/Parsers/tests/lexer لا تقوم إلا بتقسيم stdin إلى رموز، ثم تكتب النتيجة الملوّنة إلى stdout.
يمكنك استخدام هذا النوع من الأدوات كأمثلة على الشيفرة، وللاستكشاف والاختبار اليدوي.
اختبارات متفرقة
tests/external_models.
هذه الاختبارات غير محدَّثة ويجب نقلها إلى اختبارات التكامل.
يوجد اختبار منفصل لعمليات الإدخال وفق النصاب.
يشغّل هذا الاختبار عنقود ClickHouse على خوادم منفصلة ويحاكي حالات فشل متنوعة: انقسام الشبكة، وفقدان الحزم (بين عُقد ClickHouse، وبين ClickHouse وZooKeeper، وبين خادم ClickHouse والعميل، وما إلى ذلك)، وkill -9 وkill -STOP وkill -CONT، على غرار Jepsen. ثم يتحقق الاختبار من أن جميع عمليات الإدخال التي تم الإقرار بها قد كُتبت، وأن جميع عمليات الإدخال المرفوضة لم تُكتب.
الاختبار اليدوي
programs/clickhouse-server ثم شغّله باستخدام ./clickhouse-server. سيستخدم الإعدادات (config.xml وusers.xml والملفات الموجودة داخل الدليلين config.d وusers.d) من الدليل الحالي افتراضيًا. للاتصال بخادم ClickHouse، شغّل programs/clickhouse-client/clickhouse-client.
لاحظ أن جميع أدوات clickhouse (الخادم، والعميل، وما إلى ذلك) ليست سوى روابط رمزية لملف binary واحد باسم clickhouse.
يمكنك العثور على ملف binary هذا في programs/clickhouse.
يمكن أيضًا استدعاء جميع الأدوات بصيغة clickhouse tool بدلًا من clickhouse-tool.
بدلًا من ذلك، يمكنك تثبيت حزمة ClickHouse: إما إصدار stable من ClickHouse repository، أو بناء الحزمة بنفسك باستخدام ./release في جذر ClickHouse sources.
ثم ابدأ الخادم باستخدام sudo clickhouse start (أو stop لإيقاف الخادم).
ابحث عن logs في /etc/clickhouse-server/clickhouse-server.log.
عندما يكون ClickHouse مثبتًا بالفعل على نظامك، يمكنك بناء ملف binary جديد باسم clickhouse واستبدال ملف binary الحالي:
clickhouse-server تعمل بالفعل ولا تريد إيقافها، فيمكنك تغيير أرقام المنافذ في ملف config.xml (أو إعادة تعريفها في ملف داخل دليل config.d)، وتحديد مسار البيانات المناسب، ثم تشغيله.
يكاد الملف التنفيذي clickhouse لا يعتمد على أي تبعيات، ويعمل على مجموعة واسعة من توزيعات Linux.
ولإجراء اختبار سريع ومبدئي لتغييراتك على خادم، يمكنك ببساطة استخدام scp لنسخ الملف التنفيذي clickhouse الذي بنيته حديثًا إلى خادمك، ثم تشغيله كما في الأمثلة أعلاه.
اختبارات البناء
- الترجمة العابرة إلى Darwin x86_64 (macOS)
- الترجمة العابرة إلى FreeBSD x86_64
- الترجمة العابرة إلى Linux AArch64
- البناء على Ubuntu باستخدام مكتبات من حزم النظام (غير مستحسن)
- البناء مع الربط الديناميكي للمكتبات (غير مستحسن)
اختبار توافق البروتوكول
clickhouse-client القديم يعمل مع clickhouse-server الجديد، وأن clickhouse-client الجديد يعمل مع clickhouse-server القديم (وذلك ببساطة عبر تشغيل الملفات التنفيذية من الحزم المقابلة).
كما نختبر بعض الحالات تلقائيًا باستخدام اختبارات التكامل:
- ما إذا كان يمكن للنسخة الجديدة قراءة البيانات التي كتبتها نسخة قديمة من ClickHouse بنجاح؛
- وما إذا كانت الاستعلامات الموزعة تعمل في عنقود يضم إصدارات مختلفة من ClickHouse.
مساعدة من المترجم البرمجي
src) باستخدام -Wall -Wextra -Werror، إلى جانب بعض التحذيرات الإضافية المفعّلة.
لكن هذه الخيارات ليست مفعّلة لمكتبات الطرف الثالث.
يوفّر Clang تحذيرات مفيدة أكثر — يمكنك استعراضها باستخدام -Weverything واختيار ما يلائم البناء الافتراضي.
نحن نستخدم دائمًا clang لبناء ClickHouse، سواء للتطوير أو للإنتاج.
يمكنك إجراء البناء على جهازك باستخدام وضع debug (للحفاظ على بطارية حاسوبك المحمول)، لكن يُرجى ملاحظة أن المترجم البرمجي يمكنه توليد المزيد من التحذيرات مع -O3 بفضل تحسين تحليل تدفّق التحكم والتحليل بين الإجراءات.
وعند البناء باستخدام clang في وضع debug، يُستخدم إصدار debug من libc++، ما يتيح اكتشاف المزيد من الأخطاء في وقت التشغيل.
أدوات اكتشاف الأخطاء
إذا تعطلت العملية (خادم ClickHouse أو العميل) عند بدء التشغيل أثناء تشغيلها محليًا، فقد تحتاج إلى تعطيل العشوائية في تخطيط مساحة العناوين:
sudo sysctl kernel.randomize_va_space=0AddressSanitizer
Thread sanitizer
أداة فحص الذاكرة
أداة كشف السلوك غير المعرّف
Valgrind (memcheck)
re2، راجع هذه المقالة.
التشويش
src/Parsers/fuzzers/lexer_fuzzer.cpp.
تُخزَّن إعدادات LibFuzzer الخاصة وDictionaries وcorpus في tests/fuzz.
ونشجّعك على كتابة اختبارات Fuzz لكل وظيفة تتعامل مع مدخلات المستخدم.
لا يتم بناء Fuzzers افتراضيًا.
ولبناء Fuzzers، يجب ضبط الخيارين -DENABLE_FUZZING=1 و-DENABLE_TESTS=1.
ونوصي بتعطيل Jemalloc أثناء بناء Fuzzers.
يمكن العثور على configuration المستخدمة لدمج التشويش في ClickHouse مع
Google OSS-Fuzz في docker/fuzz.
ونستخدم أيضًا اختبار Fuzz بسيطًا لتوليد استعلامات SQL عشوائية والتحقق من أن server لا يتعطل أثناء تنفيذها.
يمكنك العثور عليه في 00746_sql_fuzzy.pl.
ويجب تشغيل هذا الاختبار باستمرار (طوال الليل ولمدد أطول).
ونستخدم أيضًا Query Fuzzer متقدمًا قائمًا على AST، وقادرًا على اكتشاف عدد هائل من الحالات الطرفية.
فهو يُجري تبديلات وتبديلات substitution عشوائية في AST الخاصة بالاستعلامات.
كما يتذكر عُقد AST من الاختبارات السابقة لاستخدامها في التشويش للاختبارات اللاحقة، مع معالجتها بترتيب عشوائي.
يمكنك معرفة المزيد عن هذا fuzzer في هذه المقالة على المدونة.
اختبار الإجهاد
- ألّا يتعطل الخادم، وألّا يتم تفعيل أي مصائد Debug أو sanitizer؛
- عدم وجود حالات توقف متبادل؛
- أن تكون بنية قاعدة البيانات متسقة؛
- أن يتمكن الخادم من التوقف بنجاح بعد الاختبار ثم البدء مجددًا دون استثناءات.
Thread fuzzer
تدقيق أمني
أدوات التحليل الساكن
clang-tidy مع كل commit.
كما أن فحوصات clang-static-analyzer مفعّلة أيضًا.
ويُستخدم clang-tidy كذلك لبعض فحوصات الأسلوب.
لقد قيّمنا clang-tidy وCoverity وcppcheck وPVS-Studio وtscancode وCodeQL.
ستجد تعليمات الاستخدام في الدليل tests/instructions/.
إذا كنت تستخدم CLion بوصفه بيئة تطوير متكاملة، فيمكنك الاستفادة مباشرةً من بعض فحوصات clang-tidy الجاهزة.
كما نستخدم shellcheck للتحليل الساكن لبرامج shell النصية.
التحصين
فحوصات سلامة البيانات أثناء التشغيل
- وهو ليس بطيئًا.
نمط الشيفرة
utils/check-style.
لفرض نمط الشيفرة الصحيح، يمكنك استخدام clang-format.
يوجد الملف .clang-format في جذر الشيفرة المصدرية.
وهو يتوافق في الغالب مع نمط الشيفرة المعتمد لدينا.
لكن لا يُنصح بتطبيق clang-format على الملفات الموجودة مسبقًا لأنه قد يجعل التنسيق أسوأ.
يمكنك استخدام الأداة clang-format-diff التي يمكنك العثور عليها في مستودع clang المصدري.
بدلًا من ذلك، يمكنك تجربة الأداة uncrustify لإعادة تنسيق الشيفرة.
يوجد ملف الإعدادات uncrustify.cfg في جذر الشيفرة المصدرية.
وهي أقل اختبارًا من clang-format.
يمتلك CLion منسّق شيفرة خاصًا به، ويجب ضبطه ليتوافق مع نمط الشيفرة لدينا.