Tipos de teste
- Testes funcionais - um conjunto de consultas e scripts que inclui os seguintes subconjuntos sobrepostos
- Teste rápido - o subconjunto mínimo
- Testes sem estado que não exigem o preenchimento de bancos de dados com dados
- Testes sequenciais que não podem ser executados em paralelo
- Testes de integração, executados pelo
pytestem um cluster - Testes unitários
- Testes de desempenho
- Testes de build
- Sanitizers
- Fuzzers e alguns outros; veja as seções abaixo.
Testes funcionais
./tests/queries.
Cada teste pode ser de um destes dois tipos: .sql e .sh.
- Um teste
.sqlé um script SQL simples enviado por pipe para oclickhouse-client. - Um teste
.shé um script executado por conta própria.
.sh.
Você deve usar testes .sh apenas quando precisar testar algum recurso que não possa ser exercitado com SQL puro, como enviar dados de entrada por pipe para o clickhouse-client ou testar o clickhouse-local.
Um erro comum ao testar os tipos de dados
DateTime e DateTime64 é presumir que o servidor usa um fuso horário específico (por exemplo, “UTC”). Não é o caso; os fusos horários nas execuções de teste em CI
são deliberadamente aleatorizados. A solução alternativa mais simples é especificar explicitamente o fuso horário dos valores de teste, por exemplo, toDateTime64(val, 3, 'Europe/Amsterdam').Executando um teste localmente
01428_hash_set_nan_key, vá para a pasta do repositório e execute o seguinte comando:
stderr e stdout) são gravados nos arquivos 01428_hash_set_nan_key.[stderr|stdout], que ficam ao lado do próprio teste (para queries/0_stateless/foo.sql, a saída ficará em queries/0_stateless/foo.stdout).
Consulte tests/clickhouse-test --help para ver todas as opções de clickhouse-test.
Você pode executar todos os testes ou apenas um subconjunto, fornecendo um filter para os nomes dos testes: ./clickhouse-test substring.
Também há opções para executar os testes em paralelo ou em ordem aleatória.
Executando testes rápidos
t3.2xlarge com 100 GB de armazenamento.
- Instale os pré-requisitos e faça login novamente.
- Baixe o código-fonte.
- Compile o código e execute os “testes rápidos”.
nohup ou disown para mantê-la em execução mesmo após a perda da conexão ssh.
Executando testes sem estado
m7i.8xlarge, com 200 GB de armazenamento.
- Instale os pré-requisitos e faça login novamente.
- Baixe o código-fonte.
- Compile o código.
- Execute testes sem estado, que podem ser executados em paralelo.
python -m ci.praktika run executam um job específico de integração contínua; você pode ler mais sobre a integração contínua do ClickHouse aqui.
Adicionando um novo teste
.sql ou .sh no diretório queries/0_stateless.
Em seguida, gere o arquivo .reference correspondente usando clickhouse-client < 12345_test.sql > 12345_test.reference ou ./12345_test.sh > ./12345_test.reference.
Os testes devem apenas criar, remover, fazer SELECT em etc. tabelas no banco de dados test, que é criado automaticamente com antecedência.
É permitido usar tabelas temporárias.
Para configurar localmente o mesmo ambiente da CI, instale as configurações de teste (elas usarão uma implementação simulada do ZooKeeper e ajustarão algumas configurações)
Os testes devem ser
- mínimos: criar apenas as tabelas, colunas e a complexidade estritamente necessárias,
- rápidos: não levar mais do que alguns segundos (melhor ainda: menos de um segundo),
- corretos e determinísticos: falhar se, e somente se, a funcionalidade em teste não estiver funcionando,
- isolados/sem estado: não depender do ambiente nem do tempo
- exaustivos: cobrir casos de borda como zeros, nulos, conjuntos vazios e exceções (testes negativos; use a sintaxe
-- { serverError xyz }e-- { clientError xyz }para isso), - limpar as tabelas ao final do teste (caso reste algo),
- garantir que os outros testes não estejam testando a mesma coisa (ou seja, faça um grep primeiro).
Restringindo execuções de teste
.sql, as tags são colocadas na primeira linha como um comentário SQL:
.sh, as tags são escritas como comentário na segunda linha:
| Tag name | What it does | Usage example |
|---|---|---|
disabled | O teste não é executado | |
long | O tempo de execução do teste é estendido de 1 para 10 minutos | |
deadlock | O teste é executado em loop por um longo período | |
race | Igual a deadlock. Prefira deadlock | |
shard | O servidor precisa escutar em 127.0.0.* | |
distributed | Igual a shard. Prefira shard | |
global | Igual a shard. Prefira shard | |
zookeeper | O teste requer Zookeeper ou ClickHouse Keeper para ser executado | O teste usa ReplicatedMergeTree |
replica | Igual a zookeeper. Prefira zookeeper | |
no-fasttest | O teste não é executado no teste rápido | O teste usa o table engine MySQL, que é desabilitado no teste rápido |
fasttest-only | O teste é executado apenas no teste rápido | |
no-[asan, tsan, msan, ubsan] | Desabilita testes em builds com sanitizers | O teste é executado no QEMU, que não funciona com sanitizers |
no-replicated-database | Desabilita o teste quando o banco de dados padrão usa ReplicatedDatabaseEngine | |
no-ordinary-database | Desabilita o teste quando o database engine padrão é Ordinary | |
no-parallel | Desabilita a execução de outros testes em paralelo com este | O teste lê tabelas system, e os invariantes podem ser violados |
no-parallel-replicas | Desabilita o teste quando parallel replicas estão habilitadas | |
no-debug | Desabilita testes em builds Debug | |
no-release | Desabilita testes em builds Release | |
no-darwin | Desabilita o teste no macOS (Darwin) | O teste depende de recursos específicos do Linux, como consultas distribuídas, procfs ou servidor HTTP |
no-stress, 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.
Além das configurações acima, você pode usar flags USE_* de system.build_options para definir o uso de recursos específicos do ClickHouse.
Por exemplo, se seu teste usa uma tabela MySQL, você deve adicionar a tag use-mysql.
Especificando limites para configurações aleatórias
.sh, os limites são escritos como um comentário na linha ao lado das tags ou na segunda linha, caso nenhuma tag seja especificada:
.sql, as tags são colocadas como comentário SQL na linha ao lado delas ou na primeira linha:
None para o outro.
Escolhendo o nome do teste
00422_hash_function_constexpr.sql.
Para escolher o prefixo, encontre o maior prefixo já presente no diretório e acrescente um a ele.
Verificando se ocorre um erro esperado
x é desconhecida.
Se não houver erro, ou se o erro for diferente, o teste falhará.
Se você quiser garantir que um erro ocorra no lado do cliente, use a anotação clientError.
Não verifique uma redação específica da mensagem de erro, pois ela pode mudar no futuro, e o teste pode falhar desnecessariamente.
Verifique apenas o código do erro.
Se o código de erro existente não for preciso o suficiente para o que você precisa, considere adicionar um novo.
Testando uma consulta distribuída
remote com endereços 127.0.0.{1..2} para que o servidor consulte a si próprio; ou usar clusters de teste predefinidos no arquivo de configuração do servidor, como test_shard_localhost.
Lembre-se de adicionar as palavras shard ou distributed ao nome do teste, para que ele seja executado no CI com as configurações corretas, nas quais o servidor esteja configurado para oferecer suporte a consultas distribuídas.
Trabalhando com arquivos temporários
$CLICKHOUSE_TEST_UNIQUE_NAME para dar aos arquivos temporários um nome exclusivo para o teste em execução.
Assim, você garante que o arquivo criado durante a configuração ou removido durante a limpeza está sendo usado apenas por aquele teste, e não por outro teste em execução em paralelo.
Bugs conhecidos
tests/queries/bugs.
Esses testes serão movidos para tests/queries/0_stateless quando os bugs forem corrigidos.
Testes de integração
tests/integration/README.md para saber como executar esses testes.
Observe que a integração do ClickHouse com drivers de terceiros não é testada.
Além disso, no momento, não temos testes de integração com nossos drivers JDBC e ODBC.
Testes unitários
ENABLE_TESTS do CMake.
Os testes unitários (e outros programas de teste) ficam nos subdiretórios tests espalhados pelo código.
Para executar os testes unitários, digite ninja test.
Alguns testes usam gtest, mas outros são apenas programas que retornam um código de saída diferente de zero em caso de falha.
Não é necessário ter testes unitários se o código já estiver coberto por testes funcionais (e os testes funcionais geralmente são muito mais simples de usar).
Você pode executar verificações individuais do gtest chamando o executável diretamente, por exemplo:
Testes de desempenho
tests/performance/.
Cada teste é representado por um arquivo .xml com a descrição do caso de teste.
Os testes são executados com a ferramenta docker/test/performance-comparison. Consulte o arquivo readme para ver como executá-la.
Cada teste executa uma ou mais consultas (possivelmente com combinações de parâmetros) em loop.
Se você quiser melhorar o desempenho do ClickHouse em algum cenário, e se as melhorias puderem ser observadas em consultas simples, é altamente recomendável escrever um teste de desempenho.
Além disso, também é recomendável escrever testes de desempenho ao adicionar ou modificar funções SQL relativamente isoladas e não muito obscuras.
Sempre vale a pena usar perf top ou outras ferramentas perf durante os testes.
Ferramentas e scripts de teste
tests não são testes prontos, e sim ferramentas de teste.
Por exemplo, para Lexer existe a ferramenta src/Parsers/tests/lexer, que apenas faz a tokenização da entrada padrão e grava o resultado colorido na saída padrão.
Você pode usar ferramentas desse tipo como exemplos de código e para exploração e testes manuais.
Testes diversos
tests/external_models.
Esses testes não são mantidos atualizados e devem ser transferidos para os testes de integração.
Há um teste separado para inserts com quórum.
Esse teste executa um cluster do ClickHouse em servidores separados e emula vários casos de falha: partição de rede, perda de pacotes (entre nós do ClickHouse, entre o ClickHouse e o ZooKeeper, entre o servidor ClickHouse e o cliente etc.), kill -9, kill -STOP e kill -CONT, como o Jepsen. Em seguida, o teste verifica se todos os inserts confirmados foram gravados e se todos os inserts rejeitados não foram.
Teste manual
programs/clickhouse-server e execute-o com ./clickhouse-server. Por padrão, ele usará a configuração (config.xml, users.xml e os arquivos nos diretórios config.d e users.d) do diretório atual. Para se conectar ao servidor ClickHouse, execute programs/clickhouse-client/clickhouse-client.
Observe que todas as ferramentas do ClickHouse (servidor, cliente etc.) são apenas links simbólicos para um único binário chamado clickhouse.
Você pode encontrar esse binário em programs/clickhouse.
Todas as ferramentas também podem ser invocadas como clickhouse tool em vez de clickhouse-tool.
Como alternativa, você pode instalar o pacote do ClickHouse: seja a versão estável do repositório do ClickHouse, seja compilando o pacote por conta própria com ./release na raiz do código-fonte do ClickHouse.
Em seguida, inicie o servidor com sudo clickhouse start (ou stop para parar o servidor).
Verifique os logs em /etc/clickhouse-server/clickhouse-server.log.
Quando o ClickHouse já estiver instalado no seu sistema, você pode compilar um novo binário clickhouse e substituir o binário existente:
config.xml (ou sobrescrevê-los em um arquivo no diretório config.d), definir o caminho de dados adequado e executá-lo.
O binário clickhouse quase não tem dependências e funciona em uma ampla variedade de distribuições Linux.
Para testar suas alterações de forma rápida e prática em um servidor, você pode simplesmente usar scp para copiar seu binário clickhouse recém-compilado para o servidor e então executá-lo como nos exemplos acima.
Testes de build
- compilação cruzada para Darwin x86_64 (macOS)
- compilação cruzada para FreeBSD x86_64
- compilação cruzada para Linux AArch64
- build no Ubuntu com bibliotecas de pacotes do sistema (desaconselhado)
- build com vinculação dinâmica de bibliotecas (desaconselhado)
Testando a compatibilidade do protocolo
- se os dados gravados por uma versão antiga do ClickHouse podem ser lidos com sucesso pela nova versão;
- se as consultas distribuídas funcionam em um cluster com diferentes versões do ClickHouse.
Ajuda do compilador
src) é compilado com -Wall -Wextra -Werror e com alguns avisos adicionais habilitados.
No entanto, essas opções não são habilitadas para bibliotecas de terceiros.
O Clang tem ainda mais avisos úteis — você pode procurá-los com -Weverything e escolher alguns para incluir na compilação padrão.
Sempre usamos clang para compilar o ClickHouse, tanto em desenvolvimento quanto em produção.
Você pode compilar na sua própria máquina no modo de depuração (para economizar a bateria do seu laptop), mas observe que o compilador é capaz de gerar mais avisos com -O3 devido ao melhor controle de fluxo e à análise interprocedural.
Ao compilar com clang no modo de depuração, é usada a versão de depuração do libc++, o que permite detectar mais erros em tempo de execução.
Sanitizers
Se o processo (servidor ClickHouse ou cliente) travar ao iniciar quando executado localmente, talvez seja necessário desativar a randomização do layout do espaço de endereçamento:
sudo sysctl kernel.randomize_va_space=0Sanitizador de endereços
Sanitizador de threads
Sanitizador de memória
Sanitizador de comportamento indefinido
Valgrind (memcheck)
re2; veja este artigo.
Fuzzing
src/Parsers/fuzzers/lexer_fuzzer.cpp.
As configurações, os dicionários e o corpus específicos do LibFuzzer são armazenados em tests/fuzz.
Incentivamos você a escrever testes de fuzzing para toda funcionalidade que lide com entrada do usuário.
Os fuzzers não são compilados por padrão.
Para compilar os fuzzers, as opções -DENABLE_FUZZING=1 e -DENABLE_TESTS=1 devem ser definidas.
Recomendamos desabilitar o Jemalloc ao compilar os fuzzers.
A configuração usada para integrar o fuzzing do ClickHouse ao
Google OSS-Fuzz pode ser encontrada em docker/fuzz.
Também usamos um teste de fuzzing simples para gerar consultas SQL aleatórias e verificar se o servidor não falha ao executá-las.
Você pode encontrá-lo em 00746_sql_fuzzy.pl.
Esse teste deve ser executado continuamente (durante a noite e por mais tempo).
Também usamos um fuzzer de consultas sofisticado baseado em AST, capaz de encontrar uma enorme quantidade de casos extremos.
Ele faz permutações e substituições aleatórias na AST das consultas.
Ele reutiliza nós da AST de testes anteriores no fuzzing de testes subsequentes, processando-os em ordem aleatória.
Você pode saber mais sobre esse fuzzer neste artigo do blog.
Teste de estresse
- o servidor não trava, e nenhum trap de depuração ou do sanitizer é acionado;
- não há deadlocks;
- a estrutura do banco de dados é consistente;
- o servidor pode ser encerrado com sucesso após o teste e iniciado novamente sem exceções.
Thread fuzzer
Auditoria de segurança
Analisadores estáticos
clang-tidy a cada commit.
As verificações do clang-static-analyzer também estão ativadas.
O clang-tidy também é usado para algumas verificações de estilo.
Avaliamos clang-tidy, Coverity, cppcheck, PVS-Studio, tscancode, CodeQL.
Você encontrará instruções de uso no diretório tests/instructions/.
Se você usa o CLion como IDE, pode aproveitar algumas verificações do clang-tidy prontas para uso.
Também usamos shellcheck para a análise estática de scripts shell.
Endurecimento
Verificações de integridade em tempo de execução
- e isso não é lento.
Estilo de código
utils/check-style.
Para garantir o estilo adequado do seu código, você pode usar clang-format.
O arquivo .clang-format fica na raiz do código-fonte.
Ele corresponde, em grande parte, ao nosso estilo de código atual.
Mas não é recomendado aplicar clang-format a arquivos existentes, porque isso piora a formatação.
Você pode usar a ferramenta clang-format-diff, que pode ser encontrada no repositório do código-fonte do clang.
Como alternativa, você pode tentar usar a ferramenta uncrustify para reformatar seu código.
A configuração está em uncrustify.cfg, na raiz do código-fonte.
Ela é menos testada do que clang-format.
O CLion tem seu próprio formatador de código, que precisa ser ajustado ao nosso estilo de código.
Também usamos codespell para encontrar erros de digitação no código.
Isso também é automatizado.