diff --git a/ChangeLog b/ChangeLog index a2e2cfcc3e..e1069d6f92 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +Sun Dec 23 09:34:07 2012 Eric Hodel + + * lib/rubygems/commands/check_command.rb: Added --doctor and --dry-run + options to clean up after failed uninstallation. + * test/rubygems/test_gem_commands_check_command.rb: Test for above. + + * lib/rubygems/commands/push_command.rb: Allow pushes from RubyGems + 2.0.0.preview3 + + * lib/rubygems/commands/update_command.rb: Use Gem.ruby_version + + * lib/rubygems/dependency.rb: Update style. + + * lib/rubygems/installer.rb: Ensure installed gem specifications will + be useable. Refactor. + * test/rubygems/test_gem_installer.rb: ditto. + + * lib/rubygems/validator.rb: Fixed bug with unreadable files. + + * lib/rubygems.rb: Fixed broken methods. + * test/rubygems/test_gem.rb: Test for above. + + * test/rubygems/test_gem_commands_push_command.rb: Fixed overridden + Gem.latest_rubygems_version + Sun Dec 23 01:52:01 2012 Akinori MUSHA * io.c (rb_io_lines, rb_io_bytes, rb_io_chars, rb_io_codepoints): diff --git a/lib/rubygems.rb b/lib/rubygems.rb index 29bbb586b5..77d1650056 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -98,7 +98,7 @@ require 'rbconfig' module Gem - VERSION = '2.0.0.preview2.2' + VERSION = '2.0.0.preview3' end # Must be first since it unloads the prelude from 1.9.2 @@ -123,7 +123,22 @@ module Gem /wince/i, ] - GEM_DEP_FILES = %w!gem.deps.rb Gemfile Isolate! + GEM_DEP_FILES = %w[ + gem.deps.rb + Gemfile + Isolate + ] + + ## + # Subdirectories in a gem repository + + REPOSITORY_SUBDIRECTORIES = %w[ + build_info + cache + doc + gems + specifications + ] @@win_platform = nil @@ -388,7 +403,7 @@ module Gem require 'fileutils' - %w[cache build_info doc gems specifications].each do |name| + REPOSITORY_SUBDIRECTORIES.each do |name| subdir = File.join dir, name next if File.exist? subdir FileUtils.mkdir_p subdir rescue nil # in case of perms issues -- lame @@ -750,33 +765,35 @@ module Gem @ruby end - # DOC: needs doc'd or :nodoc'd + ## + # Returns the latest release-version specification for the gem +name+. + def self.latest_spec_for name - dependency = Gem::Dependency.new name - fetcher = Gem::SpecFetcher.fetcher - spec_tuples = fetcher.find_matching dependency + dependency = Gem::Dependency.new name + fetcher = Gem::SpecFetcher.fetcher + spec_tuples, = fetcher.spec_for_dependency dependency - match = spec_tuples.select { |(n, _, p), _| - n == name and Gem::Platform.match p - }.sort_by { |(_, version, _), _| - version - }.last + spec, = spec_tuples.first - match and fetcher.fetch_spec(*match) + spec end - # DOC: needs doc'd or :nodoc'd + ## + # Returns the latest release version of RubyGems. + + def self.latest_rubygems_version + latest_version_for('rubygems-update') or + raise "Can't find 'rubygems-update' in any repo. Check `gem source list`." + end + + ## + # Returns the version of the latest release-version of gem +name+ + def self.latest_version_for name spec = latest_spec_for name spec and spec.version end - # DOC: needs doc'd or :nodoc'd - def self.latest_rubygems_version - latest_version_for("rubygems-update") or - raise "Can't find 'rubygems-update' in any repo. Check `gem source list`." - end - ## # A Gem::Version for the currently running ruby. diff --git a/lib/rubygems/commands/check_command.rb b/lib/rubygems/commands/check_command.rb index 4bdfcfa645..d7677d47a1 100644 --- a/lib/rubygems/commands/check_command.rb +++ b/lib/rubygems/commands/check_command.rb @@ -1,25 +1,44 @@ require 'rubygems/command' require 'rubygems/version_option' require 'rubygems/validator' +require 'rubygems/doctor' class Gem::Commands::CheckCommand < Gem::Command include Gem::VersionOption def initialize - super 'check', 'Check installed gems', - :alien => true + super 'check', 'Check a gem repository for added or missing files', + :alien => true, :doctor => false, :dry_run => false, :gems => true - add_option('-a', '--alien', "Report 'unmanaged' or rogue files in the", - "gem repository") do |value, options| - options[:alien] = true + add_option('-a', '--[no-]alien', + 'Report "unmanaged" or rogue files in the', + 'gem repository') do |value, options| + options[:alien] = value + end + + add_option('--[no-]doctor', + 'Clean up uninstalled gems and broken', + 'specifications') do |value, options| + options[:doctor] = value + end + + add_option('--[no-]dry-run', + 'Do not remove files, only report what', + 'would be removed') do |value, options| + options[:dry_run] = value + end + + add_option('--[no-]gems', + 'Check installed gems for problems') do |value, options| + options[:gems] = value end add_version_option 'check' end - def execute - say "Checking gems..." + def check_gems + say 'Checking gems...' say gems = get_all_gem_names rescue [] @@ -37,4 +56,31 @@ class Gem::Commands::CheckCommand < Gem::Command end end + def doctor + say 'Checking for files from uninstalled gems...' + say + + Gem.path.each do |gem_repo| + doctor = Gem::Doctor.new gem_repo, options[:dry_run] + doctor.doctor + end + end + + def execute + check_gems if options[:gems] + doctor if options[:doctor] + end + + def arguments # :nodoc: + 'GEMNAME name of gem to check' + end + + def defaults_str # :nodoc: + '--gems --alien' + end + + def usage # :nodoc: + "#{program_name} [OPTIONS] [GEMNAME ...]" + end + end diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb index 0667b47dc1..ce70836823 100644 --- a/lib/rubygems/commands/push_command.rb +++ b/lib/rubygems/commands/push_command.rb @@ -40,9 +40,17 @@ class Gem::Commands::PushCommand < Gem::Command def send_gem name args = [:post, "api/v1/gems"] + latest_rubygems_version = Gem.latest_rubygems_version - if Gem.latest_rubygems_version < Gem::Version.new(Gem::VERSION) then - alert_error "Using beta/unreleased version of rubygems. Not pushing." + if latest_rubygems_version < Gem.rubygems_version and + Gem.rubygems_version.prerelease? and + Gem::Version.new('2.0.0.preview3') != Gem.rubygems_version then + alert_error <<-ERROR +You are using a beta release of RubyGems (#{Gem::VERSION}) which is not +allowed to push gems. Please downgrade or upgrade to a release version. + +The latest released RubyGems version is #{latest_rubygems_version} + ERROR terminate_interaction 1 end diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb index 02f9657435..be76c4c7cd 100644 --- a/lib/rubygems/commands/update_command.rb +++ b/lib/rubygems/commands/update_command.rb @@ -142,7 +142,7 @@ class Gem::Commands::UpdateCommand < Gem::Command gems_to_update = which_to_update hig, options[:args], :system name, up_ver = gems_to_update.first - current_ver = Gem::Version.new Gem::VERSION + current_ver = Gem.rubygems_version target = if options[:system] == true then up_ver diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb index 217189bf8e..1e3cc168a8 100644 --- a/lib/rubygems/dependency.rb +++ b/lib/rubygems/dependency.rb @@ -10,14 +10,14 @@ class Gem::Dependency #-- # When this list is updated, be sure to change # Gem::Specification::CURRENT_SPECIFICATION_VERSION as well. - + # # REFACTOR: This type of constant, TYPES, indicates we might want - # two classes, used via inheretance or duck typing. + # two classes, used via inheritance or duck typing. TYPES = [ - :development, - :runtime, - ] + :development, + :runtime, + ] ## # Dependency name or regular expression. diff --git a/lib/rubygems/doctor.rb b/lib/rubygems/doctor.rb new file mode 100644 index 0000000000..198c758b00 --- /dev/null +++ b/lib/rubygems/doctor.rb @@ -0,0 +1,124 @@ +require 'rubygems' +require 'rubygems/user_interaction' +require 'pathname' + +## +# Cleans up after a partially-failed uninstall or for an invalid +# Gem::Specification. +# +# If a specification was removed by hand this will remove any remaining files. +# +# If a corrupt specification was installed this will clean up warnings by +# removing the bogus specification. + +class Gem::Doctor + + include Gem::UserInteraction + + ## + # Maps a gem subdirectory to the files that are expected to exist in the + # subdirectory. + + REPOSITORY_EXTENSION_MAP = { # :nodoc: + 'build_info' => '.info', + 'cache' => '.gem', + 'doc' => '', + 'gems' => '', + 'specifications' => '.gemspec' + } + + raise 'Update REPOSITORY_EXTENSION_MAP' unless + Gem::REPOSITORY_SUBDIRECTORIES == REPOSITORY_EXTENSION_MAP.keys.sort + + ## + # Creates a new Gem::Doctor that will clean up +gem_repository+. Only one + # gem repository may be cleaned at a time. + # + # If +dry_run+ is true no files or directories will be removed. + + def initialize gem_repository, dry_run = false + @gem_repository = Pathname(gem_repository) + @dry_run = dry_run + + @installed_specs = nil + end + + ## + # Specs installed in this gem repository + + def installed_specs # :nodoc: + @installed_specs ||= Gem::Specification.map { |s| s.full_name } + end + + ## + # Are we doctoring a gem repository? + + def gem_repository? + not installed_specs.empty? + end + + ## + # Cleans up uninstalled files and invalid gem specifications + + def doctor + @orig_home = Gem.dir + @orig_path = Gem.path + + say "Checking #{@gem_repository}" + + Gem.use_paths @gem_repository.to_s + + unless gem_repository? then + say 'This directory does not appear to be a RubyGems repository, ' + + 'skipping' + say + return + end + + doctor_children + + say + ensure + Gem.use_paths @orig_home, *@orig_path + end + + ## + # Cleans up children of this gem repository + + def doctor_children # :nodoc: + REPOSITORY_EXTENSION_MAP.each do |sub_directory, extension| + doctor_child sub_directory, extension + end + end + + ## + # Removes files in +sub_directory+ with +extension+ + + def doctor_child sub_directory, extension # :nodoc: + directory = @gem_repository + sub_directory + + directory.each_child do |child| + next unless child.exist? + + basename = child.basename(extension).to_s + next if installed_specs.include? basename + next if /^rubygems-\d/ =~ basename + next if 'specifications' == sub_directory and 'default' == basename + + type = child.directory? ? 'directory' : 'file' + + action = if @dry_run then + 'Extra' + else + child.rmtree + 'Removed' + end + + say "#{action} #{type} #{sub_directory}/#{child.basename}" + end + rescue Errno::ENOENT + # ignore + end + +end + diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index 6c2e6ce5a3..7f9975678f 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -202,47 +202,24 @@ class Gem::Installer # specifications/.gemspec #=> the Gem::Specification def install - verify_gem_home(options[:unpack]) - - # If we're forcing the install then disable security unless the security - # policy says that we only install signed gems. - @security_policy = nil if @force and @security_policy and - not @security_policy.only_signed - - unless @force - ensure_required_ruby_version_met - ensure_required_rubygems_version_met - ensure_dependencies_met unless @ignore_dependencies - end + pre_install_checks run_pre_install_hooks - Gem.ensure_gem_subdirectories gem_home - # Completely remove any previous gem files - FileUtils.rm_rf(gem_dir) + FileUtils.rm_rf gem_dir FileUtils.mkdir_p gem_dir extract_files - build_extensions + build_extensions + write_build_info_file run_post_build_hooks generate_bin write_spec - - unless @build_args.empty? - File.open spec.build_info_file, "w" do |f| - @build_args.each { |a| f.puts a } - end - end - - # TODO should be always cache the file? Other classes have options - # to controls if caching is done. - cache_file = File.join(gem_home, "cache", "#{spec.full_name}.gem") - - FileUtils.cp gem, cache_file unless File.exist? cache_file + write_cache_file say spec.post_install_message unless spec.post_install_message.nil? @@ -255,7 +232,7 @@ class Gem::Installer spec # TODO This rescue is in the wrong place. What is raising this exception? - # move this rescue to arround the code that actually might raise it. + # move this rescue to around the code that actually might raise it. rescue Zlib::GzipFile::Error raise Gem::InstallError, "gzip error installing #{gem}" end @@ -506,6 +483,21 @@ class Gem::Installer end end + ## + # Ensures the Gem::Specification written out for this gem is loadable upon + # installation. + + def ensure_loadable_spec + ruby = spec.to_ruby_for_cache + + begin + eval ruby + rescue StandardError, SyntaxError => e + raise Gem::InstallError, + "The specification for #{spec.full_name} is corrupt (#{e.class})" + end + end + # DOC: Missing docs or :nodoc:. def ensure_required_ruby_version_met if rrv = spec.required_ruby_version then @@ -736,5 +728,59 @@ EOF def dir gem_dir.to_s end + + ## + # Performs various checks before installing the gem such as the install + # repository is writable and its directories exist, required ruby and + # rubygems versions are met and that dependencies are installed. + # + # Version and dependency checks are skipped if this install is forced. + # + # The dependent check will be skipped this install is ignoring dependencies. + + def pre_install_checks + verify_gem_home options[:unpack] + + # If we're forcing the install then disable security unless the security + # policy says that we only install signed gems. + @security_policy = nil if + @force and @security_policy and not @security_policy.only_signed + + ensure_loadable_spec + + Gem.ensure_gem_subdirectories gem_home + + return true if @force + + ensure_required_ruby_version_met + ensure_required_rubygems_version_met + ensure_dependencies_met unless @ignore_dependencies + + true + end + + ## + # Writes the file containing the arguments for building this gem's + # extensions. + + def write_build_info_file + return if @build_args.empty? + + open spec.build_info_file, 'w' do |io| + @build_args.each do |arg| + io.puts arg + end + end + end + + ## + # Writes the .gem file to the cache directory + + def write_cache_file + cache_file = File.join gem_home, 'cache', spec.file_name + + FileUtils.cp @gem, cache_file unless File.exist? cache_file + end + end diff --git a/lib/rubygems/validator.rb b/lib/rubygems/validator.rb index f66b2c1f43..e5183d401f 100644 --- a/lib/rubygems/validator.rb +++ b/lib/rubygems/validator.rb @@ -58,13 +58,11 @@ class Gem::Validator public ErrorData = Struct.new :path, :problem do - def <=> other return nil unless self.class === other [path, problem] <=> [other.path, other.problem] end - end ## @@ -121,7 +119,6 @@ class Gem::Validator File.readable? File.join(gem_directory, file_name) } - unreadable.map! { |entry, _| entry['path'] } unreadable.sort.each do |path| errors[gem_name][path] = "Unreadable file" end @@ -153,7 +150,9 @@ class Gem::Validator end errors.each do |name, subhash| - errors[name] = subhash.map { |path, msg| ErrorData.new(path, msg) }.sort + errors[name] = subhash.map do |path, msg| + ErrorData.new path, msg + end.sort end errors diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb index 20e411a7e4..9efb056546 100644 --- a/test/rubygems/test_gem.rb +++ b/test/rubygems/test_gem.rb @@ -774,6 +774,45 @@ class TestGem < Gem::TestCase assert_equal cwd, $LOAD_PATH.shift end + def test_self_latest_spec_for + a1 = quick_spec 'a', 1 + a2 = quick_spec 'a', 2 + a3a = quick_spec 'a', '3.a' + + util_setup_fake_fetcher + util_setup_spec_fetcher a1, a2, a3a + + spec = Gem.latest_spec_for 'a' + + assert_equal a2, spec + end + + def test_self_latest_rubygems_version + r1 = quick_spec 'rubygems-update', '1.8.23' + r2 = quick_spec 'rubygems-update', '1.8.24' + r3 = quick_spec 'rubygems-update', '2.0.0.preview3' + + util_setup_fake_fetcher + util_setup_spec_fetcher r1, r2, r3 + + version = Gem.latest_rubygems_version + + assert_equal Gem::Version.new('1.8.24'), version + end + + def test_self_latest_version_for + a1 = quick_spec 'a', 1 + a2 = quick_spec 'a', 2 + a3a = quick_spec 'a', '3.a' + + util_setup_fake_fetcher + util_setup_spec_fetcher a1, a2, a3a + + version = Gem.latest_version_for 'a' + + assert_equal Gem::Version.new(2), version + end + def test_self_loaded_specs foo = quick_spec 'foo' install_gem foo diff --git a/test/rubygems/test_gem_commands_check_command.rb b/test/rubygems/test_gem_commands_check_command.rb index a71c1ebb92..b28748623e 100644 --- a/test/rubygems/test_gem_commands_check_command.rb +++ b/test/rubygems/test_gem_commands_check_command.rb @@ -9,10 +9,60 @@ class TestGemCommandsCheckCommand < Gem::TestCase @cmd = Gem::Commands::CheckCommand.new end + def gem name + spec = quick_gem name do |gem| + gem.files = %W[lib/#{name}.rb Rakefile] + end + + write_file File.join(*%W[gems #{spec.full_name} lib #{name}.rb]) + write_file File.join(*%W[gems #{spec.full_name} Rakefile]) + + spec + end + def test_initialize assert_equal "check", @cmd.command assert_equal "gem check", @cmd.program_name assert_match(/Check/, @cmd.summary) end + def test_handle_options + @cmd.handle_options %w[--no-alien --no-gems --doctor --dry-run] + + assert @cmd.options[:doctor] + refute @cmd.options[:alien] + assert @cmd.options[:dry_run] + refute @cmd.options[:gems] + end + + def test_handle_options_defaults + @cmd.handle_options [] + + assert @cmd.options[:alien] + assert @cmd.options[:gems] + refute @cmd.options[:doctor] + refute @cmd.options[:dry_run] + end + + def test_doctor + a = gem 'a' + b = gem 'b' + + FileUtils.rm b.spec_file + + assert_path_exists b.gem_dir + refute_path_exists b.spec_file + + Gem.use_paths @gemhome + + capture_io do + use_ui @ui do + @cmd.doctor + end + end + + refute_path_exists b.gem_dir + refute_path_exists b.spec_file + end + end diff --git a/test/rubygems/test_gem_commands_push_command.rb b/test/rubygems/test_gem_commands_push_command.rb index 5ff940bf42..41324b524e 100644 --- a/test/rubygems/test_gem_commands_push_command.rb +++ b/test/rubygems/test_gem_commands_push_command.rb @@ -1,14 +1,6 @@ require 'rubygems/test_case' require 'rubygems/commands/push_command' -module Gem - class << self; remove_method :latest_rubygems_version; end - - def self.latest_rubygems_version - Gem::Version.new Gem::VERSION - end -end - class TestGemCommandsPushCommand < Gem::TestCase def setup @@ -33,6 +25,23 @@ class TestGemCommandsPushCommand < Gem::TestCase Gem::RemoteFetcher.fetcher = @fetcher @cmd = Gem::Commands::PushCommand.new + + class << Gem + alias_method :orig_latest_rubygems_version, :latest_rubygems_version + + def latest_rubygems_version + Gem.rubygems_version + end + end + end + + def teardown + super + + class << Gem + remove_method :latest_rubygems_version + alias_method :latest_rubygems_version, :orig_latest_rubygems_version + end end def send_battery diff --git a/test/rubygems/test_gem_doctor.rb b/test/rubygems/test_gem_doctor.rb new file mode 100644 index 0000000000..ef702bcd67 --- /dev/null +++ b/test/rubygems/test_gem_doctor.rb @@ -0,0 +1,168 @@ +require 'rubygems/test_case' +require 'rubygems/doctor' + +class TestGemDoctor < Gem::TestCase + + def gem name + spec = quick_gem name do |gem| + gem.files = %W[lib/#{name}.rb Rakefile] + end + + write_file File.join(*%W[gems #{spec.full_name} lib #{name}.rb]) + write_file File.join(*%W[gems #{spec.full_name} Rakefile]) + + spec + end + + def test_doctor + a = gem 'a' + b = gem 'b' + c = gem 'c' + + Gem.use_paths @userhome, @gemhome + + FileUtils.rm b.spec_file + + open c.spec_file, 'w' do |io| + io.write 'this will raise an exception when evaluated.' + end + + assert_path_exists File.join(a.gem_dir, 'Rakefile') + assert_path_exists File.join(a.gem_dir, 'lib', 'a.rb') + + assert_path_exists b.gem_dir + refute_path_exists b.spec_file + + assert_path_exists c.gem_dir + assert_path_exists c.spec_file + + doctor = Gem::Doctor.new @gemhome + + capture_io do + use_ui @ui do + doctor.doctor + end + end + + assert_path_exists File.join(a.gem_dir, 'Rakefile') + assert_path_exists File.join(a.gem_dir, 'lib', 'a.rb') + + refute_path_exists b.gem_dir + refute_path_exists b.spec_file + + refute_path_exists c.gem_dir + refute_path_exists c.spec_file + + expected = <<-OUTPUT +Checking #{@gemhome} +Removed directory gems/b-2 +Removed directory gems/c-2 +Removed file specifications/c-2.gemspec + + OUTPUT + + assert_equal expected, @ui.output + + assert_equal Gem.dir, @userhome + assert_equal Gem.path, [@gemhome, @userhome] + end + + def test_doctor_dry_run + a = gem 'a' + b = gem 'b' + c = gem 'c' + + Gem.use_paths @userhome, @gemhome + + FileUtils.rm b.spec_file + + open c.spec_file, 'w' do |io| + io.write 'this will raise an exception when evaluated.' + end + + assert_path_exists File.join(a.gem_dir, 'Rakefile') + assert_path_exists File.join(a.gem_dir, 'lib', 'a.rb') + + assert_path_exists b.gem_dir + refute_path_exists b.spec_file + + assert_path_exists c.gem_dir + assert_path_exists c.spec_file + + doctor = Gem::Doctor.new @gemhome, true + + capture_io do + use_ui @ui do + doctor.doctor + end + end + + assert_path_exists File.join(a.gem_dir, 'Rakefile') + assert_path_exists File.join(a.gem_dir, 'lib', 'a.rb') + + assert_path_exists b.gem_dir + refute_path_exists b.spec_file + + assert_path_exists c.gem_dir + assert_path_exists c.spec_file + + expected = <<-OUTPUT +Checking #{@gemhome} +Extra directory gems/b-2 +Extra directory gems/c-2 +Extra file specifications/c-2.gemspec + + OUTPUT + + assert_equal expected, @ui.output + + assert_equal Gem.dir, @userhome + assert_equal Gem.path, [@gemhome, @userhome] + end + + def test_doctor_non_gem_home + other_dir = File.join @tempdir, 'other', 'dir' + + FileUtils.mkdir_p other_dir + + doctor = Gem::Doctor.new @tempdir + + capture_io do + use_ui @ui do + doctor.doctor + end + end + + assert_path_exists other_dir + + expected = <<-OUTPUT +Checking #{@tempdir} +This directory does not appear to be a RubyGems repository, skipping + + OUTPUT + + assert_equal expected, @ui.output + end + + def test_doctor_child_missing + doctor = Gem::Doctor.new @gemhome + + doctor.doctor_child 'missing', '' + + assert true # count + end + + def test_gem_repository_eh + doctor = Gem::Doctor.new @gemhome + + refute doctor.gem_repository?, 'no gems installed' + + quick_spec 'a' + + doctor = Gem::Doctor.new @gemhome + + assert doctor.gem_repository?, 'gems installed' + end + +end + diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb index 0683001f33..98f1878dfb 100644 --- a/test/rubygems/test_gem_installer.rb +++ b/test/rubygems/test_gem_installer.rb @@ -245,6 +245,34 @@ load Gem.bin_path('a', 'executable', version) 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.new a_gem + + e = assert_raises 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_loadable_spec_security_policy + a, a_gem = util_gem 'a', 2 do |s| + s.add_dependency 'garbage ~> 5' + end + + policy = Gem::Security::HighSecurity + installer = Gem::Installer.new a_gem, :security_policy => policy + + assert_raises Gem::Security::Exception do + installer.ensure_loadable_spec + end + end + def test_extract_files @installer.extract_files @@ -818,45 +846,6 @@ load Gem.bin_path('a', 'executable', version) "code.rb from prior install of same gem shouldn't remain here") end - def test_install_check_dependencies - @spec.add_dependency 'b', '> 5' - util_setup_gem - - use_ui @ui do - assert_raises Gem::InstallError do - @installer.install - end - end - end - - def test_install_check_dependencies_install_dir - gemhome2 = "#{@gemhome}2" - @spec.add_dependency 'd' - - quick_gem 'd', 2 - - FileUtils.mv @gemhome, gemhome2 - - # Don't leak any already activated gems into the installer, require - # that it work everything out on it's own. - Gem::Specification.reset - - util_setup_gem - - @installer = Gem::Installer.new @gem, :install_dir => gemhome2 - - gem_home = Gem.dir - - build_rake_in do - use_ui @ui do - @installer.install - end - end - - assert File.exist?(File.join(gemhome2, 'gems', @spec.full_name)) - assert_equal gem_home, Gem.dir - end - def test_install_force use_ui @ui do installer = Gem::Installer.new old_ruby_required, :force => true @@ -867,30 +856,6 @@ load Gem.bin_path('a', 'executable', version) assert File.exist?(gem_dir) end - def test_install_ignore_dependencies - Dir.mkdir util_inst_bindir - @spec.add_dependency 'b', '> 5' - util_setup_gem - @installer.ignore_dependencies = true - - build_rake_in do - use_ui @ui do - assert_equal @spec, @installer.install - end - end - - gemdir = File.join @gemhome, 'gems', @spec.full_name - assert File.exist?(gemdir) - - exe = File.join(gemdir, 'bin', 'executable') - assert File.exist?(exe) - exe_mode = File.stat(exe).mode & 0111 - assert_equal 0111, exe_mode, "0%o" % exe_mode unless win_platform? - assert File.exist?(File.join(gemdir, 'lib', 'code.rb')) - - assert File.exist?(File.join(@gemhome, 'specifications', @spec.spec_name)) - end - def test_install_missing_dirs FileUtils.rm_f File.join(Gem.dir, 'cache') FileUtils.rm_f File.join(Gem.dir, 'docs') @@ -999,18 +964,78 @@ load Gem.bin_path('a', 'executable', version) assert_match %r|I am a shiny gem!|, @ui.output end - def test_install_wrong_ruby_version + def test_installation_satisfies_dependency_eh + quick_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_pre_install_checks_dependencies + @spec.add_dependency 'b', '> 5' + util_setup_gem + + use_ui @ui do + assert_raises Gem::InstallError do + @installer.install + end + end + end + + def test_pre_install_checks_dependencies_ignore + @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" + @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.new gem, :install_dir => gemhome2 + + gem_home = Gem.dir + + build_rake_in do + use_ui @ui do + assert installer.pre_install_checks + end + end + end + + def test_pre_install_checks_ruby_version use_ui @ui do installer = Gem::Installer.new old_ruby_required e = assert_raises Gem::InstallError do - installer.install + installer.pre_install_checks end assert_equal 'old_ruby_required requires Ruby version = 1.4.6.', e.message end end - def test_install_wrong_rubygems_version + def test_pre_install_checks_wrong_rubygems_version spec = quick_spec 'old_rubygems_required', '1' do |s| s.required_rubygems_version = '< 0' end @@ -1022,23 +1047,13 @@ load Gem.bin_path('a', 'executable', version) use_ui @ui do @installer = Gem::Installer.new gem e = assert_raises Gem::InstallError do - @installer.install + @installer.pre_install_checks end assert_equal 'old_rubygems_required requires RubyGems version < 0. ' + "Try 'gem update --system' to update RubyGems itself.", e.message end end - def test_installation_satisfies_dependency_eh - quick_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_shebang util_make_exec @spec, "#!/usr/bin/ruby" @@ -1190,6 +1205,46 @@ load Gem.bin_path('a', 'executable', version) assert File.exist?(File.join(dest, 'bin', 'executable')) end + def test_write_build_args + refute_path_exists @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_exists @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_args_empty + refute_path_exists @spec.build_info_file + + @installer.write_build_info_file + + refute_path_exists @spec.build_info_file + end + + def test_write_cache_file + cache_file = File.join @gemhome, 'cache', @spec.file_name + gem = File.join @gemhome, @spec.file_name + + FileUtils.mv cache_file, gem + refute_path_exists cache_file + + installer = Gem::Installer.new gem + installer.spec = @spec + installer.gem_home = @gemhome + + installer.write_cache_file + + assert_path_exists cache_file + end + def test_write_spec FileUtils.rm @spec.spec_file refute File.exist?(@spec.spec_file)