1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/test/rubygems/test_gem_installer.rb
David Rodríguez 7fd88da935 [rubygems/rubygems] Fix race condition when reading & writing gemspecs concurrently
When bundler parallel installer installs gems concurrently, one can get
confusing warnings like the following:

```
"[/home/runner/work/rubygems/rubygems/bundler/tmp/2/gems/system/specifications/zeitwerk-2.4.2.gemspec] isn't a Gem::Specification (NilClass instead).
```

I've got these warnings several times in the past, but I never managed
to reproduce them, and never look deeply into the root cause, but this
time a got a cause that reproduced quite frequently, so I looked into
it.

The problem is one thread reading a gemspec while another thread is
writing it. The write of the gemspec was not protected, so
`Gem::Specification.load` could end up seeing a truncated gemspec and
thus throw this warning.

The fix involve two changes:

* Change the methods that write gemspecs to use `Gem.binary_write` which
  is protected by a lock.

* Fix `Gem.binary_write` to create the file lock at file creation time,
  not when the file already exists after.

The realworld user problem caused by this issue happens in bundler, but
I'm fixing it in rubygems first, and then I'll backport to bundler
whatever needs backporting to fix the issue on the bundler side.

https://github.com/rubygems/rubygems/commit/a672e7555c
2021-11-30 20:54:05 +09:00

2311 lines
59 KiB
Ruby

# frozen_string_literal: true
require_relative 'installer_test_case'
class TestGemInstaller < Gem::InstallerTestCase
def setup
super
common_installer_setup
@config = Gem.configuration
end
def teardown
common_installer_teardown
super
Gem.configuration = instance_variable_defined?(:@config) ? @config : nil
end
def test_app_script_text
installer = setup_base_installer
util_make_exec @spec, ''
expected = <<-EOF
#!#{Gem.ruby}
#
# This file was generated by RubyGems.
#
# The application 'a' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
Gem.use_gemdeps
version = \">= 0.a\"
str = ARGV.first
if str
str = str.b[/\\A_(.*)_\\z/, 1]
if str and Gem::Version.correct?(str)
version = str
ARGV.shift
end
end
if Gem.respond_to?(:activate_bin_path)
load Gem.activate_bin_path('a', 'executable', version)
else
gem "a", version
load Gem.bin_path("a", "executable", version)
end
EOF
wrapper = installer.app_script_text 'executable'
assert_equal expected, wrapper
end
def test_check_executable_overwrite
installer = setup_base_installer
installer.generate_bin
@spec = Gem::Specification.new do |s|
s.files = ['lib/code.rb']
s.name = "a"
s.version = "3"
s.summary = "summary"
s.description = "desc"
s.require_path = 'lib'
end
util_make_exec
installer.gem_dir = @spec.gem_dir
installer.wrappers = true
installer.generate_bin
installed_exec = File.join util_inst_bindir, 'executable'
assert_path_exist installed_exec
wrapper = File.read installed_exec
assert_match %r{generated by RubyGems}, wrapper
end
def test_check_executable_overwrite_default_bin_dir
installer = setup_base_installer(false)
bindir(Gem.bindir) do
util_conflict_executable false
ui = Gem::MockGemUi.new "n\n"
use_ui ui do
e = assert_raise Gem::InstallError do
installer.generate_bin
end
conflicted = File.join @gemhome, 'bin', 'executable'
assert_match %r{\A"executable" from a conflicts with (?:#{Regexp.quote(conflicted)}|installed executable from conflict)\z},
e.message
end
end
end
def test_check_executable_overwrite_format_executable
installer = setup_base_installer
installer.generate_bin
@spec = Gem::Specification.new do |s|
s.files = ['lib/code.rb']
s.name = "a"
s.version = "3"
s.summary = "summary"
s.description = "desc"
s.require_path = 'lib'
end
File.open File.join(util_inst_bindir, 'executable'), 'w' do |io|
io.write <<-EXEC
#!/usr/local/bin/ruby
#
# This file was generated by RubyGems
gem 'other', version
EXEC
end
util_make_exec
Gem::Installer.exec_format = 'foo-%s-bar'
installer.gem_dir = @spec.gem_dir
installer.wrappers = true
installer.format_executable = true
installer.generate_bin # should not raise
installed_exec = File.join util_inst_bindir, 'foo-executable-bar'
assert_path_exist installed_exec
wrapper = File.read installed_exec
assert_match %r{generated by RubyGems}, wrapper
ensure
Gem::Installer.exec_format = nil
end
def test_check_executable_overwrite_other_gem
installer = setup_base_installer(false)
util_conflict_executable true
ui = Gem::MockGemUi.new "n\n"
use_ui ui do
e = assert_raise Gem::InstallError do
installer.generate_bin
end
assert_equal '"executable" from a conflicts with installed executable from conflict',
e.message
end
end
def test_check_executable_overwrite_other_gem_force
installer = setup_base_installer
util_conflict_executable true
installer.wrappers = true
installer.force = true
installer.generate_bin
installed_exec = File.join util_inst_bindir, 'executable'
assert_path_exist installed_exec
wrapper = File.read installed_exec
assert_match %r{generated by RubyGems}, wrapper
end
def test_check_executable_overwrite_other_non_gem
installer = setup_base_installer
util_conflict_executable false
installer.wrappers = true
installer.generate_bin
installed_exec = File.join util_inst_bindir, 'executable'
assert_path_exist installed_exec
wrapper = File.read installed_exec
assert_match %r{generated by RubyGems}, wrapper
end unless Gem.win_platform?
def test_check_that_user_bin_dir_is_in_path
installer = setup_base_installer
bin_dir = installer.bin_dir
if Gem.win_platform?
bin_dir = bin_dir.downcase
end
orig_PATH, ENV['PATH'] =
ENV['PATH'], [ENV['PATH'], bin_dir].join(File::PATH_SEPARATOR)
use_ui @ui do
installer.check_that_user_bin_dir_is_in_path
end
assert_empty @ui.error
return unless win_platform?
ENV['PATH'] = [orig_PATH, bin_dir.tr(File::SEPARATOR, File::ALT_SEPARATOR)].join(File::PATH_SEPARATOR)
use_ui @ui do
installer.check_that_user_bin_dir_is_in_path
end
assert_empty @ui.error
ensure
ENV['PATH'] = orig_PATH
end
def test_check_that_user_bin_dir_is_in_path_tilde
pend "Tilde is PATH is not supported under MS Windows" if win_platform?
orig_PATH, ENV['PATH'] =
ENV['PATH'], [ENV['PATH'], '~/bin'].join(File::PATH_SEPARATOR)
installer = setup_base_installer
installer.bin_dir.replace File.join @userhome, 'bin'
use_ui @ui do
installer.check_that_user_bin_dir_is_in_path
end
assert_empty @ui.error
ensure
ENV['PATH'] = orig_PATH unless win_platform?
end
def test_check_that_user_bin_dir_is_in_path_not_in_path
installer = setup_base_installer
use_ui @ui do
installer.check_that_user_bin_dir_is_in_path
end
expected = installer.bin_dir
if Gem.win_platform?
expected = expected.downcase
end
assert_match expected, @ui.error
end
def test_ensure_dependency
installer = setup_base_installer
util_spec 'a'
dep = Gem::Dependency.new 'a', '>= 2'
assert installer.ensure_dependency(@spec, dep)
dep = Gem::Dependency.new 'b', '> 2'
e = assert_raise Gem::InstallError do
installer.ensure_dependency @spec, dep
end
assert_equal 'a requires b (> 2)', e.message
end
def test_ensure_loadable_spec
a, a_gem = util_gem 'a', 2 do |s|
s.add_dependency 'garbage ~> 5'
end
installer = Gem::Installer.at a_gem
e = assert_raise Gem::InstallError do
installer.ensure_loadable_spec
end
assert_equal "The specification for #{a.full_name} is corrupt " +
"(SyntaxError)", e.message
end
def test_ensure_no_race_conditions_between_installing_and_loading_gemspecs
a, a_gem = util_gem 'a', 2
Gem::Installer.at(a_gem).install
t1 = Thread.new do
5.times do
Gem::Installer.at(a_gem).install
sleep 0.1
end
end
t2 = Thread.new do
_, err = capture_output do
20.times do
Gem::Specification.load(a.spec_file)
Gem::Specification.send(:clear_load_cache)
end
end
assert_empty err
end
t1.join
t2.join
end
def test_ensure_loadable_spec_security_policy
pend 'openssl is missing' unless Gem::HAVE_OPENSSL
_, a_gem = util_gem 'a', 2 do |s|
s.add_dependency 'garbage ~> 5'
end
policy = Gem::Security::HighSecurity
installer = Gem::Installer.at a_gem, :security_policy => policy
assert_raise Gem::Security::Exception do
installer.ensure_loadable_spec
end
end
def test_extract_files
installer = setup_base_installer
installer.extract_files
assert_path_exist File.join @spec.gem_dir, 'bin/executable'
end
def test_generate_bin_bindir
installer = setup_base_installer
installer.wrappers = true
@spec.executables = %w[executable]
@spec.bindir = 'bin'
exec_file = installer.formatted_program_filename 'executable'
exec_path = File.join @spec.gem_dir, exec_file
File.open exec_path, 'w' do |f|
f.puts '#!/usr/bin/ruby'
end
installer.gem_dir = @spec.gem_dir
installer.generate_bin
assert_directory_exists util_inst_bindir
installed_exec = File.join(util_inst_bindir, 'executable')
assert_path_exist installed_exec
assert_equal mask, File.stat(installed_exec).mode unless win_platform?
wrapper = File.read installed_exec
assert_match %r{generated by RubyGems}, wrapper
end
def test_generate_bin_bindir_with_user_install_warning
bin_dir = Gem.win_platform? ? File.expand_path(ENV["WINDIR"]).upcase :
"/usr/bin"
old_path = ENV["PATH"]
ENV["PATH"] = [ENV["PATH"], bin_dir].compact.join(File::PATH_SEPARATOR)
options = {
:bin_dir => bin_dir,
:install_dir => "/non/existent",
}
inst = Gem::Installer.at '', options
Gem::Installer.path_warning = false
use_ui @ui do
inst.check_that_user_bin_dir_is_in_path
end
assert_equal "", @ui.error
ensure
ENV["PATH"] = old_path
end
def test_generate_bin_script
installer = setup_base_installer
installer.wrappers = true
util_make_exec
installer.gem_dir = @spec.gem_dir
installer.generate_bin
assert_directory_exists util_inst_bindir
installed_exec = File.join util_inst_bindir, 'executable'
assert_path_exist installed_exec
assert_equal mask, File.stat(installed_exec).mode unless win_platform?
wrapper = File.read installed_exec
assert_match %r{generated by RubyGems}, wrapper
end
def test_generate_bin_script_format
installer = setup_base_installer
installer.format_executable = true
installer.wrappers = true
util_make_exec
installer.gem_dir = @spec.gem_dir
Gem::Installer.exec_format = 'foo-%s-bar'
installer.generate_bin
assert_directory_exists util_inst_bindir
installed_exec = File.join util_inst_bindir, 'foo-executable-bar'
assert_path_exist installed_exec
ensure
Gem::Installer.exec_format = nil
end
def test_generate_bin_script_format_disabled
installer = setup_base_installer
installer.wrappers = true
util_make_exec
installer.gem_dir = @spec.gem_dir
Gem::Installer.exec_format = 'foo-%s-bar'
installer.generate_bin
assert_directory_exists util_inst_bindir
installed_exec = File.join util_inst_bindir, 'executable'
assert_path_exist installed_exec
ensure
Gem::Installer.exec_format = nil
end
def test_generate_bin_script_install_dir
installer = setup_base_installer
installer.wrappers = true
gem_dir = File.join("#{@gemhome}2", "gems", @spec.full_name)
gem_bindir = File.join gem_dir, 'bin'
FileUtils.mkdir_p gem_bindir
File.open File.join(gem_bindir, 'executable'), 'w' do |f|
f.puts "#!/bin/ruby"
end
installer.gem_home = "#{@gemhome}2"
installer.gem_dir = gem_dir
installer.bin_dir = File.join "#{@gemhome}2", 'bin'
installer.generate_bin
installed_exec = File.join("#{@gemhome}2", "bin", 'executable')
assert_path_exist installed_exec
assert_equal mask, File.stat(installed_exec).mode unless win_platform?
wrapper = File.read installed_exec
assert_match %r{generated by RubyGems}, wrapper
end
def test_generate_bin_script_no_execs
installer = setup_base_installer
installer = util_execless
installer.wrappers = true
installer.generate_bin
assert_path_not_exist util_inst_bindir, 'bin dir was created when not needed'
end
def test_generate_bin_script_no_perms
installer = setup_base_installer
installer.wrappers = true
util_make_exec
Dir.mkdir util_inst_bindir
if win_platform?
pend('test_generate_bin_script_no_perms skipped on MS Windows')
elsif Process.uid.zero?
pend('test_generate_bin_script_no_perms skipped in root privilege')
else
FileUtils.chmod 0000, util_inst_bindir
assert_raise Gem::FilePermissionError do
installer.generate_bin
end
end
ensure
FileUtils.chmod 0755, util_inst_bindir unless ($DEBUG or win_platform?)
end
def test_generate_bin_script_no_shebang
installer = setup_base_installer
installer.wrappers = true
@spec.executables = %w[executable]
gem_dir = File.join @gemhome, 'gems', @spec.full_name
gem_bindir = File.join gem_dir, 'bin'
FileUtils.mkdir_p gem_bindir
File.open File.join(gem_bindir, 'executable'), 'w' do |f|
f.puts "blah blah blah"
end
installer.generate_bin
installed_exec = File.join @gemhome, 'bin', 'executable'
assert_path_exist installed_exec
assert_equal mask, File.stat(installed_exec).mode unless win_platform?
wrapper = File.read installed_exec
assert_match %r{generated by RubyGems}, wrapper
# HACK some gems don't have #! in their executables, restore 2008/06
#assert_no_match %r|generated by RubyGems|, wrapper
end
def test_generate_bin_script_wrappers
installer = setup_base_installer
installer.wrappers = true
util_make_exec
installer.gem_dir = @spec.gem_dir
installed_exec = File.join(util_inst_bindir, 'executable')
real_exec = File.join @spec.gem_dir, 'bin', 'executable'
# fake --no-wrappers for previous install
unless Gem.win_platform?
FileUtils.mkdir_p File.dirname(installed_exec)
FileUtils.ln_s real_exec, installed_exec
end
installer.generate_bin
assert_directory_exists util_inst_bindir
assert_path_exist installed_exec
assert_equal mask, File.stat(installed_exec).mode unless win_platform?
assert_match %r{generated by RubyGems}, File.read(installed_exec)
refute_match %r{generated by RubyGems}, File.read(real_exec),
'real executable overwritten'
end
def test_generate_bin_symlink
pend "Symlinks not supported or not enabled" unless symlink_supported?
installer = setup_base_installer
installer.wrappers = false
util_make_exec
installer.gem_dir = @spec.gem_dir
installer.generate_bin
assert_directory_exists util_inst_bindir
installed_exec = File.join util_inst_bindir, 'executable'
assert_equal true, File.symlink?(installed_exec)
assert_equal(File.join(@spec.gem_dir, 'bin', 'executable'),
File.readlink(installed_exec))
end
def test_generate_bin_symlink_no_execs
installer = setup_base_installer
installer = util_execless
installer.wrappers = false
installer.generate_bin
assert_path_not_exist util_inst_bindir
end
def test_generate_bin_symlink_no_perms
installer = setup_base_installer
installer.wrappers = false
util_make_exec
installer.gem_dir = @spec.gem_dir
Dir.mkdir util_inst_bindir
if win_platform?
pend('test_generate_bin_symlink_no_perms skipped on MS Windows')
elsif Process.uid.zero?
pend('test_user_install_disabled_read_only test skipped in root privilege')
else
FileUtils.chmod 0000, util_inst_bindir
assert_raise Gem::FilePermissionError do
installer.generate_bin
end
end
ensure
FileUtils.chmod 0755, util_inst_bindir unless ($DEBUG or win_platform?)
end
def test_generate_bin_symlink_update_newer
pend "Symlinks not supported or not enabled" unless symlink_supported?
installer = setup_base_installer
installer.wrappers = false
util_make_exec
installer.gem_dir = @spec.gem_dir
installer.generate_bin
installed_exec = File.join(util_inst_bindir, 'executable')
assert_equal(File.join(@spec.gem_dir, 'bin', 'executable'),
File.readlink(installed_exec))
@spec = Gem::Specification.new do |s|
s.files = ['lib/code.rb']
s.name = "a"
s.version = "3"
s.summary = "summary"
s.description = "desc"
s.require_path = 'lib'
end
util_make_exec
installer.gem_dir = @spec.gem_dir
installer.generate_bin
installed_exec = File.join(util_inst_bindir, 'executable')
assert_equal(@spec.bin_file('executable'),
File.readlink(installed_exec),
"Ensure symlink moved to latest version")
end
def test_generate_bin_symlink_update_older
pend "Symlinks not supported or not enabled" unless symlink_supported?
installer = setup_base_installer
installer.wrappers = false
util_make_exec
installer.gem_dir = @spec.gem_dir
installer.generate_bin
installed_exec = File.join(util_inst_bindir, 'executable')
assert_equal(File.join(@spec.gem_dir, 'bin', 'executable'),
File.readlink(installed_exec))
spec = Gem::Specification.new do |s|
s.files = ['lib/code.rb']
s.name = "a"
s.version = "1"
s.summary = "summary"
s.description = "desc"
s.require_path = 'lib'
end
util_make_exec
one = @spec.dup
one.version = 1
installer = Gem::Installer.for_spec spec
installer.gem_dir = one.gem_dir
installer.generate_bin
installed_exec = File.join util_inst_bindir, 'executable'
expected = File.join @spec.gem_dir, 'bin', 'executable'
assert_equal(expected,
File.readlink(installed_exec),
"Ensure symlink not moved")
end
def test_generate_bin_symlink_update_remove_wrapper
pend "Symlinks not supported or not enabled" unless symlink_supported?
installer = setup_base_installer
installer.wrappers = true
util_make_exec
installer.gem_dir = @spec.gem_dir
installer.generate_bin
installed_exec = File.join util_inst_bindir, 'executable'
assert_path_exist installed_exec
@spec = Gem::Specification.new do |s|
s.files = ['lib/code.rb']
s.name = "a"
s.version = "3"
s.summary = "summary"
s.description = "desc"
s.require_path = 'lib'
end
util_make_exec
util_installer @spec, @gemhome
installer.wrappers = false
installer.gem_dir = @spec.gem_dir
installer.generate_bin
installed_exec = File.join util_inst_bindir, 'executable'
assert_equal(@spec.bin_file('executable'),
File.readlink(installed_exec),
"Ensure symlink moved to latest version")
end
def test_generate_bin_symlink_win32
old_win_platform = Gem.win_platform?
Gem.win_platform = true
old_alt_separator = File::ALT_SEPARATOR
File.__send__(:remove_const, :ALT_SEPARATOR)
File.const_set(:ALT_SEPARATOR, '\\')
installer = setup_base_installer
installer.wrappers = false
util_make_exec
installer.gem_dir = @spec.gem_dir
use_ui @ui do
installer.generate_bin
end
assert_directory_exists util_inst_bindir
installed_exec = File.join(util_inst_bindir, 'executable')
assert_path_exist installed_exec
if symlink_supported?
assert File.symlink?(installed_exec)
return
end
assert_match(/Unable to use symlinks, installing wrapper/i,
@ui.error)
wrapper = File.read installed_exec
assert_match(/generated by RubyGems/, wrapper)
ensure
File.__send__(:remove_const, :ALT_SEPARATOR)
File.const_set(:ALT_SEPARATOR, old_alt_separator)
Gem.win_platform = old_win_platform
end
def test_generate_bin_uses_default_shebang
pend "Symlinks not supported or not enabled" unless symlink_supported?
installer = setup_base_installer
installer.wrappers = true
util_make_exec
installer.generate_bin
default_shebang = Gem.ruby
shebang_line = File.open("#{@gemhome}/bin/executable") {|f| f.readlines.first }
assert_match(/\A#!/, shebang_line)
assert_match(/#{default_shebang}/, shebang_line)
end
def test_generate_bin_with_dangling_symlink
gem_with_dangling_symlink = File.expand_path("packages/ascii_binder-0.1.10.1.gem", __dir__)
installer = Gem::Installer.at(
gem_with_dangling_symlink,
:user_install => false,
:force => true
)
build_rake_in do
use_ui @ui do
installer.install
end
end
assert_match %r{bin/ascii_binder` is dangling symlink pointing to `bin/asciibinder`}, @ui.error
assert_empty @ui.output
end
def test_generate_plugins
installer = util_setup_installer do |spec|
write_file File.join(@tempdir, 'lib', 'rubygems_plugin.rb') do |io|
io.write "puts __FILE__"
end
spec.files += %w[lib/rubygems_plugin.rb]
end
build_rake_in do
installer.install
end
plugin_path = File.join Gem.plugindir, 'a_plugin.rb'
FileUtils.rm plugin_path
installer.generate_plugins
assert File.exist?(plugin_path), 'plugin not written'
end
def test_generate_plugins_with_install_dir
spec = quick_gem 'a' do |s|
write_file File.join(@tempdir, 'lib', 'rubygems_plugin.rb') do |io|
io.write "puts __FILE__"
end
s.files += %w[lib/rubygems_plugin.rb]
end
util_build_gem spec
plugin_path = File.join "#{@gemhome}2", 'plugins', 'a_plugin.rb'
installer = util_installer spec, "#{@gemhome}2"
assert_equal spec, installer.install
assert File.exist?(plugin_path), 'plugin not written to install_dir'
end
def test_generate_plugins_with_user_install
spec = quick_gem 'a' do |s|
write_file File.join(@tempdir, 'lib', 'rubygems_plugin.rb') do |io|
io.write "puts __FILE__"
end
s.files += %w[lib/rubygems_plugin.rb]
end
util_build_gem spec
File.chmod(0555, Gem.plugindir)
system_path = File.join(Gem.plugindir, 'a_plugin.rb')
user_path = File.join(Gem.plugindir(Gem.user_dir), 'a_plugin.rb')
installer = util_installer spec, Gem.dir, :user
assert_equal spec, installer.install
assert !File.exist?(system_path), 'plugin incorrectly written to system plugins_dir'
assert File.exist?(user_path), 'plugin not written to user plugins_dir'
end
def test_generate_plugins_with_build_root
spec = quick_gem 'a' do |s|
write_file File.join(@tempdir, 'lib', 'rubygems_plugin.rb') do |io|
io.write "puts __FILE__"
end
s.files += %w[lib/rubygems_plugin.rb]
end
util_build_gem spec
File.chmod(0555, Gem.plugindir)
system_path = File.join(Gem.plugindir, 'a_plugin.rb')
build_root = File.join(@tempdir, 'build_root')
build_root_path = File.join(build_root, Gem.plugindir.gsub(/^[a-zA-Z]:/, ''), 'a_plugin.rb')
installer = Gem::Installer.at spec.cache_file, :build_root => build_root
assert_equal spec, installer.install
assert !File.exist?(system_path), 'plugin written incorrect written to system plugins_dir'
assert File.exist?(build_root_path), 'plugin not written to build_root'
refute_includes File.read(build_root_path), build_root
end
def test_keeps_plugins_up_to_date
# NOTE: version a-2 is already installed by setup hooks
write_file File.join(@tempdir, 'lib', 'rubygems_plugin.rb') do |io|
io.write "puts __FILE__"
end
build_rake_in do
util_setup_installer do |spec|
spec.version = '1'
spec.files += %w[lib/rubygems_plugin.rb]
end.install
plugin_path = File.join Gem.plugindir, 'a_plugin.rb'
refute File.exist?(plugin_path), 'old version installed while newer version without plugin also installed, but plugin written'
util_setup_installer do |spec|
spec.version = '2'
spec.files += %w[lib/rubygems_plugin.rb]
end.install
plugin_path = File.join Gem.plugindir, 'a_plugin.rb'
assert File.exist?(plugin_path), 'latest version reinstalled, but plugin not written'
assert_match %r{\Arequire.*a-2/lib/rubygems_plugin\.rb}, File.read(plugin_path), 'written plugin has incorrect content'
util_setup_installer do |spec|
spec.version = '3'
spec.files += %w[lib/rubygems_plugin.rb]
end.install
plugin_path = File.join Gem.plugindir, 'a_plugin.rb'
assert File.exist?(plugin_path), 'latest version installed, but plugin removed'
assert_match %r{\Arequire.*a-3/lib/rubygems_plugin\.rb}, File.read(plugin_path), 'written plugin has incorrect content'
util_setup_installer do |spec|
spec.version = '4'
end.install
refute File.exist?(plugin_path), 'new version installed without a plugin while older version with a plugin installed, but plugin not removed'
end
end
def test_generates_plugins_dir_under_install_dir_if_not_there
Gem.use_paths "#{@gemhome}2" # Set GEM_HOME to an uninitialized repo
@spec = util_spec 'a'
path = Gem::Package.build @spec
installer = Gem::Installer.at path, :install_dir => "#{@gemhome}3"
assert_equal @spec, installer.install
end
def test_initialize
spec = util_spec 'a' do |s|
s.platform = Gem::Platform.new 'mswin32'
end
gem = File.join @tempdir, spec.file_name
Dir.mkdir util_inst_bindir
util_build_gem spec
FileUtils.mv spec.cache_file, @tempdir
installer = Gem::Installer.at gem
assert_equal File.join(@gemhome, 'gems', spec.full_name), installer.gem_dir
assert_equal File.join(@gemhome, 'bin'), installer.bin_dir
end
def test_initialize_user_install
@gem = setup_base_gem
installer = Gem::Installer.at @gem, :user_install => true
assert_equal File.join(Gem.user_dir, 'gems', @spec.full_name),
installer.gem_dir
assert_equal Gem.bindir(Gem.user_dir), installer.bin_dir
end
def test_initialize_user_install_bin_dir
@gem = setup_base_gem
installer =
Gem::Installer.at @gem, :user_install => true, :bin_dir => @tempdir
assert_equal File.join(Gem.user_dir, 'gems', @spec.full_name),
installer.gem_dir
assert_equal @tempdir, installer.bin_dir
end
def test_install
installer = util_setup_installer
gemdir = File.join @gemhome, 'gems', @spec.full_name
cache_file = File.join @gemhome, 'cache', @spec.file_name
stub_exe = File.join @gemhome, 'bin', 'executable'
rakefile = File.join gemdir, 'ext', 'a', 'Rakefile'
spec_file = File.join @gemhome, 'specifications', @spec.spec_name
Gem.pre_install do
assert_path_not_exist cache_file, 'cache file must not exist yet'
true
end
Gem.post_build do
assert_path_exist gemdir, 'gem install dir must exist'
assert_path_exist rakefile, 'gem executable must exist'
assert_path_not_exist stub_exe, 'gem executable must not exist'
true
end
Gem.post_install do
assert_path_exist cache_file, 'cache file must exist'
end
@newspec = nil
build_rake_in do
use_ui @ui do
@newspec = installer.install
end
end
assert_equal @spec, @newspec
assert_path_exist gemdir
assert_path_exist stub_exe, 'gem executable must exist'
exe = File.join gemdir, 'bin', 'executable'
assert_path_exist exe
exe_mode = File.stat(exe).mode & 0111
assert_equal 0111, exe_mode, "0%o" % exe_mode unless win_platform?
assert_path_exist File.join gemdir, 'lib', 'code.rb'
assert_path_exist rakefile
assert_equal spec_file, @newspec.loaded_from
assert_path_exist spec_file
assert_same installer, @post_build_hook_arg
assert_same installer, @post_install_hook_arg
assert_same installer, @pre_install_hook_arg
end
def test_install_creates_working_binstub
installer = util_setup_installer
installer.wrappers = true
gemdir = File.join @gemhome, 'gems', @spec.full_name
@newspec = nil
build_rake_in do
use_ui @ui do
@newspec = installer.install
end
end
exe = File.join gemdir, 'bin', 'executable'
e = assert_raise RuntimeError do
instance_eval File.read(exe)
end
assert_match(/ran executable/, e.message)
end
def test_conflicting_binstubs
@gem = setup_base_gem
# build old version that has a bin file
installer = util_setup_gem do |spec|
File.open File.join('bin', 'executable'), 'w' do |f|
f.puts "require 'code'"
end
File.open File.join('lib', 'code.rb'), 'w' do |f|
f.puts 'raise "I have an executable"'
end
end
installer.wrappers = true
build_rake_in do
use_ui @ui do
@newspec = installer.install
end
end
old_bin_file = File.join installer.bin_dir, 'executable'
# build new version that doesn't have a bin file
installer = util_setup_gem do |spec|
FileUtils.rm File.join('bin', 'executable')
spec.files.delete File.join('bin', 'executable')
spec.executables.delete 'executable'
spec.version = @spec.version.bump
File.open File.join('lib', 'code.rb'), 'w' do |f|
f.puts 'raise "I do not have an executable"'
end
end
build_rake_in do
use_ui @ui do
@newspec = installer.install
end
end
e = assert_raise RuntimeError do
instance_eval File.read(old_bin_file)
end
# We expect the bin stub to activate the version that actually contains
# the binstub.
assert_match('I have an executable', e.message)
end
def test_install_creates_binstub_that_understand_version
installer = util_setup_installer
installer.wrappers = true
@newspec = nil
build_rake_in do
use_ui @ui do
@newspec = installer.install
end
end
exe = File.join @gemhome, 'bin', 'executable'
ARGV.unshift "_3.0_"
begin
Gem::Specification.reset
e = assert_raise Gem::GemNotFoundException do
instance_eval File.read(exe)
end
ensure
ARGV.shift if ARGV.first == "_3.0_"
end
assert_includes(e.message, "can't find gem a (= 3.0)")
end
def test_install_creates_binstub_that_prefers_user_installed_gem_to_default
default_spec = new_default_spec('default', '2', nil, 'exe/executable')
default_spec.executables = 'executable'
install_default_gems default_spec
exe = File.join @gemhome, 'bin', 'executable'
assert_path_exist exe, "default gem's executable not installed"
installer = util_setup_installer do |spec|
spec.name = 'default'
spec.version = '2'
end
util_clear_gems
installer.wrappers = true
@newspec = nil
build_rake_in do
use_ui @ui do
@newspec = installer.install
end
end
e = assert_raise RuntimeError do
instance_eval File.read(exe)
end
assert_equal(e.message, "ran executable")
end
def test_install_creates_binstub_that_dont_trust_encoding
installer = util_setup_installer
installer.wrappers = true
@newspec = nil
build_rake_in do
use_ui @ui do
@newspec = installer.install
end
end
exe = File.join @gemhome, 'bin', 'executable'
extra_arg = "\xE4pfel".dup.force_encoding("UTF-8")
ARGV.unshift extra_arg
begin
Gem::Specification.reset
e = assert_raise RuntimeError do
instance_eval File.read(exe)
end
ensure
ARGV.shift if ARGV.first == extra_arg
end
assert_match(/ran executable/, e.message)
end
def test_install_with_no_prior_files
installer = util_setup_installer
build_rake_in do
use_ui @ui do
assert_equal @spec, installer.install
end
end
gemdir = File.join(@gemhome, 'gems', @spec.full_name)
assert_path_exist File.join gemdir, 'lib', 'code.rb'
installer = util_setup_installer
# Morph spec to have lib/other.rb instead of code.rb and recreate
@spec.files = File.join('lib', 'other.rb')
Dir.chdir @tempdir do
File.open File.join('lib', 'other.rb'), 'w' do |f|
f.puts '1'
end
use_ui ui do
FileUtils.rm @gem
Gem::Package.build @spec
end
end
installer = Gem::Installer.at @gem, :force => true
build_rake_in do
use_ui @ui do
assert_equal @spec, installer.install
end
end
assert_path_exist File.join gemdir, 'lib', 'other.rb'
assert_path_not_exist File.join gemdir, 'lib', 'code.rb',
"code.rb from prior install of same gem shouldn't remain here"
end
def test_install_force
_, missing_dep_gem = util_gem 'missing_dep', '1' do |s|
s.add_dependency 'doesnt_exist', '1'
end
use_ui @ui do
installer = Gem::Installer.at missing_dep_gem, :force => true
installer.install
end
gem_dir = File.join(@gemhome, 'gems', 'missing_dep-1')
assert_path_exist gem_dir
end
def test_install_build_root
build_root = File.join(@tempdir, 'build_root')
@gem = setup_base_gem
installer = Gem::Installer.at @gem, :build_root => build_root
assert_equal @spec, installer.install
end
def test_install_missing_dirs
installer = setup_base_installer
FileUtils.rm_f File.join(Gem.dir, 'cache')
FileUtils.rm_f File.join(Gem.dir, 'doc')
FileUtils.rm_f File.join(Gem.dir, 'specifications')
use_ui @ui do
installer.install
end
assert_directory_exists File.join(Gem.dir, 'cache')
assert_directory_exists File.join(Gem.dir, 'doc')
assert_directory_exists File.join(Gem.dir, 'specifications')
assert_path_exist File.join @gemhome, 'cache', @spec.file_name
assert_path_exist File.join @gemhome, 'specifications', @spec.spec_name
end
def test_install_post_build_false
@spec = util_spec 'a'
util_build_gem @spec
installer = util_installer @spec, @gemhome
Gem.post_build do
false
end
use_ui @ui do
e = assert_raise Gem::InstallError do
installer.install
end
location = "#{__FILE__}:#{__LINE__ - 9}"
assert_equal "post-build hook at #{location} failed for a-2", e.message
end
spec_file = File.join @gemhome, 'specifications', @spec.spec_name
assert_path_not_exist spec_file
gem_dir = File.join @gemhome, 'gems', @spec.full_name
assert_path_not_exist gem_dir
end
def test_install_post_build_nil
installer = setup_base_installer
Gem.post_build do
nil
end
use_ui @ui do
installer.install
end
spec_file = File.join @gemhome, 'specifications', @spec.spec_name
assert_path_exist spec_file
gem_dir = File.join @gemhome, 'gems', @spec.full_name
assert_path_exist gem_dir
end
def test_install_pre_install_false
@spec = util_spec 'a'
util_build_gem @spec
installer = util_installer @spec, @gemhome
Gem.pre_install do
false
end
use_ui @ui do
e = assert_raise Gem::InstallError do
installer.install
end
location = "#{__FILE__}:#{__LINE__ - 9}"
assert_equal "pre-install hook at #{location} failed for a-2", e.message
end
spec_file = File.join @gemhome, 'specifications', @spec.spec_name
assert_path_not_exist spec_file
end
def test_install_pre_install_nil
installer = setup_base_installer
Gem.pre_install do
nil
end
use_ui @ui do
installer.install
end
spec_file = File.join @gemhome, 'specifications', @spec.spec_name
assert_path_exist spec_file
end
def test_install_with_message
@spec = setup_base_spec
@spec.post_install_message = 'I am a shiny gem!'
use_ui @ui do
path = Gem::Package.build @spec
installer = Gem::Installer.at path
installer.install
end
assert_match %r{I am a shiny gem!}, @ui.output
end
def test_install_with_skipped_message
@spec = setup_base_spec
@spec.post_install_message = 'I am a shiny gem!'
use_ui @ui do
path = Gem::Package.build @spec
installer = Gem::Installer.at path, :post_install_message => false
installer.install
end
refute_match %r{I am a shiny gem!}, @ui.output
end
def test_install_extension_dir
gemhome2 = "#{@gemhome}2"
@spec = setup_base_spec
@spec.extensions << "extconf.rb"
write_file File.join(@tempdir, "extconf.rb") do |io|
io.write <<-RUBY
require "mkmf"
create_makefile("#{@spec.name}")
RUBY
end
@spec.files += %w[extconf.rb]
use_ui @ui do
path = Gem::Package.build @spec
installer = Gem::Installer.at path, :install_dir => gemhome2
installer.install
end
expected_makefile = File.join gemhome2, 'gems', @spec.full_name, 'Makefile'
assert_path_exist expected_makefile
end
def test_install_extension_dir_is_removed_on_reinstall
@spec = setup_base_spec
@spec.extensions << "extconf.rb"
write_file File.join(@tempdir, "extconf.rb") do |io|
io.write <<-RUBY
require "mkmf"
create_makefile("#{@spec.name}")
RUBY
end
@spec.files += %w[extconf.rb]
path = Gem::Package.build @spec
# Install a gem with an extension
use_ui @ui do
installer = Gem::Installer.at path
installer.install
end
# pretend that a binary file was created as part of the build
should_be_removed = File.join(@spec.extension_dir, "#{@spec.name}.so")
write_file should_be_removed do |io|
io.write "DELETE ME ON REINSTALL"
end
assert_path_exist should_be_removed
# reinstall the gem, this is also the same as pristine
use_ui @ui do
installer = Gem::Installer.at path, :force => true
installer.install
end
assert_path_not_exist should_be_removed
end
def test_install_user_extension_dir
@spec = setup_base_spec
@spec.extensions << "extconf.rb"
write_file File.join(@tempdir, "extconf.rb") do |io|
io.write <<-RUBY
require "mkmf"
create_makefile("#{@spec.name}")
RUBY
end
@spec.files += %w[extconf.rb]
# Create the non-user ext dir
expected_extension_dir = @spec.extension_dir.dup
FileUtils.mkdir_p expected_extension_dir
use_ui @ui do
path = Gem::Package.build @spec
installer = Gem::Installer.at path, :user_install => true
installer.install
end
expected_makefile = File.join Gem.user_dir, 'gems', @spec.full_name, 'Makefile'
assert_path_exist expected_makefile
assert_path_exist expected_extension_dir
assert_path_not_exist File.join expected_extension_dir, 'gem_make.out'
end
def test_find_lib_file_after_install
pend "extensions don't quite work on jruby" if Gem.java_platform?
@spec = setup_base_spec
@spec.extensions << "extconf.rb"
write_file File.join(@tempdir, "extconf.rb") do |io|
io.write <<-RUBY
require "mkmf"
CONFIG['CC'] = '$(TOUCH) $@ ||'
CONFIG['LDSHARED'] = '$(TOUCH) $@ ||'
$ruby = '#{Gem.ruby}'
create_makefile("#{@spec.name}")
RUBY
end
write_file File.join(@tempdir, "depend")
write_file File.join(@tempdir, "a.c") do |io|
io.write <<-C
#include <ruby.h>
void Init_a() { }
C
end
Dir.mkdir File.join(@tempdir, "lib")
write_file File.join(@tempdir, 'lib', "b.rb") do |io|
io.write "# b.rb"
end
@spec.files += %w[extconf.rb lib/b.rb depend a.c]
use_ui @ui do
path = Gem::Package.build @spec
installer = Gem::Installer.at path
installer.install
end
expected = File.join @spec.full_require_paths.find {|path|
File.exist? File.join path, 'b.rb'
}, 'b.rb'
assert_equal expected, @spec.matches_for_glob('b.rb').first
end
def test_install_extension_and_script
pend "Makefile creation crashes on jruby" if Gem.java_platform?
pend if /mswin/ =~ RUBY_PLATFORM && ENV.key?('GITHUB_ACTIONS') # not working from the beginning
@spec = setup_base_spec
@spec.extensions << "extconf.rb"
write_file File.join(@tempdir, "extconf.rb") do |io|
io.write <<-RUBY
require "mkmf"
create_makefile("#{@spec.name}")
RUBY
end
rb = File.join("lib", "#{@spec.name}.rb")
@spec.files += [rb]
write_file File.join(@tempdir, rb) do |io|
io.write <<-RUBY
# #{@spec.name}.rb
RUBY
end
Dir.mkdir(File.join("lib", @spec.name))
rb2 = File.join("lib", @spec.name, "#{@spec.name}.rb")
@spec.files << rb2
write_file File.join(@tempdir, rb2) do |io|
io.write <<-RUBY
# #{@spec.name}/#{@spec.name}.rb
RUBY
end
assert_path_not_exist File.join @spec.gem_dir, rb
assert_path_not_exist File.join @spec.gem_dir, rb2
use_ui @ui do
path = Gem::Package.build @spec
installer = Gem::Installer.at path
installer.install
end
assert_path_exist File.join @spec.gem_dir, rb
assert_path_exist File.join @spec.gem_dir, rb2
end
def test_install_extension_flat
pend "extensions don't quite work on jruby" if Gem.java_platform?
begin
@spec = setup_base_spec
@spec.require_paths = ["."]
@spec.extensions << "extconf.rb"
write_file File.join(@tempdir, "extconf.rb") do |io|
io.write <<-RUBY
require "mkmf"
CONFIG['CC'] = '$(TOUCH) $@ ||'
CONFIG['LDSHARED'] = '$(TOUCH) $@ ||'
$ruby = '#{Gem.ruby}'
create_makefile("#{@spec.name}")
RUBY
end
# empty depend file for no auto dependencies
@spec.files += %W[depend #{@spec.name}.c].each do |file|
write_file File.join(@tempdir, file)
end
so = File.join(@spec.gem_dir, "#{@spec.name}.#{RbConfig::CONFIG["DLEXT"]}")
assert_path_not_exist so
use_ui @ui do
path = Gem::Package.build @spec
installer = Gem::Installer.at path
installer.install
end
assert_path_exist so
rescue
puts '-' * 78
puts File.read File.join(@gemhome, 'gems', 'a-2', 'Makefile')
puts '-' * 78
path = File.join(@gemhome, 'gems', 'a-2', 'gem_make.out')
if File.exist?(path)
puts File.read(path)
puts '-' * 78
end
raise
end
end
def test_installation_satisfies_dependency_eh
installer = setup_base_installer
util_spec 'a'
dep = Gem::Dependency.new 'a', '>= 2'
assert installer.installation_satisfies_dependency?(dep)
dep = Gem::Dependency.new 'a', '> 2'
refute installer.installation_satisfies_dependency?(dep)
end
def test_installation_satisfies_dependency_eh_development
installer = setup_base_installer
installer.options[:development] = true
installer.options[:dev_shallow] = true
util_spec 'a'
dep = Gem::Dependency.new 'a', :development
assert installer.installation_satisfies_dependency?(dep)
end
def test_pre_install_checks_dependencies
installer = setup_base_installer
@spec.add_dependency 'b', '> 5'
installer = util_setup_gem
installer.force = false
use_ui @ui do
assert_raise Gem::InstallError do
installer.install
end
end
end
def test_pre_install_checks_dependencies_ignore
installer = util_setup_installer
@spec.add_dependency 'b', '> 5'
installer.ignore_dependencies = true
build_rake_in do
use_ui @ui do
assert installer.pre_install_checks
end
end
end
def test_pre_install_checks_dependencies_install_dir
gemhome2 = "#{@gemhome}2"
@gem = setup_base_gem
@spec.add_dependency 'd'
quick_gem 'd', 2
gem = File.join @gemhome, @spec.file_name
FileUtils.mv @gemhome, gemhome2
FileUtils.mkdir @gemhome
FileUtils.mv File.join(gemhome2, 'cache', @spec.file_name), gem
# Don't leak any already activated gems into the installer, require
# that it work everything out on it's own.
Gem::Specification.reset
installer = Gem::Installer.at gem, :install_dir => gemhome2
build_rake_in do
use_ui @ui do
assert installer.pre_install_checks
end
end
end
def test_pre_install_checks_malicious_name
spec = util_spec '../malicious', '1'
def spec.full_name # so the spec is buildable
"malicious-1"
end
def spec.validate(packaging, strict); end
util_build_gem spec
gem = File.join(@gemhome, 'cache', spec.file_name)
use_ui @ui do
installer = Gem::Installer.at gem
e = assert_raise Gem::InstallError do
installer.pre_install_checks
end
assert_equal '#<Gem::Specification name=../malicious version=1> has an invalid name', e.message
end
end
def test_pre_install_checks_malicious_name_before_eval
spec = util_spec "malicious\n::Object.const_set(:FROM_EVAL, true)#", '1'
def spec.full_name # so the spec is buildable
"malicious-1"
end
def spec.validate(*args); end
util_build_gem spec
gem = File.join(@gemhome, 'cache', spec.file_name)
use_ui @ui do
installer = Gem::Installer.at gem
e = assert_raise Gem::InstallError do
installer.pre_install_checks
end
assert_equal "#<Gem::Specification name=malicious\n::Object.const_set(:FROM_EVAL, true)# version=1> has an invalid name", e.message
end
refute defined?(::Object::FROM_EVAL)
end
def test_pre_install_checks_malicious_require_paths_before_eval
spec = util_spec "malicious", '1'
def spec.full_name # so the spec is buildable
"malicious-1"
end
def spec.validate(*args); end
spec.require_paths = ["malicious\n``"]
util_build_gem spec
gem = File.join(@gemhome, 'cache', spec.file_name)
use_ui @ui do
installer = Gem::Installer.at gem
e = assert_raise Gem::InstallError do
installer.pre_install_checks
end
assert_equal "#<Gem::Specification name=malicious version=1> has an invalid require_paths", e.message
end
end
def test_pre_install_checks_malicious_extensions_before_eval
pend "mswin environment disallow to create file contained the carriage return code." if Gem.win_platform?
spec = util_spec "malicious", '1'
def spec.full_name # so the spec is buildable
"malicious-1"
end
def spec.validate(*args); end
spec.extensions = ["malicious\n``"]
util_build_gem spec
gem = File.join(@gemhome, 'cache', spec.file_name)
use_ui @ui do
installer = Gem::Installer.at gem
e = assert_raise Gem::InstallError do
installer.pre_install_checks
end
assert_equal "#<Gem::Specification name=malicious version=1> has an invalid extensions", e.message
end
end
def test_pre_install_checks_malicious_specification_version_before_eval
spec = util_spec "malicious", '1'
def spec.full_name # so the spec is buildable
"malicious-1"
end
def spec.validate(*args); end
spec.specification_version = "malicious\n``"
util_build_gem spec
gem = File.join(@gemhome, 'cache', spec.file_name)
use_ui @ui do
installer = Gem::Installer.at gem
e = assert_raise Gem::InstallError do
installer.pre_install_checks
end
assert_equal "#<Gem::Specification name=malicious version=1> has an invalid specification_version", e.message
end
end
def test_pre_install_checks_malicious_dependencies_before_eval
spec = util_spec "malicious", '1'
def spec.full_name # so the spec is buildable
"malicious-1"
end
def spec.validate(*args); end
spec.add_dependency "b\nfoo", '> 5'
util_build_gem spec
gem = File.join(@gemhome, 'cache', spec.file_name)
use_ui @ui do
installer = Gem::Installer.at gem
installer.ignore_dependencies = true
e = assert_raise Gem::InstallError do
installer.pre_install_checks
end
assert_equal "#<Gem::Specification name=malicious version=1> has an invalid dependencies", e.message
end
end
def test_pre_install_checks_malicious_platform_before_eval
gem_with_ill_formated_platform = File.expand_path("packages/ill-formatted-platform-1.0.0.10.gem", __dir__)
installer = Gem::Installer.at(
gem_with_ill_formated_platform,
:install_dir => @gem_home,
:user_install => false,
:force => true
)
use_ui @ui do
e = assert_raise Gem::InstallError do
installer.pre_install_checks
end
assert_equal "x86-mswin32\n system('id > /tmp/nyangawa')# is an invalid platform", e.message
assert_empty @ui.output
end
end
def test_shebang
installer = setup_base_installer
util_make_exec @spec, "#!/usr/bin/ruby"
shebang = installer.shebang 'executable'
assert_equal "#!#{Gem.ruby}", shebang
end
def test_process_options
installer = setup_base_installer
assert_nil installer.build_root
assert_equal File.join(@gemhome, 'bin'), installer.bin_dir
assert_equal @gemhome, installer.gem_home
end
def test_process_options_build_root
build_root = File.join @tempdir, 'build_root'
bin_dir = File.join(build_root, @gemhome.gsub(/^[a-zA-Z]:/, ''), 'bin')
gem_home = File.join(build_root, @gemhome.gsub(/^[a-zA-Z]:/, ''))
plugins_dir = File.join(build_root, @gemhome.gsub(/^[a-zA-Z]:/, ''), 'plugins')
@gem = setup_base_gem
installer = use_ui(@ui) { Gem::Installer.at @gem, :build_root => build_root }
assert_equal build_root, installer.build_root
assert_equal bin_dir, installer.bin_dir
assert_equal gem_home, installer.gem_home
errors = @ui.error.split("\n")
assert_equal "WARNING: You build with buildroot.", errors.shift
assert_equal " Build root: #{build_root}", errors.shift
assert_equal " Bin dir: #{bin_dir}", errors.shift
assert_equal " Gem home: #{gem_home}", errors.shift
assert_equal " Plugins dir: #{plugins_dir}", errors.shift
end
def test_shebang_arguments
installer = setup_base_installer
util_make_exec @spec, "#!/usr/bin/ruby -ws"
shebang = installer.shebang 'executable'
assert_equal "#!#{Gem.ruby} -ws", shebang
end
def test_shebang_empty
installer = setup_base_installer
util_make_exec @spec, ''
shebang = installer.shebang 'executable'
assert_equal "#!#{Gem.ruby}", shebang
end
def test_shebang_env
installer = setup_base_installer
util_make_exec @spec, "#!/usr/bin/env ruby"
shebang = installer.shebang 'executable'
assert_equal "#!#{Gem.ruby}", shebang
end
def test_shebang_env_arguments
installer = setup_base_installer
util_make_exec @spec, "#!/usr/bin/env ruby -ws"
shebang = installer.shebang 'executable'
assert_equal "#!#{Gem.ruby} -ws", shebang
end
def test_shebang_env_shebang
installer = setup_base_installer
util_make_exec @spec, ''
installer.env_shebang = true
shebang = installer.shebang 'executable'
bin_env = get_bin_env
assert_equal("#!#{bin_env} #{RbConfig::CONFIG['ruby_install_name']}",
shebang)
end
def test_shebang_nested
installer = setup_base_installer
util_make_exec @spec, "#!/opt/local/ruby/bin/ruby"
shebang = installer.shebang 'executable'
assert_equal "#!#{Gem.ruby}", shebang
end
def test_shebang_nested_arguments
installer = setup_base_installer
util_make_exec @spec, "#!/opt/local/ruby/bin/ruby -ws"
shebang = installer.shebang 'executable'
assert_equal "#!#{Gem.ruby} -ws", shebang
end
def test_shebang_version
installer = setup_base_installer
util_make_exec @spec, "#!/usr/bin/ruby18"
shebang = installer.shebang 'executable'
assert_equal "#!#{Gem.ruby}", shebang
end
def test_shebang_version_arguments
installer = setup_base_installer
util_make_exec @spec, "#!/usr/bin/ruby18 -ws"
shebang = installer.shebang 'executable'
assert_equal "#!#{Gem.ruby} -ws", shebang
end
def test_shebang_version_env
installer = setup_base_installer
util_make_exec @spec, "#!/usr/bin/env ruby18"
shebang = installer.shebang 'executable'
assert_equal "#!#{Gem.ruby}", shebang
end
def test_shebang_version_env_arguments
installer = setup_base_installer
util_make_exec @spec, "#!/usr/bin/env ruby18 -ws"
shebang = installer.shebang 'executable'
assert_equal "#!#{Gem.ruby} -ws", shebang
end
def test_shebang_custom
installer = setup_base_installer
conf = Gem::ConfigFile.new []
conf[:custom_shebang] = 'test'
Gem.configuration = conf
util_make_exec @spec, "#!/usr/bin/ruby"
shebang = installer.shebang 'executable'
assert_equal "#!test", shebang
end
def get_bin_env
if win_platform?
""
else
%w[/usr/bin/env /bin/env].find {|f| File.executable?(f) }
end
end
def test_shebang_custom_with_expands
installer = setup_base_installer
bin_env = get_bin_env
conf = Gem::ConfigFile.new []
conf[:custom_shebang] = '1 $env 2 $ruby 3 $exec 4 $name'
Gem.configuration = conf
util_make_exec @spec, "#!/usr/bin/ruby"
shebang = installer.shebang 'executable'
assert_equal "#!1 #{bin_env} 2 #{Gem.ruby} 3 executable 4 a", shebang
end
def test_shebang_custom_with_expands_and_arguments
installer = setup_base_installer
bin_env = get_bin_env
conf = Gem::ConfigFile.new []
conf[:custom_shebang] = '1 $env 2 $ruby 3 $exec'
Gem.configuration = conf
util_make_exec @spec, "#!/usr/bin/ruby -ws"
shebang = installer.shebang 'executable'
assert_equal "#!1 #{bin_env} 2 #{Gem.ruby} -ws 3 executable", shebang
end
def test_unpack
installer = util_setup_installer
dest = File.join @gemhome, 'gems', @spec.full_name
Gem::Deprecate.skip_during do
installer.unpack dest
end
assert_path_exist File.join dest, 'lib', 'code.rb'
assert_path_exist File.join dest, 'bin', 'executable'
end
def test_write_build_info_file
installer = setup_base_installer
assert_path_not_exist @spec.build_info_file
installer.build_args = %w[
--with-libyaml-dir /usr/local/Cellar/libyaml/0.1.4
]
installer.write_build_info_file
assert_path_exist @spec.build_info_file
expected = "--with-libyaml-dir\n/usr/local/Cellar/libyaml/0.1.4\n"
assert_equal expected, File.read(@spec.build_info_file)
end
def test_write_build_info_file_empty
installer = setup_base_installer
assert_path_not_exist @spec.build_info_file
installer.write_build_info_file
assert_path_not_exist @spec.build_info_file
end
def test_write_build_info_file_install_dir
@gem = setup_base_gem
installer = Gem::Installer.at @gem, :install_dir => "#{@gemhome}2"
installer.build_args = %w[
--with-libyaml-dir /usr/local/Cellar/libyaml/0.1.4
]
installer.write_build_info_file
assert_path_not_exist @spec.build_info_file
assert_path_exist \
File.join("#{@gemhome}2", 'build_info', "#{@spec.full_name}.info")
end
def test_write_cache_file
@gem = setup_base_gem
cache_file = File.join @gemhome, 'cache', @spec.file_name
gem = File.join @gemhome, @spec.file_name
FileUtils.mv cache_file, gem
assert_path_not_exist cache_file
installer = Gem::Installer.at gem
installer.gem_home = @gemhome
installer.write_cache_file
assert_path_exist cache_file
end
def test_write_spec
@spec = setup_base_spec
FileUtils.rm @spec.spec_file
assert_path_not_exist @spec.spec_file
installer = Gem::Installer.for_spec @spec
installer.gem_home = @gemhome
installer.write_spec
assert_path_exist @spec.spec_file
loaded = Gem::Specification.load @spec.spec_file
assert_equal @spec, loaded
assert_equal Gem.rubygems_version, @spec.installed_by_version
end
def test_write_spec_writes_cached_spec
@spec = setup_base_spec
FileUtils.rm @spec.spec_file
assert_path_not_exist @spec.spec_file
@spec.files = %w[a.rb b.rb c.rb]
installer = Gem::Installer.for_spec @spec
installer.gem_home = @gemhome
installer.write_spec
# cached specs have no file manifest:
@spec.files = []
assert_equal @spec, eval(File.read(@spec.spec_file))
end
def test_dir
installer = setup_base_installer
assert_match %r{/gemhome/gems/a-2$}, installer.dir
end
def test_default_gem_loaded_from
spec = util_spec 'a'
installer = Gem::Installer.for_spec spec, :install_as_default => true
installer.install
assert_predicate spec, :default_gem?
end
def test_default_gem_without_wrappers
installer = setup_base_installer
FileUtils.rm_f File.join(Gem.dir, 'specifications')
installer.wrappers = false
installer.options[:install_as_default] = true
installer.gem_dir = @spec.gem_dir
use_ui @ui do
installer.install
end
assert_directory_exists File.join(@spec.gem_dir, 'bin')
installed_exec = File.join @spec.gem_dir, 'bin', 'executable'
assert_path_exist installed_exec
assert_directory_exists File.join(Gem.default_dir, 'specifications')
assert_directory_exists File.join(Gem.default_dir, 'specifications', 'default')
default_spec = eval File.read File.join(Gem.default_dir, 'specifications', 'default', 'a-2.gemspec')
assert_equal Gem::Version.new("2"), default_spec.version
assert_equal ['bin/executable'], default_spec.files
assert_directory_exists util_inst_bindir
installed_exec = File.join util_inst_bindir, 'executable'
assert_path_exist installed_exec
wrapper = File.read installed_exec
if symlink_supported?
refute_match %r{generated by RubyGems}, wrapper
else # when symlink not supported, it warns and fallbacks back to installing wrapper
assert_match %r{Unable to use symlinks, installing wrapper}, @ui.error
assert_match %r{generated by RubyGems}, wrapper
end
end
def test_default_gem_with_wrappers
installer = setup_base_installer
installer.wrappers = true
installer.options[:install_as_default] = true
installer.gem_dir = @spec.gem_dir
use_ui @ui do
installer.install
end
assert_directory_exists util_inst_bindir
installed_exec = File.join util_inst_bindir, 'executable'
assert_path_exist installed_exec
wrapper = File.read installed_exec
assert_match %r{generated by RubyGems}, wrapper
end
def test_default_gem_with_exe_as_bindir
@spec = quick_gem 'c' do |spec|
util_make_exec spec, '#!/usr/bin/ruby', 'exe'
end
util_build_gem @spec
@spec.cache_file
installer = util_installer @spec, @gemhome
installer.options[:install_as_default] = true
installer.gem_dir = @spec.gem_dir
use_ui @ui do
installer.install
end
assert_directory_exists File.join(@spec.gem_dir, 'exe')
installed_exec = File.join @spec.gem_dir, 'exe', 'executable'
assert_path_exist installed_exec
assert_directory_exists File.join(Gem.default_dir, 'specifications')
assert_directory_exists File.join(Gem.default_dir, 'specifications', 'default')
default_spec = eval File.read File.join(Gem.default_dir, 'specifications', 'default', 'c-2.gemspec')
assert_equal Gem::Version.new("2"), default_spec.version
assert_equal ['exe/executable'], default_spec.files
end
def test_default_gem_to_specific_install_dir
@gem = setup_base_gem
installer = util_installer @spec, "#{@gemhome}2"
installer.options[:install_as_default] = true
use_ui @ui do
installer.install
end
assert_directory_exists File.join("#{@gemhome}2", 'specifications')
assert_directory_exists File.join("#{@gemhome}2", 'specifications', 'default')
default_spec = eval File.read File.join("#{@gemhome}2", 'specifications', 'default', 'a-2.gemspec')
assert_equal Gem::Version.new("2"), default_spec.version
assert_equal ['bin/executable'], default_spec.files
end
def test_package_attribute
gem = quick_gem 'c' do |spec|
util_make_exec spec, '#!/usr/bin/ruby', 'exe'
end
installer = util_installer(gem, @gemhome)
assert_respond_to(installer, :package)
assert_kind_of(Gem::Package, installer.package)
end
def test_gem_attribute
gem = quick_gem 'c' do |spec|
util_make_exec spec, '#!/usr/bin/ruby', 'exe'
end
installer = util_installer(gem, @gemhome)
assert_respond_to(installer, :gem)
assert_kind_of(String, installer.gem)
end
def util_execless
@spec = util_spec 'z'
util_build_gem @spec
util_installer @spec, @gemhome
end
def util_conflict_executable(wrappers)
conflict = quick_gem 'conflict' do |spec|
util_make_exec spec
end
util_build_gem conflict
installer = util_installer conflict, @gemhome
installer.wrappers = wrappers
installer.generate_bin
end
def mask
0100755
end
end