blob: 9fc196258eeee7740476a52d7f1115446c08030c [file] [log] [blame]
require File.expand_path('../../../spec_helper', __FILE__)
module Pod
describe Installer::Analyzer do
describe 'Analysis' do
before do
repos = [Source.new(fixture('spec-repos/test_repo')), MasterSource.new(fixture('spec-repos/master'))]
aggregate = Pod::Source::Aggregate.new(repos)
config.sources_manager.stubs(:aggregate).returns(aggregate)
aggregate.sources.first.stubs(:url).returns(SpecHelper.test_repo_url)
@podfile = Pod::Podfile.new do
platform :ios, '6.0'
project 'SampleProject/SampleProject'
target 'SampleProject' do
pod 'JSONKit', '1.5pre'
pod 'AFNetworking', '1.0.1'
pod 'SVPullToRefresh', '0.4'
pod 'libextobjc/EXTKeyPathCoding', '0.2.3'
target 'TestRunner' do
inherit! :search_paths
pod 'libextobjc/EXTKeyPathCoding', '0.2.3'
pod 'libextobjc/EXTSynthesize', '0.2.3'
end
end
end
hash = {}
hash['PODS'] = ['JSONKit (1.5pre)', 'NUI (0.2.0)', 'SVPullToRefresh (0.4)']
hash['DEPENDENCIES'] = %w(JSONKit NUI SVPullToRefresh)
hash['SPEC CHECKSUMS'] = {}
hash['COCOAPODS'] = Pod::VERSION
@lockfile = Pod::Lockfile.new(hash)
SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
@analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile, @lockfile)
end
it 'returns whether an installation should be performed' do
@analyzer.needs_install?.should.be.true
end
it 'returns whether the Podfile has changes' do
analysis_result = @analyzer.analyze(false)
@analyzer.podfile_needs_install?(analysis_result).should.be.true
end
it 'returns whether the sandbox is not in sync with the lockfile' do
analysis_result = @analyzer.analyze(false)
@analyzer.sandbox_needs_install?(analysis_result).should.be.true
end
#--------------------------------------#
it 'computes the state of the Podfile respect to the Lockfile' do
state = @analyzer.analyze.podfile_state
state.added.should == Set.new(%w(AFNetworking libextobjc libextobjc))
state.changed.should == Set.new(%w())
state.unchanged.should == Set.new(%w(JSONKit SVPullToRefresh))
state.deleted.should == Set.new(%w(NUI))
end
#--------------------------------------#
it 'does not update unused sources' do
@analyzer.stubs(:sources).returns(config.sources_manager.master)
config.sources_manager.expects(:update).once.with('master', true)
@analyzer.update_repositories
end
it 'does not update sources if there are no dependencies' do
podfile = Podfile.new do
source 'https://github.com/CocoaPods/Specs.git'
# No dependencies specified
end
config.verbose = true
config.sources_manager.expects(:update).never
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile, nil)
analyzer.update_repositories
end
it 'does not update non-git repositories' do
tmp_directory = Pathname(Dir.tmpdir) + 'CocoaPods'
FileUtils.mkdir_p(tmp_directory)
FileUtils.cp_r(ROOT + 'spec/fixtures/spec-repos/test_repo/', tmp_directory)
non_git_repo = tmp_directory + 'test_repo'
FileUtils.rm(non_git_repo + '.git')
podfile = Podfile.new do
platform :ios, '8.0'
project 'SampleProject/SampleProject'
pod 'BananaLib', '1.0'
end
config.verbose = true
source = Source.new(non_git_repo)
config.sources_manager.expects(:update).never
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile, nil)
analyzer.stubs(:sources).returns([source])
analyzer.update_repositories
UI.output.should.match /Skipping `#{source.name}` update because the repository is not a git source repository./
FileUtils.rm_rf(non_git_repo)
end
it 'updates sources specified with dependencies' do
repo_url = 'https://url/to/specs.git'
podfile = Podfile.new do
source 'repo_1'
pod 'BananaLib', '1.0', :source => repo_url
pod 'JSONKit', :source => repo_url
end
config.verbose = true
# Note that we are explicitly ignoring 'repo_1' since it isn't used.
source = mock
source.stubs(:name).returns('repo_2')
source.stubs(:repo).returns('/repo/cache/path')
config.sources_manager.expects(:find_or_create_source_with_url).with(repo_url).returns(source)
source.stubs(:git?).returns(true)
config.sources_manager.expects(:update).once.with(source.name, true)
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile, nil)
analyzer.update_repositories
end
#--------------------------------------#
it 'generates the model to represent the target definitions' do
result = @analyzer.analyze
target, test_target = result.targets
test_target.pod_targets.map(&:name).sort.should == %w(
libextobjc-EXTKeyPathCoding-EXTSynthesize
).sort
target.pod_targets.map(&:name).sort.should == %w(
JSONKit
AFNetworking
libextobjc-EXTKeyPathCoding
SVPullToRefresh
).sort
target.support_files_dir.should == config.sandbox.target_support_files_dir('Pods-SampleProject')
target.pod_targets.map(&:archs).uniq.should == [[]]
target.user_project_path.to_s.should.include 'SampleProject/SampleProject'
target.client_root.to_s.should.include 'SampleProject'
target.user_target_uuids.should == ['A346496C14F9BE9A0080D870']
user_proj = Xcodeproj::Project.open(target.user_project_path)
user_proj.objects_by_uuid[target.user_target_uuids.first].name.should == 'SampleProject'
target.user_build_configurations.should == {
'Debug' => :debug,
'Release' => :release,
'Test' => :release,
'App Store' => :release,
}
target.platform.to_s.should == 'iOS 6.0'
end
describe 'abstract targets' do
it 'resolves' do
@podfile = Pod::Podfile.new do
project 'SampleProject/SampleProject'
use_frameworks!
abstract_target 'Alpha' do
pod 'libextobjc'
target 'SampleProject' do
pod 'libextobjc/RuntimeExtensions'
end
target 'TestRunner' do
end
end
end
@analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile, nil)
result = @analyzer.analyze
sample_project_target, test_runner_target = result.targets.sort_by(&:name)
sample_project_target.pod_targets.map(&:name).should == %w(libextobjc-iOS5.0)
test_runner_target.pod_targets.map(&:name).should == %w(libextobjc-iOS5.1)
sample_project_target.user_targets.map(&:name).should == %w(SampleProject)
test_runner_target.user_targets.map(&:name).should == %w(TestRunner)
end
end
describe 'dependent pod targets' do
it 'picks transitive dependencies up' do
@podfile = Pod::Podfile.new do
platform :ios, '8.0'
project 'SampleProject/SampleProject'
pod 'RestKit', '~> 0.23.0'
target 'TestRunner' do
pod 'RestKit/Testing', '~> 0.23.0'
end
end
@analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile, nil)
result = @analyzer.analyze
result.targets.count.should == 1
target = result.targets.first
restkit_target = target.pod_targets.find { |pt| pt.pod_name == 'RestKit' }
restkit_target.dependent_targets.map(&:pod_name).sort.should == %w(
AFNetworking
ISO8601DateFormatterValueTransformer
RKValueTransformers
SOCKit
TransitionKit
)
restkit_target.dependent_targets.all?(&:scoped).should.be.true
end
it 'picks the right variants up when there are multiple' do
@podfile = Pod::Podfile.new do
source SpecHelper.test_repo_url
platform :ios, '8.0'
project 'SampleProject/SampleProject'
# The order of target definitions is important for this test.
target 'TestRunner' do
pod 'OrangeFramework'
pod 'matryoshka/Foo'
end
target 'SampleProject' do
pod 'OrangeFramework'
end
end
@analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile, nil)
result = @analyzer.analyze
result.targets.count.should == 2
pod_target = result.targets[0].pod_targets.find { |pt| pt.pod_name == 'OrangeFramework' }
pod_target.dependent_targets.count == 1
pod_target.dependent_targets.first.specs.map(&:name).should == %w(
matryoshka
matryoshka/Outer
matryoshka/Outer/Inner
)
end
it 'does not create multiple variants across different targets that require different set of testspecs' do
@podfile = Pod::Podfile.new do
source SpecHelper.test_repo_url
platform :ios, '8.0'
project 'SampleProject/SampleProject'
target 'TestRunner' do
pod 'CoconutLib', :testspecs => ['Tests']
end
target 'SampleProject' do
pod 'CoconutLib'
end
end
@analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile, nil)
result = @analyzer.analyze
result.targets.count.should == 2
result.targets[0].pod_targets.count == 1
result.targets[0].pod_targets[0].name.should == 'CoconutLib'
result.targets[1].pod_targets.count == 1
result.targets[1].pod_targets[0].name.should == 'CoconutLib'
result.targets[0].pod_targets[0].should == result.targets[1].pod_targets[0]
end
end
describe 'deduplication' do
it 'deduplicate targets if possible' do
podfile = Pod::Podfile.new do
source SpecHelper.test_repo_url
platform :ios, '6.0'
project 'SampleProject/SampleProject'
target 'SampleProject' do
pod 'BananaLib'
pod 'monkey'
target 'TestRunner' do
pod 'BananaLib'
pod 'monkey'
end
end
target 'CLITool' do
platform :osx, '10.10'
pod 'monkey'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
result = analyzer.analyze
pod_targets = result.targets.flat_map(&:pod_targets).uniq
Hash[pod_targets.map { |t| [t.label, t.target_definitions.map(&:label).sort] }.sort].should == {
'BananaLib' => %w(Pods-SampleProject Pods-SampleProject-TestRunner),
'monkey-iOS' => %w(Pods-SampleProject Pods-SampleProject-TestRunner),
'monkey-macOS' => %w(Pods-CLITool),
}
end
it "doesn't deduplicate targets across different integration modes" do
podfile = Pod::Podfile.new do
source SpecHelper.test_repo_url
platform :ios, '6.0'
xcodeproj 'SampleProject/SampleProject'
target 'SampleProject' do
use_frameworks!
pod 'BananaLib'
target 'TestRunner' do
use_frameworks!(false)
pod 'BananaLib'
end
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
result = analyzer.analyze
pod_targets = result.targets.flat_map(&:pod_targets).uniq.sort_by(&:name)
Hash[pod_targets.map { |t| [t.label, t.target_definitions.map(&:label)] }].should == {
'BananaLib-library' => %w(Pods-SampleProject-TestRunner),
'BananaLib-framework' => %w(Pods-SampleProject),
'monkey-library' => %w(Pods-SampleProject-TestRunner),
'monkey-framework' => %w(Pods-SampleProject),
}
end
it "doesn't deduplicate targets when deduplication is disabled" do
podfile = Pod::Podfile.new do
install! 'cocoapods', :deduplicate_targets => false
source SpecHelper.test_repo_url
platform :ios, '6.0'
project 'SampleProject/SampleProject'
pod 'BananaLib'
target 'SampleProject' do
target 'TestRunner' do
pod 'BananaLib'
end
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
result = analyzer.analyze
result.targets.flat_map { |at| at.pod_targets.map { |pt| "#{at.name}/#{pt.name}" } }.sort.should == %w(
Pods-SampleProject-TestRunner/BananaLib-Pods-SampleProject-TestRunner
Pods-SampleProject-TestRunner/monkey-Pods-SampleProject-TestRunner
Pods-SampleProject/BananaLib-Pods-SampleProject
Pods-SampleProject/monkey-Pods-SampleProject
).sort
end
it "doesn't deduplicate targets when deduplication is disabled and using frameworks" do
podfile = Pod::Podfile.new do
install! 'cocoapods', :deduplicate_targets => false
source SpecHelper.test_repo_url
platform :ios, '6.0'
project 'SampleProject/SampleProject'
use_frameworks!
pod 'BananaLib'
target 'SampleProject' do
target 'TestRunner' do
pod 'BananaLib'
end
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
result = analyzer.analyze
result.targets.flat_map { |at| at.pod_targets.map { |pt| "#{at.name}/#{pt.name}" } }.sort.should == %w(
Pods-SampleProject-TestRunner/BananaLib-Pods-SampleProject-TestRunner
Pods-SampleProject-TestRunner/monkey-Pods-SampleProject-TestRunner
Pods-SampleProject/BananaLib-Pods-SampleProject
Pods-SampleProject/monkey-Pods-SampleProject
).sort
result.targets.flat_map { |at| at.pod_targets.map(&:requires_frameworks?) }.uniq.should == [true]
end
end
it 'generates the integration library appropriately if the installation will not integrate' do
@analyzer.installation_options.integrate_targets = false
target = @analyzer.analyze.targets.first
target.client_root.should == config.installation_root
target.user_target_uuids.should == []
target.user_build_configurations.should == { 'Release' => :release, 'Debug' => :debug }
target.platform.to_s.should == 'iOS 6.0'
end
describe 'no-integrate platform validation' do
before do
repos = [Source.new(fixture('spec-repos/test_repo'))]
aggregate = Pod::Source::Aggregate.new(repos)
config.sources_manager.stubs(:aggregate).returns(aggregate)
aggregate.sources.first.stubs(:url).returns(SpecHelper.test_repo_url)
end
it 'does not require a platform for an empty target' do
podfile = Pod::Podfile.new do
install! 'cocoapods', :integrate_targets => false
source SpecHelper.test_repo_url
project 'SampleProject/SampleProject'
target 'TestRunner' do
platform :osx
pod 'monkey'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
lambda { analyzer.analyze }.should.not.raise
end
it 'does not raise if a target with dependencies inherits the platform from its parent' do
podfile = Pod::Podfile.new do
install! 'cocoapods', :integrate_targets => false
source SpecHelper.test_repo_url
project 'SampleProject/SampleProject'
platform :osx
target 'TestRunner' do
pod 'monkey'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
lambda { analyzer.analyze }.should.not.raise
end
it 'raises if a target with dependencies does not have a platform' do
podfile = Pod::Podfile.new do
install! 'cocoapods', :integrate_targets => false
source SpecHelper.test_repo_url
project 'SampleProject/SampleProject'
target 'TestRunner' do
pod 'monkey'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
lambda { analyzer.analyze }.should.raise Informative
end
end
it 'returns all the configurations the user has in any of its projects and/or targets' do
target_definition = @analyzer.podfile.target_definition_list.first
target_definition.stubs(:build_configurations).returns('AdHoc' => :test)
@analyzer.analyze.all_user_build_configurations.should == {
'Debug' => :debug,
'Release' => :release,
'AdHoc' => :test,
'Test' => :release,
'App Store' => :release,
}
end
#--------------------------------------#
it 'locks the version of the dependencies which did not change in the Podfile' do
@analyzer.analyze
@analyzer.send(:locked_dependencies).map(&:payload).map(&:to_s).
should == ['JSONKit (= 1.5pre)', 'SVPullToRefresh (= 0.4)']
end
it 'does not lock the dependencies in update mode' do
@analyzer.update = true
@analyzer.analyze
@analyzer.send(:locked_dependencies).to_a.map(&:payload).should == []
end
it 'unlocks dependencies in a case-insensitive manner' do
@analyzer.update = { :pods => %w(JSONKit) }
@analyzer.analyze
@analyzer.send(:locked_dependencies).map(&:payload).map(&:to_s).
should == ['SVPullToRefresh (= 0.4)']
end
it 'unlocks all dependencies with the same root name in update mode' do
podfile = Podfile.new do
platform :ios, '8.0'
project 'SampleProject/SampleProject'
target 'SampleProject' do
pod 'AFNetworking'
pod 'AFNetworkActivityLogger'
end
end
hash = {}
hash['PODS'] = [
{ 'AFNetworkActivityLogger (2.0.3)' => ['AFNetworking/NSURLConnection (~> 2.0)', 'AFNetworking/NSURLSession (~> 2.0)'] },
{ 'AFNetworking (2.4.0)' => ['AFNetworking/NSURLConnection (= 2.4.0)', 'AFNetworking/NSURLSession (= 2.4.0)', 'AFNetworking/Reachability (= 2.4.0)', 'AFNetworking/Security (= 2.4.0)', 'AFNetworking/Serialization (= 2.4.0)', 'AFNetworking/UIKit (= 2.4.0)'] },
{ 'AFNetworking/NSURLConnection (2.4.0)' => ['AFNetworking/Reachability', 'AFNetworking/Security', 'AFNetworking/Serialization'] },
{ 'AFNetworking/NSURLSession (2.4.0)' => ['AFNetworking/Reachability', 'AFNetworking/Security', 'AFNetworking/Serialization'] },
'AFNetworking/Reachability (2.4.0)',
'AFNetworking/Security (2.4.0)',
'AFNetworking/Serialization (2.4.0)',
{ 'AFNetworking/UIKit (2.4.0)' => ['AFNetworking/NSURLConnection', 'AFNetworking/NSURLSession'] },
]
hash['DEPENDENCIES'] = ['AFNetworkActivityLogger', 'AFNetworking (2.4.0)']
hash['SPEC CHECKSUMS'] = {}
hash['COCOAPODS'] = Pod::VERSION
lockfile = Pod::Lockfile.new(hash)
analyzer = Installer::Analyzer.new(config.sandbox, podfile, lockfile)
analyzer.update = { :pods => %w(AFNetworking) }
analyzer.analyze.specifications.
find { |s| s.name == 'AFNetworking' }.
version.to_s.should == '2.6.3'
end
it 'unlocks only local pod when specification checksum changes' do
sandbox = config.sandbox
local_spec = Specification.from_hash('name' => 'LocalPod', 'version' => '1.1', 'dependencies' => { 'Expecta' => ['~> 0.0'] })
sandbox.stubs(:specification).with('LocalPod').returns(local_spec)
podfile = Podfile.new do
platform :ios, '8.0'
project 'SampleProject/SampleProject'
target 'SampleProject' do
pod 'LocalPod', :path => '../'
end
end
hash = {}
hash['PODS'] = ['Expecta (0.2.0)', { 'LocalPod (1.0)' => ['Expecta (~> 0.0)'] }]
hash['DEPENDENCIES'] = ['LocalPod (from `../`)']
hash['EXTERNAL SOURCES'] = { 'LocalPod' => { :path => '../' } }
hash['SPEC CHECKSUMS'] = { 'LocalPod' => 'DUMMY_CHECKSUM' }
hash['COCOAPODS'] = Pod::VERSION
lockfile = Pod::Lockfile.new(hash)
analyzer = Installer::Analyzer.new(sandbox, podfile, lockfile)
analyzer.analyze(false).specifications.
find { |s| s.name == 'LocalPod' }.
version.to_s.should == '1.1'
analyzer.analyze(false).specifications.
find { |s| s.name == 'Expecta' }.
version.to_s.should == '0.2.0'
end
it 'raises if change in local pod specification conflicts with lockfile' do
sandbox = config.sandbox
local_spec = Specification.from_hash('name' => 'LocalPod', 'version' => '1.0', 'dependencies' => { 'Expecta' => ['0.2.2'] })
sandbox.stubs(:specification).with('LocalPod').returns(local_spec)
podfile = Podfile.new do
platform :ios, '8.0'
project 'SampleProject/SampleProject'
target 'SampleProject' do
pod 'LocalPod', :path => '../'
end
end
hash = {}
hash['PODS'] = ['Expecta (0.2.0)', { 'LocalPod (1.0)' => ['Expecta (=0.2.0)'] }]
hash['DEPENDENCIES'] = ['LocalPod (from `../`)']
hash['EXTERNAL SOURCES'] = { 'LocalPod' => { :path => '../' } }
hash['SPEC CHECKSUMS'] = {}
hash['COCOAPODS'] = Pod::VERSION
lockfile = Pod::Lockfile.new(hash)
analyzer = Installer::Analyzer.new(sandbox, podfile, lockfile)
should.raise(Informative) do
analyzer.analyze(false)
end.message.should.match /You should run `pod update Expecta`/
end
#--------------------------------------#
it 'takes into account locked implicit dependencies' do
podfile = Podfile.new do
platform :ios, '8.0'
project 'SampleProject/SampleProject'
target 'SampleProject' do
pod 'ARAnalytics/Mixpanel'
end
end
hash = {}
hash['PODS'] = ['ARAnalytics/CoreIOS (2.8.0)', { 'ARAnalytics/Mixpanel (2.8.0)' => ['ARAnlytics/CoreIOS', 'Mixpanel'] }, 'Mixpanel (2.5.1)']
hash['DEPENDENCIES'] = %w(ARAnalytics/Mixpanel)
hash['SPEC CHECKSUMS'] = {}
hash['COCOAPODS'] = Pod::VERSION
lockfile = Pod::Lockfile.new(hash)
analyzer = Installer::Analyzer.new(config.sandbox, podfile, lockfile)
analyzer.analyze.specifications.
find { |s| s.name == 'Mixpanel' }.
version.to_s.should == '2.5.1'
end
#--------------------------------------#
it 'fetches the dependencies with external sources' do
podfile_state = Installer::Analyzer::SpecsState.new
podfile_state.added << 'BananaLib'
@analyzer.stubs(:result).returns(stub(:podfile_state => podfile_state))
@podfile.stubs(:dependencies).returns([Dependency.new('BananaLib', :git => 'example.com')])
ExternalSources::DownloaderSource.any_instance.expects(:fetch)
@analyzer.send(:fetch_external_sources)
end
it 'does not download the same source multiple times for different subspecs' do
podfile_state = Installer::Analyzer::SpecsState.new
podfile_state.added << 'ARAnalytics'
@analyzer.stubs(:result).returns(stub(:podfile_state => podfile_state))
@podfile.stubs(:dependencies).returns([
Dependency.new('ARAnalytics/Mixpanel', :git => 'https://github.com/orta/ARAnalytics', :commit => '6f1a1c314894437e7e5c09572c276e644dbfb64b'),
Dependency.new('ARAnalytics/HockeyApp', :git => 'https://github.com/orta/ARAnalytics', :commit => '6f1a1c314894437e7e5c09572c276e644dbfb64b'),
])
ExternalSources::DownloaderSource.any_instance.expects(:fetch).once
@analyzer.send(:fetch_external_sources)
end
xit 'it fetches the specification from either the sandbox or from the remote by default' do
dependency = Dependency.new('Name', :git => 'www.example.com')
ExternalSources::DownloaderSource.any_instance.expects(:specification_from_external).returns(Specification.new).once
@resolver.send(:set_from_external_source, dependency)
end
xit 'it fetches the specification from the remote if in update mode' do
dependency = Dependency.new('Name', :git => 'www.example.com')
ExternalSources::DownloaderSource.any_instance.expects(:specification).returns(Specification.new).once
@resolver.update_external_specs = false
@resolver.send(:set_from_external_source, dependency)
end
xit 'it fetches the specification only from the sandbox if pre-downloads are disabled' do
dependency = Dependency.new('Name', :git => 'www.example.com')
Sandbox.any_instance.expects(:specification).returns(Specification.new).once
@resolver.allow_pre_downloads = true
@resolver.send(:set_from_external_source, dependency)
end
#--------------------------------------#
it 'resolves the dependencies' do
@analyzer.analyze.specifications.map(&:to_s).should == [
'AFNetworking (1.0.1)',
'JSONKit (1.5pre)',
'SVPullToRefresh (0.4)',
'libextobjc/EXTKeyPathCoding (0.2.3)',
'libextobjc/EXTSynthesize (0.2.3)',
]
end
it 'adds the specifications to the correspondent libraries' do
@analyzer.analyze.targets[0].pod_targets.map(&:specs).flatten.map(&:to_s).should == [
'AFNetworking (1.0.1)',
'JSONKit (1.5pre)',
'SVPullToRefresh (0.4)',
'libextobjc/EXTKeyPathCoding (0.2.3)',
]
@analyzer.analyze.targets[1].pod_targets.map(&:specs).flatten.map(&:to_s).should == [
'libextobjc/EXTKeyPathCoding (0.2.3)',
'libextobjc/EXTSynthesize (0.2.3)',
]
end
#--------------------------------------#
it 'warns when a dependency is duplicated' do
podfile = Podfile.new do
source 'https://github.com/CocoaPods/Specs.git'
project 'SampleProject/SampleProject'
platform :ios, '8.0'
target 'SampleProject' do
pod 'RestKit', '~> 0.23.0'
pod 'RestKit', '<= 0.23.2'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile, nil)
analyzer.analyze
UI.warnings.should.match /duplicate dependencies on `RestKit`/
UI.warnings.should.match /RestKit \(~> 0.23.0\)/
UI.warnings.should.match /RestKit \(<= 0.23.2\)/
end
it 'raises when specs have incompatible cocoapods requirements' do
analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile, nil)
Specification.any_instance.stubs(:cocoapods_version).returns(Requirement.create '= 0.1.0')
should.raise(Informative) { analyzer.analyze }
end
#--------------------------------------#
it 'computes the state of the Sandbox respect to the resolved dependencies' do
@analyzer.stubs(:lockfile).returns(nil)
state = @analyzer.analyze.sandbox_state
state.added.sort.should == %w(AFNetworking JSONKit SVPullToRefresh libextobjc)
end
#-------------------------------------------------------------------------#
it 'does include pod target if any spec is not used by tests only and is part of target definition' do
spec1 = Resolver::ResolverSpecification.new(stub, false)
spec2 = Resolver::ResolverSpecification.new(stub, true)
target_definition = stub
pod_target = stub(:name => 'Pod1', :target_definitions => [target_definition], :specs => [spec1.spec, spec2.spec])
resolver_specs_by_target = { target_definition => [spec1, spec2] }
@analyzer.send(:filter_pod_targets_for_target_definition, target_definition, [pod_target], resolver_specs_by_target).should == [pod_target]
end
it 'does not include pod target if its used by tests only' do
spec1 = Resolver::ResolverSpecification.new(stub, true)
spec2 = Resolver::ResolverSpecification.new(stub, true)
target_definition = stub
pod_target = stub(:name => 'Pod1', :target_definitions => [target_definition], :specs => [spec1.spec, spec2.spec])
resolver_specs_by_target = { target_definition => [spec1, spec2] }
@analyzer.send(:filter_pod_targets_for_target_definition, target_definition, [pod_target], resolver_specs_by_target).should.be.empty
end
it 'does not include pod target if its not part of the target definition' do
spec = Resolver::ResolverSpecification.new(stub, false)
target_definition = stub
pod_target = stub(:name => 'Pod1', :target_definitions => [], :specs => [spec.spec])
resolver_specs_by_target = { target_definition => [spec] }
@analyzer.send(:filter_pod_targets_for_target_definition, target_definition, [pod_target], resolver_specs_by_target).should.be.empty
end
describe 'extension targets' do
before do
SpecHelper.create_sample_app_copy_from_fixture('Sample Extensions Project')
@podfile = Pod::Podfile.new do
source SpecHelper.test_repo_url
platform :ios, '6.0'
project 'Sample Extensions Project/Sample Extensions Project'
target 'Sample Extensions Project' do
pod 'JSONKit', '1.4'
end
target 'Today Extension' do
pod 'monkey'
end
end
end
it 'copies extension pod targets to host target, when use_frameworks!' do
@podfile.use_frameworks!
analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile)
result = analyzer.analyze
result.targets.flat_map { |at| at.pod_targets.map { |pt| "#{at.name}/#{pt.name}" } }.sort.should == [
'Pods-Sample Extensions Project/JSONKit',
'Pods-Sample Extensions Project/monkey',
'Pods-Today Extension/monkey',
].sort
end
it 'does not copy extension pod targets to host target, when not use_frameworks!' do
analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile)
result = analyzer.analyze
result.targets.flat_map { |at| at.pod_targets.map { |pt| "#{at.name}/#{pt.name}" } }.sort.should == [
'Pods-Sample Extensions Project/JSONKit',
'Pods-Today Extension/monkey',
].sort
end
it "copy a framework's pod target, when the framework is in a sub project" do
podfile = Pod::Podfile.new do
source SpecHelper.test_repo_url
use_frameworks!
platform :ios, '8.0'
project 'SampleProject/SampleProject'
target 'SampleProject' do
pod 'JSONKit'
end
target 'SampleFramework' do
project 'SampleProject/Sample Lib/Sample Lib'
pod 'monkey'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
result = analyzer.analyze
result.targets.select { |at| at.name == 'Pods-SampleProject' }.flat_map(&:pod_targets).map(&:name).sort.uniq.should == %w(
JSONKit
monkey
).sort
end
it "copy a static library's pod target, when the static library is in a sub project" do
podfile = Pod::Podfile.new do
source SpecHelper.test_repo_url
platform :ios, '8.0'
project 'SampleProject/SampleProject'
target 'SampleProject' do
pod 'JSONKit'
end
target 'SampleLib' do
project 'SampleProject/Sample Lib/Sample Lib'
pod 'monkey'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
result = analyzer.analyze
result.targets.select { |at| at.name == 'Pods-SampleProject' }.flat_map(&:pod_targets).map(&:name).sort.uniq.should == %w(
JSONKit
monkey
).sort
end
it "raises when unable to find an extension's host target" do
podfile = Pod::Podfile.new do
source SpecHelper.test_repo_url
use_frameworks!
platform :ios, '8.0'
project 'Sample Extensions Project/Sample Extensions Project'
target 'Today Extension' do
pod 'monkey'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
should.raise Informative do
analyzer.analyze
end.message.should.match /Unable to find host target\(s\) for Today Extension. Please add the host targets for the embedded targets to the Podfile\./
end
it 'warns when using a Podfile for framework-only projects' do
podfile = Pod::Podfile.new do
source SpecHelper.test_repo_url
use_frameworks!
platform :ios, '8.0'
target 'SampleLib' do
project 'SampleProject/Sample Lib/Sample Lib'
pod 'monkey'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
analyzer.analyze
UI.warnings.should.match /The Podfile contains framework or static library targets, for which the Podfile does not contain host targets \(targets which embed the framework\)\./
end
it 'warns when using a Podfile for framework-only projects' do
podfile = Pod::Podfile.new do
source SpecHelper.test_repo_url
use_frameworks!
platform :ios, '8.0'
target 'SampleFramework' do
project 'SampleProject/Sample Lib/Sample Lib'
pod 'monkey'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
analyzer.analyze
UI.warnings.should.match /The Podfile contains framework or static library targets, for which the Podfile does not contain host targets \(targets which embed the framework\)\./
end
it 'raises when the extension calls use_frameworks!, but the host target does not' do
podfile = Pod::Podfile.new do
source SpecHelper.test_repo_url
platform :ios, '8.0'
project 'Sample Extensions Project/Sample Extensions Project'
target 'Sample Extensions Project' do
pod 'JSONKit', '1.4'
end
target 'Today Extension' do
use_frameworks!
pod 'monkey'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
should.raise Informative do
analyzer.analyze
end.message.should.match /Sample Extensions Project \(false\) and Today Extension \(true\) do not both set use_frameworks!\./
end
end
#-------------------------------------------------------------------------#
describe 'Private helpers' do
describe '#sources' do
describe 'when there are no explicit sources' do
it 'defaults to the master spec repository' do
@analyzer.send(:sources).map(&:url).should ==
['https://github.com/CocoaPods/Specs.git']
end
end
describe 'when there are explicit sources' do
it 'raises if no specs repo with that URL could be added' do
podfile = Podfile.new do
source 'not-a-git-repo'
pod 'JSONKit', '1.4'
end
@analyzer.instance_variable_set(:@podfile, podfile)
should.raise Informative do
@analyzer.send(:sources)
end.message.should.match /Unable to add/
end
it 'fetches a specs repo that is specified by the podfile' do
podfile = Podfile.new do
source 'https://github.com/artsy/Specs.git'
pod 'JSONKit', '1.4'
end
@analyzer.instance_variable_set(:@podfile, podfile)
config.sources_manager.expects(:find_or_create_source_with_url).once
@analyzer.send(:sources)
end
end
end
end
end
describe 'Analysis, concerning naming' do
before do
SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
end
it 'raises when dependencies with the same name have different ' \
'external sources' do
podfile = Podfile.new do
source 'https://github.com/CocoaPods/Specs.git'
project 'SampleProject/SampleProject'
platform :ios
target 'SampleProject' do
pod 'SEGModules', :git => 'https://github.com/segiddins/SEGModules.git'
pod 'SEGModules', :git => 'https://github.com/segiddins/Modules.git'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile, nil)
e = should.raise(Informative) { analyzer.analyze }
e.message.should.match /different sources for `SEGModules`/
e.message.should.match %r{SEGModules \(from `https://github.com/segiddins/SEGModules.git`\)}
e.message.should.match %r{SEGModules \(from `https://github.com/segiddins/Modules.git`\)}
end
it 'raises when dependencies with the same root name have different ' \
'external sources' do
podfile = Podfile.new do
source 'https://github.com/CocoaPods/Specs.git'
project 'SampleProject/SampleProject'
platform :ios
target 'SampleProject' do
pod 'RestKit/Core', :git => 'https://github.com/RestKit/RestKit.git'
pod 'RestKit', :git => 'https://github.com/segiddins/RestKit.git'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile, nil)
e = should.raise(Informative) { analyzer.analyze }
e.message.should.match /different sources for `RestKit`/
e.message.should.match %r{RestKit/Core \(from `https://github.com/RestKit/RestKit.git`\)}
e.message.should.match %r{RestKit \(from `https://github.com/segiddins/RestKit.git`\)}
end
it 'raises when dependencies with the same name have different ' \
'external sources with one being nil' do
podfile = Podfile.new do
source 'https://github.com/CocoaPods/Specs.git'
project 'SampleProject/SampleProject'
platform :ios
target 'SampleProject' do
pod 'RestKit', :git => 'https://github.com/RestKit/RestKit.git'
pod 'RestKit', '~> 0.23.0'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile, nil)
e = should.raise(Informative) { analyzer.analyze }
e.message.should.match /different sources for `RestKit`/
e.message.should.match %r{RestKit \(from `https://github.com/RestKit/RestKit.git`\)}
e.message.should.match /RestKit \(~> 0.23.0\)/
end
end
describe 'podfile validation' do
before do
@sandbox = stub('Sandbox')
@podfile = stub('Podfile')
@analyzer = Installer::Analyzer.new(@sandbox, @podfile)
end
it 'raises when validating errors' do
Installer::PodfileValidator.any_instance.expects(:validate)
Installer::PodfileValidator.any_instance.expects(:valid?).returns(false)
Installer::PodfileValidator.any_instance.stubs(:errors).returns(['ERROR'])
should.raise(Informative) { @analyzer.send(:validate_podfile!) }.
message.should.match /ERROR/
end
it 'warns when validating has warnings' do
Installer::PodfileValidator.any_instance.expects(:validate)
Installer::PodfileValidator.any_instance.expects(:valid?).returns(true)
Installer::PodfileValidator.any_instance.stubs(:warnings).returns(['The Podfile does not contain any dependencies.'])
@analyzer.send(:validate_podfile!)
UI.warnings.should == "The Podfile does not contain any dependencies.\n"
end
end
describe 'using lockfile checkout options' do
before do
@podfile = Pod::Podfile.new do
target 'SampleProject' do
pod 'BananaLib', :git => 'example.com'
end
end
@dependency = @podfile.dependencies.first
@lockfile_checkout_options = { :git => 'example.com', :commit => 'commit' }
hash = {}
hash['PODS'] = ['BananaLib (1.0.0)']
hash['CHECKOUT OPTIONS'] = { 'BananaLib' => @lockfile_checkout_options }
hash['SPEC CHECKSUMS'] = {}
hash['COCOAPODS'] = Pod::VERSION
@lockfile = Pod::Lockfile.new(hash)
@analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile, @lockfile)
end
it 'returns that an update is required when there is no sandbox manifest' do
@analyzer.sandbox.stubs(:manifest).returns(nil)
@analyzer.should.send(:checkout_requires_update?, @dependency)
end
before do
@sandbox_manifest = Pod::Lockfile.new(@lockfile.internal_data.deep_dup)
@analyzer.sandbox.manifest = @sandbox_manifest
@analyzer.sandbox.stubs(:specification).with('BananaLib').returns(stub)
pod_dir = stub
pod_dir.stubs(:directory?).returns(true)
@analyzer.sandbox.stubs(:pod_dir).with('BananaLib').returns(pod_dir)
end
it 'returns whether or not an update is required' do
@analyzer.send(:checkout_requires_update?, @dependency).should == false
@sandbox_manifest.send(:checkout_options_data).delete('BananaLib')
@analyzer.send(:checkout_requires_update?, @dependency).should == true
end
before do
@analyzer.result = Installer::Analyzer::AnalysisResult.new
@analyzer.result.podfile_state = Installer::Analyzer::SpecsState.new
end
it 'uses lockfile checkout options when no source exists in the sandbox' do
@analyzer.result.podfile_state.unchanged << 'BananaLib'
@sandbox_manifest.send(:checkout_options_data).delete('BananaLib')
downloader = stub('DownloaderSource')
ExternalSources.stubs(:from_params).with(@lockfile_checkout_options, @dependency, @podfile.defined_in_file).returns(downloader)
downloader.expects(:fetch)
downloader.expects(:can_cache=).with(true).once
@analyzer.send(:fetch_external_sources)
end
it 'uses lockfile checkout options when a different checkout exists in the sandbox' do
@analyzer.result.podfile_state.unchanged << 'BananaLib'
@sandbox_manifest.send(:checkout_options_data)['BananaLib'] = @lockfile_checkout_options.merge(:commit => 'other commit')
downloader = stub('DownloaderSource')
ExternalSources.stubs(:from_params).with(@lockfile_checkout_options, @dependency, @podfile.defined_in_file).returns(downloader)
downloader.expects(:fetch)
downloader.expects(:can_cache=).with(true).once
@analyzer.send(:fetch_external_sources)
end
it 'ignores lockfile checkout options when the podfile state has changed' do
@analyzer.result.podfile_state.changed << 'BananaLib'
downloader = stub('DownloaderSource')
ExternalSources.stubs(:from_params).with(@dependency.external_source, @dependency, @podfile.defined_in_file).returns(downloader)
downloader.expects(:fetch)
downloader.expects(:can_cache=).with(true).once
@analyzer.send(:fetch_external_sources)
end
it 'ignores lockfile checkout options when updating selected pods' do
@analyzer.result.podfile_state.unchanged << 'BananaLib'
@analyzer.stubs(:update).returns(:pods => %w(BananaLib))
downloader = stub('DownloaderSource')
ExternalSources.stubs(:from_params).with(@dependency.external_source, @dependency, @podfile.defined_in_file).returns(downloader)
downloader.expects(:fetch)
downloader.expects(:can_cache=).with(true).once
@analyzer.send(:fetch_external_sources)
end
it 'ignores lockfile checkout options when updating all pods' do
@analyzer.result.podfile_state.unchanged << 'BananaLib'
@analyzer.stubs(:update).returns(true)
downloader = stub('DownloaderSource')
ExternalSources.stubs(:from_params).with(@dependency.external_source, @dependency, @podfile.defined_in_file).returns(downloader)
downloader.expects(:fetch)
downloader.expects(:can_cache=).with(true).once
@analyzer.send(:fetch_external_sources)
end
it 'does not use the cache when the podfile instructs not to clean' do
@analyzer.result.podfile_state.unchanged << 'BananaLib'
@sandbox_manifest.send(:checkout_options_data).delete('BananaLib')
downloader = stub('DownloaderSource')
ExternalSources.stubs(:from_params).with(@lockfile_checkout_options, @dependency, @podfile.defined_in_file).returns(downloader)
downloader.expects(:fetch)
downloader.expects(:can_cache=).with(false).once
@analyzer.installation_options.clean = false
@analyzer.send(:fetch_external_sources)
end
it 'does not re-fetch the external source when the sandbox has the correct revision of the source' do
@analyzer.result.podfile_state.unchanged << 'BananaLib'
@analyzer.expects(:fetch_external_source).never
@analyzer.send(:fetch_external_sources)
end
end
end
end