Commit 1baebf67 authored by Fabio Pelosin's avatar Fabio Pelosin

[Resolver] Clean-up and specs update.

parent ff745f82
......@@ -5,186 +5,253 @@ module Pod
#
# Its current implementation is naive, in the sense that it can't do full
# automatic resolves like Bundler:
#
# http://patshaughnessy.net/2011/9/24/how-does-bundler-bundle
# [how-does-bundler-bundle](http://patshaughnessy.net/2011/9/24/how-does-bundler-bundle)
#
# Another important aspect to keep in mind of the current implementation
# is that the order of the dependencies matters.
#
class Resolver
include Config::Mixin
# @return [Sandbox] The Sandbox used by the resolver to find external
# dependencies.
# @return [Sandbox] the Sandbox used by the resolver to find external
# dependencies.
#
attr_reader :sandbox
# @return [Podfile] The Podfile used by the resolver.
# @return [Podfile] the Podfile used by the resolver.
#
attr_reader :podfile
# @return [Array<Dependency>] The list of dependencies locked to a specific
# version.
# @return [Array<Dependency>] the list of dependencies locked to a specific
# version.
#
attr_reader :locked_dependencies
# @return [Bool] Whether the resolver should update the external specs
# in the resolution process. This option is used for detecting changes
# in with the Podfile without affecting the existing Pods installation
# (see `pod outdated`).
# @return [Bool] whether the resolver should update the external specs
# in the resolution process. This option is used for detecting
# changes in with the Podfile without affecting the existing Pods
# installation
#
# @TODO: This implementation is not clean, because if the spec doesn't
# exists the sandbox will actually download it and result modified.
# @note This option is used by `pod outdated`.
#
# @TODO: This implementation is not clean, because if the spec doesn't
# exists the sandbox will actually download and modify the
# installation.
#
attr_accessor :update_external_specs
# @param [Sandbox] sandbox @see sandbox
# @param [Podfile] podfile @see podfile
# @param [Array<Dependency>] locked_dependencies @see locked_dependencies
#
def initialize(sandbox, podfile, locked_dependencies = [])
@sandbox = sandbox
@podfile = podfile
@locked_dependencies = locked_dependencies
end
# @return [Hash{Podfile::TargetDefinition => Array<Specification>}]
# Returns the resolved specifications grouped by target.
#
attr_reader :specs_by_target
#-------------------------------------------------------------------------#
# @return [Array<Specification>] All The specifications loaded by the
# resolver.
#
def specs
@cached_specs.values.uniq
end
# @!group Resolution
# @return [Array<Strings>] The name of the pods that have an
# external source.
#
# @TODO: Add an attribute to the specification class?
#
attr_reader :pods_from_external_sources
public
# Identifies the specifications that should be installed.
#
# @return [Hash{TargetDefinition => Array<Specification>}] specs_by_target
# Identifies the specifications that should be installed according
# whether the resolver is in update mode or not.
# the specifications that need to be installed grouped by target
# definition.
#
def resolve
@cached_sources = Source::Aggregate.new(config.repos_dir)
@cached_sets = {}
@cached_specs = {}
@specs_by_target = {}
@pods_from_external_sources = []
# @pods_from_external_sources = []
podfile.target_definitions.values.each do |target_definition|
UI.section "Resolving dependencies for target `#{target_definition.name}' (#{target_definition.platform})" do
podfile.target_definitions.values.each do |target|
UI.section "Resolving dependencies for target `#{target.name}' (#{target.platform})" do
@loaded_specs = []
find_dependency_specs(podfile, target_definition.dependencies, target_definition)
@specs_by_target[target_definition] = @cached_specs.values_at(*@loaded_specs).sort_by(&:name)
find_dependency_specs(podfile, target.dependencies, target)
specs = cached_specs.values_at(*@loaded_specs).sort_by(&:name)
specs_by_target[target] = specs
end
end
@cached_specs.values.sort_by(&:name)
@specs_by_target
cached_specs.values.sort_by(&:name)
specs_by_target
end
#-----------------------------------------------------------------------#
# @return [Hash{Podfile::TargetDefinition => Array<Specification>}]
# returns the resolved specifications grouped by target.
#
# @note The returned specifications can be subspecs.
#
attr_reader :specs_by_target
private
# @return [Array<Specification>] All the specifications resolved.
#
def specs
specs_by_target.values.flatten.uniq
end
# @return [Array<Set>] A cache of the sets used to resolve the dependencies.
# @return [Array<Strings>] The name of the pods that have an
# external source.
#
# TODO: Not sure if needed.
#
attr_reader :cached_sets
# attr_reader :pods_from_external_sources
#-------------------------------------------------------------------------#
# !@ Resolution context
private
# @return [Source::Aggregate] A cache of the sources needed to find the
# podspecs.
# podspecs.
#
# TODO: Cache the sources globally?
#
attr_accessor :cached_sources
# @return [Hash<String => Set>] A cache that keeps tracks of the sets
# loaded by the resolution process.
#
# @note Sets keep track of the TODO:
#
attr_reader :cached_sources
attr_accessor :cached_sets
# @return [void] Resolves recursively the dependencies of a specification
# and stores them in @cached_specs
#
# @param [Specification] dependent_specification
# The specification whose dependencies are being resolved.
#
# @param [Array<Dependency>] dependencies
# The dependencies of the specification.
attr_accessor :cached_specs
#
#
attr_writer :specs_by_target
#-------------------------------------------------------------------------#
# !@ Resolution helpers
private
# Resolves recursively the dependencies of a specification and stores them
# in the @cached_specs ivar.
#
# @param [Podfile, Specification] dependent_spec
# the specification whose dependencies are being resolved.
#
# @param [Array<Dependency>] dependencies
# the dependencies of the specification.
#
# @param [TargetDefinition] target_definition
# the target definition that owns the specification.
#
# @note If there is a locked dependency with the same name of a
# given dependency the locked one is used in place of the
# dependency of the specification. In this way it is possible to
# not updated the installed pods without without introducing
# dependencies in other target definitions.
# TODO: Just add the requirement to the set?
# TODO: Use root name?
#
# @note The recursive process checks if a dependency has already been
# loaded to prevent an infinite loop. For this reason the
# @loaded_specs ivar must be cleaned when changing target
# definition.
#
#
# TODO: The set class should be aware whether it is in head mode.
#
# @param [TargetDefinition] target_definition
# The target definition that owns the specification.
# @return [void]
#
def find_dependency_specs(dependent_specification, dependencies, target_definition)
def find_dependency_specs(dependent_spec, dependencies, target_definition)
dependencies.each do |dependency|
# Replace the dependency with a more specific one if the pod is already
# installed.
# @TODO: check for compatibility?
locked_dep = locked_dependencies.find { |locked| locked.name == dependency.name }
locked_dep = locked_dependencies.find { |ld| ld.name == dependency.name }
dependency = locked_dep if locked_dep
UI.message("- #{dependency}", '', 2) do
set = find_cached_set(dependency, target_definition.platform)
set.required_by(dependency, dependent_specification.to_s)
set.required_by(dependency, dependent_spec.to_s)
# Ensure we don't resolve the same spec twice for one target
if @loaded_specs.include?(dependency.name)
validate_platform(@cached_specs[dependency.name], target_definition)
else
unless @loaded_specs.include?(dependency.name)
spec = set.specification.subspec_by_name(dependency.name)
@pods_from_external_sources << spec.pod_name if dependency.external?
@loaded_specs << spec.name
@cached_specs[spec.name] = spec
# Configure the specification
cached_specs[spec.name] = spec
# @pods_from_external_sources << spec.root_name if dependency.external?
validate_platform(spec, target_definition)
spec.activate_platform(target_definition.platform)
spec.version.head = dependency.head?
# And recursively load the dependencies of the spec.
find_dependency_specs(spec, spec.dependencies, target_definition)
validate_platform(spec, target_definition)
find_dependency_specs(spec, spec.dependencies, target_definition)
end
end
end
end
# @return [Set] The cached set for a given dependency.
# Loads or returns a previously initialized {Set} for the given dependency.
#
# @param [Dependency] dependency
# the dependency for which the set is needed.
# TODO: check dependency.specification
#
# @param [Platform] platform
# the platform on which the dependency is needed this is used by
# the sandbox to locate external sources.
# TODO why?
#
# @note If the {#update_external_specs} flag is activated the
# dependencies with external sources are always resolved against
# the remote. Otherwise the specification is retrieved from the
# sandbox that fetches the external source only if needed.
#
# TODO If the set is loaded from a normal source and then from an
# external one that information is lost.
#
# If the update_external_specs flag is activated the dependencies with
# external sources are always resolved against the remote. Otherwise the
# specification is retrieved from the sandbox that fetches the external
# source only if needed.
# @return [Set] the cached set for a given dependency.
#
def find_cached_set(dependency, platform)
set_name = dependency.name.split('/').first
@cached_sets[set_name] ||= begin
name = dependency.root_name
unless cached_sets[name]
if dependency.specification
Specification::Set::External.new(dependency.specification)
elsif external_source = dependency.external_source
if update_external_specs
external_source = ExternalSources.from_dependency(dependency)
spec = external_source.specification_from_external(@sandbox, platform)
else
external_source = ExternalSources.from_dependency(dependency)
spec = external_source.specification_from_sandbox(@sandbox, platform)
end
set = Specification::Set::External.new(spec)
if dependency.subspec_dependency?
@cached_sets[dependency.root_name] ||= set
end
set
set = Specification::Set::External.new(dependency.specification)
elsif dependency.external_source
set = set_from_external_source(dependency, platform)
else
cached_sources.search(dependency)
set = cached_sources.search(dependency)
end
cached_sets[name] = set
end
cached_sets[name]
end
# @return [void] Ensures that a spec is compatible with the platform of a
# target.
# Returns a new set created from an external source
#
def set_from_external_source(dependency, platform)
source = ExternalSources.from_dependency(dependency)
spec = if update_external_specs
source.specification_from_external(@sandbox, platform)
else
source.specification_from_sandbox(@sandbox, platform)
end
set = Specification::Set::External.new(spec)
set
end
# Ensures that a spec is compatible with the platform of a target.
#
# @raises If the spec is not supported by the target.
#
# @return [void]
#
def validate_platform(spec, target)
unless spec.available_platforms.any? { |platform| target.platform.supports?(platform) }
raise Informative, "The platform of the target `#{target.name}' "\
"(#{target.platform}) is not compatible with `#{spec}' which " \
"has a minimum requirement of #{spec.available_platforms.join(' - ')}."
unless spec.available_platforms.any? { |p| target.platform.supports?(p) }
raise Informative, "The platform of the target `#{target.name}` " \
"(#{target.platform}) is not compatible with `#{spec}` which has " \
"a minimum requirement of #{spec.available_platforms.join(' - ')}."
end
end
end
......
......@@ -2,450 +2,408 @@ require File.expand_path('../../spec_helper', __FILE__)
module Pod
describe Resolver do
before do
config.repos_dir = fixture('spec-repos')
@podfile = Podfile.new do
platform :ios
pod 'BlocksKit', '1.5.2'
end
@resolver = Resolver.new(config.sandbox, @podfile)
end
it "holds the context state, such as cached specification sets" do
@resolver.resolve
cached_sets = @resolver.send(:cached_sets)
cached_sets.values.sort_by(&:name).should == [
Source.search_by_name('A2DynamicDelegate').first,
Source.search_by_name('BlocksKit').first,
Source.search_by_name('libffi').first
].sort_by(&:name)
end
it "returns all specs needed for the dependency" do
specs = @resolver.resolve.values.flatten
specs.map(&:class).uniq.should == [Specification]
specs.map(&:name).sort.should == %w{ A2DynamicDelegate BlocksKit libffi }
end
it "does not raise if all dependencies match the platform of the root spec (Podfile)" do
@podfile.platform :ios, '6.0'
lambda { @resolver.resolve }.should.not.raise
@podfile.platform :osx, '10.7'
lambda { @resolver.resolve }.should.not.raise
end
it "raises once any of the dependencies does not match the platform of its podfile target" do
set = Source.search_by_name('BlocksKit').first
cached_sets = @resolver.send(:cached_sets)
@resolver.stubs(:cached_sets).returns({'BlocksKit' => set})
def set.stub_platform=(platform); @stubbed_platform = platform; end
def set.specification; spec = super; spec.platform = @stubbed_platform; spec; end
@podfile.platform :ios
set.stub_platform = :ios
lambda { @resolver.resolve }.should.not.raise
set.stub_platform = :osx
lambda { @resolver.resolve }.should.raise Pod::StandardError
@podfile.platform :osx
set.stub_platform = :osx
lambda { @resolver.resolve }.should.not.raise
set.stub_platform = :ios
lambda { @resolver.resolve }.should.raise Pod::StandardError
end
it "raises once any of the dependencies does not have a deployment_target compatible with its podfile target" do
set = Source.search_by_name('BlocksKit').first
@resolver.stubs(:cached_sets).returns({'BlocksKit' => set})
@podfile.platform :ios, "4.0"
Specification.any_instance.stubs(:available_platforms).returns([ Platform.new(:ios, '4.0'), Platform.new(:osx, '10.7') ])
lambda { @resolver.resolve }.should.not.raise
Specification.any_instance.stubs(:available_platforms).returns([ Platform.new(:ios, '5.0'), Platform.new(:osx, '10.7') ])
lambda { @resolver.resolve }.should.raise Pod::StandardError
end
it "resolves subspecs" do
@podfile = Podfile.new do
platform :ios
pod 'RestKit/Network'
pod 'RestKit/ObjectMapping/XML'
end
resolver = Resolver.new(@podfile, nil, stub('sandbox'))
resolver.resolve.values.flatten.map(&:name).sort.should == %w{
FileMD5Hash
ISO8601DateFormatter
LibComponentLogging-Core
LibComponentLogging-NSLog
NSData+Base64
RestKit/Network
RestKit/ObjectMapping/XML
SOCKit
XMLReader
cocoa-oauth
}
end
it "includes all the subspecs of a specification node" do
@podfile = Podfile.new do
platform :ios
pod 'RestKit'
end
resolver = Resolver.new(@podfile, nil, stub('sandbox'))
resolver.resolve.values.flatten.map(&:name).sort.should == %w{
FileMD5Hash
ISO8601DateFormatter
JSONKit
LibComponentLogging-Core
LibComponentLogging-NSLog
NSData+Base64
RestKit
RestKit/JSON
RestKit/Network
RestKit/ObjectMapping/CoreData
RestKit/ObjectMapping/JSON
RestKit/UI
SOCKit
cocoa-oauth
}
end
# TODO inline podspecs are deprecated
xit "it includes only the main subspec of a specification node" do
spec = Spec.new do |s|
s.name = 'RestKit'
s.version = '9999990.10.0'
s.preferred_dependency = 'JSON'
s.subspec 'JSON' do |js|
js.dependency 'RestKit/Network'
js.dependency 'RestKit/UI'
js.dependency 'RestKit/ObjectMapping/JSON'
js.dependency 'RestKit/ObjectMapping/CoreData'
end
s.subspec 'Network' do |ns|
ns.dependency 'LibComponentLogging-NSLog', '>= 1.0.4'
end
s.subspec 'UI'
s.subspec 'ObjectMapping' do |os|
os.subspec 'JSON'
os.subspec 'XML'
os.subspec 'CoreData'
end
end
@podfile = Podfile.new do
platform :ios
pod 'RestKit'
end
Set.any_instance.stubs(:specification).returns(spec)
resolver = Resolver.new(@podfile, nil, stub('sandbox'))
resolver.find_cached_set()
specs = resolver.resolve.values.flatten.map(&:name).sort
specs.should.not.include 'RestKit/ObjectMapping/XML'
spec = resolver.specs_by_target.values.first.first
spec.name.should == 'test'
spec.version.should == '9999990.10.0'
specs.should == %w{
LibComponentLogging-Core
LibComponentLogging-NSLog
RestKit
RestKit/JSON
RestKit/Network
RestKit/ObjectMapping/CoreData
RestKit/ObjectMapping/JSON
RestKit/UI
}
end
it "resolves subspecs with external constraints" do
@podfile = Podfile.new do
platform :ios
pod 'MainSpec/FirstSubSpec', :git => 'GIT-URL'
end
spec = Spec.new do |s|
s.name = 'MainSpec'
s.version = '1.2.3'
s.platform = :ios
s.license = 'MIT'
s.author = 'Joe the Plumber'
s.summary = 'A spec with subspecs'
s.source = { :git => '/some/url' }
s.requires_arc = true
s.subspec 'FirstSubSpec' do |fss|
fss.source_files = 'some/file'
fss.subspec 'SecondSubSpec'
end
end
ExternalSources::GitSource.any_instance.stubs(:specification_from_sandbox).returns(spec)
resolver = Resolver.new(@podfile, nil, stub('sandbox'))
resolver.resolve.values.flatten.map(&:name).sort.should == %w{ MainSpec/FirstSubSpec MainSpec/FirstSubSpec/SecondSubSpec }
end
it "marks a specification's version to be a `bleeding edge' version" do
podfile = Podfile.new do
platform :ios
pod 'FileMD5Hash'
pod 'JSONKit', :head
end
resolver = Resolver.new(podfile, nil, stub('sandbox'))
filemd5hash, jsonkit = resolver.resolve.values.first.sort_by(&:name)
filemd5hash.version.should.not.be.head
jsonkit.version.should.be.head
end
it "accepts a nil lockfile" do
lambda { Resolver.new(@podfile, nil, stub('sandbox'))}.should.not.raise
end
it "raises if it finds two conflicting dependencies" do
podfile = Podfile.new do
platform :ios
pod 'JSONKit', "1.4"
pod 'JSONKit', "1.5pre"
end
resolver = Resolver.new(podfile, nil, stub('sandbox'))
lambda {resolver.resolve}.should.raise Pod::StandardError
end
describe "Concerning Installation mode" do
describe "In general" do
before do
config.repos_dir = fixture('spec-repos')
@podfile = Podfile.new do
platform :ios
pod 'BlocksKit', '1.5.2'
pod 'JSONKit'
end
@specs = [
Specification.new do |s|
s.name = "BlocksKit"
s.version = "1.5.2"
end,
Specification.new do |s|
s.name = "JSONKit"
s.version = "1.4"
end ]
@specs.each { |s| s.activate_platform(:ios) }
@lockfile = Lockfile.generate(@podfile, @specs)
@resolver = Resolver.new(@podfile, @lockfile, stub('sandbox'))
locked_deps = [Dependency.new('BlocksKit', '1.5.2')]
@resolver = Resolver.new(config.sandbox, @podfile, locked_deps)
end
it "doesn't install pods still compatible with the Podfile" do
@resolver.resolve
@resolver.should_install?("BlocksKit").should.be.false
@resolver.should_install?("JSONKit").should.be.false
it "returns the sandbox" do
@resolver.sandbox.should == config.sandbox
end
it "doesn't update the version of pods still compatible with the Podfile" do
installed = @resolver.resolve.values.flatten.map(&:to_s)
installed.should.include? "JSONKit (1.4)"
it "returns the podfile" do
@resolver.podfile.should == @podfile
end
it "doesn't include pods removed from the Podfile" do
podfile = Podfile.new { platform :ios; pod 'JSONKit' }
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.resolve.values.flatten.map(&:name).should == %w{ JSONKit }
it "returns the locked dependencies" do
@resolver.locked_dependencies.should == [Dependency.new('BlocksKit', '1.5.2')]
end
it "reinstalls pods updated in the Podfile" do
podfile = Podfile.new do
platform :ios
pod 'JSONKit', '1.5pre'
pod 'BlocksKit', '1.5.2'
end
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
installed = @resolver.resolve.values.flatten.map(&:to_s)
installed.should.include? "BlocksKit (1.5.2)"
installed.should.include? "JSONKit (1.5pre)"
it "allows to specify whether the external sources should be updated against the remote" do
# TODO
@resolver.update_external_specs = true
@resolver.update_external_specs.should.be.true
end
it "installs pods added to the Podfile" do
podfile = Podfile.new do
platform :ios
pod 'JSONKit'
pod 'BlocksKit'
pod 'libPusher', '1.3' # New pod
end
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
installed = @resolver.resolve.values.flatten.map(&:to_s)
installed.should.include? "libPusher (1.3)"
end
#--------------------------------------#
it "handles head pods" do
podfile = Podfile.new do
platform :ios
pod 'JSONKit', :head # Existing pod switched to head mode
pod 'libPusher', :head # New pod
end
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.resolve
@resolver.should_install?("JSONKit").should.be.true
@resolver.should_install?("libPusher").should.be.true
end
it "handles pods from external dependencies" do
podfile = Podfile.new do
platform :ios
pod 'libPusher', :git => 'GIT-URL'
end
spec = Spec.new do |s|
s.name = 'libPusher'
s.version = '1.3'
end
ExternalSources::GitSource.any_instance.stubs(:specification_from_sandbox).returns(spec)
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.resolve
@resolver.should_install?("JSONKit").should.be.false
it "resolves the specification of the podfile" do
target_definition = @podfile.target_definitions[:default]
specs = @resolver.resolve[target_definition]
specs.map(&:to_s).should == [
"A2DynamicDelegate (2.0.2)",
"BlocksKit (1.5.2)",
"libffi (3.0.11)"
]
end
it "doesn't updates the repos if there no change in the pods" do
podfile = Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit'
end
config.skip_repo_update = false
Command::Repo.any_instance.expects(:run).never
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
it "returns the resolved specifications grouped by target definition" do
@resolver.resolve
target_definition = @podfile.target_definitions[:default]
specs = @resolver.specs_by_target[target_definition]
specs.map(&:to_s).should == [
"A2DynamicDelegate (2.0.2)",
"BlocksKit (1.5.2)",
"libffi (3.0.11)"
]
end
it "updates the repos if there is a new pod" do
podfile = Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit'
pod 'libPusher' # New pod
end
config.skip_repo_update = false
Command::Repo::Update.any_instance.expects(:run).once
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
it "returns all the resolved specifications" do
@resolver.resolve
@resolver.specs.map(&:class).uniq.should == [Specification]
@resolver.specs.map(&:to_s).should == [
"A2DynamicDelegate (2.0.2)",
"BlocksKit (1.5.2)",
"libffi (3.0.11)"
]
end
it "doesn't update the repos if config indicate to skip it in any case" do
podfile = Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit', :head #changed to head
pod 'libPusher' # New pod
end
config.skip_repo_update = true
Command::Repo::Update.any_instance.expects(:run).never
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.resolve
end
xit "returns the specifications that originated from external sources" do
it "updates the repos if there is a new pod" do
podfile = Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit', :head #changed to head
end
config.skip_repo_update = false
Command::Repo::Update.any_instance.expects(:run).once
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.resolve
end
end
describe "Concerning Update mode" do
#-------------------------------------------------------------------------#
describe "Resolution" do
before do
config.repos_dir = fixture('spec-repos')
previous_podfile = Podfile.new do
platform :ios
pod 'JSONKit'
pod 'libPusher'
end
@specs = [
Specification.new do |s|
s.name = "libPusher"
s.version = "1.3"
end,
Specification.new do |s|
s.name = "JSONKit"
s.version = "1.4"
end ]
@specs.each { |s| s.activate_platform(:ios) }
@lockfile = Lockfile.generate(previous_podfile, @specs)
@podfile = Podfile.new do
platform :ios
platform :ios, '6.0'
pod 'BlocksKit', '1.5.2'
pod 'JSONKit'
pod 'libPusher'
end
@resolver = Resolver.new(@podfile, @lockfile, stub('sandbox'))
@resolver.update_mode = true
@resolver = Resolver.new(config.sandbox, @podfile)
end
it "identifies the pods that can be updated" do
installed = @resolver.resolve.values.flatten.map(&:to_s)
installed.should.include? "JSONKit (999.999.999)"
@resolver.should_install?("JSONKit").should.be.true
it "holds the context state, such as cached specification sets" do
@resolver.resolve
cached_sets = @resolver.send(:cached_sets)
cached_sets.values.sort_by(&:name).should == [
Source.search_by_name('A2DynamicDelegate').first,
Source.search_by_name('BlocksKit').first,
Source.search_by_name('libffi').first
].sort_by(&:name)
end
it "respects the constraints of the podfile" do
podfile = Podfile.new do
platform :ios
pod 'BlocksKit', '1.5.2'
pod 'JSONKit', '1.4'
end
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.update_mode = true
installed = @resolver.resolve.values.flatten.map(&:to_s)
installed.should.include? "JSONKit (1.4)"
@resolver.should_install?("JSONKit").should.be.false
it "raises once any of the dependencies does not match the platform of its podfile target" do
Specification.any_instance.stubs(:available_platforms).returns([Platform.new(:ios, '999')])
e = lambda { @resolver.resolve }.should.raise Informative
e.message.should.match(/platform .* not compatible/)
end
it "installs new pods" do
installed = @resolver.resolve.values.flatten.map(&:to_s)
installed.join(' ').should.include?('BlocksKit')
@resolver.should_install?("BlocksKit").should.be.true
it "does not raise if all dependencies are supported by the platform of the target definition" do
lambda { @resolver.resolve }.should.not.raise
end
it "it always suggests to update pods in head mode" do
podfile = Podfile.new do
it "includes all the subspecs of a specification node" do
@podfile = Podfile.new do
platform :ios
pod 'libPusher', :head
pod 'RestKit'
end
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.update_mode = true
@resolver.resolve
@resolver.should_install?("libPusher").should.be.true
resolver = Resolver.new(config.sandbox, @podfile)
resolver.resolve.values.flatten.map(&:name).sort.should == %w{
FileMD5Hash
ISO8601DateFormatter
JSONKit
LibComponentLogging-Core
LibComponentLogging-NSLog
NSData+Base64
RestKit
RestKit/JSON
RestKit/Network
RestKit/ObjectMapping/CoreData
RestKit/ObjectMapping/JSON
RestKit/UI
SOCKit
cocoa-oauth
}
end
it "always updates the repos even if there is change in the pods" do
it "resolves subspecs with external constraints" do
@podfile = Podfile.new do
platform :ios
pod 'MainSpec/FirstSubSpec', :git => 'GIT-URL'
end
spec = Spec.new do |s|
s.name = 'MainSpec'
s.version = '1.2.3'
s.platform = :ios
s.license = 'MIT'
s.author = 'Joe the Plumber'
s.summary = 'A spec with subspecs'
s.source = { :git => '/some/url' }
s.requires_arc = true
s.subspec 'FirstSubSpec' do |fss|
fss.source_files = 'some/file'
fss.subspec 'SecondSubSpec'
end
end
ExternalSources::GitSource.any_instance.stubs(:specification_from_sandbox).returns(spec)
resolver = Resolver.new(config.sandbox, @podfile)
resolver.resolve.values.flatten.map(&:name).sort.should == %w{ MainSpec/FirstSubSpec MainSpec/FirstSubSpec/SecondSubSpec }
end
it "marks a specification's version to be a `bleeding edge' version" do
podfile = Podfile.new do
platform :ios
pod 'JSONKit'
pod 'libPusher'
pod 'FileMD5Hash'
pod 'JSONKit', :head
end
config.skip_repo_update = false
Command::Repo::Update.any_instance.expects(:run).once
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.update_mode = true
@resolver.resolve
resolver = Resolver.new(config.sandbox, podfile)
filemd5hash, jsonkit = resolver.resolve.values.first.sort_by(&:name)
filemd5hash.version.should.not.be.head
jsonkit.version.should.be.head
end
# TODO: stub the specification resolution for the sandbox
xit "it always suggests to update pods from external sources" do
it "raises if it finds two conflicting dependencies" do
podfile = Podfile.new do
platform :ios
pod 'libPusher', :git => "example.com"
pod 'JSONKit', "1.4"
pod 'JSONKit', "1.5pre"
end
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.update_mode = true
@resolver.resolve
@resolver.should_install?("libPusher").should.be.true
resolver = Resolver.new(config.sandbox, podfile)
e = lambda {resolver.resolve}.should.raise Pod::StandardError
e.message.should.match(/already activated version/)
end
end
# describe "Concerning Installation mode" do
# before do
# config.repos_dir = fixture('spec-repos')
# @podfile = Podfile.new do
# platform :ios
# pod 'BlocksKit', '1.5.2'
# pod 'JSONKit'
# end
# @specs = [
# Specification.new do |s|
# s.name = "BlocksKit"
# s.version = "1.5.2"
# end,
# Specification.new do |s|
# s.name = "JSONKit"
# s.version = "1.4"
# end ]
# @specs.each { |s| s.activate_platform(:ios) }
# @resolver = Resolver.new(@podfile, @lockfile, stub('sandbox'))
# end
# it "doesn't install pods still compatible with the Podfile" do
# @resolver.resolve
# @resolver.should_install?("BlocksKit").should.be.false
# @resolver.should_install?("JSONKit").should.be.false
# end
# it "doesn't update the version of pods still compatible with the Podfile" do
# installed = @resolver.resolve.values.flatten.map(&:to_s)
# installed.should.include? "JSONKit (1.4)"
# end
# it "doesn't include pods removed from the Podfile" do
# podfile = Podfile.new { platform :ios; pod 'JSONKit' }
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.resolve.values.flatten.map(&:name).should == %w{ JSONKit }
# end
# it "reinstalls pods updated in the Podfile" do
# podfile = Podfile.new do
# platform :ios
# pod 'JSONKit', '1.5pre'
# pod 'BlocksKit', '1.5.2'
# end
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# installed = @resolver.resolve.values.flatten.map(&:to_s)
# installed.should.include? "BlocksKit (1.5.2)"
# installed.should.include? "JSONKit (1.5pre)"
# end
# it "installs pods added to the Podfile" do
# podfile = Podfile.new do
# platform :ios
# pod 'JSONKit'
# pod 'BlocksKit'
# pod 'libPusher', '1.3' # New pod
# end
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# installed = @resolver.resolve.values.flatten.map(&:to_s)
# installed.should.include? "libPusher (1.3)"
# end
# it "handles head pods" do
# podfile = Podfile.new do
# platform :ios
# pod 'JSONKit', :head # Existing pod switched to head mode
# pod 'libPusher', :head # New pod
# end
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.resolve
# @resolver.should_install?("JSONKit").should.be.true
# @resolver.should_install?("libPusher").should.be.true
# end
# it "handles pods from external dependencies" do
# podfile = Podfile.new do
# platform :ios
# pod 'libPusher', :git => 'GIT-URL'
# end
# spec = Spec.new do |s|
# s.name = 'libPusher'
# s.version = '1.3'
# end
# ExternalSources::GitSource.any_instance.stubs(:specification_from_sandbox).returns(spec)
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.resolve
# @resolver.should_install?("JSONKit").should.be.false
# end
# it "doesn't updates the repos if there no change in the pods" do
# podfile = Podfile.new do
# platform :ios
# pod 'BlocksKit'
# pod 'JSONKit'
# end
# config.skip_repo_update = false
# Command::Repo.any_instance.expects(:run).never
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.resolve
# end
# it "updates the repos if there is a new pod" do
# podfile = Podfile.new do
# platform :ios
# pod 'BlocksKit'
# pod 'JSONKit'
# pod 'libPusher' # New pod
# end
# config.skip_repo_update = false
# Command::Repo::Update.any_instance.expects(:run).once
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.resolve
# end
# it "doesn't update the repos if config indicate to skip it in any case" do
# podfile = Podfile.new do
# platform :ios
# pod 'BlocksKit'
# pod 'JSONKit', :head #changed to head
# pod 'libPusher' # New pod
# end
# config.skip_repo_update = true
# Command::Repo::Update.any_instance.expects(:run).never
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.resolve
# end
# it "updates the repos if there is a new pod" do
# podfile = Podfile.new do
# platform :ios
# pod 'BlocksKit'
# pod 'JSONKit', :head #changed to head
# end
# config.skip_repo_update = false
# Command::Repo::Update.any_instance.expects(:run).once
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.resolve
# end
# end
# describe "Concerning Update mode" do
# before do
# config.repos_dir = fixture('spec-repos')
# previous_podfile = Podfile.new do
# platform :ios
# pod 'JSONKit'
# pod 'libPusher'
# end
# @specs = [
# Specification.new do |s|
# s.name = "libPusher"
# s.version = "1.3"
# end,
# Specification.new do |s|
# s.name = "JSONKit"
# s.version = "1.4"
# end ]
# @specs.each { |s| s.activate_platform(:ios) }
# @lockfile = Lockfile.generate(previous_podfile, @specs)
# @podfile = Podfile.new do
# platform :ios
# pod 'BlocksKit', '1.5.2'
# pod 'JSONKit'
# pod 'libPusher'
# end
# @resolver = Resolver.new(@podfile, @lockfile, stub('sandbox'))
# @resolver.update_mode = true
# end
# it "identifies the pods that can be updated" do
# installed = @resolver.resolve.values.flatten.map(&:to_s)
# installed.should.include? "JSONKit (999.999.999)"
# @resolver.should_install?("JSONKit").should.be.true
# end
# it "respects the constraints of the podfile" do
# podfile = Podfile.new do
# platform :ios
# pod 'BlocksKit', '1.5.2'
# pod 'JSONKit', '1.4'
# end
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.update_mode = true
# installed = @resolver.resolve.values.flatten.map(&:to_s)
# installed.should.include? "JSONKit (1.4)"
# @resolver.should_install?("JSONKit").should.be.false
# end
# it "installs new pods" do
# installed = @resolver.resolve.values.flatten.map(&:to_s)
# installed.join(' ').should.include?('BlocksKit')
# @resolver.should_install?("BlocksKit").should.be.true
# end
# it "it always suggests to update pods in head mode" do
# podfile = Podfile.new do
# platform :ios
# pod 'libPusher', :head
# end
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.update_mode = true
# @resolver.resolve
# @resolver.should_install?("libPusher").should.be.true
# end
# it "always updates the repos even if there is change in the pods" do
# podfile = Podfile.new do
# platform :ios
# pod 'JSONKit'
# pod 'libPusher'
# end
# config.skip_repo_update = false
# Command::Repo::Update.any_instance.expects(:run).once
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.update_mode = true
# @resolver.resolve
# end
# # TODO: stub the specification resolution for the sandbox
# xit "it always suggests to update pods from external sources" do
# podfile = Podfile.new do
# platform :ios
# pod 'libPusher', :git => "example.com"
# end
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.update_mode = true
# @resolver.resolve
# @resolver.should_install?("libPusher").should.be.true
# end
# end
end
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment