Sobre o teste de consultas personalizadas
O CodeQL fornece uma estrutura de teste simples para teste de regressão automatizado de consultas. Teste as consultas para garantir que elas se comportem conforme o esperado.
Durante um teste de consulta, o CodeQL compara os resultados que o usuário espera que a consulta produza com aqueles que realmente foram produzidos. Se os resultados esperados e reais forem diferentes, o teste de consulta falhará. Para corrigir o teste, você deve iterar na consulta e nos resultados esperados até que os resultados reais e os resultados esperados correspondam exatamente. Este tópico mostra como criar arquivos de teste e executar testes neles usando o subcomando test run
.
Como configurar um pacote do CodeQL de teste para consultas personalizadas
Todos os testes do CodeQL precisam ser armazenados em um pacote especial de "teste" do CodeQL. Ou seja, um diretório de arquivos de teste com um arquivo qlpack.yml
que defina:
name: <name-of-test-pack>
version: 0.0.0
dependencies:
<codeql-libraries-and-queries-to-test>: "*"
extractor: <language-of-code-to-test>
O valor dependencies
especifica os pacotes do CodeQL que contêm as consultas a serem testadas.
Normalmente, esses pacotes serão resolvidos na origem e, portanto, não é necessário especificar uma versão fixa do pacote. O extractor
define qual linguagem a CLI usará para criar bancos de dados de teste por meio dos arquivos de código armazenados nesse pacote do CodeQL. Para saber mais, confira Como personalizar a análise com pacotes CodeQL.
Você pode achar útil examinar a forma como os testes de consulta são organizados no repositório do CodeQL. Cada linguagem tem um diretório src
, o ql/<language>/ql/src
, que contém bibliotecas e consultas para analisar bases de código. Junto com o diretório src
, há um diretório test
com testes para essas bibliotecas e consultas.
Cada diretório test
é configurado como um pacote do CodeQL de teste com dois subdiretórios:
query-tests
uma série de subdiretórios com testes para consultas armazenadas no diretóriosrc
. Cada subdiretório contém código de teste e um arquivo de referência QL que especifica a consulta a ser testada.library-tests
uma série de subdiretórios com testes para arquivos de biblioteca QL. Cada subdiretório contém códigos de teste e consultas que foram gravadas como testes de unidade para uma biblioteca.
Depois de criar o arquivo qlpack.yml
, é necessário baixar todas as dependências e disponibilizá-las para a CLI. Faça isso executando o seguinte comando no mesmo diretório do arquivo qlpack.yml
:
codeql pack install
Isso gera um arquivo codeql-pack.lock.yml
que especifica todas as dependências transitivas necessárias para executar consultas neste pacote. É preciso fazer check-in desse arquivo no controle do código-fonte.
Como configura os arquivos de teste para uma consulta
Para cada consulta que deseja testar, você deve criar um subdiretório no pacote do CodeQL. Depois, adicione os seguintes arquivos ao subdiretório antes de executar o comando de teste:
-
Um arquivo de referência de consulta (arquivo
.qlref
) que define o local da consulta a ser testada. O local é definido em relação à raiz do pacote do CodeQL que contém a consulta. Normalmente, esse é um pacote do CodeQL especificado no blocodependencies
do pacote de teste. Para saber mais, confira Arquivos de referência de consulta.Você não precisará adicionar um arquivo de referência de consulta se a consulta que deseja testar estiver armazenada no diretório de teste, mas uma boa prática é armazenar as consultas separadamente dos testes. A única exceção são os testes de unidade para bibliotecas QL, que costumam ser armazenados em pacotes de teste, separados das consultas que geram alertas ou caminhos.
-
O código de exemplo no qual você deseja executar a consulta. Isso deve consistir em um ou mais arquivos contendo exemplos de código que a consulta foi projetada para identificar.
Você também pode definir os resultados esperados ao executar a consulta no código de exemplo, criando um arquivo com a extensão .expected
. Como alternativa, você pode deixar o comando de teste para criar o arquivo .expected
.
Para obter um exemplo mostrando como criar e testar uma consulta, veja o exemplo abaixo.
Note
Os arquivos .ql
, .qlref
e .expected
precisam ter nomes consistentes:
- Se você quiser especificar diretamente o próprio arquivo
.ql
no comando de teste, ele precisará ter o mesmo nome base que o arquivo correspondente.expected
. Por exemplo, se a consulta forMyJavaQuery.ql
, o arquivo de resultados esperado precisará serMyJavaQuery.expected
. - Se você quiser especificar um arquivo
.qlref
no comando, ele deverá ter o mesmo nome base que o arquivo correspondente.expected
, mas a consulta em si poderá ter um nome diferente. - Os nomes dos arquivos de código de exemplo não precisam ser consistentes com os outros arquivos de teste. Todos os arquivos de código de exemplo encontrados ao lado do arquivo
.qlref
(ou.ql
) e nos subdiretórios serão usados para criar um banco de dados de teste. Portanto, para simplificar, recomendamos que você não salve os arquivos de teste em diretórios que sejam ancestrais uns dos outros.
Em execuçãocodeql test run
Os testes de consulta do CodeQL são realizados executando o seguinte comando:
codeql test run <test|dir>
O argumento <test|dir>
pode ser um ou mais destes:
- Caminho para um arquivo
.ql
. - Caminho para um arquivo
.qlref
que faz referência a um arquivo.ql
. - Caminho para um diretório que será pesquisado recursivamente para arquivos
.ql
e.qlref
.
Você também pode especificar:
--threads:
opcionalmente, o número de threads a serem usados ao executar consultas. A opção padrão é1
. Você pode especificar mais threads para acelerar a execução da consulta. A especificação0
corresponde o número de threads ao número de processadores lógicos.
Para obter detalhes completos de todas as opções que você pode usar ao testar consultas, confira test run.
Exemplo
O exemplo a seguir mostra como configurar um teste para uma consulta que pesquisa código Java em busca de instruções if
que tenham blocos vazios then
. Ele inclui etapas para adicionar a consulta personalizada e os arquivos de teste correspondentes a fim de separar os pacotes do CodeQL fora do check-out do repositório do CodeQL. Isso garante que, ao atualizar as bibliotecas do CodeQL ou fazer check-out de um branch diferente, você não substitua as consultas e os testes personalizados.
Preparar uma consulta e arquivos de teste
-
Desenvolva a consulta. Por exemplo, a consulta simples a seguir localiza blocos vazios
then
no código Java:import java from IfStmt ifstmt where ifstmt.getThen() instanceof EmptyStmt select ifstmt, "This if statement has an empty then."
-
Salve a consulta em um arquivo chamado
EmptyThen.ql
em um diretório com outras consultas personalizadas. Por exemplo,custom-queries/java/queries/EmptyThen.ql
. -
Se você ainda não adicionou as consultas personalizadas a um pacote do CodeQL, crie um pacote do CodeQL agora. Por exemplo, se as consultas Java personalizadas forem armazenadas no
custom-queries/java/queries
, adicione um arquivoqlpack.yml
com o seguinte conteúdo acustom-queries/java/queries
:name: my-custom-queries dependencies: codeql/java-queries: "*"
Para obter mais informações sobre os pacotes do CodeQL, confira Como personalizar a análise com pacotes CodeQL.
-
Crie um pacote do CodeQL para testes Java adicionando um arquivo
qlpack.yml
com o seguinte conteúdo acustom-queries/java/tests
, atualizando odependencies
para corresponder ao nome do pacote do CodeQL de consultas personalizadas:O arquivo
qlpack.yml
a seguir informa quemy-github-user/my-query-tests
depende demy-github-user/my-custom-queries
em uma versão igual ou superior a 1.2.3 e inferior a 2.0.0. Ele também declara que a CLI deve usar o Javaextractor
ao criar bancos de dados de teste. A linhatests: .
declara que todos os arquivos.ql
no pacote devem ser executados como testes quandocodeql test run
é executado com a opção--strict-test-discovery
. Normalmente, os pacotes de teste não contêm uma propriedadeversion
. Isso impede que você os publique acidentalmente.name: my-github-user/my-query-tests dependencies: my-github-user/my-custom-queries: ^1.2.3 extractor: java tests: .
-
Execute
codeql pack install
na raiz do diretório de teste. Isso gera um arquivocodeql-pack.lock.yml
que especifica todas as dependências transitivas necessárias para executar consultas neste pacote. -
No pacote de teste Java, crie um diretório para conter os arquivos de teste associados a
EmptyThen.ql
. Por exemplo,custom-queries/java/tests/EmptyThen
. -
No novo diretório, crie
EmptyThen.qlref
para definir o local deEmptyThen.ql
. O caminho para a consulta precisa ser especificado em relação à raiz do pacote do CodeQL que contém a consulta. Nesse caso, a consulta está no diretório de nível superior do pacote do CodeQL chamadomy-custom-queries
, que é declarado como uma dependência paramy-query-tests
. Portanto,EmptyThen.qlref
deve simplesmente conterEmptyThen.ql
. -
Crie um snippet de código para ser testado. O código Java a seguir contém uma instrução vazia
if
na terceira linha. Salve-o emcustom-queries/java/tests/EmptyThen/Test.java
.class Test { public void problem(String arg) { if (arg.isEmpty()) ; { System.out.println("Empty argument"); } } public void good(String arg) { if (arg.isEmpty()) { System.out.println("Empty argument"); } } }
Executar o teste
Para executar o teste, acesse o diretório custom-queries
e execute codeql test run java/tests/EmptyThen
.
Ao ser executado, o teste:
-
Localizará um teste no diretório
EmptyThen
. -
Extrairá um banco de dados do CodeQL dos arquivos
.java
armazenados no diretórioEmptyThen
. -
Compilará a consulta referenciada pelo arquivo
EmptyThen.qlref
.Se essa etapa falhar, será porque a CLI não consegue encontrar o pacote personalizado do CodeQL. Execute novamente o comando e especifique o local do pacote personalizado do CodeQL, por exemplo:
codeql test run --search-path=java java/tests/EmptyThen
Para obter mais informações sobre como salvar o caminho de pesquisa como parte da configuração, confira Como especificar opções de comando em um arquivo de configuração do CodeQL.
-
Executará o teste executando a consulta e gerando um arquivo de resultados
EmptyThen.actual
. -
Verificará se há um arquivo
EmptyThen.expected
a ser comparado com o arquivo de resultados.actual
. -
Relatará os resultados do teste. Neste caso, uma falha:
0 tests passed; 1 tests failed:
. O teste falhou porque ainda não adicionamos um arquivo com os resultados esperados da consulta.
Exibir a saída do teste de consulta
O CodeQL gera os seguintes arquivos no diretório EmptyThen
:
EmptyThen.actual
, um arquivo que contém os resultados reais gerados pela consulta.EmptyThen.testproj
, um banco de dados de teste que você pode carregar no VS Code e usar para depurar testes com falha. Quando os testes são concluídos com êxito, esse banco de dados é excluído em uma etapa de manutenção. Você pode substituir essa etapa executandotest run
com a opção--keep-databases
.
Nesse caso, a falha era esperada e fácil de ser corrigida. Se você abrir o arquivo EmptyThen.actual
, verá os resultados do teste:
| Test.java:3:5:3:22 | stmt | This if statement has an empty then. |
Esse arquivo contém uma tabela, com uma coluna de local do resultado, juntamente com colunas separadas para cada parte da cláusula select
que a consulta gera.
Como os resultados são o esperado, podemos atualizar a extensão de arquivo para definir isso como o resultado esperado desse teste (EmptyThen.expected
).
Se você executar novamente o teste agora, a saída será semelhante, mas será concluída relatando: All 1 tests passed.
.
Se os resultados da consulta forem alterados, por exemplo, se você revisar a instrução select
da consulta, o teste falhará. Para resultados com falha, a saída da CLI inclui uma comparação unificada dos arquivos EmptyThen.expected
e EmptyThen.actual
.
Essas informações podem ser suficientes para depurar falhas de teste triviais.
Para falhas mais difíceis de serem depuradas, você pode importar EmptyThen.testproj
no CodeQL para VS Code, executar EmptyThen.ql
e exibir os resultados no código de exemplo Test.java
. Para saber mais, confira Como gerenciar bancos de dados do CodeQL.