Rubyの拡張ライブラリーにはインストールできるかのテストも書こう
RubyGemを作っている時、ピュアRubyのgemでGitコミットしたファイルがそのまま配信されるようなのはあまり問題にならないですが、拡張ライブラリーみたいにユーザーのインストール時にコマンドが走ったり、Raccみたいにリリース直前にファイルを生成してパッケージに含めたりするような物は、リリースしてからインストールできなかったり動作しないことに気付いたりするものです(済みません……)。
そういうのを事前に防ぐため、ユニットテストで「ちゃんとインストールできること」も確認するようにしましょう。
と、言いたいことはこれで全てなんですが、折角なので例も置いておきます。
ここではテスティングフレームワークにTest::Unitを使います。
ライブラリーは何でもいいんですが、RustでRubyの拡張を作る時にrb_sys gemは不要になっていたで作った拡張ライブラリーを使うことにします。
ビルドのテスト
まずは*.gemファイルをちゃんとビルドできることを確認します。
Raccで生成したファイルを入れ忘れていたり、*.gemspecファイルを書き間違えていたりすることがあるためです。
require "test/unit"
require "tempfile"
require "shellwords"
class TestPackage < Test::Unit::TestCase
def test_build
Tempfile.create do |file|
assert system("gem", "build", "my_ext.gemspec", "--output", file.to_path.shellescape, exception: true)
assert file.size > 0
assert_path_exist file.to_path
end
end
end
gem buildコマンドの--outputオプションで任意のファイルパスにパッケージを作れます。
インストールのテスト
次に拡張ライブラリーがちゃんとインストールできることをテストします。
メインはインストールの所なので準備はsetupメソッドに置いておきます。
sub_test_caseでグルーピングしておくと便利なことがあります。このgemでは無いですが、gemのインストール時にビルドオプションを渡せる場合なんかは、そのオプションの有無で別々にテストをしたり。
@@ -1,6 +1,7 @@
require "test/unit"
require "tempfile"
require "shellwords"
+require "tmpdir"
class TestPackage < Test::Unit::TestCase
def test_build
@@ -10,4 +11,27 @@ class TestPackage < Test::Unit::TestCase
assert_path_exist file.to_path
end
end
+
+ sub_test_case "Building binary on installation" do
+ def setup
+ system "rake", "build", exception: true
+
+ @gemspec = Gem::Specification.load("my_ext.gemspec")
+ end
+
+ def test_install
+ Dir.mktmpdir do |dir|
+ # macOSではDir.mktmpdirのディレクトリー名とビルド時チェックのディレクトリー名が異なるので正規化
+ dir = File.realpath(dir)
+ system "gem", "install", "--install-dir", dir.shellescape, "--no-document", "pkg/#{@gemspec.file_name.shellescape}", exception: true
+ assert_installed dir
+ end
+ end
+
+ private
+
+ def assert_installed(dir)
+ assert_path_exist File.join(dir, "gems/#{@gemspec.full_name}/lib/my_ext/#{@gemspec.name}.#{RbConfig::CONFIG["DLEXT"]}")
+ end
+ end
end
gem installコマンドの--install-dirオプションでインストール先を変更できます。
Gem::Specificationが(当たり前ですが)インストールに関する情報を色々持っていて便利なのでインスタンス変数にして使い回しています(テスト中に変更されないから定数にしてもいいですね)。
軽く動作も確認できると安心でしょう。
@@ -25,6 +25,10 @@ class TestPackage < Test::Unit::TestCase
dir = File.realpath(dir)
system "gem", "install", "--install-dir", dir.shellescape, "--no-document", "pkg/#{@gemspec.file_name.shellescape}", exception: true
assert_installed dir
+
+ libdir = File.join(dir, "gems", @gemspec.full_name, "lib")
+ output = `ruby -I #{libdir} -r my_ext -e 'print MyExt.hello("world")'`
+ assert_equal "Hello from Rust, world!", output
end
end
インストールオプションのテスト
今回のgemでは実際には動きませんが、こんな風にインストールオプション(ビルドオプション)も可能ならテストしておきましょう。
@@ -32,6 +32,18 @@ class TestPackage < Test::Unit::TestCase
end
end
+ def test_install_with_option
+ Dir.mktmpdir do |dir|
+ dir = File.realpath(dir)
+ system "gem", "install", "--install-dir", dir.shellescape, "--no-document", "pkg/#{@gemspec.file_name.shellescape}", "--", "--enable-foo", exception: true
+ assert_installed dir
+
+ libdir = File.join(dir, "gems", @gemspec.full_name, "lib")
+ output = `ruby -I #{libdir} -r my_ext -e 'print MyExt.hello_with_foo("world")'`
+ assert_equal "Hello from Rust with Foo, world!", output
+ end
+ end
+
private
def assert_installed(dir)
gem installコマンドに--enable-fooオプションを渡しています。
お終い
こんな風にしてテストしておくと、リリース後に慌てて次のリリースをすることもかなり減らせる筈です。
「パッケージにクレデンシャルが含まれていないこと」なんかもテストするといいかもですね。
この為のテストユーティリティとか、誰か作ってくれないかしら。
Discussion