Swift Digest
SE-0501 | Swift Evolution

HTMLカバレッジレポート

HTML Coverage Report

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

このダイジェストはClaude Opus 4.7 / 4.8によって生成されたものです(License)。原文はこちら

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 オプションが追加され、値として json または html を取ります。指定しない場合のデフォルトは従来どおり json です。

このオプションは複数回指定でき、1 回のテスト実行で両方の形式を同時に出力することもできます。

swift test --enable-coverage \
    --coverage-format json \
    --coverage-format html

SwiftPM は指定された一意な形式ごとにループしてレポートを生成します。

-Xcov による llvm-cov への引数の引き渡し

llvm-cov show には細かな設定オプションが多数あり、これらをすべて swift test のフラグとして露出させるのは現実的ではありません。代わりに、-Xlinker-Xcc と同様の仕組みとして -Xcov オプションが追加され、指定した引数がそのまま llvm-cov に渡されます。

-Xcov の値は次の構文を取ります。

-Xcov [<coverage-format>=]<value>

先頭に形式名 + = を付けることで、特定の形式にのみ引数を渡すことができます。形式名を付けない場合は、すべての形式に共通で渡されます。値に = を含む場合は、最初の = の前までが形式名として扱われるため、--project-title="SwiftPM" のような = 付きの引数もそのまま書けます。

swift test --enable-coverage \
    --coverage-format html --coverage-format json \
    -Xcov --title -Xcov "My title"
  • -Xcov html=--title: --title を HTML レポート生成にのみ渡す
  • -Xcov json=myarg: myarg を JSON レポート生成にのみ渡す
  • -Xcov commonArg: commonArg をすべての形式に渡す
  • -Xcov html=--project-title="SwiftPM": --project-title="SwiftPM" を HTML レポート生成にのみ渡す

未対応の形式名を指定した場合(-Xcov notASupportedFormat=value など)は形式名として解釈されず、値全体がすべての形式に渡されます。

レポートの出力場所

HTML レポートはデフォルトでは scratch path(ビルドディレクトリ)配下に作成されます。CI によっては「sandbox」扱いのディレクトリ配下にあるファイルしかアーカイブ対象にできない(例: Jenkins)ため、-Xcov 経由で llvm-cov の出力先オプションを渡して上書きできます。リポジトリが sandbox 内にチェックアウトされる前提であれば、レポートを sandbox 内の任意のパスに出してそのままアーカイブに載せる、といった運用が可能です。

--show-coverage-path の拡張

これまで swift test --show-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"
# }

既存オプションの整理

あわせて、カバレッジ関連のオプション名が次のように整理されます。

  • --enable-coverage / --disable-coverage が正規の名前となり、従来の --enable-code-coverage / --disable-code-coverage は deprecated として残ります。
  • --show-coverage-path が正規の名前となり、従来の --show-codecov-path / --show-code-coverage-path は deprecated として残ります。
  • swift build 側のカバレッジ関連ヘルプも同じ命名に統一されます。

まとめ

この Proposal により、手元での反復的な確認でも CI 上でのアーティファクト化でも、余計な wrapper スクリプトを書かずに swift test --coverage-format html 一つで HTML レポートが得られるようになります。内部的に llvm-cov を呼び出すという性質上、LLVM ツール側のセキュリティ上の挙動は Swift プロジェクトの管轄外ですが、JSON レポートの生成経路には変更がないため既存パッケージへの影響はありません。