Skip to content

macOS 27 で swift-snapshot-testing の知覚差分比較が CGRectValue 例外でテストプロセスごとクラッシュする #49

Description

@temoki

概要

Xcode 27 への移行(#48)で CI を xcode-27 ランナーに移したところ、TortoiseUITests のプロセスが PNG ゴールデンの比較中にクラッシュしました。

#48 では、比較方法から perceptualPrecision を外してバイト単位の比較に切り替え、クラッシュを回避しています。ただ、再現した環境は macOS 27 も Xcode 27 もベータ版 でした。そのため、これが Apple 側のベータ版の不具合なのか、swift-snapshot-testing 側の問題なのかはまだ分かっていません。

この issue では、Xcode 27 / macOS 27 の正式版で再発するかを確認し、それに応じて回避策を外すか、上流に報告するかを決めます。

発生した環境

項目 値
ランナーイメージ xcode-27-arm64 20260907.0173.1
OS macOS 27.0(26A5406e、ベータ版)
Xcode 27 beta 6(27A5252f、Xcode_27.0.0.app 経由で選択)
swift-snapshot-testing 1.19.4(最新リリース)
失敗した CI https://github.com/temoki/TortoiseGraphics2/actions/runs/34840305494

ローカル(macOS 26 + Xcode 27 RC)では再現しません。

症状

*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
    reason: '-[NSConcreteValue CGRectValue]: unrecognized selector sent to instance 0x75493f4000'
*** First throw call stack:
	5   CoreImage        -[CIReductionFilter offsetAndCrop] + 56
	6   CoreImage        -[CIAreaAverage outputImage] + 56
	7   CoreImage        -[CIImage imageByApplyingFilter:withInputParameters:] + 300
	8   TortoiseUITests  (extension in SnapshotTesting):CIImage.applyingAreaAverage()
	9   TortoiseUITests  SnapshotTesting.perceptuallyCompare(_:_:pixelPrecision:perceptualPrecision:)
	10  TortoiseUITests  SnapshotTesting.compare(_:_:precision:perceptualPrecision:) (NSImage)

テストが 1 件失敗するのではなく、テストプロセス全体が signal 6 で落ちます。

分かっていること

例外が起きている場所

swift-snapshot-testing の Sources/SnapshotTesting/Snapshotting/UIImage.swift で、CIAreaAverage の inputExtent に CGRect をそのまま渡しています。

func applyingAreaAverage() -> CIImage {
  applyingFilter("CIAreaAverage", parameters: [kCIInputExtentKey: extent])
}

CGRect は NSValue に変換されて渡ります。macOS 27 の Core Image はそれに CGRectValue を送りますが、macOS の NSValue には CGRectValue がありません(rectValue はある)。そのため例外になります。macOS 26 まではこの渡し方でも動いていました。

CIAreaAverage の inputExtent は、仕様上 CIVector を受け取るパラメータです。

macOS 26 で気づかなかった理由

compare はまずバイト列を比較し、一致しなかったときだけ perceptuallyCompare に進みます。ゴールデンを記録した macOS 26 では、描画結果がバイト単位で完全に一致するので、この処理まで進みません。macOS 27 では描画がわずかにずれるので、ここに進んで落ちます。

回避策(#48)

Tests/TortoiseUITests/CanvasGoldenSnapshotting.swift で .canvasGolden = .image(precision: 0.995) を定義し、Canvas の PNG ゴールデンはすべてこれで比較するようにしました。perceptualPrecision を指定しなければ、上流はバイト単位の比較だけを使うので、この処理を通りません。macOS 27 でのずれは許容誤差(バイトの 0.5%)に収まり、CI は通っています。

上流の状況(2026-09-14 時点)

  • 同じクラッシュの issue や PR はありません。
  • pointfreeco/swift-snapshot-testing#673:Xcode 27 と iOS 27 シミュレータで知覚差分の比較を使った報告があり、クラッシュせず 最後まで動いています。今回の問題は macOS 27 の Core Image に限ったもののようです。その報告者も回避策に perceptualPrecision: 1 を選んでいます。
  • PR #1119 と #1109 は知覚差分の比較まわりを変更しますが、kCIInputExtentKey の渡し方は変えていません。

検証すること

xcode-27 ランナーが macOS 27 と Xcode 27 の正式版になったら、次を確認します。

  1. ランナーの OS と Xcode のビルド番号が正式版になっていることを確認する(ジョブログの「Operating System」と「Select Xcode」)。
  2. ブランチで .canvasGolden を .image(precision: 0.995, perceptualPrecision: 0.98) に戻し、CI の Build & Test を回す。
    • クラッシュしない → 正式版で直った。回避策を残すか戻すかを決める(戻さなくても CI は通る)。
    • 同じ例外で落ちる → 次の 3 に進む。
  3. swift-snapshot-testing に issue を立てる。修正案は applyingAreaAverage() / applyingAreaMaximum() で CIVector(cgRect: extent) を渡すこと。

未確認のこと

  • 修正案(CIVector(cgRect:) を渡す)で本当に直るかは、macOS 27 で動かしていないので確認できていません。
  • CIAreaMaximum(applyingAreaMaximum())も同じ渡し方をしているので同じく落ちる可能性がありますが、今回のスタックトレースに出たのは CIAreaAverage だけです。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions