aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-12-08 01:22:39 +0000
committerdrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-12-08 01:22:39 +0000
commit7ed9b794b4e3f3f9874f2ce19401461596d8a2c0 (patch)
tree5caaf13685de34b09d2949709a77b4c650b62741
parent866b438c21ff05dfeabba8bc9aa9850e415be607 (diff)
downloadruby-7ed9b794b4e3f3f9874f2ce19401461596d8a2c0.tar.gz
* 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
-rw-r--r--ChangeLog7
-rw-r--r--lib/rubygems/request_set.rb10
-rw-r--r--lib/rubygems/request_set/gem_dependency_api.rb42
-rw-r--r--lib/rubygems/request_set/lockfile.rb220
-rw-r--r--lib/rubygems/requirement.rb18
-rw-r--r--lib/rubygems/resolver.rb40
-rw-r--r--lib/rubygems/resolver/git_set.rb26
-rw-r--r--lib/rubygems/resolver/lock_set.rb6
-rw-r--r--lib/rubygems/resolver/lock_specification.rb58
-rw-r--r--lib/rubygems/resolver/requirement_list.rb25
-rw-r--r--lib/rubygems/resolver/stats.rb44
-rw-r--r--lib/rubygems/source.rb3
-rw-r--r--lib/rubygems/source/git.rb10
-rw-r--r--lib/rubygems/source/lock.rb4
-rw-r--r--lib/rubygems/test_case.rb8
-rw-r--r--lib/rubygems/test_utilities.rb27
-rw-r--r--test/rubygems/test_gem_request_set.rb10
-rw-r--r--test/rubygems/test_gem_request_set_gem_dependency_api.rb21
-rw-r--r--test/rubygems/test_gem_request_set_lockfile.rb429
-rw-r--r--test/rubygems/test_gem_requirement.rb7
-rw-r--r--test/rubygems/test_gem_resolver.rb23
-rw-r--r--test/rubygems/test_gem_resolver_git_set.rb28
-rw-r--r--test/rubygems/test_gem_resolver_git_specification.rb16
-rw-r--r--test/rubygems/test_gem_resolver_lock_set.rb4
-rw-r--r--test/rubygems/test_gem_resolver_lock_specification.rb87
-rw-r--r--test/rubygems/test_gem_resolver_requirement_list.rb7
-rw-r--r--test/rubygems/test_gem_source_lock.rb7
27 files changed, 1081 insertions, 106 deletions
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 <drbrain@segment7.net>
+
+ * 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 <charliesome@ruby-lang.org>
* 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
@@ -430,6 +416,28 @@ class Gem::RequestSet::GemDependencyAPI
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
#
# Block form for restricting gems to a particular platform.
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
- end
-
- source_dep = '!' if source
+ @requests.sort_by { |r| r.name }.each do |request|
+ spec = request.spec
- requirement = dependency.requirement
+ if [Gem::Resolver::VendorSpecification,
+ Gem::Resolver::GitSpecification].include? spec.class then
+ out << " #{request.name}!"
+ else
+ requirement = request.request.dependency.requirement
- out << " #{dependency.name}#{source_dep}#{requirement.for_lockfile}"
+ out << " #{request.name}#{requirement.for_lockfile}"
+ end
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
@@ -258,10 +377,32 @@ class Gem::RequestSet::Lockfile
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
@@ -46,6 +46,32 @@ class Gem::Resolver::GitSet < Gem::Resolver::Set
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+
def find_all 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,21 +75,117 @@ 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.strip
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+
+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 }
+ 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}
@@ -102,6 +207,17 @@ DEPENDENCIES
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
@@ -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}
@@ -156,6 +281,106 @@ DEPENDENCIES
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],
[:entry, 'remote', 2, 1],
@@ -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
+
+ def test_backoff_higher_version_to_satisfy_dep
+ t3 = util_spec "railties", "3.2"
+ t4 = util_spec "railties", "4.0"
+
+ r3 = util_spec "rails", "3.2", "railties" => "= 3.2"
+ r4 = util_spec "rails", "4.0", "railties" => "= 4.0"
+
+ 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"
- cons = res.conflicts
+ s = set(t3, t4, r3, r4, c3, c4)
- assert_equal 1, cons.size
- con = cons.first
+ res = Gem::Resolver.new([rd, cd], s)
- assert_equal "c (= 1)", con.dependency.to_s
- assert_equal "c-2", con.activated.full_name
+ 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