Inizia a utilizzare i test del ciclo di gioco

Può essere difficile automatizzare i test dei giochi quando le app di gioco sono basate su framework UI diversi. I test dei cicli di gioco ti consentono di integrare i tuoi test nativi con Test Lab ed eseguirli facilmente sui dispositivi che selezioni. Un test del ciclo di gioco esegue il test tramite l'app di gioco simulando le azioni di un giocatore reale. Questa guida mostra come eseguire un test di ciclo di gioco, quindi visualizzare e gestire i risultati del test nella console Firebase.

Che cos'è un test del ciclo di gioco?

Poiché il rendering delle app di gioco viene eseguito su diversi framework e motori UI, come il codice C++ Unity, Unreal o OpenGL/Vulkan, gli strumenti di automazione standard non possono ispezionare o interagire con gli elementi UI. Un test del loop di gioco ti consente di integrare i test del motore nativo con Test Lab avviando il gioco con un intent contenente un numero di scenario specifico. Questo numero di scenario chiede alla tua app di eseguire logica pre-scriptata, simulazioni di AI o controlli delle prestazioni direttamente nel tuo motore grafico.

Una volta avviato, Test Lab non interagisce più con la UI del gioco. Invece, Test Lab funge da ambiente e trasporto di test: avvia lo scenario specifico, monitora eventuali arresti anomali e timeout e raccoglie i log dei risultati personalizzati quando l'app termina l'esecuzione del loop.

Differenze tra i loop di gioco e i test di instrumentazione

  • I test di instrumentazione (come Espresso o UI Automator) si basano su framework esterni per gestire le interazioni con l'interfaccia utente, fare clic sui pulsanti e verificare lo stato dell'applicazione nella gerarchia di oggetti View di Android dall'esterno dell'app.
  • I test dei loop di gioco delegano la logica di test alla tua app. L'app esegue il proprio loop di test interno o la propria simulazione senza interazione con l'interfaccia utente esterna, mentre Test Lab attiva lo scenario e raccoglie l'output del test.

Esempi

Supponiamo che tu voglia verificare che il tuo gioco mantenga almeno 60 frame al secondo su vari dispositivi fisici. Puoi implementare lo scenario 1 all'interno del codice del gioco per caricare il livello visivamente più complesso e consentire a un giocatore AI o a uno script automatico di giocarci per due minuti.

Quando Test Lab attiva lo scenario 1, il gioco avvia il playthrough con l'AI, monitora i tempi di rendering dei frame e la memoria utilizzata, trasmette queste metriche a un file di log di output personalizzato (logFile) e chiama finish() al termine. Test Lab acquisisce quindi il file di log e lo rende disponibile in Cloud Storage e nella console Firebase.

Casi d'uso

A seconda del motore grafico, puoi implementare test con uno o più loop. Un loop è un'esecuzione completa o parziale del test sulla tua app di gioco. I loop di gioco possono essere utilizzati per:

  • Esegui un livello del gioco nello stesso modo in cui lo giocherebbe un utente finale. Puoi creare uno script per l'input dell'utente, lasciare l'utente inattivo o sostituirlo con un'AI se ha senso nel tuo gioco (ad esempio, se hai un'app di gioco di auto da corsa e hai già implementato un'AI, puoi mettere un'AI al posto dell'input dell'utente).
  • Esegui il gioco con l'impostazione di qualità più alta per verificare se i dispositivi la supportano.
  • Esegui un test tecnico (compila più shader, eseguili, verifica che l'output sia quello previsto e così via).

Puoi eseguire un test di ciclo di gioco su un singolo dispositivo di test, su un insieme di dispositivi di test o su Test Lab. Tuttavia, sconsigliamo di eseguire i test di ciclo di gioco su dispositivi virtuali perché hanno frequenze fotogrammi grafiche inferiori rispetto ai dispositivi fisici.

Prima di iniziare

Per implementare un test, devi prima configurare la tua app per i test con ciclo di gioco.

  1. Nel manifest dell'app, aggiungi un nuovo filtro per intent alla tua attività:

    <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>

    In questo modo, Test Lab può avviare il tuo gioco attivandolo con un intent specifico.

  2. Nel codice (consigliamo all'interno della dichiarazione del metodo onCreate), aggiungi quanto segue:

    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
    }

    In questo modo, l'attività può controllare l'intent che la avvia. Se preferisci, puoi anche aggiungere questo codice in un secondo momento (ad esempio, dopo aver caricato inizialmente il motore grafico).

  3. Consigliato: alla fine del test, aggiungi:

    Kotlin

    yourActivity.finish()

    Java

    yourActivity.finish();

    In questo modo l'app viene chiusa al termine del test ciclo di gioco. Il test si basa sul framework dell'interfaccia utente dell'app per avviare il loop successivo e la chiusura dell'app indica che il test è terminato.

Crea ed esegui un test Ciclo di gioco

Dopo aver configurato l'app per i test dei loop di gioco, puoi creare immediatamente un test ed eseguirlo nella tua app di gioco. Puoi scegliere di eseguire un test in Test Lab utilizzando la consoleFirebase o l'interfaccia a riga di comando (CLI) gcloud oppure su un dispositivo locale utilizzando Test Loop Manager.

Esegui su un dispositivo locale

Test Loop Manager di Test Lab è un'app open source che ti aiuta a integrare i test dei cicli di gioco ed eseguirli sui tuoi dispositivi locali. Inoltre, consente al tuo team di garanzia di qualità di eseguire gli stessi loop di gioco sui propri dispositivi.

Per eseguire un test su un dispositivo locale utilizzando Test Loop Manager:

  1. Scarica Test Loop Manager su uno smartphone o tablet e installalo eseguendo:
    adb install testloopmanager.apk
  2. Sul tuo dispositivo, apri l'app Test Loop Apps sullo smartphone o sul tablet. L'app mostra un elenco di app sul tuo dispositivo che possono essere eseguite con i loop di gioco. Se non vedi la tua app di gioco qui, assicurati che il filtro per intent corrisponda a quello descritto nel primo passaggio della sezione Prima di iniziare.
  3. Seleziona l'app di gioco, quindi il numero di loop che vuoi eseguire. Nota: in questo passaggio, puoi scegliere di eseguire un sottoinsieme di loop anziché un solo loop. Per saperne di più sull'esecuzione di più loop contemporaneamente, consulta Funzionalità facoltative.
  4. Fai clic su Esegui test. Il test viene avviato immediatamente.

Esegui in Test Lab

Puoi eseguire un test del ciclo di gioco in Test Lab utilizzando la console Firebase o gcloud CLI. Prima di iniziare, se non l'hai ancora fatto, apri la console Firebase e crea un progetto.

Utilizza la console Firebase

  1. Nella console Firebase, fai clic su Test Lab nel riquadro a sinistra.
  2. Fai clic su Esegui il tuo primo test (o Esegui un test se hai già eseguito un test per il progetto).
  3. Seleziona Loop di gioco come tipo di test, quindi fai clic su Continua.
  4. Fai clic su Sfoglia e poi vai al file .apk della tua app. Nota: in questo passaggio, puoi scegliere di eseguire un sottoinsieme di loop anziché un solo loop. Per saperne di più sull'esecuzione di più loop contemporaneamente, consulta Funzionalità facoltative.
  5. Fai clic su Continua.
  6. Seleziona i dispositivi fisici da utilizzare per testare la tua app.
  7. Fai clic su Avvia test.

Per saperne di più su come iniziare a utilizzare la console Firebase, consulta Inizia a eseguire i test con la console Firebase.

Utilizza l'interfaccia a riga di comando (CLI) gcloud

  1. Se non l'hai ancora fatto, scarica e installa Google Cloud SDK

  2. Accedi a gcloud CLI utilizzando il tuo Account Google:

    gcloud auth login

  3. Imposta il tuo progetto Firebase in gcloud, dove PROJECT_ID è l'ID del tuo progetto Firebase:

    gcloud config set project PROJECT_ID
    
  4. Esegui il tuo primo test:

    gcloud firebase test android run \
     --type=game-loop --app=<var>path-to-apk</var> \
     --device model=herolte,version=23
    

Per saperne di più su come iniziare a utilizzare gcloud CLI, consulta Inizia i test dalla riga di comando gcloud.

Funzionalità facoltative

Test Lab offre diverse funzionalità facoltative che ti consentono di personalizzare ulteriormente i test, tra cui la possibilità di scrivere dati di output, il supporto di più loop di gioco ed etichette per i loop correlati.

Scrivi i dati di output

Quando Test Lab avvia il gioco, specifica un URL che rimanda a un file di risultati locale sul dispositivo di test, accessibile tramite launchIntent.getData(). Il tuo gioco può scrivere i risultati dei test, le metriche sul rendimento e i log (logFile) direttamente in questa posizione durante l'esecuzione del test.

Puoi scrivere qualsiasi contenuto arbitrario in logFile, ad esempio testo normale, dati CSV, tempi di rendering dei frame o report diagnostici personalizzati. Non esistono restrizioni o requisiti di formattazione per i contenuti che scrivi. Il file viene acquisito al termine del test e caricato nel bucket Cloud Storage insieme agli altri artefatti del test.

Test Lab segue le best practice stabilite per la condivisione di un file tra app descritte in Condivisione di un file. Nel metodo onCreate() della tua attività, dove si trova l'intent, puoi recuperare e controllare il file di output dei dati eseguendo il seguente codice:

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());
    // ...
}

Se vuoi scrivere nel file dal lato C++ della tua app di gioco, puoi passare il descrittore del file anziché il percorso del file:

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

Esempio di file di output

Sebbene Test Lab non richieda un formato specifico per logFile, la strutturazione dell'output come JSON corrispondente allo schema seguente consente alla console Firebase (https://console.firebase.google.com/project/_/testlab) di analizzare e visualizzare le metriche di riepilogo per il test del ciclo di gioco.

Dettagli chiave su questo esempio:

  • Chi fornisce i dati: tu (lo sviluppatore del test) hai la responsabilità di calcolare e scrivere tutte le metriche in questo file dal codice del gioco. Test Lab non raccoglie né genera automaticamente questi punti dati per te.
  • Aggiunta di campi personalizzati: le aree contrassegnate come /.../ indicano dove puoi aggiungere coppie chiave-valore personalizzate arbitrarie (ad esempio "rendered_vertices": 12000 o "ai_error_count": 0). Puoi aggiungere qualsiasi proprietà JSON valida all'output quando scrivi il file dalla tua app, purché i nomi dei campi non siano in conflitto con i campi dello schema, come name o start_timestamp.
  • Dove trovare il file di output: al termine del test, il file di output non elaborato viene archiviato ed è scaricabile nel bucket dei risultati di Cloud Storage (GCS) del tuo progetto all'interno della directory degli artefatti dell'esecuzione del test. Puoi anche visualizzare e ispezionare l'output analizzato nella pagina Test Lab della console Firebase.

Di seguito è riportato un esempio di file di output JSON strutturato:

{
  "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
}

Più loop di gioco

Potresti trovare utile eseguire più loop di gioco nella tua app. Un loop è un'esecuzione completa della tua app di gioco dall'inizio alla fine. Ad esempio, se il tuo gioco ha più livelli, potresti voler avere un loop di gioco per lanciare ogni livello invece di avere un loop che li ripete tutti. In questo modo, se l'app si arresta in modo anomalo al livello 32, puoi avviare direttamente quel loop di gioco per riprodurre l'arresto anomalo e testare le correzioni dei bug.

Per consentire alla tua app di eseguire più loop contemporaneamente:

  • Se stai eseguendo un test con Test Loop Manager:

    1. Aggiungi la seguente riga al manifest dell'app, all'interno dell'elemento <application>:

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

      Questo intent di avvio contiene il loop target come parametro intero. Nel campo android:value, puoi specificare un numero intero compreso tra 1 e 1024 (il numero massimo di loop consentiti per un singolo test). Tieni presente che i loop sono indicizzati a partire da 1, non da 0.

    2. Nell'app Test Loop Manager viene visualizzata una schermata di selezione che ti consente di selezionare i loop che vuoi eseguire. Se selezioni più loop, ogni loop viene avviato in sequenza dopo il completamento del loop precedente.

  • Se stai eseguendo un test con la console Firebase, inserisci un elenco o un intervallo di numeri di loop nel campo Scenari.

  • Se esegui un test con gcloud CLI, specifica un elenco di numeri di loop utilizzando il flag --scenario-numbers. Ad esempio, --scenario-numbers=1,3,5 esegue i loop 1, 3 e 5.

  • Se stai scrivendo in C++ e vuoi modificare il comportamento del loop, passa il seguente extra al codice C++ nativo:

    Kotlin

    val launchIntent = intent
    val scenario = launchIntent.getIntExtra("scenario", 0)

    Java

    Intent launchIntent = getIntent();
    int scenario = launchIntent.getIntExtra("scenario", 0);

    Ora puoi modificare il comportamento del loop in base al valore int risultante.

Etichetta i loop di gioco

Quando etichetti i loop di gioco con una o più etichette di scenario, tu e il tuo team di controllo qualità potete lanciare facilmente un insieme di loop di gioco correlati (ad es. "tutti i loop di gioco di compatibilità") e testarli in una singola matrice. Puoi creare le tue etichette o utilizzare quelle predefinite offerte da Test Lab:

  • com.google.test.loops.player_experience: cicli for utilizzati per riprodurre l'esperienza di un utente reale durante il gioco. L'obiettivo dei test con questi loop è trovare i problemi che un utente reale potrebbe riscontrare durante il gioco.
  • com.google.test.loops.gpu_compatibility: cicli for utilizzati per testare i problemi relativi alla GPU. L'obiettivo dei test con questi loop è eseguire il codice GPU che potrebbe non essere eseguito correttamente in produzione, per esporre i problemi con l'hardware e i driver.
  • com.google.test.loops.compatibility: cicli for utilizzati per testare un'ampia gamma di problemi di compatibilità, inclusi problemi di I/O e di OpenSSL.
  • com.google.test.loops.performance: cicli for utilizzati per testare le prestazioni del dispositivo. Ad esempio, un gioco potrebbe essere eseguito con le impostazioni grafiche più complesse per vedere come si comporta un nuovo dispositivo.

Per consentire alla tua app di eseguire loop con la stessa etichetta:

  • Se stai eseguendo un test con Test Loop Manager:

    1. Nel file manifest dell'app, aggiungi la seguente riga di metadati e sostituisci LABEL_NAME con un'etichetta a tua scelta:

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

      Nel campo android:value, puoi specificare un intervallo o un insieme di numeri interi da 1 a 1024 (il numero massimo di loop consentiti per un singolo test) che rappresentano i loop che vuoi etichettare. Tieni presente che i loop sono indicizzati a partire da 1, non da 0. Ad esempio, android:value="1,3-5" applica LABEL_NAME ai loop 1, 3, 4 e 5.

    2. Nell'app Test Loop Manager, inserisci una o più etichette nel campo Etichette.

  • Se esegui un test con la console Firebase, inserisci una o più etichette nel campo Etichette.

  • Se esegui un test con gcloud CLI, specifica una o più etichette di scenario utilizzando il flag --scenario-labels (ad es. --scenario-labels=performance,gpu).

Assistenza per le licenze delle app

Test Lab supporta le app che utilizzano il servizio di licensing dell'app offerto da Google Play. Per verificare correttamente le licenze durante il test della tua app con Test Lab, devi pubblicare l'app nel canale di produzione nel Play Store. Per testare la tua app nel canale alpha o beta utilizzando Test Lab, rimuovi il controllo delle licenze prima di caricare l'app su Test Lab.

Problemi noti

I test del ciclo di gioco in Test Lab presentano i seguenti problemi noti:

  • Alcuni arresti anomali non supportano i backtrace. Ad esempio, alcune build di release potrebbero sopprimere l'output del processo debuggerd utilizzando prctl(PR_SET_DUMPABLE, 0). Per saperne di più, consulta debuggerd.
  • Il livello API 19 non è attualmente supportato a causa di errori relativi alle autorizzazioni dei file.