Commit c080f472 authored by Samuel E. Giddins's avatar Samuel E. Giddins

Make the unit tests pass

parent 5f9f96f9
......@@ -20,7 +20,7 @@ module Pod
:integrate_targets => true,
:new_version_message => true,
:cache_root => Pathname.new(Dir.home) + 'Library/Caches/CocoaPods',
:cache_root => Pathname(Dir.home) + 'Library/Caches/CocoaPods',
}
public
......
......@@ -3,9 +3,7 @@ require 'claide/informative_error'
module Pod
module Downloader
require 'cocoapods/downloader/analyzer'
require 'cocoapods/downloader/cache'
require 'cocoapods/downloader/cleaner'
class DownloaderError; include CLAide::InformativeError; end
......
module Pod
module Downloader
class Analyzer
attr_reader :root
def initialize(root)
@root = root
end
end
end
end
......@@ -4,35 +4,7 @@ require 'tmpdir'
module Pod
module Downloader
class Cache
class MockSandbox
attr_accessor :spec, :target, :checkout_options
def initialize(target)
@target = target
@specs = []
end
def pod_dir(_name)
target
end
def store_podspec(_name, podspec, _external_source = false, json = false)
if podspec.is_a? String
podspec = Specification.from_string(podspec, "pod.podspec#{'.json' if json}")
elsif podspec.is_a? Pathname
podspec = Specification.from_file(podspec)
end
spec = podspec
end
def store_pre_downloaded_pod(*_); end
def store_checkout_source(_name, source)
checkout_options = source
end
def local?(_); false end
end
Result = Struct.new(:location, :spec, :checkout_options)
attr_accessor :root
......@@ -43,12 +15,11 @@ module Pod
def cache_key(pod_name, version = nil, downloader_opts = nil)
raise ArgumentError unless pod_name || (!version && !downloader_opts)
pod_name =
if version
"Release/#{pod_name}/#{version}"
elsif downloader_opts
opts = downloader_opts.to_a.sort_by(&:first).map { |k, v| "#{k}=#{v}" }.join('-')
opts = downloader_opts.to_a.sort_by(&:first).map { |k, v| "#{k}=#{v}" }.join('-').gsub(/#{File::SEPARATOR}+/, '+')
"External/#{pod_name}/#{opts}"
end
end
......@@ -57,7 +28,8 @@ module Pod
root + cache_key(name, version, downloader_opts)
end
def download_pod(name_or_spec, downloader_opts = nil, head = false)
def download_pod(name_or_spec, released = false, downloader_opts = nil, head = false)
spec = nil
if name_or_spec.is_a? Pod::Specification
spec = name_or_spec.root
name, version, downloader_opts = spec.name, spec.version, spec.source.dup
......@@ -65,38 +37,75 @@ module Pod
name = Specification.root_name(name_or_spec.to_s)
end
destination = path_for_pod(name, version, downloader_opts)
return destination if destination.directory? && !head
raise ArgumentError, 'Must give spec for a released download.' if released && !spec
source = ExternalSources::DownloaderSource.new(name, downloader_opts, nil)
result = Result.new
Dir.mktmpdir do |tmpdir|
tmpdir = Pathname(tmpdir)
mock_sandbox = MockSandbox.new(tmpdir)
source.fetch(mock_sandbox)
if spec
destination = path_for_pod(name, version)
result.checkout_options = downloader_opts
result.location = path_for_pod(name, version, downloader_opts) unless head
return result if !head && result.location.directory?
in_tmpdir do |target|
result.checkout_options = download(name, target, downloader_opts, head)
if released
result.location = destination = path_for_pod(name, version)
copy_and_clean(target, destination, spec)
else
spec = mock_sandbox.spec
checkout_options = mock_sandbox.checkout_options
destination = path_for_pod(found_spec.name, nil, mock_sandbox.checkout_options)
podspecs = Sandbox::PodspecFinder.new(target).podspecs
podspecs[name] = spec if spec
podspecs.each do |_, spec|
destination = path_for_pod(spec.name, nil, result.checkout_options)
copy_and_clean(target, destination, spec)
if name == spec.name
result.location = destination
result.spec = spec
end
end
end
end
copy_and_clean(tmpdir, destination, spec)
FileUtils.touch(tmpdir)
result
rescue Object
UI.notice("Error installing #{name}")
raise
end
[destination, checkout_options]
def download(name, target, params, head)
downloader = Downloader.for_target(target, params)
if head
unless downloader.head_supported?
raise Informative, "The pod '" + name + "' does not " \
'support the :head option, as it uses a ' + downloader.name +
' source. Remove that option to use this pod.'
end
downloader.download_head
else
downloader.download
end
if downloader.options_specific? && !head
params
else
downloader.checkout_options
end
end
def in_tmpdir(&blk)
tmpdir = Pathname(Dir.mktmpdir)
blk.call(tmpdir)
ensure
FileUtils.remove_entry(tmpdir) if tmpdir.exist?
end
def copy_and_clean(source, destination, spec)
specs_by_platform = {}
spec.available_platforms.each do |platform|
specs_by_platform[platform] = spec.recursive_subspecs.select { |ss| ss.supported_on_platform?(platform) }
specs_by_platform[platform] = [spec, *spec.recursive_subspecs].select { |ss| ss.supported_on_platform?(platform) }
end
Cleaner.new(source, specs_by_platform).clean!
destination.parent.mkpath unless destination.parent.exist?
FileUtils.move(source, destination)
FileUtils.cp_r(source, destination)
Sandbox::PodDirCleaner.new(destination, specs_by_platform).clean!
end
end
......
......@@ -102,14 +102,12 @@ module Pod
downloader = Downloader.for_target(target, params)
downloader.download
podspec_path = target + "#{name}.podspec"
json = false
unless Pathname(podspec_path).exist?
podspec_path = target + "#{name}.podspec.json"
json = true
end
podspec_finder = Sandbox::PodspecFinder.new(target)
spec = podspec_finder.podspecs[name]
raise Informative, "Unable to find a specification for '#{name}'." unless spec
store_podspec(sandbox, target + podspec_path, json)
store_podspec(sandbox, spec)
sandbox.store_pre_downloaded_pod(name)
if downloader.options_specific?
source = params
......@@ -126,7 +124,7 @@ module Pod
# @param [Sandbox] sandbox
# The sandbox where the specification should be stored.
#
# @param [Pathname, String] spec
# @param [Pathname, String, Specification] spec
# The path of the specification or its contents.
#
# @note All the concrete implementations of #{fetch} should invoke this
......@@ -144,6 +142,9 @@ module Pod
elsif spec.is_a?(String) && !json
spec = Specification.from_string(spec, 'spec.podspec').to_pretty_json
json = true
elsif spec.is_a?(Specification)
spec = spec.to_pretty_json
json = true
end
sandbox.store_podspec(name, spec, true, json)
end
......
......@@ -3,8 +3,6 @@ module Pod
# Provides support for fetching a specification file from a path local to
# the machine running the installation.
#
# Works with the {LocalPod::LocalSourcedPod} class.
#
class PathSource < AbstractExternalSource
# @see AbstractExternalSource#fetch
#
......
......@@ -298,7 +298,6 @@ module Pod
end
end
config.download_cache.download_pod(specs_by_platform.values.flatten.first.root)
@pod_installers ||= []
pod_installer = PodSourceInstaller.new(sandbox, specs_by_platform)
......
......@@ -8,6 +8,8 @@ module Pod
# @note This class needs to consider all the activated specs of a Pod.
#
class PodSourceInstaller
include Config::Mixin
# @return [Sandbox]
#
attr_reader :sandbox
......@@ -44,12 +46,6 @@ module Pod
def install!
download_source unless predownloaded? || local?
run_prepare_command
rescue Informative
raise
rescue Object
UI.notice("Error installing #{root_spec.name}")
clean!
raise
end
# Cleans the installations if appropriate.
......@@ -80,28 +76,12 @@ module Pod
# @return [void]
#
def download_source
released = !local? && !head_pod? &&!predownloaded?
download_result = config.download_cache.download_pod(root_spec, released, nil, head_pod?)
root.rmtree if root.exist?
if head_pod?
begin
downloader.download_head
@specific_source = downloader.checkout_options
rescue RuntimeError => e
if e.message == 'Abstract method'
raise Informative, "The pod '" + root_spec.name + "' does not " \
'support the :head option, as it uses a ' + downloader.name +
' source. Remove that option to use this pod.'
else
raise
end
end
else
downloader.download
unless downloader.options_specific?
@specific_source = downloader.checkout_options
end
end
FileUtils.cp_r(download_result.location, root)
if specific_source
if @specific_source = download_result.checkout_options
sandbox.store_checkout_source(root_spec.name, specific_source)
end
end
......@@ -135,25 +115,12 @@ module Pod
# @return [void]
#
def clean_installation
cleaner = Downloader::Cleaner.new(root, specs_by_platform)
cleaner = Sandbox::PodDirCleaner.new(root, specs_by_platform)
cleaner.clean!
end
#-----------------------------------------------------------------------#
public
# @!group Dependencies
# @return [Downloader] The downloader to use for the retrieving the
# source.
#
def downloader
@downloader ||= Downloader.for_target(root, root_spec.source.dup)
end
#-----------------------------------------------------------------------#
private
# @!group Convenience methods.
......
......@@ -36,9 +36,11 @@ module Pod
# +-- Pods.xcodeproj
#
class Sandbox
autoload :FileAccessor, 'cocoapods/sandbox/file_accessor'
autoload :HeadersStore, 'cocoapods/sandbox/headers_store'
autoload :PathList, 'cocoapods/sandbox/path_list'
autoload :FileAccessor, 'cocoapods/sandbox/file_accessor'
autoload :HeadersStore, 'cocoapods/sandbox/headers_store'
autoload :PathList, 'cocoapods/sandbox/path_list'
autoload :PodDirCleaner, 'cocoapods/sandbox/pod_dir_cleaner'
autoload :PodspecFinder, 'cocoapods/sandbox/podspec_finder'
# @return [Pathname] the root of the sandbox.
#
......
module Pod
module Downloader
class Cleaner
class Sandbox
class PodDirCleaner
attr_reader :root
attr_reader :specs_by_platform
......@@ -57,14 +57,13 @@ module Pod
glob_options = File::FNM_DOTMATCH | File::FNM_CASEFOLD
files = Pathname.glob(root + '**/*', glob_options).map(&:to_s)
files.reject! do |candidate|
files.reject do |candidate|
candidate = candidate.downcase
candidate.end_with?('.', '..') || cached_used.any? do |path|
path = path.downcase
path.include?(candidate) || candidate.include?(path)
end
end
files
end
# @return [Array<String>] The absolute path of all the files used by the
......
module Pod
class Sandbox
class PodspecFinder
attr_reader :root
def initialize(root)
@root = root
end
def podspecs
return @specs_by_name if @specs_by_name
@specs_by_name = {}
spec_files = Dir.glob(root + '{,*,*/*}.podspec{,.json}')
spec_files.sort_by { |p| -p.split(File::SEPARATOR).size }.each do |file|
begin
spec = Specification.from_file(file)
@specs_by_name[spec.name] = spec
rescue => e
UI.warn "Unable to load a podspec from #{file}, skipping:\n\n#{e}"
end
end
@specs_by_name
end
end
end
end
......@@ -12,6 +12,7 @@ module Bacon
c.repos_dir = fixture('spec-repos')
c.installation_root = SpecHelper.temporary_directory
c.skip_repo_update = true
c.cache_root = SpecHelper.temporary_directory + 'Cache'
end
::Pod::UI.output = ''
......
......@@ -48,10 +48,10 @@ module Pod
path = config.sandbox.pod_dir('Reachability')
podspec_path = path + 'Reachability.podspec.json'
Dir.mkdir(path)
File.open(podspec_path, 'w') { |f| f.write '{}' }
File.open(podspec_path, 'w') { |f| f.write '{"name":"Reachability"}' }
Pathname.any_instance.stubs(:rmtree)
Downloader::Git.any_instance.stubs(:download)
config.sandbox.expects(:store_podspec).with('Reachability', "{\n}\n", true, true)
config.sandbox.expects(:store_podspec).with('Reachability', %({\n "name": "Reachability"\n}\n), true, true)
@subject.send(:pre_download, config.sandbox)
end
end
......
......@@ -16,7 +16,7 @@ module Pod
it 'downloads the source' do
@spec.source = { :git => SpecHelper.fixture('banana-lib'), :tag => 'v1.0' }
@installer.install!
@installer.specific_source.should.be.nil
@installer.specific_source[:tag].should == 'v1.0'
pod_folder = config.sandbox.pod_dir('BananaLib')
pod_folder.should.exist
end
......@@ -49,27 +49,7 @@ module Pod
}
end
it 'cleans up directory when an error occurs during download' do
config.sandbox.store_head_pod('BananaLib')
pod_folder = config.sandbox.pod_dir('BananaLib')
partially_downloaded_file = pod_folder + 'partially_downloaded_file'
mock_downloader = Object.new
singleton_class = class << mock_downloader; self; end
singleton_class.send(:define_method, :download_head) do
FileUtils.mkdir_p(pod_folder)
FileUtils.touch(partially_downloaded_file)
raise('some network error')
end
@installer.stubs(:downloader).returns(mock_downloader)
lambda do
@installer.install!
end.should.raise(RuntimeError).message.should.equal('some network error')
partially_downloaded_file.should.not.exist
end
it 'fails when using :head for Http source' do
it 'fails when using :head for http source' do
config.sandbox.store_head_pod('BananaLib')
@spec.source = { :http => 'http://dl.google.com/googleadmobadssdk/googleadmobsearchadssdkios.zip' }
@spec.source_files = 'GoogleAdMobSearchAdsSDK/*.h'
......@@ -153,81 +133,5 @@ module Pod
end
#-------------------------------------------------------------------------#
describe 'Private Helpers' do
it 'returns the clean paths' do
@installer.send(:download_source)
paths = @installer.send(:clean_paths)
relative_paths = paths.map { |p| p.gsub("#{temporary_directory}/", '') }
# Because there are thousands of files inside .git/, we're excluding
# them from the comparison.
paths_without_git = relative_paths.reject { |p| p.include? 'Pods/BananaLib/.git/' }
paths_without_git.sort.should == [
'Pods/BananaLib/.git',
'Pods/BananaLib/.gitmodules',
'Pods/BananaLib/BananaLib.podspec',
'Pods/BananaLib/libPusher',
'Pods/BananaLib/sub-dir',
'Pods/BananaLib/sub-dir/sub-dir-2',
'Pods/BananaLib/sub-dir/sub-dir-2/somefile.txt',
]
end
it 'returns the used files' do
@installer.send(:download_source)
paths = @installer.send(:used_files)
relative_paths = paths.map { |p| p.gsub("#{temporary_directory}/", '') }
relative_paths.sort.should == [
'Pods/BananaLib/Classes/Banana.h',
'Pods/BananaLib/Classes/Banana.m',
'Pods/BananaLib/Classes/BananaLib.pch',
'Pods/BananaLib/Classes/BananaPrivate.h',
'Pods/BananaLib/LICENSE',
'Pods/BananaLib/README',
'Pods/BananaLib/Resources/logo-sidebar.png',
]
end
it 'handles Pods with multiple file accessors' do
spec = fixture_spec('banana-lib/BananaLib.podspec')
spec.source = { :git => SpecHelper.fixture('banana-lib') }
spec.source_files = []
spec.ios.source_files = 'Classes/*.h'
spec.osx.source_files = 'Classes/*.m'
ios_spec = spec.dup
osx_spec = spec.dup
specs_by_platform = { :ios => [ios_spec], :osx => [osx_spec] }
@installer = Installer::PodSourceInstaller.new(config.sandbox, specs_by_platform)
@installer.send(:download_source)
paths = @installer.send(:used_files)
relative_paths = paths.map { |p| p.gsub("#{temporary_directory}/", '') }
relative_paths.sort.should == [
'Pods/BananaLib/Classes/Banana.h',
'Pods/BananaLib/Classes/Banana.m',
'Pods/BananaLib/Classes/BananaLib.pch',
'Pods/BananaLib/Classes/BananaPrivate.h',
'Pods/BananaLib/LICENSE',
'Pods/BananaLib/README',
'Pods/BananaLib/Resources/logo-sidebar.png',
]
end
it 'compacts the used files as nil would be converted to the empty string' do
Sandbox::FileAccessor.any_instance.stubs(:source_files)
Sandbox::FileAccessor.any_instance.stubs(:vendored_libraries)
Sandbox::FileAccessor.any_instance.stubs(:resources).returns(nil)
Sandbox::FileAccessor.any_instance.stubs(:preserve_paths)
Sandbox::FileAccessor.any_instance.stubs(:prefix_header)
Sandbox::FileAccessor.any_instance.stubs(:readme)
Sandbox::FileAccessor.any_instance.stubs(:license)
Sandbox::FileAccessor.any_instance.stubs(:vendored_frameworks)
paths = @installer.send(:used_files)
paths.should == []
end
end
#-------------------------------------------------------------------------#
end
end
......@@ -312,7 +312,7 @@ module Pod
@installer.stubs(:podfile).returns(podfile)
@installer.stubs(:lockfile).returns(nil)
Downloader::Git.any_instance.expects(:download_head).once
Downloader::Git.any_instance.expects(:checkout_options).returns({})
Downloader::Git.any_instance.stubs(:checkout_options).returns({})
@installer.prepare
@installer.resolve_dependencies
@installer.send(:root_specs).sort_by(&:name).map(&:version).map(&:head?).should == [true, nil]
......
require File.expand_path('../../../spec_helper', __FILE__)
module Pod
describe Sandbox::PodDirCleaner do
before do
@spec = fixture_spec('banana-lib/BananaLib.podspec')
specs_by_platform = { :ios => [@spec] }
@root = temporary_directory + 'BananaLib'
Downloader.for_target(@root, :git => SpecHelper.fixture('banana-lib')).download
@cleaner = Sandbox::PodDirCleaner.new(@root, specs_by_platform)
end
it 'returns the clean paths' do
paths = @cleaner.send(:clean_paths)
relative_paths = paths.map { |p| p.gsub("#{@root}/", '') }
# Because there are thousands of files inside .git/, we're excluding
# them from the comparison.
paths_without_git = relative_paths.reject { |p| p.include? '.git/' }
paths_without_git.sort.should == [
'.git',
'.gitmodules',
'BananaLib.podspec',
'libPusher',
'sub-dir',
'sub-dir/sub-dir-2',
'sub-dir/sub-dir-2/somefile.txt',
]
end
it 'returns the used files' do
paths = @cleaner.send(:used_files)
relative_paths = paths.map { |p| p.gsub("#{@root}/", '') }
relative_paths.sort.should == [
'Classes/Banana.h',
'Classes/Banana.m',
'Classes/BananaLib.pch',
'Classes/BananaPrivate.h',
'LICENSE',
'README',
'Resources/logo-sidebar.png',
]
end
it 'handles Pods with multiple file accessors' do
spec = fixture_spec('banana-lib/BananaLib.podspec')
spec.source_files = []
spec.ios.source_files = 'Classes/*.h'
spec.osx.source_files = 'Classes/*.m'
ios_spec = spec.dup
osx_spec = spec.dup
specs_by_platform = { :ios => [ios_spec], :osx => [osx_spec] }
@cleaner = Sandbox::PodDirCleaner.new(@root, specs_by_platform)
paths = @cleaner.send(:used_files)
relative_paths = paths.map { |p| p.gsub("#{@root}/", '') }
relative_paths.sort.should == [
'Classes/Banana.h',
'Classes/Banana.m',
'Classes/BananaLib.pch',
'Classes/BananaPrivate.h',
'LICENSE',
'README',
'Resources/logo-sidebar.png',
]
end
it 'compacts the used files as nil would be converted to the empty string' do
Sandbox::FileAccessor.any_instance.stubs(:source_files)
Sandbox::FileAccessor.any_instance.stubs(:vendored_libraries)
Sandbox::FileAccessor.any_instance.stubs(:resources).returns(nil)
Sandbox::FileAccessor.any_instance.stubs(:preserve_paths)
Sandbox::FileAccessor.any_instance.stubs(:prefix_header)
Sandbox::FileAccessor.any_instance.stubs(:readme)
Sandbox::FileAccessor.any_instance.stubs(:license)
Sandbox::FileAccessor.any_instance.stubs(:vendored_frameworks)
paths = @cleaner.send(:used_files)
paths.should == []
end
end
end
......@@ -103,6 +103,7 @@ module Pod
it 'returns the path of the search index' do
SourcesManager.unstub(:search_index_path)
config.cache_root = Config::DEFAULTS[:cache_root]
path = SourcesManager.search_index_path.to_s
path.should.match %r{Library/Caches/CocoaPods/search_index.yaml}
end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment