1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* 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.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@43643 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
drbrain 2013-11-10 17:51:40 +00:00
parent 31d355aaa9
commit 4f6779bac7
75 changed files with 3143 additions and 616 deletions

View file

@ -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]

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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/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/installer_set'
require 'rubygems/dependency_resolver/vendor_set'
require 'rubygems/dependency_resolver/vendor_specification'

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,4 +1,6 @@
class Gem::DependencyResolver::ComposedSet
class Gem::DependencyResolver::ComposedSet < Gem::DependencyResolver::Set
attr_reader :sets # :nodoc:
def initialize *sets
@sets = sets

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
##
# Returns +true+ if this gem is installable for the current platform.
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 full_name
"#{@spec.name}-#{@spec.version}"
end
def name
@spec.name
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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -31,6 +31,11 @@ class Gem::RequestSet
attr_accessor :development
##
# Sets used for resolution
attr_reader :sets # :nodoc:
##
# Treat missing dependencies as silent errors
@ -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

View file

@ -3,16 +3,125 @@
class Gem::RequestSet::GemDependencyAPI
##
# The dependency groups created by #group in the dependency API file.
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],
}
attr_reader :dependency_groups
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,
}
##
# A Hash containing gem names and files to require from those gems.
attr_reader :requires
##
# A set of gems that are loaded via the +:path+ option to #gem
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
return
end
case WINDOWS[platform_name]
when :only then
next false unless Gem.win_platform?
when :never then
next false if Gem.win_platform?
end
@set.gem name, *requirements
VERSION_MAP[platform_name].satisfied_by? Gem.ruby_version
end
end
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]
message = "Your Ruby version is #{RUBY_VERSION}, " +
"but your #{gem_deps_file} specified #{version}"
raise ArgumentError,
'you must specify engine_version along with the ruby engine' if
engine and not engine_version
raise Gem::RubyVersionMismatch, message
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
if engine_version then
my_engine_version = Object.const_get "#{Gem.ruby_engine.upcase}_VERSION"
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

View file

@ -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

View file

@ -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
##
@ -107,10 +117,28 @@ class Gem::Requirement
end
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

View file

@ -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
@ -697,6 +698,13 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
res.body = template.result binding
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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -17,6 +17,11 @@ class Gem::SpecFetcher
attr_reader :latest_specs # :nodoc:
##
# Sources for this SpecFetcher
attr_reader :sources # :nodoc:
##
# Cache of all released specs
@ -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

View file

@ -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:

View file

@ -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-----

View file

@ -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
@ -1088,6 +1111,62 @@ Also, a list:
Gem::Specification.new name, v(version), &block
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.

View file

@ -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:

View file

@ -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

View file

@ -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'

View file

@ -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

View file

@ -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

View file

@ -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'

View file

@ -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'
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
@ -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

View file

@ -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'

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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']],
}
def test_initialize
set = @DR::APISet.new
@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)
assert_equal URI('https://rubygems.org/api/v1/dependencies'),
set.dep_uri
end
def test_prefetch
b_entry = {
:name => 'b',
:number => '2',
:platform => 'ruby',
:dependencies => [['a', '>= 0']],
}
def test_initialize_uri
set = @DR::APISet.new @gem_repo
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')
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'
assert_equal [entry], @api_set.versions('b'), 'version data must be cached'
assert_equal URI('http://gems.example.com/'),
set.dep_uri
end
end

View file

@ -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

View file

@ -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'

View file

@ -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

View file

@ -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
assert_same Gem::SpecFetcher.fetcher, fetcher
end
util_setup_spec_fetcher a_2, a_2_p
def test_initialize_source
set = @DR::IndexSet.new 'http://alternate.example'
source = Gem::Source.new @gem_repo
version = v 2
fetcher = set.instance_variable_get :@f
set = Gem::DependencyResolver::IndexSet.new
first = set.load_spec 'a', version, Gem::Platform.local, source
util_setup_spec_fetcher # clear
second = set.load_spec 'a', version, Gem::Platform.local, source
assert_same first, second
refute_same Gem::SpecFetcher.fetcher, fetcher
end
end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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"

View file

@ -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_gem_group_method
groups = []
@gda.group :a do
groups = @gda.send :gem_group, 'a', :group => :b, :groups => [:c, :d]
end
assert_equal [:a, :b, :c, :d], groups.sort_by { |group| group.to_s }
end
def test_group
@gda.group :test do
@gda.gem 'a'
end
assert_equal [['a']], @gda.dependency_groups[:test]
assert_empty @set.dependencies
end
def test_group_multiple
@gda.group :a do
@gda.gem 'a', :group => :b, :groups => [:c, :d]
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

View file

@ -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

View file

@ -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')

View file

@ -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

View file

@ -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))

View file

@ -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)

View file

@ -1,5 +1,5 @@
require 'rubygems/test_case'
require 'rubygems/source/local'
require 'rubygems/source'
require 'fileutils'

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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