require File.expand_path('../../spec_helper', __FILE__)

require 'cocoapods_stats/sender'

# @return [Lockfile]
#
def generate_lockfile(lockfile_version: Pod::VERSION)
  hash = {}
  hash['PODS'] = []
  hash['DEPENDENCIES'] = []
  hash['SPEC CHECKSUMS'] = {}
  hash['COCOAPODS'] = lockfile_version
  Pod::Lockfile.new(hash)
end

# @return [Podfile]
#
def generate_podfile(pods = ['JSONKit'])
  Pod::Podfile.new do
    platform :ios
    project SpecHelper.fixture('SampleProject/SampleProject'), 'Test' => :debug, 'App Store' => :release
    target 'SampleProject' do
      pods.each { |name| pod name }
      target 'SampleProjectTests' do
        inherit! :search_paths
      end
    end
  end
end

# @return [Podfile]
#
def generate_local_podfile
  Pod::Podfile.new do
    platform :ios
    project SpecHelper.fixture('SampleProject/SampleProject'), 'Test' => :debug, 'App Store' => :release
    target 'SampleProject' do
      pod 'Reachability', :path => SpecHelper.fixture('integration/Reachability')
      target 'SampleProjectTests' do
        inherit! :search_paths
      end
    end
  end
end

#-----------------------------------------------------------------------------#

module Pod
  describe Installer do
    before do
      CocoaPodsStats::Sender.any_instance.stubs(:send)
      podfile = generate_podfile
      lockfile = generate_lockfile
      @installer = Installer.new(config.sandbox, podfile, lockfile)
      @installer.installation_options.integrate_targets = false
    end

    #-------------------------------------------------------------------------#

    describe 'In general' do
      before do
        @installer.stubs(:resolve_dependencies)
        @installer.stubs(:download_dependencies)
        @installer.stubs(:verify_no_duplicate_framework_names)
        @installer.stubs(:verify_no_static_framework_transitive_dependencies)
        @installer.stubs(:verify_framework_usage)
        @installer.stubs(:generate_pods_project)
        @installer.stubs(:integrate_user_project)
        @installer.stubs(:run_plugins_post_install_hooks)
        @installer.stubs(:ensure_plugins_are_installed!)
        @installer.stubs(:perform_post_install_actions)
      end

      it 'in runs the pre-install hooks before cleaning the Pod sources' do
        @installer.unstub(:download_dependencies)
        @installer.stubs(:create_file_accessors)
        @installer.stubs(:install_pod_sources)
        def @installer.run_podfile_pre_install_hooks
          @hook_called = true
        end

        def @installer.clean_pod_sources
          @hook_called.should.be.true
        end
        @installer.install!
      end

      it 'in runs the post-install hooks before serializing the Pods project' do
        @installer.stubs(:prepare_pods_project)
        @installer.stubs(:run_podfile_pre_install_hooks)
        @installer.stubs(:install_file_references)
        @installer.stubs(:install_libraries)
        @installer.stubs(:set_target_dependencies)
        @installer.stubs(:write_lockfiles)
        @installer.stubs(:aggregate_targets).returns([])
        @installer.unstub(:generate_pods_project)
        def @installer.run_podfile_post_install_hooks
          @hook_called = true
        end

        def @installer.write_pod_project
          @hook_called.should.be.true
        end
        @installer.install!
      end

      it 'runs source provider hooks before analyzing' do
        @installer.unstub(:resolve_dependencies)
        @installer.stubs(:validate_build_configurations)
        @installer.stubs(:clean_sandbox)
        def @installer.run_source_provider_hooks
          @hook_called = true
        end

        def @installer.analyze(*)
          @hook_called.should.be.true
        end
        @installer.install!
      end

      it 'includes sources from source provider plugins' do
        plugin_name = 'test-plugin'
        Pod::HooksManager.register(plugin_name, :source_provider) do |context, options|
          source_url = options['sources'].first
          return unless source_url
          source = Pod::Source.new(source_url)
          context.add_source(source)
        end

        test_source_name = 'https://github.com/artsy/Specs.git'
        plugins_hash = Installer::DEFAULT_PLUGINS.merge(plugin_name => { 'sources' => [test_source_name] })
        @installer.podfile.stubs(:plugins).returns(plugins_hash)
        @installer.unstub(:resolve_dependencies)
        @installer.stubs(:validate_build_configurations)
        @installer.stubs(:clean_sandbox)
        @installer.stubs(:ensure_plugins_are_installed!)
        @installer.stubs(:analyze)

        analyzer = Installer::Analyzer.new(config.sandbox, @installer.podfile, @installer.lockfile)
        analyzer.stubs(:analyze)
        @installer.stubs(:create_analyzer).returns(analyzer)
        @installer.install!

        source = Pod::Source.new(test_source_name)
        names = analyzer.sources.map(&:name)
        names.should.include(source.name)
      end

      it 'integrates the user targets if the corresponding config is set' do
        @installer.installation_options.integrate_targets = true
        @installer.expects(:integrate_user_project)
        @installer.install!
      end

      it "doesn't integrates the user targets if the corresponding config is not set" do
        @installer.installation_options.integrate_targets = false
        @installer.expects(:integrate_user_project).never
        @installer.install!
      end

      it 'prints a list of deprecated pods' do
        spec = Spec.new
        spec.name = 'RestKit'
        spec.deprecated_in_favor_of = 'AFNetworking'
        spec_two = Spec.new
        spec_two.name = 'BlocksKit'
        spec_two.deprecated = true
        @installer.stubs(:root_specs).returns([spec, spec_two])
        @installer.send(:warn_for_deprecations)
        UI.warnings.should.include 'deprecated in favor of AFNetworking'
        UI.warnings.should.include 'BlocksKit has been deprecated'
      end

      it 'does not raise if command is run outside sandbox directory' do
        Dir.chdir(@installer.sandbox.root.parent) do
          should.not.raise(Informative) { @installer.install! }
        end
      end

      it 'raises if command is run in sandbox directory' do
        Dir.chdir(@installer.sandbox.root) do
          should.raise Informative do
            @installer.install!
          end.message.should.match /should.*run.*outside.*Pods directory.*Current directory.*\./m
        end
      end

      describe 'handling CocoaPods version updates' do
        it 'does not deintegrate when there is no lockfile' do
          installer = Pod::Installer.new(config.sandbox, generate_podfile, nil)
          UI.expects(:section).never
          installer.send(:deintegrate_if_different_major_version)
        end

        it 'does not deintegrate when the major version is the same' do
          should_not_deintegrate = %w(1.0.0 1.0.1 1.1.0 1.2.2)
          should_not_deintegrate.each do |version|
            lockfile = generate_lockfile(:lockfile_version => version)
            installer = Pod::Installer.new(config.sandbox, generate_podfile, lockfile)
            Pathname.expects(:glob).never
            installer.send(:deintegrate_if_different_major_version)
          end
        end

        it 'does deintegrate when the major version is different' do
          should_not_deintegrate = %w(0.39.0 2.0.0 10.0-beta)
          should_not_deintegrate.each do |version|
            lockfile = generate_lockfile(:lockfile_version => version)
            installer = Pod::Installer.new(config.sandbox, generate_podfile, lockfile)
            project = fixture('SampleProject/SampleProject.xcodeproj')
            Pathname.expects(:glob).with(config.installation_root + '*.xcodeproj').returns([project])
            Deintegrator.any_instance.expects(:deintegrate_project)
            installer.send(:deintegrate_if_different_major_version)
          end
        end
      end
    end

    #-------------------------------------------------------------------------#

    describe '#determine_dependency_product_type' do
      it 'does propagate that frameworks are required to all pod targets' do
        Sandbox::FileAccessor.any_instance.stubs(:vendored_libraries).returns([])
        fixture_path = ROOT + 'spec/fixtures'
        config.repos_dir = fixture_path + 'spec-repos'
        podfile = Pod::Podfile.new do
          platform :ios, '8.0'
          project 'SampleProject/SampleProject'
          use_frameworks!
          pod 'BananaLib',       :path => (fixture_path + 'banana-lib').to_s
          pod 'OrangeFramework', :path => (fixture_path + 'orange-framework').to_s
          pod 'matryoshka',      :path => (fixture_path + 'matryoshka').to_s
          pod 'monkey',          :path => (fixture_path + 'monkey').to_s

          target 'SampleProject'
          target 'TestRunner' do
            inherit! :search_paths
            pod 'monkey', :path => (fixture_path + 'monkey').to_s
          end
        end
        lockfile = generate_lockfile

        @installer = Installer.new(config.sandbox, podfile, lockfile)
        @installer.installation_options.integrate_targets = false
        @installer.install!

        target = @installer.aggregate_targets.first
        target.requires_frameworks?.should == true
        target.pod_targets.select(&:requires_frameworks?).map(&:name).sort.should == %w(
          BananaLib
          OrangeFramework
          matryoshka
          monkey
        )
      end
    end

    #-------------------------------------------------------------------------#

    describe '#verify_no_duplicate_framework_names' do
      it 'detects duplicate framework names' do
        Sandbox::FileAccessor.any_instance.stubs(:vendored_frameworks).
          returns([Pathname('a/monkey.framework')]).then.
          returns([Pathname('b/monkey.framework')]).then.
          returns([Pathname('c/monkey.framework')])
        fixture_path = ROOT + 'spec/fixtures'
        config.repos_dir = fixture_path + 'spec-repos'
        podfile = Pod::Podfile.new do
          platform :ios, '8.0'
          project 'SampleProject/SampleProject'
          pod 'BananaLib',       :path => (fixture_path + 'banana-lib').to_s
          pod 'OrangeFramework', :path => (fixture_path + 'orange-framework').to_s
          pod 'matryoshka',      :path => (fixture_path + 'matryoshka').to_s
          pod 'monkey',          :path => (fixture_path + 'monkey').to_s
          target 'SampleProject'
        end
        lockfile = generate_lockfile

        @installer = Installer.new(config.sandbox, podfile, lockfile)
        @installer.installation_options.integrate_targets = false
        should.raise(Informative) { @installer.install! }.message.should.match /conflict.*monkey/
      end

      it 'allows duplicate references to the same expanded framework path' do
        Sandbox::FileAccessor.any_instance.stubs(:vendored_frameworks).returns([fixture('monkey/dynamic-monkey.framework')])
        Sandbox::FileAccessor.any_instance.stubs(:dynamic_binary?).returns(true)
        fixture_path = ROOT + 'spec/fixtures'
        config.repos_dir = fixture_path + 'spec-repos'
        podfile = Pod::Podfile.new do
          platform :ios, '8.0'
          project 'SampleProject/SampleProject'
          use_frameworks!
          pod 'BananaLib',       :path => (fixture_path + 'banana-lib').to_s
          pod 'monkey',          :path => (fixture_path + 'monkey').to_s
          target 'SampleProject'
        end
        lockfile = generate_lockfile

        @installer = Installer.new(config.sandbox, podfile, lockfile)
        @installer.installation_options.integrate_targets = false
        should.not.raise(Informative) { @installer.install! }
      end
    end

    #-------------------------------------------------------------------------#

    describe '#verify_no_static_framework_transitive_dependencies' do
      before do
        fixture_path = ROOT + 'spec/fixtures'
        config.repos_dir = fixture_path + 'spec-repos'
        @podfile = Pod::Podfile.new do
          install! 'cocoapods', 'integrate_targets' => false
          platform :ios, '8.0'
          project 'SampleProject/SampleProject'
          use_frameworks!
          pod 'BananaLib',       :path => (fixture_path + 'banana-lib').to_s
          pod 'OrangeFramework', :path => (fixture_path + 'orange-framework').to_s
          pod 'matryoshka',      :path => (fixture_path + 'matryoshka').to_s
          pod 'monkey',          :path => (fixture_path + 'monkey').to_s
          target 'SampleProject'
        end
        @lockfile = generate_lockfile

        @file = Pathname('/yolo.m')
        @file.stubs(:realpath).returns(@file)

        @lib_thing = Pathname('/libThing.a')
        @lib_thing.stubs(:realpath).returns(@lib_thing)
      end

      it 'detects transitive static dependencies which are linked directly to the user target' do
        Sandbox::FileAccessor.any_instance.stubs(:vendored_libraries).returns([@lib_thing])
        @installer = Installer.new(config.sandbox, @podfile, @lockfile)
        should.raise(Informative) { @installer.install! }.message.should.match /transitive.*libThing/
      end

      it 'allows transitive static dependencies which contain other source code' do
        Sandbox::FileAccessor.any_instance.stubs(:source_files).returns([@file])
        Sandbox::FileAccessor.any_instance.stubs(:vendored_libraries).returns([@lib_thing])
        @installer = Installer.new(config.sandbox, @podfile, @lockfile)
        should.not.raise(Informative) { @installer.install! }
      end

      it 'allows transitive static dependencies when both dependencies are linked against the user target' do
        PodTarget.any_instance.stubs(:should_build? => false)
        Sandbox::FileAccessor.any_instance.stubs(:vendored_libraries).returns([@lib_thing])
        @installer = Installer.new(config.sandbox, @podfile, @lockfile)
        should.not.raise(Informative) { @installer.install! }
      end
    end

    #-------------------------------------------------------------------------#

    describe '#verify_framework_usage' do
      it 'raises when Swift pods are used without explicit `use_frameworks!`' do
        fixture_path = ROOT + 'spec/fixtures'
        config.repos_dir = fixture_path + 'spec-repos'
        podfile = Pod::Podfile.new do
          platform :ios, '8.0'
          project 'SampleProject/SampleProject'
          pod 'OrangeFramework', :path => (fixture_path + 'orange-framework').to_s
          pod 'matryoshka',      :path => (fixture_path + 'matryoshka').to_s
          target 'SampleProject'
        end
        lockfile = generate_lockfile

        @installer = Installer.new(config.sandbox, podfile, lockfile)
        @installer.installation_options.integrate_targets = false
        should.raise(Informative) { @installer.install! }.message.should.match /use_frameworks/
      end
    end

    #-------------------------------------------------------------------------#

    describe 'Dependencies Resolution' do
      describe 'updating spec repos' do
        it 'does not updates the repositories by default' do
          config.sources_manager.expects(:update).never
          @installer.send(:resolve_dependencies)
        end

        it 'updates the repositories if that was requested' do
          @installer.repo_update = true
          config.sources_manager.expects(:update).once
          @installer.send(:resolve_dependencies)
        end
      end

      #--------------------------------------#

      describe '#analyze' do
        it 'prints a warning if the version of the Lockfile is higher than the one of the executable' do
          Lockfile.any_instance.stubs(:cocoapods_version).returns(Version.new('999'))
          STDERR.expects(:puts)
          @installer.send(:analyze)
        end

        it 'analyzes the Podfile, the Lockfile and the Sandbox' do
          @installer.send(:analyze)
          @installer.analysis_result.sandbox_state.added.should == Set.new(%w(JSONKit))
        end

        it 'stores the targets created by the analyzer' do
          @installer.send(:analyze)
          @installer.aggregate_targets.map(&:name).sort.should == ['Pods-SampleProject', 'Pods-SampleProjectTests']
          @installer.pod_targets.map(&:name).sort.should == ['JSONKit']
        end

        it 'configures the analyzer to use update mode if appropriate' do
          @installer.update = true
          Installer::Analyzer.any_instance.expects(:update=).with(true)
          @installer.send(:analyze)
          @installer.aggregate_targets.map(&:name).sort.should == ['Pods-SampleProject', 'Pods-SampleProjectTests']
          @installer.pod_targets.map(&:name).sort.should == ['JSONKit']
        end
      end

      #--------------------------------------#

      describe '#validate_whitelisted_configurations' do
        it "raises when a whitelisted configuration doesn’t exist in the user's project" do
          target_definition = @installer.podfile.target_definitions.values.first
          target_definition.whitelist_pod_for_configuration('JSONKit', 'YOLO')
          @installer.send(:analyze)
          should.raise Informative do
            @installer.send(:validate_build_configurations)
          end
        end

        it "does not raise if all whitelisted configurations exist in the user's project" do
          target_definition = @installer.podfile.target_definitions.values.first
          target_definition.whitelist_pod_for_configuration('JSONKit', 'Test')
          @installer.send(:analyze)
          should.not.raise do
            @installer.send(:validate_build_configurations)
          end
        end
      end

      #--------------------------------------#

      describe '#clean_sandbox' do
        before do
          @analysis_result = Installer::Analyzer::AnalysisResult.new
          @analysis_result.specifications = []
          @analysis_result.sandbox_state = Installer::Analyzer::SpecsState.new
          @pod_targets = [PodTarget.new([stub('Spec')], [fixture_target_definition], config.sandbox)]
          @installer.stubs(:analysis_result).returns(@analysis_result)
          @installer.stubs(:pod_targets).returns(@pod_targets)
        end

        it 'cleans the header stores' do
          config.sandbox.public_headers.expects(:implode!)
          @installer.pod_targets.each do |pods_target|
            pods_target.build_headers.expects(:implode!)
          end
          @installer.send(:clean_sandbox)
        end

        it 'deletes the sources of the removed Pods' do
          @analysis_result.sandbox_state.add_name('Deleted-Pod', :deleted)
          config.sandbox.expects(:clean_pod).with('Deleted-Pod')
          @installer.send(:clean_sandbox)
        end
      end
    end

    #-------------------------------------------------------------------------#

    describe 'Downloading dependencies' do
      describe '#install_pod_sources' do
        it 'installs all the Pods which are marked as needing installation' do
          spec = fixture_spec('banana-lib/BananaLib.podspec')
          spec_2 = Spec.new
          spec_2.name = 'RestKit'
          @installer.stubs(:root_specs).returns([spec, spec_2])
          sandbox_state = Installer::Analyzer::SpecsState.new
          sandbox_state.added << 'BananaLib'
          sandbox_state.changed << 'RestKit'
          @installer.stubs(:sandbox_state).returns(sandbox_state)
          @installer.expects(:install_source_of_pod).with('BananaLib')
          @installer.expects(:install_source_of_pod).with('RestKit')
          @installer.send(:install_pod_sources)
        end

        it 'correctly configures the Pod source installer' do
          spec = fixture_spec('banana-lib/BananaLib.podspec')
          pod_target = PodTarget.new([spec], [fixture_target_definition], config.sandbox)
          pod_target.stubs(:platform).returns(:ios)
          @installer.stubs(:pod_targets).returns([pod_target])
          @installer.instance_variable_set(:@installed_specs, [])
          Installer::PodSourceInstaller.any_instance.expects(:install!)
          @installer.send(:install_source_of_pod, 'BananaLib')
        end

        it 'maintains the list of the installed specs' do
          spec = fixture_spec('banana-lib/BananaLib.podspec')
          pod_target = PodTarget.new([spec], [fixture_target_definition], config.sandbox)
          pod_target.stubs(:platform).returns(:ios)
          @installer.stubs(:pod_targets).returns([pod_target, pod_target])
          @installer.instance_variable_set(:@installed_specs, [])
          Installer::PodSourceInstaller.any_instance.stubs(:install!)
          @installer.send(:install_source_of_pod, 'BananaLib')
          @installer.installed_specs.should == [spec]
        end

        it 'prints the previous version of a pod while updating the spec' do
          spec = Spec.new
          spec.name = 'RestKit'
          spec.version = '2.0'
          manifest = Lockfile.new({})
          manifest.stubs(:version).with('RestKit').returns('1.0')
          @installer.sandbox.stubs(:manifest).returns(manifest)
          @installer.stubs(:root_specs).returns([spec])
          sandbox_state = Installer::Analyzer::SpecsState.new
          sandbox_state.changed << 'RestKit'
          @installer.stubs(:sandbox_state).returns(sandbox_state)
          @installer.expects(:install_source_of_pod).with('RestKit')
          @installer.send(:install_pod_sources)
          UI.output.should.include 'was 1.0'
        end

        #--------------------------------------#

        describe '#clean' do
          it 'it cleans only if the config instructs to do it' do
            @installer.installation_options.clean = false
            @installer.send(:clean_pod_sources)
            Installer::PodSourceInstaller.any_instance.expects(:install!).never
          end
        end

        #--------------------------------------#
      end
    end

    #-------------------------------------------------------------------------#

    describe 'Generating pods project' do
      describe '#prepare_pods_project' do
        before do
          @installer.stubs(:aggregate_targets).returns([])
        end

        it "creates build configurations for all of the user's targets" do
          @installer.installation_options.integrate_targets = true
          @installer.send(:analyze)
          @installer.send(:prepare_pods_project)
          @installer.pods_project.build_configurations.map(&:name).sort.should == ['App Store', 'Debug', 'Release', 'Test']
        end

        it 'sets STRIP_INSTALLED_PRODUCT to NO for all configurations for the whole project' do
          @installer.installation_options.integrate_targets = true
          @installer.send(:analyze)
          @installer.send(:prepare_pods_project)
          @installer.pods_project.build_settings('Debug')['STRIP_INSTALLED_PRODUCT'].should == 'NO'
          @installer.pods_project.build_settings('Test')['STRIP_INSTALLED_PRODUCT'].should == 'NO'
          @installer.pods_project.build_settings('Release')['STRIP_INSTALLED_PRODUCT'].should == 'NO'
          @installer.pods_project.build_settings('App Store')['STRIP_INSTALLED_PRODUCT'].should == 'NO'
        end

        before do
          @installer.stubs(:analysis_result).returns(stub(:all_user_build_configurations => {}, :target_inspections => nil))
        end

        it 'creates the Pods project' do
          @installer.send(:prepare_pods_project)
          @installer.pods_project.class.should == Pod::Project
        end

        it 'preserves Pod paths specified as absolute or rooted to home' do
          local_podfile = generate_local_podfile
          local_installer = Installer.new(config.sandbox, local_podfile)
          local_installer.send(:analyze)
          local_installer.send(:prepare_pods_project)
          group = local_installer.pods_project.group_for_spec('Reachability')
          Pathname.new(group.path).should.be.absolute
        end

        it 'adds the Podfile to the Pods project' do
          config.stubs(:podfile_path).returns(Pathname.new('/Podfile'))
          @installer.send(:prepare_pods_project)
          @installer.pods_project['Podfile'].should.be.not.nil
        end

        it 'sets the deployment target for the whole project' do
          target_definition_osx = fixture_target_definition('OSX Target', Platform.new(:osx, '10.8'))
          target_definition_ios = fixture_target_definition('iOS Target', Platform.new(:ios, '6.0'))
          aggregate_target_osx = AggregateTarget.new(target_definition_osx, config.sandbox)
          aggregate_target_ios = AggregateTarget.new(target_definition_ios, config.sandbox)
          @installer.stubs(:aggregate_targets).returns([aggregate_target_osx, aggregate_target_ios])
          @installer.stubs(:pod_targets).returns([])
          @installer.send(:prepare_pods_project)
          build_settings = @installer.pods_project.build_configurations.map(&:build_settings)
          build_settings.each do |build_setting|
            build_setting['MACOSX_DEPLOYMENT_TARGET'].should == '10.8'
            build_setting['IPHONEOS_DEPLOYMENT_TARGET'].should == '6.0'
          end
        end
      end

      #--------------------------------------#

      describe '#install_file_references' do
        it 'installs the file references' do
          @installer.stubs(:pod_targets).returns([])
          Installer::FileReferencesInstaller.any_instance.expects(:install!)
          @installer.send(:install_file_references)
        end
      end

      #--------------------------------------#

      describe '#install_libraries' do
        it 'install the targets of the Pod project' do
          spec = fixture_spec('banana-lib/BananaLib.podspec')
          target_definition = Podfile::TargetDefinition.new(:default, nil)
          target_definition.store_pod('BananaLib')
          pod_target = PodTarget.new([spec], [target_definition], config.sandbox)
          @installer.stubs(:aggregate_targets).returns([])
          @installer.stubs(:pod_targets).returns([pod_target])
          Installer::PodTargetInstaller.any_instance.expects(:install!)
          @installer.send(:install_libraries)
        end

        it 'does not skip empty pod targets' do
          spec = fixture_spec('banana-lib/BananaLib.podspec')
          target_definition = Podfile::TargetDefinition.new(:default, nil)
          pod_target = PodTarget.new([spec], [target_definition], config.sandbox)
          @installer.stubs(:aggregate_targets).returns([])
          @installer.stubs(:pod_targets).returns([pod_target])
          Installer::PodTargetInstaller.any_instance.expects(:install!).once
          @installer.send(:install_libraries)
        end

        xit 'adds the frameworks required by to the pod to the project for informative purposes' do
          Specification::Consumer.any_instance.stubs(:frameworks).returns(['QuartzCore'])
          @installer.install!
          names = @installer.sandbox.project['Frameworks'].children.map(&:name)
          names.sort.should == ['Foundation.framework', 'QuartzCore.framework']
        end
      end

      #--------------------------------------#

      describe '#set_target_dependencies' do
        def test_extension_target(symbol_type)
          mock_user_target = mock('UserTarget', :symbol_type => symbol_type)
          @target.stubs(:user_targets).returns([mock_user_target])

          build_settings = {}
          mock_configuration = mock('BuildConfiguration', :build_settings => build_settings)
          @mock_target.stubs(:build_configurations).returns([mock_configuration])

          @installer.send(:set_target_dependencies)

          build_settings.should == { 'APPLICATION_EXTENSION_API_ONLY' => 'YES' }
        end

        before do
          spec = fixture_spec('banana-lib/BananaLib.podspec')

          target_definition = Podfile::TargetDefinition.new(:default, @installer.podfile)
          @pod_target = PodTarget.new([spec], [target_definition], config.sandbox)
          @target = AggregateTarget.new(target_definition, config.sandbox)

          @mock_target = mock('PodNativeTarget')

          mock_project = mock('PodsProject', :frameworks_group => mock('FrameworksGroup'))
          @installer.stubs(:pods_project).returns(mock_project)

          @target.stubs(:native_target).returns(@mock_target)
          @target.stubs(:pod_targets).returns([@pod_target])
          @installer.stubs(:aggregate_targets).returns([@target])
        end

        it 'sets resource bundles for not build pods as target dependencies of the user target' do
          @pod_target.stubs(:resource_bundle_targets).returns(['dummy'])
          @pod_target.stubs(:should_build? => false)
          @mock_target.expects(:add_dependency).with('dummy')

          @installer.send(:set_target_dependencies)
        end

        it 'configures APPLICATION_EXTENSION_API_ONLY for app extension targets' do
          test_extension_target(:app_extension)
        end

        it 'configures APPLICATION_EXTENSION_API_ONLY for watch extension targets' do
          test_extension_target(:watch_extension)
        end

        it 'configures APPLICATION_EXTENSION_API_ONLY for watchOS 2 extension targets' do
          test_extension_target(:watch2_extension)
        end

        it 'configures APPLICATION_EXTENSION_API_ONLY for tvOS extension targets' do
          test_extension_target(:tv_extension)
        end

        it 'configures APPLICATION_EXTENSION_API_ONLY for targets where the user target has it set' do
          mock_user_target = mock('UserTarget', :symbol_type => :application)
          mock_user_target.expects(:common_resolved_build_setting).with('APPLICATION_EXTENSION_API_ONLY').returns('YES')
          @target.stubs(:user_targets).returns([mock_user_target])

          build_settings = {}
          mock_configuration = mock('BuildConfiguration', :build_settings => build_settings)
          @mock_target.stubs(:build_configurations).returns([mock_configuration])

          @installer.send(:set_target_dependencies)

          build_settings.should == { 'APPLICATION_EXTENSION_API_ONLY' => 'YES' }
        end

        it 'does not try to set APPLICATION_EXTENSION_API_ONLY if there are no pod targets' do
          lambda do
            mock_user_target = mock('UserTarget', :symbol_type => :app_extension)
            @target.stubs(:user_targets).returns([mock_user_target])

            @target.stubs(:native_target).returns(nil)
            @target.stubs(:pod_targets).returns([])

            @installer.send(:set_target_dependencies)
          end.should.not.raise NoMethodError
        end
      end

      #--------------------------------------#

      describe '#write_pod_project' do
        before do
          @installer.stubs(:aggregate_targets).returns([])
          @installer.stubs(:analysis_result).returns(stub(:all_user_build_configurations => {}, :target_inspections => nil))
          @installer.send(:prepare_pods_project)
        end

        it 'recursively sorts the project' do
          Xcodeproj::Project.any_instance.stubs(:recreate_user_schemes)
          @installer.pods_project.main_group.expects(:sort)
          @installer.send(:write_pod_project)
        end

        it 'saves the project to the given path' do
          Xcodeproj::Project.any_instance.stubs(:recreate_user_schemes)
          temporary_directory + 'Pods/Pods.xcodeproj'
          @installer.pods_project.expects(:save)
          @installer.send(:write_pod_project)
        end

        it 'shares schemes of development pods' do
          spec = fixture_spec('banana-lib/BananaLib.podspec')
          pod_target = fixture_pod_target(spec)

          @installer.stubs(:pod_targets).returns([pod_target])
          @installer.sandbox.stubs(:development_pods).returns('BananaLib' => nil)

          Xcodeproj::XCScheme.expects(:share_scheme).with(
            @installer.pods_project.path,
            'BananaLib')

          @installer.send(:share_development_pod_schemes)
        end

        it "uses the user project's object version for the pods project" do
          tmp_directory = Pathname(Dir.tmpdir) + 'CocoaPods'
          FileUtils.mkdir_p(tmp_directory)
          proj = Xcodeproj::Project.new(tmp_directory + 'Yolo.xcodeproj', false, 1)
          proj.save

          aggregate_target = AggregateTarget.new(fixture_target_definition, config.sandbox)
          aggregate_target.user_project = proj
          @installer.stubs(:aggregate_targets).returns([aggregate_target])

          @installer.send(:prepare_pods_project)
          @installer.pods_project.object_version.should == '1'

          FileUtils.rm_rf(tmp_directory)
        end
      end

      #--------------------------------------#

      describe '#write_lockfiles' do
        before do
          @analysis_result = Installer::Analyzer::AnalysisResult.new
          @analysis_result.specifications = [fixture_spec('banana-lib/BananaLib.podspec')]
          @installer.stubs(:analysis_result).returns(@analysis_result)
        end

        it 'generates the lockfile' do
          @installer.send(:write_lockfiles)
          @installer.lockfile.pod_names.should == ['BananaLib']
        end

        it 'writes the lockfile' do
          @installer.send(:write_lockfiles)
          lockfile = Lockfile.from_file(temporary_directory + 'Podfile.lock')
          lockfile.pod_names.should == ['BananaLib']
        end

        it 'writes the sandbox manifest' do
          @installer.send(:write_lockfiles)
          lockfile = Lockfile.from_file(temporary_directory + 'Pods/Manifest.lock')
          lockfile.pod_names.should == ['BananaLib']
        end
      end
    end

    #-------------------------------------------------------------------------#

    describe 'Integrating client projects' do
      it 'integrates the client projects' do
        @installer.stubs(:aggregate_targets).returns([AggregateTarget.new(fixture_target_definition, config.sandbox)])
        Installer::UserProjectIntegrator.any_instance.expects(:integrate!)
        @installer.send(:integrate_user_project)
      end
    end

    describe 'Plugins Hooks' do
      before do
        @installer.send(:analyze)
        @specs = @installer.pod_targets.map(&:specs).flatten
        @spec = @specs.find { |spec| spec && spec.name == 'JSONKit' }
        @installer.stubs(:installed_specs).returns(@specs)
      end

      describe 'DEFAULT_PLUGINS' do
        before do
          @default_plugins = @installer.send(:plugins)
        end

        it 'includes cocoapods-stats' do
          @default_plugins['cocoapods-stats'].should == {}
        end
      end

      it 'runs plugins pre install hook' do
        context = stub
        Installer::PreInstallHooksContext.expects(:generate).returns(context)
        HooksManager.expects(:run).with(:pre_install, context, Installer::DEFAULT_PLUGINS)
        @installer.send(:run_plugins_pre_install_hooks)
      end

      it 'runs plugins post install hook' do
        context = stub
        Installer::PostInstallHooksContext.expects(:generate).returns(context)
        HooksManager.expects(:run).with(:post_install, context, Installer::DEFAULT_PLUGINS)
        @installer.send(:run_plugins_post_install_hooks)
      end

      it 'runs plugins source provider hook' do
        context = stub
        context.stubs(:sources).returns([])
        Installer::SourceProviderHooksContext.expects(:generate).returns(context)
        HooksManager.expects(:run).with(:source_provider, context, Installer::DEFAULT_PLUGINS)
        @installer.send(:run_source_provider_hooks)
      end

      it 'only runs the podfile-specified hooks' do
        context = stub
        Installer::PostInstallHooksContext.expects(:generate).returns(context)
        plugins_hash = Installer::DEFAULT_PLUGINS.merge('cocoapods-keys' => { 'keyring' => 'Eidolon' })
        @installer.podfile.stubs(:plugins).returns(plugins_hash)
        HooksManager.expects(:run).with(:post_install, context, plugins_hash)
        @installer.send(:run_plugins_post_install_hooks)
      end

      it 'raises if a podfile-specified plugin is not loaded' do
        @installer.podfile.stubs(:plugins).returns('cocoapods-keys' => {})
        Command::PluginManager.stubs(:specifications).returns([])
        should.raise Informative do
          @installer.send(:ensure_plugins_are_installed!)
        end.message.should.match /require.*plugin.*`cocoapods-keys`/
      end

      it 'does not raise if all podfile-specified plugins are loaded' do
        @installer.podfile.stubs(:plugins).returns('cocoapods-keys' => {})
        spec = stub
        spec.stubs(:name).returns('cocoapods-keys')
        Command::PluginManager.stubs(:specifications).returns([spec])
        should.not.raise do
          @installer.send(:ensure_plugins_are_installed!)
        end
      end
    end

    #-------------------------------------------------------------------------#

    describe 'Podfile Hooks' do
      before do
        podfile = Pod::Podfile.new do
          platform :ios
        end
        @installer = Installer.new(config.sandbox, podfile)
        @installer.installation_options.integrate_targets = false
      end

      it 'runs the pre install hooks' do
        @installer.podfile.expects(:pre_install!).with(@installer)
        @installer.install!
      end

      it 'runs the post install hooks' do
        @installer.podfile.expects(:post_install!).with(@installer)
        @installer.install!
      end
    end
  end
end
