Primeiros passos com testes de loop de jogo

Pode ser difícil automatizar testes de jogos quando esses aplicativos são criados em diferentes frameworks de IU. Os testes de loop de jogo permitem que você integre seus testes nativos com o Test Lab e os execute facilmente nos dispositivos selecionados. Um teste de loop de jogo executa seu teste por meio do app de jogos e simula as ações de um jogador real. Este guia mostra como executar um teste de loop de jogo e, em seguida, visualizar e gerenciar os resultados do teste no Console do Firebase.

Dependendo do mecanismo do jogo, é possível implementar testes com um ou vários loops. Um loop é uma execução completa ou parcial do teste no seu app de jogos. Os loops de jogo podem ser usados para:

  • Executar uma fase do jogo como se ela fosse jogada por um usuário final. É possível criar um script para os comandos do usuário, deixar o usuário inativo ou substituí-lo por uma IA se fizer sentido no seu jogo (por exemplo, digamos que você tenha um aplicativo de jogo de corrida e já tenha uma IA implementada). É fácil colocar um piloto de IA no comando do usuário.
  • Executar o jogo na configuração de qualidade mais alta para ver se os dispositivos a suportam.
  • Executar um teste técnico que inclua a compilação de múltiplos sombreadores, a execução deles, a verificação da saída esperada etc.

É possível executar um teste de loop de jogo em um único dispositivo de teste, um conjunto de dispositivos de teste ou no Test Lab. No entanto, não recomendamos a execução de testes de loop de jogo em dispositivos virtuais porque eles têm taxas de quadros gráficos mais baixas do que as dos dispositivos físicos.

Antes de começar

Para implementar um teste, você precisa primeiro configurar seu app para testes de loop de jogo.

  1. No manifesto do app, adicione um novo filtro de intent à sua atividade:

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

    Isso permite que o Test Lab inicie seu jogo acionando-o com uma intent específica.

  2. No seu código (recomendamos na declaração do método onCreate), adicione o seguinte:

    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
    }

    Kotlin+KTX

    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
    }

    Isso permite que sua atividade verifique a intent que a inicia. Também é possível adicionar esse código mais tarde, se preferir (por exemplo, depois de carregar inicialmente o mecanismo do jogo).

  3. Recomendado: no final do teste, adicione:

    Java

    yourActivity.finish();

    Kotlin+KTX

    yourActivity.finish()

    Isso fecha seu app quando o teste de loop de jogo é concluído. O teste depende do framework da IU do seu app para iniciar o próximo loop. O fechamento do app informa que o teste foi concluído.

Criar e executar um teste de loop de jogo

Depois de configurar seu app para testes de loop de jogo, será possível criar um teste imediatamente e executá-lo no app de jogos. É possível optar por executar um teste no Test Lab usando o Console do Firebase ou a interface de linha de comando (CLI) do gcloud ou em um dispositivo local usando o Test Loop Manager.

Executar em um dispositivo local

O Test Loop Manager do Test Lab é um app de código aberto que ajuda a integrar testes de loop de jogo e a executá-los nos seus dispositivos locais. Ele também permite que sua equipe de controle de qualidade execute os mesmos loops de jogo nos dispositivos.

Para executar um teste em um dispositivo local usando o Test Loop Manager:

  1. Faça o download do Test Loop Manager em um smartphone ou tablet e instale-o executando:
    adb install testloopmanager.apk
  2. No seu dispositivo, abra o aplicativo Test Loop Apps no smartphone ou tablet. O app exibe uma lista de apps no seu dispositivo que podem ser executados com loops de jogo. Caso você não veja seu app de jogos aqui, verifique se o filtro de intent corresponde ao descrito na primeira etapa da seção Antes de começar.
  3. Selecione seu app de jogos e o número de loops que você quer executar. Observação: nesta etapa, é possível optar por executar um subconjunto de loops em vez de apenas um loop. Para mais informações sobre como executar vários loops de uma só vez, consulte Recursos opcionais.
  4. Clique em Executar teste. Seu teste começa a ser executado imediatamente.

Executar no Test Lab

É possível executar um teste de loop de jogo no Test Lab usando o Console do Firebase ou a CLI do gcloud. Antes de começar, abra o Console do Firebase e crie um projeto.

Usar o Console do Firebase

  1. No Console do Firebase, clique em Test Lab à esquerda.
  2. Clique em Execute seu primeiro teste ou em Executar um teste, se ele já tiver sido executado no seu projeto.
  3. Selecione Loop de jogo como tipo de teste e clique em Continuar.
  4. Clique em Procurar e navegue até o arquivo .apk do app. Observação: nesta etapa, é possível optar por executar um subconjunto de loops em vez de apenas um loop. Para mais informações sobre como executar vários loops de uma só vez, consulte Recursos opcionais.
  5. Clique em Continuar.
  6. Selecione os dispositivos físicos a serem usados para testar seu app.
  7. Clique em Iniciar testes.

Para mais informações sobre os primeiros passos com o Console do Firebase, consulte Primeiros passos com o Firebase Test Lab no Console do Firebase.

Usar a linha de comando (CLI) do gcloud

  1. Faça o download e instale o SDK do Google Cloud, caso ainda não tenha feito isso.

  2. Faça login na CLI do gcloud usando sua Conta do Google:

    gcloud auth login

  3. Defina seu projeto do Firebase no gcloud, em que PROJECT_ID é o ID do projeto do Firebase:

    gcloud config set project PROJECT_ID
    
  4. Execute seu primeiro teste:

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

Para mais informações sobre os primeiros passos com a CLI do gcloud, consulte Primeiros passos com o Firebase Test Lab na linha de comando gcloud.

Recursos opcionais

O Test Lab oferece vários recursos opcionais que permitem personalizar ainda mais seus testes, incluindo a capacidade de gravar dados de saída, compatibilidade com vários loops de jogos e rótulos para loops relacionados.

Gravar dados de saída

Seu teste de loop de jogo pode gravar a saída em um arquivo especificado no método launchIntent.getData(). Depois de executar um teste, será possível acessar esses dados de saída na seção Test Lab do Console do Firebase (consulte o exemplo de arquivo de saída de teste do loop de jogo).

O Test Lab segue as práticas recomendadas para compartilhar um arquivo entre apps descritos em Como compartilhar um arquivo. No método onCreate() da atividade, em que a intent está localizada, é possível verificar o arquivo de saída de dados executando o seguinte código:

Java

Intent launchIntent = getIntent();
Uri logFile = launchIntent.getData();
if (logFile != null) {
    Log.i(TAG, "Log file " + logFile.getEncodedPath());
    // ...
}

Kotlin+KTX

val launchIntent = intent
val logFile = launchIntent.data
logFile?.let {
    Log.i(TAG, "Log file ${it.encodedPath}")
    // ...
}

Se você quiser gravar no arquivo do lado do C++ do seu app de jogo, transmita o descritor do arquivo em vez do caminho do arquivo:

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

Kotlin+KTX

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

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

Exemplo de arquivo de saída

Use os arquivos de dados de saída (formatados como no exemplo abaixo) para exibir os resultados do teste de loop de jogo na seção Test Lab do Console do Firebase. As áreas mostradas como /.../ podem conter todos os campos personalizados necessários, desde que não entrem em conflito com os nomes de outros campos usados neste arquivo:

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

Múltiplos loops de jogo

Pode ser útil executar vários loops de jogo no seu app. Um loop é uma execução completa do seu app de jogo do início ao fim. Por exemplo, se o seu jogo tem vários níveis, é possível ter um loop de jogo para iniciar cada um deles em vez de ter somente um que itere por todos eles. Dessa forma, se o app falhar no nível 32, será possível iniciar esse loop de jogo diretamente para reproduzir a falha e testar as correções de bugs.

Para permitir que seu aplicativo execute vários loops de uma só vez:

  • Se você estiver executando um teste com o Test Loop Manager:

    1. Adicione a seguinte linha ao manifesto do app, dentro do elemento <application>:

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

      Esse intent de inicialização contém o loop de destino como um parâmetro inteiro. No campo android:value, é possível especificar um número inteiro de 1 a 1024 (o número máximo de loops permitidos para um único teste). Observe que os loops são indexados a partir de 1, não de 0.

    2. No aplicativo Test Loop Manager, é exibida uma tela de seleção que permite selecionar quais loops você quer executar. Se você selecionar vários loops, cada loop será iniciado em sequência após a conclusão do loop anterior.

  • Se você estiver executando um teste com o Console do Firebase, insira uma lista ou um intervalo de números de loop no campo Cenários.

  • Se você estiver executando um teste com a CLI do gcloud, especifique uma lista de números de loop usando a sinalização --scenario-numbers. Por exemplo, --scenario-numbers=1,3,5 executa loops 1, 3 e 5.

  • Se você estiver escrevendo C++ e quiser alterar o comportamento do seu loop, transmita o seguinte extra para o código C++ nativo:

    Java

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

    Kotlin+KTX

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

    Agora é possível alterar o comportamento do seu loop com base no valor int resultante.

Marcar loops de jogo

Ao rotular seus loops de jogo com um ou mais rótulos de cenário, você e sua equipe de controle de qualidade podem lançar facilmente um conjunto de loops de jogo relacionados (por exemplo, "todos os loops de jogo de compatibilidade") e testá-los em uma única matriz. É possível criar seus próprios rótulos ou usar os predefinidos oferecidos pelo Test Lab:

  • com.google.test.loops.player_experience: loops usados para reproduzir a experiência de um usuário real ao jogar o jogo. O objetivo do teste com esses loops é localizar os problemas que o usuário encontraria durante o jogo.
  • com.google.test.loops.gpu_compatibility: loops usados para testar problemas relacionados à GPU. O objetivo do teste com esses loops é executar o código GPU que poderia não ser executado corretamente na produção e, dessa forma, evidenciar problemas com hardware e drivers.
  • com.google.test.loops.compatibility: loops usados para testar uma ampla variedade de problemas de compatibilidade, incluindo problemas de E/S e de OpenSSL.
  • com.google.test.loops.performance: loops usados para testar o desempenho do dispositivo. Por exemplo, um jogo pode ser executado com as configurações mais complexas de gráfico para ver como um novo dispositivo se comporta.

Para permitir que seu aplicativo execute loops com o mesmo rótulo:

  • Se você estiver executando um teste com o Test Loop Manager:

    1. No manifesto do seu app, adicione a seguinte linha de metadados e substitua LABEL_NAME por um rótulo de sua escolha:

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

      No campo android:value, é possível especificar um intervalo ou um conjunto de números inteiros de 1 a 1024 (o número máximo de loops permitidos para um único teste) que representam os loops que você quer rotular. Observe que os loops são indexados a partir de 1, não de 0. Por exemplo, android:value="1,3-5" aplica LABEL_NAME aos loops 1, 3, 4 e 5.

    2. No aplicativo Test Loop Manager, insira um ou mais rótulos no campo Rótulos.

  • Se você estiver executando um teste com o Console do Firebase, insira um ou mais rótulos no campo Rótulos.

  • Se você estiver executando um teste com a CLI do gcloud, especifique um ou mais rótulos de cenário usando a sinalização --scenario-labels (por exemplo, --scenario-labels=performance,gpu).

Suporte ao licenciamento de apps

O Test Lab é compatível com apps que usam o serviço de licenciamento de apps oferecido pelo Google Play. Para verificar o licenciamento ao testar seu app com o Test Lab, publique o app no canal de produção na Play Store. Para testar seu app no Canal Beta ou Alfa usando o Test Lab, remova a verificação de licenciamento antes de fazer o upload do seu app no Test Lab.

Problemas conhecidos

Os testes de loop de jogo no Test Lab têm os seguintes problemas conhecidos:

  • Algumas falhas não são compatíveis com backtraces. Por exemplo, em algumas versões, a saída do processo debuggerd pode ser suprimida usando prctl(PR_SET_DUMPABLE, 0). Para saber mais, veja debuggerd.
  • A API nível 19 não é compatível atualmente devido a erros de permissão de arquivo.