Swift Digest
ST-0019 | Swift Evolution

タグ・バグ・時間制限の trait のメタデータを event stream に含める

Include metadata for tags, bugs, and time limit traits in event stream

Proposal
ST-0019
Authors
Sam Khouri
Review Manager
Paul LeMarquand
Status
Implemented (Swift 6.4)

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

01 何が問題だったのか

Swift Testing は、テスト実行中のイベントを JSON Lines 形式で書き出す JSON event stream を備えており、IDE や CI/CD などの外部ツールはこれを購読してテスト結果を解析・可視化します。ところが現状の JSON スキーマには、テストに付与されたメタデータの一部しか含まれておらず、外部ツールから利用できないものがありました。

具体的には、以下のメタデータが JSON 出力に含まれていませんでした。

  • タグ.tags(...))。テストをカテゴリ分けするために使われます
  • バグの関連付け.bug(...))。テストに関連するバグや課題を表します
  • 時間制限.timeLimit(...))。テストの実行時間に上限を与えます

これらが取得できないことで、外部ツールでは次のような体験を提供しづらい状況がありました。

  • IDE 拡張からタグでテストを絞り込む
  • CI/CD でテストカテゴリ別にレポートを生成する
  • パフォーマンス監視ツールで時間制限つきテストを追跡する
  • バグトラッカと連携して、失敗したテストに関連するバグを表示する

これらのメタデータは Swift Testing 内部では既に保持されているにもかかわらず、JSON event stream のスキーマには露出していなかった、というのが本 Proposal の出発点です。

02 どのように解決されるのか

JSON event stream のスキーマバージョン "6.4" 以降で、テスト関数およびテストスイートを表す構造に "tags""bugs""timeLimit" の 3 つのフィールドを追加します。これらはいずれもオプショナルで、対応する trait がテストに付与されていない場合は出力から省略され、JSON が無駄に肥大化したり null で埋め尽くされたりしないようになっています。

追加は純粋に additive で、既存のスキーマバージョン("0""6.3" など)を消費しているツールには影響しません。

スキーマの追加内容

<test-suite> および <test-function> 構造に対する BNF 上の差分は次のとおりです。

   ["displayName": <string>,]
   "sourceLocation": <source-location>,
   "id": <test-id>,
-  "isParameterized": <bool>
+  "isParameterized": <bool>,
+  ["tags": <array:tag>,]
+  ["bugs": <array:bug>,]
+  ["timeLimit": <number>]
 }

+<tag> ::= <string>
+
+<bug> ::= {
+  ["url": <string>,]
+  ["id": <string>,]
+  ["title": <string>]
+}

各フィールドの意味は次のとおりです。

  • tags: テストに付与されたタグの文字列表現の配列
  • bugs: テストに関連付けられたバグの配列。要素は url / id / title を持つオブジェクトで、いずれもオプショナル
  • timeLimit: テストに設定された時間制限を秒数で表す数値

出力例

たとえば次のようなテスト関数を考えます。

extension Tag {
  @Tag public static var blue: Self
  @Tag public static var red: Self

  public enum Foo {
    @Tag public static var bar: Self
  }
}

@Test(
  .tags(.blue),
  .tags(Tag.red),
  .bug("https://my.defect.com/1234"),
  .bug(id: "12345", "other defect"),
  .timeLimit(.minutes(testTimeLimit + 1)),
  .timeLimit(.minutes(testTimeLimit)),
  .timeLimit(.minutes(testTimeLimit + 10)),
  arguments: expectedArgs as [String]
)
func example() {}

このテストに対して新スキーマで生成される JSON の payload には、次のような形でメタデータが含まれます。

{
  "kind": "test",
  "payload": {
    "bugs": [
      { "url": "https://my.defect.com/1234" },
      { "id": "12345", "title": "other defect" }
    ],
    "tags": [
      "blue",
      "red"
    ],
    "timeLimit": 3
  }
}

タグの文字列化ルール

tags フィールドの各要素は、@Tag で宣言された Swift のプロパティを文字列化したものになります。Tag 型からの相対パスとして表現され、Tag 自体や Testing モジュール名のような共通の前置きは取り除かれます。

trait の書き方 JSON での文字列
.tags(.blue) "blue"
.tags(Tag.blue) "blue"
.tags(Testing.Tag.blue) "blue"
.tags(.Foo.bar) "Foo.bar"
.tags(Tag.Foo.bar) "Foo.bar"
.tags(Testing.Tag.Foo.bar) "Foo.bar"

これにより、Swift コード上の表記の揺れにかかわらず、同じタグであれば JSON 上でも同じ文字列で識別できます。

バグと時間制限の取り扱い

bugs の各要素では url / id / title のいずれもオプショナルで、.bug(_:).bug(id:_:) などの呼び出しで指定された情報のみが含まれます。指定されていないキーはオブジェクトから省略されます。

timeLimit は秒単位の数値です。同一テストに複数の .timeLimit(...) が付与されている場合は、Swift Testing 内部で最も短い時間制限が選ばれ、その値が秒に変換されて出力されます。上記の例では、testTimeLimit を含む 3 つの指定のうち最も短い .minutes(testTimeLimit)(テスト実行時の値で 3 秒)が採用されています。

ツール側で期待できる体験

これらのフィールドが揃うことで、JSON event stream を消費するツール側で次のような体験を組み立てられるようになります。

  • IDE がテストレポートをタグで並べ替え・絞り込みする
  • CI 上でタグごとに集計したテストレポートを生成する
  • 時間制限つきテストを抽出し、その実行時間を追跡する
  • 既知バグに関連付けられたテストの失敗・成功を、バグトラッカ側と紐付けて表示する

03 今後の見通し

本 Proposal の議論の中で、いくつかの発展方向が挙げられています。いずれも将来の構想であり、実現を約束するものではありません。

  • timeLimit の単位の表現。今回追加される timeLimit フィールドは、Swift Testing 既存 API との一貫性を優先して短い名前を採用し、単位(秒)はスキーマ仕様の文書側に書く形になっています。timeLimitInSeconds のように単位をフィールド名に含める方向や、値とともに単位を表現する方向については、別途 Proposal を立てて検討するとされています。
  • 追加のメタデータの露出。今回扱った tags / bugs / timeLimit 以外にも、Swift Testing が持つ trait をエコシステムの発展に合わせて JSON event stream に出していく余地があるとされています。