RubyCocoa 今日のコミット 2014-06-07 テストコードのruby-2.1対応
ruby-1.8と2.1の非互換でテスト失敗するところの対応あれこれ。
ruby-1.8ではrangeの範囲の値削除だったけど、1.9以降ではその位置にnilが入る。doc/ChangeLog-1.9.3に
array.c (rb_ary_update): a[n,m]=nil no longer works as element deletion.
とあるとおり、動作が変わったようです。
RubyCocoaでは、osx/objc/oc_attachments.rbでNSArrayがArrayと同じように、NSStringがStringと同じように動作するようになどCocoa側のクラスにメソッド追加したりしてる。
で、今回問題になったのは、NSArray#[range] = nilとArray#[range]= nilが同じ動作になってるよね、ってテストなんだけど、対処としては単純にruby-1.9以降ではnilを与えるテストをスキップするようにした。NSArrayは要素にnil入れられないしね。
しかしこの互換機能はのちのちハマりそうだなあ。どっかで最新のRubyの動作にまとめて合わせてやる必要がありそうだね。ちょっと気が重い…
ruby-1.9以降ではInteger(nil)はArgumentErrorになる。(1.8では0を返す)
rubyの値をObjective-Cに持ってくるとこ変換のテストで、nilをCのintに変換する検証があったので、ruby-2.xではそのassertを実行しないようにした。
- String#[i] (r2527)
ruby-1.8では位置iのバイトの値(整数)だけど、1.9以降はi文字めの1文字を返す。バイトの値を取り出すところだったのでString#bytesを使うように書き換え。
これはちょっとちがう話。RubyのオブジェクトをObjective-C側に渡したとき、もともとCocoaなオブジェクトならそれを、StringやArrayなんかはNSStringやNSArrayに変換されるようになっている。そういったものでないRubyのオブジェクトはObjective-CのRBObjectというラッパクラスでくるんで渡すようにしている。
そのラッパオブジェクトと元のRubyオブジェクトの関連はキャッシュとして保持してるけれど、その動作のテスト。
def test_rbobject test = OSX::TestRBObject.alloc.init o = SimpleClass.new n = test.addressOfObject(o) GC.start # add assert(n != test.addressOfObject(o)) # 1 a = OSX::NSMutableArray.array a << o n = test.addressOfObject(o) GC.start # add assert(n == test.addressOfObject(o)) # 2 end
1つめのassertでは新しくRBObjectのインスタンスが生成されるからアドレスがちがって、2つめのassertではaの要素として保持されるからキャッシュが残るという確認。で、ruby-2.1上では1つめのassert()が失敗する(アドレスが一致する)ようになっていた。キャッシュを掃除するためにGC.startを追加したのが今日の修正。そもそも1.8でもたまたま動いてただけで、テストの意図としては元からGC.start必要だったんじゃないかなあ。