Mapeamento de teste

Esta é uma breve introdução ao mapeamento de testes e uma explicação sobre como começar a configurar testes no Android Open Source Project (AOSP).

Sobre o mapeamento de testes

O mapeamento de teste é uma abordagem baseada em Gerrit que permite que os desenvolvedores criem regras de teste pré-envio e pós-envio diretamente na árvore de origem do Android e deixem as decisões de filiais e dispositivos a serem testadas na infraestrutura de teste. As definições de mapeamento de teste são arquivos JSON com o nome TEST_MAPPING que podem ser colocados em qualquer diretório de origem.

O Atest pode usar os arquivos TEST_MAPPING para executar testes de pré-envio nos diretórios associados. Com o mapeamento de teste, é possível adicionar o mesmo conjunto de testes às verificações de pré-envio com uma mudança mínima na árvore de origem do Android.

Confira estes exemplos:

O mapeamento de testes depende do Trade Federation (TF) para execução de testes e geração de relatórios de resultados.

Definir grupos de teste

Teste os grupos de mapeamento com um grupo de teste. O nome de um grupo de teste pode ser qualquer string. Por exemplo, presubmit pode ser o nome de um grupo de testes a serem executados ao validar mudanças. E postsubmit podem ser os testes usados para validar os builds depois que as alterações são mescladas.

Regras de script de compilação do pacote

Para que o Trade Federation test harness execute módulos de teste para um determinado build, esses módulos precisam ter um test_suites definido para Soong ou um LOCAL_COMPATIBILITY_SUITE definido para o Make em uma destas duas suítes:

  • general-tests serve para testes que não dependem de recursos específicos do dispositivo, como hardware específico do fornecedor que a maioria dos dispositivos não tem. A maioria dos testes precisa estar no pacote general-tests, mesmo que sejam específicos para uma ABI ou para recursos de bit ou hardware, como HWASan (há um alvo test_suites separado para cada ABI), e mesmo que precisem ser executados em um dispositivo.
  • device-tests é para testes que dependem de recursos específicos do dispositivo. Normalmente, esses testes são encontrados em vendor/. Específico do dispositivo se refere apenas aos recursos exclusivos de um dispositivo. Isso se aplica a testes JUnit e GTest (que geralmente precisam ser marcados como general-tests, mesmo que sejam específicos da ABI).

Exemplos:

Android.bp: test_suites: ["general-tests"],
Android.mk: LOCAL_COMPATIBILITY_SUITE := general-tests

Configurar testes para execução em um conjunto de testes

Para que um teste seja executado em um pacote de teste, ele precisa:

  • Não pode ter nenhum provedor de build.
  • É necessário limpar o teste depois que ele for concluído, por exemplo, excluindo todos os arquivos temporários gerados durante o teste.
  • Mude as configurações do sistema para o valor padrão ou original.
  • Não deve presumir que um dispositivo está em um determinado estado, por exemplo, pronto para root. A maioria dos testes não exige privilégios de root para execução. Se um teste precisar exigir raiz, ele precisará especificar isso com RootTargetPreparer no AndroidTest.xml, como no exemplo a seguir:

    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
    

Criar arquivos de mapeamento de teste

Para o diretório que exige cobertura de teste, adicione um arquivo JSON TEST_MAPPING semelhante ao exemplo. Essas regras garantem que os testes sejam executados nas verificações antes do envio quando algum arquivo é tocado nesse diretório ou em qualquer um dos subdiretórios.

Siga um exemplo

Confira um exemplo de arquivo TEST_MAPPING (ele está no formato JSON, mas com comentários suportados):

{
  "presubmit": [
    // JUnit test with options and file patterns.
    {
      "name": "CtsWindowManagerDeviceTestCases",
      "options": [
        {
          "include-annotation": "android.platform.test.annotations.RequiresDevice"
        }
      ],
      "file_patterns": ["(/|^)Window[^/]*\\.java", "(/|^)Activity[^/]*\\.java"]
    },
    // Device-side GTest with options.
    {
      "name" : "hello_world_test",
      "options": [
        {
          "native-test-flag": "\"servicename1 servicename2\""
        },
        {
          "native-test-timeout": "6000"
        }
      ]
    }
    // Host-side GTest.
    {
      "name" : "net_test_avrcp",
      "host" : true
    }
  ],
  "postsubmit": [
    {
      "name": "CtsDeqpTestCases",
      "options": [
        {
          // Use regex in include-filter which is supported in AndroidJUnitTest
          "include-filter": "dEQP-EGL.functional.color_clears.*"
        }
      ]
    }
  ],
  "imports": [
    {
      "path": "frameworks/base/services/core/java/com/android/server/am"
    }
  ]
}

Definir atributos

No exemplo, presubmit e postsubmit são os nomes de cada grupo de teste. Consulte Definir grupos de teste para mais informações sobre grupos de teste.

É possível definir o nome do módulo de teste ou do teste de integração da Trade Federation (caminho de recurso para o arquivo XML de teste, por exemplo, uiautomator/uiautomator-demo) no valor do atributo name. O campo name não pode usar a classe name ou o método de teste name. Para restringir os testes a serem executados, use opções como include-filter. Consulte a amostra de uso de include-filter.

A configuração host de um teste indica se ele é executado sem dispositivo no host ou não. O valor padrão é false, o que significa que o teste precisa de um dispositivo para ser executado. Os tipos de teste com suporte são HostGTest para binários do GTest e HostTest para testes JUnit.

O atributo file_patterns permite definir uma lista de strings de expressão regular para corresponder ao caminho relativo de qualquer arquivo de código-fonte (relativo ao diretório que contém o arquivo TEST_MAPPING). No exemplo, o teste CtsWindowManagerDeviceTestCases é executado no pré-envio somente quando um arquivo Java começa com Window ou Activity, que existe no mesmo diretório que o arquivo TEST_MAPPING ou qualquer um dos subdiretórios dele. As barras invertidas (\) precisam ser escritas com escape porque estão em um arquivo JSON.

O atributo imports permite incluir testes em outros arquivos TEST_MAPPING sem copiar o conteúdo. Os arquivos TEST_MAPPING nos diretórios pais do caminho importado também são incluídos. O mapeamento de teste permite importações aninhadas. Isso significa que dois arquivos TEST_MAPPING podem importar uns aos outros, e o mapeamento de teste pode mesclar os testes incluídos.

O atributo options contém outras opções de linha de comando do Tradefed.

Para conferir uma lista completa de opções disponíveis para um determinado teste, execute:

tradefed.sh run commandAndExit [test_module] --help

Consulte Processamento de opções no Tradefed para mais detalhes sobre como as opções funcionam.

Executar testes com o Atest

Para executar as regras de teste antes do envio localmente:

  1. Acesse o diretório que contém o arquivo TEST_MAPPING.
  2. Execute o comando:

    atest
    

Todos os testes pré-envio configurados nos arquivos TEST_MAPPING do diretório atual e nos diretórios pais são executados. O Atest localiza e executa dois testes para pré-envio (A e B).

Essa é a maneira mais simples de executar testes de pré-envio em arquivos TEST_MAPPING no diretório de trabalho atual (CWD, na sigla em inglês) e nos diretórios pai. O Atest localiza e usa o arquivo TEST_MAPPING no CWD e em todos os diretórios pai.

Código-fonte da estrutura

Este exemplo mostra como configurar arquivos TEST_MAPPING na árvore de origem:

src
├── project_1
│   └── TEST_MAPPING
├── project_2
│   └── TEST_MAPPING
└── TEST_MAPPING

Conteúdo de src/TEST_MAPPING:

{
  "presubmit": [
    {
      "name": "A"
    }
  ]
}

Conteúdo de src/project_1/TEST_MAPPING:

{
  "presubmit": [
    {
      "name": "B"
    }
  ],
  "postsubmit": [
    {
      "name": "C"
    }
  ],
  "other_group": [
    {
      "name": "X"
    }
  ]}

Conteúdo de src/project_2/TEST_MAPPING:

{
  "presubmit": [
    {
      "name": "D"
    }
  ],
  "import": [
    {
      "path": "src/project_1"
    }
  ]}

Especificar diretórios de destino

É possível especificar um diretório de destino para executar testes em arquivos TEST_MAPPING nesse diretório. O comando a seguir executa dois testes (A, B):

atest --test-mapping src/project_1

Executar regras de teste pós-envio

Também é possível usar esse comando para executar as regras de teste pós-envio definidas em TEST_MAPPING em src_path (padrão para CWD) e nos diretórios principais:

atest [--test-mapping] [src_path]:postsubmit

Executar apenas testes que não exigem dispositivo

É possível usar a opção --host para o Atest para executar apenas os testes configurados no host que não exigem um dispositivo. Sem essa opção, o Atest executa os dois testes, aqueles que exigem um dispositivo e aqueles executados em um host que não exigem um dispositivo. Os testes são executados em duas suítes separadas:

atest [--test-mapping] --host

Identificar grupos de teste

Você pode especificar grupos de teste no comando "Atest". O comando a seguir executa todos os testes postsubmit relacionados a arquivos no diretório src/project_1, que contém apenas um teste (C).

Ou use :all para executar todos os testes, independentemente do grupo. O comando a seguir executa quatro testes (A, B, C, X):

atest --test-mapping src/project_1:all

Incluir subdiretórios

Por padrão, a execução de testes em TEST_MAPPING com o Atest executa apenas testes de pré-envio configurados no arquivo TEST_MAPPING no CWD (ou diretório especificado) e nos diretórios pais. Se você quiser executar testes em todos os arquivos TEST_MAPPING nos subdiretórios, use a opção --include-subdir para forçar o Atest a incluir esses testes também.

atest --include-subdir

Sem a opção --include-subdir, o Atest executa apenas o teste A. Com a opção --include-subdir, o Atest executa dois testes (A, B).

Compatível com comentário no nível da linha

É possível adicionar um comentário no formato // no nível da linha para detalhar o arquivo TEST_MAPPING com uma descrição da configuração a seguir. O ATest e a Trade Federation pré-processam TEST_MAPPING em um formato JSON válido sem comentários. Para manter o arquivo JSON limpo, apenas o comentário de formato // no nível da linha é aceito.

Exemplo:

{
  // For presubmit test group.
  "presubmit": [
    {
      // Run test on module A.
      "name": "A"
    }
  ]
}