테스트 유형
- 기능 테스트 - 다음과 같은 서로 겹치는 하위 집합으로 구성된 쿼리 및 스크립트 모음
- 빠른 테스트 - 최소 하위 집합
- 데이터를 데이터베이스에 채울 필요가 없는 상태 비저장 테스트
- 병렬로 실행할 수 없는 순차 테스트
- 클러스터에서
pytest로 실행되는 통합 테스트 - 단위 테스트
- 성능 테스트
- 빌드 테스트
- Sanitizers
- 퍼저 그 밖의 몇 가지 테스트도 있으며, 아래 섹션을 참조하십시오.
기능 테스트
./tests/queries 디렉터리에 있습니다.
각 테스트는 .sql과 .sh 두 가지 유형 중 하나입니다.
.sql테스트는clickhouse-client에 파이프로 전달되는 간단한 SQL 스크립트입니다..sh테스트는 독립적으로 실행되는 스크립트입니다.
.sh 테스트보다 SQL 테스트를 사용하는 것이 좋습니다.
순수 SQL만으로 검증할 수 없는 기능을 테스트해야 할 때만 .sh 테스트를 사용하십시오. 예를 들어 입력 데이터를 clickhouse-client로 파이프하거나 clickhouse-local을 테스트하는 경우가 이에 해당합니다.
데이터 타입
DateTime 및 DateTime64를 테스트할 때 흔히 하는 실수는 서버가 특정 시간대(예: “UTC”)를 사용한다고 가정하는 것입니다. 실제로는 그렇지 않습니다. CI 테스트 실행에서는 시간대가 의도적으로 무작위로 설정됩니다. 가장 쉬운 해결 방법은 테스트 값에 시간대를 명시적으로 지정하는 것입니다. 예를 들어 toDateTime64(val, 3, 'Europe/Amsterdam')와 같이 지정할 수 있습니다.로컬에서 테스트 실행하기
01428_hash_set_nan_key 테스트를 실행하려면 리포지토리 폴더로 이동한 후 다음 명령을 실행합니다:
stderr 및 stdout)는 테스트 파일과 같은 위치에 있는 01428_hash_set_nan_key.[stderr|stdout] 파일에 기록됩니다(queries/0_stateless/foo.sql의 경우 출력은 queries/0_stateless/foo.stdout에 기록됩니다).
clickhouse-test의 모든 옵션은 tests/clickhouse-test --help를 참조하십시오.
테스트 이름 filter를 지정하여 모든 테스트를 실행하거나 일부 테스트만 실행할 수 있습니다: ./clickhouse-test substring.
테스트를 병렬로 실행하거나 무작위 순서로 실행하는 옵션도 있습니다.
빠른 테스트 실행
t3.2xlarge AWS amd64 Ubuntu 인스턴스에서 작동합니다.
- 필수 구성 요소를 설치한 후 다시 로그인하세요.
- 소스 코드를 가져오세요.
- 코드를 빌드한 후 “빠른 테스트”를 실행합니다.
ssh 연결이 끊어진 후에도 계속 실행되도록 nohup 또는 disown을 사용할 수 있습니다.
상태 비저장 테스트 실행
m7i.8xlarge AWS amd64 Ubuntu instance에서 작동합니다.
- 필수 구성 요소를 설치한 뒤 다시 로그인하세요.
- 소스 코드를 가져옵니다.
- 코드를 빌드하세요.
- 병렬 실행이 가능한 상태 비저장 테스트를 실행합니다.
python -m ci.praktika run 명령은 특정 CI 작업을 실행합니다. ClickHouse CI에 대한 자세한 내용은 여기에서 확인할 수 있습니다.
새 테스트 추가
queries/0_stateless 디렉터리에 .sql 또는 .sh 파일을 생성합니다.
그런 다음 clickhouse-client < 12345_test.sql > 12345_test.reference 또는 ./12345_test.sh > ./12345_test.reference를 사용해 해당 .reference 파일을 생성합니다.
테스트에서는 미리 자동 생성되는 test 데이터베이스에서만 테이블 생성, 삭제, 조회 등의 작업을 수행해야 합니다.
임시 테이블을 사용해도 됩니다.
로컬에서 CI와 동일한 환경을 설정하려면 테스트 구성을 설치합니다(이 구성은 Zookeeper 모의 구현을 사용하며 일부 설정을 조정합니다)
테스트는 다음과 같아야 합니다.
- 최소화: 꼭 필요한 최소한의 테이블, 컬럼, 복잡성만 생성합니다.
- 신속성: 몇 초를 넘기지 않아야 하며(더 바람직하게는 1초 미만),
- 정확성과 결정성: 테스트 대상 기능이 작동하지 않을 때에만 실패해야 합니다.
- 격리/무상태성: 환경이나 타이밍에 의존하지 않아야 합니다.
- 철저함: 0, NULL, 빈 집합, 예외와 같은 경계 사례까지 다뤄야 합니다(음수 테스트에는
-- { serverError xyz }및-- { clientError xyz }구문을 사용하십시오), - 테스트 끝에 테이블을 정리합니다(남은 것이 있을 수 있으므로).
- 다른 테스트가 동일한 내용을 테스트하지 않는지 확인합니다(즉, 먼저 grep해 보십시오).
테스트 실행 제한
.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 | 빠른 테스트에서는 테스트를 실행하지 않습니다 | 테스트에서 빠른 테스트에 비활성화된 MySQL 테이블 엔진을 사용합니다 |
fasttest-only | 테스트를 빠른 테스트에서만 실행합니다 | |
no-[asan, tsan, msan, ubsan] | sanitizers가 적용된 빌드에서는 테스트를 비활성화합니다 | 테스트가 sanitizer와 호환되지 않는 QEMU에서 실행됩니다 |
no-replicated-database | 기본 데이터베이스가 ReplicatedDatabaseEngine를 사용할 때 테스트를 비활성화합니다 | |
no-ordinary-database | 기본 데이터베이스 엔진이 Ordinary일 때 테스트를 비활성화합니다 | |
no-parallel | 이 테스트와 다른 테스트를 병렬로 실행하지 않도록 합니다 | 테스트가 system 테이블에서 읽기 때문에 불변식이 깨질 수 있습니다 |
no-parallel-replicas | 병렬 레플리카가 활성화된 경우 테스트를 비활성화합니다 | |
no-debug | Debug 빌드에서는 테스트를 비활성화합니다 | |
no-release | Release 빌드에서는 테스트를 비활성화합니다 | |
no-darwin | macOS(Darwin)에서는 테스트를 비활성화합니다 | 테스트가 분산 쿼리, procfs, HTTP 서버와 같은 Linux 전용 기능에 의존합니다 |
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.
위 설정에 더해, 특정 ClickHouse 기능 사용 여부를 정의하기 위해 system.build_options의 USE_* 플래그도 사용할 수 있습니다.
예를 들어 테스트에서 MySQL 테이블을 사용하는 경우 use-mysql 태그를 추가해야 합니다.
무작위 설정에 대한 제한 지정
.sh 테스트에서는 제한을 태그 옆 줄의 주석에 작성하거나, 태그를 지정하지 않은 경우 둘째 줄의 주석에 작성합니다:
.sql 테스트에서는 태그를 tags 옆줄이나 첫 번째 줄에 SQL 주석으로 넣습니다.
None을 사용할 수 있습니다.
테스트 이름 선택
00422_hash_function_constexpr.sql과 같이 5자리 prefix로 시작하며, 그 뒤에 설명적인 이름이 붙습니다.
prefix를 선택하려면 디렉터리에 이미 있는 가장 큰 prefix를 찾은 다음 1을 더하십시오.
반드시 발생해야 하는 오류 확인
x에 대해 코드 49의 오류를 반환하는지 확인합니다.
오류가 발생하지 않거나 다른 오류가 발생하면 테스트는 실패합니다.
클라이언트 측에서 오류가 발생하는지 확인하려면 대신 clientError 어노테이션을 사용하십시오.
오류 메시지의 특정 문구는 확인하지 마십시오. 향후 변경될 수 있으므로 테스트가 불필요하게 실패할 수 있습니다.
오류 코드만 확인하십시오.
기존 오류 코드가 필요에 비해 충분히 정확하지 않다면 새 코드를 추가하는 것을 고려하십시오.
분산 쿼리 테스트하기
127.0.0.{1..2} 주소와 함께 remote 테이블 함수를 활용할 수 있습니다. 또는 서버 설정 파일에 미리 정의된 test_shard_localhost 같은 테스트 클러스터를 사용할 수도 있습니다.
서버가 분산 쿼리를 지원하도록 구성된 올바른 환경에서 CI가 테스트를 실행할 수 있도록 테스트 이름에 shard 또는 distributed라는 단어를 추가해야 합니다.
임시 파일 작업
$CLICKHOUSE_TEST_UNIQUE_NAME를 사용해 현재 실행 중인 테스트마다 임시 파일에 고유한 이름을 지정해야 합니다.
그러면 설정 중에 만들거나 정리 중에 삭제하는 파일이 해당 테스트에서만 사용하는 파일이며, 병렬로 실행 중인 다른 테스트의 파일이 아님을 확실히 할 수 있습니다.
알려진 버그
tests/queries/bugs 디렉터리에 배치합니다.
이 테스트는 버그가 수정되면 tests/queries/0_stateless로 이동됩니다.
통합 테스트
tests/integration/README.md를 참조하십시오.
ClickHouse와 서드파티 드라이버 간 통합은 테스트하지 않습니다.
또한 현재는 JDBC 및 ODBC 드라이버에 대한 통합 테스트도 제공하지 않습니다.
단위 테스트
ENABLE_TESTS CMake 옵션으로 테스트 빌드를 활성화하거나 비활성화할 수 있습니다.
단위 테스트(및 기타 테스트 프로그램)는 코드베이스 전반의 tests 하위 디렉터리에 있습니다.
단위 테스트를 실행하려면 ninja test를 입력하십시오.
일부 테스트는 gtest를 사용하지만, 일부는 테스트가 실패하면 0이 아닌 종료 코드를 반환하는 단순한 프로그램입니다.
코드가 이미 기능 테스트로 커버된다면 단위 테스트를 반드시 작성할 필요는 없습니다(그리고 기능 테스트는 일반적으로 사용하기가 훨씬 더 간단합니다).
개별 gtest 테스트 항목은 실행 파일을 직접 호출하여 실행할 수 있습니다. 예시는 다음과 같습니다:
성능 테스트
tests/performance/에 있습니다.
각 테스트는 테스트 케이스 설명이 포함된 .xml 파일로 구성됩니다.
테스트는 docker/test/performance-comparison 도구로 실행됩니다. 실행 방법은 readme 파일을 참조하십시오.
각 테스트는 하나 이상의 쿼리(매개변수 조합을 포함할 수 있음)를 루프에서 실행합니다.
특정 시나리오에서 ClickHouse의 성능을 개선하려고 하며, 그 개선 효과를 단순한 쿼리로 확인할 수 있다면 성능 테스트를 작성할 것을 강력히 권장합니다.
또한 비교적 독립적이고 지나치게 난해하지 않은 SQL 함수를 추가하거나 수정할 때도 성능 테스트를 작성하는 것이 좋습니다.
테스트 중에는 언제나 perf top 또는 다른 perf 도구를 함께 사용하는 것이 유용합니다.
테스트 도구와 스크립트
tests 디렉터리의 일부 프로그램은 미리 준비된 테스트가 아니라 테스트 도구입니다.
예를 들어 Lexer에는 stdin을 토큰화한 뒤 결과를 색상 적용된 형태로 stdout에 출력하는 src/Parsers/tests/lexer 도구가 있습니다.
이런 도구는 코드 예시로 활용할 수 있을 뿐 아니라, 기능을 살펴보거나 수동으로 테스트할 때도 사용할 수 있습니다.
기타 테스트
tests/external_models에는 머신 러닝 모델에 대한 테스트가 있습니다.
이 테스트는 더 이상 업데이트되지 않으므로 통합 테스트로 옮겨야 합니다.
쿼럼 삽입을 위한 별도의 테스트도 있습니다.
이 테스트는 별도의 서버에서 ClickHouse 클러스터를 실행하고 다양한 장애 상황을 에뮬레이션합니다. 예를 들어 네트워크 분리, 패킷 손실(ClickHouse 노드 간, ClickHouse와 ZooKeeper 간, ClickHouse 서버와 클라이언트 간 등), kill -9, kill -STOP, kill -CONT를 Jepsen과 유사한 방식으로 수행합니다. 그런 다음 승인된 모든 삽입은 기록되었고 거부된 모든 삽입은 기록되지 않았는지 확인합니다.
수동 테스트
programs/clickhouse-server로 디렉터리를 변경한 뒤 ./clickhouse-server를 실행합니다. 그러면 기본적으로 현재 디렉터리의 구성(config.xml, users.xml, config.d 및 users.d 디렉터리 내부의 파일)을 사용합니다. ClickHouse 서버에 연결하려면 programs/clickhouse-client/clickhouse-client를 실행합니다.
모든 clickhouse 도구(server, client 등)는 실제로 clickhouse라는 단일 바이너리를 가리키는 심볼릭 링크일 뿐입니다.
이 바이너리는 programs/clickhouse에 있습니다.
모든 도구는 clickhouse-tool 대신 clickhouse tool 형식으로도 호출할 수 있습니다.
또는 ClickHouse 패키지를 설치할 수도 있습니다. ClickHouse 리포지토리의 안정 릴리스를 사용하거나, ClickHouse 소스 루트에서 ./release를 실행해 직접 패키지를 빌드할 수 있습니다.
그런 다음 sudo clickhouse start로 서버를 시작합니다(서버를 중지하려면 stop을 사용합니다).
로그는 /etc/clickhouse-server/clickhouse-server.log에서 확인하십시오.
시스템에 ClickHouse가 이미 설치되어 있다면, 새 clickhouse 바이너리를 빌드한 다음 기존 바이너리로 대체할 수 있습니다:
config.xml에서 포트 번호를 변경하거나 (config.d 디렉터리의 파일에서 재정의할 수도 있습니다), 적절한 데이터 경로를 설정한 뒤 실행할 수 있습니다.
clickhouse 실행 파일은 의존성이 거의 없어서 다양한 Linux 배포판에서 동작합니다.
서버에서 변경 사항을 빠르게 시험해 보려면, 새로 빌드한 clickhouse 실행 파일을 서버로 scp한 다음 위의 예시처럼 실행하면 됩니다.
빌드 테스트
- Darwin x86_64(macOS)용 크로스 컴파일
- FreeBSD x86_64용 크로스 컴파일
- Linux AArch64용 크로스 컴파일
- 시스템 패키지의 라이브러리를 사용해 Ubuntu에서 빌드(권장되지 않음)
- 라이브러리를 공유 링크 방식으로 빌드(권장되지 않음)
프로토콜 호환성 테스트
- 이전 버전의 ClickHouse에서 기록한 데이터를 새 버전에서 성공적으로 읽을 수 있는지
- 서로 다른 ClickHouse 버전으로 구성된 클러스터에서 분산 쿼리가 정상적으로 작동하는지
컴파일러의 도움
src 디렉터리에 있음)는 -Wall -Wextra -Werror와 몇 가지 추가 경고 옵션을 활성화한 상태로 빌드됩니다.
다만 이러한 옵션은 서드파티 라이브러리에는 활성화되지 않습니다.
Clang에는 훨씬 더 유용한 경고가 많습니다. -Weverything으로 확인한 뒤, 그중 일부를 기본 빌드에 포함하도록 선택할 수 있습니다.
ClickHouse는 개발 환경과 프로덕션 환경 모두에서 항상 clang으로 빌드합니다.
로컬 머신에서는 디버그 모드로 빌드할 수 있습니다(노트북 배터리를 절약하기 위해서입니다). 하지만 더 나은 제어 흐름 및 프로시저 간 분석 덕분에, 컴파일러는 -O3에서 더 많은 경고를 생성할 수 있다는 점에 유의하십시오.
clang으로 디버그 모드에서 빌드하면 libc++의 디버그 버전이 사용되며, 이를 통해 런타임에 더 많은 오류를 포착할 수 있습니다.
Sanitizers
로컬에서 실행할 때 프로세스(ClickHouse 서버 또는 클라이언트)가 시작 시점에 충돌하면 주소 공간 배치 난수화(ASLR)를 비활성화해야 할 수 있습니다:
sudo sysctl kernel.randomize_va_space=0Address 새니타이저
스레드 새니타이저
메모리 새니타이저
정의되지 않은 동작 새니타이저
Valgrind (memcheck)
re2 라이브러리에는 알려진 오탐이 1건 있습니다. 자세한 내용은 이 글을 참조하십시오.
퍼징
src/Parsers/fuzzers/lexer_fuzzer.cpp에서 확인할 수 있습니다.
libFuzzer 전용 구성, dictionaries, corpus는 tests/fuzz에 저장됩니다.
사용자 입력을 처리하는 모든 기능에 대해 퍼즈 테스트를 작성할 것을 권장합니다.
퍼저는 기본적으로 빌드되지 않습니다.
퍼저를 빌드하려면 -DENABLE_FUZZING=1 및 -DENABLE_TESTS=1 옵션을 모두 설정해야 합니다.
퍼저를 빌드할 때는 Jemalloc을 비활성화하는 것을 권장합니다.
ClickHouse 퍼징을
Google OSS-Fuzz에 통합하는 데 사용하는 구성은 docker/fuzz에서 확인할 수 있습니다.
또한 무작위 SQL 쿼리를 생성하고, 이를 실행해도 server가 종료되지 않는지 확인하는 간단한 퍼즈 테스트도 사용합니다.
이는 00746_sql_fuzzy.pl에서 확인할 수 있습니다.
이 테스트는 지속적으로(하룻밤 이상 장시간) 실행해야 합니다.
또한 많은 코너 케이스를 찾아낼 수 있는 정교한 AST 기반 쿼리 퍼저도 사용합니다.
이 퍼저는 쿼리 AST에서 무작위 순열과 치환을 수행합니다.
이전 테스트의 AST 노드를 기억해 두었다가, 이를 무작위 순서로 처리하면서 이후 테스트를 퍼징하는 데 활용합니다.
이 퍼저에 대해 자세히 알아보려면 이 블로그 글을 참조하십시오.
스트레스 테스트
- 서버가 충돌하지 않고, 디버그 또는 새니타이저 트랩이 발생하지 않습니다.
- 교착 상태가 없습니다.
- 데이터베이스 구조의 일관성이 유지됩니다.
- 테스트 후 서버를 예외 없이 정상적으로 중지한 뒤 다시 시작할 수 있습니다.
Thread fuzzer
보안 감사
정적 분석기
clang-tidy를 실행합니다.
clang-static-analyzer 검사도 활성화되어 있습니다.
일부 스타일 검사에도 clang-tidy를 사용합니다.
clang-tidy, Coverity, cppcheck, PVS-Studio, tscancode, CodeQL을 평가했습니다.
사용 방법에 대한 지침은 tests/instructions/ 디렉터리에서 확인할 수 있습니다.
CLion을 IDE로 사용하는 경우 일부 clang-tidy 검사를 별도 설정 없이 활용할 수 있습니다.
셸 스크립트의 정적 분석에도 shellcheck를 사용합니다.
강화
런타임 무결성 검사
- 그리고 성능 저하도 거의 없습니다.
코드 스타일
utils/check-style 스크립트를 사용할 수 있습니다.
코드 스타일을 올바르게 적용하려면 clang-format을 사용할 수 있습니다.
.clang-format 파일은 소스 루트에 있습니다.
이 파일은 대체로 실제 코드 스타일과 일치합니다.
하지만 기존 파일에 clang-format을 적용하면 포맷팅이 오히려 나빠질 수 있으므로 권장하지 않습니다.
clang 소스 리포지토리에서 clang-format-diff 도구를 찾을 수 있습니다.
또는 uncrustify 도구를 사용해 코드를 다시 포맷할 수도 있습니다.
구성은 소스 루트의 uncrustify.cfg에 있습니다.
이 도구는 clang-format만큼 충분히 테스트되지는 않았습니다.
CLion에는 자체 코드 포매터가 있으며, 코드 스타일에 맞게 조정해야 합니다.
또한 코드의 오탈자를 찾기 위해 codespell도 사용합니다.
이 역시 자동화되어 있습니다.