Swift Digest
SE-0501 | Swift Evolution

HTML Coverage Report

Proposal
SE-0501
Authors
Sam Khouri
Review Manager
David Cummings
Status
Accepted

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 レポートの生成経路には変更がないため既存パッケージへの影響はありません。