JRuby 10.1(CRuby 4.0互換)のCI対応と旧バージョン向けワークアラウンドの整理
JRuby 10.1がリリースされたことを受け、rspecのCIマトリクスに追加するとともに、EOLとなったJRuby 9.1向けのハック群を削除し、既知の互換性問題を適切にpend/skipで管理するよう整理しました。
背景
JRuby 10.1(CRuby 4.0互換)の正式リリースに伴い、rspecのテストスイートを新バージョン上で実行できるようにする必要がありました。同時に、長らくEOLとなっていたJRuby 9.1(9.1.17.0)向けのワークアラウンドがコードベースに残っており、メンテナンスコストになっていました。
具体的には、aruba_support.rbにはJRuby 9.1.17.0のrequire_relativeバグを回避するためのKernelモンキーパッチが存在し、aruba.rbではNameError/LoadErrorを握りつぶすrescueブロックがJRuby 9.1専用に実装されていました。これらは現行バージョンでは不要であるにもかかわらず、コードの可読性を下げる要因となっていました。
JRuby 10.1上でテストを実行した結果、jruby/jruby#9398(ネストしたrescue内でのcircular causesエラー)とrspec/rspec#321(respond_to?スタブ時の無限再帰問題)という2件の既知の互換性問題が特定されています。
技術的な変更
CIマトリクスへのJRuby 10.1追加
.github/workflows/rspec.ymlのJRubyテストマトリクスにJRuby 10.1が追加されました。
変更前:
ruby:
- jruby-9.4
- jruby-10.0
変更後:
ruby:
- jruby-10.1 # ruby 4.0 compatible
- jruby-10.0 # ruby 3.4 compatible
- jruby-9.4 # ruby 3.1 compatible
各エントリにCRuby互換バージョンのコメントが付記され、どのRubyバージョン互換として実行されているかが明示されるようになりました。
JRuby 9.1向けワークアラウンドの削除
spec/support/aruba_support.rbから、JRuby 9.1.17.0固有のrequire_relativeバグを回避する22行のKernelモンキーパッチが削除されました。rspec-mocks/spec/support/aruba.rbでは、JRuby 9.1.17.0でのロード失敗を握りつぶすためのbegin/rescue NameError, LoadErrorブロックが除去され、aruba/rspecのrequireが直接的なコードになりました。
その他にも、以下のJRuby 9.1/9.2向けのガード条件が各スペックファイルから削除されています:
-
rspec-core/spec/integration/bisect_spec.rb:JRuby 9.1.17.0でのskip -
rspec-core/spec/rspec/core/formatters/exception_presenter_spec.rb:JRuby 9.2未満でのpending(jruby/jruby#4737参照) -
rspec-core/spec/rspec/core_spec.rb:JRuby環境でのrubygemsのallowed_loaded_features例外 -
rspec-core/spec/rspec/core/formatters/snippet_extractor_spec.rb:JRuby向けの:skipフラグ
JRuby 10.1の既知問題に対するpend/skip
特定済みの互換性問題については、バージョン条件付きでpendingまたはskipを設定する方式が採用されました。
rspec-mocks/spec/rspec/mocks/any_instance_spec.rbでは、jruby/jruby#9398に起因するネストしたrescue内でのcircular causesエラーに対し、テストメタデータの:pendingオプションを使用しています:
broken_on_jruby_10_1 =
if RSpec::Support::Ruby.jruby? && RSpec::Support::Ruby.jruby_version >= '10.1.0.0'
"re-raising exceptions currently broken due to https://github.com/jruby/jruby/issues/9398"
else
false
end
it "stubs the method correctly", :pending => broken_on_jruby_10_1 do
rspec-mocks/spec/rspec/mocks/matchers/receive_spec.rbでは、rspec/rspec#321に起因するrespond_to?スタブ時のフレーキーな無限再帰について、ブロック内条件分岐によるskipを使用しています:
if RSpec::Support::Ruby.jruby? && RSpec::Support::Ruby.jruby_version >= '10.1.0.0'
skip 'Flaky on JRuby 10.1 due to some interaction with Object#instance_exec (?) https://github.com/rspec/rspec/issues/321'
end
rspec-support/spec/rspec/support_spec.rbでは、同じくjruby/jruby#9398に起因するNameErrorの代わりにArgumentError: circular causesが送出される問題をブロック内pendingで管理しています。
spec_file_load_errors_specの条件修正
rspec-core/spec/integration/spec_file_load_errors_spec.rbでは、テストレベルの:skip属性として設定されていたRSpec::Support::Ruby.jruby?による一括スキップが、ブロック内の条件付きスキップに変更されました。これにより、JRuby 10.0以降(バージョン文字列比較で'10'未満のもの)のみをスキップし、JRuby 10.1ではテストが実行されるようになっています。
設計判断
JRubyのテストサポートをベストエフォートと位置づける方針が今回の変更全体を貫いています。PR説明にも「running specs on JRuby I believe is best efforts anyway」と明示されており、完全な互換性保証ではなく、既知の問題を追跡可能な形で管理することを優先しています。
JRuby 10.1固有の問題に対してpend/skipを選択するにあたり、pendingとskipが使い分けられています。pendingはテストが実行されて期待通り失敗することを確認するのに対し、skipはテスト実行自体を省略します。receive_spec.rbでskipが選ばれた理由は、当該テストが「フレーキー(不安定)」であるためで、結果が非決定的な場合にはpendingよりskipが適切と判断されています。いずれの場合も、Issue URLをコメントとして埋め込むことで、ワークアラウンドの理由と追跡先を明示しています。
JRuby 9.1向けのモンキーパッチをインライン除去し、aruba.rbのrescueブロックも削除することで、テストサポートコードがシンプルになりました。これはEOL済みバージョンのサポートを明示的に打ち切る判断であり、メンテナが現行バージョンのJRuby問題に集中できる体制を整えています。
まとめ
この変更はJRuby 10.1(CRuby 4.0互換)をrspecのCI対象に加えながら、EOL済みのJRuby 9.1向けワークアラウンドを一掃した整理です。既知の互換性問題は上流Issueへのリンク付きでpend/skipとして記録されており、JRubyサポートをベストエフォートとして維持しつつ問題の追跡可能性を確保しています。