Тестирование
Функциональные тесты
Функциональные тесты являются самыми простыми и удобными в использовании. Большинство возможностей ClickHouse могут быть протестированы с помощью функциональных тестов, и их необходимо использовать для каждого изменения в коде ClickHouse, которое может быть протестировано таким образом.
Каждый функциональный тест отправляет один или несколько запросов на работающий сервер ClickHouse и сравнивает результат с эталонным.
Тесты находятся в директории queries
. Существует два подпапки: stateless
и stateful
.
- Stateless тесты выполняют запросы без каких-либо предзагруженных тестовых данных — они часто создают небольшие синтетические наборы данных на лету, внутри самого теста.
- Stateful тесты требуют предварительно загруженных тестовых данных из ClickHouse, и они доступны общественности.
Каждый тест может быть одного из двух типов: .sql
и .sh
.
- Тест
.sql
— это простой SQL-скрипт, который передается вclickhouse-client
. - Тест
.sh
— это скрипт, который выполняется сам по себе.
SQL-тесты, как правило, предпочтительнее, чем тесты .sh
. Вы должны использовать тесты .sh
, только если вам нужно протестировать какую-либо функцию, которая не может быть протестирована с помощью чистого SQL, например, перенаправление данных ввода в clickhouse-client
или тестирование clickhouse-local
.
Распространенной ошибкой при тестировании типов данных DateTime
и DateTime64
является предположение, что сервер использует определенный часовой пояс (например, "UTC"). Это не так, часовые пояса в CI тестах преднамеренно рандомизированы. Самый простой обходной путь — явно указать часовой пояс для тестовых значений, например, toDateTime64(val, 3, 'Europe/Amsterdam')
.
Запуск теста локально
Запустите сервер ClickHouse локально, прослушивающий по умолчанию на порту (9000). Чтобы запустить, например, тест 01428_hash_set_nan_key
, перейдите в папку репозитория и выполните следующую команду:
Результаты теста (stderr
и stdout
) записываются в файлы 01428_hash_set_nan_key.[stderr|stdout]
, которые находятся рядом с тестом (для queries/0_stateless/foo.sql
, выходные данные будут в queries/0_stateless/foo.stdout
).
Смотрите tests/clickhouse-test --help
для всех опций clickhouse-test
. Вы можете запустить все тесты или запустить подмножество тестов, предоставив фильтр для имен тестов: ./clickhouse-test substring
. Также имеются опции для запуска тестов параллельно или в случайном порядке.
Добавление нового теста
Чтобы добавить новый тест, сначала создайте файл .sql
или .sh
в директории queries/0_stateless
. Затем создайте соответствующий файл .reference
, используя clickhouse-client < 12345_test.sql > 12345_test.reference
или ./12345_test.sh > ./12345_test.reference
.
Тесты должны только создавать, удалять, выбирать из и т.д. таблицы в базе данных test
, которая автоматически создается заранее. Использование временных таблиц допустимо.
Чтобы настроить ту же среду, что и в CI локально, установите конфигурации тестов (они будут использовать имитацию Zookeeper и настроят некоторые параметры):
Тесты должны
- быть минимальными: только создавать минимально необходимые таблицы, колонки и сложность,
- быть быстрыми: не занимать больше нескольких секунд (лучше: доли секунды),
- быть правильными и детерминированными: неудача только в том случае, если тестируемая функция не работает,
- быть изолированными/stateless: не полагаться на окружение и время,
- быть исчерпывающими: охватывать крайние случаи, такие как нули, null, пустые наборы, исключения (негативные тесты, используйте синтаксис
-- { serverError xyz }
и-- { clientError xyz }
для этого), - очищать таблицы в конце теста (в случае остатков),
- убедиться, что другие тесты не тестируют то же самое (т.е. использовать grep в начале).
Ограничение запуска тестов
У теста может быть ноль или более тегов, указывающих ограничения, в каких контекстах тест запускается в CI.
Для тестов .sql
теги размещаются в первой строке как SQL-комментарий:
Для тестов .sh
теги записываются как комментарий на второй строке:
Список доступных тегов:
Название тега | Что он делает | Пример использования |
---|---|---|
disabled | Тест не запускается | |
long | Время выполнения теста увеличено с 1 до 10 минут | |
deadlock | Тест запускается в цикле на длительное время | |
race | То же самое, что и deadlock . Предпочитайте deadlock | |
shard | Сервер должен прослушивать 127.0.0.* | |
distributed | То же самое, что и shard . Предпочитайте shard | |
global | То же самое, что и shard . Предпочайте shard | |
zookeeper | Тест требует Zookeeper или ClickHouse Keeper для запуска | Тест использует ReplicatedMergeTree |
replica | То же самое, что и zookeeper . Предпочитайте zookeeper | |
no-fasttest | Тест не запускается под Fast test | Тест использует движок таблиц MySQL , который отключен в Fast test |
no-[asan, tsan, msan, ubsan] | Отключает тесты в сборке с санитайзерами | Тест запускается под QEMU, который не работает с санитайзерами |
no-replicated-database | ||
no-ordinary-database | ||
no-parallel | Отключает запуск других тестов параллельно с этим | Тест читает из system таблиц, и инварианты могут быть нарушены |
no-parallel-replicas | ||
no-debug | ||
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 |
В дополнение к вышеуказанным настройкам, вы можете использовать флаги USE_*
из system.build_options
для определения использования конкретных возможностей ClickHouse. Например, если ваш тест использует таблицу MySQL, вы должны добавить тег use-mysql
.
Указание ограничений для случайных настроек
Тест может указать минимальные и максимальные разрешенные значения для настроек, которые могут быть рандомизированы во время выполнения теста.
Для тестов .sh
ограничения записываются как комментарий в строке рядом с тегами или на второй строке, если теги не указаны:
Для тестов .sql
теги размещаются в качестве SQL-комментария в строке рядом с тегами или в первой строке:
Если вам нужно указать только одно ограничение, вы можете использовать None
для другого.
Выбор имени теста
Имя теста начинается с пятизначного префикса, за которым следует описательное имя, например, 00422_hash_function_constexpr.sql
. Чтобы выбрать префикс, найдите наибольший префикс, уже присутствующий в директории, и увеличьте его на один.
Тем временем могут быть добавлены некоторые другие тесты с тем же числовым префиксом, но это нормально и не приведет к каким-либо проблемам, вам не нужно будет менять его позже.
Проверка на ошибку, которая должна произойти
Иногда вы хотите протестировать, что серверная ошибка возникает при некорректном запросе. Мы поддерживаем специальные аннотации для этого в SQL-тестах, в следующей форме:
Этот тест обеспечивает возврат сервером ошибки с кодом 49 о несуществующей колонке x
. Если ошибки нет или ошибка отличается, тест провалится. Если вы хотите убедиться, что ошибка происходит на стороне клиента, используйте аннотацию clientError
.
Не проверяйте конкретное wording сообщения об ошибке, оно может измениться в будущем, и тест будет ненужно ломаться. Проверяйте только код ошибки. Если существующий код ошибки недостаточно точен для ваших нужд, рассмотрите возможность добавления нового.
Тестирование распределенного запроса
Если вы хотите использовать распределенные запросы в функциональных тестах, вы можете воспользоваться функцией таблицы remote
с адресами 127.0.0.{1..2}
для сервера, чтобы запросить его сам; или вы можете использовать предопределенные тестовые кластеры в конфигурационном файле сервера, такие как test_shard_localhost
. Не забудьте добавить слова shard
или distributed
к имени теста, чтобы он выполнялся в CI в правильных конфигурациях, где сервер настроен на поддержку распределенных запросов.
Работа с временными файлами
Иногда в тесте оболочки вам может понадобиться создать файл на лету для работы с ним. Имейте в виду, что некоторые проверки CI выполняют тесты параллельно, поэтому, если вы создаете или удаляете временный файл в вашем скрипте без уникального имени, это может привести к сбоям некоторых проверок CI, таких как Flaky. Чтобы обойти это, вы должны использовать переменную окружения $CLICKHOUSE_TEST_UNIQUE_NAME
, чтобы дать временным файлам имя, уникальное для выполняемого теста. Таким образом, вы можете быть уверены, что файл, который вы создаете во время настройки или удаляете во время очистки, является единственным в использовании данным тестом, а не каким-либо другим тестом, который выполняется параллельно.
Известные ошибки
Если мы знаем о некоторых ошибках, которые могут быть легко воспроизведены функциональными тестами, мы помещаем подготовленные функциональные тесты в директорию tests/queries/bugs
. Эти тесты будут перемещены в tests/queries/0_stateless
, когда ошибки будут исправлены.
Интеграционные тесты
Интеграционные тесты позволяют тестировать ClickHouse в кластерной конфигурации и взаимодействие ClickHouse с другими серверами, такими как MySQL, Postgres, MongoDB. Они полезны для эмуляции разрывов сети, потерь пакетов и т.д. Эти тесты запускаются под Docker и создают несколько контейнеров с различным программным обеспечением.
Смотрите tests/integration/README.md
, чтобы узнать, как запускать эти тесты.
Обратите внимание, что интеграция ClickHouse с сторонними драйверами не тестируется. Также на данный момент у нас нет интеграционных тестов с нашими JDBC и ODBC драйверами.
Юнит-тесты
Юнит-тесты полезны, когда вы хотите протестировать не ClickHouse в целом, а отдельную изолированную библиотеку или класс. Вы можете включить или отключить сборку тестов с помощью параметра CMake ENABLE_TESTS
. Юнит-тесты (и другие тестовые программы) находятся в подкаталогах tests
по всему коду. Чтобы запустить юнит-тесты, наберите ninja test
. Некоторые тесты используют gtest
, но некоторые являются просто программами, которые возвращают ненулевой код выхода при неудаче теста.
Нет необходимости иметь юнит-тесты, если код уже охвачен функциональными тестами (и функциональные тесты обычно гораздо проще в использовании).
Вы можете запускать отдельные проверки gtest, вызывая исполняемый файл напрямую, например:
Нагрузочные тесты
Нагрузочные тесты позволяют измерить и сравнить производительность некоторой изолированной части ClickHouse на синтетических запросах. Нагрузочные тесты находятся в директории tests/performance/
. Каждый тест представлен файлом .xml
с описанием тестового случая. Тесты запускаются с помощью инструмента docker/test/performance-comparison
. Смотрите файл readme для вызова.
Каждый тест выполняет один или несколько запросов (возможно, с комбинациями параметров) в цикле.
Если вы хотите улучшить производительность ClickHouse в каком-либо сценарии, и если улучшения могут быть наблюдаемы на простых запросах, настоятельно рекомендуется написать нагрузочный тест. Также рекомендуется писать нагрузочные тесты, когда вы добавляете или модифицируете SQL функции, которые относительно изолированы и не слишком скрыты. Всегда имеет смысл использовать perf top
или другие инструменты perf
во время ваших тестов.
Инструменты и скрипты тестирования
Некоторые программы в директории tests
не являются подготовленными тестами, а являются инструментами тестирования. Например, для Lexer
существует инструмент src/Parsers/tests/lexer
, который просто токенизирует stdin и записывает цветной результат в stdout. Вы можете использовать эти инструменты как примеры кода и для исследований и ручного тестирования.
Разные тесты
Существуют тесты для моделей машинного обучения в tests/external_models
. Эти тесты не обновляются и должны быть перенесены в интеграционные тесты.
Существует отдельный тест на вставки с кворумом. Этот тест запускает кластер ClickHouse на отдельных серверах и эмулирует различные случаи сбоя: разрыв сети, потеря пакетов (между узлами ClickHouse, между ClickHouse и ZooKeeper, между сервером ClickHouse и клиентом и т.д.), kill -9
, kill -STOP
и kill -CONT
, как Jepsen. Затем тест проверяет, что все подтвержденные вставки были записаны, а все отклоненные вставки не были.
Тест кворума был написан отдельной командой до того, как ClickHouse стал открытым исходным кодом. Эта команда больше не работает с ClickHouse. Тест случайно был написан на Java. По этим причинам тест кворума должен быть переписан и перемещён в интеграционные тесты.
Ручное тестирование
Когда вы разрабатываете новую функцию, разумно также протестировать её вручную. Вы можете сделать это с помощью следующих шагов:
Соберите ClickHouse. Запустите ClickHouse из терминала: перейдите в директорию programs/clickhouse-server
и запустите его с ./clickhouse-server
. Он будет использовать конфигурацию (config.xml
, users.xml
и файлы в директориях config.d
и users.d
) из текущей директории по умолчанию. Для подключения к серверу ClickHouse запустите programs/clickhouse-client/clickhouse-client
.
Обратите внимание, что все инструменты clickhouse (сервер, клиент и т.д.) — это просто симлинки на один бинарный файл под названием clickhouse
. Вы можете найти этот бинарный файл в programs/clickhouse
. Все инструменты также могут быть вызваны как clickhouse tool
вместо clickhouse-tool
.
В качестве альтернативы вы можете установить пакет ClickHouse: либо стабильную версию из репозитория ClickHouse, либо вы можете собрать пакет для себя с помощью ./release
в корне исходников ClickHouse. Затем запустите сервер с sudo clickhouse start
(или stop
, чтобы остановить сервер). Ищите логи по адресу /etc/clickhouse-server/clickhouse-server.log
.
Когда ClickHouse уже установлен в вашей системе, вы можете собрать новый бинарный файл clickhouse
и заменить существующий бинарный файл:
Также вы можете остановить системный сервер clickhouse и запустить собственный с той же конфигурацией, но с выводом логов в терминал:
Пример с gdb:
Если системный сервер clickhouse уже работает, и вы не хотите его останавливать, вы можете изменить номера портов в вашем config.xml
(или переопределить их в файле в директории config.d
), предоставить подходящий путь к данным и запустить его.
Бинарный файл clickhouse
почти не имеет зависимостей и работает на широком спектре дистрибутивов Linux. Чтобы быстро и наглядно протестировать ваши изменения на сервере, вы можете просто scp
ваш свежесобранный бинарный файл clickhouse
на ваш сервер, а затем запустить его, как в приведенных выше примерах.
Тесты сборки
Тесты сборки позволяют проверить, что сборка не сломана на различных альтернативных конфигурациях и на некоторых чуждых системах. Эти тесты также автоматизированы.
Примеры:
- кросс-компиляция для Darwin x86_64 (macOS)
- кросс-компиляция для FreeBSD x86_64
- кросс-компиляция для Linux AArch64
- сборка на Ubuntu с библиотеками из системных пакетов (не рекомендуется)
- сборка с общим связыванием библиотек (не рекомендуется)
Например, сборка с системными пакетами является плохой практикой, потому что мы не можем гарантировать, какую именно версию пакетов будет иметь система. Но это действительно нужно управляющим Debian. По этой причине мы, по крайней мере, должны поддерживать этот вариант сборки. Другим примером является общее связывание, которое является распространенным источником проблем, но это необходимо для некоторых энтузиастов.
Хотя мы не можем запускать все тесты на всех вариантах сборок, мы хотим проверять хотя бы то, что различные варианты сборок не сломаны. Для этой цели мы используем тесты сборки.
Мы также тестируем, что нет единиц перевода, которые слишком длинные для компиляции или требуют слишком много ОЗУ.
Мы также тестируем, что нет слишком крупных кадров стека.
Тестирование совместимости протоколов
Когда мы расширяем сетевой протокол ClickHouse, мы вручную тестируем, что старый clickhouse-client
работает с новым clickhouse-server
, и новый clickhouse-client
работает со старым clickhouse-server
(просто запуская бинарники из соответствующих пакетов).
Мы также автоматически тестируем некоторые случаи с интеграционными тестами:
- если данные, записанные старой версией ClickHouse, могут быть успешно прочитаны новой версией;
- работают ли распределенные запросы в кластере с разными версиями ClickHouse.
Помощь от компилятора
Основной код ClickHouse (который находится в директории src
) создается с -Wall -Wextra -Werror
и с некоторыми дополнительными включенными предупреждениями. Хотя эти опции не включены для сторонних библиотек.
Clang имеет даже более полезные предупреждения — вы можете искать их с помощью -Weverything
и выбрать что-то для сборки по умолчанию.
Мы всегда используем clang для сборки ClickHouse, как для разработки, так и для производства. Вы можете собирать на своем собственном компьютере в режиме отладки (чтобы сэкономить заряд батареи), но имейте в виду, что компилятор может генерировать больше предупреждений с -O3
из-за лучшего контроля потока и межпроцедурного анализа. При сборке с clang в режиме отладки используется отладочная версия libc++
, что позволяет ловить больше ошибок во время выполнения.
Санитайзеры
Если процесс (сервер ClickHouse или клиент) завершается с ошибкой в момент запуска, вам может потребоваться отключить рандомизацию компоновки адресов: sudo sysctl kernel.randomize_va_space=0
Санитайзер адресов
Мы запускаем функциональные, интеграционные, стрессовые и юнит-тесты под ASan на основе каждого коммита.
Санитайзер потоков
Мы запускаем функциональные, интеграционные, стрессовые и юнит-тесты под TSan на основе каждого коммита.
Санитайзер памяти
Мы запускаем функциональные, интеграционные, стрессовые и юнит-тесты под MSan на основе каждого коммита.
Санитайзер неопределенного поведения
Мы запускаем функциональные, интеграционные, стрессовые и юнит-тесты под UBSan на основе каждого коммита. Код некоторых сторонних библиотек не проверяется на наличие UB.
Valgrind (Memcheck)
Ранее мы запускали функциональные тесты под Valgrind всю ночь, но больше этого не делаем. Это занимает несколько часов. В настоящее время существует один известный ложный сигнал в библиотеке re2
, см. эту статью.
Фuzz-тестирование
Фuzz-тестирование ClickHouse реализовано как с использованием libFuzzer, так и случайных SQL-запросов. Все тесты на фuzz-тестирование должны выполняться с санитайзерами (адресом и неопределенным).
LibFuzzer используется для изолированного фuzz-тестирования библиотечного кода. Фuzz-тесты реализованы как часть тестового кода и имеют постфикс "_fuzzer" в названии. Пример фuzz-теста можно найти по адресу src/Parsers/fuzzers/lexer_fuzzer.cpp
. Конфигурации, специфичные для LibFuzzer, словари и корпуса находятся в tests/fuzz
. Мы призываем вас писать фuzz-тесты для каждой функции, обрабатывающей ввод пользователя.
Фuzz-тесты по умолчанию не собираются. Чтобы собрать фuzz-тесты, необходимо установить оба параметра -DENABLE_FUZZING=1
и -DENABLE_TESTS=1
. Рекомендуется отключить Jemalloc при сборке фuzz-тестов. Конфигурация, используемая для интеграции фuzz-тестирования ClickHouse в Google OSS-Fuzz, может быть найдена в docker/fuzz
.
Мы также используем простой фuzz-тест для генерации случайных SQL-запросов и проверки того, что сервер не завершается при их выполнении. Вы можете найти его в 00746_sql_fuzzy.pl
. Этот тест следует выполнять непрерывно (в течение ночи и дольше).
Мы также используем сложный фuzzer на основе AST, который способен находить огромное количество крайних случаев. Он выполняет случайные перестановки и замены в запросах AST. Он запоминает узлы AST из предыдущих тестов для использования их при фuzz-тестировании последующих тестов, обрабатывая их в случайном порядке. Вы можете узнать больше об этом фузере в этой блоге-статье.
Нагрузочный тест
Нагрузочные тесты являются еще одним случаем фuzz-тестирования. Они запускают все функциональные тесты параллельно в случайном порядке с одним сервером. Результаты тестов не проверяются.
Проверяется, что:
- сервер не завершается, не возникает отладочных или санитайзер-трапов;
- нет взаимных блокировок;
- структура базы данных остается последовательной;
- сервер может успешно остановиться после теста и снова запуститься без исключений.
Существует пять вариантов (Debug, ASan, TSan, MSan, UBSan).
Фuzzer потоков
Фuzzer потоков (пожалуйста, не путайте с санитайзером потоков) — это другой вид фuzz-тестирования, который позволяет рандомизировать порядок выполнения потоков. Это помогает находить еще больше специальных случаев.
Аудит безопасности
Наша команда безопасности провела некоторый базовый обзор возможностей ClickHouse с точки зрения безопасности.
Статические анализаторы
Мы запускаем clang-tidy
на основе каждого коммита. Проверки clang-static-analyzer
также включены. clang-tidy
также используется для некоторых проверок стиля.
Мы оценили clang-tidy
, Coverity
, cppcheck
, PVS-Studio
, tscancode
, CodeQL
. Вы найдете инструкции по использованию в директории tests/instructions/
.
Если вы используете CLion
в качестве IDE, вы можете использовать некоторые проверки clang-tidy
из коробки.
Мы также используем shellcheck
для статического анализа shell-скриптов.
Укрепление
В сборке отладки мы используем пользовательский аллокатор, который делает ASLR для выделений на уровне пользователя.
Мы также вручную защищаем области памяти, которые должны оставаться только для чтения после выделения.
В сборке отладки мы также используем настраиваемую версию libc, которая гарантирует, что не будут вызываться "вредные" (устаревшие, небезопасные, не потокобезопасные) функции.
Отладочные утверждения широко используются.
В сборке отладки, если выбрасывается исключение с кодом "логическая ошибка" (означает ошибку), программа завершается преждевременно. Это позволяет использовать исключения в сборке релиза, но делает это утверждением в сборке отладки.
Используется отладочная версия jemalloc для сборок отладки. Используется отладочная версия libc++ для сборок отладки.
Проверки целостности во время исполнения
Данные, хранящиеся на диске, имеют контрольные суммы. Данные в таблицах MergeTree проверяются по трем способам одновременно* (сжатые блоки данных, несжатые блоки данных, общая контрольная сумма по блокам). Данные, переданные по сети между клиентом и сервером или между серверами, также имеют контрольные суммы. Репликация обеспечивает битово-идентичные данные на репликах.
Это необходимо для защиты от неисправного оборудования (битовая порча на запоминающих устройствах, битовые сбои в ОЗУ сервера, битовые сбои в ОЗУ сетевого контроллера, битовые сбои в ОЗУ сетевого коммутатора, битовые сбои в ОЗУ клиента, битовые сбои на проводе). Обратите внимание, что битовые сбои являются распространенными и могут происходить даже на ECC ОЗУ и при наличии контрольных сумм TCP (если вам удастся запустить тысячи серверов, обрабатывающих петабайты данных каждый день). Смотрите видео (на русском).
ClickHouse предоставляет диагностику, которая поможет инженерам операций выявить неисправное оборудование.
* и это не медленно.
Стиль кода
Правила стиля кода описаны здесь.
Чтобы проверить некоторые распространенные нарушения стиля, вы можете использовать скрипт utils/check-style
.
Чтобы принудительно соблюсти правильный стиль вашего кода, вы можете использовать clang-format
. Файл .clang-format
находится в корне исходников. Он в основном соответствует нашему актуальному стилю кода. Но не рекомендуется применять clang-format
к существующим файлам, так как это ухудшает форматирование. Вы можете использовать инструмент clang-format-diff
, который вы можете найти в репозитории исходников clang.
В качестве альтернативы вы можете попробовать инструмент uncrustify
, чтобы переформатировать ваш код. Конфигурация находится в uncrustify.cfg
в корне источников. Он менее тестировался, чем clang-format
.
CLion
имеет свой собственный форматировщик кода, который должен быть настроен для нашего стиля кода.
Мы также используем codespell
для нахождения опечаток в коде. Это также автоматизировано.
Показатели покрытия тестов
Мы также отслеживаем покрытие тестов, но только для функциональных тестов и только для clickhouse-server. Это выполняется на ежедневной основе.
Тесты для тестов
Существует автоматическая проверка на ненадежные тесты. Он запускает все новые тесты 100 раз (для функциональных тестов) или 10 раз (для интеграционных тестов). Если хотя бы один раз тест не прошел, он считается ненадежным.
Автоматизация тестирования
Мы запускаем тесты с помощью GitHub Actions.
Работы по сборке и тесты запускаются в песочнице на основе каждого коммита. Получившиеся пакеты и результаты тестов публикуются на GitHub и могут быть загружены по прямым ссылкам. Артефакты хранятся в течение нескольких месяцев. Когда вы отправляете запрос на скачивание на GitHub, мы помечаем его как "можно тестировать", и наша CI-система соберет пакеты ClickHouse (релизные, отладочные, с санитайзерами адресов и т.д.) для вас.