قد يكون من الصعب إجراء اختبارات تلقائية للألعاب عندما يتم إنشاء تطبيقات الألعاب على أُطر عمل مختلفة لواجهة المستخدم. تتيح لك اختبارات "حلقة الألعاب" دمج اختباراتك الأصلية مع Test Lab وتشغيلها بسهولة على الأجهزة التي تختارها. يُجري اختبار "حلقة الألعاب" اختبارك من خلال لعبة على الأجهزة الجوّالة مع محاكاة إجراءات لاعب حقيقي. يوضّح لك هذا الدليل كيفية إجراء اختبار حلقة الألعاب، ثم عرض نتائج الاختبار وإدارتها في وحدة تحكّم Firebase.
ما هو اختبار حلقة الألعاب؟
بما أنّ الألعاب على الأجهزة الجوّالة يتم عرضها على مستوى أُطر عمل ومحركات مختلفة لواجهة المستخدم، مثل Unity أو Unreal أو رمز OpenGL/Vulkan C++، لا يمكن لأدوات التشغيل الآلي العادية فحص عناصر في واجهة المستخدم أو التفاعل معها. يتيح لك اختبار حلقة الألعاب دمج اختبارات المحرّك الأصلي مع Test Lab من خلال تشغيل لعبتك باستخدام نية تحتوي على رقم سيناريو محدّد. يطلب رقم السيناريو هذا من تطبيقك تشغيل منطق مُعدّ مسبقًا أو محاكاة الذكاء الاصطناعي أو عمليات التحقّق من الأداء مباشرةً داخل محرّك اللعبة.
بعد تشغيل Test Lab، لا يتفاعل مع واجهة مستخدم لعبتك. بدلاً من ذلك، يعمل Test Lab كبيئة اختبار ونقل: فهو يطلق السيناريو المحدد، ويراقب الأعطال والمهلات، ويجمع أي سجلات نتائج مخصصة عندما ينتهي تطبيقك من تشغيل الحلقة.
أوجه الاختلاف بين حلقات الألعاب واختبارات قياس الأداء
- تعتمد اختبارات قياس حالة التطبيق (مثل Espresso أو UI Automator) على أُطر عمل خارجية لتوجيه تفاعلات واجهة المستخدم والنقر على الأزرار وتأكيد حالة التطبيق في هيكلية طرق العرض في Android من خارج تطبيقك.
- تفوِّض اختبارات حلقة الألعاب منطق الاختبار إلى تطبيقك. يشغِّل تطبيقك حلقة الاختبار الداخلي أو المحاكاة الخاصة به بدون تفاعل خارجي مع واجهة المستخدم، بينما يشغِّل Test Lab السيناريو الخاص بك ويجمع نتائج الاختبار.
أمثلة
لنفترض أنّك تريد التأكّد من أنّ لعبتك تحافظ على معدّل 60 لقطة في الثانية على الأقل على أجهزة فعلية مختلفة. يمكنك تنفيذ السيناريو 1 داخل
رمز اللعبة لتحميل المستوى الأكثر تعقيدًا من الناحية المرئية والسماح للاعب مستند إلى الذكاء الاصطناعي أو
نص برمجي آلي باللعب لمدة دقيقتين.
عندما يشغّل Test Lab السيناريو 1، تبدأ لعبتك في تشغيل الذكاء الاصطناعي، وتراقب الوقت المستغرَق لعرض اللقطات واستخدام الذاكرة، وتُرسل هذه المقاييس إلى ملف سجلّ مخرجات مخصّص (logFile)، وتستدعي finish() عند اكتمال العملية.
بعد ذلك، يلتقط Test Lab ملف السجلّ ويوفّره لك في Cloud Storage ووحدة تحكّم Firebase.
حالات الاستخدام
بناءً على محرّك اللعبة، يمكنك تنفيذ الاختبارات باستخدام حلقة واحدة أو عدة حلقات. الحلقة هي تشغيل كامل أو جزئي للاختبار على لعبتك على الأجهزة الجوّالة. ويمكن استخدام حلقات الألعاب لإجراء ما يلي:
- شغِّل مستوى من لعبتك بالطريقة نفسها التي يلعبها المستخدم النهائي. يمكنك إما كتابة نص برمجي لإدخال المستخدم، أو ترك المستخدم في وضع عدم النشاط، أو استبدال المستخدم بذكاء اصطناعي إذا كان ذلك منطقيًا في لعبتك (على سبيل المثال، إذا كان لديك لعبة على الأجهزة الجوّالة لسيارات السباق ولديك بالفعل ذكاء اصطناعي مطبّق، يمكنك وضع سائق ذكاء اصطناعي مسؤول عن إدخال المستخدم).
- شغِّل لعبتك بأعلى إعدادات الجودة لمعرفة ما إذا كانت الأجهزة متوافقة معها.
- إجراء اختبار فني (تجميع عدة برامج تظليل وتنفيذها والتحقّق من أنّ النتيجة كما هو متوقّع، وما إلى ذلك)
يمكنك إجراء اختبار "حلقة الألعاب" على جهاز اختباري واحد أو مجموعة من الأجهزة الاختبارية أو على Test Lab. ومع ذلك، لا ننصح بإجراء اختبارات "حلقة الألعاب" على الأجهزة الافتراضية لأنّ عدد اللقطات في الثانية للرسومات بها أقل من الأجهزة الفعلية.
قبل البدء
لتنفيذ اختبار، عليك أولاً ضبط تطبيقك لإجراء اختبارات "حلقة الألعاب".
في بيان تطبيقك، أضِف intent filter جديدًا إلى نشاطك:
<activity android:name=".MyActivity"> <intent-filter> <action android:name="com.google.intent.action.TEST_LOOP"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="application/javascript"/> </intent-filter> <intent-filter> ... (other intent filters here) </intent-filter> </activity>يتيح ذلك لـ "Test Lab" تشغيل لعبتك من خلال تفعيلها باستخدام نية محدّدة.
في الرمز البرمجي (ننصحك بإضافة ما يلي داخل تعريف طريقة
onCreate):Kotlin
val launchIntent = intent if (launchIntent.action == "com.google.intent.action.TEST_LOOP") { val scenario = launchIntent.getIntExtra("scenario", 0) // Code to handle your game loop here }
Java
Intent launchIntent = getIntent(); if(launchIntent.getAction().equals("com.google.intent.action.TEST_LOOP")) { int scenario = launchIntent.getIntExtra("scenario", 0); // Code to handle your game loop here }
يتيح ذلك لنشاطك التحقّق من الغرض الذي يطلقه. يمكنك أيضًا إضافة هذا الرمز لاحقًا إذا أردت (على سبيل المثال، بعد تحميل محرّك اللعبة في البداية).
إجراء مقترَح: في نهاية الاختبار، أضِف ما يلي:
Kotlin
yourActivity.finish()
Java
yourActivity.finish();
يؤدي ذلك إلى إغلاق تطبيقك عند اكتمال اختبار "حلقة الألعاب". يعتمد الاختبار على إطار عمل واجهة المستخدم في تطبيقك لبدء التكرار التالي، ويؤدي إغلاق تطبيقك إلى إعلامه بأنّ الاختبار قد انتهى.
إنشاء اختبار حلقة الألعاب وتنفيذه
بعد ضبط تطبيقك لإجراء اختبارات "حلقة الألعاب"، يمكنك إنشاء اختبار وتشغيله على الفور في لعبة على الأجهزة الجوّالة. يمكنك اختيار إجراء اختبار في Test Lab باستخدام وحدة تحكّم Firebase أو واجهة سطر أوامر gcloud، أو على جهاز محلي باستخدام "اختبار مدير الحلقة".
التشغيل على جهاز محلي
اختبار مدير الحلقة من Test Lab هو تطبيق مفتوح المصدر يساعدك في دمج اختبارات "حلقة الألعاب" وتشغيلها على أجهزتك المحلية. كما يسمح لفريق ضمان الجودة بتشغيل حلقات الألعاب نفسها على أجهزتهم.
لإجراء اختبار على جهاز محلي باستخدام تطبيق "اختبار مدير الحلقة"، اتّبِع الخطوات التالية:
- نزِّل تطبيق اختبار مدير الحلقة
على هاتف أو جهاز لوحي وثبِّته من خلال تشغيل:
adb install testloopmanager.apk
- على جهازك، افتح تطبيق تطبيقات حلقة الاختبار على هاتفك أو جهازك اللوحي. يعرض التطبيق قائمة بالتطبيقات على جهازك التي يمكن تشغيلها باستخدام حلقات الألعاب. إذا لم تظهر لك لعبتك على الأجهزة الجوّالة هنا، تأكَّد من أنّ فلتر الأهداف يتطابق مع الفلتر الموضّح في الخطوة الأولى من قسم "قبل البدء".
- اختَر لعبتك على الأجهزة الجوّالة، ثم اختَر عدد التكرارات التي تريد تشغيلها. ملاحظة: في هذه الخطوة، يمكنك اختيار تشغيل مجموعة فرعية من الحلقات بدلاً من حلقة واحدة فقط. لمزيد من المعلومات حول تشغيل عدة حلقات في آنٍ واحد، يُرجى الاطّلاع على الميزات الاختيارية.
- انقر على إجراء اختبار. يبدأ الاختبار على الفور.
التشغيل في Test Lab
يمكنك إجراء اختبار حلقة الألعاب في "Test Lab" باستخدام وحدة تحكّم Firebase أو gcloud CLI. قبل البدء، افتح وحدة تحكّم Firebase وأنشئ مشروعًا، إذا لم يسبق لك ذلك.
استخدام وحدة تحكّم "Firebase"
- في وحدة تحكّم Firebase، انقر على Test Lab من اللوحة اليمنى.
- انقر على إجراء الاختبار الأول (أو إجراء اختبار إذا سبق لمشروعك إجراء اختبار).
- اختَر حلقة الألعاب كنوع الاختبار، ثم انقر على متابعة.
- انقر على تصفّح، ثم تصفَّح للوصول إلى ملف
.apkالخاص بتطبيقك. ملاحظة: في هذه الخطوة، يمكنك اختيار تشغيل مجموعة فرعية من الحلقات بدلاً من حلقة واحدة فقط. لمزيد من المعلومات حول تشغيل عدة حلقات في آنٍ واحد، يُرجى الاطّلاع على الميزات الاختيارية. - انقر على متابعة.
- اختَر الأجهزة الفعلية التي تريد استخدامها لاختبار تطبيقك.
- انقر على بدء الاختبارات.
لمزيد من المعلومات حول بدء استخدام وحدة تحكّم Firebase، يُرجى الاطّلاع على بدء الاختبار باستخدام وحدة تحكّم Firebase.
استخدام سطر أوامر gcloud (CLI)
نزِّل حزمة تطوير البرامج (SDK) في Google Cloud وثبِّتها إذا لم يسبق لك ذلك.
سجِّل الدخول إلى gcloud CLI باستخدام حساب Google:
gcloud auth loginاضبط مشروع Firebase في gcloud، حيث
PROJECT_IDهو رقم تعريف مشروع Firebase:gcloud config set project PROJECT_ID
إجراء الاختبار الأول:
gcloud firebase test android run \ --type=game-loop --app=<var>path-to-apk</var> \ --device model=herolte,version=23
لمزيد من المعلومات حول بدء استخدام gcloud CLI، يُرجى الاطّلاع على مقالة بدء الاختبار من سطر أوامر gcloud.
الميزات الاختيارية
يوفّر Test Lab العديد من الميزات الاختيارية التي تتيح لك تخصيص اختباراتك بشكل أكبر، بما في ذلك إمكانية كتابة بيانات الإخراج، وإتاحة حلقات ألعاب متعددة، وتصنيفات للحلقات ذات الصلة.
كتابة بيانات الناتج
عندما يشغّل Test Lab لعبتك، يحدّد عنوان URL يشير إلى ملف نتائج محلي على جهاز الاختبار، ويمكن الوصول إليه باستخدام launchIntent.getData(). يمكن للّعبة كتابة نتائج الاختبار ومقاييس الأداء والسجلّات (logFile) مباشرةً في هذا الموقع أثناء تشغيل الاختبار.
يمكنك كتابة أي محتوى عشوائي في logFile، مثل النص العادي أو بيانات CSV أو الوقت المستغرَق لعرض الإطارات أو التقارير التشخيصية المخصّصة. لا توجد
قيود أو متطلبات تنسيق للمحتوى الذي تكتبه؛ حيث يلتقط
الملف عند انتهاء الاختبار ويحمّله إلى حزمة Cloud Storage
إلى جانب عناصر الاختبار الأخرى.
يتّبع Test Lab أفضل الممارسات المعتمَدة لمشاركة ملف بين التطبيقات
الموضّحة في مقالة مشاركة ملف.
في إجراء onCreate() الخاص بالنشاط، حيث يقع الغرض، يمكنك استرداد ملف إخراج البيانات والتحقّق منه من خلال تشغيل الرمز البرمجي التالي:
Kotlin
val launchIntent = intent val logFile = launchIntent.data logFile?.let { Log.i(TAG, "Log file ${it.encodedPath}") // ... }
Java
Intent launchIntent = getIntent(); Uri logFile = launchIntent.getData(); if (logFile != null) { Log.i(TAG, "Log file " + logFile.getEncodedPath()); // ... }
إذا أردت الكتابة في الملف من جهة C++ في تطبيق اللعبة، يمكنك تمرير واصِف الملف بدلاً من مسار الملف:
Kotlin
val launchIntent = intent val logFile = launchIntent.data var fd = -1 logFile?.let { Log.i(TAG, "Log file ${it.encodedPath}") fd = try { contentResolver .openAssetFileDescriptor(logFile, "w")!! .parcelFileDescriptor .fd } catch (e: FileNotFoundException) { e.printStackTrace() -1 } catch (e: NullPointerException) { e.printStackTrace() -1 } } // C++ code invoked here. // native_function(fd);
Java
Intent launchIntent = getIntent(); Uri logFile = launchIntent.getData(); int fd = -1; if (logFile != null) { Log.i(TAG, "Log file " + logFile.getEncodedPath()); try { fd = getContentResolver() .openAssetFileDescriptor(logFile, "w") .getParcelFileDescriptor() .getFd(); } catch (FileNotFoundException e) { e.printStackTrace(); fd = -1; } catch (NullPointerException e) { e.printStackTrace(); fd = -1; } } // C++ code invoked here. // native_function(fd);
C++
#include <unistd.h> JNIEXPORT void JNICALL Java_my_package_name_MyActivity_native_function(JNIEnv *env, jclass type, jint log_file_descriptor) { // The file descriptor needs to be duplicated. int my_file_descriptor = dup(log_file_descriptor); }
مثال على ملف الإخراج
على الرغم من أنّ Test Lab لا يتطلّب تنسيقًا محدّدًا لـ logFile،
فإنّ تنظيم الناتج بتنسيق JSON يتطابق مع المخطط أدناه يسمح
لوحدة تحكّم Firebase (https://console.firebase.google.com/project/_/testlab) بتحليل وعرض مقاييس
الملخّص لاختبار حلقة الألعاب.
تفاصيل أساسية حول هذا المثال:
- مقدِّم البيانات: أنت (مطوِّر الاختبار) مسؤول عن احتساب جميع المقاييس وكتابتها في هذا الملف من رمز لعبتك. لا يجمع Test Lab نقاط البيانات هذه أو ينشئها لك تلقائيًا.
- إضافة حقول مخصّصة: تشير المناطق التي تحمل العلامة
/.../إلى الأماكن التي يمكنك فيها إضافة أزواج عشوائية من المفاتيح والقيم المخصّصة (على سبيل المثال،"rendered_vertices": 12000أو"ai_error_count": 0). يمكنك إضافة أي سمة JSON صالحة إلى الناتج عند كتابة الملف من تطبيقك، طالما أنّ أسماء الحقول لا تتعارض مع حقول المخطط مثلnameأوstart_timestamp. - مكان العثور على ملف الناتج: بعد اكتمال الاختبار، يتم تخزين ملف الناتج الأوّلي
ويمكن تنزيله من حزمة نتائج Cloud Storage (
GCS) الخاصة بمشروعك داخل دليل عناصر عملية الاختبار. يمكنك أيضًا عرض الناتج الذي تم تحليله وفحصه في صفحة Test Lab ضمن وحدة تحكّم Firebase.
في ما يلي مثال على ملف مخرجات JSON منظَّم:
{
"name": "test name",
"start_timestamp": 0, // Timestamp of the test start (in us).
Can be absolute or relative
"driver_info": "...",
"frame_stats": [
{
"timestamp": 1200000, // Timestamp at which this section was written
It contains value regarding the period
start_timestamp(0) -> this timestamp (1200000 us)
"avg_frame_time": 15320, // Average time to render a frame in ns
"nb_swap": 52, // Number of frame rendered
"threads": [
{
"name": "physics",
"Avg_time": 8030 // Average time spent in this thread per frame in us
},
{
"name": "AI",
"Avg_time": 2030 // Average time spent in this thread per frame in us
}
],
/.../ // Any custom field you want (vertices display on the screen, nb units …)
},
{
// Next frame data here, same format as above
}
],
"loading_stats": [
{
"name": "assets_level_1",
"total_time": 7850, // in us
/.../
},
{
"name": "victory_screen",
"total_time": 554, // in us
/.../
}
],
/.../, // You can add custom fields here
}
حلقات ألعاب متعددة
قد يكون من المفيد تشغيل عدة حلقات ألعاب في تطبيقك. الحلقة هي تشغيل كامل لتطبيق اللعبة من البداية إلى النهاية. على سبيل المثال، إذا كانت لعبتك تتضمّن مستويات متعددة، قد تحتاج إلى حلقة ألعاب واحدة لتشغيل كل مستوى بدلاً من حلقة واحدة تتكرر في جميع المستويات. بهذه الطريقة، إذا تعطّل تطبيقك في المستوى 32، يمكنك تشغيل حلقة الألعاب هذه مباشرةً لإعادة إنشاء العطل واختبار إصلاحات الأخطاء.
لتمكين تطبيقك من تشغيل عدة تكرارات حلقية في آنٍ واحد:
إذا كنت تُجري اختبارًا باستخدام تطبيق Test Loop Manager:
أضِف السطر التالي إلى بيان تطبيقك داخل العنصر
<application>:<meta-data android:name="com.google.test.loops" android:value="5" />
يحتوي هدف التشغيل هذا على التكرار الحلقي المستهدَف كمَعلمة عدد صحيح. في حقل
android:value، يمكنك تحديد عدد صحيح من 1 إلى 1024 (الحد الأقصى لعدد التكرارات المسموح بها لاختبار واحد). يُرجى العِلم أنّ الحلقات تتم فهرستها بدءًا من 1 وليس 0.في تطبيق "اختبار مدير الحلقة"، تظهر شاشة اختيار تتيح لك تحديد الحلقات التي تريد تشغيلها. إذا اخترت تكرارات متعددة، يتم تشغيل كل تكرار بالتسلسل بعد اكتمال التكرار السابق.
إذا كنت تجري اختبارًا باستخدام وحدة تحكّم Firebase، أدخِل قائمة أو نطاقًا من أرقام التكرار في حقل السيناريوهات.
إذا كنت تجري اختبارًا باستخدام gcloud CLI، حدِّد قائمة بأرقام التكرار الحلقي باستخدام العلامة
--scenario-numbers. على سبيل المثال، يُشغِّل--scenario-numbers=1,3,5التكرارات الحلقية 1 و3 و5.إذا كنت تكتب بلغة C++ وتريد تغيير سلوك التكرار، يمكنك تمرير البيانات الإضافية التالية إلى رمز C++ الأصلي:
Kotlin
val launchIntent = intent val scenario = launchIntent.getIntExtra("scenario", 0)
Java
Intent launchIntent = getIntent(); int scenario = launchIntent.getIntExtra("scenario", 0);
يمكنك الآن تغيير سلوك الحلقة بناءً على قيمة
intالناتجة.
تصنيف حلقات الألعاب
عند تصنيف حلقات الألعاب باستخدام تصنيف سيناريو واحد أو أكثر، يمكنك أنت وفريق تأكيد الجودة إطلاق مجموعة من حلقات الألعاب ذات الصلة بسهولة (مثل "جميع حلقات الألعاب المتوافقة") واختبارها في مصفوفة واحدة. يمكنك إنشاء تصنيفاتك الخاصة أو استخدام التصنيفات المحدّدة مسبقًا التي يوفّرها Test Lab:
-
com.google.test.loops.player_experience: يشير إلى حلقات "for" المستخدَمة لإعادة إنشاء تجربة مستخدم حقيقي أثناء تشغيل اللعبة. الهدف من الاختبار باستخدام هذه الحلقات هو العثور على المشكلات التي قد يواجهها مستخدم حقيقي أثناء لعب اللعبة. com.google.test.loops.gpu_compatibility: حلقات For تُستخدم لاختبار المشاكل المتعلّقة بوحدة معالجة الرسومات الهدف من الاختبار باستخدام هذه الحلقات هو تنفيذ رمز وحدة معالجة الرسومات الذي قد لا يعمل بشكل صحيح في مرحلة الإنتاج، وذلك للكشف عن المشاكل في الأجهزة وبرامج التشغيل.-
com.google.test.loops.compatibility: حلقات For تُستخدم لاختبار مجموعة واسعة من مشاكل التوافق، بما في ذلك مشاكل الإدخال/الإخراج ومشاكل OpenSSL. com.google.test.loops.performance: حلقات For تُستخدم لاختبار أداء الجهاز على سبيل المثال، قد يتم تشغيل لعبة بأكثر إعدادات الرسومات تعقيدًا لمعرفة كيفية أداء الجهاز الجديد.
لتفعيل تطبيقك لتشغيل تكرارات حلقية بالتصنيف نفسه:
إذا كنت تُجري اختبارًا باستخدام تطبيق Test Loop Manager:
في بيان تطبيقك، أضِف سطر البيانات الوصفية التالي واستبدِل LABEL_NAME بتصنيف من اختيارك:
<meta-data android:name="com.google.test.loops.LABEL_NAME" android:value="1,3-5" />
في حقل
android:value، يمكنك تحديد نطاق أو مجموعة من الأعداد الصحيحة من 1 إلى 1024 (الحد الأقصى لعدد التكرارات المسموح بها لاختبار واحد) والتي تمثل التكرارات التي تريد تصنيفها. يُرجى العِلم أنّ الحلقات تتم فهرستها بدءًا من 1 وليس 0. على سبيل المثال، يطبّقandroid:value="1,3-5"LABEL_NAME على التكرارات 1 و3 و4 و5.في تطبيق "اختبار مدير الحلقة"، أدخِل تصنيفًا واحدًا أو أكثر في حقل التصنيفات.
إذا كنت تجري اختبارًا باستخدام وحدة تحكّم Firebase، أدخِل تصنيفًا واحدًا أو أكثر في حقل التصنيفات.
إذا كنت تُجري اختبارًا باستخدام gcloud CLI، حدِّد تصنيف سيناريو واحدًا أو أكثر باستخدام العلامة
--scenario-labels(على سبيل المثال،--scenario-labels=performance,gpu).
دعم منح تراخيص التطبيقات
يتيح تطبيق "Test Lab" استخدام التطبيقات التي تستخدم خدمة منح تراخيص التطبيقات التي يوفّرها Google Play. للتحقّق من الترخيص بنجاح عند اختبار تطبيقك باستخدام Test Lab، يجب نشر تطبيقك في قناة الإصدار العلني في متجر Google Play. لاختبار تطبيقك في القناة التجريبية أو قناة الإصدار الأولي باستخدام Test Lab، عليك إزالة عملية التحقّق من الترخيص قبل تحميل تطبيقك إلى Test Lab.
المشكلات المعروفة
تتضمّن اختبارات "حلقة الألعاب" في Test Lab المشاكل المعروفة التالية:
- لا تتيح بعض الأعطال تتبُّع الأخطاء. على سبيل المثال، قد تمنع بعض بُنى الإصدارات
إخراج عملية
debuggerdباستخدامprctl(PR_SET_DUMPABLE, 0). لمزيد من المعلومات، يُرجى الاطّلاع على المقالةdebuggerd. - لا يتوفّر حاليًا المستوى 19 من واجهة برمجة التطبيقات بسبب أخطاء في أذونات الملفات.