Types de tests
- Tests fonctionnels - un ensemble de requêtes et de scripts comprenant les sous-ensembles suivants, qui se recoupent
- Test rapide - le sous-ensemble minimal
- Tests sans état qui ne nécessitent pas de préremplir les bases de données avec des données
- Tests séquentiels qui ne peuvent pas être exécutés en parallèle
- Tests d’intégration, exécutés par
pytestdans un cluster - Tests unitaires
- Tests de performance
- Tests de build
- Sanitizers
- Fuzzers et quelques autres ; voir les sections ci-dessous.
Tests fonctionnels
./tests/queries.
Chaque test peut être de l’un des deux types suivants : .sql et .sh.
- Un test
.sqlest un simple script SQL envoyé àclickhouse-clientvia un pipe. - Un test
.shest un script exécuté directement.
.sh.
N’utilisez les tests .sh que lorsque vous devez tester une fonctionnalité qui ne peut pas l’être en SQL pur, par exemple pour envoyer des données d’entrée à clickhouse-client via un pipe ou pour tester clickhouse-local.
Une erreur fréquente lors des tests des types de données
DateTime et DateTime64 consiste à supposer que le serveur utilise un fuseau horaire spécifique (par ex. “UTC”). Ce n’est pas le cas : les fuseaux horaires utilisés dans les exécutions de tests CI
sont délibérément choisis de manière aléatoire. La solution de contournement la plus simple consiste à spécifier explicitement le fuseau horaire des valeurs de test, par ex. toDateTime64(val, 3, 'Europe/Amsterdam').Exécuter un test en local
01428_hash_set_nan_key, placez-vous dans le dossier du dépôt et lancez la commande suivante :
stderr et stdout) sont enregistrés dans les fichiers 01428_hash_set_nan_key.[stderr|stdout], situés à côté du test correspondant (pour queries/0_stateless/foo.sql, la sortie se trouvera dans queries/0_stateless/foo.stdout).
Consultez tests/clickhouse-test --help pour connaître toutes les options de clickhouse-test.
Vous pouvez exécuter tous les tests ou un sous-ensemble de tests en fournissant un filtre sur les noms de test : ./clickhouse-test substring.
Il existe également des options pour exécuter les tests en parallèle ou dans un ordre aléatoire.
Exécution des tests rapides
t3.2xlarge avec 100 Go d’espace de stockage.
- Installez les prérequis, puis reconnectez-vous.
- Récupérez le code source.
- Compilez le code et exécutez les “tests rapides”.
nohup ou disown pour qu’elle continue à s’exécuter après la perte de la connexion ssh.
Exécution des tests sans état
m7i.8xlarge avec 200 Go de stockage.
- Installez les prérequis, puis reconnectez-vous.
- Récupérez le code source.
- Compilez le code.
- Exécutez des tests sans état, qui peuvent s’exécuter en parallèle.
python -m ci.praktika run lancent un job d’intégration continue spécifique. Pour en savoir plus sur la CI de ClickHouse, consultez cette page.
Ajouter un nouveau test
.sql ou .sh dans le répertoire queries/0_stateless.
Générez ensuite le fichier .reference correspondant à l’aide de clickhouse-client < 12345_test.sql > 12345_test.reference ou de ./12345_test.sh > ./12345_test.reference.
Les tests doivent uniquement créer, supprimer, interroger, etc. des tables dans la base de données test, qui est automatiquement créée au préalable.
Il est possible d’utiliser des tables temporaires.
Pour retrouver localement le même environnement qu’en CI, installez les configurations de test (elles utiliseront une implémentation factice de ZooKeeper et ajusteront certains paramètres)
Les tests doivent être
- minimaux : ne créer que les tables, colonnes et la complexité strictement nécessaires,
- rapides : ne pas prendre plus de quelques secondes (mieux : moins d’une seconde),
- corrects et déterministes : échouer si et seulement si la fonctionnalité testée ne fonctionne pas,
- isolés/sans état : ne pas dépendre de l’environnement ni du moment de l’exécution,
- exhaustifs : couvrir les cas limites comme les zéros, les valeurs NULL, les ensembles vides et les exceptions (tests négatifs : utilisez pour cela la syntaxe
-- { serverError xyz }et-- { clientError xyz }), - nettoyer les tables à la fin du test (au cas où il resterait des éléments),
- s’assurer que les autres tests ne vérifient pas la même chose (autrement dit, faites d’abord un grep).
Tests basés sur des modèles avec Jinja
.sql peut être écrit sous forme de modèle Jinja2 en ajoutant le suffixe .j2 au nom du fichier ; ainsi, foo.sql devient foo.sql.j2. Avant d’exécuter le test, clickhouse-test génère à partir du modèle un script .sql ordinaire, puis exécute le résultat.
C’est utile lorsqu’un test répète la même requête avec de légères variations : une boucle génère les requêtes à partir d’un modèle compact au lieu de les écrire une par une à la main. Les constructions les plus couramment utilisées sont :
{% for ... %} ... {% endfor %}pour répéter un bloc,{{ expression }}pour insérer une valeur dans la sortie,-%}et{%-pour supprimer les espaces adjacents afin que le script généré reste propre.
<name>.reference contenant les résultats après expansion complète, soit sous la forme d’un modèle <name>.reference.j2, que clickhouse-test génère de la même façon avant la comparaison. Utilisez la forme avec modèle lorsque la sortie attendue suit elle aussi un schéma répétitif. Pour plus d’exemples, consultez les fichiers *.sql.j2 existants dans tests/queries/0_stateless/.
Limiter l’exécution des tests
.sql, les tags sont placés sur la première ligne sous forme de commentaire SQL :
.sh, les tags sont écrits sous forme de commentaire à la deuxième ligne :
| Nom du tag | Description | Exemple d’utilisation |
|---|---|---|
disabled | Le test n’est pas exécuté | |
long | Le temps d’exécution du test passe de 1 à 10 minutes | |
deadlock | Le test est exécuté en boucle pendant une longue période | |
race | Identique à deadlock. Préférez deadlock | |
shard | Le serveur doit écouter sur 127.0.0.* | |
distributed | Identique à shard. Préférez shard | |
global | Identique à shard. Préférez shard | |
zookeeper | Le test nécessite ZooKeeper ou ClickHouse Keeper pour s’exécuter | Le test utilise ReplicatedMergeTree |
replica | Identique à zookeeper. Préférez zookeeper | |
no-fasttest | Le test n’est pas exécuté dans test rapide | Le test utilise le moteur de table MySQL, qui est désactivé dans test rapide |
fasttest-only | Le test est exécuté uniquement dans test rapide | |
no-[asan, tsan, msan, ubsan] | Désactive les tests dans les builds avec des sanitizers | Le test est exécuté sous QEMU, qui ne fonctionne pas avec les sanitizers |
no-replicated-database | Désactive le test lorsque la base de données par défaut utilise ReplicatedDatabaseEngine | |
no-ordinary-database | Désactive le test lorsque le moteur de base de données par défaut est Ordinary | |
no-parallel | Empêche l’exécution d’autres tests en parallèle avec celui-ci | Le test lit les tables system et les invariants peuvent être rompus |
no-parallel-replicas | Désactive le test lorsque les répliques parallèles sont activées | |
no-debug | Désactive les tests dans les builds Debug | |
no-release | Désactive les tests dans les builds Release | |
no-darwin | Désactive le test sur macOS (Darwin) | Le test repose sur des fonctionnalités propres à Linux, comme les distributed queries, procfs ou le serveur HTTP |
no-polymorphic-parts, no-random-settings, no-random-merge-tree-settings, no-backward-compatibility-check, no-cpu-x86_64, no-cpu-aarch64, no-cpu-ppc64le, no-s3-storage.
En plus des paramètres ci-dessus, vous pouvez utiliser les flags USE_* de system.build_options pour indiquer l’utilisation de fonctionnalités ClickHouse particulières.
Par exemple, si votre test utilise une table MySQL, vous devez ajouter le tag use-mysql.
Définition des limites pour les paramètres aléatoires
.sh, les limites sont indiquées sous forme de commentaire sur la ligne à côté des tags, ou sur la deuxième ligne si aucun tag n’est spécifié :
.sql, les tags sont indiqués sous forme de commentaire SQL sur la ligne suivante ou sur la première ligne :
None pour l’autre.
Choisir le nom du test
00422_hash_function_constexpr.sql.
Pour choisir le préfixe, repérez le plus grand préfixe déjà présent dans le directory, puis incrémentez-le de un.
Vérifier qu’une erreur doit se produire
x.
S’il n’y a pas d’erreur, ou si l’erreur est différente, le test échouera.
Si vous voulez vous assurer qu’une erreur se produit côté client, utilisez plutôt l’annotation clientError.
Ne vérifiez pas le libellé exact du message d’erreur : il peut changer à l’avenir et faire échouer le test inutilement.
Vérifiez uniquement le code d’erreur.
Si le code d’erreur existant n’est pas assez précis pour vos besoins, envisagez d’en ajouter un nouveau.
Tester une requête distribuée
remote avec les adresses 127.0.0.{1..2} pour que le serveur puisse s’interroger lui-même, ou utiliser des clusters de test prédéfinis dans le fichier de configuration du serveur, comme test_shard_localhost.
N’oubliez pas d’ajouter les mots shard ou distributed au nom du test, afin qu’il soit exécuté par la CI dans les configurations appropriées, où le serveur est configuré pour prendre en charge les requêtes distribuées.
Travailler avec les fichiers temporaires
$CLICKHOUSE_TEST_UNIQUE_NAME afin de donner aux fichiers temporaires un nom unique pour le test en cours d’exécution.
Vous avez ainsi la garantie que le fichier que vous créez pendant la phase de préparation ou supprimez pendant la phase de nettoyage est uniquement utilisé par ce test, et non par un autre test exécuté en parallèle.
Bogues connus
tests/queries/bugs.
Ces tests seront déplacés vers tests/queries/0_stateless une fois les bogues corrigés.
Tests d’intégration
tests/integration/README.md pour savoir comment exécuter ces tests.
Notez que l’intégration de ClickHouse avec des drivers tiers n’est pas testée.
Par ailleurs, nous ne disposons actuellement pas de tests d’intégration pour nos drivers JDBC et ODBC.
Tests unitaires
ENABLE_TESTS.
Les tests unitaires (et les autres programmes de test) se trouvent dans les sous-répertoires tests du code.
Pour exécuter les tests unitaires, tapez ninja test.
Certains tests utilisent gtest, mais d’autres sont simplement des programmes qui renvoient un code de sortie non nul en cas d’échec.
Il n’est pas nécessaire d’avoir des tests unitaires si le code est déjà couvert par des tests fonctionnels (et les tests fonctionnels sont généralement beaucoup plus simples à utiliser).
Vous pouvez exécuter des vérifications gtest individuelles en appelant directement l’exécutable, par exemple :
Tests de performance
tests/performance/.
Chaque test est représenté par un fichier .xml contenant une description du cas de test.
Les tests sont exécutés avec l’outil docker/test/performance-comparison. Consultez le fichier README pour savoir comment l’utiliser.
Chaque test exécute une ou plusieurs requêtes (éventuellement avec des combinaisons de paramètres) en boucle.
Si vous souhaitez améliorer les performances de ClickHouse dans un scénario donné, et si les améliorations peuvent être observées sur des requêtes simples, il est fortement recommandé d’écrire un test de performance.
Il est également recommandé d’écrire des tests de performance lorsque vous ajoutez ou modifiez des fonctions SQL relativement isolées et peu obscures.
Il est toujours judicieux d’utiliser perf top ou d’autres outils perf pendant vos tests.
Outils et scripts de test
tests ne sont pas des tests à proprement parler, mais des outils de test.
Par exemple, pour Lexer, il existe un outil src/Parsers/tests/lexer qui se contente d’effectuer la tokenisation de stdin et d’écrire le résultat en couleur sur stdout.
Vous pouvez utiliser ce type d’outils comme exemples de code, ainsi que pour l’exploration et les tests manuels.
Tests divers
tests/external_models.
Ces tests ne sont plus mis à jour et doivent être portés dans les tests d’intégration.
Il existe un test distinct pour les insertions avec quorum.
Ce test exécute un cluster ClickHouse sur des serveurs distincts et émule divers cas de défaillance : partition réseau, perte de paquets (entre les nœuds ClickHouse, entre ClickHouse et ZooKeeper, entre le serveur ClickHouse et le client, etc.), kill -9, kill -STOP et kill -CONT, comme Jepsen. Ensuite, le test vérifie que toutes les insertions validées ont bien été écrites et qu’aucune des insertions rejetées ne l’a été.
Tests manuels
programs/clickhouse-server et lancez-le avec ./clickhouse-server. Par défaut, il utilisera la configuration (config.xml, users.xml et les fichiers contenus dans les répertoires config.d et users.d) du répertoire courant. Pour vous connecter au serveur ClickHouse, exécutez programs/clickhouse-client/clickhouse-client.
Notez que tous les outils ClickHouse (serveur, client, etc.) ne sont en réalité que des liens symboliques vers un unique binaire nommé clickhouse.
Vous trouverez ce binaire dans programs/clickhouse.
Tous les outils peuvent également être appelés sous la forme clickhouse tool au lieu de clickhouse-tool.
Vous pouvez également installer le paquet ClickHouse : soit la release stable depuis le dépôt ClickHouse, soit compiler vous-même le paquet avec ./release à la racine des sources ClickHouse.
Démarrez ensuite le serveur avec sudo clickhouse start (ou stop pour arrêter le serveur).
Consultez les logs dans /etc/clickhouse-server/clickhouse-server.log.
Si ClickHouse est déjà installé sur votre système, vous pouvez compiler un nouveau binaire clickhouse et remplacer le binaire existant :
clickhouse-server est déjà en cours d’exécution et que vous ne souhaitez pas l’arrêter, vous pouvez modifier les numéros de port dans votre config.xml (ou les surcharger dans un fichier du répertoire config.d), indiquer un chemin de données approprié, puis le lancer.
Le binaire clickhouse n’a pratiquement aucune dépendance et fonctionne sur un large éventail de distributions Linux.
Pour tester rapidement vos modifications sur un serveur de manière un peu sommaire, vous pouvez simplement copier votre binaire clickhouse fraîchement compilé sur votre serveur avec scp, puis l’exécuter comme dans les exemples ci-dessus.
Tests de build
- compilation croisée pour Darwin x86_64 (macOS)
- compilation croisée pour FreeBSD x86_64
- compilation croisée pour Linux AArch64
- build sur Ubuntu avec des bibliothèques issues des paquets système (déconseillé)
- build avec liaison dynamique des bibliothèques (déconseillé)
Tester la compatibilité du protocole
- si les données écrites par une ancienne version de ClickHouse peuvent être lues correctement par la nouvelle version ;
- si les requêtes distribuées fonctionnent dans un cluster utilisant différentes versions de ClickHouse.
Aide du compilateur
src) est compilé avec -Wall -Wextra -Werror, ainsi qu’avec quelques avertissements supplémentaires activés.
En revanche, ces options ne sont pas activées pour les bibliothèques tierces.
Clang propose encore davantage d’avertissements utiles : vous pouvez les passer en revue avec -Weverything et en retenir certains pour la compilation par défaut.
Nous utilisons toujours clang pour compiler ClickHouse, aussi bien en développement qu’en production.
Vous pouvez compiler sur votre propre machine en mode debug (pour économiser la batterie de votre ordinateur portable), mais notez que le compilateur peut générer davantage d’avertissements avec -O3 grâce à une meilleure analyse du flux de contrôle et à l’analyse interprocédurale.
Lors d’une compilation avec clang en mode debug, la version debug de libc++ est utilisée, ce qui permet de détecter davantage d’erreurs à l’exécution.
Sanitizers
Si le processus (serveur ClickHouse ou client) plante au démarrage lors d’une exécution en local, vous devrez peut-être désactiver la randomisation de l’espace d’adressage :
sudo sysctl kernel.randomize_va_space=0Sanitizer d’adresses
Thread sanitizer
Sanitizer mémoire
Sanitizer pour comportements indéfinis
Valgrind (memcheck)
re2, voir cet article.
Fuzzing
src/Parsers/fuzzers/lexer_fuzzer.cpp.
Les configurations, dictionnaires et corpus spécifiques à LibFuzzer sont stockés dans tests/fuzz.
Nous vous encourageons à écrire des tests de fuzzing pour chaque fonctionnalité qui traite des entrées utilisateur.
Les fuzzers ne sont pas compilés par défaut.
Pour compiler les fuzzers, les options -DENABLE_FUZZING=1 et -DENABLE_TESTS=1 doivent toutes deux être définies.
Nous recommandons de désactiver Jemalloc lors de la compilation des fuzzers.
La configuration utilisée pour intégrer le fuzzing de ClickHouse à
Google OSS-Fuzz se trouve dans docker/fuzz.
Nous utilisons également un test de fuzzing simple pour générer des requêtes SQL aléatoires et vérifier que le serveur ne s’arrête pas pendant leur exécution.
Vous pouvez le trouver dans 00746_sql_fuzzy.pl.
Ce test doit être exécuté en continu (pendant la nuit et au-delà).
Nous utilisons également un fuzzer de requêtes sophistiqué basé sur l’AST, capable de trouver un très grand nombre de cas limites.
Il effectue des permutations et des substitutions aléatoires dans l’AST des requêtes.
Il mémorise les nœuds AST des tests précédents pour les réutiliser dans le fuzzing des tests suivants, qu’il traite dans un ordre aléatoire.
Vous pouvez en apprendre davantage sur ce fuzzer dans cet article de blog.
Test de stress
- le serveur ne plante pas et aucun piège de débogage ou de sanitizer ne se déclenche ;
- il n’y a pas d’interblocages ;
- la structure de la base de données est cohérente ;
- le serveur peut s’arrêter correctement après le test puis redémarrer sans exception.
Thread fuzzer
Audit de sécurité
Analyseurs statiques
clang-tidy à chaque commit.
Les vérifications clang-static-analyzer sont également activées.
clang-tidy est aussi utilisé pour certaines vérifications de style.
Nous avons évalué clang-tidy, Coverity, cppcheck, PVS-Studio, tscancode, CodeQL.
Vous trouverez les instructions d’utilisation dans le répertoire tests/instructions/.
Si vous utilisez CLion comme IDE, vous pouvez profiter immédiatement de certaines vérifications clang-tidy.
Nous utilisons également shellcheck pour l’analyse statique des scripts shell.
Durcissement
Contrôles d’intégrité à l’exécution
- et ce n’est pas lent.
Style de code
utils/check-style.
Pour imposer un style correct à votre code, vous pouvez utiliser clang-format.
Le fichier .clang-format se trouve à la racine du code source.
Il correspond en grande partie à notre style de code actuel.
Mais il n’est pas recommandé d’appliquer clang-format à des fichiers existants, car cela dégrade le formatage.
Vous pouvez utiliser l’outil clang-format-diff, que vous trouverez dans le dépôt source de clang.
Vous pouvez aussi essayer l’outil uncrustify pour reformater votre code.
Le fichier de configuration uncrustify.cfg se trouve à la racine du code source.
Il est moins éprouvé que clang-format.
CLion possède son propre formateur de code, qui doit être ajusté à notre style de code.