À propos du test des requêtes personnalisées
CodeQL fournit un framework de test simple pour le test de régression automatisé des requêtes. Testez vos requêtes pour vous assurer qu’elles se comportent comme prévu.
Au cours d’un test de requête, CodeQL compare les résultats que l’utilisateur attend de la requête avec ceux réellement générés. Si les résultats attendus et réels diffèrent, le test de requête échoue. Pour corriger le test, vous devez effectuer une itération sur la requête et les résultats attendus jusqu’à ce que les résultats réels et les résultats attendus correspondent exactement. Cette rubrique vous montre comment créer des fichiers de test et exécuter des tests dessus à l’aide de la sous-commande test run
.
Configuration d’un pack de test CodeQL pour les requêtes personnalisées
Tous les tests CodeQL doivent être stockés dans un pack CodeQL de « test » spécial. Autrement dit, un répertoire pour les fichiers de test avec un fichier qlpack.yml
qui définit :
name: <name-of-test-pack>
version: 0.0.0
dependencies:
<codeql-libraries-and-queries-to-test>: "*"
extractor: <language-of-code-to-test>
La valeur dependencies
spécifie les packs CodeQL contenant des requêtes à tester.
En général, ces packs sont résolus à partir de la source. Il n’est donc pas nécessaire de spécifier une version fixe du pack. L’extracteur (extractor
) définit le langage qu’utilisera l’interface CLI pour créer des bases de données de test à partir des fichiers de code stockés dans ce pack CodeQL. Pour plus d’informations, consultez « Personnalisation de l’analyse avec des packs CodeQL ».
Il peut être utile d’examiner la façon dont les tests de requêtes sont organisés dans le dépôt CodeQL. Chaque langage a un répertoire src
, ql/<language>/ql/src
, qui contient des bibliothèques et des requêtes pour l’analyse des codebases. En plus du répertoire src
, il existe un répertoire test
contenant des tests pour ces bibliothèques et requêtes.
Chaque répertoire test
est configuré en tant que pack de test CodeQL avec deux sous-répertoires :
query-tests
série de sous-répertoires contenant des tests pour les requêtes stockées dans le répertoiresrc
. Chaque sous-répertoire contient du code de test et un fichier de référence QL qui spécifie la requête à tester.library-tests
série de sous-répertoires contenant des tests pour les fichiers de bibliothèque QL. Chaque sous-répertoire contient du code et des requêtes de test qui ont été écrites en tant que tests unitaires pour une bibliothèque.
Après avoir créé le fichier qlpack.yml
, vous devez vous assurer que toutes les dépendances sont téléchargées et disponibles dans l’interface CLI. Pour ce faire, exécutez la commande suivante dans le même répertoire que le fichier qlpack.yml
:
codeql pack install
Cette commande génère un fichier codeql-pack.lock.yml
qui spécifie toutes les dépendances transitives requises pour exécuter les requêtes dans ce pack. Ce fichier doit être activé dans le contrôle de code source.
Configuration des fichiers de test pour une requête
Pour chaque requête que vous souhaitez tester, vous devez créer un sous-répertoire dans le pack de test CodeQL. Ajoutez ensuite les fichiers suivants au sous-répertoire avant d’exécuter la commande de test :
-
Fichier de référence de requête (fichier
.qlref
) définissant l’emplacement de la requête à tester. L’emplacement est défini par rapport à la racine du pack CodeQL qui contient la requête. En général, il s’agit d’un pack CodeQL spécifié dans le blocdependencies
du pack de test. Pour plus d’informations, consultez « Fichiers de référence de requête ».Vous n’avez pas besoin d’ajouter un fichier de référence de requête si la requête que vous souhaitez tester est stockée dans le répertoire de test, mais il est généralement recommandé de stocker les requêtes séparément des tests. La seule exception concerne les tests unitaires pour les bibliothèques QL, qui sont susceptibles d’être stockées dans des packs de test, indépendamment des requêtes qui génèrent des alertes ou des chemins.
-
Exemple de code sur lequel vous souhaitez exécuter votre requête. Il doit s’agir d’un ou de plusieurs fichiers contenant des exemples du code que la requête est chargée d’identifier.
Vous pouvez également définir les résultats attendus lorsque vous exécutez la requête sur l’exemple de code, en créant un fichier avec l’extension .expected
. Ou bien vous pouvez laisser la commande de test créer le fichier .expected
pour vous.
Pour obtenir un exemple montrant comment créer et tester une requête, consultez l’exemple ci-dessous.
Remarque : vos fichiers .ql
, .qlref
et .expected
doivent avoir des noms cohérents :
-
Si vous souhaitez spécifier directement le fichier
.ql
lui-même dans la commande de test, il doit avoir le même nom de base que le fichier.expected
correspondant. Par exemple, si la requête estMyJavaQuery.ql
, le fichier de résultats attendu doit êtreMyJavaQuery.expected
. -
Si vous souhaitez spécifier un fichier
.qlref
dans la commande, il doit avoir le même nom de base que le fichier.expected
correspondant, mais la requête elle-même peut avoir un nom différent. -
Les noms des exemples de fichiers de code n’ont pas besoin d’être cohérents avec les autres fichiers de test. Tous les exemples de fichiers de code se trouvant en regard du fichier
.qlref
(ou.ql
) et dans tous les sous-répertoires seront utilisés pour créer une base de données de test. Par conséquent, par souci de simplicité, nous vous recommandons de ne pas enregistrer les fichiers de test dans des répertoires qui sont des ancêtres les uns des autres.
En cours d’exécution codeql test run
Les tests de requêtes CodeQL sont exécutés en exécutant la commande suivante :
codeql test run <test|dir>
L’argument <test|dir>
peut être un ou plusieurs des éléments suivants :
- Chemin d’un fichier
.ql
. - Chemin d’un fichier
.qlref
qui fait référence à un fichier.ql
. - Chemin d’un répertoire dans lequel les fichiers
.ql
et.qlref
seront recherchés de manière récursive.
Vous pouvez également spécifier :
--threads:
Facultatif. Nombre de threads à utiliser lors de l’exécution de requêtes. L’option par défaut est1
. Vous pouvez spécifier d’autres threads pour accélérer l’exécution des requêtes. Le fait de spécifier0
définir le nombre de threads sur le nombre de processeurs logiques.
Pour des détails complets sur toutes les options que vous pouvez utiliser lors d’un test de requêtes, consultez « test run ».
Exemple
L’exemple suivant vous montre comment configurer un test pour une requête qui recherche dans du code Java des instructions if
qui ont des blocs then
vides. Il comprend des étapes permettant d’ajouter la requête personnalisée et les fichiers de test correspondants à des packs CodeQL distincts en dehors de votre extraction du dépôt CodeQL. Cela garantit que, lorsque vous mettez à jour les bibliothèques CodeQL ou que vous extrayez une autre branche, vous ne remplacerez pas vos requêtes et tests personnalisés.
Préparer une requête et des fichiers de test
-
Développez la requête. Par exemple, la requête simple suivante recherche les blocs
then
vides dans le code Java :import java from IfStmt ifstmt where ifstmt.getThen() instanceof EmptyStmt select ifstmt, "This if statement has an empty then."
-
Enregistrez la requête dans un fichier nommé
EmptyThen.ql
dans un répertoire avec vos autres requêtes personnalisées. Par exemple :custom-queries/java/queries/EmptyThen.ql
. -
Si vous n’avez pas encore ajouté vos requêtes personnalisées à un pack CodeQL, créez maintenant un pack CodeQL. Par exemple, si vos requêtes Java personnalisées sont stockées dans
custom-queries/java/queries
, ajoutez un fichierqlpack.yml
avec le contenu suivant àcustom-queries/java/queries
:name: my-custom-queries dependencies: codeql/java-queries: "*"
Pour plus d’informations sur les packs CodeQL, consultez « Personnalisation de l’analyse avec des packs CodeQL ».
-
Créez un pack CodeQL pour vos tests Java en ajoutant un fichier
qlpack.yml
avec le contenu suivant àcustom-queries/java/tests
, en mettant à jour les dépendances (dependencies
) pour qu’elles correspondent au nom de votre pack CodeQL de requêtes personnalisées :Le fichier
qlpack.yml
suivant indique quemy-github-user/my-query-tests
dépend demy-github-user/my-custom-queries
dont la version est ultérieure ou égale à 1.2.3 et antérieure à 2.0.0. Il déclare également que l’interface CLI doit utiliser l’extracteur (extractor
) Java lors de la création de bases de données de test. La lignetests: .
déclare que tous les fichiers.ql
du pack doivent être exécutés en tant que tests quandcodeql test run
est exécuté avec l’option--strict-test-discovery
. En général, les packs de test ne contiennent pas de propriétéversion
. Cela vous empêche de les publier accidentellement.name: my-github-user/my-query-tests dependencies: my-github-user/my-custom-queries: ^1.2.3 extractor: java-kotlin tests: .
-
Exécutez
codeql pack install
à la racine du répertoire de test. Cette commande génère un fichiercodeql-pack.lock.yml
qui spécifie toutes les dépendances transitives requises pour exécuter les requêtes dans ce pack. -
Dans le pack de test Java, créez un répertoire pour contenir les fichiers de test associés à
EmptyThen.ql
. Par exemple :custom-queries/java/tests/EmptyThen
. -
Dans le nouveau répertoire, créez
EmptyThen.qlref
pour définir l’emplacement deEmptyThen.ql
. Le chemin de la requête doit être spécifié par rapport à la racine du pack CodeQL qui contient la requête. Dans ce cas, la requête se trouve dans le répertoire de niveau supérieur du pack CodeQL nommémy-custom-queries
, qui est déclaré en tant que dépendance pourmy-query-tests
. Par conséquent,EmptyThen.qlref
doit simplement contenirEmptyThen.ql
. -
Créez un extrait de code à tester. Le code Java suivant contient une instruction
if
vide sur la troisième ligne. Enregistrez-le danscustom-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"); } } }
Exécuter le test
Pour exécuter le test, accédez au répertoire custom-queries
et exécutez codeql test run java/tests/EmptyThen
.
Lorsque le test s’exécute, il :
-
Recherche un test dans le répertoire
EmptyThen
. -
Extrait une base de données CodeQL à partir des fichiers
.java
stockés dans le répertoireEmptyThen
. -
Compile la requête référencée par le fichier
EmptyThen.qlref
.Si cette étape échoue, c’est parce que l’interface CLI ne trouve pas votre pack CodeQL personnalisé. Réexécutez la commande et spécifiez l’emplacement de votre pack CodeQL personnalisé ; par exemple :
codeql test run --search-path=java java/tests/EmptyThen
Pour plus d’informations sur l’enregistrement du chemin de recherche dans le cadre de votre configuration, consultez « Spécification des options de commande dans un fichier de configuration CodeQL ».
-
Exécute le test en exécutant la requête et en générant un fichier de résultats
EmptyThen.actual
. -
Recherche un fichier
EmptyThen.expected
à comparer avec le fichier de résultats.actual
. -
Signale les résultats du test ; dans le cas présent, un échec :
0 tests passed; 1 tests failed:
. Le test a échoué car nous n’avons pas encore ajouté un fichier avec les résultats attendus de la requête.
Afficher la sortie du test de requête
CodeQL génère les fichiers suivants dans le répertoire EmptyThen
:
EmptyThen.actual
, un fichier qui contient les résultats réels générés par la requête.EmptyThen.testproj
, une base de données de test que vous pouvez charger dans VS Code et utiliser pour déboguer les tests défaillants. Lorsque les tests se terminent correctement, cette base de données est supprimée dans une étape de nettoyage. Vous pouvez remplacer cette étape en exécutanttest run
avec l’option--keep-databases
.
Dans le cas présent, l’échec était attendu et est facile à corriger. Si vous ouvrez le fichier EmptyThen.actual
, vous pouvez voir les résultats du test :
| Test.java:3:5:3:22 | stmt | This if statement has an empty then. |
Ce fichier contient une table, avec une colonne pour l’emplacement du résultat, ainsi que des colonnes distinctes pour chaque partie de la clause select
que la requête génère.
Étant donné que les résultats correspondent à ce que nous attendions, nous pouvons mettre à jour l’extension de fichier pour la définir ce résultat comme résultat attendu pour ce test (EmptyThen.expected
).
Si vous réexécutez le test maintenant, la sortie sera similaire, mais il se terminera en signalant : All 1 tests passed.
.
Si les résultats de la requête changent, par exemple si vous révisez l’instruction select
pour la requête, le test échouera. Pour les résultats ayant échoué, la sortie de l’interface CLI inclut une différence unifiée des fichiers EmptyThen.expected
et EmptyThen.actual
.
Ces informations peuvent être suffisantes pour déboguer des échecs de test triviaux.
Pour les échecs qui sont plus difficiles à déboguer, vous pouvez importer EmptyThen.testproj
dans CodeQL pour VS Code, exécuter EmptyThen.ql
et afficher les résultats dans l’exemple de code Test.java
. Pour plus d’informations, consultez « Gestion de bases de données CodeQL ».