テストの種類
- 機能テスト - クエリとスクリプトのセットで、次のような重なり合うサブセットが含まれます
- Fast test - 最小のサブセット
- データをデータベースに投入する必要がない Stateless tests
- 並列実行できない逐次テスト
- クラスター内で
pytestにより実行される 結合テスト - 単体テスト
- 性能テスト
- ビルドテスト
- サニタイザ
- ファザー そのほかにもいくつかあります。以下のセクションを参照してください。
機能テスト
./tests/queries ディレクトリにあります。
各テストは、.sql と .sh の 2 種類のいずれかです。
.sqlテストは、clickhouse-clientにパイプして渡すシンプルな SQL スクリプトです。.shテストは、それ自体で実行されるスクリプトです。
.sh テストより SQL テストのほうが望ましいです。
.sh テストを使うのは、clickhouse-client に入力データをパイプする、または clickhouse-local をテストするなど、純粋な SQL だけでは検証できない機能をテストしなければならない場合に限るべきです。
データ型
DateTime と DateTime64 をテストする際によくある間違いは、server が特定のタイムゾーン (たとえば “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 を参照してください。
すべてのテストを実行することも、テスト名のフィルターを指定して一部のテストのみを実行することもできます: ./clickhouse-test substring。
テストを並列実行したり、ランダムな順序で実行したりするためのオプションもあります。
Fast test の実行
t3.2xlarge の AWS amd64 Ubuntu インスタンスで動作します。
- 必要なパッケージをインストールし、再ログインします。
- ソースコードを取得します。
- コードをビルドして、“fast tests”を実行します。
ssh 接続が切れた後も実行を継続できるように、nohup または disown を使用できます。
ステートレステストの実行
m7i.8xlarge の AWS amd64 Ubuntu インスタンスで動作します。
- 必要な前提条件をインストールし、再ログインします。
- ソースコードを取得します。
- コードをビルドします。
- 並列に実行できるステートレステストを実行します。
python -m ci.praktika run を実行すると、特定の継続的インテグレーションジョブが実行されます。ClickHouse CI の詳細は、こちらを参照してください。
新しいテストの追加
queries/0_stateless ディレクトリに .sql または .sh ファイルを作成します。
次に、clickhouse-client < 12345_test.sql > 12345_test.reference または ./12345_test.sh > ./12345_test.reference を使って、対応する .reference ファイルを生成します。
テストでは、事前に自動作成されるデータベース test 内のテーブルに対してのみ、CREATE、drop、select などの操作を行ってください。
一時テーブルを使用しても問題ありません。
CI と同じ環境をローカルでセットアップするには、テスト用の設定をインストールします (これにより ZooKeeper のモック実装が使用され、一部の設定が調整されます)
テストは次の条件を満たすべきです
- 必要最小限であること: 必要最小限のテーブル、カラム、複雑さだけにとどめること
- 高速であること: 数秒以上かからないこと (望ましくは1秒未満)
- 正確かつ決定論的であること: テスト対象の機能が動作していない場合に、かつその場合に限って失敗すること
- 独立しておりステートレスであること: 環境やタイミングに依存しないこと
- 網羅的であること: 0、NULL、空集合、例外などのコーナーケースをカバーすること (ネガティブテストでは、そのために構文
-- { serverError xyz }および-- { clientError xyz }を使用すること) - テストの最後にテーブルをクリーンアップすること (残り物があった場合に備えて)
- 他のテストで同じ内容をテストしていないことを確認すること (つまり、先に grep すること) 。
テスト実行の制限
.sql テストでは、タグは SQL コメントとして先頭行に記述します。
.sh テストでは、タグは2行目にコメントとして記述します:
| Tag name | What it does | Usage example |
|---|---|---|
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 では無効になっている |
fasttest-only | テストは Fast test でのみ実行される | |
no-[asan, tsan, msan, ubsan] | サニタイザ を使用したビルドではテストを無効にする | テストは QEMU 上で実行されるが、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 テストでは、制限はタグのある行の横にコメントとして記述します。タグが指定されていない場合は 2 行目に記述します。
.sql テストでは、タグは SQL コメントとして、タグ記述の次の行、または先頭行に配置します。
None を使用できます。
テスト名の選択
00422_hash_function_constexpr.sql のように、5桁のプレフィックスの後に内容を表す名前を付けたものにします。
プレフィックスを選ぶには、そのdirectory内ですでに使われている最大のプレフィックスを見つけ、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 で有効または無効にできます。
単体テスト (およびその他のテストプログラム) は、コード中の各所にある tests サブディレクトリに配置されています。
単体テストを実行するには、ninja test を実行します。
一部のテストでは gtest を使用していますが、テストが失敗したときに非ゼロの終了コードを返すだけのプログラムもあります。
コードがすでに 機能テスト でカバーされている場合は、単体テストは必須ではありません (しかも通常は 機能テスト のほうがはるかに簡単に使えます) 。
個別の gtest チェックは、実行可能ファイルを直接呼び出して実行できます。たとえば次のようになります。
性能テスト
tests/performance/ にあります。
各テストは、テストケースの説明を含む .xml ファイルで定義されます。
テストの実行には docker/test/performance-comparison ツールを使用します。実行方法については readme ファイルを参照してください。
各テストでは、1 つまたは複数のクエリ (パラメータの組み合わせを含む場合があります) をループで実行します。
特定のシナリオで ClickHouse の性能を改善したい場合で、その改善が単純なクエリで確認できるのであれば、性能テストを作成することを強く推奨します。
また、比較的独立していて複雑すぎない SQL 関数を追加または変更する際にも、性能テストを作成することを推奨します。
テスト中は、perf top やその他の perf ツールを使うことが常に有効です。
テストツールとスクリプト
testsディレクトリ内のプログラムの中には、実行用のテストとして用意されたものではなく、テストツールであるものがあります。
たとえば Lexer には、標準入力をトークン化し、その結果を色分けして標準出力に出力するツール src/Parsers/tests/lexer があります。
この種のツールは、コード例として使えるほか、調査や手動テストにも利用できます。
その他のテスト
tests/external_models には機械学習モデル用のテストがあります。
これらのテストは更新されておらず、結合テストに移行する必要があります。
クォーラム insert 用の個別のテストもあります。
このテストでは、ClickHouse クラスターを別々のサーバー上で実行し、さまざまな障害ケースをエミュレートします。たとえば、ネットワーク分断、パケット損失 (ClickHouse ノード間、ClickHouse と ZooKeeper 間、ClickHouse server とクライアント間など) 、kill -9、kill -STOP、kill -CONT などで、Jepsen のようなテストです。その後、このテストは、確認応答されたすべての insert が書き込まれており、拒否されたすべての insert は書き込まれていないことを確認します。
手動テスト
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 を使用します。
手元のマシンではデバッグモードでビルドしてもかまいません (ノート PC のバッテリー節約のため) が、-O3 では制御フローやプロシージャ間解析がより適切に行われるため、コンパイラはより多くの警告を生成できる点に注意してください。
clang をデバッグモードでビルドすると、libc++ のデバッグ版が使用され、実行時により多くのエラーを検出できます。
サニタイザ
ローカルで実行している際にプロセス (ClickHouse server またはクライアント) が起動時にクラッシュする場合は、アドレス空間配置のランダム化を無効にする必要があることがあります:
sudo sysctl kernel.randomize_va_space=0Address sanitizer
スレッドサニタイザー
メモリサニタイザー
未定義動作サニタイザー
Valgrind (memcheck)
re2 ライブラリでは既知の誤検知が 1 件あります。詳しくはこちらの記事を参照してください。
ファジング
src/Parsers/fuzzers/lexer_fuzzer.cpp にあります。
LibFuzzer 固有の設定、辞書、コーパスは tests/fuzz に格納されています。
ユーザー入力を扱うあらゆる機能について、ファズテストを書くことを推奨します。
ファザーはデフォルトではビルドされません。
ファザーをビルドするには、-DENABLE_FUZZING=1 と -DENABLE_TESTS=1 の両方のオプションを設定する必要があります。
ファザーをビルドする際は、Jemalloc を無効にすることを推奨します。
ClickHouse のファジングを
Google OSS-Fuzz に統合するための設定は docker/fuzz にあります。
また、ランダムな SQL クエリを生成し、それらを実行してもサーバーが停止しないことを確認する、単純なファズテストも使用しています。
これは 00746_sql_fuzzy.pl にあります。
このテストは継続的に (夜通し、さらに長時間) 実行する必要があります。
さらに、非常に多くのコーナーケースを見つけられる、高度な AST ベースのクエリファザーも使用しています。
これはクエリ AST に対してランダムな並べ替えや置換を行います。
過去のテストの AST ノードを記憶し、それらを後続のテストのファジングに利用しながら、ランダムな順序で処理します。
このファザーについては、このブログ記事で詳しく知ることができます。
ストレステスト
- サーバーがクラッシュせず、debug や sanitizer のトラップがトリガーされないこと。
- デッドロックが発生しないこと。
- データベース構造の整合性が保たれていること。
- テスト後にサーバーを正常に停止でき、例外を発生させることなく再起動できること。
Thread fuzzer
セキュリティ監査
静的アナライザ
clang-tidy はコミットごとに実行しています。
clang-static-analyzer のチェックも有効化されています。
clang-tidy は一部のスタイルチェックにも使われています。
clang-tidy、Coverity、cppcheck、PVS-Studio、tscancode、CodeQL は評価済みです。
使用方法については、tests/instructions/ ディレクトリにある手順を参照してください。
IDE として CLion を使用している場合は、いくつかの clang-tidy チェックをそのまま利用できます。
シェルスクリプトの静的解析には shellcheck も使用しています。
ハードニング
実行時の整合性チェック
- しかも低速ではありません。
コードスタイル
utils/check-style スクリプトを使用できます。
コードのスタイルを整えるには、clang-format を使用できます。
.clang-format ファイルはソースルートにあります。
これは実際のコードスタイルにおおむね沿っています。
ただし、既存のファイルに clang-format を適用するとフォーマットがかえって悪くなるため、推奨されません。
代わりに、clang のソースリポジトリにある clang-format-diff ツールを使用できます。
また、コードを再整形するために uncrustify を試すこともできます。
設定ファイルはソースルートの uncrustify.cfg にあります。
こちらは clang-format ほど十分にはテストされていません。
CLion には独自のコードフォーマッタがあり、私たちのコードスタイルに合わせて調整する必要があります。
また、コード内のタイプミスを見つけるために codespell も使用しています。
これも自動化されています。