بدء استخدام اختبارات Game Loop

قد يكون من الصعب تنفيذ اختبارات الألعاب آليًا عندما يتم إنشاء تطبيقات الألعاب باستخدام أُطر واجهة مستخدم مختلفة. تتيح لك اختبارات حلقة الألعاب دمج اختباراتك الأصلية مع 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. ومع ذلك، لا ننصح بإجراء اختبارات "حلقة الألعاب" على الأجهزة الافتراضية لأنّ معدّلات عرض الرسومات فيها أقل من الأجهزة الفعلية.

قبل البدء

لتنفيذ اختبار، عليك أولاً إعداد تطبيقك لاختبارات "حلقة الألعاب".

  1. في بيان تطبيقك، أضِف 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 تشغيل لعبتك من خلال تفعيلها بنية محدّدة.

  2. في الرمز البرمجي (ننصح بإضافته داخل تعريف طريقة 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
    }

    يسمح ذلك لنشاطك بالتحقّق من الغرض الذي يتم تشغيله من خلاله. يمكنك أيضًا إضافة هذا الرمز لاحقًا إذا كنت تفضّل ذلك (مثلاً، بعد تحميل محرك الألعاب في البداية).

  3. ننصحك بإضافة ما يلي في نهاية الاختبار:

    Kotlin

    yourActivity.finish()

    Java

    yourActivity.finish();

    يؤدي ذلك إلى إغلاق تطبيقك عند اكتمال اختبار "حلقة الألعاب". يعتمد الاختبار على إطار عمل واجهة المستخدم لتطبيقك من أجل بدء التكرار التالي، ويؤدي إغلاق تطبيقك إلى إعلام إطار العمل بأنّ الاختبار قد انتهى.

إنشاء اختبار "حلقة الألعاب" وتشغيله

بعد إعداد تطبيقك لإجراء اختبارات "حلقة الألعاب"، يمكنك إنشاء اختبار وتشغيله على الفور في لعبة على الأجهزة الجوّالة. ويمكنك اختيار إجراء اختبار في Test Lab باستخدام وحدة تحكّم Firebase أو واجهة سطر الأوامر (CLI) في gcloud، أو على جهاز محلي باستخدام أداة اختبار مدير الحلقة.

التشغيل على جهاز محلي

Test LabTest Loop Manager هو تطبيق مفتوح المصدر يساعدك في دمج اختبارات "حلقة الألعاب" وتشغيلها على أجهزتك المحلية. كما تتيح لفريق ضمان الجودة تشغيل حلقات اللعبة نفسها على أجهزتهم.

لتشغيل اختبار على جهاز محلي باستخدام "أداة إدارة حلقات الاختبار"، اتّبِع الخطوات التالية:

  1. نزِّل Test Loop Manager على هاتف أو جهاز لوحي وثبِّته من خلال تنفيذ ما يلي:
    adb install testloopmanager.apk
  2. على جهازك، افتح تطبيق تطبيقات Test Loop على هاتفك أو جهازك اللوحي. يعرض التطبيق قائمة بالتطبيقات على جهازك التي يمكن تشغيلها باستخدام حلقات الألعاب. إذا لم يظهر تطبيق الألعاب هنا، تأكَّد من أنّ فلتر الغرض يتطابق مع الفلتر الموضّح في الخطوة الأولى من قسم "قبل البدء".
  3. اختَر لعبة على الأجهزة الجوّالة، ثم اختَر عدد مرات التكرار التي تريد تنفيذها. ملاحظة: في هذه الخطوة، يمكنك اختيار تشغيل مجموعة فرعية من الحلقات بدلاً من حلقة واحدة فقط. لمزيد من المعلومات حول تشغيل عدة حلقات في الوقت نفسه، يُرجى الاطّلاع على الميزات الاختيارية.
  4. انقر على إجراء اختبار. يبدأ اختبارك على الفور.

التشغيل في Test Lab

يمكنك إجراء اختبار "حلقة الألعاب" في Test Lab باستخدام وحدة تحكّم Firebase أو gcloud CLI. قبل البدء، افتح وحدة تحكّم Firebase وأنشئ مشروعًا إذا لم يسبق لك ذلك.

استخدام وحدة تحكّم Firebase

  1. في وحدة تحكّم Firebase، انقر على Test Lab من اللوحة اليمنى.
  2. انقر على إجراء اختبارك الأول (أو إجراء اختبار إذا سبق أن أجرى مشروعك اختبارًا).
  3. اختَر حلقة الألعاب كنوع الاختبار، ثمّ انقر على متابعة.
  4. انقر على استعراض، ثم استعرض ملف .apk لتطبيقك. ملاحظة: في هذه الخطوة، يمكنك اختيار تشغيل مجموعة فرعية من الحلقات بدلاً من حلقة واحدة فقط. لمزيد من المعلومات حول تشغيل عدة مقاطع متكررة في الوقت نفسه، يُرجى الاطّلاع على الميزات الاختيارية.
  5. انقر على متابعة.
  6. اختَر الأجهزة الفعلية التي تريد استخدامها لاختبار تطبيقك.
  7. انقر على بدء الاختبارات.

لمزيد من المعلومات حول كيفية بدء استخدام وحدة تحكّم Firebase، يُرجى الاطّلاع على بدء الاختبار باستخدام وحدة تحكّم Firebase.

استخدام سطر أوامر gcloud

  1. إذا لم يسبق لك ذلك، نزِّل Google Cloud SDK وثبِّتها.

  2. سجِّل الدخول إلى gcloud CLI باستخدام حساب Google:

    gcloud auth login

  3. اضبط مشروعك على Firebase في gcloud، حيث يمثّل PROJECT_ID رقم تعريف مشروعك على Firebase:

    gcloud config set project PROJECT_ID
    
  4. إجراء الاختبار الأول:

    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، يمكنك تشغيل حلقة اللعبة هذه مباشرةً لإعادة إنتاج الخطأ واختبار إصلاحات الأخطاء.

لتفعيل إمكانية تشغيل حلقات متعددة في تطبيقك في الوقت نفسه، اتّبِع الخطوات التالية:

  • إذا كنت تجري اختبارًا باستخدام "أداة اختبار الحلقات":

    1. أضِف السطر التالي إلى بيان تطبيقك، داخل العنصر <application>:

      <meta-data
        android:name="com.google.test.loops"
        android:value="5" />

      يتضمّن هدف التشغيل هذا حلقة الاستهداف كمَعلمة عدد صحيح. في حقل android:value، يمكنك تحديد عدد صحيح من 1 إلى 1024 (الحد الأقصى لعدد التكرارات المسموح به لاختبار واحد). يُرجى العِلم أنّ الفهارس تبدأ من 1، وليس 0.

    2. في تطبيق &quot;أداة إدارة حلقات الاختبار&quot;، ستظهر شاشة اختيار تتيح لك تحديد الحلقات التي تريد تشغيلها. إذا اخترت حلقات متعدّدة، يتم تشغيل كل حلقة بالتسلسل بعد اكتمال الحلقة السابقة.

  • إذا كنت تجري اختبارًا باستخدام وحدة تحكّم 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: حلقات التكرار المستخدَمة لمحاكاة تجربة المستخدم الحقيقي عند تشغيل اللعبة والهدف من الاختبار باستخدام هذه الحلقات هو رصد المشاكل التي قد يواجهها المستخدم أثناء لعب اللعبة.
  • com.google.test.loops.gpu_compatibility: حلقات التكرار المستخدَمة لاختبار المشاكل المتعلّقة بوحدة معالجة الرسومات والهدف من الاختبار باستخدام هذه الحلقات هو تنفيذ رمز وحدة معالجة الرسومات الذي قد لا يعمل بشكل صحيح في الإصدار العلني، وذلك للكشف عن المشاكل المتعلقة بالأجهزة وبرامج التشغيل.
  • com.google.test.loops.compatibility: حلقات for المستخدَمة لاختبار مجموعة كبيرة من مشاكل التوافق، بما في ذلك مشاكل الإدخال/الإخراج ومشاكل OpenSSL
  • com.google.test.loops.performance: حلقات التكرار المستخدَمة لاختبار أداء الجهاز على سبيل المثال، قد يتم تشغيل لعبة بأكثر إعدادات الرسومات تعقيدًا لمعرفة طريقة عمل جهاز جديد.

لتفعيل إمكانية تشغيل الحلقات التي تحمل التصنيف نفسه في تطبيقك، اتّبِع الخطوات التالية:

  • إذا كنت تجري اختبارًا باستخدام "مدير حلقة الاختبار"، اتّبِع الخطوات التالية:

    1. في بيان التطبيق، أضِف سطر البيانات الوصفية التالي واستبدِل 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.

    2. في تطبيق "أداة إدارة حلقات الاختبار"، أدخِل تصنيفًا واحدًا أو أكثر في حقل التصنيفات.

  • إذا كنت تجري اختبارًا باستخدام وحدة تحكّم Firebase، أدخِل تصنيفًا واحدًا أو أكثر في حقل التصنيفات.

  • إذا كنت تجري اختبارًا باستخدام gcloud CLI، حدِّد تصنيفًا واحدًا أو أكثر للسيناريو باستخدام العلامة --scenario-labels (مثلاً، --scenario-labels=performance,gpu).

دعم منح تراخيص التطبيقات

يتوافق Test Lab مع التطبيقات التي تستخدم خدمة ترخيص التطبيقات التي يقدّمها Google Play. للتأكّد من نجاح عملية التحقّق من الترخيص عند اختبار تطبيقك باستخدام Test Lab، يجب نشر تطبيقك في قناة الإصدار العلني على &quot;متجر Play&quot;. لاختبار تطبيقك في قناة الإصدار الأولي أو الإصدار التجريبي باستخدام Test Lab، عليك إزالة عملية التحقّق من الترخيص قبل تحميل تطبيقك إلى Test Lab.

المشكلات المعروفة

تتضمّن اختبارات حلقة الألعاب في Test Lab المشاكل المعروفة التالية:

  • لا تتوافق بعض الأعطال مع عمليات تتبُّع تسلسل استدعاء الدوال البرمجية. على سبيل المثال، قد تحظر بعض إصدارات التطبيق إخراج عملية debuggerd باستخدام prctl(PR_SET_DUMPABLE, 0). لمزيد من المعلومات، يُرجى الاطّلاع على debuggerd.
  • لا يتوافق المستوى 19 من واجهة برمجة التطبيقات حاليًا مع التطبيق بسبب أخطاء في أذونات الملفات.