RustプログラムのCoverageをGRCOVで取得する

2021/09/13

rust は単体テストの実行等は cargo に統合されていて非常に扱い安いのですが、カバレッジの取得や確認は統合されていません。

また、カバレッジの取得には nightly コンパイラが必要になる等ちょっとした要件があります。

プロジェクトにカバレッジの取得を追加する度に毎度google検索していたりするので、これを一旦まとめておきます。

カバレッジの取得に必要な物

カバレッジ取得の流れ

カバレッジの取得はカバレッジプロファイリングオプションを付けてターゲットをコンパイルする、テストを実行しカバレッジ情報を取得する。 grcov でソースとカバレッジ情報を統合する流れになります。

カバレッジプロファイル指定でのビルド

プロファイリングオプションを付けてビルドするには基本的には RUSTFLAGS を指定して cargo build するだけです。nightly コンパイラを使うのを考慮すると以下になります。

CARGO_INCREMENTAL=0, RUSTFLAGS="-Zinstrument-coverage -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off" cargo +nightly build

インクリメンタルビルドの抑止はオプション違いのバイナリがビルドディレクトリ内で混ざるのを防ぐ為に必要です。

注意するべきは、このまま実行するとプロジェクトの /target/debug 配下にカバレッジプロファイル出力が埋め込まれたバイナリが生成されます。

カバレッジ取得後にそのままのバイナリを使ってテストや諸々を実行すると膨大なカバレッジプロファイル出力が生成される可能性があります。通常のバイナリに戻すには cargo clean && cargo build をする必要がありますが、clean 後のビルドは中々重いのでできれば避けたいです。(clean せずに build すると単なるタイムスタンプの違いによって評価されるので最悪)

これを避けるには CARGO_BUILD_TARGET_DIR 環境変数でカバレッジ用バイナリを別ディレクトリに生成させる事ができます。

テストの実行

プロファイルインストルメントが設定されたバイナリが用意できたら、プロファイル情報の出力先を指定してテストを実行します。

Running the instrumented program

LLVM_PROFILE_FILE 環境変数によりプロファイル情報の出力先が指定されます。 rust プログラムでは target 配下を指定するべきでしょう。

カバレッジプロファイル情報の集約

grcov によってカバレッジプロファイル情報を集約しソースとのマッピングをします。 grcov は lcov 等主要なカバレッジ結果フォーマットでの出力を生成できるので、手元のエディタでのハイライト表示に使えるフォーマットを指定するとエディタ上でカバレッジがハイライトされます。

注意点としては grcov は内部でLLVMのプロファイルツールを利用しており、基本的には rust のツールチェインとしてインストールされた物を利用します。

デフォルトを Stable コンパイラでコンパイルしていてカバレッジだけを Nightly でコンパイルしている場合には grcov に Nightly のツールチェインを利用させる必要があります。

環境変数 RUSTUP_TOOLCHAIN によって grcov に Nightly ツールチェインを指定する事ができます。

scripting

ここまでの説明をスクリプト化したのが以下になります。

#!/usr/bin/env bash

set -eux
export CARGO_BUILD_TARGET_DIR="./target/cover"
export RUSTFLAGS="-Zinstrument-coverage -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off"
export RUSTDOCFLAGS="-Cpanic=abort"

cargo +nightly build --verbose

rm -rf ./target/cover/debug/deps/profraw
mkdir ./target/cover/debug/deps/profraw/

export LLVM_PROFILE_FILE="./target/cover/debug/deps/profraw/coverage-%p-%m.profraw"

cargo +nightly test --verbose

RUSTUP_TOOLCHAIN="nightly" grcov ./target/cover/debug/deps/profraw/ -b ./target/cover/debug/deps -s . -t lcov --llvm --branch --ignore-not-existing --ignore "/*" -o ./target/lcov.info

最終的に ./target/lcov.info にカバレッジ情報が出力されます。

cargo-make でのカバレッジビルド

cargo-make は rust 向けのメイクツールで、ビルド及びテストの一連の実行が可能です。

この cargo-make 向けの grcov でのカバレッジ採取用の Makefile を cargo-make-grcov-coverageで公開しています。

カバレッジ情報をエディタ表示

vscode の場合には Code Coverage Highlighter が良いかなと思いました。 Code Coverage としか比較していませんが、uncover を suggestion に上げて来られるのはちょっとうっとおしすぎるので色分けが見れるぐらいがちょうど良いと思います。

拡張機能の設定で生成されたカバレッジ情報を読み込むように指定するだけで、テストでカバーされている行とそうでない行が色分け表示されます。