איך מתחילים לעבוד עם בדיקות של לולאת משחקים

יכול להיות קשה להפוך את בדיקת המשחקים לאוטומטית כשאפליקציות המשחקים מבוססות על מסגרות שונות של ממשק משתמש. בדיקות Game Loop מאפשרות לכם לשלב את הבדיקות המקוריות שלכם עם Test Lab ולהריץ אותן בקלות במכשירים שתבחרו. בדיקת Game Loop מריצה את הבדיקה דרך אפליקציית המשחקים תוך כדי הדמיה של פעולות של שחקן אמיתי. במדריך הזה נסביר איך להריץ בדיקת Game Loop, ואז להציג ולנהל את תוצאות הבדיקה במסוף Firebase.

מהי בדיקת game loop?

אפליקציות משחקים מעובדות באמצעות מנועים וסביבות שונות של ממשקי משתמש, כמו Unity,‏ Unreal או קוד OpenGL/Vulkan C++‎, ולכן כלי אוטומציה רגילים לא יכולים לבדוק רכיבים בממשק המשתמש או לבצע בהם פעולות. בדיקת Game Loop מאפשרת לשלב את בדיקות המנוע המקורי עם Test Lab על ידי הפעלת המשחק עם Intent שמכיל מספר תרחיש ספציפי. מספר התרחיש הזה מנחה את האפליקציה להריץ לוגיקה מוגדרת מראש, סימולציות של AI או בדיקות ביצועים ישירות בתוך מנוע המשחק.

אחרי ההפעלה, Test Lab לא יוצר אינטראקציה נוספת עם ממשק המשתמש של המשחק. במקום זאת, Test Lab פועל כסביבה וכאמצעי להעברת בדיקות: הוא מפעיל את התרחיש הספציפי, עוקב אחרי קריסות וזמנים קצובים לתפוגה, ואוסף את כל יומני התוצאות בהתאמה אישית כשהאפליקציה מסיימת להריץ את הלולאה.

ההבדלים בין לולאות משחק לבין בדיקות אינסטרומנטציה

  • בדיקות אינסטרומנטציה (כמו Espresso או UI Automator) מסתמכות על מסגרות חיצוניות כדי להניע אינטראקציות בממשק המשתמש, ללחוץ על לחצנים ולבדוק את מצב האפליקציה בהיררכיית התצוגות של Android מחוץ לאפליקציה.
  • בדיקות game loop מעבירות את לוגיקת הבדיקה לאפליקציה. האפליקציה מריצה game loop פנימי או סימולציה משלה בלי אינטראקציה חיצונית עם ממשק המשתמש, ו-Test Lab מפעיל את התרחיש ואוסף את פלט הבדיקה.

דוגמאות

נניח שאתם רוצים לוודא שהמשחק שלכם שומר על קצב של לפחות 60 פריימים לשנייה במכשירים פיזיים שונים. אתם יכולים להטמיע את תרחיש 1 בקוד המשחק כדי לטעון את השלב הכי מורכב מבחינה ויזואלית, ולאפשר לשחקן AI או לסקריפט אוטומטי לשחק בו במשך שתי דקות.

כשהפונקציה Test Lab מפעילה את התרחיש 1, המשחק מתחיל את ההפעלה של ה-AI, עוקב אחרי זמני הרינדור של הפריימים והשימוש בזיכרון, מעביר את המדדים האלה בסטרימינג לקובץ יומן פלט בהתאמה אישית (logFile) וקורא לפונקציה finish() בסיום. ‫Test Lab מתעד את קובץ היומן ומאפשר לכם לגשת אליו ב-Cloud Storage ובמסוף Firebase.

תרחישים לדוגמה

בהתאם למנוע המשחק, אתם יכולים להטמיע בדיקות עם לולאה אחת או כמה לולאות. game loop הוא הרצה מלאה או חלקית של הבדיקה באפליקציית המשחקים. אפשר להשתמש ב-game loops כדי:

  • מפעילים שלב במשחק באותו אופן שבו משתמש קצה היה משחק בו. אתם יכולים להגדיר סקריפט לקלט של המשתמש, להשאיר את המשתמש במצב של חוסר פעילות או להחליף את המשתמש ב-AI אם זה הגיוני במשחק (לדוגמה, אם יש לכם אפליקציית משחקים של מכוניות מרוץ וכבר הטמעתם AI, אתם יכולים להגדיר נהג AI שאחראי לקלט של המשתמש).
  • מריצים את המשחק בהגדרת האיכות הגבוהה ביותר כדי לבדוק אם המכשירים תומכים בה.
  • להריץ בדיקה טכנית (לקמפל כמה שיידרים, להפעיל אותם, לבדוק שהפלט תקין וכו').

אפשר להריץ בדיקת Game Loop במכשיר בדיקה אחד, בקבוצה של מכשירי בדיקה או ב-Test Lab. עם זאת, לא מומלץ להריץ בדיקות Game Loop במכשירים וירטואליים כי קצב הפריימים של הגרפיקה שלהם נמוך יותר מאשר במכשירים פיזיים.

לפני שמתחילים

כדי להטמיע בדיקה, קודם צריך להגדיר את האפליקציה לבדיקות Game Loop.

  1. בקובץ המניפסט של האפליקציה, מוסיפים מסנן Intent חדש לפעילות:

    <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 יכול להפעיל את המשחק באמצעות טריגר עם Intent ספציפי.

  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();

    הפעולה הזו סוגרת את האפליקציה כשהבדיקה של Game Loop מסתיימת. הבדיקה מסתמכת על מסגרת ממשק המשתמש של האפליקציה כדי להתחיל את הלולאה הבאה, וסגירת האפליקציה מציינת שהבדיקה הסתיימה.

יצירה והרצה של בדיקת Game Loop

אחרי שמגדירים את האפליקציה לבדיקות של Game Loop, אפשר ליצור בדיקה מיד ולהריץ אותה באפליקציית המשחקים. אפשר להריץ בדיקה ב-Test Lab באמצעות מסוף Firebase או ממשק שורת הפקודה (CLI) של gcloud, או במכשיר מקומי באמצעות Test Loop Manager.

הפעלה במכשיר מקומי

Test Loop Manager של Test Lab היא אפליקציה בקוד פתוח שעוזרת לשלב בדיקות של game loop ולהריץ אותן במכשירים המקומיים. היא גם מאפשרת לצוות אבטחת האיכות להריץ את אותן לולאות משחק במכשירים שלהם.

כדי להריץ בדיקה במכשיר מקומי באמצעות Test Loop Manager:

  1. מורידים את Test Loop Manager בטלפון או בטאבלט ומתקינים אותה באמצעות הפקודה הבאה:
    adb install testloopmanager.apk
  2. במכשיר, פותחים את האפליקציה Test Loop Apps בטלפון או בטאבלט. באפליקציה מוצגת רשימה של אפליקציות במכשיר שאפשר להפעיל באמצעות לולאות משחק. אם אפליקציית המשחקים לא מופיעה כאן, צריך לוודא שמסנן ה-Intent תואם לזה שמתואר בשלב הראשון בקטע לפני שמתחילים.
  3. בוחרים את אפליקציית המשחקים ואז את מספר הלולאות שרוצים להריץ. הערה: בשלב הזה, אתם יכולים לבחור להריץ קבוצת משנה של לולאות במקום לולאה אחת בלבד. למידע נוסף על הפעלת כמה לולאות בו-זמנית, ראו תכונות אופציונליות.
  4. לוחצים על הפעלת הבדיקה. הבדיקה מתחילה לפעול מיד.

ריצה ב-Test Lab

אפשר להריץ בדיקת Game Loop ב-Test Lab באמצעות מסוף Firebase או ה-CLI של gcloud. לפני שמתחילים, אם עדיין לא עשיתם את זה, פתחו את מסוף Firebase וצרו פרויקט.

שימוש במסוף Firebase

  1. במסוף Firebase, לוחצים על Test Lab בחלונית הימנית.
  2. לוחצים על Run Your First Test (או על Run a Test אם כבר הרצתם בדיקה בפרויקט).
  3. בוחרים באפשרות Game Loop כסוג הבדיקה ולוחצים על Continue.
  4. לוחצים על עיון, ואז מחפשים את קובץ ה-.apk של האפליקציה. הערה: בשלב הזה, אתם יכולים לבחור להריץ קבוצת משנה של לולאות במקום לולאה אחת בלבד. למידע נוסף על הפעלת כמה לולאות בו-זמנית, ראו תכונות אופציונליות.
  5. לוחצים על המשך.
  6. בוחרים את המכשירים הפיזיים שבהם רוצים להשתמש כדי לבדוק את האפליקציה.
  7. לוחצים על התחלת הבדיקות.

Firebase איך מתחילים לבדוק באמצעות מסוף Firebase

שימוש בממשק שורת הפקודה (CLI) של gcloud

  1. אם עדיין לא עשיתם זאת, הורידו והתקינו את Google Cloud SDK.

  2. נכנסים ל-CLI של gcloud באמצעות חשבון 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
    

למידע נוסף על תחילת העבודה עם ה-CLI של gcloud, קראו את המאמר התחלת הבדיקה משורת הפקודה של gcloud.

תכונות אופציונליות

ב-Test Lab יש כמה תכונות אופציונליות שמאפשרות להתאים אישית את הבדיקות, כולל היכולת לכתוב נתוני פלט, תמיכה בכמה לולאות משחק ותוויות ללולאות קשורות.

כתיבת נתוני פלט

כש-Test Lab מפעיל את המשחק, הוא מציין כתובת URL שמפנה לקובץ תוצאות מקומי במכשיר הבדיקה, שאפשר לגשת אליו באמצעות launchIntent.getData(). במהלך הרצת הבדיקה, המשחק יכול לכתוב את תוצאות הבדיקה, מדדי הביצועים והיומנים (logFile) ישירות למיקום הזה.

אתם יכולים לכתוב כל תוכן שרירותי ב-logFile, כמו טקסט פשוט, נתוני CSV, זמני רינדור של פריימים או דוחות אבחון בהתאמה אישית. אין הגבלות או דרישות עיצוב לתוכן שאתם כותבים. הוא מתעד את הקובץ כשהבדיקה מסתיימת ומעלה אותו לקטגוריה של Cloud Storage לצד פריטי המידע האחרים של הבדיקה.

‫Test Lab פועלת לפי השיטות המומלצות המקובלות לשיתוף קובץ בין אפליקציות, כפי שמתואר במאמר שיתוף קובץ. ב-method‏ onCreate() של הפעילות, שבה נמצא ה-intent, אפשר לאחזר את קובץ הפלט של הנתונים ולבדוק אותו על ידי הפעלת הקוד הבא:

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) יוכל לנתח ולהציג מדדי סיכום לבדיקת game loop.

פרטים חשובים על הדוגמה הזו:

  • מי מספק את הנתונים: אתם (מפתחי הבדיקה) אחראים לחשב את כל המדדים ולכתוב אותם בקובץ הזה מקוד המשחק. ‫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
}

כמה לולאות משחק

יכול להיות שתרצו להריץ כמה לולאות משחק באפליקציה. לולאה היא הרצה מלאה של אפליקציית המשחק מההתחלה ועד הסוף. לדוגמה, אם יש כמה שלבים במשחק, יכול להיות שתרצו להפעיל כל שלב באמצעות game loop נפרד, במקום להשתמש ב-game loop אחד שעובר על כל השלבים. כך, אם האפליקציה קורסת בשלב 32, אתם יכולים להפעיל ישירות את הלולאה של המשחק כדי לשחזר את הקריסה ולבדוק את תיקוני הבאגים.

כדי לאפשר לאפליקציה להריץ כמה לולאות בו-זמנית:

  • אם אתם מריצים בדיקה באמצעות Test Loop Manager:

    1. מוסיפים את השורה הבאה למניפסט של האפליקציה, בתוך הרכיב <application>:

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

      ה-Intent הזה להפעלה מכיל את הלולאה המטורגטת כפרמטר של מספר שלם. בשדה android:value, אפשר לציין מספר שלם מ-1 עד 1024 (המספר המקסימלי של לולאות שמותרות לבדיקה אחת). שימו לב שהאינדקס של הלולאות מתחיל מ-1, לא מ-0.

    2. באפליקציה Test Loop Manager, מופיע מסך בחירה שבו אפשר לבחור את הלולאות שרוצים להריץ. אם בוחרים כמה לולאות, כל לולאה מופעלת ברצף אחרי שהלולאה הקודמת מסתיימת.

  • אם אתם מריצים בדיקה באמצעות מסוף Firebase, הזינו רשימה או טווח של מספרי לולאות בשדה Scenarios.

  • אם אתם מריצים בדיקה באמצעות ה-CLI של gcloud, ציינו רשימה של מספרי לולאות באמצעות הדגל --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: ללולאות שמשמשות לבדיקת בעיות שקשורות ל-GPU. מטרת הבדיקה באמצעות הלולאות האלה היא להריץ קוד GPU שאולי לא יפעל כמו שצריך בסביבת הייצור, כדי לחשוף בעיות בחומרה ובמנהלי ההתקנים.
  • ‫com.google.test.loops.compatibility: לולאות For שמשמשות לבדיקת מגוון רחב של בעיות תאימות, כולל בעיות קלט/פלט ובעיות OpenSSL.
  • ‫com.google.test.loops.performance: לולאות For שמשמשות לבדיקת הביצועים של המכשיר. לדוגמה, יכול להיות שמשחק יופעל בהגדרות הגרפיקה המורכבות ביותר כדי לבדוק איך מכשיר חדש מתנהג.

כדי לאפשר לאפליקציה להריץ לולאות עם אותה תווית:

  • אם אתם מריצים בדיקה באמצעות Test Loop Manager:

    1. במניפסט של האפליקציה, מוסיפים את שורת המטא-נתונים הבאה ומחליפים את LABEL_NAME בתווית לבחירתכם:

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

      בשדה android:value, אתם יכולים לציין טווח או קבוצה של מספרים שלמים מ-1 עד 1,024 (המספר המקסימלי של לולאות שמותרות לבדיקה אחת) שמייצגים את הלולאות שאתם רוצים לתייג. שימו לב שהלולאות נוספות לאינדקס החל מ-1, ולא מ-0. לדוגמה, android:value="1,3-5" מחיל את LABEL_NAME על הלולאות 1, 3, 4 ו-5.

    2. באפליקציה Test Loop Manager, מזינים תווית אחת או יותר בשדה Labels.

  • אם אתם מריצים בדיקה באמצעות מסוף Firebase, הזינו תווית אחת או יותר בשדה Labels.

  • אם אתם מריצים בדיקה באמצעות ה-CLI של gcloud, ציינו תווית אחת או יותר לתרחיש באמצעות הדגל --scenario-labels (למשל, --scenario-labels=performance,gpu).

תמיכה ברישוי לשימוש באפליקציה

‫Test Lab תומך באפליקציות שמשתמשות בשירות רישוי לשימוש באפליקציה שמוצע על ידי Google Play. כדי לבדוק את הרישוי בהצלחה כשבודקים את האפליקציה באמצעות Test Lab, צריך לפרסם את האפליקציה בערוץ הייצור בחנות Play. כדי לבדוק את האפליקציה בערוץ אלפא או בערוץ בטא באמצעות Test Lab, צריך להסיר את בדיקת הרישיון לפני שמעלים את האפליקציה אל Test Lab.

בעיות מוכרות

אלה הבעיות המוכרות בבדיקות Game Loop ב-Test Lab:

  • בחלק מהקריסות אין תמיכה במעקב לאחור. לדוגמה, יכול להיות שבגרסאות build מסוימות להפצה, הפלט של התהליך debuggerd ידוכא באמצעות prctl(PR_SET_DUMPABLE, 0). מידע נוסף זמין במאמר debuggerd.
  • רמת API‏ 19 לא נתמכת כרגע בגלל שגיאות בהרשאות לקבצים.