إجراء اختبار حلقة الألعاب

قد يكون من الصعب إجراء اختبار الألعاب تلقائيًا عندما تكون تطبيقات الألعاب تستند إلى أطر عمل مختلفة لواجهة المستخدم. تتيح لك اختبارات Game Loop دمج اختباراتك الأصلية مع Test Lab وتشغيلها بسهولة على الأجهزة التي تختارها. يوضّح هذا الدليل كيفية تحضير اختبار حلقة الألعاب لتشغيله باستخدام مركز الاختبار الافتراضي لمنصة Firebase.

لمحة عن اختبارات حلقة الألعاب

ما هو اختبار "حلقة الألعاب"؟

يحاكي اختبار حلقة الألعاب إجراءات لاعب حقيقي للتأكّد من أنّ لعبتك تحقّق أداءً جيدًا للمستخدمين بطريقة سريعة وقابلة للتوسّع. التكرار الحلقي هو إجراء اختبار كامل أو جزئي على تطبيق الألعاب. يمكنك إجراء اختبار Game Loop محليًا على جهاز محاكاة أو على مجموعة من الأجهزة في Test Lab. يمكن استخدام اختبارات Game Loop في ما يلي:

  • شغِّل لعبتك على النحو الذي يريده المستخدم النهائي. يمكنك إما كتابة نص يدخِله المستخدم، أو تركه في وضع عدم النشاط، أو استبدال المستخدم باستخدام الذكاء الاصطناعي (AI) (على سبيل المثال، إذا استخدمت الذكاء الاصطناعي في لعبة سباق السيارات، يمكنك توجيه سائق يعمل بالذكاء الاصطناعي إلى المعلومات التي يُدخلها المستخدم).
  • شغِّل لعبتك بأعلى جودة لمعرفة الأجهزة المتوافقة معه.
  • يمكنك إجراء اختبار فني، مثل جمع عدة أدوات تظليل وتنفيذها والتأكد من أنّ النتائج على النحو المتوقّع.

الخطوة 1: تسجيل مخطط عنوان URL المخصّص من Test Lab

  1. في Xcode، اختَر هدف مشروع.

  2. انقر على علامة التبويب المعلومات، ثم أضِف نوع عنوان URL جديد.

  3. في حقل مخططات عناوين URL، أدخل firebase-game-loop. يمكنك أيضًا تسجيل مخطط عنوان URL المخصّص من خلال إضافته إلى ملف إعداد Info.plist الخاص بمشروعك في أي مكان داخل العلامة <dict>:

    <key>CFBundleURLTypes</key>
     <array>
         <dict>
             <key>CFBundleURLName</key>
             <string></string>
             <key>CFBundleTypeRole</key>
             <string>Editor</string>
             <key>CFBundleURLSchemes</key>
             <array>
                 <string>firebase-game-loop</string>
             </array>
         </dict>
     </array>
    

تم إعداد تطبيقك الآن لإجراء اختبار باستخدام مركز الاختبار الافتراضي.

الخطوة 2: ضبط تطبيقك اختياريًا

تنفيذ عدة تكرارات

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

في صفحة التفويض في تطبيقك، يمكنك إلغاء طريقة application(_:open:options:):

Swift

func application(_app: UIApplication,
                 open url: URL
                 options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    let components = URLComponents(url: url, resolvingAgainstBaseURL: true)!
    if components.scheme == "firebase-game-loop" {
        // ...Enter Game Loop Test logic to override application(_:open:options:).
    }
    return true
}

Objective-C

- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary &lt;UIApplicationOpenURLOptionsKey, id&gt; *)options {
  if ([url.scheme isEqualToString:(@"firebase-game-loop")]) {
      // ...Enter Game Loop Test logic to override application(_:open:options:).
  }
}

عند تنفيذ تكرارات متعددة في الاختبار، يتم تمرير التكرار الحلقي الحالي كمعلمة لعنوان URL المستخدَم لتشغيل التطبيق. يمكنك أيضًا الحصول على رقم الحلقة الحالية عن طريق تحليل كائن URLComponents المستخدَم لجلب مخطط عنوان URL المخصّص:

Swift

if components.scheme == "firebase-game-loop" {
    // Iterate over all parameters and find the one with the key "scenario".
    let scenarioNum = Int(components.queryItems!.first(where: { $0.name == "scenario" })!.value!)!
    // ...Write logic specific to the current loop (scenarioNum).
}

Objective-C

if ([url.scheme isEqualToString:(@"firebase-game-loop")]) {
    // Launch the app as part of a game loop.
    NSURLComponents *components = [NSURLComponents componentsWithURL:url
                                             resolvingAgainstBaseURL:YES];
    for (NSURLQueryItem *item in [components queryItems]) {
        if ([item.name isEqualToString:@"scenario"]) {
            NSInteger scenarioNum = [item.value integerValue];
            // ...Write logic specific to the current loop (scenarioNum).
        }
    }
}

إنهاء الاختبار مبكرًا

بشكل تلقائي، يستمر تشغيل اختبار حلقة الألعاب إلى أن تصل إلى مهلة خمس دقائق، حتى بعد تنفيذ جميع التكرارات. عند انقضاء المهلة، ينتهي الاختبار ويلغي أي تكرارات معلّقة. يمكنك تسريع الاختبار أو إنهاؤه مبكرًا من خلال طلب مخطط عنوان URL المخصّص من Test Lab firebase-game-loop-complete في AppDelegate في تطبيقك. على سبيل المثال:

Swift

/// End the loop by calling our custom url scheme.
func finishLoop() {
    let url = URL(string: "firebase-game-loop-complete://")!
    UIApplication.shared.open(url)
}

Objective-C

- (void)finishLoop {
  UIApplication *app = [UIApplication sharedApplication];
  [app openURL:[NSURL URLWithString:@"firebase-game-loop-complete://"]
      options:@{}
completionHandler:^(BOOL success) {}];
}

ينهي اختبار "حلقة اللعبة" التكرار الحالي وينفّذ التكرار الحلقي التالي. عندما لا يكون هناك المزيد من التكرارات الحلقية لتشغيلها، ينتهي الاختبار.

كتابة نتائج اختبار مخصّصة

يمكنك ضبط اختبار Game Loop لكتابة نتائج اختبار مخصّصة لنظام ملفات جهازك. وبهذه الطريقة، عندما يبدأ الاختبار، يخزِّن Test Lab ملفات النتائج في دليل GameLoopsResults على جهاز الاختبار (الذي يجب عليك إنشاؤه بنفسك). عند انتهاء الاختبار، ينقل Test Lab جميع الملفات من دليل GameLoopResults إلى حزمة مشروعك. يُرجى مراعاة ما يلي عند إعداد الاختبار:

  • يتم تحميل جميع ملفات النتائج بغض النظر عن نوع الملف أو حجمه أو كميته.

  • لا يعالج Test Lab نتائج الاختبار حتى تنتهي جميع الحلقات في الاختبار، لذا إذا كان الاختبار يتضمن حلقات متعددة تكتب البيانات، فتأكد من إلحاقها بملف نتيجة فريد أو إنشاء ملف نتيجة لكل حلقة. بهذه الطريقة، يمكنك تجنب استبدال النتائج من تكرار حلقي سابق.

لإعداد الاختبار من أجل كتابة نتائج اختبار مخصّصة:

  1. في دليل Documents لتطبيقك، أنشِئ دليلاً باسم GameLoopResults.

  2. من أي مكان في رمز التطبيق (على سبيل المثال، تفويض تطبيقك)، يمكنك إضافة ما يلي:

    Swift

    /// Write to a results file.
    func writeResults() {
      let text = "Greetings from game loops!"
      let fileName = "results.txt"
      let fileManager = FileManager.default
      do {
    
      let docs = try fileManager.url(for: .documentDirectory,
                                     in: .userDomainMask,
                                     appropriateFor: nil,
                                     create: true)
      let resultsDir = docs.appendingPathComponent("GameLoopResults")
      try fileManager.createDirectory(
          at: resultsDir,
          withIntermediateDirectories: true,
          attributes: nil)
      let fileURL = resultsDir.appendingPathComponent(fileName)
      try text.write(to: fileURL, atomically: false, encoding: .utf8)
      } catch {
        // ...Handle error writing to file.
      }
    }
    

    Objective-C

    /// Write to a results file.
    - (void)writeResults:(NSString *)message {
        // Locate and create the results directory (if it doesn't exist already).
        NSFileManager *manager = [NSFileManager defaultManager];
        NSURL* url = [[manager URLsForDirectory:NSDocumentDirectory
                                      inDomains:NSUserDomainMask] lastObject];
        NSURL* resultsDir = [url URLByAppendingPathComponent:@"GameLoopResults"
                                                 isDirectory:YES];
        [manager createDirectoryAtURL:resultsDir
          withIntermediateDirectories:NO
                           attributes:nil
                                error:nil];
    
        // Write the result message to a text file.
        NSURL* resultFile = [resultsDir URLByAppendingPathComponent:@"result.txt"];
        if ([manager fileExistsAtPath:[resultFile path]]) {
            // Append to the existing file
            NSFileHandle *handle = [NSFileHandle fileHandleForWritingToURL:resultFile
                                                                     error:nil];
            [handle seekToEndOfFile];
            [handle writeData:[message dataUsingEncoding:NSUTF8StringEncoding]];
            [handle closeFile];
        } else {
            // Create and write to the file.
            [message writeToURL:resultFile
                     atomically:NO
                       encoding:NSUTF8StringEncoding error:nil];
        }
    }
    

الخطوة 3: توقيع تطبيقك

  1. يُرجى التأكّد من توقيع جميع العناصر في التطبيق. على سبيل المثال، يمكنك تنفيذ ذلك من خلال Xcode عن طريق تحديد إعدادات التوقيع مثل الملف الشخصي لتوفير المتطلبات اللازمة والهوية. لمزيد من المعلومات، يُرجى الاطّلاع على: Apple Codesigning

الخطوة 4: تجميع تطبيقك للتحميل

أنشئ ملف IPA لتطبيقك (ستحتاج إلى تحديد موقعه لاحقًا).

  1. من القائمة المنسدلة التي تظهر، انقر على المنتج > الأرشيف. اختَر أحدث أرشيف، ثم انقر على توزيع التطبيق.

  2. في النافذة التي تظهر، انقر على التطوير > التالي.

  3. اختياري: للحصول على إصدار أسرع، ألغِ اختيار إعادة الإنشاء من رمز البت، ثم انقر على التالي. لا يتطلّب مركز الاختبار الافتراضي تحسين أداء التطبيق أو إعادة بنائه لإجراء اختبار، لذا يمكنك إيقاف هذا الخيار بأمان.

  4. انقر على تصدير، ثم أدخِل الدليل الذي تريد تنزيل ملف IPA لتطبيقك فيه.

الخطوة 5: التحقّق من توقيع التطبيق

  1. تحقَّق من توقيع التطبيق عن طريق فك ضغط ملف .ipa ثم تشغيل codesign --verify --deep --verbose /path/to/MyApp.app، حيث يكون "MyApp" هو اسم التطبيق داخل المجلد غير المضغوط (يختلف باختلاف كل مشروع). الناتج المتوقَّع هو MyApp.app: valid on disk.

الخطوة 6: إجراء الاختبار على الجهاز

يمكنك إجراء الاختبار محليًا للتحقّق من سلوكه قبل تنفيذه باستخدام مركز الاختبار الافتراضي. لاختبار اللعبة محليًا، حمِّل اللعبة في محاكي وشغِّل ما يلي:

xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://
  • يمكنك العثور على المعرّف الفريد للجهاز للمحاكي من خلال تشغيل الأمر instruments -s devices.

  • في حال تم تشغيل مُحاكي واحد فقط، أدخِل السلسلة الخاصة "booted" بدلاً من SIMULATOR_UDID.

إذا كان الاختبار يتضمن تكرارات متعددة، يمكنك تحديد التكرار الحلقي الذي تريد تشغيله من خلال تمرير رقم التكرار إلى علامة scenario. تجدر الإشارة إلى أنّه لا يمكنك تنفيذ أكثر من تكرار واحد في كل مرة عند إجراء الاختبار محليًا. على سبيل المثال، إذا كنت تريد تشغيل التكرارات 1 و2 و5، فيجب تشغيل أمر منفصل لكل حلقة:

xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=1
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=2
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=5

الخطوات اللاحقة

يمكنك إجراء الاختبار باستخدام وحدة تحكُّم Firebase أو gcloud CLI.