HTML Coverage Report
01 何が問題だったのか
SwiftPM は swift test --enable-code-coverage でテストを実行するとコードカバレッジを取得できますが、出力できるレポート形式は JSON のみでした。JSON は CI や外部ツールに取り込んで後処理するのには適していますが、人間が手元で眺めてどの行がカバーされているかを確認する用途にはあまり向きません。
開発者が HTML 形式のレポートを欲しい場合、現状は一度 swift test --enable-code-coverage を走らせてから、swift test --show-codecov-path でカバレッジデータの位置を得て、さらに自分で llvm-cov show を次のように組み立てて呼び出す必要がありました。
swift test --enable-code-coverage
swift test --show-codecov-path
llvm-cov show \
--project-title="HelloWorld" \
--format="html" \
--output-dir=".coverage" \
--instr-profile=".build/arm64-apple-macosx/debug/codecov/default.profdata" \
".build/.../HelloWorldPackageTests.xctest/Contents/MacOS/HelloWorldPackageTests" \
"Sources"
この手作業にはいくつかの問題があります。
- ビルド成果物のパスや profdata の場所、テストバイナリの位置を利用者が自分で組み立てる必要がある
- CI 上で HTML レポートをアーティファクトとして残したい場合にも、各プロジェクトで同じような wrapper スクリプトを書くことになる
- 手元で iterate しながら「いまの変更で十分にカバーできているか」を素早く確認したいユースケースに対して、ステップが多すぎる
SwiftPM 自身がすでに JSON カバレッジの生成を取り仕切っているのだから、同じ枠組みから HTML レポートも出せるべき、というのがこの Proposal の出発点です。
02 どのように解決されるのか
swift test にカバレッジレポートの出力形式を選ぶためのオプションを追加し、SwiftPM 自身が llvm-cov show を適切な引数で呼び出して HTML レポートを生成できるようにします。JSON レポートの生成に関するこれまでの挙動は変更されません。
--coverage-format による形式の選択
swift test に --coverage-format オプション(別名 --codecov-format / --code-coverage-format)が追加され、値として json または html を取ります。指定しない場合のデフォルトは従来どおり json です。
このオプションは複数回指定でき、1 回のテスト実行で両方の形式を同時に出力することもできます。
swift test --enable-code-coverage \
--coverage-format json \
--coverage-format html
SwiftPM は指定された一意な形式ごとにループしてレポートを生成します。
HTML レポートの設定: response file
llvm-cov show には細かな設定オプションが多数あるため、これらをすべて swift test のフラグとして露出させるのは現実的ではありません。代わりに、リポジトリ直下の次の場所に置かれる response file から読み込みます。
<repo>/.swiftpm/configuration/coverage.html.report.args.txt
このファイルに書かれた引数はそのまま llvm-cov show に渡されます。ユーザーが誤って --format=text のような指定を入れていても、SwiftPM は response file の引数の後ろに常に --format=html を追加するため、最終的には必ず HTML レポートが生成されます。response file の内容自体の妥当性検査は行われず、SwiftPM が解釈するのは出力先の特定に必要な範囲のみです。
レポートの出力場所
HTML レポートはデフォルトでは scratch path(ビルドディレクトリ)配下に作成されます。CI によっては「sandbox」扱いのディレクトリ配下にあるファイルしかアーカイブ対象にできない(例: Jenkins)ため、response file で出力先を上書きできるようになっています。リポジトリが sandbox 内にチェックアウトされる前提であれば、レポートを sandbox 内の任意のパスに出してそのままアーカイブに載せる、といった運用が可能です。
--show-coverage-path の拡張
これまで swift test --show-coverage-path(--show-codecov-path / --show-code-coverage-path)は、JSON カバレッジレポートの絶対パスを 1 行で表示するだけでした。--coverage-format が複数形式に対応したことで、出力も形式ごとに見分けられる必要があります。
--coverage-formatが指定されない、または 1 回だけ指定された場合の挙動は従来どおり--coverage-formatが複数指定された場合、--show-coverage-pathの出力は形式ごとのパスを並べた形になる
--show-coverage-path はオプション付きの値を取れるようになり、text(デフォルト)または json のいずれかを指定できます。text は人間向けのテキスト表示、json は機械可読な JSON オブジェクトで、キーが形式名、値がその出力先パスになります。
# 複数形式 + テキスト表示(デフォルト)
swift test -c release --build-system swiftbuild \
--show-coverage-path \
--coverage-format html --coverage-format json
# →
# Html: .../codecov/Simple-html
# Json: .../codecov/Simple.json
# 複数形式 + JSON 表示
swift test -c release --build-system swiftbuild \
--show-coverage-path json \
--coverage-format html --coverage-format json
# →
# {
# "html" : ".../codecov/SwiftPM-html",
# "json" : ".../codecov/SwiftPM.json"
# }
まとめ
この Proposal により、手元での反復的な確認でも CI 上でのアーティファクト化でも、余計な wrapper スクリプトを書かずに swift test --coverage-format html 一つで HTML レポートが得られるようになります。内部的に llvm-cov を呼び出すという性質上、LLVM ツール側のセキュリティ上の挙動は Swift プロジェクトの管轄外ですが、JSON レポートの生成経路には変更がないため既存パッケージへの影響はありません。