最近、 colszowka/simplecov のバグを踏みました。
こうやって 不運にもバグを踏んでしまった 運よくOSSに貢献できるチャンスを得てしまったときの原因調査の方法などを紹介します。
今回のバグ
simplecov はカバレッジ率を計測するgemで、以前から便利に使ってました。
しかし、今回 simplecov v0.11.1 と grosser/parallel_tests を一緒に使ったところ、突然カバレッジ率が下がってしまう問題が起きました。
原因の調べ方
1. breakpoint を仕込む
GitHub でソースコードを読んで、処理の流れを理解する。
なんとなく原因かもしれない処理が見つかったら、その近辺のメソッドを上書きして調べる。
module SimpleCovDebug def start(profile = nil, &block) require 'byebug' byebug super end end # https://github.com/colszowka/simplecov/blob/v0.11.1/lib/simplecov.rb SimpleCov.singleton_class.prepend(SimpleCovDebug) module ResultDebug def initialize(original_result) require 'byebug' byebug super end end # https://github.com/colszowka/simplecov/blob/v0.11.1/lib/simplecov/result.rb SimpleCov::Result.prepend(ResultDebug)
適当なmoduleを作って、 prepend
で gem のクラスを上書きします。これにより、実行時にbyebugで止まるようになります。*1
2. 原因のコミットを探す
過去のバージョンを試し、バグの原因となったコミットを見つけ出します。
gem 'rubocop', '0.10.0', require: false
0.11.0
→ 0.10.0
と試し、 0.10.0
では問題が起きていないことが分かりました。次は原因のコミットを調べます。
simplecov のリポジトリを適当な場所にcloneします。
$ git clone https://github.com/colszowka/simplecov.git /tmp/simplecov
次に、gemのインストール先をローカルに変えます。
gem 'rubocop', require: false, path: '/tmp/simplecov'
あとは GitHubで v0.10.0...v0.11.0 の compare を見ながら、二分探索で探していきます。
コンソールを2つ開き、片方でテスト実行、もう1つでチェックアウトする感じ。*2
# コンソール 1 $ cd /tmp/simplecov $ git checkout $sha1 # コンソール 2 $ bin/rake parallel:spec
バグの再現するコミットを見つけたら、そのコミットに紐づくPull Requestを見ます。
Pull Request を読むと、 require されていないファイルのカバレッジを 全行を 0(uncover) で追加する という内容のようです。
これは並列で動くと死にそうですね!
バグを直す
原因が分かったら、ローカルのリポジトリでブランチを作り、直します。
$ cd /tmp/simplecov $ git checkout -b fix_a_bug # 直す $ git commit -m "Fix coverage rate of the parallel_tests"
実際に動かしてみて、直ったことを確認します。
まとめ
あまりgemの直し方を書いてる記事を見かけなかったので、書いてみました。
なにか参考になれば幸いです。
Pull Request
今回の修正は Pull Request を投げているのですが、別の方の修正と似たようなことをやっていてマージされるか微妙です。
ruby の coverage.c を確認したり、最小限のパッチになるようにしたつもりなんだけどなぁ…。 もっと英語力を身につける必要があるのかもしれない。