Skip to main content

Test de requêtes personnalisées

Vous pouvez configurer des tests pour vos requêtes CodeQL pour garantir qu’elles continuent à retourner les résultats attendus avec les nouvelles versions de CodeQL CLI.

Qui peut utiliser cette fonctionnalité ?

GitHub CodeQL est concédé sous licence par utilisateur lors de l’installation. Vous pouvez utiliser CodeQL uniquement pour certaines tâches soumises aux restrictions de licence. Pour plus d’informations, consultez « À propos de CodeQL CLI ».

Si vous disposez d’une licence GitHub Advanced Security, vous pouvez utiliser CodeQL pour l’analyse automatisée, l’intégration continue et la livraison continue. Pour plus d’informations, consultez « À propos de GitHub Advanced Security ».

À 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épertoire src. 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 bloc dependencies 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 est MyJavaQuery.ql, le fichier de résultats attendu doit être MyJavaQuery.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 est 1. Vous pouvez spécifier d’autres threads pour accélérer l’exécution des requêtes. Le fait de spécifier 0 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

  1. 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."
    
  2. 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.

  3. 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 fichier qlpack.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 ».

  4. 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 que my-github-user/my-query-tests dépend de my-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 ligne tests: . déclare que tous les fichiers .ql du pack doivent être exécutés en tant que tests quand codeql 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: .
    
  5. Exécutez codeql pack install à la racine du répertoire de test. 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.

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

  7. Dans le nouveau répertoire, créez EmptyThen.qlref pour définir l’emplacement de EmptyThen.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 pour my-query-tests. Par conséquent, EmptyThen.qlref doit simplement contenir EmptyThen.ql.

  8. Créez un extrait de code à tester. Le code Java suivant contient une instruction if vide sur la troisième ligne. Enregistrez-le dans custom-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 :

  1. Recherche un test dans le répertoire EmptyThen.

  2. Extrait une base de données CodeQL à partir des fichiers .java stockés dans le répertoire EmptyThen.

  3. 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 ».

  4. Exécute le test en exécutant la requête et en générant un fichier de résultats EmptyThen.actual.

  5. Recherche un fichier EmptyThen.expected à comparer avec le fichier de résultats .actual.

  6. 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écutant test 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 « Analyse de vos projets » dans CodeQL pour obtenir de l’aide sur VS Code.

Pour aller plus loin