با تست های Game Loop برای iOS شروع کنید

با استفاده از تست‌های Game Loop، می‌توانید تست‌هایی را به صورت native برای موتور بازی خود بنویسید و سپس آنها را در Test Lab روی دستگاه‌هایی که انتخاب می‌کنید اجرا کنید. به این ترتیب، نیازی نیست نگران نوشتن برای رابط کاربری یا فریم‌ورک‌های تست مختلف باشید. یک تست Game Loop، اعمال یک بازیکن واقعی را شبیه‌سازی می‌کند و وقتی آن را در Test Lab اجرا می‌کنید، روشی سریع و مقیاس‌پذیر برای تأیید عملکرد خوب بازی شما برای کاربرانتان فراهم می‌کند.

این صفحه به شما نشان می‌دهد که چگونه یک تست حلقه بازی (Game Loop) را اجرا کنید، سپس نتایج تست خود را در صفحه Test Lab کنسول Firebase مشاهده و مدیریت کنید. همچنین می‌توانید تست‌های خود را با ویژگی‌های اختیاری، مانند نوشتن نتایج تست سفارشی یا پایان دادن زودهنگام به تست، بیشتر سفارشی کنید.

تست حلقه بازی چیست؟

یک حلقه، اجرای کامل یا جزئی تست شما روی برنامه بازی‌تان است. می‌توانید یک تست حلقه بازی را به صورت محلی روی یک شبیه‌ساز یا روی مجموعه‌ای از دستگاه‌ها در Test Lab اجرا کنید. تست‌های حلقه بازی را می‌توان برای موارد زیر استفاده کرد:

  • بازی خود را همانطور که یک کاربر نهایی آن را بازی می‌کند، اجرا کنید. می‌توانید ورودی کاربر را اسکریپت کنید، بگذارید کاربر بیکار باشد، یا کاربر را با یک هوش مصنوعی جایگزین کنید (برای مثال، اگر هوش مصنوعی را در یک بازی مسابقه اتومبیل‌رانی پیاده‌سازی کرده‌اید، می‌توانید یک راننده هوش مصنوعی را مسئول ورودی کاربر قرار دهید).

  • بازی خود را با بالاترین کیفیت اجرا کنید تا ببینید کدام دستگاه‌ها می‌توانند از آن پشتیبانی کنند.

  • یک تست فنی اجرا کنید، مانند کامپایل کردن چندین شیدر، اجرای آنها و بررسی اینکه خروجی مطابق انتظار است.

مرحله ۱: ثبت طرح URL سفارشی Test Lab

ابتدا، باید طرح URL سفارشی Firebase 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>
    

اکنون برنامه شما برای اجرای یک تست با استفاده از Test Lab پیکربندی شده است.

مرحله ۲ (اختیاری): برنامه خود را برای اجرای چندین حلقه پیکربندی کنید

اگر برنامه شما چندین طرح URL سفارشی ثبت شده دارد و قصد دارید چندین حلقه (یا سناریو) را در تست خود اجرا کنید، باید در زمان اجرا مشخص کنید که می‌خواهید کدام حلقه‌ها را در برنامه خود اجرا کنید.

در نماینده برنامه خود، متد application(_:open:options:) بازنویسی کنید:

سویفت

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
}

هدف-سی

- (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 سفارشی استفاده می‌شود، بدست آورید:

سویفت

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).
}

هدف-سی

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 یا با رابط خط فرمان بتای gcloud اجرا کنید. اگر قبلاً این کار را نکرده‌اید، یک فایل IPA برای برنامه خود ایجاد کنید (بعداً باید آن را پیدا کنید).

اجرای تست در کنسول Firebase

  1. اگر هنوز این کار را نکرده‌اید، کنسول Firebase را باز کنید و یک پروژه ایجاد کنید.

  2. در صفحه Test Lab کنسول Firebase ، روی اجرای اولین تست خود > اجرای یک حلقه بازی iOS کلیک کنید.

  3. در بخش بارگذاری برنامه ، روی مرور کلیک کنید، سپس فایل IPA برنامه خود را انتخاب کنید (اگر قبلاً این کار را نکرده‌اید، یک فایل IPA برای برنامه خود ایجاد کنید ).

  4. اختیاری : اگر می‌خواهید چندین حلقه (یا سناریو) را همزمان اجرا کنید یا حلقه‌های خاصی را برای اجرا انتخاب کنید، شماره حلقه‌ها را در فیلد سناریوها وارد کنید.

    برای مثال، وقتی «۱-۳، ۵» را وارد می‌کنید، Test Lab حلقه‌های ۱، ۲، ۳ و ۵ را اجرا می‌کند. به طور پیش‌فرض (اگر چیزی در فیلد Scenarios وارد نکنید)، Test Lab فقط حلقه ۱ را اجرا می‌کند.

  5. در بخش دستگاه‌ها ، یک یا چند دستگاه فیزیکی را که می‌خواهید برنامه خود را روی آنها آزمایش کنید، انتخاب کنید، سپس روی شروع آزمایش‌ها کلیک کنید.

با رابط خط فرمان بتای gcloud یک تست اجرا کنید

  1. اگر هنوز این کار را نکرده‌اید، محیط SDK محلی gcloud خود را پیکربندی کنید، سپس مطمئن شوید که کامپوننت بتای gcloud را نصب کرده‌اید.

  2. دستور gcloud beta firebase test ios run اجرا کنید و از پرچم‌های زیر برای پیکربندی اجرا استفاده کنید:

پرچم‌هایی برای تست‌های حلقه بازی
--type

الزامی : نوع تست iOS که می‌خواهید اجرا کنید را مشخص می‌کند. می‌توانید نوع تست را xctest (پیش‌فرض) یا game-loop وارد کنید.

--app

الزامی : مسیر مطلق ( Google Cloud Storage یا سیستم فایل) به فایل IPA برنامه شما. این علامت فقط هنگام اجرای تست‌های حلقه بازی معتبر است.

--scenario-numbers

حلقه‌هایی (یا سناریوهایی) که می‌خواهید در برنامه خود اجرا کنید. می‌توانید یک حلقه، یک لیست یا چندین حلقه یا مجموعه‌ای از حلقه‌ها را وارد کنید. حلقه پیش‌فرض ۱ است.

برای مثال، --scenario-numbers=1-3,5 ‎ حلقه‌های ۱، ۲، ۳ و ۵ را اجرا می‌کند.

--device-model

دستگاه فیزیکی که می‌خواهید تست خود را روی آن اجرا کنید (ببینید از کدام دستگاه‌های موجود می‌توانید استفاده کنید).

--timeout

حداکثر مدت زمانی که می‌خواهید تست شما اجرا شود. می‌توانید یک عدد صحیح برای نمایش مدت زمان بر حسب ثانیه، یا یک عدد صحیح و شمارشی برای نمایش مدت زمان به عنوان واحد زمانی طولانی‌تر وارد کنید.

برای مثال:

  • --timeout=200 وقتی تست شما تا ۲۰۰ ثانیه طول بکشد، آن را مجبور به خاتمه می‌کند.
  • --timeout=1h تست شما را مجبور می‌کند وقتی تا یک ساعت طول می‌کشد، خاتمه یابد.

برای مثال، دستور زیر یک تست حلقه بازی را اجرا می‌کند که حلقه‌های ۱، ۴، ۶، ۷ و ۸ را روی آیفون ۸ پلاس اجرا می‌کند:

gcloud beta firebase test ios run
 --type game-loop --app path/to/my/App.ipa --scenario-numbers 1,4,6-8
 --device-model=iphone8plus

برای اطلاعات بیشتر در مورد gcloud CLI، به مستندات مرجع مراجعه کنید.

اجرای یک تست به صورت محلی

برای اجرای تست خود به صورت محلی، برنامه بازی خود را در یک شبیه‌ساز بارگذاری کنید و دستور زیر را اجرا کنید:

xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://
  • شما می‌توانید UDID شبیه‌ساز خود را با اجرای دستور instruments -s devices پیدا کنید.

  • اگر فقط یک شبیه‌ساز در حال اجرا است، به جای SIMULATOR_UDID رشته‌ی ویژه‌ی "booted" را وارد کنید.

اگر تست شما شامل چندین حلقه است، می‌توانید با ارسال شماره حلقه به پرچم scenario ، مشخص کنید که می‌خواهید کدام حلقه را اجرا کنید. توجه داشته باشید که هنگام اجرای تست به صورت محلی، فقط می‌توانید یک حلقه را در یک زمان اجرا کنید. به عنوان مثال، اگر می‌خواهید حلقه‌های ۱، ۲ و ۵ را اجرا کنید، باید برای هر حلقه یک دستور جداگانه اجرا کنید:

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

پایان دادن زودهنگام به آزمون

به طور پیش‌فرض، یک تست حلقه بازی (Game Loop) تا زمانی که به زمان انقضای پنج دقیقه برسد، حتی زمانی که تمام حلقه‌ها اجرا شده باشند، به اجرا ادامه می‌دهد. وقتی به زمان انقضای پنج دقیقه رسید، تست پایان می‌یابد و هر حلقه در حال انتظاری را لغو می‌کند. می‌توانید با فراخوانی طرح URL سفارشی firebase-game-loop-complete از Test Lab در AppDelegate برنامه خود، تست خود را سرعت بخشیده یا آن را زودتر به پایان برسانید. برای مثال:

سویفت

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

هدف-سی

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

تست حلقه بازی شما حلقه فعلی را خاتمه داده و حلقه بعدی را اجرا می‌کند. وقتی حلقه دیگری برای اجرا وجود نداشته باشد، تست به پایان می‌رسد.

نوشتن نتایج تست سفارشی

شما می‌توانید تست حلقه بازی خود را طوری پیکربندی کنید که نتایج تست سفارشی را در سیستم فایل دستگاه شما بنویسد. به این ترتیب، وقتی تست شروع به اجرا می‌کند، Test Lab فایل‌های نتیجه را در یک دایرکتوری GameLoopsResults در دستگاه تست شما (که باید خودتان ایجاد کنید) ذخیره می‌کند. وقتی تست پایان می‌یابد، Test Lab تمام فایل‌ها را از دایرکتوری GameLoopResults به سطل پروژه شما منتقل می‌کند. هنگام تنظیم تست خود موارد زیر را در نظر داشته باشید:

  • تمام فایل‌های نتیجه صرف نظر از نوع، اندازه یا تعداد فایل آپلود می‌شوند.

  • Test Lab تا زمانی که اجرای تمام حلقه‌های تست شما تمام نشده باشد، نتایج تست شما را پردازش نمی‌کند، بنابراین اگر تست شما شامل چندین حلقه است که خروجی را می‌نویسند، مطمئن شوید که آنها را به یک فایل نتیجه منحصر به فرد اضافه می‌کنید یا برای هر حلقه یک فایل نتیجه ایجاد می‌کنید. به این ترتیب، می‌توانید از بازنویسی نتایج حلقه قبلی جلوگیری کنید.

برای تنظیم آزمون خود برای نوشتن نتایج آزمون سفارشی:

  1. در پوشه Documents برنامه خود، پوشه‌ای به نام GameLoopResults ایجاد کنید.

  2. از هر جایی از کد برنامه‌تان (مثلاً نماینده برنامه‌تان)، موارد زیر را اضافه کنید:

    سویفت

    /// 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.
      }
    }
    

    هدف-سی

    /// 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];
        }
    }