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