きりかノート 3冊め

おあそびプログラミング

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を実行しないようにした。

ruby-1.8では位置iのバイトの値(整数)だけど、1.9以降はi文字めの1文字を返す。バイトの値を取り出すところだったのでString#bytesを使うように書き換え。

  • Ruby - ObjC間のオブジェクト変換のテストを修正 (r2529)

これはちょっとちがう話。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必要だったんじゃないかなあ。