From 7ed9b794b4e3f3f9874f2ce19401461596d8a2c0 Mon Sep 17 00:00:00 2001 From: drbrain Date: Sun, 8 Dec 2013 01:22:39 +0000 Subject: [PATCH] * lib/rubygems: Update to RubyGems master 14749ce. This fixes bugs handling of gem dependencies lockfiles (Gemfile.lock). * test/rubygems: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@44054 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 7 + lib/rubygems/request_set.rb | 10 + .../request_set/gem_dependency_api.rb | 42 +- lib/rubygems/request_set/lockfile.rb | 222 +++++++-- lib/rubygems/requirement.rb | 18 +- lib/rubygems/resolver.rb | 40 +- lib/rubygems/resolver/git_set.rb | 26 ++ lib/rubygems/resolver/lock_set.rb | 6 +- lib/rubygems/resolver/lock_specification.rb | 58 +++ lib/rubygems/resolver/requirement_list.rb | 25 +- lib/rubygems/resolver/stats.rb | 44 ++ lib/rubygems/source.rb | 3 + lib/rubygems/source/git.rb | 10 + lib/rubygems/source/lock.rb | 4 + lib/rubygems/test_case.rb | 8 +- lib/rubygems/test_utilities.rb | 27 +- test/rubygems/test_gem_request_set.rb | 10 +- ...test_gem_request_set_gem_dependency_api.rb | 21 + .../rubygems/test_gem_request_set_lockfile.rb | 431 +++++++++++++++++- test/rubygems/test_gem_requirement.rb | 7 + test/rubygems/test_gem_resolver.rb | 23 +- test/rubygems/test_gem_resolver_git_set.rb | 28 +- .../test_gem_resolver_git_specification.rb | 16 + test/rubygems/test_gem_resolver_lock_set.rb | 4 +- .../test_gem_resolver_lock_specification.rb | 87 ++++ .../test_gem_resolver_requirement_list.rb | 7 +- test/rubygems/test_gem_source_lock.rb | 7 + 27 files changed, 1083 insertions(+), 108 deletions(-) create mode 100644 lib/rubygems/resolver/lock_specification.rb create mode 100644 lib/rubygems/resolver/stats.rb create mode 100644 test/rubygems/test_gem_resolver_lock_specification.rb diff --git a/ChangeLog b/ChangeLog index 431718daca..757e07abee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Sun Dec 8 10:21:36 2013 Eric Hodel + + * lib/rubygems: Update to RubyGems master 14749ce. This fixes bugs + handling of gem dependencies lockfiles (Gemfile.lock). + + * test/rubygems: ditto. + Sun Dec 8 09:40:00 2013 Charlie Somerville * array.c (rb_ary_or): use RHASH_TBL_RAW instead of RHASH_TBL diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb index 54e4d9dee4..68a8112c37 100644 --- a/lib/rubygems/request_set.rb +++ b/lib/rubygems/request_set.rb @@ -158,6 +158,10 @@ class Gem::RequestSet specs.map { |s| s.full_name }.sort.each do |s| puts " #{s}" end + + if Gem.configuration.really_verbose + @resolver.stats.display + end else installed = install options, &block @@ -169,6 +173,8 @@ class Gem::RequestSet end def install_into dir, force = true, options = {} + gem_home, ENV['GEM_HOME'] = ENV['GEM_HOME'], dir + existing = force ? [] : specs_in(dir) existing.delete_if { |s| @always_install.include? s } @@ -195,6 +201,8 @@ class Gem::RequestSet end installed + ensure + ENV['GEM_HOME'] = gem_home end ## @@ -229,6 +237,8 @@ class Gem::RequestSet resolver.development = @development resolver.soft_missing = @soft_missing + @resolver = resolver + @requests = resolver.resolve end diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb index 0c27b1a61a..efce979177 100644 --- a/lib/rubygems/request_set/gem_dependency_api.rb +++ b/lib/rubygems/request_set/gem_dependency_api.rb @@ -221,13 +221,7 @@ class Gem::RequestSet::GemDependencyAPI return unless (groups & @without_groups).empty? - unless source_set then - raise ArgumentError, - "duplicate source (default) for gem #{name}" if - @gem_sources.include? name - - @gem_sources[name] = :default - end + pin_gem_source name, :default unless source_set gem_requires name, options @@ -246,9 +240,7 @@ class Gem::RequestSet::GemDependencyAPI return unless repository = options.delete(:git) - raise ArgumentError, - "duplicate source git: #{repository} for gem #{name}" if - @gem_sources.include? name + pin_gem_source name, :git, repository reference = nil reference ||= options.delete :ref @@ -260,8 +252,6 @@ class Gem::RequestSet::GemDependencyAPI @git_set.add_git_gem name, repository, reference, submodules - @gem_sources[name] = repository - true end @@ -310,14 +300,10 @@ class Gem::RequestSet::GemDependencyAPI def gem_path name, options # :nodoc: return unless directory = options.delete(:path) - raise ArgumentError, - "duplicate source path: #{directory} for gem #{name}" if - @gem_sources.include? name + pin_gem_source name, :path, directory @vendor_set.add_vendor_gem name, directory - @gem_sources[name] = directory - true end @@ -429,6 +415,28 @@ class Gem::RequestSet::GemDependencyAPI @current_groups = nil end + ## + # Pins the gem +name+ to the given +source+. Adding a gem with the same + # name from a different +source+ will raise an exception. + + def pin_gem_source name, type = :default, source = nil + source_description = + case type + when :default then '(default)' + when :path then "path: #{source}" + when :git then "git: #{source}" + else '(unknown)' + end + + raise ArgumentError, + "duplicate source #{source_description} for gem #{name}" if + @gem_sources.fetch(name, source) != source + + @gem_sources[name] = source + end + + private :pin_gem_source + ## # :category: Gem Dependencies DSL # diff --git a/lib/rubygems/request_set/lockfile.rb b/lib/rubygems/request_set/lockfile.rb index d70b09f7a2..81bc4d620d 100644 --- a/lib/rubygems/request_set/lockfile.rb +++ b/lib/rubygems/request_set/lockfile.rb @@ -1,3 +1,5 @@ +require 'strscan' + ## # Parses a gem.deps.rb.lock file and constructs a LockSet containing the # dependencies found inside. If the lock file is missing no LockSet is @@ -29,11 +31,11 @@ class Gem::RequestSet::Lockfile # Raises a ParseError with the given +message+ which was encountered at a # +line+ and +column+ while parsing. - def initialize message, line, column, path + def initialize message, column, line, path @line = line @column = column @path = path - super "#{message} (at #{line}:#{column})" + super "#{message} (at line #{line} column #{column})" end end @@ -62,30 +64,31 @@ class Gem::RequestSet::Lockfile def add_DEPENDENCIES out # :nodoc: out << "DEPENDENCIES" - @set.dependencies.sort.map do |dependency| - source = @requests.find do |req| - req.name == dependency.name and - req.spec.class == Gem::Resolver::VendorSpecification + @requests.sort_by { |r| r.name }.each do |request| + spec = request.spec + + if [Gem::Resolver::VendorSpecification, + Gem::Resolver::GitSpecification].include? spec.class then + out << " #{request.name}!" + else + requirement = request.request.dependency.requirement + + out << " #{request.name}#{requirement.for_lockfile}" end - - source_dep = '!' if source - - requirement = dependency.requirement - - out << " #{dependency.name}#{source_dep}#{requirement.for_lockfile}" end out << nil end def add_GEM out # :nodoc: - out << "GEM" + return if @spec_groups.empty? source_groups = @spec_groups.values.flatten.group_by do |request| request.spec.source.uri end - source_groups.map do |group, requests| + source_groups.sort_by { |group,| group.to_s }.map do |group, requests| + out << "GEM" out << " remote: #{group}" out << " specs:" @@ -100,6 +103,33 @@ class Gem::RequestSet::Lockfile out << " #{dependency.name}#{requirement.for_lockfile}" end end + out << nil + end + end + + def add_GIT out + return unless git_requests = + @spec_groups.delete(Gem::Resolver::GitSpecification) + + by_repository_revision = git_requests.group_by do |request| + source = request.spec.source + [source.repository, source.rev_parse] + end + + out << "GIT" + by_repository_revision.each do |(repository, revision), requests| + out << " remote: #{repository}" + out << " revision: #{revision}" + out << " specs:" + + requests.sort_by { |request| request.name }.each do |request| + out << " #{request.name} (#{request.version})" + + dependencies = request.spec.dependencies.sort_by { |dep| dep.name } + dependencies.each do |dep| + out << " #{dep.name}#{dep.requirement.for_lockfile}" + end + end end out << nil @@ -148,27 +178,28 @@ class Gem::RequestSet::Lockfile ## # Gets the next token for a Lockfile - def get expected_type = nil, expected_value = nil # :nodoc: + def get expected_types = nil, expected_value = nil # :nodoc: @current_token = @tokens.shift - type, value, line, column = @current_token + type, value, column, line = @current_token - if expected_type and expected_type != type then + if expected_types and not Array(expected_types).include? type then unget message = "unexpected token [#{type.inspect}, #{value.inspect}], " + - "expected #{expected_type.inspect}" + "expected #{expected_types.inspect}" - raise ParseError.new message, line, column, "#{@gem_deps_file}.lock" + raise ParseError.new message, column, line, "#{@gem_deps_file}.lock" end if expected_value and expected_value != value then unget message = "unexpected token [#{type.inspect}, #{value.inspect}], " + - "expected [#{expected_type.inspect}, #{expected_value.inspect}]" + "expected [#{expected_types.inspect}, " + + "#{expected_value.inspect}]" - raise ParseError.new message, line, column, "#{@gem_deps_file}.lock" + raise ParseError.new message, column, line, "#{@gem_deps_file}.lock" end @current_token @@ -187,6 +218,8 @@ class Gem::RequestSet::Lockfile case data when 'DEPENDENCIES' then parse_DEPENDENCIES + when 'GIT' then + parse_GIT when 'GEM' then parse_GEM when 'PLATFORMS' then @@ -195,7 +228,7 @@ class Gem::RequestSet::Lockfile type, = get until @tokens.empty? or peek.first == :section end else - raise "BUG: unhandled token #{type} (#{data.inspect}) at #{line}:#{column}" + raise "BUG: unhandled token #{type} (#{data.inspect}) at line #{line} column #{column}" end end end @@ -204,7 +237,37 @@ class Gem::RequestSet::Lockfile while not @tokens.empty? and :text == peek.first do _, name, = get :text - @set.gem name + requirements = [] + + case peek[0] + when :bang then + get :bang + + git_spec = @set.sets.select { |set| + Gem::Resolver::GitSet === set + }.map { |set| + set.specs[name] + }.first + + requirements << git_spec.version + when :l_paren then + get :l_paren + + loop do + _, op, = get :requirement + _, version, = get :text + + requirements << "#{op} #{version}" + + break unless peek[0] == :comma + + get :comma + end + + get :r_paren + end + + @set.gem name, *requirements skip :newline end @@ -223,20 +286,76 @@ class Gem::RequestSet::Lockfile skip :newline set = Gem::Resolver::LockSet.new source + last_spec = nil while not @tokens.empty? and :text == peek.first do - _, name, = get :text + _, name, column, = get :text case peek[0] - when :newline then # ignore + when :newline then + last_spec.add_dependency Gem::Dependency.new name if column == 6 when :l_paren then get :l_paren - _, version, = get :text + type, data, = get [:text, :requirement] + + if type == :text and column == 4 then + last_spec = set.add name, data, Gem::Platform::RUBY + else + dependency = parse_dependency name, data + + last_spec.add_dependency dependency + end get :r_paren + else + raise "BUG: unknown token #{peek}" + end - set.add name, version, Gem::Platform::RUBY + skip :newline + end + + @set.sets << set + end + + def parse_GIT # :nodoc: + get :entry, 'remote' + _, repository, = get :text + + skip :newline + + get :entry, 'revision' + _, revision, = get :text + + skip :newline + + get :entry, 'specs' + + skip :newline + + set = Gem::Resolver::GitSet.new + last_spec = nil + + while not @tokens.empty? and :text == peek.first do + _, name, column, = get :text + + case peek[0] + when :newline then + last_spec.add_dependency Gem::Dependency.new name if column == 6 + when :l_paren then + get :l_paren + + type, data, = get [:text, :requirement] + + if type == :text and column == 4 then + last_spec = set.add_git_spec name, data, repository, revision, true + else + dependency = parse_dependency name, data + + last_spec.spec.dependencies << dependency + end + + get :r_paren else raise "BUG: unknown token #{peek}" end @@ -257,11 +376,33 @@ class Gem::RequestSet::Lockfile end end + ## + # Parses the requirements following the dependency +name+ and the +op+ for + # the first token of the requirements and returns a Gem::Dependency object. + + def parse_dependency name, op # :nodoc: + return Gem::Dependency.new name unless peek[0] == :text + + _, version, = get :text + + requirements = ["#{op} #{version}"] + + while peek[0] == :comma do + get :comma + _, op, = get :requirement + _, version, = get :text + + requirements << "#{op} #{version}" + end + + Gem::Dependency.new name, requirements + end + ## # Peeks at the next token for Lockfile def peek # :nodoc: - @tokens.first + @tokens.first || :EOF end def skip type # :nodoc: @@ -284,6 +425,8 @@ class Gem::RequestSet::Lockfile add_PATH out + add_GIT out + add_GEM out add_PLATFORMS out @@ -321,14 +464,13 @@ class Gem::RequestSet::Lockfile until s.eos? do pos = s.pos - # leading whitespace is for the user's convenience - next if s.scan(/ +/) + pos = s.pos if leading_whitespace = s.scan(/ +/) if s.scan(/[<|=>]{7}/) then message = "your #{lock_file} contains merge conflict markers" - line, column = token_pos pos + column, line = token_pos pos - raise ParseError.new message, line, column, lock_file + raise ParseError.new message, column, line, lock_file end @tokens << @@ -339,7 +481,13 @@ class Gem::RequestSet::Lockfile @line += 1 token when s.scan(/[A-Z]+/) then - [:section, s.matched, *token_pos(pos)] + if leading_whitespace then + text = s.matched + text += s.scan(/[^\s)]*/).to_s # in case of no match + [:text, text, *token_pos(pos)] + else + [:section, s.matched, *token_pos(pos)] + end when s.scan(/([a-z]+):\s/) then s.pos -= 1 # rewind for possible newline [:entry, s[1], *token_pos(pos)] @@ -347,7 +495,13 @@ class Gem::RequestSet::Lockfile [:l_paren, nil, *token_pos(pos)] when s.scan(/\)/) then [:r_paren, nil, *token_pos(pos)] - when s.scan(/[^\s)]*/) then + when s.scan(/<=|>=|=|~>|<|>|!=/) then + [:requirement, s.matched, *token_pos(pos)] + when s.scan(/,/) then + [:comma, nil, *token_pos(pos)] + when s.scan(/!/) then + [:bang, nil, *token_pos(pos)] + when s.scan(/[^\s),!]*/) then [:text, s.matched, *token_pos(pos)] else raise "BUG: can't create token for: #{s.string[s.pos..-1].inspect}" diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb index 2b112a8022..ece9d00f38 100644 --- a/lib/rubygems/requirement.rb +++ b/lib/rubygems/requirement.rb @@ -133,7 +133,15 @@ class Gem::Requirement # Formats this requirement for use in a Gem::RequestSet::Lockfile. def for_lockfile # :nodoc: - " (#{to_s})" unless [DefaultRequirement] == @requirements + return if [DefaultRequirement] == @requirements + + list = requirements.sort_by { |_, version| + version + }.map { |op, version| + "#{op} #{version}" + }.uniq + + " (#{list.join ', '})" end ## @@ -147,6 +155,14 @@ class Gem::Requirement end end + ## + # true if the requirement is for only an exact version + + def exact? + return false unless @requirements.size == 1 + @requirements[0][0] == "=" + end + def as_list # :nodoc: requirements.map { |op, version| "#{op} #{version}" }.sort end diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb index 8f11acc197..ad9373cb86 100644 --- a/lib/rubygems/resolver.rb +++ b/lib/rubygems/resolver.rb @@ -36,6 +36,8 @@ class Gem::Resolver attr_reader :missing + attr_reader :stats + ## # When a missing dependency, don't stop. Just go on and record what was # missing. @@ -93,6 +95,7 @@ class Gem::Resolver @development = false @missing = [] @soft_missing = false + @stats = Gem::Resolver::Stats.new end def explain stage, *data # :nodoc: @@ -132,10 +135,13 @@ class Gem::Resolver s.dependencies.reverse_each do |d| next if d.type == :development and not @development reqs.add Gem::Resolver::DependencyRequest.new(d, act) + @stats.requirement! end @set.prefetch reqs + @stats.record_requirements reqs + reqs end @@ -151,8 +157,11 @@ class Gem::Resolver request = Gem::Resolver::DependencyRequest.new n, nil needed.add request + @stats.requirement! end + @stats.record_requirements needed + res = resolve_for needed, nil raise Gem::DependencyResolutionError, res if @@ -268,6 +277,8 @@ class Gem::Resolver states = [] while !needed.empty? + @stats.iteration! + dep = needed.remove explain :try, [dep, dep.requester ? dep.requester.request : :toplevel] explain_list :next5, needed.next5 @@ -279,12 +290,33 @@ class Gem::Resolver next if dep.matches_spec? existing conflict = handle_conflict dep, existing - explain :conflict, conflict.explain - state = find_conflict_state conflict, states + return conflict unless dep.requester + + explain :conflict, dep, :existing, existing.full_name + + depreq = dep.requester.request + + state = nil + until states.empty? + x = states.pop + + i = existing.request.requester + explain :consider, x.spec.full_name, [depreq.name, dep.name, i ? i.name : :top] + + if x.spec.name == depreq.name or + x.spec.name == dep.name or + (i && (i.name == x.spec.name)) + explain :found, x.spec.full_name + state = x + break + end + end return conflict unless state + @stats.backtracking! + needed, specs = resolve_for_conflict needed, specs, state states << state unless state.possibles.empty? @@ -346,6 +378,8 @@ class Gem::Resolver # what makes conflict resolution possible. states << State.new(needed.dup, specs, dep, spec, possible, []) + @stats.record_depth states + explain :states, states.map { |s| s.dep } needed = requests spec, act, needed @@ -404,6 +438,7 @@ require 'rubygems/resolver/activation_request' require 'rubygems/resolver/conflict' require 'rubygems/resolver/dependency_request' require 'rubygems/resolver/requirement_list' +require 'rubygems/resolver/stats' require 'rubygems/resolver/set' require 'rubygems/resolver/api_set' @@ -423,5 +458,6 @@ require 'rubygems/resolver/git_specification' require 'rubygems/resolver/index_specification' require 'rubygems/resolver/installed_specification' require 'rubygems/resolver/local_specification' +require 'rubygems/resolver/lock_specification' require 'rubygems/resolver/vendor_specification' diff --git a/lib/rubygems/resolver/git_set.rb b/lib/rubygems/resolver/git_set.rb index ed809c124f..1a2b230b80 100644 --- a/lib/rubygems/resolver/git_set.rb +++ b/lib/rubygems/resolver/git_set.rb @@ -45,6 +45,32 @@ class Gem::Resolver::GitSet < Gem::Resolver::Set @need_submodules[repository] = submodules end + ## + # Adds and returns a GitSpecification with the given +name+ and +version+ + # which came from a +repository+ at the given +reference+. If +submodules+ + # is true they are checked out along with the repository. + # + # This fills in the prefetch information as enough information about the gem + # is present in the arguments. + + def add_git_spec name, version, repository, reference, submodules # :nodoc: + add_git_gem name, repository, reference, submodules + + source = Gem::Source::Git.new name, repository, reference + source.root_dir = @root_dir + + spec = Gem::Specification.new do |s| + s.name = name + s.version = version + end + + git_spec = Gem::Resolver::GitSpecification.new self, spec, source + + @specs[spec.name] = git_spec + + git_spec + end + ## # Finds all git gems matching +req+ diff --git a/lib/rubygems/resolver/lock_set.rb b/lib/rubygems/resolver/lock_set.rb index ef5395597d..cdb41b22bf 100644 --- a/lib/rubygems/resolver/lock_set.rb +++ b/lib/rubygems/resolver/lock_set.rb @@ -24,10 +24,12 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set version = Gem::Version.new version spec = - Gem::Resolver::IndexSpecification.new self, name, version, @source, - platform + Gem::Resolver::LockSpecification.new self, name, version, @source, + platform @specs << spec + + spec end ## diff --git a/lib/rubygems/resolver/lock_specification.rb b/lib/rubygems/resolver/lock_specification.rb new file mode 100644 index 0000000000..4bc21b9402 --- /dev/null +++ b/lib/rubygems/resolver/lock_specification.rb @@ -0,0 +1,58 @@ +## +# The LockSpecification comes from a lockfile (Gem::RequestSet::Lockfile). +# +# A LockSpecification's dependency information is pre-filled from the +# lockfile. + +class Gem::Resolver::LockSpecification < Gem::Resolver::Specification + + def initialize set, name, version, source, platform + super() + + @name = name + @platform = platform + @set = set + @source = source + @version = version + + @dependencies = [] + @spec = nil + end + + ## + # This is a null install as a locked specification is considered installed. + # +options+ are ignored. + + def install options + destination = options[:install_dir] || Gem.dir + + if File.exist? File.join(destination, 'specifications', spec.spec_name) then + yield nil + return + end + + super + end + + ## + # Adds +dependency+ from the lockfile to this specification + + def add_dependency dependency # :nodoc: + @dependencies << dependency + end + + ## + # A specification constructed from the lockfile is returned + + def spec + @spec ||= Gem::Specification.new do |s| + s.name = @name + s.version = @version + s.platform = @platform + + s.dependencies.concat @dependencies + end + end + +end + diff --git a/lib/rubygems/resolver/requirement_list.rb b/lib/rubygems/resolver/requirement_list.rb index fe1d77afc3..a6bfaab307 100644 --- a/lib/rubygems/resolver/requirement_list.rb +++ b/lib/rubygems/resolver/requirement_list.rb @@ -13,10 +13,12 @@ class Gem::Resolver::RequirementList # Creates a new RequirementList. def initialize + @exact = [] @list = [] end def initialize_copy other # :nodoc: + @exact = @exact.dup @list = @list.dup end @@ -24,7 +26,11 @@ class Gem::Resolver::RequirementList # Adds Resolver::DependencyRequest +req+ to this requirements list. def add(req) - @list.push req + if req.requirement.exact? + @exact.push req + else + @list.push req + end req end @@ -34,22 +40,34 @@ class Gem::Resolver::RequirementList def each # :nodoc: return enum_for __method__ unless block_given? + @exact.each do |requirement| + yield requirement + end + @list.each do |requirement| yield requirement end end + ## + # How many elements are in the list + + def size + @exact.size + @list.size + end + ## # Is the list empty? def empty? - @list.empty? + @exact.empty? && @list.empty? end ## # Remove the oldest DependencyRequest from the list. def remove + return @exact.shift unless @exact.empty? @list.shift end @@ -57,6 +75,7 @@ class Gem::Resolver::RequirementList # Returns the oldest five entries from the list. def next5 - @list[0,5] + x = @exact[0,5] + x + @list[0,5 - x.size] end end diff --git a/lib/rubygems/resolver/stats.rb b/lib/rubygems/resolver/stats.rb new file mode 100644 index 0000000000..c31e5be962 --- /dev/null +++ b/lib/rubygems/resolver/stats.rb @@ -0,0 +1,44 @@ +class Gem::Resolver::Stats + def initialize + @max_depth = 0 + @max_requirements = 0 + @requirements = 0 + @backtracking = 0 + @iterations = 0 + end + + def record_depth(stack) + if stack.size > @max_depth + @max_depth = stack.size + end + end + + def record_requirements(reqs) + if reqs.size > @max_requirements + @max_requirements = reqs.size + end + end + + def requirement! + @requirements += 1 + end + + def backtracking! + @backtracking += 1 + end + + def iteration! + @iterations += 1 + end + + PATTERN = "%20s: %d\n" + + def display + $stdout.puts "=== Resolver Statistics ===" + $stdout.printf PATTERN, "Max Depth", @max_depth + $stdout.printf PATTERN, "Total Requirements", @requirements + $stdout.printf PATTERN, "Max Requirements", @max_requirements + $stdout.printf PATTERN, "Backtracking #", @backtracking + $stdout.printf PATTERN, "Iteration #", @iterations + end +end diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb index 9553db18c6..7f34e43528 100644 --- a/lib/rubygems/source.rb +++ b/lib/rubygems/source.rb @@ -202,7 +202,10 @@ class Gem::Source q.group 2, '[Remote:', ']' do q.breakable q.text @uri.to_s + if api = api_uri + q.breakable + q.text 'API URI: ' q.text api.to_s end end diff --git a/lib/rubygems/source/git.rb b/lib/rubygems/source/git.rb index e8b03165c1..74cbab9562 100644 --- a/lib/rubygems/source/git.rb +++ b/lib/rubygems/source/git.rb @@ -147,6 +147,16 @@ class Gem::Source::Git < Gem::Source File.join base_dir, 'gems', "#{@name}-#{dir_shortref}" end + def pretty_print q # :nodoc: + q.group 2, '[Git: ', ']' do + q.breakable + q.text @repository + + q.breakable + q.text @reference + end + end + ## # The directory where the git gem's repository will be cached. diff --git a/lib/rubygems/source/lock.rb b/lib/rubygems/source/lock.rb index d8a46d8d10..2ba7702bda 100644 --- a/lib/rubygems/source/lock.rb +++ b/lib/rubygems/source/lock.rb @@ -40,5 +40,9 @@ class Gem::Source::Lock < Gem::Source @wrapped.fetch_spec name_tuple end + def uri # :nodoc: + @wrapped.uri + end + end diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb index f3967aba8b..314682f13b 100644 --- a/lib/rubygems/test_case.rb +++ b/lib/rubygems/test_case.rb @@ -85,6 +85,10 @@ class Gem::TestCase < MiniTest::Unit::TestCase attr_accessor :fetcher # :nodoc: + attr_accessor :gem_repo # :nodoc: + + attr_accessor :uri # :nodoc: + def assert_activate expected, *specs specs.each do |spec| case spec @@ -1196,8 +1200,8 @@ Also, a list: # end # end - def spec_fetcher - Gem::TestCase::SpecFetcherSetup.declare self do |spec_fetcher_setup| + def spec_fetcher repository = @gem_repo + Gem::TestCase::SpecFetcherSetup.declare self, repository do |spec_fetcher_setup| yield spec_fetcher_setup if block_given? end end diff --git a/lib/rubygems/test_utilities.rb b/lib/rubygems/test_utilities.rb index ee5ef01203..3deb71fa46 100644 --- a/lib/rubygems/test_utilities.rb +++ b/lib/rubygems/test_utilities.rb @@ -199,16 +199,17 @@ class Gem::TestCase::SpecFetcherSetup # Executes a SpecFetcher setup block. Yields an instance then creates the # gems and specifications defined in the instance. - def self.declare test - setup = new test + def self.declare test, repository + setup = new test, repository yield setup setup.execute end - def initialize test # :nodoc: - @test = test + def initialize test, repository # :nodoc: + @test = test + @repository = repository @gems = {} @installed = [] @@ -298,12 +299,22 @@ class Gem::TestCase::SpecFetcherSetup require 'socket' require 'rubygems/remote_fetcher' - @test.fetcher = Gem::FakeFetcher.new - Gem::RemoteFetcher.fetcher = @test.fetcher + unless @test.fetcher then + @test.fetcher = Gem::FakeFetcher.new + Gem::RemoteFetcher.fetcher = @test.fetcher + end Gem::Specification.reset - @test.util_setup_spec_fetcher(*@gems.keys) + begin + gem_repo, @test.gem_repo = @test.gem_repo, @repository + @test.uri = URI @repository + + @test.util_setup_spec_fetcher(*@gems.keys) + ensure + @test.gem_repo = gem_repo + @test.uri = URI gem_repo + end # This works around util_setup_spec_fetcher adding all created gems to the # installed set. @@ -313,7 +324,7 @@ class Gem::TestCase::SpecFetcherSetup @gems.each do |spec, gem| next unless gem - @test.fetcher.data["http://gems.example.com/gems/#{spec.file_name}"] = + @test.fetcher.data["#{@repository}gems/#{spec.file_name}"] = Gem.read_binary(gem) FileUtils.cp gem, spec.cache_file diff --git a/test/rubygems/test_gem_request_set.rb b/test/rubygems/test_gem_request_set.rb index 324d0cd7a8..aaff97ab4f 100644 --- a/test/rubygems/test_gem_request_set.rb +++ b/test/rubygems/test_gem_request_set.rb @@ -95,6 +95,7 @@ class TestGemRequestSet < Gem::TestCase fetcher.gem 'a', 1 fetcher.gem 'a', 2 fetcher.gem 'b', 1, 'a' => '>= 0' + fetcher.clear end rs = Gem::RequestSet.new @@ -107,7 +108,7 @@ GEM specs: a (1) b (1) - a + a (~> 1.0) PLATFORMS #{Gem::Platform::RUBY} @@ -127,6 +128,9 @@ DEPENDENCIES assert_includes installed, 'b-1' assert_includes installed, 'a-1' + + assert_path_exists File.join @gemhome, 'specifications', 'a-1.gemspec' + assert_path_exists File.join @gemhome, 'specifications', 'b-1.gemspec' end def test_load_gemdeps @@ -301,7 +305,9 @@ DEPENDENCIES rs.resolve - installed = rs.install_into @tempdir + installed = rs.install_into @tempdir do + assert_equal @tempdir, ENV['GEM_HOME'] + end assert_path_exists File.join @tempdir, 'specifications', 'a-1.gemspec' assert_path_exists File.join @tempdir, 'specifications', 'b-1.gemspec' diff --git a/test/rubygems/test_gem_request_set_gem_dependency_api.rb b/test/rubygems/test_gem_request_set_gem_dependency_api.rb index c41d983744..cadaf76883 100644 --- a/test/rubygems/test_gem_request_set_gem_dependency_api.rb +++ b/test/rubygems/test_gem_request_set_gem_dependency_api.rb @@ -514,6 +514,27 @@ end assert_same @GDA, Gem::RequestSet::GemDepedencyAPI end + def test_pin_gem_source + gda = @GDA.new @set, nil + + gda.send :pin_gem_source, 'a' + gda.send :pin_gem_source, 'a' + + e = assert_raises ArgumentError do + gda.send :pin_gem_source, 'a', :path, 'vendor/a' + end + + assert_equal "duplicate source path: vendor/a for gem a", + e.message + + e = assert_raises ArgumentError do + gda.send :pin_gem_source, 'a', :git, 'git://example/repo.git' + end + + assert_equal "duplicate source git: git://example/repo.git for gem a", + e.message + end + def test_platform_mswin win_platform, Gem.win_platform = Gem.win_platform?, false diff --git a/test/rubygems/test_gem_request_set_lockfile.rb b/test/rubygems/test_gem_request_set_lockfile.rb index 18c44144fa..6c933d4ba1 100644 --- a/test/rubygems/test_gem_request_set_lockfile.rb +++ b/test/rubygems/test_gem_request_set_lockfile.rb @@ -13,8 +13,10 @@ class TestGemRequestSetLockfile < Gem::TestCase @set = Gem::RequestSet.new + @git_set = Gem::Resolver::GitSet.new @vendor_set = Gem::Resolver::VendorSet.new + @set.instance_variable_set :@git_set, @git_set @set.instance_variable_set :@vendor_set, @vendor_set @gem_deps_file = 'gem.deps.rb' @@ -49,15 +51,22 @@ class TestGemRequestSetLockfile < Gem::TestCase @lockfile.get :text end - expected = 'unexpected token [:section, "x"], expected :text (at 5:1)' + expected = + 'unexpected token [:section, "x"], expected :text (at line 1 column 5)' assert_equal expected, e.message - assert_equal 5, e.line - assert_equal 1, e.column + assert_equal 1, e.line + assert_equal 5, e.column assert_equal File.expand_path("#{@gem_deps_file}.lock"), e.path end + def test_get_type_multiple + @lockfile.instance_variable_set :@tokens, [[:section, 'x', 5, 1]] + + assert @lockfile.get [:text, :section] + end + def test_get_type_value_mismatch @lockfile.instance_variable_set :@tokens, [[:section, 'x', 5, 1]] @@ -66,17 +75,17 @@ class TestGemRequestSetLockfile < Gem::TestCase end expected = - 'unexpected token [:section, "x"], expected [:section, "y"] (at 5:1)' + 'unexpected token [:section, "x"], expected [:section, "y"] (at line 1 column 5)' assert_equal expected, e.message - assert_equal 5, e.line - assert_equal 1, e.column + assert_equal 1, e.line + assert_equal 5, e.column assert_equal File.expand_path("#{@gem_deps_file}.lock"), e.path end def test_parse - write_lockfile <<-LOCKFILE + write_lockfile <<-LOCKFILE.strip GEM remote: #{@gem_repo} specs: @@ -104,6 +113,113 @@ DEPENDENCIES assert_equal %w[a-2], lockfile_set.specs.map { |tuple| tuple.full_name } end + def test_parse_dependencies + write_lockfile <<-LOCKFILE +GEM + remote: #{@gem_repo} + specs: + a (2) + +PLATFORMS + #{Gem::Platform::RUBY} + +DEPENDENCIES + a (>= 1, <= 2) + LOCKFILE + + @lockfile.parse + + assert_equal [dep('a', '>= 1', '<= 2')], @set.dependencies + + assert_equal [Gem::Platform::RUBY], @lockfile.platforms + + lockfile_set = @set.sets.find do |set| + Gem::Resolver::LockSet === set + end + + assert lockfile_set, 'could not find a LockSet' + + assert_equal %w[a-2], lockfile_set.specs.map { |tuple| tuple.full_name } + end + + def test_parse_GIT + write_lockfile <<-LOCKFILE +GIT + remote: git://example/a.git + revision: master + specs: + a (2) + b (>= 3) + +DEPENDENCIES + a! + LOCKFILE + + @lockfile.parse + + assert_equal [dep('a', '= 2')], @set.dependencies + + lockfile_set = @set.sets.find do |set| + Gem::Resolver::LockSet === set + end + + refute lockfile_set, 'fount a LockSet' + + git_set = @set.sets.find do |set| + Gem::Resolver::GitSet === set + end + + assert git_set, 'could not find a GitSet' + + assert_equal %w[a-2], git_set.specs.values.map { |s| s.full_name } + + assert_equal [dep('b', '>= 3')], git_set.specs.values.first.dependencies + end + + def test_parse_gem_specs_dependency + write_lockfile <<-LOCKFILE +GEM + remote: #{@gem_repo} + specs: + a (2) + b (= 3) + c (~> 4) + d + e (~> 5.0, >= 5.0.1) + +PLATFORMS + #{Gem::Platform::RUBY} + +DEPENDENCIES + a + LOCKFILE + + @lockfile.parse + + assert_equal [dep('a')], @set.dependencies + + assert_equal [Gem::Platform::RUBY], @lockfile.platforms + + lockfile_set = @set.sets.find do |set| + Gem::Resolver::LockSet === set + end + + assert lockfile_set, 'could not find a LockSet' + + assert_equal %w[a-2], lockfile_set.specs.map { |tuple| tuple.full_name } + + spec = lockfile_set.specs.first + + expected = [ + dep('b', '= 3'), + dep('c', '~> 4'), + dep('d'), + dep('e', '~> 5.0', '>= 5.0.1'), + ] + + assert_equal expected, spec.dependencies + end + def test_parse_missing @lockfile.parse @@ -120,6 +236,8 @@ DEPENDENCIES assert_equal :token, @lockfile.peek assert_equal :token, @lockfile.get + + assert_equal :EOF, @lockfile.peek end def test_skip @@ -147,6 +265,13 @@ GEM remote: #{@gem_repo} specs: a (2) + b (= 2) + c (!= 3) + d (> 4) + e (< 5) + f (>= 6) + g (<= 7) + h (~> 8) PLATFORMS #{Gem::Platform::RUBY} @@ -155,6 +280,106 @@ DEPENDENCIES a LOCKFILE + expected = [ + [:section, 'GEM', 0, 0], + [:newline, nil, 3, 0], + + [:entry, 'remote', 2, 1], + [:text, @gem_repo, 10, 1], + [:newline, nil, 34, 1], + + [:entry, 'specs', 2, 2], + [:newline, nil, 8, 2], + + [:text, 'a', 4, 3], + [:l_paren, nil, 6, 3], + [:text, '2', 7, 3], + [:r_paren, nil, 8, 3], + [:newline, nil, 9, 3], + + [:text, 'b', 6, 4], + [:l_paren, nil, 8, 4], + [:requirement, '=', 9, 4], + [:text, '2', 11, 4], + [:r_paren, nil, 12, 4], + [:newline, nil, 13, 4], + + [:text, 'c', 6, 5], + [:l_paren, nil, 8, 5], + [:requirement, '!=', 9, 5], + [:text, '3', 12, 5], + [:r_paren, nil, 13, 5], + [:newline, nil, 14, 5], + + [:text, 'd', 6, 6], + [:l_paren, nil, 8, 6], + [:requirement, '>', 9, 6], + [:text, '4', 11, 6], + [:r_paren, nil, 12, 6], + [:newline, nil, 13, 6], + + [:text, 'e', 6, 7], + [:l_paren, nil, 8, 7], + [:requirement, '<', 9, 7], + [:text, '5', 11, 7], + [:r_paren, nil, 12, 7], + [:newline, nil, 13, 7], + + [:text, 'f', 6, 8], + [:l_paren, nil, 8, 8], + [:requirement, '>=', 9, 8], + [:text, '6', 12, 8], + [:r_paren, nil, 13, 8], + [:newline, nil, 14, 8], + + [:text, 'g', 6, 9], + [:l_paren, nil, 8, 9], + [:requirement, '<=', 9, 9], + [:text, '7', 12, 9], + [:r_paren, nil, 13, 9], + [:newline, nil, 14, 9], + + [:text, 'h', 6, 10], + [:l_paren, nil, 8, 10], + [:requirement, '~>', 9, 10], + [:text, '8', 12, 10], + [:r_paren, nil, 13, 10], + [:newline, nil, 14, 10], + + [:newline, nil, 0, 11], + + [:section, 'PLATFORMS', 0, 12], + [:newline, nil, 9, 12], + + [:text, Gem::Platform::RUBY, 2, 13], + [:newline, nil, 6, 13], + + [:newline, nil, 0, 14], + + [:section, 'DEPENDENCIES', 0, 15], + [:newline, nil, 12, 15], + + [:text, 'a', 2, 16], + [:newline, nil, 3, 16], + ] + + assert_equal expected, @lockfile.tokenize + end + + def test_tokenize_capitals + write_lockfile <<-LOCKFILE +GEM + remote: #{@gem_repo} + specs: + Ab (2) + +PLATFORMS + #{Gem::Platform::RUBY} + +DEPENDENCIES + Ab + LOCKFILE + expected = [ [:section, 'GEM', 0, 0], [:newline, nil, 3, 0], @@ -163,11 +388,11 @@ DEPENDENCIES [:newline, nil, 34, 1], [:entry, 'specs', 2, 2], [:newline, nil, 8, 2], - [:text, 'a', 4, 3], - [:l_paren, nil, 6, 3], - [:text, '2', 7, 3], - [:r_paren, nil, 8, 3], - [:newline, nil, 9, 3], + [:text, 'Ab', 4, 3], + [:l_paren, nil, 7, 3], + [:text, '2', 8, 3], + [:r_paren, nil, 9, 3], + [:newline, nil, 10, 3], [:newline, nil, 0, 4], [:section, 'PLATFORMS', 0, 5], [:newline, nil, 9, 5], @@ -176,8 +401,8 @@ DEPENDENCIES [:newline, nil, 0, 7], [:section, 'DEPENDENCIES', 0, 8], [:newline, nil, 12, 8], - [:text, 'a', 2, 9], - [:newline, nil, 3, 9], + [:text, 'Ab', 2, 9], + [:newline, nil, 4, 9], ] assert_equal expected, @lockfile.tokenize @@ -190,7 +415,7 @@ DEPENDENCIES @lockfile.tokenize end - assert_equal "your #{@lock_file} contains merge conflict markers (at 0:0)", + assert_equal "your #{@lock_file} contains merge conflict markers (at line 0 column 0)", e.message write_lockfile '|||||||' @@ -199,7 +424,7 @@ DEPENDENCIES @lockfile.tokenize end - assert_equal "your #{@lock_file} contains merge conflict markers (at 0:0)", + assert_equal "your #{@lock_file} contains merge conflict markers (at line 0 column 0)", e.message write_lockfile '=======' @@ -208,7 +433,7 @@ DEPENDENCIES @lockfile.tokenize end - assert_equal "your #{@lock_file} contains merge conflict markers (at 0:0)", + assert_equal "your #{@lock_file} contains merge conflict markers (at line 0 column 0)", e.message write_lockfile '>>>>>>>' @@ -217,16 +442,74 @@ DEPENDENCIES @lockfile.tokenize end - assert_equal "your #{@lock_file} contains merge conflict markers (at 0:0)", + assert_equal "your #{@lock_file} contains merge conflict markers (at line 0 column 0)", e.message end + def test_tokenize_git + write_lockfile <<-LOCKFILE +DEPENDENCIES + a! + LOCKFILE + + expected = [ + [:section, 'DEPENDENCIES', 0, 0], + [:newline, nil, 12, 0], + + [:text, 'a', 2, 1], + [:bang, nil, 3, 1], + [:newline, nil, 4, 1], + ] + + assert_equal expected, @lockfile.tokenize + end + def test_tokenize_missing tokens = @lockfile.tokenize assert_empty tokens end + def test_tokenize_multiple + write_lockfile <<-LOCKFILE +GEM + remote: #{@gem_repo} + specs: + a (2) + b (~> 3.0, >= 3.0.1) + LOCKFILE + + expected = [ + [:section, 'GEM', 0, 0], + [:newline, nil, 3, 0], + + [:entry, 'remote', 2, 1], + [:text, @gem_repo, 10, 1], + [:newline, nil, 34, 1], + + [:entry, 'specs', 2, 2], + [:newline, nil, 8, 2], + + [:text, 'a', 4, 3], + [:l_paren, nil, 6, 3], + [:text, '2', 7, 3], + [:r_paren, nil, 8, 3], + [:newline, nil, 9, 3], + + [:text, 'b', 6, 4], + [:l_paren, nil, 8, 4], + [:requirement, '~>', 9, 4], + [:text, '3.0', 12, 4], + [:comma, nil, 15, 4], + [:requirement, '>=', 17, 4], + [:text, '3.0.1', 20, 4], + [:r_paren, nil, 25, 4], + [:newline, nil, 26, 4], + ] + + assert_equal expected, @lockfile.tokenize + end + def test_to_s_gem spec_fetcher do |fetcher| fetcher.spec 'a', 2 @@ -274,6 +557,8 @@ PLATFORMS DEPENDENCIES a + b + c LOCKFILE assert_equal expected, @lockfile.to_s @@ -328,6 +613,7 @@ PLATFORMS DEPENDENCIES a (>= 1) + b LOCKFILE assert_equal expected, @lockfile.to_s @@ -346,8 +632,6 @@ PATH specs: #{name} (#{version}) -GEM - PLATFORMS #{Gem::Platform::RUBY} @@ -371,8 +655,6 @@ PATH specs: #{name} (#{version}) -GEM - PLATFORMS #{Gem::Platform::RUBY} @@ -408,6 +690,111 @@ DEPENDENCIES assert_equal expected, @lockfile.to_s end + def test_to_s_gem_source + spec_fetcher do |fetcher| + fetcher.spec 'a', 2 + fetcher.clear + end + + spec_fetcher 'http://other.example/' do |fetcher| + fetcher.spec 'b', 2 + fetcher.clear + end + + Gem.sources << 'http://other.example/' + + @set.gem 'a' + @set.gem 'b' + + expected = <<-LOCKFILE +GEM + remote: #{@gem_repo} + specs: + a (2) + +GEM + remote: http://other.example/ + specs: + b (2) + +PLATFORMS + #{Gem::Platform::RUBY} + +DEPENDENCIES + a + b + LOCKFILE + + assert_equal expected, @lockfile.to_s + end + + def test_to_s_git + _, _, repository, = git_gem + + head = nil + + Dir.chdir repository do + FileUtils.mkdir 'b' + + Dir.chdir 'b' do + b = Gem::Specification.new 'b', 1 do |s| + s.add_dependency 'a', '~> 1.0' + s.add_dependency 'c', '~> 1.0' + end + + open 'b.gemspec', 'w' do |io| + io.write b.to_ruby + end + + system @git, 'add', 'b.gemspec' + system @git, 'commit', '--quiet', '-m', 'add b/b.gemspec' + end + + FileUtils.mkdir 'c' + + Dir.chdir 'c' do + c = Gem::Specification.new 'c', 1 + + open 'c.gemspec', 'w' do |io| + io.write c.to_ruby + end + + system @git, 'add', 'c.gemspec' + system @git, 'commit', '--quiet', '-m', 'add c/c.gemspec' + end + + head = `#{@git} rev-parse HEAD`.strip + end + + @git_set.add_git_gem 'a', repository, 'HEAD', true + @git_set.add_git_gem 'b', repository, 'HEAD', true + @git_set.add_git_gem 'c', repository, 'HEAD', true + + @set.gem 'b' + + expected = <<-LOCKFILE +GIT + remote: #{repository} + revision: #{head} + specs: + a (1) + b (1) + a (~> 1.0) + c (~> 1.0) + c (1) + +PLATFORMS + ruby + +DEPENDENCIES + a! + b! + c! + LOCKFILE + + assert_equal expected, @lockfile.to_s + end + def test_unget @lockfile.instance_variable_set :@current_token, :token diff --git a/test/rubygems/test_gem_requirement.rb b/test/rubygems/test_gem_requirement.rb index 29a4675bc9..8adaf898bc 100644 --- a/test/rubygems/test_gem_requirement.rb +++ b/test/rubygems/test_gem_requirement.rb @@ -47,6 +47,13 @@ class TestGemRequirement < Gem::TestCase def test_for_lockfile assert_equal ' (~> 1.0)', req('~> 1.0').for_lockfile + assert_equal ' (~> 1.0, >= 1.0.1)', req('>= 1.0.1', '~> 1.0').for_lockfile + + duped = req '= 1.0' + duped.requirements << ['=', v('1.0')] + + assert_equal ' (= 1.0)', duped.for_lockfile + assert_nil Gem::Requirement.default.for_lockfile end diff --git a/test/rubygems/test_gem_resolver.rb b/test/rubygems/test_gem_resolver.rb index 7383114af4..97ac64b85e 100644 --- a/test/rubygems/test_gem_resolver.rb +++ b/test/rubygems/test_gem_resolver.rb @@ -228,14 +228,27 @@ class TestGemResolver < Gem::TestCase res = Gem::Resolver.new([ad, bd], s) assert_resolves_to [a1, b1, c1, d4], res + end - cons = res.conflicts + def test_backoff_higher_version_to_satisfy_dep + t3 = util_spec "railties", "3.2" + t4 = util_spec "railties", "4.0" - assert_equal 1, cons.size - con = cons.first + r3 = util_spec "rails", "3.2", "railties" => "= 3.2" + r4 = util_spec "rails", "4.0", "railties" => "= 4.0" - assert_equal "c (= 1)", con.dependency.to_s - assert_equal "c-2", con.activated.full_name + rd = make_dep "rails", "3.2" + + c3 = util_spec "coffee", "3.0", "railties" => "~> 3.0" + c4 = util_spec "coffee", "4.0", "railties" => "~> 4.0" + + cd = make_dep "coffee" + + s = set(t3, t4, r3, r4, c3, c4) + + res = Gem::Resolver.new([rd, cd], s) + + assert_resolves_to [r3, t3, c3], res end def test_raises_dependency_error diff --git a/test/rubygems/test_gem_resolver_git_set.rb b/test/rubygems/test_gem_resolver_git_set.rb index b1a8d838bb..4643624ee0 100644 --- a/test/rubygems/test_gem_resolver_git_set.rb +++ b/test/rubygems/test_gem_resolver_git_set.rb @@ -36,13 +36,31 @@ class TestGemResolverGitSet < Gem::TestCase assert @set.need_submodules[repository] end + def test_add_git_spec + name, version, repository, revision = git_gem + + @set.add_git_spec name, version, repository, revision, true + + dependency = dep 'a' + + specs = @set.find_all dependency + + spec = specs.first + + assert_equal "#{name}-#{version}", spec.full_name + + assert @set.need_submodules[repository] + + refute_path_exists spec.source.repo_cache_dir + end + def test_find_all name, _, repository, = git_gem @set.add_git_gem name, repository, 'master', false dependency = dep 'a', '~> 1.0' - req = Gem::Resolver::ActivationRequest.new dependency, nil + req = Gem::Resolver::DependencyRequest.new dependency, nil @reqs.add req @set.prefetch @reqs @@ -66,7 +84,7 @@ class TestGemResolverGitSet < Gem::TestCase @set.add_git_gem name, repository, 'master', false dependency = dep name - req = Gem::Resolver::ActivationRequest.new dependency, nil + req = Gem::Resolver::DependencyRequest.new dependency, nil @reqs.add req @set.prefetch @reqs @@ -80,7 +98,7 @@ class TestGemResolverGitSet < Gem::TestCase @set.add_git_gem name, repository, 'master', false dependency = dep name - req = Gem::Resolver::ActivationRequest.new dependency, nil + req = Gem::Resolver::DependencyRequest.new dependency, nil @reqs.add req @set.prefetch @reqs @@ -98,7 +116,7 @@ class TestGemResolverGitSet < Gem::TestCase @set.add_git_gem name, repository, 'master', false dependency = dep 'b' - req = Gem::Resolver::ActivationRequest.new dependency, nil + req = Gem::Resolver::DependencyRequest.new dependency, nil @reqs.add req @set.prefetch @reqs @@ -112,7 +130,7 @@ class TestGemResolverGitSet < Gem::TestCase @set.add_git_gem name, repository, 'master', false dependency = dep name - req = Gem::Resolver::ActivationRequest.new dependency, nil + req = Gem::Resolver::DependencyRequest.new dependency, nil @reqs.add req @set.root_dir = "#{@gemhome}2" diff --git a/test/rubygems/test_gem_resolver_git_specification.rb b/test/rubygems/test_gem_resolver_git_specification.rb index b0163bc782..b13e4a83fd 100644 --- a/test/rubygems/test_gem_resolver_git_specification.rb +++ b/test/rubygems/test_gem_resolver_git_specification.rb @@ -80,5 +80,21 @@ class TestGemResolverGitSpecification < Gem::TestCase assert_path_exists File.join git_spec.spec.extension_install_dir, 'b.rb' end + def test_install_installed + git_gem 'a', 1 + + git_spec = Gem::Resolver::GitSpecification.new @set, @spec + + git_spec.install({}) + + called = false + + git_spec.install({}) do |installer| + called = installer + end + + assert called + end + end diff --git a/test/rubygems/test_gem_resolver_lock_set.rb b/test/rubygems/test_gem_resolver_lock_set.rb index 6d904fbaee..51ddad42f0 100644 --- a/test/rubygems/test_gem_resolver_lock_set.rb +++ b/test/rubygems/test_gem_resolver_lock_set.rb @@ -12,11 +12,11 @@ class TestGemResolverLockSet < Gem::TestCase end def test_add - @set.add 'a', '2', Gem::Platform::RUBY + spec = @set.add 'a', '2', Gem::Platform::RUBY assert_equal %w[a-2], @set.specs.map { |t| t.full_name } - spec = @set.specs.first + assert_kind_of Gem::Resolver::LockSpecification, spec assert_equal @set, spec.set assert_equal 'a', spec.name diff --git a/test/rubygems/test_gem_resolver_lock_specification.rb b/test/rubygems/test_gem_resolver_lock_specification.rb new file mode 100644 index 0000000000..5741950fe0 --- /dev/null +++ b/test/rubygems/test_gem_resolver_lock_specification.rb @@ -0,0 +1,87 @@ +require 'rubygems/test_case' +require 'rubygems/resolver' + +class TestGemResolverLockSpecification < Gem::TestCase + + def setup + super + + @LS = Gem::Resolver::LockSpecification + + @source = Gem::Source.new @gem_repo + @set = Gem::Resolver::LockSet.new @source + end + + def test_initialize + spec = @LS.new @set, 'a', v(2), @source, Gem::Platform::RUBY + + assert_equal 'a', spec.name + assert_equal v(2), spec.version + assert_equal Gem::Platform::RUBY, spec.platform + + assert_equal @source, spec.source + end + + def test_add_dependency + l_spec = @LS.new @set, 'a', v(2), @source, Gem::Platform::RUBY + + b_dep = dep('b', '>= 0') + + l_spec.add_dependency b_dep + + assert_equal [b_dep], l_spec.dependencies + end + + def test_install + spec_fetcher do |fetcher| + fetcher.gem 'a', 2 + fetcher.clear + end + + spec = @LS.new @set, 'a', v(2), @source, Gem::Platform::RUBY + + called = false + + spec.install({}) do |installer| + called = installer + end + + refute_nil called + end + + def test_install_installed + spec = @LS.new @set, 'a', v(2), @source, Gem::Platform::RUBY + + FileUtils.touch File.join(@gemhome, 'specifications', spec.spec.spec_name) + + called = false + + spec.install({}) do |installer| + called = installer + end + + assert_nil called + end + + def test_spec + version = v(2) + + l_spec = @LS.new @set, 'a', version, @source, Gem::Platform::RUBY + + b_dep = dep 'b', '>= 0' + c_dep = dep 'c', '~> 1' + + l_spec.add_dependency b_dep + l_spec.add_dependency c_dep + + spec = l_spec.spec + + assert_equal 'a', spec.name + assert_equal version, spec.version + assert_equal Gem::Platform::RUBY, spec.platform + + assert_equal [b_dep, c_dep], l_spec.spec.dependencies + end + +end + diff --git a/test/rubygems/test_gem_resolver_requirement_list.rb b/test/rubygems/test_gem_resolver_requirement_list.rb index 3d09ce5f92..fd9dccb70f 100644 --- a/test/rubygems/test_gem_resolver_requirement_list.rb +++ b/test/rubygems/test_gem_resolver_requirement_list.rb @@ -9,10 +9,11 @@ class TestGemResolverRequirementList < Gem::TestCase end def test_each - @list.add 1 - @list.add 2 + dep = Gem::Dependency.new "a", "= 1" + req = Gem::Resolver::DependencyRequest.new(dep, nil) + @list.add req - assert_equal [1, 2], @list.each.to_a + assert_equal [req], @list.each.to_a end end diff --git a/test/rubygems/test_gem_source_lock.rb b/test/rubygems/test_gem_source_lock.rb index d114dccbb7..c7c4b8ca3f 100644 --- a/test/rubygems/test_gem_source_lock.rb +++ b/test/rubygems/test_gem_source_lock.rb @@ -103,5 +103,12 @@ class TestGemSourceLock < Gem::TestCase assert_equal(-1, vendor.<=>(lock), 'vendor <=> lock') end + def test_uri + remote = Gem::Source.new @gem_repo + lock = Gem::Source::Lock.new remote + + assert_equal URI(@gem_repo), lock.uri + end + end