summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog15
-rw-r--r--lib/rubygems.rb2
-rw-r--r--lib/rubygems/basic_specification.rb8
-rw-r--r--lib/rubygems/commands/install_command.rb19
-rw-r--r--lib/rubygems/commands/update_command.rb2
-rw-r--r--lib/rubygems/dependency_installer.rb8
-rw-r--r--lib/rubygems/dependency_resolver.rb135
-rw-r--r--lib/rubygems/dependency_resolver/activation_request.rb49
-rw-r--r--lib/rubygems/dependency_resolver/api_set.rb18
-rw-r--r--lib/rubygems/dependency_resolver/api_specification.rb27
-rw-r--r--lib/rubygems/dependency_resolver/best_set.rb21
-rw-r--r--lib/rubygems/dependency_resolver/composed_set.rb4
-rw-r--r--lib/rubygems/dependency_resolver/current_set.rb5
-rw-r--r--lib/rubygems/dependency_resolver/dependency_conflict.rb11
-rw-r--r--lib/rubygems/dependency_resolver/dependency_request.rb20
-rw-r--r--lib/rubygems/dependency_resolver/index_set.rb34
-rw-r--r--lib/rubygems/dependency_resolver/index_specification.rb38
-rw-r--r--lib/rubygems/dependency_resolver/installed_specification.rb40
-rw-r--r--lib/rubygems/dependency_resolver/installer_set.rb22
-rw-r--r--lib/rubygems/dependency_resolver/lock_set.rb60
-rw-r--r--lib/rubygems/dependency_resolver/set.rb28
-rw-r--r--lib/rubygems/dependency_resolver/spec_specification.rb58
-rw-r--r--lib/rubygems/dependency_resolver/specification.rb60
-rw-r--r--lib/rubygems/dependency_resolver/vendor_set.rb23
-rw-r--r--lib/rubygems/dependency_resolver/vendor_specification.rb42
-rw-r--r--lib/rubygems/errors.rb2
-rw-r--r--lib/rubygems/exceptions.rb17
-rw-r--r--lib/rubygems/platform.rb8
-rw-r--r--lib/rubygems/remote_fetcher.rb1
-rw-r--r--lib/rubygems/request_set.rb65
-rw-r--r--lib/rubygems/request_set/gem_dependency_api.rb279
-rw-r--r--lib/rubygems/request_set/lockfile.rb347
-rw-r--r--lib/rubygems/requirement.rb50
-rw-r--r--lib/rubygems/server.rb18
-rw-r--r--lib/rubygems/source.rb18
-rw-r--r--lib/rubygems/source/vendor.rb5
-rw-r--r--lib/rubygems/source_list.rb71
-rw-r--r--lib/rubygems/spec_fetcher.rb18
-rw-r--r--lib/rubygems/specification.rb94
-rw-r--r--lib/rubygems/ssl_certs/DigiCertHighAssuranceEVRootCA.pem23
-rw-r--r--lib/rubygems/test_case.rb95
-rw-r--r--lib/rubygems/version.rb2
-rw-r--r--test/rubygems/test_bundled_ca.rb2
-rw-r--r--test/rubygems/test_gem.rb33
-rw-r--r--test/rubygems/test_gem_commands_dependency_command.rb11
-rw-r--r--test/rubygems/test_gem_commands_install_command.rb10
-rw-r--r--test/rubygems/test_gem_commands_outdated_command.rb11
-rw-r--r--test/rubygems/test_gem_commands_specification_command.rb53
-rw-r--r--test/rubygems/test_gem_dependency_installer.rb36
-rw-r--r--test/rubygems/test_gem_dependency_resolution_error.rb28
-rw-r--r--test/rubygems/test_gem_dependency_resolver.rb178
-rw-r--r--test/rubygems/test_gem_dependency_resolver_activation_request.rb63
-rw-r--r--test/rubygems/test_gem_dependency_resolver_api_set.rb69
-rw-r--r--test/rubygems/test_gem_dependency_resolver_best_set.rb31
-rw-r--r--test/rubygems/test_gem_dependency_resolver_dependency_conflict.rb20
-rw-r--r--test/rubygems/test_gem_dependency_resolver_dependency_request.rb20
-rw-r--r--test/rubygems/test_gem_dependency_resolver_index_set.rb48
-rw-r--r--test/rubygems/test_gem_dependency_resolver_index_specification.rb13
-rw-r--r--test/rubygems/test_gem_dependency_resolver_installer_set.rb13
-rw-r--r--test/rubygems/test_gem_dependency_resolver_lock_set.rb57
-rw-r--r--test/rubygems/test_gem_dependency_resolver_vendor_set.rb10
-rw-r--r--test/rubygems/test_gem_dependency_resolver_vendor_specification.rb6
-rw-r--r--test/rubygems/test_gem_remote_fetcher.rb90
-rw-r--r--test/rubygems/test_gem_request_set.rb73
-rw-r--r--test/rubygems/test_gem_request_set_gem_dependency_api.rb332
-rw-r--r--test/rubygems/test_gem_request_set_lockfile.rb404
-rw-r--r--test/rubygems/test_gem_requirement.rb14
-rw-r--r--test/rubygems/test_gem_server.rb106
-rw-r--r--test/rubygems/test_gem_source.rb14
-rw-r--r--test/rubygems/test_gem_source_list.rb24
-rw-r--r--test/rubygems/test_gem_source_local.rb2
-rw-r--r--test/rubygems/test_gem_source_vendor.rb13
-rw-r--r--test/rubygems/test_gem_spec_fetcher.rb17
-rw-r--r--test/rubygems/test_gem_specification.rb86
-rw-r--r--test/rubygems/test_gem_stub_specification.rb2
75 files changed, 3139 insertions, 612 deletions
diff --git a/ChangeLog b/ChangeLog
index 2ba685e1c9..9e5677ff0e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+Mon Nov 11 02:51:17 2013 Eric Hodel <drbrain@segment7.net>
+
+ * lib/rubygems: Update to RubyGems master 4bdc4f2. Important changes
+ in this commit:
+
+ RubyGems now chooses the test server port reliably. Patch by akr.
+
+ Partial implementation of bundler's Gemfile format.
+
+ Refactorings to improve the new resolver.
+
+ Fixes bugs in the resolver.
+
+ * test/rubygems: Tests for the above.
+
Mon Nov 11 01:02:06 2013 Zachary Scott <e@zzak.io>
* lib/timeout.rb: [DOC] Add note about change from #8730 [Fixes GH-440]
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 1c84356bd7..1c8acda74d 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -8,7 +8,7 @@
require 'rbconfig'
module Gem
- VERSION = '2.2.0'
+ VERSION = '2.2.0.preview.2'
end
# Must be first since it unloads the prelude from 1.9.2
diff --git a/lib/rubygems/basic_specification.rb b/lib/rubygems/basic_specification.rb
index 7d0469000b..a29ed0aa6d 100644
--- a/lib/rubygems/basic_specification.rb
+++ b/lib/rubygems/basic_specification.rb
@@ -196,5 +196,13 @@ class Gem::BasicSpecification
raise NotImplementedError
end
+ ##
+ # Whether this specification is stubbed - i.e. we have information
+ # about the gem from a stub line, without having to evaluate the
+ # entire gemspec file.
+ def stubbed?
+ raise NotImplementedError
+ end
+
end
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index 1f3210ff5d..ad90b37fdc 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -22,6 +22,7 @@ class Gem::Commands::InstallCommand < Gem::Command
defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
:format_executable => false,
:version => Gem::Requirement.default,
+ :without_groups => [],
})
super 'install', 'Install a gem into the local repository', defaults
@@ -42,6 +43,13 @@ class Gem::Commands::InstallCommand < Gem::Command
o[:gemdeps] = v
end
+ add_option(:"Install/Update", '--without GROUPS', Array,
+ 'Omit the named groups (comma separated)',
+ 'when installing from a gem dependencies',
+ 'file') do |v,o|
+ o[:without_groups].concat v.map { |without| without.intern }
+ end
+
add_option(:"Install/Update", '--default',
'Add the gem\'s full specification to',
'specifications/default and extract only its bin') do |v,o|
@@ -133,8 +141,8 @@ to write the specification by hand. For example:
end
def execute
- if gf = options[:gemdeps] then
- install_from_gemdeps gf
+ if options.include? :gemdeps then
+ install_from_gemdeps
return # not reached
end
@@ -154,14 +162,11 @@ to write the specification by hand. For example:
terminate_interaction exit_code
end
- def install_from_gemdeps gf # :nodoc:
+ def install_from_gemdeps # :nodoc:
require 'rubygems/request_set'
rs = Gem::RequestSet.new
- rs.load_gemdeps gf
-
- rs.resolve
- specs = rs.install options do |req, inst|
+ specs = rs.install_from_gemdeps options do |req, inst|
s = req.full_spec
if inst
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
index c3532841c3..b4ee59b3bb 100644
--- a/lib/rubygems/commands/update_command.rb
+++ b/lib/rubygems/commands/update_command.rb
@@ -112,7 +112,7 @@ command to remove old versions.
spec_tuples, errors = fetcher.search_for_dependency dependency
- error = errors.find { |errors| errors.respond_to? :exception }
+ error = errors.find { |e| e.respond_to? :exception }
raise error if error
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index c6985b27c0..22ff6f5cb7 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -250,6 +250,14 @@ class Gem::DependencyInstaller
if gem_name =~ /\.gem$/ and File.file? gem_name then
src = Gem::Source::SpecificFile.new(gem_name)
set.add src.spec, src
+ elsif gem_name =~ /\.gem$/ then
+ Dir[gem_name].each do |name|
+ begin
+ src = Gem::Source::SpecificFile.new name
+ set.add src.spec, src
+ rescue Gem::Package::FormatError
+ end
+ end
else
local = Gem::Source::Local.new
diff --git a/lib/rubygems/dependency_resolver.rb b/lib/rubygems/dependency_resolver.rb
index 10cecf7972..35fbe925ad 100644
--- a/lib/rubygems/dependency_resolver.rb
+++ b/lib/rubygems/dependency_resolver.rb
@@ -30,7 +30,16 @@ class Gem::DependencyResolver
attr_accessor :soft_missing
def self.compose_sets *sets
- Gem::DependencyResolver::ComposedSet.new(*sets)
+ sets.compact!
+
+ case sets.length
+ when 0 then
+ raise ArgumentError, 'one set in the composition must be non-nil'
+ when 1 then
+ sets.first
+ else
+ Gem::DependencyResolver::ComposedSet.new(*sets)
+ end
end
##
@@ -53,12 +62,27 @@ class Gem::DependencyResolver
@set = set || Gem::DependencyResolver::IndexSet.new
@needed = needed
- @conflicts = nil
+ @conflicts = []
@development = false
@missing = []
@soft_missing = false
end
+ ##
+ # Creates an ActivationRequest for the given +dep+ and the last +possible+
+ # specification.
+ #
+ # Returns the Specification and the ActivationRequest
+
+ def activation_request dep, possible # :nodoc:
+ spec = possible.pop
+
+ activation_request =
+ Gem::DependencyResolver::ActivationRequest.new spec, dep, possible
+
+ return spec, activation_request
+ end
+
def requests s, act, reqs=nil
s.dependencies.reverse_each do |d|
next if d.type == :development and not @development
@@ -95,27 +119,38 @@ class Gem::DependencyResolver
##
# Finds the State in +states+ that matches the +conflict+ so that we can try
# other possible sets.
+ #
+ # If no good candidate is found, the first state is tried.
def find_conflict_state conflict, states # :nodoc:
+ rejected = []
+
until states.empty? do
- if conflict.for_spec? states.last.spec
- state = states.last
+ state = states.pop
+
+ if conflict.for_spec? state.spec
state.conflicts << [state.spec, conflict]
return state
- else
- states.pop
end
+
+ rejected << state
end
- nil
+ return rejected.shift
+ ensure
+ rejected = rejected.concat states
+ states.replace rejected
end
##
- # Extracts the specifications that may be able to fulfill +dependency+
+ # Extracts the specifications that may be able to fulfill +dependency+ and
+ # returns those that match the local platform and all those that match.
def find_possible dependency # :nodoc:
- possible = @set.find_all dependency
- select_local_platforms possible
+ all = @set.find_all dependency
+ matching_platform = select_local_platforms all
+
+ return matching_platform, all
end
def handle_conflict(dep, existing)
@@ -134,7 +169,7 @@ class Gem::DependencyResolver
Gem::DependencyResolver::DependencyConflict.new depreq, existing, dep
end
- @conflicts << conflict
+ @conflicts << conflict unless @conflicts.include? conflict
return conflict
end
@@ -150,7 +185,29 @@ class Gem::DependencyResolver
# +conflicts+ is a [DependencyRequest, DependencyConflict] hit tried to
# activate the state.
#
- State = Struct.new(:needed, :specs, :dep, :spec, :possibles, :conflicts)
+ State = Struct.new(:needed, :specs, :dep, :spec, :possibles, :conflicts) do
+ def summary # :nodoc:
+ nd = needed.map { |s| s.to_s }.sort if nd
+
+ if specs then
+ ss = specs.map { |s| s.full_name }.sort
+ ss.unshift ss.length
+ end
+
+ d = dep.to_s
+ d << " from #{dep.requester.full_name}" if dep.requester
+
+ ps = possibles.map { |p| p.full_name }.sort
+ ps.unshift ps.length
+
+ cs = conflicts.map do |(s, c)|
+ [s.full_name, c.conflicting_dependencies.map { |cd| cd.to_s }]
+ end
+
+ { :needed => nd, :specs => ss, :dep => d, :spec => spec.full_name,
+ :possibles => ps, :conflicts => cs }
+ end
+ end
##
# The meat of the algorithm. Given +needed+ DependencyRequest objects and
@@ -178,20 +235,22 @@ class Gem::DependencyResolver
needed, specs = resolve_for_conflict needed, specs, state
+ states << state unless state.possibles.empty?
+
next
end
- possible = find_possible dep
+ matching, all = find_possible dep
- case possible.size
+ case matching.size
when 0
- resolve_for_zero dep
+ resolve_for_zero dep, all
when 1
needed, specs =
- resolve_for_single needed, specs, dep, possible
+ resolve_for_single needed, specs, dep, matching
else
needed, specs =
- resolve_for_multiple needed, specs, states, dep, possible
+ resolve_for_multiple needed, specs, states, dep, matching
end
end
@@ -208,10 +267,8 @@ class Gem::DependencyResolver
raise Gem::ImpossibleDependenciesError.new state.dep, state.conflicts if
state.possibles.empty?
- spec = state.possibles.pop
-
# Retry resolution with this spec and add it's dependencies
- act = Gem::DependencyResolver::ActivationRequest.new spec, state.dep
+ spec, act = activation_request state.dep, state.possibles
needed = requests spec, act, state.needed
specs = Gem::List.prepend state.specs, act
@@ -230,19 +287,11 @@ class Gem::DependencyResolver
[s.source, s.version, s.platform == Gem::Platform::RUBY ? -1 : 1]
end
- # To figure out which to pick, we keep resolving given each one being
- # activated and if there isn't a conflict, we know we've found a full set.
- #
- # We use an until loop rather than reverse_each to keep the stack short
- # since we're using a recursive algorithm.
- spec = possible.pop
+ spec, act = activation_request dep, possible
# We may need to try all of +possible+, so we setup state to unwind back
# to current +needed+ and +specs+ so we can try another. This is code is
# what makes conflict resolution possible.
-
- act = Gem::DependencyResolver::ActivationRequest.new spec, dep
-
states << State.new(needed, specs, dep, spec, possible, [])
needed = requests spec, act, needed
@@ -256,8 +305,7 @@ class Gem::DependencyResolver
# dependencies by adding them to +needed+.
def resolve_for_single needed, specs, dep, possible # :nodoc:
- spec = possible.first
- act = Gem::DependencyResolver::ActivationRequest.new spec, dep, false
+ spec, act = activation_request dep, possible
specs = Gem::List.prepend specs, act
@@ -274,11 +322,11 @@ class Gem::DependencyResolver
##
# When there are no possible specifications for +dep+ our work is done.
- def resolve_for_zero dep # :nodoc:
+ def resolve_for_zero dep, platform_mismatch # :nodoc:
@missing << dep
unless @soft_missing
- raise Gem::UnsatisfiableDependencyError, dep
+ raise Gem::UnsatisfiableDependencyError.new(dep, platform_mismatch)
end
end
@@ -287,23 +335,30 @@ class Gem::DependencyResolver
def select_local_platforms specs # :nodoc:
specs.select do |spec|
- Gem::Platform.match spec.platform
+ Gem::Platform.installable? spec
end
end
end
-require 'rubygems/dependency_resolver/api_set'
-require 'rubygems/dependency_resolver/api_specification'
require 'rubygems/dependency_resolver/activation_request'
-require 'rubygems/dependency_resolver/composed_set'
-require 'rubygems/dependency_resolver/current_set'
require 'rubygems/dependency_resolver/dependency_conflict'
require 'rubygems/dependency_resolver/dependency_request'
+
+require 'rubygems/dependency_resolver/set'
+require 'rubygems/dependency_resolver/api_set'
+require 'rubygems/dependency_resolver/composed_set'
+require 'rubygems/dependency_resolver/best_set'
+require 'rubygems/dependency_resolver/current_set'
require 'rubygems/dependency_resolver/index_set'
-require 'rubygems/dependency_resolver/index_specification'
-require 'rubygems/dependency_resolver/installed_specification'
require 'rubygems/dependency_resolver/installer_set'
+require 'rubygems/dependency_resolver/lock_set'
require 'rubygems/dependency_resolver/vendor_set'
+
+require 'rubygems/dependency_resolver/specification'
+require 'rubygems/dependency_resolver/spec_specification'
+require 'rubygems/dependency_resolver/api_specification'
+require 'rubygems/dependency_resolver/index_specification'
+require 'rubygems/dependency_resolver/installed_specification'
require 'rubygems/dependency_resolver/vendor_specification'
diff --git a/lib/rubygems/dependency_resolver/activation_request.rb b/lib/rubygems/dependency_resolver/activation_request.rb
index 25af6378ac..c5d1e24d85 100644
--- a/lib/rubygems/dependency_resolver/activation_request.rb
+++ b/lib/rubygems/dependency_resolver/activation_request.rb
@@ -47,11 +47,21 @@ class Gem::DependencyResolver::ActivationRequest
end
def inspect # :nodoc:
- others_possible = nil
- others_possible = ' (others possible)' if @others_possible
+ others =
+ case @others_possible
+ when true then # TODO remove at RubyGems 3
+ ' (others possible)'
+ when false then # TODO remove at RubyGems 3
+ nil
+ else
+ unless @others_possible.empty? then
+ others = @others_possible.map { |s| s.full_name }
+ " (others possible: #{others.join ', '})"
+ end
+ end
'#<%s for %p from %s%s>' % [
- self.class, @spec, @request, others_possible
+ self.class, @spec, @request, others
]
end
@@ -59,10 +69,15 @@ class Gem::DependencyResolver::ActivationRequest
# Indicates if the requested gem has already been installed.
def installed?
- this_spec = full_spec
+ case @spec
+ when Gem::DependencyResolver::VendorSpecification then
+ true
+ else
+ this_spec = full_spec
- Gem::Specification.any? do |s|
- s == this_spec
+ Gem::Specification.any? do |s|
+ s == this_spec
+ end
end
end
@@ -75,7 +90,12 @@ class Gem::DependencyResolver::ActivationRequest
# requests for the same Dependency request.
def others_possible?
- @others_possible
+ case @others_possible
+ when true, false then
+ @others_possible
+ else
+ not @others_possible.empty?
+ end
end
##
@@ -95,9 +115,18 @@ class Gem::DependencyResolver::ActivationRequest
q.text ' for '
q.pp @request
-
- q.breakable
- q.text ' (other possible)' if @others_possible
+ case @others_possible
+ when false then
+ when true then
+ q.breakable
+ q.text 'others possible'
+ else
+ unless @others_possible.empty? then
+ q.breakable
+ q.text 'others '
+ q.pp @others_possible.map { |s| s.full_name }
+ end
+ end
end
end
diff --git a/lib/rubygems/dependency_resolver/api_set.rb b/lib/rubygems/dependency_resolver/api_set.rb
index 469c005a09..9dd34562b1 100644
--- a/lib/rubygems/dependency_resolver/api_set.rb
+++ b/lib/rubygems/dependency_resolver/api_set.rb
@@ -2,11 +2,21 @@
# The global rubygems pool, available via the rubygems.org API.
# Returns instances of APISpecification.
-class Gem::DependencyResolver::APISet
+class Gem::DependencyResolver::APISet < Gem::DependencyResolver::Set
- def initialize
+ ##
+ # The URI for the dependency API this APISet uses.
+
+ attr_reader :dep_uri # :nodoc:
+
+ ##
+ # Creates a new APISet that will retrieve gems from +uri+ using the RubyGems
+ # API described at http://guides.rubygems.org/rubygems-org-api
+
+ def initialize uri = 'https://rubygems.org/api/v1/dependencies'
+ uri = URI uri unless URI === uri # for ruby 1.8
@data = Hash.new { |h,k| h[k] = [] }
- @dep_uri = URI 'https://rubygems.org/api/v1/dependencies'
+ @dep_uri = uri
end
##
@@ -46,7 +56,7 @@ class Gem::DependencyResolver::APISet
##
# Return data for all versions of the gem +name+.
- def versions name
+ def versions name # :nodoc:
if @data.key?(name)
return @data[name]
end
diff --git a/lib/rubygems/dependency_resolver/api_specification.rb b/lib/rubygems/dependency_resolver/api_specification.rb
index ae688780dd..5178d7c28e 100644
--- a/lib/rubygems/dependency_resolver/api_specification.rb
+++ b/lib/rubygems/dependency_resolver/api_specification.rb
@@ -1,18 +1,21 @@
##
-# Represents a specification retrieved via the rubygems.org
-# API. This is used to avoid having to load the full
-# Specification object when all we need is the name, version,
-# and dependencies.
+# Represents a specification retrieved via the rubygems.org API.
+#
+# This is used to avoid loading the full Specification object when all we need
+# is the name, version, and dependencies.
-class Gem::DependencyResolver::APISpecification
+class Gem::DependencyResolver::APISpecification < Gem::DependencyResolver::Specification
- attr_reader :dependencies
- attr_reader :name
- attr_reader :platform
- attr_reader :set # :nodoc:
- attr_reader :version
+ ##
+ # Creates an APISpecification for the given +set+ from the rubygems.org
+ # +api_data+.
+ #
+ # See http://guides.rubygems.org/rubygems-org-api/#misc_methods for the
+ # format of the +api_data+.
def initialize(set, api_data)
+ super()
+
@set = set
@name = api_data[:name]
@version = Gem::Version.new api_data[:number]
@@ -31,9 +34,5 @@ class Gem::DependencyResolver::APISpecification
@dependencies == other.dependencies
end
- def full_name
- "#{@name}-#{@version}"
- end
-
end
diff --git a/lib/rubygems/dependency_resolver/best_set.rb b/lib/rubygems/dependency_resolver/best_set.rb
new file mode 100644
index 0000000000..987eea552e
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/best_set.rb
@@ -0,0 +1,21 @@
+##
+# The BestSet chooses the best available method to query a remote index.
+#
+# It combines IndexSet and APISet
+
+class Gem::DependencyResolver::BestSet < Gem::DependencyResolver::ComposedSet
+
+ ##
+ # Creates a BestSet for the given +sources+ or Gem::sources if none are
+ # specified. +sources+ must be a Gem::SourceList.
+
+ def initialize sources = Gem.sources
+ super()
+
+ sources.each_source do |source|
+ @sets << source.dependency_resolver_set
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/dependency_resolver/composed_set.rb b/lib/rubygems/dependency_resolver/composed_set.rb
index fb38128bb0..aeecf047b8 100644
--- a/lib/rubygems/dependency_resolver/composed_set.rb
+++ b/lib/rubygems/dependency_resolver/composed_set.rb
@@ -1,4 +1,6 @@
-class Gem::DependencyResolver::ComposedSet
+class Gem::DependencyResolver::ComposedSet < Gem::DependencyResolver::Set
+
+ attr_reader :sets # :nodoc:
def initialize *sets
@sets = sets
diff --git a/lib/rubygems/dependency_resolver/current_set.rb b/lib/rubygems/dependency_resolver/current_set.rb
index 13bc490e9e..ef15c9d7f3 100644
--- a/lib/rubygems/dependency_resolver/current_set.rb
+++ b/lib/rubygems/dependency_resolver/current_set.rb
@@ -3,14 +3,11 @@
# all the normal settings that control where to look
# for installed gems.
-class Gem::DependencyResolver::CurrentSet
+class Gem::DependencyResolver::CurrentSet < Gem::DependencyResolver::Set
def find_all req
req.dependency.matching_specs
end
- def prefetch gems
- end
-
end
diff --git a/lib/rubygems/dependency_resolver/dependency_conflict.rb b/lib/rubygems/dependency_resolver/dependency_conflict.rb
index 1755d910c3..092f000cdb 100644
--- a/lib/rubygems/dependency_resolver/dependency_conflict.rb
+++ b/lib/rubygems/dependency_resolver/dependency_conflict.rb
@@ -8,12 +8,21 @@ class Gem::DependencyResolver::DependencyConflict
attr_reader :dependency
+ attr_reader :failed_dep # :nodoc:
+
def initialize(dependency, activated, failed_dep=dependency)
@dependency = dependency
@activated = activated
@failed_dep = failed_dep
end
+ def == other
+ self.class === other and
+ @dependency == other.dependency and
+ @activated == other.activated and
+ @failed_dep == other.failed_dep
+ end
+
##
# Return the 2 dependency objects that conflicted
@@ -71,6 +80,8 @@ class Gem::DependencyResolver::DependencyConflict
current = current.request.requester
end
+ path = ['user request (gem command or Gemfile)'] if path.empty?
+
path
end
diff --git a/lib/rubygems/dependency_resolver/dependency_request.rb b/lib/rubygems/dependency_resolver/dependency_request.rb
index 05e447c3be..36b77ab558 100644
--- a/lib/rubygems/dependency_resolver/dependency_request.rb
+++ b/lib/rubygems/dependency_resolver/dependency_request.rb
@@ -32,6 +32,22 @@ class Gem::DependencyResolver::DependencyRequest
@dependency.name
end
+ # Indicate that the request is for a gem explicitly requested by the user
+ def explicit?
+ @requester.nil?
+ end
+
+ # Indicate that the requset is for a gem requested as a dependency of another gem
+ def implicit?
+ !explicit?
+ end
+
+ # Return a String indicating who caused this request to be added (only
+ # valid for implicit requests)
+ def request_context
+ @requester ? @requester.request : "(unknown)"
+ end
+
def pretty_print q # :nodoc:
q.group 2, '[Dependency request ', ']' do
q.breakable
@@ -43,6 +59,10 @@ class Gem::DependencyResolver::DependencyRequest
end
end
+ def requirement
+ @dependency.requirement
+ end
+
def to_s # :nodoc:
@dependency.to_s
end
diff --git a/lib/rubygems/dependency_resolver/index_set.rb b/lib/rubygems/dependency_resolver/index_set.rb
index 8c8bc4319d..04d6ec816f 100644
--- a/lib/rubygems/dependency_resolver/index_set.rb
+++ b/lib/rubygems/dependency_resolver/index_set.rb
@@ -2,10 +2,17 @@
# The global rubygems pool represented via the traditional
# source index.
-class Gem::DependencyResolver::IndexSet
+class Gem::DependencyResolver::IndexSet < Gem::DependencyResolver::Set
- def initialize
- @f = Gem::SpecFetcher.fetcher
+ def initialize source = nil # :nodoc:
+ @f =
+ if source then
+ sources = Gem::SourceList.from [source]
+
+ Gem::SpecFetcher.new sources
+ else
+ Gem::SpecFetcher.fetcher
+ end
@all = Hash.new { |h,k| h[k] = [] }
@@ -39,26 +46,5 @@ class Gem::DependencyResolver::IndexSet
res
end
- ##
- # Called from IndexSpecification to get a true Specification
- # object.
-
- def load_spec name, ver, platform, source
- key = "#{name}-#{ver}-#{platform}"
-
- @specs.fetch key do
- tuple = Gem::NameTuple.new name, ver, platform
-
- @specs[key] = source.fetch_spec tuple
- end
- end
-
- ##
- # No prefetching needed since we load the whole index in
- # initially.
-
- def prefetch gems
- end
-
end
diff --git a/lib/rubygems/dependency_resolver/index_specification.rb b/lib/rubygems/dependency_resolver/index_specification.rb
index 6cf267dac1..9b4057f0c8 100644
--- a/lib/rubygems/dependency_resolver/index_specification.rb
+++ b/lib/rubygems/dependency_resolver/index_specification.rb
@@ -3,17 +3,20 @@
# delay needed to download full Specification objects when only the +name+
# and +version+ are needed.
-class Gem::DependencyResolver::IndexSpecification
+class Gem::DependencyResolver::IndexSpecification < Gem::DependencyResolver::Specification
- attr_reader :name
-
- attr_reader :platform
-
- attr_reader :source
-
- attr_reader :version
+ ##
+ # An IndexSpecification is created from the index format described in `gem
+ # help generate_index`.
+ #
+ # The +set+ contains other specifications for this (URL) +source+.
+ #
+ # The +name+, +version+ and +platform+ are the name, version and platform of
+ # the gem.
def initialize set, name, version, source, platform
+ super()
+
@set = set
@name = name
@version = version
@@ -23,14 +26,13 @@ class Gem::DependencyResolver::IndexSpecification
@spec = nil
end
+ ##
+ # The dependencies of the gem for this specification
+
def dependencies
spec.dependencies
end
- def full_name
- "#{@name}-#{@version}"
- end
-
def inspect # :nodoc:
'#<%s %s source %s>' % [self.class, full_name, @source]
end
@@ -51,8 +53,16 @@ class Gem::DependencyResolver::IndexSpecification
end
end
- def spec
- @spec ||= @set.load_spec(@name, @version, @platform, @source)
+ ##
+ # Fetches a Gem::Specification for this IndexSpecification from the #source.
+
+ def spec # :nodoc:
+ @spec ||=
+ begin
+ tuple = Gem::NameTuple.new @name, @version, @platform
+
+ @source.fetch_spec tuple
+ end
end
end
diff --git a/lib/rubygems/dependency_resolver/installed_specification.rb b/lib/rubygems/dependency_resolver/installed_specification.rb
index ca20ace61e..4b591661a8 100644
--- a/lib/rubygems/dependency_resolver/installed_specification.rb
+++ b/lib/rubygems/dependency_resolver/installed_specification.rb
@@ -1,12 +1,8 @@
-class Gem::DependencyResolver::InstalledSpecification
+##
+# An InstalledSpecification represents a gem that is already installed
+# locally.
- attr_reader :spec
-
- def initialize set, spec, source=nil
- @set = set
- @source = source
- @spec = spec
- end
+class Gem::DependencyResolver::InstalledSpecification < Gem::DependencyResolver::SpecSpecification
def == other # :nodoc:
self.class === other and
@@ -14,29 +10,25 @@ class Gem::DependencyResolver::InstalledSpecification
@spec == other.spec
end
- def dependencies
- @spec.dependencies
- end
-
- def full_name
- "#{@spec.name}-#{@spec.version}"
- end
+ ##
+ # Returns +true+ if this gem is installable for the current platform.
- def name
- @spec.name
+ def installable_platform?
+ # BACKCOMPAT If the file is coming out of a specified file, then we
+ # ignore the platform. This code can be removed in RG 3.0.
+ if @source.kind_of? Gem::Source::SpecificFile
+ return true
+ else
+ Gem::Platform.match @spec.platform
+ end
end
- def platform
- @spec.platform
- end
+ ##
+ # The source for this specification
def source
@source ||= Gem::Source::Installed.new
end
- def version
- @spec.version
- end
-
end
diff --git a/lib/rubygems/dependency_resolver/installer_set.rb b/lib/rubygems/dependency_resolver/installer_set.rb
index 2993766d3a..801b60a8bb 100644
--- a/lib/rubygems/dependency_resolver/installer_set.rb
+++ b/lib/rubygems/dependency_resolver/installer_set.rb
@@ -2,23 +2,23 @@
# A set of gems for installation sourced from remote sources and local .gem
# files
-class Gem::DependencyResolver::InstallerSet
+class Gem::DependencyResolver::InstallerSet < Gem::DependencyResolver::Set
##
# List of Gem::Specification objects that must always be installed.
- attr_reader :always_install
+ attr_reader :always_install # :nodoc:
##
# Only install gems in the always_install list
- attr_accessor :ignore_dependencies
+ attr_accessor :ignore_dependencies # :nodoc:
##
# Do not look in the installed set when finding specifications. This is
# used by the --install-dir option to `gem install`
- attr_accessor :ignore_installed
+ attr_accessor :ignore_installed # :nodoc:
def initialize domain
@domain = domain
@@ -36,14 +36,14 @@ class Gem::DependencyResolver::InstallerSet
##
# Should local gems should be considered?
- def consider_local?
+ def consider_local? # :nodoc:
@domain == :both or @domain == :local
end
##
# Should remote gems should be considered?
- def consider_remote?
+ def consider_remote? # :nodoc:
@domain == :both or @domain == :remote
end
@@ -101,7 +101,7 @@ class Gem::DependencyResolver::InstallerSet
##
# Loads remote prerelease specs if +dep+ is a prerelease dependency
- def load_remote_specs dep
+ def load_remote_specs dep # :nodoc:
types = [:released]
types << :prerelease if dep.prerelease?
@@ -123,7 +123,7 @@ class Gem::DependencyResolver::InstallerSet
# Called from IndexSpecification to get a true Specification
# object.
- def load_spec name, ver, platform, source
+ def load_spec name, ver, platform, source # :nodoc:
key = "#{name}-#{ver}-#{platform}"
@specs.fetch key do
@@ -133,12 +133,6 @@ class Gem::DependencyResolver::InstallerSet
end
end
- ##
- # No prefetching needed since we load the whole index in initially.
-
- def prefetch(reqs)
- end
-
def pretty_print q # :nodoc:
q.group 2, '[InstallerSet', ']' do
q.breakable
diff --git a/lib/rubygems/dependency_resolver/lock_set.rb b/lib/rubygems/dependency_resolver/lock_set.rb
new file mode 100644
index 0000000000..f95c7f0fd6
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/lock_set.rb
@@ -0,0 +1,60 @@
+##
+# A set of gems from a gem dependencies lockfile.
+
+class Gem::DependencyResolver::LockSet < Gem::DependencyResolver::Set
+
+ attr_reader :specs # :nodoc:
+
+ ##
+ # Creates a new LockSet from the given +source+
+
+ def initialize source
+ @source = source
+ @specs = []
+ end
+
+ ##
+ # Creates a new IndexSpecification in this set using the given +name+,
+ # +version+ and +platform+.
+ #
+ # The specification's set will be the current set, and the source will be
+ # the current set's source.
+
+ def add name, version, platform # :nodoc:
+ version = Gem::Version.new version
+
+ spec =
+ Gem::DependencyResolver::IndexSpecification.new self, name, version,
+ @source, platform
+
+ @specs << spec
+ end
+
+ ##
+ # Returns an Array of IndexSpecification objects matching the
+ # DependencyRequest +req+.
+
+ def find_all req
+ @specs.select do |spec|
+ req.matches_spec? spec
+ end
+ end
+
+ ##
+ # Loads a Gem::Specification with the given +name+, +version+ and
+ # +platform+. +source+ is ignored.
+
+ def load_spec name, version, platform, source # :nodoc:
+ dep = Gem::Dependency.new name, version
+
+ found = @specs.find do |spec|
+ dep.matches_spec? spec and spec.platform == platform
+ end
+
+ tuple = Gem::NameTuple.new found.name, found.version, found.platform
+
+ found.source.fetch_spec tuple
+ end
+
+end
+
diff --git a/lib/rubygems/dependency_resolver/set.rb b/lib/rubygems/dependency_resolver/set.rb
new file mode 100644
index 0000000000..65801871ac
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/set.rb
@@ -0,0 +1,28 @@
+##
+# DependencyResolver sets are used to look up specifications (and their
+# dependencies) used in resolution. This set is abstract.
+
+class Gem::DependencyResolver::Set
+
+ ##
+ # The find_all method must be implemented. It returns all
+ # DependencyResolver Specification objects matching the given
+ # DependencyRequest +req+.
+
+ def find_all req
+ raise NotImplementedError
+ end
+
+ ##
+ # The #prefetch method may be overridden, but this is not necessary. This
+ # default implementation does nothing, which is suitable for sets where
+ # looking up a specification is cheap (such as installed gems).
+ #
+ # When overridden, the #prefetch method should look up specifications
+ # matching +reqs+.
+
+ def prefetch reqs
+ end
+
+end
+
diff --git a/lib/rubygems/dependency_resolver/spec_specification.rb b/lib/rubygems/dependency_resolver/spec_specification.rb
new file mode 100644
index 0000000000..cca1d58b9f
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/spec_specification.rb
@@ -0,0 +1,58 @@
+##
+# The DependencyResolver::SpecSpecification contains common functionality for
+# DependencyResolver specifications that are backed by a Gem::Specification.
+
+class Gem::DependencyResolver::SpecSpecification < Gem::DependencyResolver::Specification
+
+ attr_reader :spec # :nodoc:
+
+ ##
+ # A SpecSpecification is created for a +set+ for a Gem::Specification in
+ # +spec+. The +source+ is either where the +spec+ came from, or should be
+ # loaded from.
+
+ def initialize set, spec, source = nil
+ @set = set
+ @source = source
+ @spec = spec
+ end
+
+ ##
+ # The dependencies of the gem for this specification
+
+ def dependencies
+ spec.dependencies
+ end
+
+ ##
+ # The name and version of the specification.
+ #
+ # Unlike Gem::Specification#full_name, the platform is not included.
+
+ def full_name
+ "#{spec.name}-#{spec.version}"
+ end
+
+ ##
+ # The name of the gem for this specification
+
+ def name
+ spec.name
+ end
+
+ ##
+ # The platform this gem works on.
+
+ def platform
+ spec.platform
+ end
+
+ ##
+ # The version of the gem for this specification.
+
+ def version
+ spec.version
+ end
+
+end
+
diff --git a/lib/rubygems/dependency_resolver/specification.rb b/lib/rubygems/dependency_resolver/specification.rb
new file mode 100644
index 0000000000..6fbd241316
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/specification.rb
@@ -0,0 +1,60 @@
+##
+# A DependencyResolver::Specification contains a subset of the information
+# contained in a Gem::Specification. Only the information necessary for
+# dependency resolution in the resolver is included.
+
+class Gem::DependencyResolver::Specification
+
+ ##
+ # The dependencies of the gem for this specification
+
+ attr_reader :dependencies
+
+ ##
+ # The name of the gem for this specification
+
+ attr_reader :name
+
+ ##
+ # The platform this gem works on.
+
+ attr_reader :platform
+
+ ##
+ # The set this specification came from.
+
+ attr_reader :set
+
+ ##
+ # The source for this specification
+
+ attr_reader :source
+
+ ##
+ # The version of the gem for this specification.
+
+ attr_reader :version
+
+ ##
+ # Sets default instance variables for the specification.
+
+ def initialize
+ @dependencies = nil
+ @name = nil
+ @platform = nil
+ @set = nil
+ @source = nil
+ @version = nil
+ end
+
+ ##
+ # The name and version of the specification.
+ #
+ # Unlike Gem::Specification#full_name, the platform is not included.
+
+ def full_name
+ "#{@name}-#{@version}"
+ end
+
+end
+
diff --git a/lib/rubygems/dependency_resolver/vendor_set.rb b/lib/rubygems/dependency_resolver/vendor_set.rb
index 716c2a8e26..87eb6fd818 100644
--- a/lib/rubygems/dependency_resolver/vendor_set.rb
+++ b/lib/rubygems/dependency_resolver/vendor_set.rb
@@ -13,17 +13,18 @@
# The directory vendor/rake must contain an unpacked rake gem along with a
# rake.gemspec (watching the given name).
-class Gem::DependencyResolver::VendorSet
+class Gem::DependencyResolver::VendorSet < Gem::DependencyResolver::Set
- def initialize
- @specs = {}
+ def initialize # :nodoc:
+ @directories = {}
+ @specs = {}
end
##
# Adds a specification to the set with the given +name+ which has been
# unpacked into the given +directory+.
- def add_vendor_gem name, directory
+ def add_vendor_gem name, directory # :nodoc:
gemspec = File.join directory, "#{name}.gemspec"
spec = Gem::Specification.load gemspec
@@ -33,7 +34,8 @@ class Gem::DependencyResolver::VendorSet
key = "#{spec.name}-#{spec.version}-#{spec.platform}"
- @specs[key] = spec
+ @specs[key] = spec
+ @directories[spec] = directory
end
##
@@ -44,7 +46,8 @@ class Gem::DependencyResolver::VendorSet
@specs.values.select do |spec|
req.matches_spec? spec
end.map do |spec|
- Gem::DependencyResolver::VendorSpecification.new self, spec, nil
+ source = Gem::Source::Vendor.new @directories[spec]
+ Gem::DependencyResolver::VendorSpecification.new self, spec, source
end
end
@@ -53,17 +56,11 @@ class Gem::DependencyResolver::VendorSet
# +source+ is defined when the specification was added to index it is not
# used.
- def load_spec name, version, platform, source
+ def load_spec name, version, platform, source # :nodoc:
key = "#{name}-#{version}-#{platform}"
@specs.fetch key
end
- ##
- # No prefetch is needed as the index is loaded at creation time.
-
- def prefetch gems
- end
-
end
diff --git a/lib/rubygems/dependency_resolver/vendor_specification.rb b/lib/rubygems/dependency_resolver/vendor_specification.rb
index 2f18fab4ce..27b2fd6df2 100644
--- a/lib/rubygems/dependency_resolver/vendor_specification.rb
+++ b/lib/rubygems/dependency_resolver/vendor_specification.rb
@@ -1,43 +1,15 @@
-class Gem::DependencyResolver::VendorSpecification
+##
+# A VendorSpecification represents a gem that has been unpacked into a project
+# and is being loaded through a gem dependencies file through the +path:+
+# option.
- attr_reader :spec
-
- attr_reader :set
-
- def initialize set, spec, source=nil
- @set = set
- @source = source
- @spec = spec
- end
+class Gem::DependencyResolver::VendorSpecification < Gem::DependencyResolver::SpecSpecification
def == other # :nodoc:
self.class === other and
@set == other.set and
- @spec == other.spec
- end
-
- def dependencies
- @spec.dependencies
- end
-
- def full_name
- "#{@spec.name}-#{@spec.version}"
- end
-
- def name
- @spec.name
- end
-
- def platform
- @spec.platform
- end
-
- def source
- @source ||= Gem::Source::Vendor.new
- end
-
- def version
- @spec.version
+ @spec == other.spec and
+ @source == other.source
end
end
diff --git a/lib/rubygems/errors.rb b/lib/rubygems/errors.rb
index 4a92011e8b..3c5486a800 100644
--- a/lib/rubygems/errors.rb
+++ b/lib/rubygems/errors.rb
@@ -19,8 +19,6 @@ module Gem
attr_accessor :requirement
end
- # FIX: does this need to exist? The subclass is the only other reference
- # I can find.
class ErrorReason; end
# Generated when trying to lookup a gem to indicate that the gem
diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb
index 30d9880d8a..6d92b144b6 100644
--- a/lib/rubygems/exceptions.rb
+++ b/lib/rubygems/exceptions.rb
@@ -35,7 +35,7 @@ class Gem::DependencyResolutionError < Gem::Exception
@conflict = conflict
a, b = conflicting_dependencies
- super "unable to resolve conflicting dependencies '#{a}' and '#{b}'"
+ super "conflicting dependencies #{a} and #{b}\n#{@conflict.explanation}"
end
def conflicting_dependencies
@@ -226,10 +226,17 @@ class Gem::UnsatisfiableDependencyError < Gem::Exception
# Creates a new UnsatisfiableDepedencyError for the unsatisfiable
# Gem::DependencyResolver::DependencyRequest +dep+
- def initialize dep
- requester = dep.requester ? dep.requester.request : '(unknown)'
-
- super "Unable to resolve dependency: #{requester} requires #{dep}"
+ def initialize dep, platform_mismatch=nil
+ if platform_mismatch and !platform_mismatch.empty?
+ plats = platform_mismatch.map { |x| x.platform.to_s }.sort.uniq
+ super "Unable to resolve dependency: No match for '#{dep}' on this platform. Found: #{plats.join(', ')}"
+ else
+ if dep.explicit?
+ super "Unable to resolve dependency: user requested '#{dep}'"
+ else
+ super "Unable to resolve dependency: '#{dep.request_context}' requires '#{dep}'"
+ end
+ end
@dependency = dep
end
diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb
index 247ee6ed3e..e050959dc6 100644
--- a/lib/rubygems/platform.rb
+++ b/lib/rubygems/platform.rb
@@ -29,6 +29,14 @@ class Gem::Platform
end
end
+ def self.installable?(spec)
+ if spec.respond_to? :installable_platform?
+ spec.installable_platform?
+ else
+ match spec.platform
+ end
+ end
+
def self.new(arch) # :nodoc:
case arch
when Gem::Platform::CURRENT then
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index bab96cba1a..b96c77033a 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -78,7 +78,6 @@ class Gem::RemoteFetcher
end
##
- #
# Given a source at +uri+, calculate what hostname to actually
# connect to query the data for it.
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb
index 9a7ac83e91..3a997f32ee 100644
--- a/lib/rubygems/request_set.rb
+++ b/lib/rubygems/request_set.rb
@@ -32,6 +32,11 @@ class Gem::RequestSet
attr_accessor :development
##
+ # Sets used for resolution
+
+ attr_reader :sets # :nodoc:
+
+ ##
# Treat missing dependencies as silent errors
attr_accessor :soft_missing
@@ -53,13 +58,15 @@ class Gem::RequestSet
def initialize *deps
@dependencies = deps
- @always_install = []
- @development = false
- @requests = []
- @soft_missing = false
- @sorted = nil
- @specs = nil
- @vendor_set = nil
+ @always_install = []
+ @dependency_names = {}
+ @development = false
+ @requests = []
+ @sets = []
+ @soft_missing = false
+ @sorted = nil
+ @specs = nil
+ @vendor_set = nil
yield self if block_given?
end
@@ -68,7 +75,13 @@ class Gem::RequestSet
# Declare that a gem of name +name+ with +reqs+ requirements is needed.
def gem name, *reqs
- @dependencies << Gem::Dependency.new(name, reqs)
+ if dep = @dependency_names[name] then
+ dep.requirement.concat reqs
+ else
+ dep = Gem::Dependency.new name, reqs
+ @dependency_names[name] = dep
+ @dependencies << dep
+ end
end
##
@@ -78,7 +91,14 @@ class Gem::RequestSet
@dependencies.concat deps
end
- def install options, &block
+ ##
+ # Installs gems for this RequestSet using the Gem::Installer +options+.
+ #
+ # If a +block+ is given an activation +request+ and +installer+ are yielded.
+ # The +installer+ will be +nil+ if a gem matching the request was already
+ # installed.
+
+ def install options, &block # :yields: request, installer
if dir = options[:install_dir]
return install_into dir, false, options, &block
end
@@ -109,6 +129,21 @@ class Gem::RequestSet
specs
end
+ ##
+ # Installs from the gem dependencies files in the +:gemdeps+ option in
+ # +options+, yielding to the +block+ as in #install.
+ #
+ # If +:without_groups+ is given in the +options+, those groups in the gem
+ # dependencies file are not used. See Gem::Installer for other +options+.
+
+ def install_from_gemdeps options, &block
+ load_gemdeps options[:gemdeps], options[:without_groups]
+
+ resolve
+
+ install options, &block
+ end
+
def install_into dir, force = true, options = {}
existing = force ? [] : specs_in(dir)
existing.delete_if { |s| @always_install.include? s }
@@ -148,10 +183,11 @@ class Gem::RequestSet
##
# Load a dependency management file.
- def load_gemdeps path
+ def load_gemdeps path, without_groups = []
@vendor_set = Gem::DependencyResolver::VendorSet.new
gf = Gem::RequestSet::GemDependencyAPI.new self, path
+ gf.without_groups = without_groups if without_groups
gf.load
end
@@ -160,13 +196,10 @@ class Gem::RequestSet
# objects to be activated.
def resolve set = Gem::DependencyResolver::IndexSet.new
- sets = [set, @vendor_set].compact
+ @sets << set
+ @sets << @vendor_set
- set = if sets.size == 1 then
- sets.first
- else
- Gem::DependencyResolver.compose_sets(*sets)
- end
+ set = Gem::DependencyResolver.compose_sets(*@sets)
resolver = Gem::DependencyResolver.new @dependencies, set
resolver.development = @development
diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb
index f11ffb12c3..e8f3138990 100644
--- a/lib/rubygems/request_set/gem_dependency_api.rb
+++ b/lib/rubygems/request_set/gem_dependency_api.rb
@@ -3,10 +3,114 @@
class Gem::RequestSet::GemDependencyAPI
+ ENGINE_MAP = { # :nodoc:
+ :jruby => %w[jruby],
+ :jruby_18 => %w[jruby],
+ :jruby_19 => %w[jruby],
+ :maglev => %w[maglev],
+ :mri => %w[ruby],
+ :mri_18 => %w[ruby],
+ :mri_19 => %w[ruby],
+ :mri_20 => %w[ruby],
+ :mri_21 => %w[ruby],
+ :rbx => %w[rbx],
+ :ruby => %w[ruby rbx maglev],
+ :ruby_18 => %w[ruby rbx maglev],
+ :ruby_19 => %w[ruby rbx maglev],
+ :ruby_20 => %w[ruby rbx maglev],
+ :ruby_21 => %w[ruby rbx maglev],
+ }
+
+ x86_mingw = Gem::Platform.new 'x86-mingw32'
+ x64_mingw = Gem::Platform.new 'x64-mingw32'
+
+ PLATFORM_MAP = { # :nodoc:
+ :jruby => Gem::Platform::RUBY,
+ :jruby_18 => Gem::Platform::RUBY,
+ :jruby_19 => Gem::Platform::RUBY,
+ :maglev => Gem::Platform::RUBY,
+ :mingw => x86_mingw,
+ :mingw_18 => x86_mingw,
+ :mingw_19 => x86_mingw,
+ :mingw_20 => x86_mingw,
+ :mingw_21 => x86_mingw,
+ :mri => Gem::Platform::RUBY,
+ :mri_18 => Gem::Platform::RUBY,
+ :mri_19 => Gem::Platform::RUBY,
+ :mri_20 => Gem::Platform::RUBY,
+ :mri_21 => Gem::Platform::RUBY,
+ :mswin => Gem::Platform::RUBY,
+ :rbx => Gem::Platform::RUBY,
+ :ruby => Gem::Platform::RUBY,
+ :ruby_18 => Gem::Platform::RUBY,
+ :ruby_19 => Gem::Platform::RUBY,
+ :ruby_20 => Gem::Platform::RUBY,
+ :ruby_21 => Gem::Platform::RUBY,
+ :x64_mingw => x64_mingw,
+ :x64_mingw_20 => x64_mingw,
+ :x64_mingw_21 => x64_mingw
+ }
+
+ gt_eq_0 = Gem::Requirement.new '>= 0'
+ tilde_gt_1_8_0 = Gem::Requirement.new '~> 1.8.0'
+ tilde_gt_1_9_0 = Gem::Requirement.new '~> 1.9.0'
+ tilde_gt_2_0_0 = Gem::Requirement.new '~> 2.0.0'
+ tilde_gt_2_1_0 = Gem::Requirement.new '~> 2.1.0'
+
+ VERSION_MAP = { # :nodoc:
+ :jruby => gt_eq_0,
+ :jruby_18 => tilde_gt_1_8_0,
+ :jruby_19 => tilde_gt_1_9_0,
+ :maglev => gt_eq_0,
+ :mingw => gt_eq_0,
+ :mingw_18 => tilde_gt_1_8_0,
+ :mingw_19 => tilde_gt_1_9_0,
+ :mingw_20 => tilde_gt_2_0_0,
+ :mingw_21 => tilde_gt_2_1_0,
+ :mri => gt_eq_0,
+ :mri_18 => tilde_gt_1_8_0,
+ :mri_19 => tilde_gt_1_9_0,
+ :mri_20 => tilde_gt_2_0_0,
+ :mri_21 => tilde_gt_2_1_0,
+ :mswin => gt_eq_0,
+ :rbx => gt_eq_0,
+ :ruby => gt_eq_0,
+ :ruby_18 => tilde_gt_1_8_0,
+ :ruby_19 => tilde_gt_1_9_0,
+ :ruby_20 => tilde_gt_2_0_0,
+ :ruby_21 => tilde_gt_2_1_0,
+ :x64_mingw => gt_eq_0,
+ :x64_mingw_20 => tilde_gt_2_0_0,
+ :x64_mingw_21 => tilde_gt_2_1_0,
+ }
+
+ WINDOWS = { # :nodoc:
+ :mingw => :only,
+ :mingw_18 => :only,
+ :mingw_19 => :only,
+ :mingw_20 => :only,
+ :mingw_21 => :only,
+ :mri => :never,
+ :mri_18 => :never,
+ :mri_19 => :never,
+ :mri_20 => :never,
+ :mri_21 => :never,
+ :mswin => :only,
+ :rbx => :never,
+ :ruby => :never,
+ :ruby_18 => :never,
+ :ruby_19 => :never,
+ :ruby_20 => :never,
+ :ruby_21 => :never,
+ :x64_mingw => :only,
+ :x64_mingw_20 => :only,
+ :x64_mingw_21 => :only,
+ }
+
##
- # The dependency groups created by #group in the dependency API file.
+ # A Hash containing gem names and files to require from those gems.
- attr_reader :dependency_groups
+ attr_reader :requires
##
# A set of gems that are loaded via the +:path+ option to #gem
@@ -14,6 +118,11 @@ class Gem::RequestSet::GemDependencyAPI
attr_reader :vendor_set # :nodoc:
##
+ # The groups of gems to exclude from installation
+
+ attr_accessor :without_groups
+
+ ##
# Creates a new GemDependencyAPI that will add dependencies to the
# Gem::RequestSet +set+ based on the dependency API description in +path+.
@@ -21,9 +130,13 @@ class Gem::RequestSet::GemDependencyAPI
@set = set
@path = path
- @current_groups = nil
- @dependency_groups = Hash.new { |h, group| h[group] = [] }
- @vendor_set = @set.vendor_set
+ @current_groups = nil
+ @current_platform = nil
+ @default_sources = true
+ @requires = Hash.new { |h, name| h[name] = [] }
+ @vendor_set = @set.vendor_set
+ @gem_sources = {}
+ @without_groups = []
end
##
@@ -47,10 +160,32 @@ class Gem::RequestSet::GemDependencyAPI
options = requirements.pop if requirements.last.kind_of?(Hash)
options ||= {}
- if directory = options.delete(:path) then
- @vendor_set.add_vendor_gem name, directory
+ source_set = gem_path name, options
+
+ return unless gem_platforms options
+
+ groups = gem_group name, options
+
+ 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
+ gem_requires name, options
+
+ @set.gem name, *requirements
+ end
+
+ ##
+ # Handles the :group and :groups +options+ for the gem with the given
+ # +name+.
+
+ def gem_group name, options # :nodoc:
g = options.delete :group
all_groups = g ? Array(g) : []
@@ -59,19 +194,81 @@ class Gem::RequestSet::GemDependencyAPI
all_groups |= @current_groups if @current_groups
- unless all_groups.empty? then
- all_groups.each do |group|
- gem_arguments = [name, *requirements]
- gem_arguments << options unless options.empty?
- @dependency_groups[group] << gem_arguments
+ all_groups
+ end
+
+ private :gem_group
+
+ ##
+ # Handles the path: option from +options+ for gem +name+.
+ #
+ # Returns +true+ if the path option was handled.
+
+ 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
+
+ @vendor_set.add_vendor_gem name, directory
+
+ @gem_sources[name] = directory
+
+ true
+ end
+
+ private :gem_path
+
+ ##
+ # Handles the platforms: option from +options+. Returns true if the
+ # platform matches the current platform.
+
+ def gem_platforms options # :nodoc:
+ platform_names = Array(options.delete :platforms)
+ platform_names << @current_platform if @current_platform
+
+ return true if platform_names.empty?
+
+ platform_names.any? do |platform_name|
+ raise ArgumentError, "unknown platform #{platform_name.inspect}" unless
+ platform = PLATFORM_MAP[platform_name]
+
+ next false unless Gem::Platform.match platform
+
+ if engines = ENGINE_MAP[platform_name] then
+ next false unless engines.include? Gem.ruby_engine
+ end
+
+ case WINDOWS[platform_name]
+ when :only then
+ next false unless Gem.win_platform?
+ when :never then
+ next false if Gem.win_platform?
end
- return
+ VERSION_MAP[platform_name].satisfied_by? Gem.ruby_version
end
+ end
- @set.gem name, *requirements
+ private :gem_platforms
+
+ ##
+ # Handles the require: option from +options+ and adds those files, or the
+ # default file to the require list for +name+.
+
+ def gem_requires name, options # :nodoc:
+ if options.include? :require then
+ if requires = options.delete(:require) then
+ @requires[name].concat requires
+ end
+ else
+ @requires[name] << name
+ end
end
+ private :gem_requires
+
##
# Returns the basename of the file the dependencies were loaded from
@@ -96,9 +293,12 @@ class Gem::RequestSet::GemDependencyAPI
# :category: Gem Dependencies DSL
def platform what
- if what == :ruby
- yield
- end
+ @current_platform = what
+
+ yield
+
+ ensure
+ @current_platform = nil
end
##
@@ -112,23 +312,58 @@ class Gem::RequestSet::GemDependencyAPI
# +:engine+ options from Bundler are currently ignored.
def ruby version, options = {}
- return true if version == RUBY_VERSION
+ engine = options[:engine]
+ engine_version = options[:engine_version]
+
+ raise ArgumentError,
+ 'you must specify engine_version along with the ruby engine' if
+ engine and not engine_version
+
+ unless RUBY_VERSION == version then
+ message = "Your Ruby version is #{RUBY_VERSION}, " +
+ "but your #{gem_deps_file} requires #{version}"
+
+ raise Gem::RubyVersionMismatch, message
+ end
+
+ if engine and engine != Gem.ruby_engine then
+ message = "Your ruby engine is #{Gem.ruby_engine}, " +
+ "but your #{gem_deps_file} requires #{engine}"
+
+ raise Gem::RubyVersionMismatch, message
+ end
- message = "Your Ruby version is #{RUBY_VERSION}, " +
- "but your #{gem_deps_file} specified #{version}"
+ if engine_version then
+ my_engine_version = Object.const_get "#{Gem.ruby_engine.upcase}_VERSION"
- raise Gem::RubyVersionMismatch, message
+ if engine_version != my_engine_version then
+ message =
+ "Your ruby engine version is #{Gem.ruby_engine} #{my_engine_version}, " +
+ "but your #{gem_deps_file} requires #{engine} #{engine_version}"
+
+ raise Gem::RubyVersionMismatch, message
+ end
+ end
+
+ return true
end
##
# :category: Gem Dependencies DSL
+ #
+ # Sets +url+ as a source for gems for this dependency API.
def source url
+ Gem.sources.clear if @default_sources
+
+ @default_sources = false
+
+ Gem.sources << url
end
# TODO: remove this typo name at RubyGems 3.0
- Gem::RequestSet::DepedencyAPI = self # :nodoc:
+ Gem::RequestSet::GemDepedencyAPI = self # :nodoc:
end
diff --git a/lib/rubygems/request_set/lockfile.rb b/lib/rubygems/request_set/lockfile.rb
new file mode 100644
index 0000000000..a9c419549d
--- /dev/null
+++ b/lib/rubygems/request_set/lockfile.rb
@@ -0,0 +1,347 @@
+require 'pathname'
+
+class Gem::RequestSet::Lockfile
+
+ ##
+ # Raised when a lockfile cannot be parsed
+
+ class ParseError < Gem::Exception
+
+ ##
+ # The column where the error was encountered
+
+ attr_reader :column
+
+ ##
+ # The line where the error was encountered
+
+ attr_reader :line
+
+ ##
+ # The location of the lock file
+
+ attr_reader :path
+
+ ##
+ # Raises a ParseError with the given +message+ which was encountered at a
+ # +line+ and +column+ while parsing.
+
+ def initialize message, line, column, path
+ @line = line
+ @column = column
+ @path = path
+ super "#{message} (at #{line}:#{column})"
+ end
+
+ end
+
+ ##
+ # The platforms for this Lockfile
+
+ attr_reader :platforms
+
+ ##
+ # Creates a new Lockfile for the given +request_set+ and +gem_deps_file+
+ # location.
+
+ def initialize request_set, gem_deps_file
+ @set = request_set
+ @gem_deps_file = Pathname(gem_deps_file).expand_path
+ @gem_deps_dir = @gem_deps_file.dirname
+
+ @current_token = nil
+ @line = 0
+ @line_pos = 0
+ @platforms = []
+ @tokens = []
+ end
+
+ 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::DependencyResolver::VendorSpecification
+ 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"
+
+ source_groups = @spec_groups.values.flatten.group_by do |request|
+ request.spec.source.uri
+ end
+
+ source_groups.map do |group, requests|
+ out << " remote: #{group}"
+ out << " specs:"
+
+ requests.sort_by { |request| request.name }.each do |request|
+ platform = "-#{request.spec.platform}" unless
+ Gem::Platform::RUBY == request.spec.platform
+
+ out << " #{request.name} (#{request.version}#{platform})"
+
+ request.full_spec.dependencies.sort.each do |dependency|
+ requirement = dependency.requirement
+ out << " #{dependency.name}#{requirement.for_lockfile}"
+ end
+ end
+ end
+
+ out << nil
+ end
+
+ def add_PATH out # :nodoc:
+ return unless path_requests =
+ @spec_groups.delete(Gem::DependencyResolver::VendorSpecification)
+
+ out << "PATH"
+ path_requests.each do |request|
+ directory = Pathname(request.spec.source.uri).expand_path
+
+ out << " remote: #{directory.relative_path_from @gem_deps_dir}"
+ out << " specs:"
+ out << " #{request.name} (#{request.version})"
+ end
+
+ out << nil
+ end
+
+ def add_PLATFORMS out # :nodoc:
+ out << "PLATFORMS"
+
+ platforms = @requests.map { |request| request.spec.platform }.uniq
+ platforms.delete Gem::Platform::RUBY if platforms.length > 1
+
+ platforms.each do |platform|
+ out << " #{platform}"
+ end
+
+ out << nil
+ end
+
+ ##
+ # Gets the next token for a Lockfile
+
+ def get expected_type = nil, expected_value = nil # :nodoc:
+ @current_token = @tokens.shift
+
+ type, value, line, column = @current_token
+
+ if expected_type and expected_type != type then
+ unget
+
+ message = "unexpected token [#{type.inspect}, #{value.inspect}], " +
+ "expected #{expected_type.inspect}"
+
+ raise ParseError.new message, line, column, "#{@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}]"
+
+ raise ParseError.new message, line, column, "#{@gem_deps_file}.lock"
+ end
+
+ @current_token
+ end
+
+ def parse # :nodoc:
+ tokenize
+
+ until @tokens.empty? do
+ type, data, column, line = get
+
+ case type
+ when :section then
+ skip :newline
+
+ case data
+ when 'DEPENDENCIES' then
+ parse_DEPENDENCIES
+ when 'GEM' then
+ parse_GEM
+ when 'PLATFORMS' then
+ parse_PLATFORMS
+ else
+ type, = get until @tokens.empty? or peek.first == :section
+ end
+ else
+ raise "BUG: unhandled token #{type} (#{data.inspect}) at #{line}:#{column}"
+ end
+ end
+ end
+
+ def parse_DEPENDENCIES # :nodoc:
+ while not @tokens.empty? and :text == peek.first do
+ _, name, = get :text
+
+ @set.gem name
+
+ skip :newline
+ end
+ end
+
+ def parse_GEM # :nodoc:
+ get :entry, 'remote'
+ _, data, = get :text
+
+ source = Gem::Source.new data
+
+ skip :newline
+
+ get :entry, 'specs'
+
+ skip :newline
+
+ set = Gem::DependencyResolver::LockSet.new source
+
+ while not @tokens.empty? and :text == peek.first do
+ _, name, = get :text
+
+ case peek[0]
+ when :newline then # ignore
+ when :l_paren then
+ get :l_paren
+
+ _, version, = get :text
+
+ get :r_paren
+
+ set.add name, version, Gem::Platform::RUBY
+ else
+ raise "BUG: unknown token #{peek}"
+ end
+
+ skip :newline
+ end
+
+ @set.sets << set
+ end
+
+ def parse_PLATFORMS # :nodoc:
+ while not @tokens.empty? and :text == peek.first do
+ _, name, = get :text
+
+ @platforms << name
+
+ skip :newline
+ end
+ end
+
+ ##
+ # Peeks at the next token for Lockfile
+
+ def peek # :nodoc:
+ @tokens.first
+ end
+
+ def skip type # :nodoc:
+ get while not @tokens.empty? and peek.first == type
+ end
+
+ def to_s
+ @set.resolve
+
+ out = []
+
+ @requests = @set.sorted_requests
+
+ @spec_groups = @requests.group_by do |request|
+ request.spec.class
+ end
+
+ add_PATH out
+
+ add_GEM out
+
+ add_PLATFORMS out
+
+ add_DEPENDENCIES out
+
+ out.join "\n"
+ end
+
+ ##
+ # Calculates the column (by byte) and the line of the current token based on
+ # +byte_offset+.
+
+ def token_pos byte_offset # :nodoc:
+ [byte_offset - @line_pos, @line]
+ end
+
+ def tokenize # :nodoc:
+ @line = 0
+ @line_pos = 0
+
+ @platforms = []
+ @tokens = []
+ @current_token = nil
+
+ lock_file = "#{@gem_deps_file}.lock"
+
+ @input = File.read lock_file
+ s = StringScanner.new @input
+
+ until s.eos? do
+ pos = s.pos
+
+ # leading whitespace is for the user's convenience
+ next if s.scan(/ +/)
+
+ if s.scan(/[<|=>]{7}/) then
+ message = "your #{lock_file} contains merge conflict markers"
+ line, column = token_pos pos
+
+ raise ParseError.new message, line, column, lock_file
+ end
+
+ @tokens <<
+ case
+ when s.scan(/\r?\n/) then
+ token = [:newline, nil, *token_pos(pos)]
+ @line_pos = s.pos
+ @line += 1
+ token
+ when s.scan(/[A-Z]+/) then
+ [:section, s.matched, *token_pos(pos)]
+ when s.scan(/([a-z]+):\s/) then
+ s.pos -= 1 # rewind for possible newline
+ [:entry, s[1], *token_pos(pos)]
+ when s.scan(/\(/) then
+ [:l_paren, nil, *token_pos(pos)]
+ when s.scan(/\)/) then
+ [:r_paren, 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}"
+ end
+ end
+
+ @tokens
+ end
+
+ ##
+ # Ungets the last token retrieved by #get
+
+ def unget # :nodoc:
+ @tokens.unshift @current_token
+ end
+
+end
+
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb
index 4bf1ed914f..2b112a8022 100644
--- a/lib/rubygems/requirement.rb
+++ b/lib/rubygems/requirement.rb
@@ -21,11 +21,21 @@ class Gem::Requirement
}
quoted = OPS.keys.map { |k| Regexp.quote k }.join "|"
- PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{Gem::Version::VERSION_PATTERN})\\s*"
+ PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{Gem::Version::VERSION_PATTERN})\\s*" # :nodoc:
+
+ ##
+ # A regular expression that matches a requirement
+
PATTERN = /\A#{PATTERN_RAW}\z/
+ ##
+ # The default requirement matches any version
+
DefaultRequirement = [">=", Gem::Version.new(0)]
+ ##
+ # Raised when a bad requirement is encountered
+
class BadRequirementError < ArgumentError; end
##
@@ -108,9 +118,27 @@ class Gem::Requirement
end
##
+ # Concatenates the +new+ requirements onto this requirement.
+
+ def concat new
+ new = new.flatten
+ new.compact!
+ new.uniq!
+ new = new.map { |r| self.class.parse r }
+
+ @requirements.concat new
+ end
+
+ ##
+ # Formats this requirement for use in a Gem::RequestSet::Lockfile.
+
+ def for_lockfile # :nodoc:
+ " (#{to_s})" unless [DefaultRequirement] == @requirements
+ end
+
+ ##
# true if this gem has no requirements.
- # FIX: maybe this should be using #default ?
def none?
if @requirements.size == 1
@requirements[0] == DefaultRequirement
@@ -152,11 +180,11 @@ class Gem::Requirement
yaml_initialize coder.tag, coder.map
end
- def to_yaml_properties
+ def to_yaml_properties # :nodoc:
["@requirements"]
end
- def encode_with(coder)
+ def encode_with coder # :nodoc:
coder.add 'requirements', @requirements
end
@@ -200,15 +228,13 @@ class Gem::Requirement
as_list.join ", "
end
- # DOC: this should probably be :nodoc'd
- def == other
+ def == other # :nodoc:
Gem::Requirement === other and to_s == other.to_s
end
private
- # DOC: this should probably be :nodoc'd
- def fix_syck_default_key_in_requirements
+ def fix_syck_default_key_in_requirements # :nodoc:
Gem.load_yaml
# Fixup the Syck DefaultKey bug
@@ -220,9 +246,9 @@ class Gem::Requirement
end
end
-# This is needed for compatibility with older yaml
-# gemspecs.
-
class Gem::Version
- Requirement = Gem::Requirement
+ # This is needed for compatibility with older yaml
+ # gemspecs.
+
+ Requirement = Gem::Requirement # :nodoc:
end
diff --git a/lib/rubygems/server.rb b/lib/rubygems/server.rb
index 3ca588ae92..ca6dc683f5 100644
--- a/lib/rubygems/server.rb
+++ b/lib/rubygems/server.rb
@@ -445,7 +445,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
@spec_dirs = @gem_dirs.map { |gem_dir| File.join gem_dir, 'specifications' }
@spec_dirs.reject! { |spec_dir| !File.directory? spec_dir }
- Gem::Specification.dirs = @gem_dirs
+ reset_gems
@have_rdoc_4_plus = nil
end
@@ -470,7 +470,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end
def latest_specs(req, res)
- Gem::Specification.reset
+ reset_gems
res['content-type'] = 'application/x-gzip'
@@ -531,7 +531,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end
def quick(req, res)
- Gem::Specification.reset
+ reset_gems
res['content-type'] = 'text/plain'
add_date res
@@ -567,7 +567,8 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end
def root(req, res)
- Gem::Specification.reset
+ reset_gems
+
add_date res
raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." unless
@@ -698,6 +699,13 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end
##
+ # Updates the server to use the latest installed gems.
+
+ def reset_gems # :nodoc:
+ Gem::Specification.dirs = @gem_dirs
+ end
+
+ ##
# Returns true and prepares http response, if rdoc for the requested gem
# name pattern was found.
#
@@ -787,7 +795,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end
def specs(req, res)
- Gem::Specification.reset
+ reset_gems
add_date res
diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb
index 394ea6cf22..bcf814b010 100644
--- a/lib/rubygems/source.rb
+++ b/lib/rubygems/source.rb
@@ -52,6 +52,24 @@ class Gem::Source
alias_method :eql?, :==
+ ##
+ # Returns a Set that can fetch specifications from this source.
+
+ def dependency_resolver_set # :nodoc:
+ uri = api_uri
+
+ bundler_api_uri = api_uri + './api/v1/dependencies'
+
+ begin
+ fetcher = Gem::RemoteFetcher.fetcher
+ fetcher.fetch_path bundler_api_uri, nil, true
+ rescue Gem::RemoteFetcher::FetchError
+ Gem::DependencyResolver::IndexSet.new self
+ else
+ Gem::DependencyResolver::APISet.new bundler_api_uri
+ end
+ end
+
def hash
@uri.hash
end
diff --git a/lib/rubygems/source/vendor.rb b/lib/rubygems/source/vendor.rb
index 701bc7fe84..f2cf540c8d 100644
--- a/lib/rubygems/source/vendor.rb
+++ b/lib/rubygems/source/vendor.rb
@@ -2,5 +2,10 @@
# This represents a vendored source that is similar to an installed gem.
class Gem::Source::Vendor < Gem::Source::Installed
+
+ def initialize uri
+ @uri = uri
+ end
+
end
diff --git a/lib/rubygems/source_list.rb b/lib/rubygems/source_list.rb
index 7bd8ef0b78..e6da50c2e5 100644
--- a/lib/rubygems/source_list.rb
+++ b/lib/rubygems/source_list.rb
@@ -1,28 +1,40 @@
require 'rubygems/source'
class Gem::SourceList
+
+ include Enumerable
+
+ ##
+ # Creates a new SourceList
+
def initialize
@sources = []
end
+ ##
+ # The sources in this list
+
attr_reader :sources
+ ##
+ # Creates a new SourceList from an array of sources.
+
def self.from(ary)
list = new
- if ary
- ary.each do |x|
- list << x
- end
- end
+ list.replace ary
return list
end
- def initialize_copy(other)
+ def initialize_copy(other) # :nodoc:
@sources = @sources.dup
end
+ ##
+ # Appends +obj+ to the source list which may be a Gem::Source, URI or URI
+ # String.
+
def <<(obj)
src = case obj
when URI
@@ -37,8 +49,12 @@ class Gem::SourceList
src
end
+ ##
+ # Replaces this SourceList with the sources in +other+ See #<< for
+ # acceptable items in +other+.
+
def replace(other)
- @sources.clear
+ clear
other.each do |x|
self << x
@@ -47,28 +63,58 @@ class Gem::SourceList
self
end
+ ##
+ # Removes all sources from the SourceList.
+
+ def clear
+ @sources.clear
+ end
+
+ ##
+ # Yields each source URI in the list.
+
def each
@sources.each { |s| yield s.uri.to_s }
end
+ ##
+ # Yields each source in the list.
+
def each_source(&b)
@sources.each(&b)
end
+ ##
+ # Returns true if there are no sources in this SourceList.
+
+ def empty?
+ @sources.empty?
+ end
+
def ==(other)
to_a == other
end
+ ##
+ # Returns an Array of source URI Strings.
+
def to_a
@sources.map { |x| x.uri.to_s }
end
alias_method :to_ary, :to_a
+ ##
+ # Returns the first source in the list.
+
def first
@sources.first
end
+ ##
+ # Returns true if this source list includes +other+ which may be a
+ # Gem::Source or a source URI.
+
def include?(other)
if other.kind_of? Gem::Source
@sources.include? other
@@ -77,11 +123,14 @@ class Gem::SourceList
end
end
- def delete(uri)
- if uri.kind_of? Gem::Source
- @sources.delete uri
+ ##
+ # Deletes +source+ from the source list which may be a Gem::Source or a URI.
+
+ def delete source
+ if source.kind_of? Gem::Source
+ @sources.delete source
else
- @sources.delete_if { |x| x.uri.to_s == uri.to_s }
+ @sources.delete_if { |x| x.uri.to_s == source.to_s }
end
end
end
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
index 4bef93351f..22fa68db69 100644
--- a/lib/rubygems/spec_fetcher.rb
+++ b/lib/rubygems/spec_fetcher.rb
@@ -18,6 +18,11 @@ class Gem::SpecFetcher
attr_reader :latest_specs # :nodoc:
##
+ # Sources for this SpecFetcher
+
+ attr_reader :sources # :nodoc:
+
+ ##
# Cache of all released specs
attr_reader :specs # :nodoc:
@@ -37,7 +42,16 @@ class Gem::SpecFetcher
@fetcher = fetcher
end
- def initialize
+ ##
+ # Creates a new SpecFetcher. Ordinarily you want to use
+ # Gem::SpecFetcher::fetcher which uses the Gem.sources.
+ #
+ # If you need to retrieve specifications from a different +source+, you can
+ # send it as an argument.
+
+ def initialize sources = nil
+ @sources = sources || Gem.sources
+
@update_cache =
begin
File.stat(Gem.user_home).uid == Process.uid
@@ -197,7 +211,7 @@ class Gem::SpecFetcher
errors = []
list = {}
- Gem.sources.each_source do |source|
+ @sources.each_source do |source|
begin
names = case type
when :latest
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index 6b8528f238..5330c108ba 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -856,12 +856,8 @@ class Gem::Specification < Gem::BasicSpecification
# this resets the list of known specs.
def self.dirs= dirs
- # TODO: find extra calls to dir=
- # warn "NOTE: dirs= called from #{caller.first} for #{dirs.inspect}"
-
self.reset
- # ugh
@@dirs = Array(dirs).map { |dir| File.join dir, "specifications" }
end
@@ -1105,9 +1101,6 @@ class Gem::Specification < Gem::BasicSpecification
# Removes +spec+ from the known specs.
def self.remove_spec spec
- # TODO: beat on the tests
- raise "wtf: #{spec.full_name} not in #{all_names.inspect}" unless
- _all.include? spec
_all.delete spec
stubs.delete_if { |s| s.full_name == spec.full_name }
end
@@ -1400,7 +1393,7 @@ class Gem::Specification < Gem::BasicSpecification
# Returns the build_args used to install the gem
def build_args
- if File.exist? build_info_file
+ if File.exists? build_info_file
File.readlines(build_info_file).map { |x| x.strip }
else
[]
@@ -1788,6 +1781,7 @@ class Gem::Specification < Gem::BasicSpecification
end
def init_with coder # :nodoc:
+ @installed_by_version ||= nil
yaml_initialize coder.tag, coder.map
end
@@ -2293,9 +2287,9 @@ class Gem::Specification < Gem::BasicSpecification
end
end
- if defined?(@installed_by_version) && @installed_by_version then
+ if @installed_by_version then
result << nil
- result << " s.installed_by_version = \"#{Gem::VERSION}\""
+ result << " s.installed_by_version = \"#{Gem::VERSION}\" if s.respond_to? :installed_by_version"
end
unless dependencies.empty? then
@@ -2488,7 +2482,6 @@ class Gem::Specification < Gem::BasicSpecification
end
end
- # FIX: uhhhh single element array.each?
[:authors].each do |field|
val = self.send field
raise Gem::InvalidSpecificationException, "#{field} may not be empty" if
@@ -2540,7 +2533,6 @@ licenses is empty. Use a license abbreviation from:
# reject lazy developers:
- # FIX: Doesn't this just evaluate to "FIXME" or "TODO"?
lazy = '"FIxxxXME" or "TOxxxDO"'.gsub(/xxx/, '')
unless authors.grep(/FI XME|TO DO/x).empty? then
@@ -2586,19 +2578,80 @@ licenses is empty. Use a license abbreviation from:
warning "#{executable_path} is missing #! line" unless shebang
end
+ validate_dependencies
+
+ true
+ ensure
+ if $! or @warnings > 0 then
+ alert_warning "See http://guides.rubygems.org/specification-reference/ for help"
+ end
+ end
+
+ ##
+ # Checks that dependencies use requirements as we recommend. Warnings are
+ # issued when dependencies are open-ended or overly strict for semantic
+ # versioning.
+
+ def validate_dependencies # :nodoc:
+ seen = {}
+
dependencies.each do |dep|
+ if prev = seen[dep.name] then
+ raise Gem::InvalidSpecificationException, <<-MESSAGE
+duplicate dependency on #{dep}, (#{prev.requirement}) use:
+ add_runtime_dependency '#{dep.name}', '#{dep.requirement}', '#{prev.requirement}'
+ MESSAGE
+ end
+
+ seen[dep.name] = dep
+
prerelease_dep = dep.requirements_list.any? do |req|
Gem::Requirement.new(req).prerelease?
end
warning "prerelease dependency on #{dep} is not recommended" if
prerelease_dep
- end
- true
- ensure
- if $! or @warnings > 0 then
- alert_warning "See http://guides.rubygems.org/specification-reference/ for help"
+ overly_strict = dep.requirement.requirements.length == 1 &&
+ dep.requirement.requirements.any? do |op, version|
+ op == '~>' and
+ not version.prerelease? and
+ version.segments.length > 2
+ end
+
+ if overly_strict then
+ _, dep_version = dep.requirement.requirements.first
+
+ base = dep_version.segments.first 2
+
+ warning <<-WARNING
+pessimistic dependency on #{dep} may be overly strict
+ if #{dep.name} is semantically versioned, use:
+ add_#{dep.type}_dependency '#{dep.name}', '~> #{base.join '.'}', '>= #{dep_version}'
+ WARNING
+ end
+
+ open_ended = dep.requirement.requirements.all? do |op, version|
+ not version.prerelease? and (op == '>' or op == '>=')
+ end
+
+ if open_ended then
+ op, dep_version = dep.requirement.requirements.first
+
+ base = dep_version.segments.first 2
+
+ bugfix = if op == '>' then
+ ", '> #{dep_version}'"
+ elsif op == '>=' and base != dep_version.segments then
+ ", '>= #{dep_version}'"
+ end
+
+ warning <<-WARNING
+open-ended dependency on #{dep} is not recommended
+ if #{dep.name} is semantically versioned, use:
+ add_#{dep.type}_dependency '#{dep.name}', '~> #{base.join '.'}'#{bugfix}
+ WARNING
+ end
end
end
@@ -2633,7 +2686,10 @@ licenses is empty. Use a license abbreviation from:
return @version
end
- # FIX: have this handle the platform/new_platform/original_platform bullshit
+ def stubbed?
+ false
+ end
+
def yaml_initialize(tag, vals) # :nodoc:
vals.each do |ivar, val|
case ivar
@@ -2667,6 +2723,8 @@ licenses is empty. Use a license abbreviation from:
instance_variable_set "@#{attribute}", value
end
+
+ @installed_by_version ||= nil
end
def warning statement # :nodoc:
diff --git a/lib/rubygems/ssl_certs/DigiCertHighAssuranceEVRootCA.pem b/lib/rubygems/ssl_certs/DigiCertHighAssuranceEVRootCA.pem
new file mode 100644
index 0000000000..9e6810ab70
--- /dev/null
+++ b/lib/rubygems/ssl_certs/DigiCertHighAssuranceEVRootCA.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
+RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
+PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
+xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
+Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
+hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
+EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
+MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
+FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
+nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
+eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
+hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
+Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
+vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
++OkuE6N36B9K
+-----END CERTIFICATE-----
diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb
index de85c0fceb..08109c6405 100644
--- a/lib/rubygems/test_case.rb
+++ b/lib/rubygems/test_case.rb
@@ -241,6 +241,8 @@ class Gem::TestCase < MiniTest::Unit::TestCase
@orig_ENV_HOME = ENV['HOME']
ENV['HOME'] = @userhome
Gem.instance_variable_set :@user_home, nil
+ Gem.send :remove_instance_variable, :@ruby_version if
+ Gem.instance_variables.include? :@ruby_version
FileUtils.mkdir_p @gemhome
FileUtils.mkdir_p @userhome
@@ -376,7 +378,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
gem = File.join @tempdir, "gems", "#{spec.full_name}.gem"
- unless File.exist? gem
+ unless File.exists? gem
use_ui Gem::MockGemUi.new do
Dir.chdir @tempdir do
Gem::Package.build spec
@@ -898,14 +900,35 @@ Also, a list:
spec_fetcher.prerelease_specs[@uri] << spec.name_tuple
end
- v = Gem.marshal_version
+ # HACK for test_download_to_cache
+ unless Gem::RemoteFetcher === @fetcher then
+ v = Gem.marshal_version
- Gem::Specification.each do |spec|
- path = "#{@gem_repo}quick/Marshal.#{v}/#{spec.original_name}.gemspec.rz"
- data = Marshal.dump spec
- data_deflate = Zlib::Deflate.deflate data
- @fetcher.data[path] = data_deflate
- end unless Gem::RemoteFetcher === @fetcher # HACK for test_download_to_cache
+ specs = all.map { |spec| spec.name_tuple }
+ s_zip = util_gzip Marshal.dump Gem::NameTuple.to_basic specs
+
+ latest_specs = Gem::Specification.latest_specs.map do |spec|
+ spec.name_tuple
+ end
+
+ l_zip = util_gzip Marshal.dump Gem::NameTuple.to_basic latest_specs
+
+ prerelease_specs = prerelease.map { |spec| spec.name_tuple }
+ p_zip = util_gzip Marshal.dump Gem::NameTuple.to_basic prerelease_specs
+
+ @fetcher.data["#{@gem_repo}specs.#{v}.gz"] = s_zip
+ @fetcher.data["#{@gem_repo}latest_specs.#{v}.gz"] = l_zip
+ @fetcher.data["#{@gem_repo}prerelease_specs.#{v}.gz"] = p_zip
+
+ v = Gem.marshal_version
+
+ Gem::Specification.each do |spec|
+ path = "#{@gem_repo}quick/Marshal.#{v}/#{spec.original_name}.gemspec.rz"
+ data = Marshal.dump spec
+ data_deflate = Zlib::Deflate.deflate data
+ @fetcher.data[path] = data_deflate
+ end
+ end
nil # force errors
end
@@ -1089,6 +1112,62 @@ Also, a list:
end
##
+ # Creates a SpecFetcher pre-filled with the gems or specs defined in the
+ # block.
+ #
+ # Yields a +fetcher+ object that responds to +spec+ and +gem+. +spec+ adds
+ # a specification to the SpecFetcher while +gem+ adds both a specification
+ # and the gem data to the RemoteFetcher so the built gem can be downloaded.
+ #
+ # If only the a-3 gem is supposed to be downloaded you can save setup
+ # time by creating only specs for the other versions:
+ #
+ # spec_fetcher do |fetcher|
+ # fetcher.spec 'a', 1
+ # fetcher.spec 'a', 2, 'b' => 3 # dependency on b = 3
+ # fetcher.gem 'a', 3 do |spec|
+ # # spec is a Gem::Specification
+ # # ...
+ # end
+ # end
+
+ def spec_fetcher
+ gems = {}
+
+ fetcher = Object.new
+ fetcher.instance_variable_set :@test, self
+ fetcher.instance_variable_set :@gems, gems
+
+ def fetcher.gem name, version, dependencies = nil, &block
+ spec, gem = @test.util_gem name, version, dependencies, &block
+
+ @gems[spec] = gem
+
+ spec
+ end
+
+ def fetcher.spec name, version, dependencies = nil, &block
+ spec = @test.util_spec name, version, dependencies, &block
+
+ @gems[spec] = nil
+
+ spec
+ end
+
+ yield fetcher
+
+ util_setup_fake_fetcher unless @fetcher
+ util_setup_spec_fetcher(*gems.keys)
+
+ gems.each do |spec, gem|
+ next unless gem
+
+ @fetcher.data["http://gems.example.com/gems/#{spec.file_name}"] =
+ Gem.read_binary(gem)
+ end
+ end
+
+ ##
# Construct a new Gem::Version.
def v string
diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb
index 15e72292b1..fda8b0b5d4 100644
--- a/lib/rubygems/version.rb
+++ b/lib/rubygems/version.rb
@@ -145,8 +145,6 @@ class Gem::Version
include Comparable
- # FIX: These are only used once, in .correct?. Do they deserve to be
- # constants?
VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?' # :nodoc:
ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/ # :nodoc:
diff --git a/test/rubygems/test_bundled_ca.rb b/test/rubygems/test_bundled_ca.rb
index d2ccdaf484..711cd1b8f2 100644
--- a/test/rubygems/test_bundled_ca.rb
+++ b/test/rubygems/test_bundled_ca.rb
@@ -34,7 +34,7 @@ class TestBundledCA < Gem::TestCase
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.cert_store = bundled_certificate_store
http.get('/')
- rescue Errno::ENOENT
+ rescue Errno::ENOENT, Errno::ETIMEDOUT
skip "#{host} seems offline, I can't tell whether ssl would work."
rescue OpenSSL::SSL::SSLError => e
# Only fail for certificate verification errors
diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb
index 88d0b1c432..f42225635e 100644
--- a/test/rubygems/test_gem.rb
+++ b/test/rubygems/test_gem.rb
@@ -420,12 +420,13 @@ class TestGem < Gem::TestCase
end
def test_self_latest_spec_for
- a1 = quick_spec 'a', 1
- a2 = quick_spec 'a', 2
- a3a = quick_spec 'a', '3.a'
+ a2 = nil
- util_setup_fake_fetcher
- util_setup_spec_fetcher a1, a2, a3a
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', '3.a'
+ a2 = fetcher.spec 'a', 2
+ end
spec = Gem.latest_spec_for 'a'
@@ -433,12 +434,11 @@ class TestGem < Gem::TestCase
end
def test_self_latest_rubygems_version
- r1 = quick_spec 'rubygems-update', '1.8.23'
- r2 = quick_spec 'rubygems-update', '1.8.24'
- r3 = quick_spec 'rubygems-update', '2.0.0.preview3'
-
- util_setup_fake_fetcher
- util_setup_spec_fetcher r1, r2, r3
+ spec_fetcher do |fetcher|
+ fetcher.spec 'rubygems-update', '1.8.23'
+ fetcher.spec 'rubygems-update', '1.8.24'
+ fetcher.spec 'rubygems-update', '2.0.0.preview3'
+ end
version = Gem.latest_rubygems_version
@@ -446,12 +446,11 @@ class TestGem < Gem::TestCase
end
def test_self_latest_version_for
- a1 = quick_spec 'a', 1
- a2 = quick_spec 'a', 2
- a3a = quick_spec 'a', '3.a'
-
- util_setup_fake_fetcher
- util_setup_spec_fetcher a1, a2, a3a
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2
+ fetcher.spec 'a', '3.a'
+ end
version = Gem.latest_version_for 'a'
diff --git a/test/rubygems/test_gem_commands_dependency_command.rb b/test/rubygems/test_gem_commands_dependency_command.rb
index a5670e7575..99b04bcd19 100644
--- a/test/rubygems/test_gem_commands_dependency_command.rb
+++ b/test/rubygems/test_gem_commands_dependency_command.rb
@@ -168,17 +168,10 @@ ERROR: Only reverse dependencies for local gems are supported.
end
def test_execute_remote
- foo = quick_gem 'foo' do |gem|
- gem.add_dependency 'bar', '> 1'
+ spec_fetcher do |fetcher|
+ fetcher.spec 'foo', 2, 'bar' => '> 1'
end
- @fetcher = Gem::FakeFetcher.new
- Gem::RemoteFetcher.fetcher = @fetcher
-
- util_setup_spec_fetcher foo
-
- FileUtils.rm File.join(@gemhome, 'specifications', foo.spec_name)
-
@cmd.options[:args] = %w[foo]
@cmd.options[:domain] = :remote
diff --git a/test/rubygems/test_gem_commands_install_command.rb b/test/rubygems/test_gem_commands_install_command.rb
index 625d85e372..55246502a5 100644
--- a/test/rubygems/test_gem_commands_install_command.rb
+++ b/test/rubygems/test_gem_commands_install_command.rb
@@ -855,4 +855,14 @@ ERROR: Possible alternatives: non_existent_with_hint
assert_equal 'gem.deps.rb', @cmd.options[:gemdeps]
end
+ def test_handle_options_without
+ @cmd.handle_options %w[--without test]
+
+ assert_equal [:test], @cmd.options[:without_groups]
+
+ @cmd.handle_options %w[--without test,development]
+
+ assert_equal [:test, :development], @cmd.options[:without_groups]
+ end
+
end
diff --git a/test/rubygems/test_gem_commands_outdated_command.rb b/test/rubygems/test_gem_commands_outdated_command.rb
index 72696d6549..eee273af5c 100644
--- a/test/rubygems/test_gem_commands_outdated_command.rb
+++ b/test/rubygems/test_gem_commands_outdated_command.rb
@@ -14,13 +14,10 @@ class TestGemCommandsOutdatedCommand < Gem::TestCase
end
def test_execute
- remote_10 = quick_spec 'foo', '1.0'
- remote_20 = quick_spec 'foo', '2.0'
-
- Gem::RemoteFetcher.fetcher = @fetcher = Gem::FakeFetcher.new
-
- util_clear_gems
- util_setup_spec_fetcher remote_10, remote_20
+ spec_fetcher do |fetcher|
+ fetcher.spec 'foo', '1.0'
+ fetcher.spec 'foo', '2.0'
+ end
quick_gem 'foo', '0.1'
quick_gem 'foo', '0.2'
diff --git a/test/rubygems/test_gem_commands_specification_command.rb b/test/rubygems/test_gem_commands_specification_command.rb
index 252d0bdd55..fb28302cc3 100644
--- a/test/rubygems/test_gem_commands_specification_command.rb
+++ b/test/rubygems/test_gem_commands_specification_command.rb
@@ -152,14 +152,9 @@ class TestGemCommandsSpecificationCommand < Gem::TestCase
end
def test_execute_remote
- foo = quick_gem 'foo'
-
- @fetcher = Gem::FakeFetcher.new
- Gem::RemoteFetcher.fetcher = @fetcher
-
- util_setup_spec_fetcher foo
-
- FileUtils.rm File.join(@gemhome, 'specifications', foo.spec_name)
+ spec_fetcher do |fetcher|
+ fetcher.spec 'foo', 1
+ end
@cmd.options[:args] = %w[foo]
@cmd.options[:domain] = :remote
@@ -173,16 +168,10 @@ class TestGemCommandsSpecificationCommand < Gem::TestCase
end
def test_execute_remote_with_version
- foo1 = quick_gem 'foo', "1"
- foo2 = quick_gem 'foo', "2"
-
- @fetcher = Gem::FakeFetcher.new
- Gem::RemoteFetcher.fetcher = @fetcher
-
- util_setup_spec_fetcher foo1, foo2
-
- FileUtils.rm File.join(@gemhome, 'specifications', foo1.spec_name)
- FileUtils.rm File.join(@gemhome, 'specifications', foo2.spec_name)
+ spec_fetcher do |fetcher|
+ fetcher.spec 'foo', "1"
+ fetcher.spec 'foo', "2"
+ end
@cmd.options[:args] = %w[foo]
@cmd.options[:version] = "1"
@@ -198,16 +187,12 @@ class TestGemCommandsSpecificationCommand < Gem::TestCase
end
def test_execute_remote_without_prerelease
- foo = new_spec 'foo', '2.0.0'
- foo_pre = new_spec 'foo', '2.0.1.pre'
-
- install_specs foo, foo_pre
+ spec_fetcher do |fetcher|
+ foo = fetcher.spec 'foo', '2.0.0'
+ foo_pre = fetcher.spec 'foo', '2.0.1.pre'
- @fetcher = Gem::FakeFetcher.new
- Gem::RemoteFetcher.fetcher = @fetcher
-
- util_setup_spec_fetcher foo
- util_setup_spec_fetcher foo_pre
+ install_specs foo, foo_pre
+ end
@cmd.options[:args] = %w[foo]
@cmd.options[:domain] = :remote
@@ -225,16 +210,12 @@ class TestGemCommandsSpecificationCommand < Gem::TestCase
end
def test_execute_remote_with_prerelease
- foo = new_spec 'foo', '2.0.0'
- foo_pre = new_spec 'foo', '2.0.1.pre'
+ spec_fetcher do |fetcher|
+ foo = fetcher.spec 'foo', '2.0.0'
+ foo_pre = fetcher.spec 'foo', '2.0.1.pre'
- install_specs foo, foo_pre
-
- @fetcher = Gem::FakeFetcher.new
- Gem::RemoteFetcher.fetcher = @fetcher
-
- util_setup_spec_fetcher foo
- util_setup_spec_fetcher foo_pre
+ install_specs foo, foo_pre
+ end
@cmd.options[:args] = %w[foo]
@cmd.options[:domain] = :remote
diff --git a/test/rubygems/test_gem_dependency_installer.rb b/test/rubygems/test_gem_dependency_installer.rb
index 8433be6bd0..f8c3dd493b 100644
--- a/test/rubygems/test_gem_dependency_installer.rb
+++ b/test/rubygems/test_gem_dependency_installer.rb
@@ -682,7 +682,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst.install 'b'
end
- expected = "Unable to resolve dependency: b (= 1) requires a (>= 0)"
+ expected = "Unable to resolve dependency: 'b (= 1)' requires 'a (>= 0)'"
assert_equal expected, e.message
end
@@ -816,6 +816,17 @@ class TestGemDependencyInstaller < Gem::TestCase
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
end
+ def test_install_platform_is_ignored_when_a_file_is_specified
+ _, a_gem = util_gem 'a', '1' do |s|
+ s.platform = Gem::Platform.new %w[cpu other_platform 1]
+ end
+
+ inst = Gem::DependencyInstaller.new :domain => :local
+ inst.install a_gem
+
+ assert_equal %w[a-1-cpu-other_platform-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
if defined? OpenSSL then
def test_install_security_policy
util_setup_gems
@@ -904,6 +915,29 @@ class TestGemDependencyInstaller < Gem::TestCase
assert_equal Gem::Source.new(@gem_repo), s.source
end
+ def test_find_spec_by_name_and_version_wildcard
+ util_gem 'a', 1
+ FileUtils.mv 'gems/a-1.gem', @tempdir
+
+ FileUtils.touch 'rdoc.gem'
+
+ inst = Gem::DependencyInstaller.new
+
+ available = inst.find_spec_by_name_and_version('*.gem')
+
+ assert_equal %w[a-1], available.each_spec.map { |spec| spec.full_name }
+ end
+
+ def test_find_spec_by_name_and_version_wildcard_bad_gem
+ FileUtils.touch 'rdoc.gem'
+
+ inst = Gem::DependencyInstaller.new
+
+ assert_raises Gem::Package::FormatError do
+ inst.find_spec_by_name_and_version '*.gem'
+ end
+ end
+
def test_find_spec_by_name_and_version_bad_gem
FileUtils.touch 'rdoc.gem'
diff --git a/test/rubygems/test_gem_dependency_resolution_error.rb b/test/rubygems/test_gem_dependency_resolution_error.rb
new file mode 100644
index 0000000000..7f8cf0c5cc
--- /dev/null
+++ b/test/rubygems/test_gem_dependency_resolution_error.rb
@@ -0,0 +1,28 @@
+require 'rubygems/test_case'
+
+class TestGemDependencyResolutionError < Gem::TestCase
+
+ def setup
+ super
+
+ @DR = Gem::DependencyResolver
+
+ @spec = quick_spec 'a', 2
+
+ @a1_req = @DR::DependencyRequest.new dep('a', '= 1'), nil
+ @a2_req = @DR::DependencyRequest.new dep('a', '= 2'), nil
+
+ @activated = @DR::ActivationRequest.new @spec, @a2_req
+
+ @conflict = @DR::DependencyConflict.new @a1_req, @activated
+
+ @error = Gem::DependencyResolutionError.new @conflict
+ end
+
+ def test_message
+ assert_match %r%^conflicting dependencies a \(= 1\) and a \(= 2\)$%,
+ @error.message
+ end
+
+end
+
diff --git a/test/rubygems/test_gem_dependency_resolver.rb b/test/rubygems/test_gem_dependency_resolver.rb
index bd349996c1..7d968494b8 100644
--- a/test/rubygems/test_gem_dependency_resolver.rb
+++ b/test/rubygems/test_gem_dependency_resolver.rb
@@ -3,6 +3,12 @@ require 'rubygems/dependency_resolver'
class TestGemDependencyResolver < Gem::TestCase
+ def setup
+ super
+
+ @DR = Gem::DependencyResolver
+ end
+
def make_dep(name, *req)
Gem::Dependency.new(name, *req)
end
@@ -21,7 +27,58 @@ class TestGemDependencyResolver < Gem::TestCase
assert_equal exp, act, msg
rescue Gem::DependencyResolutionError => e
- flunk "#{e.message}\n#{e.conflict.explanation}"
+ flunk e.message
+ end
+
+ def test_self_compose_sets_multiple
+ index_set = @DR::IndexSet.new
+ vendor_set = @DR::VendorSet.new
+
+ composed = @DR.compose_sets index_set, vendor_set
+
+ assert_kind_of Gem::DependencyResolver::ComposedSet, composed
+
+ assert_equal [index_set, vendor_set], composed.sets
+ end
+
+ def test_self_compose_sets_nil
+ index_set = @DR::IndexSet.new
+
+ composed = @DR.compose_sets index_set, nil
+
+ assert_same index_set, composed
+
+ e = assert_raises ArgumentError do
+ @DR.compose_sets nil
+ end
+
+ assert_equal 'one set in the composition must be non-nil', e.message
+ end
+
+ def test_self_compose_sets_single
+ index_set = @DR::IndexSet.new
+
+ composed = @DR.compose_sets index_set
+
+ assert_same index_set, composed
+ end
+
+ def test_handle_conflict
+ a1 = util_spec 'a', 1
+
+ r1 = Gem::DependencyResolver::DependencyRequest.new dep('a', '= 1'), nil
+ r2 = Gem::DependencyResolver::DependencyRequest.new dep('a', '= 2'), nil
+ r3 = Gem::DependencyResolver::DependencyRequest.new dep('a', '= 3'), nil
+
+ existing = Gem::DependencyResolver::ActivationRequest.new a1, r1, false
+
+ res = Gem::DependencyResolver.new [a1]
+
+ res.handle_conflict r2, existing
+ res.handle_conflict r2, existing
+ res.handle_conflict r3, existing
+
+ assert_equal 2, res.conflicts.length
end
def test_no_overlap_specificly
@@ -71,10 +128,15 @@ class TestGemDependencyResolver < Gem::TestCase
end
def test_picks_best_platform
- is = Gem::DependencyResolver::IndexSpecification
+ is = Gem::DependencyResolver::IndexSpecification
unknown = Gem::Platform.new 'unknown'
- a2_p1 = quick_spec 'a', 2 do |s| s.platform = Gem::Platform.local end
- a3_p2 = quick_spec 'a', 3 do |s| s.platform = unknown end
+ a2_p1 = a3_p2 = nil
+
+ spec_fetcher do |fetcher|
+ a2_p1 = fetcher.spec 'a', 2 do |s| s.platform = Gem::Platform.local end
+ a3_p2 = fetcher.spec 'a', 3 do |s| s.platform = unknown end
+ end
+
v2 = v(2)
v3 = v(3)
source = Gem::Source.new @gem_repo
@@ -183,8 +245,6 @@ class TestGemDependencyResolver < Gem::TestCase
r.resolve
end
- assert_equal "unable to resolve conflicting dependencies 'c (= 2)' and 'c (= 1)'", e.message
-
deps = [make_dep("c", "= 2"), make_dep("c", "= 1")]
assert_equal deps, e.conflicting_dependencies
@@ -209,7 +269,7 @@ class TestGemDependencyResolver < Gem::TestCase
r.resolve
end
- assert_equal "Unable to resolve dependency: (unknown) requires a (>= 0)",
+ assert_equal "Unable to resolve dependency: user requested 'a (>= 0)'",
e.message
assert_equal "a (>= 0)", e.dependency.to_s
@@ -229,6 +289,37 @@ class TestGemDependencyResolver < Gem::TestCase
assert_equal "a (= 3)", e.dependency.to_s
end
+ def test_raises_and_reports_a_toplevel_request_properly
+ a1 = util_spec "a", "1"
+ ad = make_dep "a", "= 3"
+
+ r = Gem::DependencyResolver.new([ad], set(a1))
+
+ e = assert_raises Gem::UnsatisfiableDepedencyError do
+ r.resolve
+ end
+
+ assert_equal "Unable to resolve dependency: user requested 'a (= 3)'",
+ e.message
+ end
+
+ def test_raises_and_reports_an_implicit_request_properly
+ a1 = util_spec "a", "1" do |s|
+ s.add_runtime_dependency 'b', '= 2'
+ end
+
+ ad = make_dep "a", "= 1"
+
+ r = Gem::DependencyResolver.new([ad], set(a1))
+
+ e = assert_raises Gem::UnsatisfiableDepedencyError do
+ r.resolve
+ end
+
+ assert_equal "Unable to resolve dependency: 'a (= 1)' requires 'b (= 2)'",
+ e.message
+ end
+
def test_raises_when_possibles_are_exhausted
a1 = util_spec "a", "1", "c" => ">= 2"
b1 = util_spec "b", "1", "c" => "= 1"
@@ -244,18 +335,22 @@ class TestGemDependencyResolver < Gem::TestCase
r = Gem::DependencyResolver.new([ad, bd], s)
- e = assert_raises Gem::ImpossibleDependenciesError do
+ e = assert_raises Gem::DependencyResolutionError do
r.resolve
end
- assert_match "a-1 requires c (>= 2) but it conflicted", e.message
+ dependency = e.conflict.dependency
- assert_equal "c (>= 2)", e.dependency.to_s
+ assert_equal 'a', dependency.name
+ assert_equal req('>= 0'), dependency.requirement
- s, con = e.conflicts[0]
- assert_equal "c-3", s.full_name
- assert_equal "c (= 1)", con.dependency.to_s
- assert_equal "b-1", con.requester.full_name
+ activated = e.conflict.activated
+ assert_equal 'c-2', activated.full_name
+
+ assert_equal dep('c', '>= 2'), activated.request.dependency
+
+ assert_equal [dep('c', '= 1'), dep('c', '>= 2')],
+ e.conflict.conflicting_dependencies
end
def test_keeps_resolving_after_seeing_satisfied_dep
@@ -312,7 +407,7 @@ class TestGemDependencyResolver < Gem::TestCase
r = Gem::DependencyResolver.new([d1, d2, d3], s)
- assert_raises Gem::ImpossibleDependenciesError do
+ assert_raises Gem::DependencyResolutionError do
r.resolve
end
end
@@ -335,6 +430,42 @@ class TestGemDependencyResolver < Gem::TestCase
end
end
+ def test_resolve_bug_699
+ a1 = util_spec 'a', '1', 'b' => '= 2',
+ 'c' => '~> 1.0.3'
+
+ b1 = util_spec 'b', '2', 'c' => '~> 1.0'
+
+ c1 = util_spec 'c', '1.0.9'
+ c2 = util_spec 'c', '1.1.0'
+ c3 = util_spec 'c', '1.2.0'
+
+ s = set a1, b1, c1, c2, c3
+
+ a_dep = dep 'a', '= 1'
+
+ r = Gem::DependencyResolver.new [a_dep], s
+
+ assert_resolves_to [a1, b1, c1], r
+ end
+
+ def test_resolve_rollback
+ a1 = util_spec 'a', 1
+ a2 = util_spec 'a', 2
+
+ b1 = util_spec 'b', 1, 'a' => '~> 1.0'
+ b2 = util_spec 'b', 2, 'a' => '~> 2.0'
+
+ s = set a1, a2, b1, b2
+
+ a_dep = dep 'a', '~> 1.0'
+ b_dep = dep 'b'
+
+ r = Gem::DependencyResolver.new [a_dep, b_dep], s
+
+ assert_resolves_to [a1, b1], r
+ end
+
# actionmailer 2.3.4
# activemerchant 1.5.0
# activesupport 2.3.5, 2.3.4
@@ -388,5 +519,22 @@ class TestGemDependencyResolver < Gem::TestCase
assert_equal [a1, a1_p1], selected
end
+ def test_raises_and_explains_when_platform_prevents_install
+ a1 = util_spec "a", "1" do |s|
+ s.platform = Gem::Platform.new %w[c p 1]
+ end
+
+ ad = make_dep "a", "= 1"
+
+ r = Gem::DependencyResolver.new([ad], set(a1))
+
+ e = assert_raises Gem::UnsatisfiableDepedencyError do
+ r.resolve
+ end
+
+ assert_match "No match for 'a (= 1)' on this platform. Found: c-p-1",
+ e.message
+ end
+
end
diff --git a/test/rubygems/test_gem_dependency_resolver_activation_request.rb b/test/rubygems/test_gem_dependency_resolver_activation_request.rb
new file mode 100644
index 0000000000..ac554d128c
--- /dev/null
+++ b/test/rubygems/test_gem_dependency_resolver_activation_request.rb
@@ -0,0 +1,63 @@
+require 'rubygems/test_case'
+
+class TestGemDependencyResolverActivationRequest < Gem::TestCase
+
+ def setup
+ super
+
+ @DR = Gem::DependencyResolver
+
+ @dep = @DR::DependencyRequest.new dep('a', '>= 0'), nil
+
+ source = Gem::Source::Local.new
+ platform = Gem::Platform::RUBY
+
+ @a1 = @DR::IndexSpecification.new nil, 'a', v(1), source, platform
+ @a2 = @DR::IndexSpecification.new nil, 'a', v(2), source, platform
+ @a3 = @DR::IndexSpecification.new nil, 'a', v(3), source, platform
+
+ @req = @DR::ActivationRequest.new @a3, @dep, [@a1, @a2]
+ end
+
+ def test_inspect
+ assert_match 'a-3', @req.inspect
+ assert_match 'from a (>= 0)', @req.inspect
+ assert_match '(others possible: a-1, a-2)', @req.inspect
+ end
+
+ def test_inspect_legacy
+ req = @DR::ActivationRequest.new @a3, @dep, true
+
+ assert_match '(others possible)', req.inspect
+
+ req = @DR::ActivationRequest.new @a3, @dep, false
+
+ refute_match '(others possible)', req.inspect
+ end
+
+ def test_installed_eh
+ v_spec = Gem::DependencyResolver::VendorSpecification.new nil, @a3
+
+ @req = @DR::ActivationRequest.new v_spec, @dep, [@a1, @a2]
+
+ assert @req.installed?
+ end
+
+ def test_others_possible_eh
+ assert @req.others_possible?
+
+ req = @DR::ActivationRequest.new @a3, @dep, []
+
+ refute req.others_possible?
+
+ req = @DR::ActivationRequest.new @a3, @dep, true
+
+ assert req.others_possible?
+
+ req = @DR::ActivationRequest.new @a3, @dep, false
+
+ refute req.others_possible?
+ end
+
+end
+
diff --git a/test/rubygems/test_gem_dependency_resolver_api_set.rb b/test/rubygems/test_gem_dependency_resolver_api_set.rb
index c1e7eda191..ef99b6ca7f 100644
--- a/test/rubygems/test_gem_dependency_resolver_api_set.rb
+++ b/test/rubygems/test_gem_dependency_resolver_api_set.rb
@@ -7,73 +7,20 @@ class TestGemDependencyResolverAPISet < Gem::TestCase
super
@DR = Gem::DependencyResolver
-
- @api_set = @DR::APISet.new
- @uri = 'https://rubygems.org/api/v1/dependencies'
- @fetcher = Gem::FakeFetcher.new
- Gem::RemoteFetcher.fetcher = @fetcher
- end
-
- def test_find_all
- b_entry = {
- :name => 'b',
- :number => '2',
- :platform => 'ruby',
- :dependencies => [['a', '>= 0']],
- }
-
- @fetcher.data["#{@uri}?gems=b"] = Marshal.dump [b_entry]
-
- b_req = @DR::DependencyRequest.new dep('b', '>= 0'), nil
-
- expected = [
- @DR::APISpecification.new(@api_set, b_entry)
- ]
-
- assert_equal expected, @api_set.find_all(b_req)
end
- def test_prefetch
- b_entry = {
- :name => 'b',
- :number => '2',
- :platform => 'ruby',
- :dependencies => [['a', '>= 0']],
- }
+ def test_initialize
+ set = @DR::APISet.new
- a_entry = {
- :name => 'a',
- :number => '2',
- :platform => 'ruby',
- :dependencies => [],
- }
-
- @fetcher.data["#{@uri}?gems=a,b"] = Marshal.dump [a_entry, b_entry]
-
- a_req = @DR::DependencyRequest.new dep('a', '>= 0'), nil
- b_req = @DR::DependencyRequest.new dep('b', '>= 0'), nil
-
- @api_set.prefetch([b_req, a_req])
-
- assert_equal [a_entry], @api_set.versions('a')
- assert_equal [b_entry], @api_set.versions('b')
+ assert_equal URI('https://rubygems.org/api/v1/dependencies'),
+ set.dep_uri
end
- def test_versions_cache
- entry = {
- :name => 'b',
- :number => '2',
- :platform => 'ruby',
- :dependencies => [['a', '>= 0']],
- }
-
- @fetcher.data["#{@uri}?gems=b"] = Marshal.dump [entry]
-
- assert_equal [entry], @api_set.versions('b')
-
- @fetcher.data["#{@uri}?gems=b"] = 'garbage'
+ def test_initialize_uri
+ set = @DR::APISet.new @gem_repo
- assert_equal [entry], @api_set.versions('b'), 'version data must be cached'
+ assert_equal URI('http://gems.example.com/'),
+ set.dep_uri
end
end
diff --git a/test/rubygems/test_gem_dependency_resolver_best_set.rb b/test/rubygems/test_gem_dependency_resolver_best_set.rb
new file mode 100644
index 0000000000..20fbf4514f
--- /dev/null
+++ b/test/rubygems/test_gem_dependency_resolver_best_set.rb
@@ -0,0 +1,31 @@
+require 'rubygems/test_case'
+require 'rubygems/dependency_resolver'
+
+class TestGemDependencyResolverBestSet < Gem::TestCase
+
+ def setup
+ super
+
+ @DR = Gem::DependencyResolver
+ end
+
+ def test_find_all_index
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2
+ fetcher.spec 'b', 1
+ end
+
+ set = @DR::BestSet.new
+
+ dependency = dep 'a', '~> 1'
+
+ req = @DR::DependencyRequest.new dependency, nil
+
+ found = set.find_all req
+
+ assert_equal %w[a-1], found.map { |s| s.full_name }
+ end
+
+end
+
diff --git a/test/rubygems/test_gem_dependency_resolver_dependency_conflict.rb b/test/rubygems/test_gem_dependency_resolver_dependency_conflict.rb
index 5043865e5c..964997568a 100644
--- a/test/rubygems/test_gem_dependency_resolver_dependency_conflict.rb
+++ b/test/rubygems/test_gem_dependency_resolver_dependency_conflict.rb
@@ -20,6 +20,26 @@ class TestGemDependencyResolverDependencyConflict < Gem::TestCase
assert_equal expected, conflict.explanation
end
+ def test_explanation_user_request
+ @DR = Gem::DependencyResolver
+
+ spec = quick_spec 'a', 2
+
+ a1_req = @DR::DependencyRequest.new dep('a', '= 1'), nil
+ a2_req = @DR::DependencyRequest.new dep('a', '= 2'), nil
+
+ activated = @DR::ActivationRequest.new spec, a2_req
+
+ conflict = @DR::DependencyConflict.new a1_req, activated
+
+ expected = <<-EXPECTED
+ Activated a-2 instead of (= 1) via:
+ user request (gem command or Gemfile)
+ EXPECTED
+
+ assert_equal expected, conflict.explanation
+ end
+
def test_request_path
root =
dependency_request dep('net-ssh', '>= 2.0.13'), 'rye', '0.9.8'
diff --git a/test/rubygems/test_gem_dependency_resolver_dependency_request.rb b/test/rubygems/test_gem_dependency_resolver_dependency_request.rb
new file mode 100644
index 0000000000..f5b3a6960e
--- /dev/null
+++ b/test/rubygems/test_gem_dependency_resolver_dependency_request.rb
@@ -0,0 +1,20 @@
+require 'rubygems/test_case'
+
+class TestGemDependencyResolverDependencyRequest < Gem::TestCase
+
+ def setup
+ super
+
+ @DR = Gem::DependencyResolver::DependencyRequest
+ end
+
+ def test_requirement
+ dependency = dep 'a', '>= 1'
+
+ dr = @DR.new dependency, nil
+
+ assert_equal dependency, dr.dependency
+ end
+
+end
+
diff --git a/test/rubygems/test_gem_dependency_resolver_index_set.rb b/test/rubygems/test_gem_dependency_resolver_index_set.rb
index 82ea486792..83b7cce501 100644
--- a/test/rubygems/test_gem_dependency_resolver_index_set.rb
+++ b/test/rubygems/test_gem_dependency_resolver_index_set.rb
@@ -3,50 +3,26 @@ require 'rubygems/dependency_resolver'
class TestGemDependencyResolverIndexSet < Gem::TestCase
- def test_load_spec
- @fetcher = Gem::FakeFetcher.new
- Gem::RemoteFetcher.fetcher = @fetcher
+ def setup
+ super
- a_2 = quick_spec 'a', 2
- a_2_p = quick_spec 'a', 2 do |s| s.platform = Gem::Platform.local end
-
- Gem::Specification.add_specs a_2, a_2_p
-
- util_setup_spec_fetcher a_2, a_2_p
-
- source = Gem::Source.new @gem_repo
- version = v 2
-
- set = Gem::DependencyResolver::IndexSet.new
-
- spec = set.load_spec 'a', version, Gem::Platform.local, source
-
- assert_equal a_2_p.full_name, spec.full_name
+ @DR = Gem::DependencyResolver
end
- def test_load_spec_cached
- @fetcher = Gem::FakeFetcher.new
- Gem::RemoteFetcher.fetcher = @fetcher
+ def test_initialize
+ set = @DR::IndexSet.new
- a_2 = quick_spec 'a', 2
- a_2_p = quick_spec 'a', 2 do |s| s.platform = Gem::Platform.local end
+ fetcher = set.instance_variable_get :@f
- Gem::Specification.add_specs a_2, a_2_p
-
- util_setup_spec_fetcher a_2, a_2_p
-
- source = Gem::Source.new @gem_repo
- version = v 2
-
- set = Gem::DependencyResolver::IndexSet.new
-
- first = set.load_spec 'a', version, Gem::Platform.local, source
+ assert_same Gem::SpecFetcher.fetcher, fetcher
+ end
- util_setup_spec_fetcher # clear
+ def test_initialize_source
+ set = @DR::IndexSet.new 'http://alternate.example'
- second = set.load_spec 'a', version, Gem::Platform.local, source
+ fetcher = set.instance_variable_get :@f
- assert_same first, second
+ refute_same Gem::SpecFetcher.fetcher, fetcher
end
end
diff --git a/test/rubygems/test_gem_dependency_resolver_index_specification.rb b/test/rubygems/test_gem_dependency_resolver_index_specification.rb
index b1e74a3cd1..c8e02ff62d 100644
--- a/test/rubygems/test_gem_dependency_resolver_index_specification.rb
+++ b/test/rubygems/test_gem_dependency_resolver_index_specification.rb
@@ -31,15 +31,12 @@ class TestGemDependencyResolverIndexSpecification < Gem::TestCase
end
def test_spec
- @fetcher = Gem::FakeFetcher.new
- Gem::RemoteFetcher.fetcher = @fetcher
+ a_2_p = nil
- a_2 = quick_spec 'a', 2
- a_2_p = quick_spec 'a', 2 do |s| s.platform = Gem::Platform.local end
-
- Gem::Specification.add_specs a_2, a_2_p
-
- util_setup_spec_fetcher a_2, a_2_p
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2
+ a_2_p = fetcher.spec 'a', 2 do |s| s.platform = Gem::Platform.local end
+ end
source = Gem::Source.new @gem_repo
version = v 2
diff --git a/test/rubygems/test_gem_dependency_resolver_installer_set.rb b/test/rubygems/test_gem_dependency_resolver_installer_set.rb
index b6b50a12fe..516a4d03fe 100644
--- a/test/rubygems/test_gem_dependency_resolver_installer_set.rb
+++ b/test/rubygems/test_gem_dependency_resolver_installer_set.rb
@@ -4,15 +4,12 @@ require 'rubygems/dependency_resolver'
class TestGemDependencyResolverInstallerSet < Gem::TestCase
def test_load_spec
- @fetcher = Gem::FakeFetcher.new
- Gem::RemoteFetcher.fetcher = @fetcher
+ a_2_p = nil
- a_2 = quick_spec 'a', 2
- a_2_p = quick_spec 'a', 2 do |s| s.platform = Gem::Platform.local end
-
- Gem::Specification.add_specs a_2, a_2_p
-
- util_setup_spec_fetcher a_2, a_2_p
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2
+ a_2_p = fetcher.spec 'a', 2 do |s| s.platform = Gem::Platform.local end
+ end
source = Gem::Source.new @gem_repo
version = v 2
diff --git a/test/rubygems/test_gem_dependency_resolver_lock_set.rb b/test/rubygems/test_gem_dependency_resolver_lock_set.rb
new file mode 100644
index 0000000000..6142f2b8d0
--- /dev/null
+++ b/test/rubygems/test_gem_dependency_resolver_lock_set.rb
@@ -0,0 +1,57 @@
+require 'rubygems/test_case'
+require 'rubygems/dependency_resolver'
+
+class TestGemDependencyResolverLockSet < Gem::TestCase
+
+ def setup
+ super
+
+ @source = Gem::Source.new @gem_repo
+
+ @set = Gem::DependencyResolver::LockSet.new @source
+ end
+
+ def test_add
+ @set.add 'a', '2', Gem::Platform::RUBY
+
+ assert_equal %w[a-2], @set.specs.map { |t| t.full_name }
+
+ spec = @set.specs.first
+
+ assert_equal @set, spec.set
+ 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_find_all
+ @set.add 'a', '2', Gem::Platform::RUBY
+ @set.add 'b', '2', Gem::Platform::RUBY
+
+ found = @set.find_all dep 'a'
+
+ assert_equal %w[a-2], found.map { |s| s.full_name }
+ end
+
+ def test_load_spec
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2
+ end
+
+ version = v(2)
+ @set.add 'a', version, Gem::Platform::RUBY
+
+ loaded = @set.load_spec 'a', version, Gem::Platform::RUBY, @source
+
+ assert_kind_of Gem::Specification, loaded
+
+ assert_equal 'a-2', loaded.full_name
+ end
+
+ def test_prefetch
+ assert_respond_to @set, :prefetch
+ end
+
+end
+
diff --git a/test/rubygems/test_gem_dependency_resolver_vendor_set.rb b/test/rubygems/test_gem_dependency_resolver_vendor_set.rb
index b925ff3f47..227cf369c8 100644
--- a/test/rubygems/test_gem_dependency_resolver_vendor_set.rb
+++ b/test/rubygems/test_gem_dependency_resolver_vendor_set.rb
@@ -20,7 +20,7 @@ class TestGemDependencyResolverVendorSet < Gem::TestCase
end
def test_add_vendor_gem_missing
- name, version, directory = vendor_gem
+ name, _, directory = vendor_gem
FileUtils.rm_r directory
@@ -45,15 +45,19 @@ class TestGemDependencyResolverVendorSet < Gem::TestCase
spec = @set.load_spec name, version, Gem::Platform::RUBY, nil
+ source = Gem::Source::Vendor.new directory
+
expected = [
- Gem::DependencyResolver::VendorSpecification.new(@set, spec, nil)
+ Gem::DependencyResolver::VendorSpecification.new(@set, spec, source)
]
assert_equal expected, found
end
def test_load_spec
- assert_raises KeyError do
+ error = Object.const_defined?(:KeyError) ? KeyError : IndexError
+
+ assert_raises error do
@set.load_spec 'a', v(1), Gem::Platform::RUBY, nil
end
end
diff --git a/test/rubygems/test_gem_dependency_resolver_vendor_specification.rb b/test/rubygems/test_gem_dependency_resolver_vendor_specification.rb
index c1d668c777..77d78d1dc7 100644
--- a/test/rubygems/test_gem_dependency_resolver_vendor_specification.rb
+++ b/test/rubygems/test_gem_dependency_resolver_vendor_specification.rb
@@ -60,12 +60,6 @@ class TestGemDependencyResolverVendorSpecification < Gem::TestCase
assert_equal Gem::Platform::RUBY, v_spec.platform
end
- def test_source
- v_spec = Gem::DependencyResolver::VendorSpecification.new @set, @spec
-
- assert_equal Gem::Source::Vendor.new, v_spec.source
- end
-
def test_version
spec = Gem::Specification.new 'a', 1
diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb
index fe6da708b6..5f36e71807 100644
--- a/test/rubygems/test_gem_remote_fetcher.rb
+++ b/test/rubygems/test_gem_remote_fetcher.rb
@@ -75,12 +75,6 @@ gems:
PROXY_DATA = SERVER_DATA.gsub(/0.4.11/, '0.4.2')
- # don't let 1.8 and 1.9 autotest collide
- RUBY_VERSION =~ /(\d+)\.(\d+)\.(\d+)/
- # don't let parallel runners collide
- PROXY_PORT = process_based_port + 100 + $1.to_i * 100 + $2.to_i * 10 + $3.to_i
- SERVER_PORT = process_based_port + 200 + $1.to_i * 100 + $2.to_i * 10 + $3.to_i
-
DIR = File.expand_path(File.dirname(__FILE__))
def setup
@@ -93,8 +87,8 @@ gems:
self.class.enable_yaml = true
self.class.enable_zip = false
- base_server_uri = "http://localhost:#{SERVER_PORT}"
- @proxy_uri = "http://localhost:#{PROXY_PORT}"
+ base_server_uri = "http://localhost:#{self.class.normal_server_port}"
+ @proxy_uri = "http://localhost:#{self.class.proxy_server_port}"
@server_uri = base_server_uri + "/yaml"
@server_z_uri = base_server_uri + "/yaml.Z"
@@ -712,12 +706,20 @@ gems:
attr_accessor :enable_zip, :enable_yaml
def start_servers
- @normal_server ||= start_server(SERVER_PORT, SERVER_DATA)
- @proxy_server ||= start_server(PROXY_PORT, PROXY_DATA)
+ @normal_server ||= start_server(SERVER_DATA)
+ @proxy_server ||= start_server(PROXY_DATA)
@enable_yaml = true
@enable_zip = false
end
+ def normal_server_port
+ @normal_server[:server].config[:Port]
+ end
+
+ def proxy_server_port
+ @proxy_server[:server].config[:Port]
+ end
+
DIR = File.expand_path(File.dirname(__FILE__))
def start_ssl_server(config = {})
@@ -763,45 +765,45 @@ gems:
private
- def start_server(port, data)
- Thread.new do
+ def start_server(data)
+ null_logger = NilLog.new
+ s = WEBrick::HTTPServer.new(
+ :Port => 0,
+ :DocumentRoot => nil,
+ :Logger => null_logger,
+ :AccessLog => null_logger
+ )
+ s.mount_proc("/kill") { |req, res| s.shutdown }
+ s.mount_proc("/yaml") { |req, res|
+ if @enable_yaml
+ res.body = data
+ res['Content-Type'] = 'text/plain'
+ res['content-length'] = data.size
+ else
+ res.status = "404"
+ res.body = "<h1>NOT FOUND</h1>"
+ res['Content-Type'] = 'text/html'
+ end
+ }
+ s.mount_proc("/yaml.Z") { |req, res|
+ if @enable_zip
+ res.body = Zlib::Deflate.deflate(data)
+ res['Content-Type'] = 'text/plain'
+ else
+ res.status = "404"
+ res.body = "<h1>NOT FOUND</h1>"
+ res['Content-Type'] = 'text/html'
+ end
+ }
+ th = Thread.new do
begin
- null_logger = NilLog.new
- s = WEBrick::HTTPServer.new(
- :Port => port,
- :DocumentRoot => nil,
- :Logger => null_logger,
- :AccessLog => null_logger
- )
- s.mount_proc("/kill") { |req, res| s.shutdown }
- s.mount_proc("/yaml") { |req, res|
- if @enable_yaml
- res.body = data
- res['Content-Type'] = 'text/plain'
- res['content-length'] = data.size
- else
- res.status = "404"
- res.body = "<h1>NOT FOUND</h1>"
- res['Content-Type'] = 'text/html'
- end
- }
- s.mount_proc("/yaml.Z") { |req, res|
- if @enable_zip
- res.body = Zlib::Deflate.deflate(data)
- res['Content-Type'] = 'text/plain'
- else
- res.status = "404"
- res.body = "<h1>NOT FOUND</h1>"
- res['Content-Type'] = 'text/html'
- end
- }
s.start
rescue Exception => ex
- abort ex.message
- puts "ERROR during server thread: #{ex.message}"
+ abort "ERROR during server thread: #{ex.message}"
end
end
- sleep 0.2 # Give the servers time to startup
+ th[:server] = s
+ th
end
def cert(filename)
diff --git a/test/rubygems/test_gem_request_set.rb b/test/rubygems/test_gem_request_set.rb
index 12a1942d54..4aaa15ac68 100644
--- a/test/rubygems/test_gem_request_set.rb
+++ b/test/rubygems/test_gem_request_set.rb
@@ -6,6 +6,8 @@ class TestGemRequestSet < Gem::TestCase
super
Gem::RemoteFetcher.fetcher = @fetcher = Gem::FakeFetcher.new
+
+ @DR = Gem::DependencyResolver
end
def test_gem
@@ -17,6 +19,15 @@ class TestGemRequestSet < Gem::TestCase
assert_equal [Gem::Dependency.new("a", "=2")], rs.dependencies
end
+ def test_gem_duplicate
+ rs = Gem::RequestSet.new
+
+ rs.gem 'a', '1'
+ rs.gem 'a', '2'
+
+ assert_equal [dep('a', '= 1', '= 2')], rs.dependencies
+ end
+
def test_import
rs = Gem::RequestSet.new
rs.gem 'a'
@@ -26,6 +37,26 @@ class TestGemRequestSet < Gem::TestCase
assert_equal [dep('a'), dep('b')], rs.dependencies
end
+ def test_install_from_gemdeps
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ rs = Gem::RequestSet.new
+ installed = []
+
+ Tempfile.open 'gem.deps.rb' do |io|
+ io.puts 'gem "a"'
+ io.flush
+
+ rs.install_from_gemdeps :gemdeps => io.path do |req, installer|
+ installed << req.full_name
+ end
+ end
+
+ assert_includes installed, 'a-2'
+ end
+
def test_load_gemdeps
rs = Gem::RequestSet.new
@@ -41,6 +72,19 @@ class TestGemRequestSet < Gem::TestCase
assert rs.vendor_set
end
+ def test_load_gemdeps_without_groups
+ rs = Gem::RequestSet.new
+
+ Tempfile.open 'gem.deps.rb' do |io|
+ io.puts 'gem "a", :group => :test'
+ io.flush
+
+ rs.load_gemdeps io.path, [:test]
+ end
+
+ assert_empty rs.dependencies
+ end
+
def test_resolve
a = util_spec "a", "2", "b" => ">= 2"
b = util_spec "b", "2"
@@ -56,6 +100,21 @@ class TestGemRequestSet < Gem::TestCase
assert_equal ["a-2", "b-2"], names
end
+ def test_resolve_incompatible
+ a1 = util_spec 'a', 1
+ a2 = util_spec 'a', 2
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a', '= 1'
+ rs.gem 'a', '= 2'
+
+ set = StaticSet.new [a1, a2]
+
+ assert_raises Gem::UnsatisfiableDependencyError do
+ rs.resolve set
+ end
+ end
+
def test_resolve_vendor
a_name, _, a_directory = vendor_gem 'a', 1 do |s|
s.add_dependency 'b', '~> 2.0'
@@ -82,6 +141,9 @@ class TestGemRequestSet < Gem::TestCase
names = res.map { |s| s.full_name }.sort
assert_equal ["a-1", "b-2"], names
+
+ assert_equal [@DR::IndexSet, @DR::VendorSet],
+ rs.sets.map { |set| set.class }
end
def test_sorted_requests
@@ -99,13 +161,10 @@ class TestGemRequestSet < Gem::TestCase
end
def test_install_into
- a, ad = util_gem "a", "1", "b" => "= 1"
- b, bd = util_gem "b", "1"
-
- util_setup_spec_fetcher a, b
-
- @fetcher.data["http://gems.example.com/gems/#{a.file_name}"] = Gem.read_binary(ad)
- @fetcher.data["http://gems.example.com/gems/#{b.file_name}"] = Gem.read_binary(bd)
+ spec_fetcher do |fetcher|
+ fetcher.gem "a", "1", "b" => "= 1"
+ fetcher.gem "b", "1"
+ end
rs = Gem::RequestSet.new
rs.gem "a"
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 5c35484421..367f3ba20a 100644
--- a/test/rubygems/test_gem_request_set_gem_dependency_api.rb
+++ b/test/rubygems/test_gem_request_set_gem_dependency_api.rb
@@ -16,20 +16,56 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
@gda.instance_variable_set :@vendor_set, @vendor_set
end
+ def with_engine_version name, version
+ engine = RUBY_ENGINE if Object.const_defined? :RUBY_ENGINE
+ engine_version_const = "#{Gem.ruby_engine.upcase}_VERSION"
+ engine_version = Object.const_get engine_version_const
+
+ Object.send :remove_const, :RUBY_ENGINE if engine
+ Object.send :remove_const, engine_version_const if name == 'ruby' and
+ Object.const_defined? engine_version_const
+
+ new_engine_version_const = "#{name.upcase}_VERSION"
+ Object.const_set :RUBY_ENGINE, name if name
+ Object.const_set new_engine_version_const, version if version
+
+ Gem.instance_variable_set :@ruby_version, Gem::Version.new(version)
+
+ yield
+
+ ensure
+ Object.send :remove_const, :RUBY_ENGINE if name
+ Object.send :remove_const, new_engine_version_const if version
+
+ Object.send :remove_const, engine_version_const if name == 'ruby' and
+ Object.const_defined? engine_version_const
+
+ Object.const_set :RUBY_ENGINE, engine if engine
+ Object.const_set engine_version_const, engine_version unless
+ Object.const_defined? engine_version_const
+
+ Gem.send :remove_instance_variable, :@ruby_version if
+ Gem.instance_variables.include? :@ruby_version
+ end
+
def test_gem
@gda.gem 'a'
assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[a], @gda.requires['a']
end
def test_gem_group
@gda.gem 'a', :group => :test
- expected = {
- :test => [['a']],
- }
+ assert_equal [dep('a')], @set.dependencies
+ end
- assert_equal expected, @gda.dependency_groups
+ def test_gem_group_without
+ @gda.without_groups << :test
+
+ @gda.gem 'a', :group => :test
assert_empty @set.dependencies
end
@@ -37,14 +73,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
def test_gem_groups
@gda.gem 'a', :groups => [:test, :development]
- expected = {
- :development => [['a']],
- :test => [['a']],
- }
-
- assert_equal expected, @gda.dependency_groups
-
- assert_empty @set.dependencies
+ assert_equal [dep('a')], @set.dependencies
end
def test_gem_path
@@ -59,6 +88,133 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
assert_equal "#{name}-#{version}", loaded.full_name
end
+ def test_gem_platforms
+ with_engine_version 'ruby', '2.0.0' do
+ @gda.gem 'a', :platforms => :ruby
+
+ refute_empty @set.dependencies
+ end
+ end
+
+ def test_gem_platforms_bundler_ruby
+ win_platform, Gem.win_platform = Gem.win_platform?, false
+
+ with_engine_version 'ruby', '2.0.0' do
+ set = Gem::RequestSet.new
+ gda = @GDA.new set, 'gem.deps.rb'
+ gda.gem 'a', :platforms => :ruby
+
+ refute_empty set.dependencies
+ end
+
+ with_engine_version 'rbx', '2.0.0' do
+ set = Gem::RequestSet.new
+ gda = @GDA.new set, 'gem.deps.rb'
+ gda.gem 'a', :platforms => :ruby
+
+ refute_empty set.dependencies
+ end
+
+ with_engine_version 'jruby', '1.7.6' do
+ set = Gem::RequestSet.new
+ gda = @GDA.new set, 'gem.deps.rb'
+ gda.gem 'a', :platforms => :ruby
+
+ assert_empty set.dependencies
+ end
+
+ Gem.win_platform = true
+
+ with_engine_version 'ruby', '2.0.0' do
+ set = Gem::RequestSet.new
+ gda = @GDA.new set, 'gem.deps.rb'
+ gda.gem 'a', :platforms => :ruby
+
+ assert_empty set.dependencies
+ end
+
+ Gem.win_platform = win_platform
+ end
+
+ def test_gem_platforms_engine
+ with_engine_version 'jruby', '1.7.6' do
+ @gda.gem 'a', :platforms => :mri
+
+ assert_empty @set.dependencies
+ end
+ end
+
+ def test_gem_platforms_maglev
+ with_engine_version 'maglev', '1.0.0' do
+ set = Gem::RequestSet.new
+ gda = @GDA.new set, 'gem.deps.rb'
+ gda.gem 'a', :platforms => :ruby
+
+ refute_empty set.dependencies
+
+ set = Gem::RequestSet.new
+ gda = @GDA.new set, 'gem.deps.rb'
+ gda.gem 'a', :platforms => :maglev
+
+ refute_empty set.dependencies
+ end
+ end
+
+ def test_gem_platforms_multiple
+ win_platform, Gem.win_platform = Gem.win_platform?, false
+
+ with_engine_version 'ruby', '2.0.0' do
+ @gda.gem 'a', :platforms => [:mswin, :jruby]
+
+ assert_empty @set.dependencies
+ end
+
+ ensure
+ Gem.win_platform = win_platform
+ end
+
+ def test_gem_platforms_version
+ with_engine_version 'ruby', '2.0.0' do
+ @gda.gem 'a', :platforms => :ruby_18
+
+ assert_empty @set.dependencies
+ end
+ end
+
+ def test_gem_platforms_unknown
+ e = assert_raises ArgumentError do
+ @gda.gem 'a', :platforms => :unknown
+ end
+
+ assert_equal 'unknown platform :unknown', e.message
+ end
+
+ def test_gem_require
+ @gda.gem 'a', :require => %w[b c]
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[b c], @gda.requires['a']
+ end
+
+ def test_gem_require_false
+ @gda.gem 'a', :require => false
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_empty @gda.requires
+ end
+
+ def test_gem_require_without_group
+ @gda.without_groups << :test
+
+ @gda.gem 'a', :group => :test
+
+ assert_empty @set.dependencies
+
+ assert_empty @gda.requires['a']
+ end
+
def test_gem_requirement
@gda.gem 'a', '~> 1.0'
@@ -77,6 +233,31 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
assert_equal [dep('c')], @set.dependencies
end
+ def test_gem_source_mismatch
+ name, _, directory = vendor_gem
+
+ gda = @GDA.new @set, nil
+ gda.gem name
+
+ e = assert_raises ArgumentError do
+ gda.gem name, :path => directory
+ end
+
+ assert_equal "duplicate source path: #{directory} for gem #{name}",
+ e.message
+
+ gda = @GDA.new @set, nil
+ gda.instance_variable_set :@vendor_set, @vendor_set
+ gda.gem name, :path => directory
+
+ e = assert_raises ArgumentError do
+ gda.gem name
+ end
+
+ assert_equal "duplicate source (default) for gem #{name}",
+ e.message
+ end
+
def test_gem_deps_file
assert_equal 'gem.deps.rb', @gda.gem_deps_file
@@ -85,25 +266,22 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
assert_equal 'Gemfile', gda.gem_deps_file
end
- def test_group
- @gda.group :test do
- @gda.gem 'a'
- end
+ def test_gem_group_method
+ groups = []
- assert_equal [['a']], @gda.dependency_groups[:test]
+ @gda.group :a do
+ groups = @gda.send :gem_group, 'a', :group => :b, :groups => [:c, :d]
+ end
- assert_empty @set.dependencies
+ assert_equal [:a, :b, :c, :d], groups.sort_by { |group| group.to_s }
end
- def test_group_multiple
- @gda.group :a do
- @gda.gem 'a', :group => :b, :groups => [:c, :d]
+ def test_group
+ @gda.group :test do
+ @gda.gem 'a'
end
- assert_equal [['a']], @gda.dependency_groups[:a]
- assert_equal [['a']], @gda.dependency_groups[:b]
- assert_equal [['a']], @gda.dependency_groups[:c]
- assert_equal [['a']], @gda.dependency_groups[:d]
+ assert_equal [dep('a')], @set.dependencies
end
def test_load
@@ -121,18 +299,12 @@ end
gda.load
- expected = {
- :test => [['b']],
- }
-
- assert_equal expected, gda.dependency_groups
-
- assert_equal [dep('a')], @set.dependencies
+ assert_equal [dep('a'), dep('b')], @set.dependencies
end
end
def test_name_typo
- assert_same @GDA, Gem::RequestSet::DepedencyAPI
+ assert_same @GDA, Gem::RequestSet::GemDepedencyAPI
end
def test_platform_mswin
@@ -152,11 +324,30 @@ end
end
def test_platforms
+ win_platform, Gem.win_platform = Gem.win_platform?, false
+
@gda.platforms :ruby do
@gda.gem 'a'
end
assert_equal [dep('a')], @set.dependencies
+
+ @gda.platforms :mswin do
+ @gda.gem 'b'
+ end
+
+ assert_equal [dep('a')], @set.dependencies
+
+ Gem.win_platform = true
+
+ @gda.platforms :mswin do
+ @gda.gem 'c'
+ end
+
+ assert_equal [dep('a'), dep('c')], @set.dependencies
+
+ ensure
+ Gem.win_platform = win_platform
end
def test_ruby
@@ -164,8 +355,42 @@ end
end
def test_ruby_engine
- assert @gda.ruby RUBY_VERSION,
- :engine => 'jruby', :engine_version => '1.7.4'
+ with_engine_version 'jruby', '1.7.6' do
+ assert @gda.ruby RUBY_VERSION,
+ :engine => 'jruby', :engine_version => '1.7.6'
+
+ end
+ end
+
+ def test_ruby_engine_mismatch_engine
+ with_engine_version 'ruby', '2.0.0' do
+ e = assert_raises Gem::RubyVersionMismatch do
+ @gda.ruby RUBY_VERSION, :engine => 'jruby', :engine_version => '1.7.4'
+ end
+
+ assert_equal 'Your ruby engine is ruby, but your gem.deps.rb requires jruby',
+ e.message
+ end
+ end
+
+ def test_ruby_engine_mismatch_version
+ with_engine_version 'jruby', '1.7.6' do
+ e = assert_raises Gem::RubyVersionMismatch do
+ @gda.ruby RUBY_VERSION, :engine => 'jruby', :engine_version => '1.7.4'
+ end
+
+ assert_equal 'Your ruby engine version is jruby 1.7.6, but your gem.deps.rb requires jruby 1.7.4',
+ e.message
+ end
+ end
+
+ def test_ruby_engine_no_engine_version
+ e = assert_raises ArgumentError do
+ @gda.ruby RUBY_VERSION, :engine => 'jruby'
+ end
+
+ assert_equal 'you must specify engine_version along with the ruby engine',
+ e.message
end
def test_ruby_mismatch
@@ -173,7 +398,42 @@ end
@gda.ruby '1.8.0'
end
- assert_equal "Your Ruby version is #{RUBY_VERSION}, but your gem.deps.rb specified 1.8.0", e.message
+ assert_equal "Your Ruby version is #{RUBY_VERSION}, but your gem.deps.rb requires 1.8.0", e.message
+ end
+
+ def test_source
+ sources = Gem.sources
+
+ @gda.source 'http://first.example'
+
+ assert_equal %w[http://first.example], Gem.sources
+
+ assert_same sources, Gem.sources
+
+ @gda.source 'http://second.example'
+
+ assert_equal %w[http://first.example http://second.example], Gem.sources
+ end
+
+ def test_with_engine_version
+ version = RUBY_VERSION
+ engine = Gem.ruby_engine
+
+ engine_version_const = "#{Gem.ruby_engine.upcase}_VERSION"
+ engine_version = Object.const_get engine_version_const
+
+ with_engine_version 'other', '1.2.3' do
+ assert_equal 'other', Gem.ruby_engine
+ assert_equal '1.2.3', OTHER_VERSION
+
+ assert_equal version, RUBY_VERSION if engine
+ end
+
+ assert_equal version, RUBY_VERSION
+ assert_equal engine, Gem.ruby_engine
+
+ assert_equal engine_version, Object.const_get(engine_version_const) if
+ engine
end
end
diff --git a/test/rubygems/test_gem_request_set_lockfile.rb b/test/rubygems/test_gem_request_set_lockfile.rb
new file mode 100644
index 0000000000..9e947f54ec
--- /dev/null
+++ b/test/rubygems/test_gem_request_set_lockfile.rb
@@ -0,0 +1,404 @@
+require 'rubygems/test_case'
+require 'rubygems/request_set'
+require 'rubygems/request_set/lockfile'
+
+class TestGemRequestSetLockfile < Gem::TestCase
+
+ def setup
+ super
+
+ Gem::RemoteFetcher.fetcher = @fetcher = Gem::FakeFetcher.new
+
+ util_set_arch 'i686-darwin8.10.1'
+
+ @set = Gem::RequestSet.new
+
+ @vendor_set = Gem::DependencyResolver::VendorSet.new
+
+ @set.instance_variable_set :@vendor_set, @vendor_set
+
+ @gem_deps_file = 'gem.deps.rb'
+
+ @lockfile = Gem::RequestSet::Lockfile.new @set, @gem_deps_file
+ end
+
+ def write_gem_deps gem_deps
+ open @gem_deps_file, 'w' do |io|
+ io.write gem_deps
+ end
+ end
+
+ def write_lockfile lockfile
+ @lock_file = File.expand_path "#{@gem_deps_file}.lock"
+
+ open @lock_file, 'w' do |io|
+ io.write lockfile
+ end
+ end
+
+ def test_get
+ @lockfile.instance_variable_set :@tokens, [:token]
+
+ assert_equal :token, @lockfile.get
+ end
+
+ def test_get_type_mismatch
+ @lockfile.instance_variable_set :@tokens, [[:section, 'x', 5, 1]]
+
+ e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ @lockfile.get :text
+ end
+
+ expected = 'unexpected token [:section, "x"], expected :text (at 5:1)'
+
+ assert_equal expected, e.message
+
+ assert_equal 5, e.line
+ assert_equal 1, e.column
+ assert_equal File.expand_path("#{@gem_deps_file}.lock"), e.path
+ end
+
+ def test_get_type_value_mismatch
+ @lockfile.instance_variable_set :@tokens, [[:section, 'x', 5, 1]]
+
+ e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ @lockfile.get :section, 'y'
+ end
+
+ expected =
+ 'unexpected token [:section, "x"], expected [:section, "y"] (at 5:1)'
+
+ assert_equal expected, e.message
+
+ assert_equal 5, e.line
+ assert_equal 1, e.column
+ assert_equal File.expand_path("#{@gem_deps_file}.lock"), e.path
+ end
+
+ def test_parse
+ write_lockfile <<-LOCKFILE
+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::DependencyResolver::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_peek
+ @lockfile.instance_variable_set :@tokens, [:token]
+
+ assert_equal :token, @lockfile.peek
+
+ assert_equal :token, @lockfile.get
+ end
+
+ def test_skip
+ tokens = [[:token]]
+
+ @lockfile.instance_variable_set :@tokens, tokens
+
+ @lockfile.skip :token
+
+ assert_empty tokens
+ end
+
+ def test_token_pos
+ assert_equal [5, 0], @lockfile.token_pos(5)
+
+ @lockfile.instance_variable_set :@line_pos, 2
+ @lockfile.instance_variable_set :@line, 1
+
+ assert_equal [3, 1], @lockfile.token_pos(5)
+ end
+
+ def test_tokenize
+ write_lockfile <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+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],
+ [:newline, nil, 0, 4],
+ [:section, 'PLATFORMS', 0, 5],
+ [:newline, nil, 9, 5],
+ [:text, Gem::Platform::RUBY, 2, 6],
+ [:newline, nil, 6, 6],
+ [:newline, nil, 0, 7],
+ [:section, 'DEPENDENCIES', 0, 8],
+ [:newline, nil, 12, 8],
+ [:text, 'a', 2, 9],
+ [:newline, nil, 3, 9],
+ ]
+
+ assert_equal expected, @lockfile.tokenize
+ end
+
+ def test_tokenize_conflict_markers
+ write_lockfile '<<<<<<<'
+
+ e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ @lockfile.tokenize
+ end
+
+ assert_equal "your #{@lock_file} contains merge conflict markers (at 0:0)",
+ e.message
+
+ write_lockfile '|||||||'
+
+ e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ @lockfile.tokenize
+ end
+
+ assert_equal "your #{@lock_file} contains merge conflict markers (at 0:0)",
+ e.message
+
+ write_lockfile '======='
+
+ e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ @lockfile.tokenize
+ end
+
+ assert_equal "your #{@lock_file} contains merge conflict markers (at 0:0)",
+ e.message
+
+ write_lockfile '>>>>>>>'
+
+ e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ @lockfile.tokenize
+ end
+
+ assert_equal "your #{@lock_file} contains merge conflict markers (at 0:0)",
+ e.message
+ end
+
+ def test_to_s_gem
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2
+ end
+
+ @set.gem 'a'
+
+ expected = <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_to_s_gem_dependency
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2, 'c' => '>= 0', 'b' => '>= 0'
+ fetcher.spec 'b', 2
+ fetcher.spec 'c', 2
+ end
+
+ @set.gem 'a'
+
+ expected = <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+ b
+ c
+ b (2)
+ c (2)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_to_s_gem_dependency_non_default
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2, 'b' => '>= 1'
+ fetcher.spec 'b', 2
+ end
+
+ @set.gem 'b'
+ @set.gem 'a'
+
+ expected = <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+ b (>= 1)
+ b (2)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a
+ b
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_to_s_gem_dependency_requirement
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2, 'b' => '>= 0'
+ fetcher.spec 'b', 2
+ end
+
+ @set.gem 'a', '>= 1'
+
+ expected = <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+ b
+ b (2)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a (>= 1)
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_to_s_gem_path
+ name, version, directory = vendor_gem
+
+ @vendor_set.add_vendor_gem name, directory
+
+ @set.gem 'a'
+
+ expected = <<-LOCKFILE
+PATH
+ remote: #{directory}
+ specs:
+ #{name} (#{version})
+
+GEM
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a!
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_to_s_gem_path_absolute
+ name, version, directory = vendor_gem
+
+ @vendor_set.add_vendor_gem name, File.expand_path(directory)
+
+ @set.gem 'a'
+
+ expected = <<-LOCKFILE
+PATH
+ remote: #{directory}
+ specs:
+ #{name} (#{version})
+
+GEM
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a!
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_to_s_gem_platform
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2 do |spec|
+ spec.platform = Gem::Platform.local
+ end
+ end
+
+ @set.gem 'a'
+
+ expected = <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2-#{Gem::Platform.local})
+
+PLATFORMS
+ #{Gem::Platform.local}
+
+DEPENDENCIES
+ a
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_unget
+ @lockfile.instance_variable_set :@current_token, :token
+
+ @lockfile.unget
+
+ assert_equal :token, @lockfile.get
+ end
+
+end
+
diff --git a/test/rubygems/test_gem_requirement.rb b/test/rubygems/test_gem_requirement.rb
index 01db08e84f..29a4675bc9 100644
--- a/test/rubygems/test_gem_requirement.rb
+++ b/test/rubygems/test_gem_requirement.rb
@@ -3,6 +3,14 @@ require "rubygems/requirement"
class TestGemRequirement < Gem::TestCase
+ def test_concat
+ r = req '>= 1'
+
+ r.concat ['< 2']
+
+ assert_equal [['>=', v(1)], ['<', v(2)]], r.requirements
+ end
+
def test_equals2
r = req "= 1.2"
assert_equal r, r.dup
@@ -36,6 +44,12 @@ class TestGemRequirement < Gem::TestCase
assert_equal false, r.none?
end
+ def test_for_lockfile
+ assert_equal ' (~> 1.0)', req('~> 1.0').for_lockfile
+
+ assert_nil Gem::Requirement.default.for_lockfile
+ end
+
def test_parse
assert_equal ['=', Gem::Version.new(1)], Gem::Requirement.parse(' 1')
assert_equal ['=', Gem::Version.new(1)], Gem::Requirement.parse('= 1')
diff --git a/test/rubygems/test_gem_server.rb b/test/rubygems/test_gem_server.rb
index d50d3525ff..bf30399db2 100644
--- a/test/rubygems/test_gem_server.rb
+++ b/test/rubygems/test_gem_server.rb
@@ -85,6 +85,30 @@ class TestGemServer < Gem::TestCase
Marshal.load(@res.body)
end
+ def test_latest_specs_gemdirs
+ data = StringIO.new "GET /latest_specs.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
+ dir = "#{@gemhome}2"
+
+ spec = quick_spec 'z', 9
+
+ specs_dir = File.join dir, 'specifications'
+ FileUtils.mkdir_p specs_dir
+
+ open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ io.write spec.to_ruby
+ end
+
+ server = Gem::Server.new dir, process_based_port, false
+
+ @req.parse data
+
+ server.latest_specs @req, @res
+
+ assert_equal 200, @res.status
+
+ assert_equal [['z', v(9), Gem::Platform::RUBY]], Marshal.load(@res.body)
+ end
+
def test_latest_specs_gz
data = StringIO.new "GET /latest_specs.#{Gem.marshal_version}.gz HTTP/1.0\r\n\r\n"
@req.parse data
@@ -120,8 +144,41 @@ class TestGemServer < Gem::TestCase
assert_equal 2, @server.server.listeners.length
end
+ def test_quick_gemdirs
+ data = StringIO.new "GET /quick/Marshal.4.8/z-9.gemspec.rz HTTP/1.0\r\n\r\n"
+ dir = "#{@gemhome}2"
+
+ server = Gem::Server.new dir, process_based_port, false
+
+ @req.parse data
+
+ server.quick @req, @res
+
+ assert_equal 404, @res.status
+
+ spec = quick_spec 'z', 9
+
+ specs_dir = File.join dir, 'specifications'
+
+ FileUtils.mkdir_p specs_dir
+
+ open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ io.write spec.to_ruby
+ end
+
+ data.rewind
+
+ req = WEBrick::HTTPRequest.new :Logger => nil
+ res = WEBrick::HTTPResponse.new :HTTPVersion => '1.0'
+ req.parse data
+
+ server.quick req, res
+
+ assert_equal 200, res.status
+ end
+
def test_quick_missing
- data = StringIO.new "GET /quick/z-9.gemspec.rz HTTP/1.0\r\n\r\n"
+ data = StringIO.new "GET /quick/Marshal.4.8/z-9.gemspec.rz HTTP/1.0\r\n\r\n"
@req.parse data
@server.quick @req, @res
@@ -188,6 +245,29 @@ class TestGemServer < Gem::TestCase
assert_equal 'text/html', @res['content-type']
end
+ def test_root_gemdirs
+ data = StringIO.new "GET / HTTP/1.0\r\n\r\n"
+ dir = "#{@gemhome}2"
+
+ spec = quick_spec 'z', 9
+
+ specs_dir = File.join dir, 'specifications'
+ FileUtils.mkdir_p specs_dir
+
+ open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ io.write spec.to_ruby
+ end
+
+ server = Gem::Server.new dir, process_based_port, false
+
+ @req.parse data
+
+ server.root @req, @res
+
+ assert_equal 200, @res.status
+ assert_match 'z 9', @res.body
+ end
+
def test_specs
data = StringIO.new "GET /specs.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
@req.parse data
@@ -203,6 +283,30 @@ class TestGemServer < Gem::TestCase
Marshal.load(@res.body)
end
+ def test_specs_gemdirs
+ data = StringIO.new "GET /specs.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
+ dir = "#{@gemhome}2"
+
+ spec = quick_spec 'z', 9
+
+ specs_dir = File.join dir, 'specifications'
+ FileUtils.mkdir_p specs_dir
+
+ open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ io.write spec.to_ruby
+ end
+
+ server = Gem::Server.new dir, process_based_port, false
+
+ @req.parse data
+
+ server.specs @req, @res
+
+ assert_equal 200, @res.status
+
+ assert_equal [['z', v(9), Gem::Platform::RUBY]], Marshal.load(@res.body)
+ end
+
def test_specs_gz
data = StringIO.new "GET /specs.#{Gem.marshal_version}.gz HTTP/1.0\r\n\r\n"
@req.parse data
diff --git a/test/rubygems/test_gem_source.rb b/test/rubygems/test_gem_source.rb
index 61fb682001..ed981cbc09 100644
--- a/test/rubygems/test_gem_source.rb
+++ b/test/rubygems/test_gem_source.rb
@@ -68,6 +68,20 @@ class TestGemSource < Gem::TestCase
assert cache_dir !~ /:/, "#{cache_dir} should not contain a :"
end
+ def test_dependency_resolver_set_bundler_api
+ @fetcher.data["#{@gem_repo}api/v1/dependencies"] = 'data'
+
+ set = @source.dependency_resolver_set
+
+ assert_kind_of Gem::DependencyResolver::APISet, set
+ end
+
+ def test_dependency_resolver_set_marshal_api
+ set = @source.dependency_resolver_set
+
+ assert_kind_of Gem::DependencyResolver::IndexSet, set
+ end
+
def test_fetch_spec
spec_uri = "#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}#{@a1.spec_name}"
@fetcher.data["#{spec_uri}.rz"] = util_zip(Marshal.dump(@a1))
diff --git a/test/rubygems/test_gem_source_list.rb b/test/rubygems/test_gem_source_list.rb
index e2d6da62cd..43db204a50 100644
--- a/test/rubygems/test_gem_source_list.rb
+++ b/test/rubygems/test_gem_source_list.rb
@@ -18,6 +18,10 @@ class TestGemSourceList < Gem::TestCase
assert_equal [Gem::Source.new(@uri)], sl.sources
end
+ def test_Enumerable
+ assert_includes Gem::SourceList.ancestors, Enumerable
+ end
+
def test_append
sl = Gem::SourceList.new
source = (sl << @uri)
@@ -30,6 +34,16 @@ class TestGemSourceList < Gem::TestCase
assert_equal [source], sl.sources
end
+ def test_clear
+ sl = Gem::SourceList.new
+
+ sl << 'http://source.example'
+
+ sl.clear
+
+ assert_empty sl
+ end
+
def test_replace
sl = Gem::SourceList.new
sl.replace [@uri]
@@ -49,6 +63,16 @@ class TestGemSourceList < Gem::TestCase
end
end
+ def test_empty?
+ sl = Gem::SourceList.new
+
+ assert_empty sl
+
+ sl << 'http://source.example'
+
+ refute_empty sl
+ end
+
def test_equal_to_another_list
sl2 = Gem::SourceList.new
sl2 << Gem::Source.new(@uri)
diff --git a/test/rubygems/test_gem_source_local.rb b/test/rubygems/test_gem_source_local.rb
index b3b444ccde..19e1c4b759 100644
--- a/test/rubygems/test_gem_source_local.rb
+++ b/test/rubygems/test_gem_source_local.rb
@@ -1,5 +1,5 @@
require 'rubygems/test_case'
-require 'rubygems/source/local'
+require 'rubygems/source'
require 'fileutils'
diff --git a/test/rubygems/test_gem_source_vendor.rb b/test/rubygems/test_gem_source_vendor.rb
new file mode 100644
index 0000000000..17403a1fc8
--- /dev/null
+++ b/test/rubygems/test_gem_source_vendor.rb
@@ -0,0 +1,13 @@
+require 'rubygems/test_case'
+require 'rubygems/source'
+
+class TestGemSourceVendor < Gem::TestCase
+
+ def test_initialize
+ source = Gem::Source::Vendor.new 'vendor/foo'
+
+ assert_equal 'vendor/foo', source.uri
+ end
+
+end
+
diff --git a/test/rubygems/test_gem_spec_fetcher.rb b/test/rubygems/test_gem_spec_fetcher.rb
index 9e9a8e4d69..54e6e4176f 100644
--- a/test/rubygems/test_gem_spec_fetcher.rb
+++ b/test/rubygems/test_gem_spec_fetcher.rb
@@ -52,7 +52,22 @@ class TestGemSpecFetcher < Gem::TestCase
['x', Gem::Version.new(1), 'ruby']]
end
- def test_initialize_unwritable_home_dir
+ def test_initialize
+ fetcher = Gem::SpecFetcher.new
+
+ assert_same Gem.sources, fetcher.sources
+ end
+
+ def test_initialize_source
+ alternate = 'http://alternate.example'
+ fetcher = Gem::SpecFetcher.new alternate
+
+ refute_same Gem.sources, fetcher.sources
+
+ assert_equal alternate, fetcher.sources
+ end
+
+ def test_initialize_nonexistent_home_dir
FileUtils.rmdir Gem.user_home
assert Gem::SpecFetcher.new
diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb
index 0623e8d51d..3936bc0681 100644
--- a/test/rubygems/test_gem_specification.rb
+++ b/test/rubygems/test_gem_specification.rb
@@ -873,6 +873,31 @@ dependencies: []
Gem::Specification.outdated_and_latest_version.to_a
end
+ def test_self_remove_spec
+ assert_includes Gem::Specification.all_names, 'a-1'
+ assert_includes Gem::Specification.stubs.map { |s| s.full_name }, 'a-1'
+
+ Gem::Specification.remove_spec @a1
+
+ refute_includes Gem::Specification.all_names, 'a-1'
+ refute_includes Gem::Specification.stubs.map { |s| s.full_name }, 'a-1'
+ end
+
+ def test_self_remove_spec_removed
+ open @a1.spec_file, 'w' do |io|
+ io.write @a1.to_ruby
+ end
+
+ Gem::Specification.reset
+
+ FileUtils.rm @a1.spec_file # bug #698
+
+ Gem::Specification.remove_spec @a1
+
+ refute_includes Gem::Specification.all_names, 'a-1'
+ refute_includes Gem::Specification.stubs.map { |s| s.full_name }, 'a-1'
+ end
+
DATA_PATH = File.expand_path "../data", __FILE__
def test_handles_private_null_type
@@ -1886,7 +1911,7 @@ Gem::Specification.new do |s|
s.rubygems_version = "#{Gem::VERSION}"
s.summary = "this is a summary"
- s.installed_by_version = "#{Gem::VERSION}"
+ s.installed_by_version = "#{Gem::VERSION}" if s.respond_to? :installed_by_version
if s.respond_to? :specification_version then
s.specification_version = #{Gem::Specification::CURRENT_SPECIFICATION_VERSION}
@@ -2128,6 +2153,15 @@ end
Dir.chdir @tempdir do
@a1.add_runtime_dependency 'b', '>= 1.0.rc1'
@a1.add_development_dependency 'c', '>= 2.0.rc2'
+ @a1.add_runtime_dependency 'd', '~> 1.2.3'
+ @a1.add_runtime_dependency 'e', '~> 1.2.3.4'
+ @a1.add_runtime_dependency 'g', '~> 1.2.3', '>= 1.2.3.4'
+ @a1.add_runtime_dependency 'h', '>= 1.2.3', '<= 2'
+ @a1.add_runtime_dependency 'i', '>= 1.2'
+ @a1.add_runtime_dependency 'j', '>= 1.2.3'
+ @a1.add_runtime_dependency 'k', '> 1.2'
+ @a1.add_runtime_dependency 'l', '> 1.2.3'
+ @a1.add_runtime_dependency 'm', '~> 2.1.0'
use_ui @ui do
@a1.validate
@@ -2136,9 +2170,57 @@ end
expected = <<-EXPECTED
#{w}: prerelease dependency on b (>= 1.0.rc1) is not recommended
#{w}: prerelease dependency on c (>= 2.0.rc2, development) is not recommended
+#{w}: pessimistic dependency on d (~> 1.2.3) may be overly strict
+ if d is semantically versioned, use:
+ add_runtime_dependency 'd', '~> 1.2', '>= 1.2.3'
+#{w}: pessimistic dependency on e (~> 1.2.3.4) may be overly strict
+ if e is semantically versioned, use:
+ add_runtime_dependency 'e', '~> 1.2', '>= 1.2.3.4'
+#{w}: open-ended dependency on i (>= 1.2) is not recommended
+ if i is semantically versioned, use:
+ add_runtime_dependency 'i', '~> 1.2'
+#{w}: open-ended dependency on j (>= 1.2.3) is not recommended
+ if j is semantically versioned, use:
+ add_runtime_dependency 'j', '~> 1.2', '>= 1.2.3'
+#{w}: open-ended dependency on k (> 1.2) is not recommended
+ if k is semantically versioned, use:
+ add_runtime_dependency 'k', '~> 1.2', '> 1.2'
+#{w}: open-ended dependency on l (> 1.2.3) is not recommended
+ if l is semantically versioned, use:
+ add_runtime_dependency 'l', '~> 1.2', '> 1.2.3'
+#{w}: pessimistic dependency on m (~> 2.1.0) may be overly strict
+ if m is semantically versioned, use:
+ add_runtime_dependency 'm', '~> 2.1', '>= 2.1.0'
+#{w}: See http://guides.rubygems.org/specification-reference/ for help
EXPECTED
- assert_match expected, @ui.error, 'warning'
+ assert_equal expected, @ui.error, 'warning'
+ end
+ end
+
+ def test_validate_dependencies_open_ended
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @a1.add_runtime_dependency 'b', '~> 1.2'
+ @a1.add_runtime_dependency 'b', '>= 1.2.3'
+
+ use_ui @ui do
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ expected = <<-EXPECTED
+duplicate dependency on b (>= 1.2.3), (~> 1.2) use:
+ add_runtime_dependency 'b', '>= 1.2.3', '~> 1.2'
+ EXPECTED
+
+ assert_equal expected, e.message
+ end
+
+ assert_equal <<-EXPECTED, @ui.error
+#{w}: See http://guides.rubygems.org/specification-reference/ for help
+ EXPECTED
end
end
diff --git a/test/rubygems/test_gem_stub_specification.rb b/test/rubygems/test_gem_stub_specification.rb
index 1c200ec1ed..bb04fb4dcc 100644
--- a/test/rubygems/test_gem_stub_specification.rb
+++ b/test/rubygems/test_gem_stub_specification.rb
@@ -23,8 +23,6 @@ class TestStubSpecification < Gem::TestCase
def test_initialize_extension
stub = stub_with_extension
- gem_dir = File.join stub.gems_dir, stub.full_name
-
ext_install_dir = Pathname(stub.extension_install_dir)
full_gem_path = Pathname(stub.full_gem_path)
relative_install_dir = ext_install_dir.relative_path_from full_gem_path