├── .gitignore ├── .ruby-version ├── Gemfile ├── Guardfile ├── LICENSE ├── README.md ├── Rakefile ├── bin └── xcoder ├── examples ├── Cedar │ ├── Specs │ │ ├── Specs-Info.plist │ │ ├── Specs-Prefix.pch │ │ ├── en.lproj │ │ │ └── InfoPlist.strings │ │ └── main.m │ └── Vendor │ │ └── Frameworks │ │ └── Cedar-iPhone.framework │ │ ├── Cedar-iPhone │ │ ├── Headers │ │ ├── Resources │ │ └── Versions │ │ ├── A │ │ ├── Cedar-iPhone │ │ └── Headers │ │ │ ├── ActualValue.h │ │ │ ├── Base.h │ │ │ ├── BeCloseTo.h │ │ │ ├── BeEmpty.h │ │ │ ├── BeGTE.h │ │ │ ├── BeGreaterThan.h │ │ │ ├── BeInstanceOf.h │ │ │ ├── BeLTE.h │ │ │ ├── BeLessThan.h │ │ │ ├── BeNil.h │ │ │ ├── BeSameInstanceAs.h │ │ │ ├── BeTruthy.h │ │ │ ├── CDRColorizedReporter.h │ │ │ ├── CDRExampleBase.h │ │ │ ├── CDRExampleDetailsViewController.h │ │ │ ├── CDRExampleParent.h │ │ │ ├── CDRExampleReporter.h │ │ │ ├── CDRExampleReporterViewController.h │ │ │ ├── CDRFunctions.h │ │ │ ├── CDRSharedExampleGroupPool.h │ │ │ ├── CDRSpec.h │ │ │ ├── CDRSpecFailure.h │ │ │ ├── Cedar.h │ │ │ ├── CedarApplicationDelegate.h │ │ │ ├── CedarComparators.h │ │ │ ├── CedarMatchers.h │ │ │ ├── CedarStringifiers.h │ │ │ ├── ComparatorsBase.h │ │ │ ├── ComparatorsContainer.h │ │ │ ├── CompareEqual.h │ │ │ ├── CompareGreaterThan.h │ │ │ ├── Contain.h │ │ │ ├── Equal.h │ │ │ ├── RaiseException.h │ │ │ ├── ShouldSyntax.h │ │ │ ├── SpecHelper.h │ │ │ ├── StringifiersBase.h │ │ │ └── StringifiersContainer.h │ │ └── Current ├── EGORefreshTableHeaderView │ └── Vendor │ │ └── EGORefreshTableHeaderView │ │ ├── EGORefreshTableHeaderView.h │ │ └── EGORefreshTableHeaderView.m └── Reachability │ └── Vendor │ └── Reachability │ ├── Reachability.h │ └── Reachability.m ├── lib ├── xcode │ ├── build_file.rb │ ├── build_phase.rb │ ├── builder.rb │ ├── builder │ │ ├── base_builder.rb │ │ ├── build_parser.rb │ │ ├── project_target_config_builder.rb │ │ └── scheme_builder.rb │ ├── buildspec.rb │ ├── configuration.rb │ ├── configuration_list.rb │ ├── configuration_owner.rb │ ├── configurations │ │ ├── array_property.rb │ │ ├── boolean_property.rb │ │ ├── enumeration_property.rb │ │ ├── key_value_array_property.rb │ │ ├── space_delimited_string_property.rb │ │ ├── string_property.rb │ │ └── targeted_device_family_property.rb │ ├── container_item_proxy.rb │ ├── core_ext │ │ ├── array.rb │ │ ├── boolean.rb │ │ ├── fixnum.rb │ │ ├── hash.rb │ │ └── string.rb │ ├── deploy │ │ ├── ftp.rb │ │ ├── kickfolio.rb │ │ ├── s3.rb │ │ ├── ssh.rb │ │ ├── templates │ │ │ ├── index.rhtml │ │ │ └── manifest.rhtml │ │ ├── testflight.rb │ │ └── web_assets.rb │ ├── file_reference.rb │ ├── group.rb │ ├── info_plist.rb │ ├── keychain.rb │ ├── parsers │ │ └── plutil_project_parser.rb │ ├── platform.rb │ ├── project.rb │ ├── project_reference.rb │ ├── provisioning_profile.rb │ ├── registry.rb │ ├── resource.rb │ ├── scheme.rb │ ├── shell │ │ └── command.rb │ ├── simple_identifier_generator.rb │ ├── target.rb │ ├── target_dependency.rb │ ├── terminal_output.rb │ ├── test │ │ ├── formatters │ │ │ ├── junit_formatter.rb │ │ │ └── stdout_formatter.rb │ │ ├── parsers │ │ │ ├── kif_parser.rb │ │ │ └── ocunit_parser.rb │ │ ├── report.rb │ │ └── report │ │ │ ├── suite_result.rb │ │ │ └── test_result.rb │ ├── variant_group.rb │ ├── version.rb │ └── workspace.rb ├── xcoder.rb └── xcoder │ └── rake_task.rb ├── spec ├── Provisioning │ ├── AdHoc.mobileprovision │ ├── AppStore.mobileprovision │ ├── Test.keychain │ └── TestUser.p12 ├── TestProject │ ├── ApplicationTests │ │ ├── ApplicationTests-Info.plist │ │ ├── ApplicationTests-Prefix.pch │ │ ├── ApplicationTests.h │ │ ├── ApplicationTests.m │ │ └── en.lproj │ │ │ └── InfoPlist.strings │ ├── Buildfile │ ├── Default-568h@2x.png │ ├── LogicTests │ │ ├── AnotherTest.h │ │ ├── AnotherTest.m │ │ ├── TestProjectTests-Info.plist │ │ ├── TestProjectTests.h │ │ ├── TestProjectTests.m │ │ └── en.lproj │ │ │ └── InfoPlist.strings │ ├── TestProject.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── TestProject.xcscheme │ └── TestProject │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── TestProject-Info.plist │ │ ├── TestProject-Prefix.pch │ │ ├── en.lproj │ │ └── InfoPlist.strings │ │ └── main.m ├── TestWorkspace.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── WorkspaceScheme.xcscheme ├── TestWorkspace2.xcworkspace │ └── contents.xcworkspacedata ├── build_phase_spec.rb ├── builder_spec.rb ├── configuration_list_spec.rb ├── configuration_spec.rb ├── deploy_spec.rb ├── group_spec.rb ├── integration │ ├── builder_spec.rb │ ├── cedar_install_spec.rb │ ├── pull_to_refresh_install_spec.rb │ ├── reachability_install_spec.rb │ └── universal_framework_spec.rb ├── keychain_spec.rb ├── ocunit_parser_spec.rb ├── parsers │ ├── plutil_project_parser.rb │ └── spec_helper.rb ├── project_spec.rb ├── provisioning_profile_spec.rb ├── registry_spec.rb ├── scheme_spec.rb ├── spec_helper.rb ├── target_spec.rb ├── workspace_spec.rb └── xcode_spec.rb └── xcoder.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac 2 | .DS_Store 3 | 4 | # Dependency management 5 | 6 | .bundle 7 | Gemfile.lock 8 | 9 | # Documentation 10 | 11 | .yardoc 12 | doc/ 13 | 14 | # Gem creation and packaging 15 | 16 | pkg/* 17 | *.gem 18 | 19 | # Tests and Test Related Files 20 | 21 | spec/TestProject/TestProject.xcodeproj/xcuserdata 22 | spec/TestProject/TestProject.xcodeproj/project.xcworkspace/ 23 | spec/TestProject/build 24 | spec/test-reports 25 | 26 | # local vendor/bundle 27 | vendor/bundle/* 28 | 29 | # RubyMine project 30 | .idea/* 31 | 32 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.0.0 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | gem 'rake' 5 | 6 | gem 'builder' 7 | gem 'multi_json' 8 | gem 'plist' 9 | gem 'rest-client' 10 | 11 | # Documentation 12 | gem 'yard' 13 | gem 'redcarpet' 14 | 15 | # Deployment 16 | gem 'net-ssh' 17 | gem 'net-scp' 18 | gem 'aws-sdk' 19 | 20 | group :test do 21 | gem 'rspec' 22 | gem 'guard' 23 | gem 'guard-rspec' 24 | end 25 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # A sample Guardfile 2 | # More info at https://github.com/guard/guard#readme 3 | 4 | guard 'rspec', :version => 2, :cli => "--color --format d --tag ~integration" do 5 | 6 | watch(%r{^spec/.+_spec\.rb$}) 7 | # As the registry and resource file affect most every file, the entire 8 | # suite should be run when they are changed 9 | watch(%r{^lib/xcode/(?:registry|resource)\.rb$}) { "spec" } 10 | watch(%r{^lib/xcode/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } 11 | watch('spec/spec_helper.rb') { "spec" } 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 cisimple 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "yard" 3 | require "yard/rake/yardoc_task" 4 | 5 | task :default => [:specs, :build] 6 | 7 | desc "Run specs" 8 | task :specs do 9 | system "rspec --color --format d --tag ~integration" 10 | end 11 | 12 | desc "Run integration tests" 13 | task :integration => :reset do 14 | system "rspec --color --format d --tag integration" 15 | end 16 | 17 | namespace :doc do 18 | desc "Generate YARD docs" 19 | YARD::Rake::YardocTask.new(:generate) do |t| 20 | t.files = ['lib/**/*.rb', '-', 'README.md'] # optional 21 | # t.options = ["-o ../xcoder-doc"] 22 | end 23 | end 24 | 25 | task :reset => ['test_project:reset'] 26 | 27 | namespace :test_project do 28 | 29 | task :reset do 30 | puts "Reseting the TestProject Project File" 31 | system "git checkout -- spec/TestProject" 32 | puts "Removing any User schemes generated in the project" 33 | system "rm -rf spec/TestProject/TestProject.xcodeproj/xcuserdata" 34 | puts "Removing any installed files" 35 | system "git clean -df spec/TestProject" 36 | end 37 | 38 | end 39 | 40 | 41 | require './lib/xcoder/rake_task' 42 | 43 | Xcode::RakeTask.new :xcode do |xcoder| 44 | xcoder.directory = 'spec' 45 | end 46 | -------------------------------------------------------------------------------- /bin/xcoder: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'xcoder' 3 | require 'optparse' 4 | 5 | require 'xcode/buildspec' 6 | 7 | options = {} 8 | OptionParser.new do |opts| 9 | opts.banner = "Usage: xcoder [options]" 10 | 11 | 12 | opts.separator "" 13 | opts.separator "Specific options:" 14 | 15 | opts.on("-d", "--describe", "Dump the structure of the projects/workspaces in the working directory") do |v| 16 | Xcode.workspaces.each do |w| 17 | puts w.describe 18 | end 19 | 20 | Xcode.projects.each do |p| 21 | puts p.describe 22 | end 23 | # options[:verbose] = v 24 | end 25 | 26 | opts.on("--install-profile [PROFILE]", "Install the given profile into ~/Library") do |profile| 27 | Xcode::ProvisioningProfile.new(profile).install 28 | end 29 | 30 | opts.on("--show-sdks", "Show the available SDKs") do 31 | Xcode::Platforms.supported.each do |p| 32 | puts "#{p.name}: #{p.platform}, #{p.version}" 33 | end 34 | end 35 | 36 | opts.separator "" 37 | opts.separator "Buildspec options:" 38 | 39 | opts.on("-r", "--run [task]", "Run the Buildspec with the given task, defaults to 'deploy'") do |task| 40 | Xcode::Buildspec.parse 41 | task = 'deploy' if task.nil? 42 | Rake::Task[task].invoke 43 | end 44 | 45 | opts.on("-T", "--tasks", "List the available Buildspec tasks") do 46 | Xcode::Buildspec.parse 47 | puts Rake.application.tasks 48 | end 49 | 50 | opts.separator "" 51 | opts.separator "Common options:" 52 | 53 | opts.on_tail("-h", "--help", "Show this message") do 54 | puts opts 55 | exit 56 | end 57 | 58 | opts.on_tail("-l", "--loglevel LEVEL", "Show only output at LEVEL or below " + Xcode::TerminalOutput::LEVELS.join(", ")) do |level| 59 | Xcode::TerminalOutput.log_level = level.to_sym 60 | end 61 | 62 | # opts.on_tail("-vv", "Show VERY verbose (debug level) output") do 63 | # Xcode::TerminalOutput.log_level = :debug 64 | # end 65 | 66 | opts.on_tail("-q", "Hide all output except errors (equivilent to -l :error") do 67 | Xcode::TerminalOutput.log_level = :error 68 | end 69 | 70 | opts.on_tail("--version", "Show version") do 71 | puts "Xcoder #{Xcode::VERSION}" 72 | exit 73 | end 74 | end.parse! -------------------------------------------------------------------------------- /examples/Cedar/Specs/Specs-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFiles 12 | 13 | CFBundleIdentifier 14 | company.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | 1.0 27 | LSRequiresIPhoneOS 28 | 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /examples/Cedar/Specs/Specs-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'Specs' target in the 'Specs' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_3_0 8 | #warning "This project uses features only available in iOS SDK 3.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /examples/Cedar/Specs/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /examples/Cedar/Specs/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Specs 4 | // 5 | // Created by Franklin Webber on 2/8/12. 6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | int main(int argc, char *argv[]) { 13 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 14 | 15 | int retVal = UIApplicationMain(argc, argv, nil, @"CedarApplicationDelegate"); 16 | [pool release]; 17 | return retVal; 18 | } -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Cedar-iPhone: -------------------------------------------------------------------------------- 1 | Versions/Current/Cedar-iPhone -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Headers: -------------------------------------------------------------------------------- 1 | Versions/Current/Headers -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Cedar-iPhone: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayh/xcoder/0affa3e8f0a5c138ea25c004341d62b23c6b6711/examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Cedar-iPhone -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/ActualValue.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "StringifiersBase.h" 5 | #import "CDRSpecFailure.h" 6 | 7 | namespace Cedar { namespace Matchers { 8 | 9 | void CDR_fail(const char *fileName, int lineNumber, NSString * reason); 10 | 11 | template class ActualValue; 12 | 13 | #pragma mark class ActualValueMatchProxy 14 | template 15 | class ActualValueMatchProxy { 16 | private: 17 | template 18 | ActualValueMatchProxy(const ActualValueMatchProxy &); 19 | template 20 | ActualValueMatchProxy & operator=(const ActualValueMatchProxy &); 21 | 22 | public: 23 | explicit ActualValueMatchProxy(const ActualValue &, bool negate = false); 24 | ActualValueMatchProxy(); 25 | 26 | template void operator()(const MatcherType &) const; 27 | ActualValueMatchProxy negate() const; 28 | 29 | private: 30 | const ActualValue & actualValue_; 31 | bool negate_; 32 | }; 33 | 34 | template 35 | ActualValueMatchProxy::ActualValueMatchProxy(const ActualValue & actualValue, bool negate /*= false */) 36 | : actualValue_(actualValue), negate_(negate) {} 37 | 38 | template template 39 | void ActualValueMatchProxy::operator()(const MatcherType & matcher) const { 40 | if (negate_) { 41 | actualValue_.execute_negative_match(matcher); 42 | } else { 43 | actualValue_.execute_positive_match(matcher); 44 | } 45 | } 46 | 47 | template 48 | ActualValueMatchProxy ActualValueMatchProxy::negate() const { 49 | return ActualValueMatchProxy(actualValue_, !negate_); 50 | } 51 | 52 | #pragma mark class ActualValue 53 | template 54 | class ActualValue { 55 | private: 56 | template 57 | ActualValue(const ActualValue &); 58 | template 59 | ActualValue & operator=(const ActualValue &); 60 | 61 | public: 62 | explicit ActualValue(const char *, int, const T &); 63 | ~ActualValue(); 64 | 65 | ActualValueMatchProxy to; 66 | ActualValueMatchProxy to_not; 67 | 68 | private: 69 | template void execute_positive_match(const MatcherType &) const; 70 | template void execute_negative_match(const MatcherType &) const; 71 | friend class ActualValueMatchProxy; 72 | 73 | private: 74 | const T & value_; 75 | std::string fileName_; 76 | int lineNumber_; 77 | }; 78 | 79 | template 80 | ActualValue::ActualValue(const char *fileName, int lineNumber, const T & value) : fileName_(fileName), lineNumber_(lineNumber), value_(value), to(*this), to_not(*this, true) { 81 | } 82 | 83 | template 84 | ActualValue::~ActualValue() { 85 | } 86 | 87 | template template 88 | void ActualValue::execute_positive_match(const MatcherType & matcher) const { 89 | if (!matcher.matches(value_)) { 90 | CDR_fail(fileName_.c_str(), lineNumber_, matcher.failure_message_for(value_)); 91 | } 92 | } 93 | 94 | template template 95 | void ActualValue::execute_negative_match(const MatcherType & matcher) const { 96 | if (matcher.matches(value_)) { 97 | CDR_fail(fileName_.c_str(), lineNumber_, matcher.negative_failure_message_for(value_)); 98 | } 99 | } 100 | 101 | template 102 | const ActualValue CDR_expect(const char *fileName, int lineNumber, const T & actualValue) { 103 | return ActualValue(fileName, lineNumber, actualValue); 104 | } 105 | 106 | inline void CDR_fail(const char *fileName, int lineNumber, NSString * reason) { 107 | [[CDRSpecFailure specFailureWithReason:reason 108 | fileName:[NSString stringWithUTF8String:fileName] 109 | lineNumber:lineNumber] raise]; 110 | } 111 | 112 | }} 113 | 114 | #ifndef CEDAR_MATCHERS_COMPATIBILITY_MODE 115 | #define expect(x) CDR_expect(__FILE__, __LINE__, (x)) 116 | #define fail(x) CDR_fail(__FILE__, __LINE__, (x)) 117 | #endif 118 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/Base.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "CedarStringifiers.h" 5 | 6 | namespace Cedar { namespace Matchers { 7 | struct BaseMessageBuilder { 8 | template 9 | static NSString * string_for_actual_value(const U & value) { 10 | return Stringifiers::string_for(value); 11 | } 12 | }; 13 | 14 | /** 15 | * Basic functionality for all matchers. Meant to be used as a convenience base class for 16 | * matcher classes. 17 | */ 18 | template 19 | class Base { 20 | private: 21 | Base & operator=(const Base &); 22 | 23 | public: 24 | Base(); 25 | virtual ~Base() = 0; 26 | // Allow default copy ctor. 27 | 28 | template 29 | NSString * failure_message_for(const U &) const; 30 | template 31 | NSString * negative_failure_message_for(const U &) const; 32 | 33 | protected: 34 | virtual NSString * failure_message_end() const = 0; 35 | }; 36 | 37 | template 38 | Base::Base() {} 39 | template 40 | Base::~Base() {} 41 | 42 | template template 43 | NSString * Base::failure_message_for(const U & value) const { 44 | return [NSString stringWithFormat:@"Expected <%@> to %@", MessageBuilder_::string_for_actual_value(value), this->failure_message_end()]; 45 | } 46 | 47 | template template 48 | NSString * Base::negative_failure_message_for(const U & value) const { 49 | return [NSString stringWithFormat:@"Expected <%@> to not %@", MessageBuilder_::string_for_actual_value(value), this->failure_message_end()]; 50 | } 51 | }} 52 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/BeCloseTo.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Base.h" 3 | 4 | namespace Cedar { namespace Matchers { 5 | template 6 | class BeCloseTo : public Base<> { 7 | private: 8 | BeCloseTo & operator=(const BeCloseTo &); 9 | 10 | public: 11 | explicit BeCloseTo(const T & expectedValue); 12 | ~BeCloseTo(); 13 | // Allow default copy ctor. 14 | 15 | BeCloseTo & within(float threshold); 16 | 17 | template 18 | bool matches(const U &) const; 19 | bool matches(NSNumber * const &) const; 20 | 21 | protected: 22 | virtual NSString * failure_message_end() const; 23 | 24 | private: 25 | template 26 | bool subtractable_types_match(const U &, const V &) const; 27 | 28 | private: 29 | const T & expectedValue_; 30 | float threshold_; 31 | }; 32 | 33 | template 34 | BeCloseTo be_close_to(const T & expectedValue) { 35 | return BeCloseTo(expectedValue); 36 | } 37 | 38 | template 39 | BeCloseTo::BeCloseTo(const T & expectedValue) 40 | : Base<>(), expectedValue_(expectedValue), threshold_(0.01) { 41 | } 42 | 43 | template 44 | BeCloseTo::~BeCloseTo() { 45 | } 46 | 47 | template 48 | BeCloseTo & BeCloseTo::within(float threshold) { 49 | threshold_ = threshold; 50 | return *this; 51 | } 52 | 53 | template 54 | /*virtual*/ NSString * BeCloseTo::failure_message_end() const { 55 | return [NSString stringWithFormat:@"be close to <%@> (within %@)", Stringifiers::string_for(expectedValue_), Stringifiers::string_for(threshold_)]; 56 | } 57 | 58 | template template 59 | bool BeCloseTo::subtractable_types_match(const U & actualValue, const V & expectedValue) const { 60 | return actualValue > expectedValue - threshold_ && actualValue < expectedValue + threshold_; 61 | } 62 | 63 | #pragma mark Generic 64 | template template 65 | bool BeCloseTo::matches(const U & actualValue) const { 66 | return this->subtractable_types_match(actualValue, expectedValue_); 67 | } 68 | 69 | #pragma mark NSNumber 70 | template 71 | bool BeCloseTo::matches(NSNumber * const & actualValue) const { 72 | return this->matches([actualValue floatValue]); 73 | } 74 | 75 | template<> template 76 | bool BeCloseTo::matches(const U & actualValue) const { 77 | return this->subtractable_types_match(actualValue, [expectedValue_ floatValue]); 78 | } 79 | }} 80 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/BeEmpty.h: -------------------------------------------------------------------------------- 1 | #import "Base.h" 2 | 3 | namespace Cedar { namespace Matchers { 4 | class BeEmpty : public Base<> { 5 | private: 6 | BeEmpty & operator=(const BeEmpty &); 7 | 8 | public: 9 | inline BeEmpty() : Base<>() {} 10 | inline ~BeEmpty() {} 11 | // Allow default copy ctor. 12 | 13 | inline const BeEmpty & operator()() const { return *this; } 14 | 15 | template 16 | bool matches(const U &) const; 17 | 18 | protected: 19 | inline /*virtual*/ NSString * failure_message_end() const { return @"be empty"; } 20 | }; 21 | 22 | static const BeEmpty be_empty = BeEmpty(); 23 | 24 | #pragma mark Generic 25 | template 26 | bool BeEmpty::matches(const U & actualValue) const { 27 | return Comparators::compare_empty(actualValue); 28 | } 29 | }} 30 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/BeGTE.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Base.h" 3 | 4 | namespace Cedar { namespace Matchers { 5 | 6 | template 7 | class BeGTE : public Base<> { 8 | private: 9 | BeGTE & operator=(const BeGTE &); 10 | 11 | public: 12 | explicit BeGTE(const T & expectedValue); 13 | ~BeGTE(); 14 | // Allow default copy ctor. 15 | 16 | template 17 | bool matches(const U &) const; 18 | 19 | protected: 20 | virtual NSString * failure_message_end() const; 21 | 22 | private: 23 | const T & expectedValue_; 24 | }; 25 | 26 | template 27 | BeGTE be_gte(const T & expectedValue) { 28 | return BeGTE(expectedValue); 29 | } 30 | 31 | template 32 | BeGTE be_greater_than_or_equal_to(const T & expectedValue) { 33 | return be_gte(expectedValue); 34 | } 35 | 36 | template 37 | BeGTE::BeGTE(const T & expectedValue) 38 | : Base<>(), expectedValue_(expectedValue) { 39 | } 40 | 41 | template 42 | BeGTE::~BeGTE() { 43 | } 44 | 45 | template 46 | /*virtual*/ NSString * BeGTE::failure_message_end() const { 47 | return [NSString stringWithFormat:@"be greater than or equal to <%@>", Stringifiers::string_for(expectedValue_)]; 48 | } 49 | 50 | template template 51 | bool BeGTE::matches(const U & actualValue) const { 52 | return Comparators::compare_greater_than(actualValue, expectedValue_) || Comparators::compare_equal(actualValue, expectedValue_); 53 | } 54 | 55 | #pragma mark operators 56 | template 57 | bool operator>=(const ActualValue & actualValue, const U & expectedValue) { 58 | return actualValue.to >= expectedValue; 59 | } 60 | 61 | template 62 | bool operator>=(const ActualValueMatchProxy & actualValueMatchProxy, const U & expectedValue) { 63 | actualValueMatchProxy(be_gte(expectedValue)); 64 | return true; 65 | } 66 | }} 67 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/BeGreaterThan.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Base.h" 3 | 4 | namespace Cedar { namespace Matchers { 5 | 6 | template 7 | class BeGreaterThan : public Base<> { 8 | private: 9 | BeGreaterThan & operator=(const BeGreaterThan &); 10 | 11 | public: 12 | explicit BeGreaterThan(const T & expectedValue); 13 | ~BeGreaterThan(); 14 | // Allow default copy ctor. 15 | 16 | template 17 | bool matches(const U &) const; 18 | 19 | protected: 20 | virtual NSString * failure_message_end() const; 21 | 22 | private: 23 | const T & expectedValue_; 24 | }; 25 | 26 | template 27 | BeGreaterThan be_greater_than(const T & expectedValue) { 28 | return BeGreaterThan(expectedValue); 29 | } 30 | 31 | template 32 | BeGreaterThan::BeGreaterThan(const T & expectedValue) 33 | : Base<>(), expectedValue_(expectedValue) { 34 | } 35 | 36 | template 37 | BeGreaterThan::~BeGreaterThan() { 38 | } 39 | 40 | template 41 | /*virtual*/ NSString * BeGreaterThan::failure_message_end() const { 42 | return [NSString stringWithFormat:@"be greater than <%@>", Stringifiers::string_for(expectedValue_)]; 43 | } 44 | 45 | template template 46 | bool BeGreaterThan::matches(const U & actualValue) const { 47 | return Comparators::compare_greater_than(actualValue, expectedValue_); 48 | } 49 | 50 | #pragma mark operators 51 | template 52 | bool operator>(const ActualValue & actualValue, const U & expectedValue) { 53 | return actualValue.to > expectedValue; 54 | } 55 | 56 | template 57 | bool operator>(const ActualValueMatchProxy & actualValueMatchProxy, const U & expectedValue) { 58 | actualValueMatchProxy(be_greater_than(expectedValue)); 59 | return true; 60 | } 61 | }} 62 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/BeInstanceOf.h: -------------------------------------------------------------------------------- 1 | #import "Base.h" 2 | 3 | namespace Cedar { namespace Matchers { 4 | struct BeInstanceOfMessageBuilder { 5 | template 6 | static NSString * string_for_actual_value(const U & value) { 7 | return [NSString stringWithFormat:@"%@ (%@)", value, [value class]]; 8 | } 9 | }; 10 | 11 | class BeInstanceOf : public Base { 12 | private: 13 | BeInstanceOf & operator=(const BeInstanceOf &); 14 | 15 | public: 16 | explicit BeInstanceOf(const Class expectedValue); 17 | ~BeInstanceOf(); 18 | // Allow default copy ctor. 19 | 20 | template 21 | bool matches(const U &) const; 22 | 23 | BeInstanceOf & or_any_subclass(); 24 | 25 | protected: 26 | virtual NSString * failure_message_end() const; 27 | 28 | private: 29 | const Class expectedClass_; 30 | bool includeSubclasses_; 31 | }; 32 | 33 | inline BeInstanceOf be_instance_of(const Class expectedValue) { 34 | return BeInstanceOf(expectedValue); 35 | } 36 | 37 | inline BeInstanceOf::BeInstanceOf(const Class expectedClass) 38 | : Base(), expectedClass_(expectedClass), includeSubclasses_(false) {} 39 | 40 | inline BeInstanceOf::~BeInstanceOf() {} 41 | 42 | inline BeInstanceOf & BeInstanceOf::or_any_subclass() { 43 | includeSubclasses_ = true; 44 | return *this; 45 | } 46 | 47 | inline /*virtual*/ NSString * BeInstanceOf::failure_message_end() const { 48 | NSMutableString *messageEnd = [NSMutableString stringWithFormat:@"be an instance of class <%@>", expectedClass_]; 49 | if (includeSubclasses_) { 50 | [messageEnd appendString:@", or any of its subclasses"]; 51 | } 52 | return messageEnd; 53 | } 54 | 55 | #pragma mark Generic 56 | template 57 | bool BeInstanceOf::matches(const U & actualValue) const { 58 | if (includeSubclasses_) { 59 | return [actualValue isKindOfClass:expectedClass_]; 60 | } else { 61 | return [actualValue isMemberOfClass:expectedClass_]; 62 | } 63 | } 64 | }} 65 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/BeLTE.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Base.h" 3 | 4 | namespace Cedar { namespace Matchers { 5 | 6 | template 7 | class BeLTE : public Base<> { 8 | private: 9 | BeLTE & operator=(const BeLTE &); 10 | 11 | public: 12 | explicit BeLTE(const T & expectedValue); 13 | ~BeLTE(); 14 | // Allow default copy ctor. 15 | 16 | template 17 | bool matches(const U &) const; 18 | 19 | protected: 20 | virtual NSString * failure_message_end() const; 21 | 22 | private: 23 | const T & expectedValue_; 24 | }; 25 | 26 | template 27 | BeLTE be_lte(const T & expectedValue) { 28 | return BeLTE(expectedValue); 29 | } 30 | 31 | template 32 | BeLTE be_less_than_or_equal_to(const T & expectedValue) { 33 | return be_lte(expectedValue); 34 | } 35 | 36 | template 37 | BeLTE::BeLTE(const T & expectedValue) 38 | : Base<>(), expectedValue_(expectedValue) { 39 | } 40 | 41 | template 42 | BeLTE::~BeLTE() { 43 | } 44 | 45 | template 46 | /*virtual*/ NSString * BeLTE::failure_message_end() const { 47 | return [NSString stringWithFormat:@"be less than or equal to <%@>", Stringifiers::string_for(expectedValue_)]; 48 | } 49 | 50 | template template 51 | bool BeLTE::matches(const U & actualValue) const { 52 | return !Comparators::compare_greater_than(actualValue, expectedValue_); 53 | } 54 | 55 | #pragma mark operators 56 | template 57 | bool operator<=(const ActualValue & actualValue, const U & expectedValue) { 58 | return actualValue.to <= expectedValue; 59 | } 60 | 61 | template 62 | bool operator<=(const ActualValueMatchProxy & actualValueMatchProxy, const U & expectedValue) { 63 | actualValueMatchProxy(be_lte(expectedValue)); 64 | return true; 65 | } 66 | }} 67 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/BeLessThan.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Base.h" 3 | 4 | namespace Cedar { namespace Matchers { 5 | 6 | template 7 | class BeLessThan : public Base<> { 8 | private: 9 | BeLessThan & operator=(const BeLessThan &); 10 | 11 | public: 12 | explicit BeLessThan(const T & expectedValue); 13 | ~BeLessThan(); 14 | // Allow default copy ctor. 15 | 16 | template 17 | bool matches(const U &) const; 18 | 19 | protected: 20 | virtual NSString * failure_message_end() const; 21 | 22 | private: 23 | const T & expectedValue_; 24 | }; 25 | 26 | template 27 | BeLessThan be_less_than(const T & expectedValue) { 28 | return BeLessThan(expectedValue); 29 | } 30 | 31 | template 32 | BeLessThan::BeLessThan(const T & expectedValue) 33 | : Base<>(), expectedValue_(expectedValue) { 34 | } 35 | 36 | template 37 | BeLessThan::~BeLessThan() { 38 | } 39 | 40 | template 41 | /*virtual*/ NSString * BeLessThan::failure_message_end() const { 42 | return [NSString stringWithFormat:@"be less than <%@>", Stringifiers::string_for(expectedValue_)]; 43 | } 44 | 45 | template template 46 | bool BeLessThan::matches(const U & actualValue) const { 47 | return !Comparators::compare_greater_than(actualValue, expectedValue_) && !Comparators::compare_equal(actualValue, expectedValue_); 48 | } 49 | 50 | #pragma mark operators 51 | template 52 | bool operator<(const ActualValue & actualValue, const U & expectedValue) { 53 | return actualValue.to < expectedValue; 54 | } 55 | 56 | template 57 | bool operator<(const ActualValueMatchProxy & actualValueMatchProxy, const U & expectedValue) { 58 | actualValueMatchProxy(be_less_than(expectedValue)); 59 | return true; 60 | } 61 | }} 62 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/BeNil.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Base.h" 3 | #import "CDRSpecFailure.h" 4 | 5 | #include 6 | 7 | namespace Cedar { namespace Matchers { 8 | struct BeNilMessageBuilder { 9 | template 10 | static NSString * string_for_actual_value(const U & value) { 11 | throw std::logic_error("Should never generate a failure message for a nil comparison to non-pointer type."); 12 | } 13 | 14 | template 15 | static NSString * string_for_actual_value(U * const & value) { 16 | return value ? [NSString stringWithFormat:@"%p", value] : @"nil"; 17 | } 18 | }; 19 | 20 | class BeNil : public Base { 21 | private: 22 | BeNil & operator=(const BeNil &); 23 | 24 | public: 25 | inline BeNil() : Base() {} 26 | inline ~BeNil() {} 27 | // Allow default copy ctor. 28 | 29 | inline const BeNil & operator()() const { return *this; } 30 | 31 | template 32 | bool matches(const U &) const; 33 | 34 | template 35 | bool matches(U * const &) const; 36 | 37 | protected: 38 | inline /*virtual*/ NSString * failure_message_end() const { return @"be nil"; } 39 | }; 40 | 41 | static const BeNil be_nil = BeNil(); 42 | 43 | #pragma mark Generic 44 | template 45 | bool BeNil::matches(const U & actualValue) const { 46 | [[CDRSpecFailure specFailureWithReason:@"Attempt to compare non-pointer type to nil"] raise]; 47 | return NO; 48 | } 49 | 50 | template 51 | bool BeNil::matches(U * const & actualValue) const { 52 | return !actualValue; 53 | } 54 | 55 | }} 56 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/BeSameInstanceAs.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Base.h" 3 | 4 | namespace Cedar { namespace Matchers { 5 | template 6 | class BeSameInstanceAs : public Base<> { 7 | private: 8 | BeSameInstanceAs & operator=(const BeSameInstanceAs &); 9 | 10 | public: 11 | explicit BeSameInstanceAs(T * const expectedValue); 12 | ~BeSameInstanceAs(); 13 | // Allow default copy ctor. 14 | 15 | template 16 | bool matches(const U &) const; 17 | 18 | template 19 | bool matches(U * const &) const; 20 | 21 | protected: 22 | virtual NSString * failure_message_end() const; 23 | 24 | private: 25 | const T * expectedValue_; 26 | }; 27 | 28 | template 29 | BeSameInstanceAs be_same_instance_as(T * const expectedValue) { 30 | return BeSameInstanceAs(expectedValue); 31 | } 32 | 33 | template 34 | BeSameInstanceAs::BeSameInstanceAs(T * const expectedValue) 35 | : Base<>(), expectedValue_(expectedValue) { 36 | } 37 | 38 | template 39 | BeSameInstanceAs::~BeSameInstanceAs() { 40 | } 41 | 42 | template 43 | /*virtual*/ NSString * BeSameInstanceAs::failure_message_end() const { 44 | return [NSString stringWithFormat:@"be same instance as <%p>", expectedValue_]; 45 | } 46 | 47 | #pragma mark Generic 48 | template template 49 | bool BeSameInstanceAs::matches(const U & actualValue) const { 50 | [[CDRSpecFailure specFailureWithReason:@"Attempt to compare non-pointer type for sameness."] raise]; 51 | return NO; 52 | } 53 | 54 | template template 55 | bool BeSameInstanceAs::matches(U * const & actualValue) const { 56 | return actualValue == expectedValue_; 57 | } 58 | 59 | }} 60 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/BeTruthy.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Base.h" 3 | 4 | namespace Cedar { namespace Matchers { 5 | class BeTruthy : public Base<> { 6 | private: 7 | BeTruthy & operator=(const BeTruthy &); 8 | 9 | public: 10 | inline BeTruthy() : Base<>() {} 11 | inline ~BeTruthy() {} 12 | // Allow default copy ctor. 13 | 14 | inline const BeTruthy & operator()() const { return *this; } 15 | 16 | template 17 | bool matches(const U &) const; 18 | 19 | protected: 20 | inline /*virtual*/ NSString * failure_message_end() const { return @"evaluate to true"; } 21 | }; 22 | 23 | static const BeTruthy be_truthy = BeTruthy(); 24 | 25 | #pragma mark Generic 26 | template 27 | bool BeTruthy::matches(const U & actualValue) const { 28 | return !!actualValue; 29 | } 30 | 31 | }} 32 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CDRColorizedReporter.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "CDRDefaultReporter.h" 3 | 4 | @interface CDRColorizedReporter : CDRDefaultReporter 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CDRExampleBase.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "CDRExampleParent.h" 3 | 4 | @protocol CDRExampleReporter; 5 | 6 | typedef void (^CDRSpecBlock)(void); 7 | 8 | enum CDRExampleState { 9 | CDRExampleStateIncomplete = 0x00, 10 | CDRExampleStateSkipped = 0x01, 11 | CDRExampleStatePassed = 0x03, 12 | CDRExampleStatePending = 0x07, 13 | CDRExampleStateFailed = 0x0F, 14 | CDRExampleStateError = 0x1F 15 | }; 16 | typedef enum CDRExampleState CDRExampleState; 17 | 18 | @interface CDRExampleBase : NSObject { 19 | NSString *text_; 20 | id parent_; 21 | BOOL focused_; 22 | } 23 | 24 | @property (nonatomic, readonly) NSString *text; 25 | @property (nonatomic, assign) id parent; 26 | @property (nonatomic, assign, getter=isFocused) BOOL focused; 27 | 28 | - (id)initWithText:(NSString *)text; 29 | 30 | - (void)run; 31 | - (BOOL)shouldRun; 32 | 33 | - (BOOL)hasChildren; 34 | - (BOOL)hasFocusedExamples; 35 | 36 | - (NSString *)message; 37 | - (NSString *)fullText; 38 | - (NSMutableArray *)fullTextInPieces; 39 | @end 40 | 41 | @interface CDRExampleBase (RunReporting) 42 | - (CDRExampleState)state; 43 | - (float)progress; 44 | @end 45 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CDRExampleDetailsViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class CDRExampleBase; 4 | 5 | @interface CDRExampleDetailsViewController : UIViewController 6 | 7 | @property (nonatomic, assign) UINavigationBar *navigationBar; 8 | @property (nonatomic, assign) UILabel *fullTextLabel, *messageLabel; 9 | 10 | - (id)initWithExample:(CDRExampleBase *)example; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CDRExampleParent.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @protocol CDRExampleParent 4 | 5 | - (BOOL)shouldRun; 6 | 7 | - (void)setUp; 8 | - (void)tearDown; 9 | 10 | @optional 11 | - (BOOL)hasFullText; 12 | - (NSString *)fullText; 13 | - (NSMutableArray *)fullTextInPieces; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CDRExampleReporter.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @protocol CDRExampleReporter 4 | 5 | - (void)runWillStartWithGroups:(NSArray *)groups; 6 | - (void)runDidComplete; 7 | - (int)result; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CDRExampleReporterViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "CDRExampleReporter.h" 3 | 4 | @interface CDRExampleReporterViewController : UINavigationController { 5 | } 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CDRFunctions.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NSArray *CDRReportersFromEnv(const char*defaultReporterClassName); 4 | 5 | int runSpecs(); 6 | int runAllSpecs() __attribute__((deprecated)); 7 | int runSpecsWithCustomExampleReporters(NSArray *reporters); 8 | NSArray *specClassesToRun(); 9 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CDRSharedExampleGroupPool.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @protocol CDRSharedExampleGroupPool 4 | @end 5 | 6 | typedef void (^CDRSharedExampleGroupBlock)(NSDictionary *); 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | void sharedExamplesFor(NSString *, CDRSharedExampleGroupBlock); 12 | void itShouldBehaveLike(NSString *); 13 | #ifdef __cplusplus 14 | } 15 | #endif 16 | 17 | @interface CDRSharedExampleGroupPool : NSObject 18 | @end 19 | 20 | @interface CDRSharedExampleGroupPool (SharedExampleGroupDeclaration) 21 | - (void)declareSharedExampleGroups; 22 | @end 23 | 24 | #define SHARED_EXAMPLE_GROUPS_BEGIN(name) \ 25 | @interface SharedExampleGroupPoolFor##name : CDRSharedExampleGroupPool \ 26 | @end \ 27 | @implementation SharedExampleGroupPoolFor##name \ 28 | - (void)declareSharedExampleGroups { 29 | 30 | #define SHARED_EXAMPLE_GROUPS_END \ 31 | } \ 32 | @end 33 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CDRSpec.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "CDRExampleBase.h" 3 | 4 | @protocol CDRExampleReporter; 5 | @class CDRExampleGroup, CDRExample, SpecHelper; 6 | 7 | @protocol CDRSpec 8 | @end 9 | 10 | extern const CDRSpecBlock PENDING; 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | void beforeEach(CDRSpecBlock); 16 | void afterEach(CDRSpecBlock); 17 | 18 | CDRExampleGroup * describe(NSString *, CDRSpecBlock); 19 | CDRExampleGroup * context(NSString *, CDRSpecBlock); 20 | CDRExample * it(NSString *, CDRSpecBlock); 21 | 22 | CDRExampleGroup * xcontext(NSString *, CDRSpecBlock); 23 | CDRExampleGroup * xdescribe(NSString *, CDRSpecBlock); 24 | CDRExample * xit(NSString *, CDRSpecBlock); 25 | 26 | CDRExampleGroup * fdescribe(NSString *, CDRSpecBlock); 27 | CDRExampleGroup * fcontext(NSString *, CDRSpecBlock); 28 | CDRExample * fit(NSString *, CDRSpecBlock); 29 | 30 | void fail(NSString *); 31 | #ifdef __cplusplus 32 | } 33 | 34 | #import "ActualValue.h" 35 | #import "ShouldSyntax.h" 36 | #import "CedarComparators.h" 37 | #import "CedarMatchers.h" 38 | 39 | #endif // __cplusplus 40 | 41 | @interface CDRSpec : NSObject { 42 | CDRExampleGroup *rootGroup_; 43 | CDRExampleGroup *currentGroup_; 44 | } 45 | 46 | @property (nonatomic, retain) CDRExampleGroup *currentGroup, *rootGroup; 47 | - (void)defineBehaviors; 48 | @end 49 | 50 | @interface CDRSpec (SpecDeclaration) 51 | - (void)declareBehaviors; 52 | @end 53 | 54 | #define SPEC_BEGIN(name) \ 55 | @interface name : CDRSpec \ 56 | @end \ 57 | @implementation name \ 58 | - (void)declareBehaviors { 59 | 60 | #define SPEC_END \ 61 | } \ 62 | @end 63 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CDRSpecFailure.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface CDRSpecFailure : NSException { 4 | NSString *fileName_; 5 | int lineNumber_; 6 | } 7 | 8 | @property (nonatomic, retain, readonly) NSString *fileName; 9 | @property (nonatomic, assign, readonly) int lineNumber; 10 | 11 | + (id)specFailureWithReason:(NSString *)reason; 12 | + (id)specFailureWithReason:(NSString *)reason fileName:(NSString *)fileName lineNumber:(int)lineNumber; 13 | + (id)specFailureWithRaisedObject:(NSObject *)object; 14 | 15 | - (id)initWithReason:(NSString *)reason; 16 | - (id)initWithReason:(NSString *)reason fileName:(NSString *)fileName lineNumber:(int)lineNumber; 17 | - (id)initWithRaisedObject:(NSObject *)object; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/Cedar.h: -------------------------------------------------------------------------------- 1 | #if TARGET_OS_IPHONE 2 | #import "CDRFunctions.h" 3 | #import "CedarApplicationDelegate.h" 4 | #import "CDRExampleReporterViewController.h" 5 | #else 6 | #import "CDRFunctions.h" 7 | #endif 8 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CedarApplicationDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | int runSpecsWithinUIApplication(); 4 | void exitWithStatusFromUIApplication(int status); 5 | 6 | @class CDRExampleReporterViewController; 7 | 8 | // In some cases CDRIPhoneOTestRunner needs to spin up an instance of Cedar app. 9 | // It appears that SenTestingKit fails to start up the test when CedarApplicationDelegate 10 | // is used. Solution is to use a subclass of UIApplicaton. 11 | @interface CedarApplication : UIApplication { 12 | UIWindow *window_; 13 | CDRExampleReporterViewController *viewController_; 14 | } 15 | @end 16 | 17 | // Needed for backwards compatibility with existing projects using CedarApplicationDelegate 18 | @interface CedarApplicationDelegate : NSObject { 19 | UIWindow *window_; 20 | CDRExampleReporterViewController *viewController_; 21 | } 22 | @end 23 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CedarComparators.h: -------------------------------------------------------------------------------- 1 | #import "ComparatorsBase.h" 2 | #import "ComparatorsContainer.h" 3 | 4 | #ifdef CEDAR_CUSTOM_COMPARATORS 5 | #import CEDAR_CUSTOM_COMPARATORS 6 | #endif 7 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CedarMatchers.h: -------------------------------------------------------------------------------- 1 | // Base 2 | #import "Equal.h" 3 | #import "BeTruthy.h" 4 | #import "BeNil.h" 5 | #import "BeCloseTo.h" 6 | #import "BeSameInstanceAs.h" 7 | #import "BeInstanceOf.h" 8 | #import "BeGreaterThan.h" 9 | #import "BeGTE.h" 10 | #import "BeLessThan.h" 11 | #import "BeLTE.h" 12 | #import "RaiseException.h" 13 | 14 | // Container 15 | #import "BeEmpty.h" 16 | #import "Contain.h" 17 | 18 | #ifdef CEDAR_CUSTOM_MATCHERS 19 | #import CEDAR_CUSTOM_MATCHERS 20 | #endif 21 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CedarStringifiers.h: -------------------------------------------------------------------------------- 1 | #import "StringifiersBase.h" 2 | #import "StringifiersContainer.h" 3 | 4 | #ifdef CEDAR_CUSTOM_STRINGIFIERS 5 | #import CEDAR_CUSTOM_STRINGIFIERS 6 | #endif 7 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/ComparatorsBase.h: -------------------------------------------------------------------------------- 1 | #import "CompareEqual.h" 2 | #import "CompareGreaterThan.h" 3 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/ComparatorsContainer.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | 6 | // Container 7 | namespace Cedar { namespace Matchers { namespace Comparators { 8 | #pragma mark compare_empty 9 | template 10 | bool compare_empty(const T & container) { 11 | return 0 == [container count]; 12 | } 13 | 14 | template 15 | bool compare_empty(const typename std::vector & container) { 16 | return container.empty(); 17 | } 18 | 19 | template 20 | bool compare_empty(const typename std::map & container) { 21 | return container.empty(); 22 | } 23 | 24 | template 25 | bool compare_empty(const typename std::set & container) { 26 | return container.empty(); 27 | } 28 | 29 | #pragma mark compare_contains 30 | template 31 | bool compare_contains(const T & container, const U & element) { 32 | return [container containsObject:element]; 33 | } 34 | 35 | template 36 | bool compare_contains(NSString * const container, const U & element) { 37 | NSRange range = [container rangeOfString:element]; 38 | return container && range.location != NSNotFound; 39 | } 40 | 41 | template 42 | bool compare_contains(NSMutableString * const container, const U & element) { 43 | return compare_contains(static_cast(container), element); 44 | } 45 | 46 | template 47 | bool compare_contains(NSDictionary * const container, const U & element) { 48 | [[NSException exceptionWithName:NSInternalInconsistencyException reason:@"Unexpected use of 'contain' matcher with dictionary; use contain_key or contain_value" userInfo:nil] raise]; 49 | return false; 50 | } 51 | 52 | template 53 | bool compare_contains(NSMutableDictionary * const container, const U & element) { 54 | return compare_contains(static_cast(container), element); 55 | } 56 | 57 | namespace { 58 | template 59 | class CompareEqualTo { 60 | private: 61 | CompareEqualTo & operator=(const CompareEqualTo &); 62 | 63 | public: 64 | explicit CompareEqualTo(const T & rhs): rhs_(rhs) {} 65 | // Allow default copy ctor. 66 | ~CompareEqualTo() {} 67 | 68 | template 69 | bool operator()(const U & lhs) const { return compare_equal(lhs, rhs_); } 70 | 71 | private: 72 | const T & rhs_; 73 | }; 74 | } 75 | 76 | template 77 | bool compare_contains(const typename std::vector & container, const U & element) { 78 | return container.end() != std::find_if(container.begin(), container.end(), CompareEqualTo(element)); 79 | } 80 | 81 | template 82 | bool compare_contains(const typename std::map & container, const V & element) { 83 | return compare_contains(static_cast(nil), element); 84 | } 85 | 86 | template 87 | bool compare_contains(const typename std::set & container, const U & element) { 88 | return container.end() != std::find_if(container.begin(), container.end(), CompareEqualTo(element)); 89 | } 90 | }}} 91 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CompareEqual.h: -------------------------------------------------------------------------------- 1 | namespace Cedar { namespace Matchers { namespace Comparators { 2 | 3 | #pragma mark Generic 4 | template 5 | bool compare_equal(const T & actualValue, const U & expectedValue) { 6 | if (strcmp(@encode(T), "@") == 0 && strcmp(@encode(U), "@") == 0) { 7 | NSValue *actualValueId = [NSValue value:&actualValue withObjCType:@encode(id)]; 8 | NSValue *expectedValueId = [NSValue value:&expectedValue withObjCType:@encode(id)]; 9 | return [[actualValueId nonretainedObjectValue] isEqual:[expectedValueId nonretainedObjectValue]]; 10 | } else { 11 | return actualValue == expectedValue; 12 | } 13 | } 14 | 15 | #pragma mark NSNumber 16 | inline bool compare_equal(NSNumber * const actualValue, NSNumber * const expectedValue) { 17 | return [actualValue isEqualToNumber:expectedValue]; 18 | } 19 | 20 | inline bool compare_equal(NSNumber * const actualValue, const id expectedValue) { 21 | return [expectedValue isEqual:actualValue]; 22 | } 23 | 24 | inline bool compare_equal(NSNumber * const actualValue, NSObject * const expectedValue) { 25 | return compare_equal(actualValue, static_cast(expectedValue)); 26 | } 27 | 28 | inline bool compare_equal(NSNumber * const actualValue, NSValue * const expectedValue) { 29 | return compare_equal(actualValue, static_cast(expectedValue)); 30 | } 31 | 32 | inline bool compare_equal(const id actualValue, NSNumber * const expectedValue) { 33 | return compare_equal(expectedValue, actualValue); 34 | } 35 | 36 | inline bool compare_equal(NSObject * const actualValue, NSNumber * const expectedValue) { 37 | return compare_equal(expectedValue, actualValue); 38 | } 39 | 40 | template 41 | bool compare_equal(NSNumber * const actualValue, const U & expectedValue) { 42 | return [actualValue floatValue] == expectedValue; 43 | } 44 | 45 | template 46 | bool compare_equal(const T & actualValue, NSNumber * const expectedValue) { 47 | return compare_equal(expectedValue, actualValue); 48 | } 49 | 50 | }}} 51 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/CompareGreaterThan.h: -------------------------------------------------------------------------------- 1 | namespace Cedar { namespace Matchers { namespace Comparators { 2 | 3 | #pragma mark Generic 4 | template 5 | bool compare_greater_than(const T & actualValue, const U & expectedValue) { 6 | return actualValue > expectedValue; 7 | } 8 | 9 | #pragma mark NSNumber 10 | inline bool compare_greater_than(NSNumber * const actualValue, NSNumber * const expectedValue) { 11 | return NSOrderedDescending == [actualValue compare:expectedValue]; 12 | } 13 | 14 | template 15 | bool compare_greater_than(NSNumber * const actualValue, const U & expectedValue) { 16 | return [actualValue floatValue] > expectedValue; 17 | } 18 | 19 | inline bool compare_greater_than(NSNumber * const actualValue, const id expectedValue) { 20 | if ([expectedValue respondsToSelector:@selector(floatValue)]) { 21 | return compare_greater_than(actualValue, [expectedValue floatValue]); 22 | } 23 | return false; 24 | } 25 | 26 | inline bool compare_greater_than(NSNumber * const actualValue, NSObject * const expectedValue) { 27 | return compare_greater_than(actualValue, static_cast(expectedValue)); 28 | } 29 | 30 | inline bool compare_greater_than(NSNumber * const actualValue, NSValue * const expectedValue) { 31 | return compare_greater_than(actualValue, static_cast(expectedValue)); 32 | } 33 | 34 | template 35 | bool compare_greater_than(const T & actualValue, NSNumber * const expectedValue) { 36 | return actualValue > [expectedValue floatValue]; 37 | } 38 | 39 | inline bool compare_greater_than(const id actualValue, NSNumber * const expectedValue) { 40 | if ([actualValue respondsToSelector:@selector(floatValue)]) { 41 | return compare_greater_than([actualValue floatValue], expectedValue); 42 | } 43 | return false; 44 | } 45 | 46 | inline bool compare_greater_than(NSObject * const actualValue, NSNumber * const expectedValue) { 47 | return compare_greater_than(static_cast(actualValue), expectedValue); 48 | } 49 | 50 | inline bool compare_greater_than(NSValue * const actualValue, NSNumber * const expectedValue) { 51 | return compare_greater_than(static_cast(actualValue), expectedValue); 52 | } 53 | 54 | }}} 55 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/Contain.h: -------------------------------------------------------------------------------- 1 | #import "Base.h" 2 | 3 | namespace Cedar { namespace Matchers { 4 | template 5 | class Contain : public Base<> { 6 | private: 7 | Contain & operator=(const Contain &); 8 | 9 | public: 10 | explicit Contain(const T & element); 11 | ~Contain(); 12 | // Allow default copy ctor. 13 | 14 | template 15 | bool matches(const U &) const; 16 | 17 | protected: 18 | virtual NSString * failure_message_end() const; 19 | 20 | private: 21 | const T & element_; 22 | }; 23 | 24 | template 25 | inline Contain contain(const T & element) { 26 | return Contain(element); 27 | } 28 | 29 | template 30 | inline Contain::Contain(const T & element) 31 | : Base<>(), element_(element) { 32 | } 33 | 34 | template 35 | Contain::~Contain() { 36 | } 37 | 38 | template 39 | inline /*virtual*/ NSString * Contain::failure_message_end() const { 40 | return [NSString stringWithFormat:@"contain <%@>", Stringifiers::string_for(element_)]; 41 | } 42 | 43 | #pragma mark Generic 44 | template template 45 | bool Contain::matches(const U & actualValue) const { 46 | return Comparators::compare_contains(actualValue, element_); 47 | } 48 | }} 49 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/Equal.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Base.h" 3 | 4 | namespace Cedar { namespace Matchers { 5 | 6 | template 7 | class Equal : public Base<> { 8 | private: 9 | Equal & operator=(const Equal &); 10 | 11 | public: 12 | explicit Equal(const T & expectedValue); 13 | ~Equal(); 14 | // Allow default copy ctor. 15 | 16 | template 17 | bool matches(const U &) const; 18 | 19 | protected: 20 | virtual NSString * failure_message_end() const; 21 | 22 | private: 23 | const T & expectedValue_; 24 | }; 25 | 26 | template 27 | Equal equal(const T & expectedValue) { 28 | return Equal(expectedValue); 29 | } 30 | 31 | template 32 | Equal::Equal(const T & expectedValue) 33 | : Base<>(), expectedValue_(expectedValue) { 34 | } 35 | 36 | template 37 | Equal::~Equal() { 38 | } 39 | 40 | template 41 | /*virtual*/ NSString * Equal::failure_message_end() const { 42 | return [NSString stringWithFormat:@"equal <%@>", Stringifiers::string_for(expectedValue_)]; 43 | } 44 | 45 | template template 46 | bool Equal::matches(const U & actualValue) const { 47 | return Comparators::compare_equal(actualValue, expectedValue_); 48 | } 49 | 50 | #pragma mark equality operators 51 | template 52 | bool operator==(const ActualValue & actualValue, const U & expectedValue) { 53 | return actualValue.to == expectedValue; 54 | } 55 | 56 | template 57 | bool operator==(const ActualValueMatchProxy & actualValueMatchProxy, const U & expectedValue) { 58 | actualValueMatchProxy(equal(expectedValue)); 59 | return true; 60 | } 61 | 62 | template 63 | bool operator!=(const ActualValue & actualValue, const U & expectedValue) { 64 | return actualValue.to != expectedValue; 65 | } 66 | 67 | template 68 | bool operator!=(const ActualValueMatchProxy & actualValueMatchProxy, const U & expectedValue) { 69 | actualValueMatchProxy.negate()(equal(expectedValue)); 70 | return true; 71 | } 72 | }} 73 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/RaiseException.h: -------------------------------------------------------------------------------- 1 | #import "Base.h" 2 | 3 | namespace Cedar { namespace Matchers { 4 | class RaiseException : public Base<> { 5 | typedef void (^empty_block_t)(); 6 | 7 | private: 8 | RaiseException & operator=(const RaiseException &); 9 | 10 | public: 11 | explicit RaiseException(Class = nil, bool = false); 12 | ~RaiseException(); 13 | // Allow default copy ctor. 14 | 15 | RaiseException & operator()(Class = nil); 16 | RaiseException operator()(Class = nil) const; 17 | 18 | RaiseException & or_subclass(); 19 | RaiseException or_subclass() const; 20 | 21 | bool matches(empty_block_t) const; 22 | 23 | protected: 24 | virtual NSString * failure_message_end() const; 25 | 26 | private: 27 | const Class expectedExceptionClass_; 28 | bool allowSubclasses_; 29 | }; 30 | 31 | RaiseException raise() __attribute__((deprecated)); // Please use raise_exception 32 | inline RaiseException raise() { 33 | return RaiseException(); 34 | } 35 | 36 | static const RaiseException raise_exception = RaiseException(); 37 | 38 | inline RaiseException::RaiseException(Class expectedExceptionClass /*= nil*/, bool allowSubclasses /*= false */) 39 | : Base<>(), expectedExceptionClass_(expectedExceptionClass), allowSubclasses_(allowSubclasses) { 40 | } 41 | 42 | inline RaiseException::~RaiseException() { 43 | } 44 | 45 | inline RaiseException & RaiseException::operator()(Class expectedExceptionClass /*= nil*/) { 46 | return *this; 47 | } 48 | 49 | inline RaiseException RaiseException::operator()(Class expectedExceptionClass /*= nil*/) const { 50 | return RaiseException(expectedExceptionClass); 51 | } 52 | 53 | inline RaiseException & RaiseException::or_subclass() { 54 | allowSubclasses_ = true; 55 | return *this; 56 | } 57 | 58 | inline RaiseException RaiseException::or_subclass() const { 59 | return RaiseException(expectedExceptionClass_, true); 60 | } 61 | 62 | inline bool RaiseException::matches(empty_block_t block) const { 63 | @try { 64 | block(); 65 | } 66 | @catch (NSException *exception) { 67 | return !expectedExceptionClass_ || (allowSubclasses_ ? [exception isKindOfClass:expectedExceptionClass_] : [exception isMemberOfClass:expectedExceptionClass_]); 68 | } 69 | return false; 70 | } 71 | 72 | /*virtual*/ inline NSString * RaiseException::failure_message_end() const { 73 | NSMutableString *message = [NSMutableString stringWithFormat:@"raise an exception"]; 74 | if (expectedExceptionClass_) { 75 | [message appendString:@" of class"]; 76 | if (allowSubclasses_) { 77 | [message appendString:@", or subclass of class,"]; 78 | } 79 | [message appendFormat:@" <%@>", NSStringFromClass(expectedExceptionClass_)]; 80 | } 81 | return message; 82 | } 83 | }} 84 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/ShouldSyntax.h: -------------------------------------------------------------------------------- 1 | #import "ActualValue.h" 2 | 3 | namespace Cedar { namespace Matchers { 4 | 5 | struct ActualValueMarker { 6 | const char *fileName; 7 | int lineNumber; 8 | }; 9 | 10 | template 11 | const ActualValue operator,(const T & actualValue, const ActualValueMarker & marker) { 12 | return ActualValue(marker.fileName, marker.lineNumber, actualValue); 13 | } 14 | 15 | template 16 | const ActualValueMatchProxy operator,(const ActualValue & actualValue, bool negate) { 17 | return negate ? actualValue.to_not : actualValue.to; 18 | } 19 | 20 | template 21 | void operator,(const ActualValueMatchProxy & matchProxy, const MatcherType & matcher) { 22 | matchProxy(matcher); 23 | } 24 | 25 | }} 26 | 27 | #ifndef CEDAR_MATCHERS_DISALLOW_SHOULD 28 | #define should ,(ActualValueMarker){__FILE__, __LINE__},false, 29 | #define should_not ,(ActualValueMarker){__FILE__, __LINE__},true, 30 | #endif 31 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/SpecHelper.h: -------------------------------------------------------------------------------- 1 | #import "CDRSpec.h" 2 | #import "CDRSharedExampleGroupPool.h" 3 | #import "CDRExampleParent.h" 4 | 5 | @interface SpecHelper : NSObject { 6 | NSMutableDictionary *sharedExampleContext_, *sharedExampleGroups_; 7 | NSArray *globalBeforeEachClasses_, *globalAfterEachClasses_; 8 | BOOL shouldOnlyRunFocused_; 9 | } 10 | 11 | @property (nonatomic, retain, readonly) NSMutableDictionary *sharedExampleContext; 12 | @property (nonatomic, retain) NSArray *globalBeforeEachClasses, *globalAfterEachClasses; 13 | 14 | @property (nonatomic, assign) BOOL shouldOnlyRunFocused; 15 | 16 | + (SpecHelper *)specHelper; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/StringifiersBase.h: -------------------------------------------------------------------------------- 1 | #import 2 | #include 3 | 4 | namespace Cedar { namespace Matchers { namespace Stringifiers { 5 | template 6 | NSString * string_for(const U & value) { 7 | if (0 == strncmp(@encode(U), "@", 1)) { 8 | NSValue *valueId = [NSValue value:&value withObjCType:@encode(id)]; 9 | return [[valueId nonretainedObjectValue] description]; 10 | } else { 11 | std::stringstream temp; 12 | temp << value; 13 | return [NSString stringWithCString:temp.str().c_str() encoding:NSUTF8StringEncoding]; 14 | } 15 | } 16 | 17 | inline NSString * string_for(const char value) { 18 | return string_for(static_cast(value)); 19 | } 20 | 21 | inline NSString * string_for(const Class & value) { 22 | return NSStringFromClass(value); 23 | } 24 | 25 | inline NSString * string_for(const BOOL value) { 26 | return value ? @"YES" : @"NO"; 27 | } 28 | 29 | inline NSString * string_for(NSNumber * const value) { 30 | return string_for([value floatValue]); 31 | } 32 | }}} 33 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/A/Headers/StringifiersContainer.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | namespace Cedar { namespace Matchers { namespace Stringifiers { 6 | namespace { 7 | template 8 | NSString * comma_and_newline_delimited_list(const Container & container) { 9 | NSMutableString *result = [NSMutableString string]; 10 | bool first = true; 11 | for (typename Container::const_iterator it = container.begin(); it != container.end(); ++it, first = false) { 12 | if (!first) { 13 | [result appendString:@","]; 14 | } 15 | [result appendString:[NSString stringWithFormat:@"\n %@", string_for(*it)]]; 16 | } 17 | return result; 18 | } 19 | } 20 | 21 | template 22 | NSString * string_for(const typename std::vector & container) { 23 | return [NSString stringWithFormat:@"(%@\n)", comma_and_newline_delimited_list(container)]; 24 | } 25 | 26 | template 27 | NSString * string_for(const typename std::map & container) { 28 | NSMutableString *result = [NSMutableString stringWithString:@"{"]; 29 | 30 | for (typename std::map::const_iterator it = container.begin(); it != container.end(); ++it) { 31 | [result appendString:[NSString stringWithFormat:@"\n %@ = %@;", string_for(it->first), string_for(it->second)]]; 32 | } 33 | [result appendString:@"\n}"]; 34 | return result; 35 | } 36 | 37 | template 38 | NSString * string_for(const typename std::set & container) { 39 | return [NSString stringWithFormat:@"{(%@\n)}", comma_and_newline_delimited_list(container)]; 40 | } 41 | }}} 42 | -------------------------------------------------------------------------------- /examples/Cedar/Vendor/Frameworks/Cedar-iPhone.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /examples/EGORefreshTableHeaderView/Vendor/EGORefreshTableHeaderView/EGORefreshTableHeaderView.h: -------------------------------------------------------------------------------- 1 | // 2 | // EGORefreshTableHeaderView.h 3 | // Demo 4 | // 5 | // Created by Devin Doty on 10/14/09October14. 6 | // Copyright 2009 enormego. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | // 26 | 27 | #import 28 | #import 29 | 30 | typedef enum{ 31 | EGOOPullRefreshPulling = 0, 32 | EGOOPullRefreshNormal, 33 | EGOOPullRefreshLoading, 34 | } EGOPullRefreshState; 35 | 36 | @protocol EGORefreshTableHeaderDelegate; 37 | @interface EGORefreshTableHeaderView : UIView { 38 | 39 | id _delegate; 40 | EGOPullRefreshState _state; 41 | 42 | UILabel *_lastUpdatedLabel; 43 | UILabel *_statusLabel; 44 | CALayer *_arrowImage; 45 | UIActivityIndicatorView *_activityView; 46 | 47 | 48 | } 49 | 50 | @property(nonatomic,assign) id delegate; 51 | 52 | - (id)initWithFrame:(CGRect)frame arrowImageName:(NSString *)arrow textColor:(UIColor *)textColor; 53 | 54 | - (void)refreshLastUpdatedDate; 55 | - (void)egoRefreshScrollViewDidScroll:(UIScrollView *)scrollView; 56 | - (void)egoRefreshScrollViewDidEndDragging:(UIScrollView *)scrollView; 57 | - (void)egoRefreshScrollViewDataSourceDidFinishedLoading:(UIScrollView *)scrollView; 58 | 59 | @end 60 | @protocol EGORefreshTableHeaderDelegate 61 | - (void)egoRefreshTableHeaderDidTriggerRefresh:(EGORefreshTableHeaderView*)view; 62 | - (BOOL)egoRefreshTableHeaderDataSourceIsLoading:(EGORefreshTableHeaderView*)view; 63 | @optional 64 | - (NSDate*)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView*)view; 65 | @end 66 | -------------------------------------------------------------------------------- /examples/Reachability/Vendor/Reachability/Reachability.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: Reachability.h 4 | Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs. 5 | 6 | Version: 2.1 7 | 8 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. 9 | ("Apple") in consideration of your agreement to the following terms, and your 10 | use, installation, modification or redistribution of this Apple software 11 | constitutes acceptance of these terms. If you do not agree with these terms, 12 | please do not use, install, modify or redistribute this Apple software. 13 | 14 | In consideration of your agreement to abide by the following terms, and subject 15 | to these terms, Apple grants you a personal, non-exclusive license, under 16 | Apple's copyrights in this original Apple software (the "Apple Software"), to 17 | use, reproduce, modify and redistribute the Apple Software, with or without 18 | modifications, in source and/or binary forms; provided that if you redistribute 19 | the Apple Software in its entirety and without modifications, you must retain 20 | this notice and the following text and disclaimers in all such redistributions 21 | of the Apple Software. 22 | Neither the name, trademarks, service marks or logos of Apple Inc. may be used 23 | to endorse or promote products derived from the Apple Software without specific 24 | prior written permission from Apple. Except as expressly stated in this notice, 25 | no other rights or licenses, express or implied, are granted by Apple herein, 26 | including but not limited to any patent rights that may be infringed by your 27 | derivative works or by other works in which the Apple Software may be 28 | incorporated. 29 | 30 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO 31 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED 32 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR 33 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN 34 | COMBINATION WITH YOUR PRODUCTS. 35 | 36 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR 37 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 38 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR 40 | DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF 41 | CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF 42 | APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2010 Apple Inc. All Rights Reserved. 45 | 46 | */ 47 | 48 | 49 | #import 50 | #import 51 | #import 52 | 53 | typedef enum { 54 | NotReachable = 0, 55 | ReachableViaWiFi, 56 | ReachableViaWWAN 57 | } NetworkStatus; 58 | #define kReachabilityChangedNotification @"kNetworkReachabilityChangedNotification" 59 | 60 | @interface Reachability: NSObject 61 | { 62 | BOOL localWiFiRef; 63 | SCNetworkReachabilityRef reachabilityRef; 64 | } 65 | 66 | //reachabilityWithHostName- Use to check the reachability of a particular host name. 67 | + (Reachability*) reachabilityWithHostName: (NSString*) hostName; 68 | 69 | //reachabilityWithAddress- Use to check the reachability of a particular IP address. 70 | + (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress; 71 | 72 | //reachabilityForInternetConnection- checks whether the default route is available. 73 | // Should be used by applications that do not connect to a particular host 74 | + (Reachability*) reachabilityForInternetConnection; 75 | 76 | //reachabilityForLocalWiFi- checks whether a local wifi connection is available. 77 | + (Reachability*) reachabilityForLocalWiFi; 78 | 79 | //Start listening for reachability notifications on the current run loop 80 | - (BOOL) startNotifier; 81 | - (void) stopNotifier; 82 | 83 | - (NetworkStatus) currentReachabilityStatus; 84 | //WWAN may be available, but not active until a connection has been established. 85 | //WiFi may require a connection for VPN on Demand. 86 | - (BOOL) connectionRequired; 87 | @end 88 | 89 | 90 | -------------------------------------------------------------------------------- /lib/xcode/build_file.rb: -------------------------------------------------------------------------------- 1 | module Xcode 2 | 3 | # 4 | # PBXBuildFile are entries within the project that create a link between the 5 | # file and the PBXFileReference. One is created for each file added to a build 6 | # target. 7 | # 8 | module BuildFile 9 | 10 | # 11 | # Create the properties hash for a build file with the given file reference 12 | # identifier. 13 | # 14 | # @param [String] file_identifier the unique identifier for the file 15 | # @return [Hash] the properties hash for a default BuildFile. 16 | # 17 | def self.buildfile(file_identifier,settings) 18 | properties = { 'isa' => "PBXBuildFile", 'fileRef' => file_identifier } 19 | properties.merge!('settings' => settings) unless settings.empty? 20 | properties 21 | end 22 | 23 | end 24 | end -------------------------------------------------------------------------------- /lib/xcode/builder.rb: -------------------------------------------------------------------------------- 1 | require 'xcode/provisioning_profile' 2 | require 'xcode/test/parsers/ocunit_parser.rb' 3 | require 'xcode/test/parsers/kif_parser.rb' 4 | require 'xcode/builder/base_builder.rb' 5 | require 'xcode/builder/project_target_config_builder.rb' 6 | require 'xcode/builder/scheme_builder.rb' -------------------------------------------------------------------------------- /lib/xcode/builder/project_target_config_builder.rb: -------------------------------------------------------------------------------- 1 | module Xcode 2 | module Builder 3 | class ProjectTargetConfigBuilder < BaseBuilder 4 | 5 | def prepare_xcodebuild sdk=nil 6 | cmd = super sdk 7 | cmd << "-project \"#{@target.project.path}\"" 8 | cmd << "-target \"#{@target.name}\"" 9 | cmd << "-config \"#{@config.name}\"" 10 | cmd 11 | end 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /lib/xcode/builder/scheme_builder.rb: -------------------------------------------------------------------------------- 1 | module Xcode 2 | 3 | class Workspace 4 | def to_xcodebuild_option 5 | "-workspace \"#{self.path}\"" 6 | end 7 | end 8 | 9 | class Project 10 | def to_xcodebuild_option 11 | "-project \"#{self.path}\"" 12 | end 13 | end 14 | 15 | module Builder 16 | class SchemeBuilder < BaseBuilder 17 | 18 | def initialize(scheme) 19 | @scheme = scheme 20 | @target = @scheme.build_targets.last 21 | super @target, @target.config(@scheme.archive_config) 22 | end 23 | 24 | def prepare_xcodebuild sdk=@sdk 25 | cmd = super sdk 26 | cmd << @scheme.parent.to_xcodebuild_option 27 | cmd << "-scheme \"#{@scheme.name}\"" 28 | cmd << "-configuration \"#{@scheme.archive_config}\"" 29 | cmd 30 | end 31 | 32 | def prepare_test_command sdk=@sdk 33 | cmd = prepare_xcodebuild sdk 34 | cmd << "test" 35 | cmd 36 | end 37 | 38 | def test options = {:sdk => @sdk, :show_output => false} 39 | unless @scheme.testable? 40 | print_task :builder, "Nothing to test", :warning 41 | else 42 | super options 43 | end 44 | end 45 | 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/xcode/configuration_list.rb: -------------------------------------------------------------------------------- 1 | module Xcode 2 | module ConfigurationList 3 | 4 | # 5 | # @example configuration list 6 | # 7 | # 7165D47D146B4EA100DE2F0E /* Build configuration list for PBXNativeTarget "TestProject" */ = { 8 | # isa = XCConfigurationList; 9 | # buildConfigurations = ( 10 | # 7165D47E146B4EA100DE2F0E /* Debug */, 11 | # 7165D47F146B4EA100DE2F0E /* Release */, 12 | # ); 13 | # defaultConfigurationIsVisible = 0; 14 | # defaultConfigurationName = Release; 15 | # }; 16 | def self.configuration_list 17 | { 'isa' => 'XCConfigurationList', 18 | 'buildConfigurations' => [], 19 | 'defaultConfigurationIsVisible' => '0', 20 | 'defaultConfigurationName' => '' } 21 | end 22 | 23 | # 24 | # @return [Hash] a hash of symbol names to configuration names. 25 | # 26 | def self.symbol_config_name_to_config_name 27 | { :debug => 'Debug', :release => 'Release' } 28 | end 29 | 30 | # 31 | # Create a configuration for this ConfigurationList. This configuration needs 32 | # to have a name. 33 | # 34 | # @note unique names are currently not enforced but likely necessary for the 35 | # the target to be build successfully. 36 | # 37 | # @param [Types] name Description 38 | # 39 | def create_config(name) 40 | 41 | name = ConfigurationList.symbol_config_name_to_config_name[name] if ConfigurationList.symbol_config_name_to_config_name[name] 42 | 43 | # @todo a configuration has additional fields that are ususally set with 44 | # some target information for the title. 45 | 46 | new_config = @registry.add_object(Configuration.default_properties(name)) 47 | @properties['buildConfigurations'] << new_config.identifier 48 | 49 | yield new_config if block_given? 50 | 51 | new_config.save! 52 | end 53 | 54 | # 55 | # @return [BuildConfiguration] the build configuration that is set to default; 56 | # nil if no configuration has been set as default. 57 | # 58 | def default_config 59 | build_configurations.find {|config| config.name == default_configuration_name } 60 | end 61 | 62 | # 63 | # @return [String] the name of the default build configuration; nil if no 64 | # configuration has been set as default. 65 | # 66 | def default_config_name 67 | default_configuration_name 68 | end 69 | 70 | # 71 | # @todo allow the ability for a configuration to set itself as default and/or 72 | # let a configuration be specified as a parameter here. Though we need 73 | # to check to see that the configuration is part of the this configuration 74 | # list. 75 | # 76 | # @param [String] name of the build configuration to set as the default 77 | # configuration; specify nil if you want to remove any default configuration. 78 | # 79 | def set_default_config(name) 80 | # @todo ensure that the name specified is one of the available configurations 81 | @properties['defaultConfigurationName'] = name 82 | end 83 | 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/xcode/configuration_owner.rb: -------------------------------------------------------------------------------- 1 | module Xcode 2 | module ConfigurationOwner 3 | 4 | # 5 | # @return [Array] the configurations that this target 6 | # or project supports. These are generally 'Debug' or 'Release' but may be 7 | # custom created configurations. 8 | # 9 | def configs 10 | build_configuration_list.build_configurations.map do |config| 11 | config.target = self 12 | config 13 | end 14 | end 15 | 16 | # 17 | # Return a specific build configuration. 18 | # 19 | # @note an exception is raised if no configuration matches the specified name. 20 | # 21 | # @param [String] name of a configuration to return 22 | # 23 | # @return [BuildConfiguration] a specific build configuration that 24 | # matches the specified name. 25 | # 26 | def config(name) 27 | config = configs.select {|config| config.name == name.to_s }.first 28 | raise "No such config #{name}, available configs are #{configs.map {|c| c.name}.join(', ')}" if config.nil? 29 | yield config if block_given? 30 | config 31 | end 32 | 33 | # 34 | # Create a configuration for the target or project. 35 | # 36 | # @example creating a new 'App Store Submission' configuration for a project 37 | # 38 | # project.create_config 'App Store Submission' # => Configuration 39 | # 40 | # @example creating a new 'Ad Hoc' configuration for a target 41 | # 42 | # target.create_config 'Ad Hoc' do |config| 43 | # # configuration the new debug config. 44 | # end 45 | # 46 | # @param [String] name of the configuration to create 47 | # @return [BuildConfiguration] that is created 48 | # 49 | def create_configuration(name) 50 | # To create a configuration, we need to create or retrieve the configuration list 51 | 52 | created_config = build_configuration_list.create_config(name) do |config| 53 | yield config if block_given? 54 | end 55 | 56 | created_config 57 | end 58 | 59 | # 60 | # Create multiple configurations for a target or project. 61 | # 62 | # @example creating 'Release' and 'Debug for a new target 63 | # 64 | # new_target = project.create_target 'UniversalBinary' 65 | # new_target.create_configurations 'Debug', 'Release' do |config| 66 | # # set up the configurations 67 | # end 68 | # 69 | # @param [String,Array] configuration_names the names of the 70 | # configurations to create. 71 | # 72 | def create_configurations(*configuration_names) 73 | 74 | configuration_names.compact.flatten.map do |config_name| 75 | created_config = create_configuration config_name do |config| 76 | yield config if block_given? 77 | end 78 | 79 | created_config.save! 80 | end 81 | 82 | end 83 | 84 | end 85 | 86 | end -------------------------------------------------------------------------------- /lib/xcode/configurations/array_property.rb: -------------------------------------------------------------------------------- 1 | 2 | module Xcode 3 | module Configuration 4 | 5 | # 6 | # Within the a build settings for a configuration there are a number of 7 | # settings that are stored as Arrays. This helper module is for the most part 8 | # a pass-through method to provide parity with the other methods. 9 | # 10 | module ArrayProperty 11 | extend self 12 | 13 | # 14 | # As arrays are stored as arrays this is not particularly different. 15 | # 16 | # @param [Array] value to be parsed into the correct format 17 | # 18 | def open(value) 19 | Array(value) 20 | end 21 | 22 | # 23 | # @param [Nil,Array,String] value that is being saved back which can 24 | # be in a multitude of formats as long as it responds_to? #to_a 25 | # 26 | def save(value) 27 | Array(value) 28 | end 29 | 30 | def append(original,value) 31 | (open(original) + Array(value)).uniq 32 | end 33 | 34 | end 35 | 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/xcode/configurations/boolean_property.rb: -------------------------------------------------------------------------------- 1 | 2 | module Xcode 3 | module Configuration 4 | 5 | # 6 | # Within the a build settings for a configuration there are a number of 7 | # settings that are stored as Objective-C boolean values. This helper module 8 | # provides the opening and saving of these values. 9 | # 10 | # When opened the value returns is going to be an Array. 11 | # 12 | # @example setting and getting a property 13 | # 14 | # debug_config project.target('SomeTarget').config('Debug') 15 | # debug_config.always_search_user_paths # => false 16 | # debug_config.always_search_user_paths = true 17 | # debug_config.always_search_user_paths # true 18 | # 19 | module BooleanProperty 20 | extend self 21 | 22 | # 23 | # @param [Nil,TrueClass,FalseClass,String] value to convert to boolean 24 | # @return [TrueClass,FalseClass] the boolean value based on the specified 25 | # value. 26 | # 27 | def open(value) 28 | value.to_s =~ /^YES$/ 29 | end 30 | 31 | # 32 | # @param [String,FalseClass,TrueClass] value to convert to the Obj-C boolean 33 | # @return [String] YES or NO 34 | # 35 | def save(value) 36 | value.to_s =~ /^(?:NO|false)$/ ? "NO" : "YES" 37 | end 38 | 39 | # 40 | # @note Appending boolean properties has no real good default operation. What 41 | # happens in this case is that whatever you decide to append will automatically 42 | # override the previously existing settings. 43 | # 44 | # @param [Types] original the original value to be appended 45 | # @param [Types] value Description 46 | # 47 | def append(original,value) 48 | save(value) 49 | end 50 | 51 | end 52 | 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/xcode/configurations/enumeration_property.rb: -------------------------------------------------------------------------------- 1 | 2 | module Xcode 3 | class EnumerationProperty 4 | 5 | attr_reader :enumeration 6 | 7 | def initialize(*args) 8 | @enumeration = args.flatten.compact 9 | end 10 | 11 | def open(value) 12 | warn "Configuration property contains a value '#{value}' not within the enumeration." unless enumeration.include?(value) 13 | value 14 | end 15 | 16 | def save(value) 17 | raise "Configuration property value specified '#{value}' not within the enumeration." unless enumeration.include?(value) 18 | value 19 | end 20 | 21 | def append(original,value) 22 | warn "Overriding configuration property '#{original}' with new value '#{value}'" unless original == value 23 | save(value) 24 | end 25 | 26 | end 27 | end -------------------------------------------------------------------------------- /lib/xcode/configurations/key_value_array_property.rb: -------------------------------------------------------------------------------- 1 | 2 | module Xcode 3 | module Configuration 4 | 5 | # 6 | # Within the a build settings for a configuration there are a number of 7 | # settings that are stored as key-value pairs in Arrays. 8 | # 9 | module KeyValueArrayProperty 10 | extend self 11 | 12 | # 13 | # As arrays are stored as arrays this is not particularly different. 14 | # 15 | # @param [Array] value to be parsed into the correct format 16 | # 17 | def open(value) 18 | Array(value) 19 | end 20 | 21 | # 22 | # @param [Nil,Array,String] value that is being saved back which can 23 | # be in a multitude of formats as long as it responds_to? #to_a 24 | # 25 | def save(value) 26 | Array(value) 27 | end 28 | 29 | # 30 | # To ensure uniqueness, the original value array is added to the new value 31 | # array and then all the key-values pairs are placed in a Hash then mapped 32 | # back out to a key=value pair array. 33 | # 34 | def append(original,value) 35 | all_values = (open(original) + Array(value)).map {|key_value| key_value.split("=") }.flatten 36 | Hash[*all_values].map {|k,v| "#{k}=#{v}" } 37 | end 38 | 39 | end 40 | 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/xcode/configurations/space_delimited_string_property.rb: -------------------------------------------------------------------------------- 1 | 2 | module Xcode 3 | module Configuration 4 | 5 | # 6 | # Within the a build settings for a configuration there are a number of 7 | # settings that are stored a space-delimited strings. This helper module 8 | # provides the opening and saving of these values. 9 | # 10 | # When opened the value returns is going to be an Array. 11 | # 12 | # @example common build settings that are space delimited 13 | # 14 | # Supported Platforms : SUPPORTED_PLATFORMS 15 | # User Header Search Paths : USER_HEADER_SEARCH_PATHS 16 | # Other Test Flags : OTHER_TEST_FLAGS 17 | # 18 | # @example setting and getting supported platforms 19 | # 20 | # debug_config project.target('SomeTarget').config('Debug') 21 | # debug_config.supported_platforms # => [] 22 | # debug_config.supported_platforms = "PLATFORM A" 23 | # debug_config.supported_platforms # => [ "PLATFORM A" ] 24 | # 25 | module SpaceDelimitedString 26 | extend self 27 | 28 | # 29 | # While the space delimited string can and is often stored in that way, 30 | # it appears as though Xcode is now possibly storing these values in a format 31 | # that the parser is returning as an Array. So if the raw value is an 32 | # array, simply return that raw value instead of attempting to convert it. 33 | # 34 | # @param [Nil,String] value stored within the build settings 35 | # @return [Array] a list of the strings that are within this string 36 | # 37 | def open(value) 38 | value.is_a?(Array) ? value : value.to_s.split(" ") 39 | end 40 | 41 | # 42 | # @param [Array,String] value to be converted into the correct format 43 | # @return [String] the space-delimited string 44 | # 45 | def save(value) 46 | Array(value).join(" ") 47 | end 48 | 49 | # 50 | # Space Delimited Strings are not unlike arrays and those we assume that the 51 | # inputs are going to be two arrays that will be joined and then ensured 52 | # that only the unique values are saved. 53 | # 54 | # @param [Nil,String] original the original value stored within the field 55 | # @param [Nil,String,Array] value the new values that will coerced into an array 56 | # and joined with the original values. 57 | # 58 | def append(original,value) 59 | save( ( open(original) + Array(value)).uniq ) 60 | end 61 | 62 | end 63 | 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/xcode/configurations/string_property.rb: -------------------------------------------------------------------------------- 1 | 2 | module Xcode 3 | module Configuration 4 | 5 | # 6 | # Within the a build settings for a configuration there are a number of 7 | # settings that are stored simply as strings. This helper module 8 | # is for the most part a pass-through method to provide parity with the 9 | # other methods. 10 | # 11 | module StringProperty 12 | extend self 13 | 14 | def open(value) 15 | value.to_s 16 | end 17 | 18 | def save(value) 19 | value.to_s 20 | end 21 | 22 | def append(original,value) 23 | open(original) + value.to_s 24 | end 25 | 26 | end 27 | 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/xcode/configurations/targeted_device_family_property.rb: -------------------------------------------------------------------------------- 1 | 2 | module Xcode 3 | module Configuration 4 | 5 | # 6 | # Within the a build settings there is a setting for the Targeted Device 7 | # Family which assigns particular numeric values to the platform types. 8 | # 9 | # Instead of manipulating the numeric values, this will perform a conversion 10 | # return an array of symbols with the platforms like :iphone and :ipad. 11 | # 12 | module TargetedDeviceFamily 13 | extend self 14 | 15 | # 16 | # @param [String] value convert the comma-delimited list of platforms 17 | # @return [Array] the platform names supported. 18 | # 19 | def open(value) 20 | value.to_s.split(",").map do |platform_number| 21 | platforms[platform_number] 22 | end 23 | end 24 | 25 | # 26 | # @param [String,Array] value convert the array of platform names 27 | # @return [String] the comma-delimited list of numeric values representing 28 | # the platforms. 29 | # 30 | def save(value) 31 | Array(value).map do |platform_name| 32 | platforms.map {|number,name| number if name.to_s == platform_name.to_s.downcase } 33 | end.flatten.compact.uniq.join(",") 34 | end 35 | 36 | # 37 | # @param [String] original is the current string value stored in the configuration 38 | # that needs to be converted into an Array of names. 39 | # @param [String,Array] value the new values to include in the device 40 | # family. 41 | # 42 | def append(original,value) 43 | save(open(original) + Array(value)) 44 | end 45 | 46 | private 47 | 48 | def platforms 49 | { "1" => :iphone, "2" => :ipad } 50 | end 51 | 52 | end 53 | 54 | end 55 | end -------------------------------------------------------------------------------- /lib/xcode/container_item_proxy.rb: -------------------------------------------------------------------------------- 1 | 2 | module Xcode 3 | 4 | # 5 | # Within a Target Dependency there is a ContainerItemProxy object which likely 6 | # holds the reference to the project (important if there are multiple projects) 7 | # and the target within that project. 8 | # 9 | # @see TargetDependency#create_dependency_on 10 | # @see Target#add_dependency 11 | # 12 | module ContainerItemProxy 13 | 14 | # 15 | # Generate default properties for a Container Item Proxy 16 | # 17 | # @see TargetDependency 18 | # 19 | # /* Begin PBXContainerItemProxy section */ 20 | # 98A30E0314CDF2D800DF81EF /* PBXContainerItemProxy */ = { 21 | # isa = PBXContainerItemProxy; 22 | # containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; 23 | # proxyType = 1; 24 | # remoteGlobalIDString = 98A1E39414CDED2C00D4AB9D; /* Target in the Project */ 25 | # remoteInfo = "Facebook Framework"; 26 | # }; 27 | # /* End PBXContainerItemProxy section */ 28 | # 29 | def self.default(project_identifier,target_identifier,target_name) 30 | { 'isa' => 'PBXContainerItemProxy', 31 | 'containerPortal' => project_identifier, 32 | 'proxyType' => 1, 33 | 'remoteGlobalIDString' => target_identifier, 34 | # @todo It is unclear if the remoteInfo name is necessary and it is currently 35 | # unclear to me how this value is set. At the moment it simply set with 36 | # the target name supplied. 37 | 'remoteInfo' => target_name } 38 | end 39 | 40 | end 41 | end -------------------------------------------------------------------------------- /lib/xcode/core_ext/array.rb: -------------------------------------------------------------------------------- 1 | class Array 2 | 3 | # 4 | # Arrays in an Xcode project take a particular format. 5 | # 6 | # @note the last element in the array can have a comma; it is optional. 7 | # 8 | # @example output format: 9 | # 10 | # ( 11 | # ITEM1, 12 | # ITEM2, 13 | # ITEM3 14 | # ) 15 | # 16 | def to_xcplist 17 | plist_of_items = map {|item| item.to_xcplist }.join(",\n") 18 | 19 | %{( 20 | #{plist_of_items} 21 | )} 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/xcode/core_ext/boolean.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class TrueClass 4 | 5 | # 6 | # Xcode project's expect boolean trues to be the word YES. 7 | # 8 | def to_xcplist 9 | "YES" 10 | end 11 | end 12 | 13 | class FalseClass 14 | 15 | # 16 | # Xcode project's expect boolean trues to be the word NO. 17 | # 18 | def to_xcplist 19 | "NO" 20 | end 21 | end -------------------------------------------------------------------------------- /lib/xcode/core_ext/fixnum.rb: -------------------------------------------------------------------------------- 1 | class Fixnum 2 | def to_xcplist 3 | to_s 4 | end 5 | end -------------------------------------------------------------------------------- /lib/xcode/core_ext/hash.rb: -------------------------------------------------------------------------------- 1 | class Hash 2 | 3 | # 4 | # Hashes in an Xcode project take a particular format. 5 | # 6 | # @note the keys are represeted in this output with quotes; this is actually 7 | # optional for certain keys based on their format. This is done to ensure 8 | # that all keys that do not conform are ensured proper output. 9 | # 10 | # @example output format: 11 | # 12 | # { 13 | # "KEY" = "VALUE"; 14 | # "KEY" = "VALUE"; 15 | # "KEY" = "VALUE"; 16 | # } 17 | # 18 | def to_xcplist 19 | plist_of_items = map do |k,v| 20 | "#{k.to_xcplist} = #{v.to_xcplist};" 21 | end.join("\n") 22 | 23 | %{{ 24 | #{plist_of_items} 25 | }} 26 | end 27 | end -------------------------------------------------------------------------------- /lib/xcode/core_ext/string.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | class String 4 | 5 | # 6 | # Xcode format for a string is exactly the same as you would expect in JSON 7 | # 8 | def to_xcplist 9 | to_json 10 | end 11 | 12 | # 13 | # Similar to ActiveRecord's underscore method. Return a string version 14 | # underscored. This is used specifically to convert the property keys into 15 | # Ruby friendly names as they are used for creating method names. 16 | # 17 | # @return [String] convert camel-cased words, generating underscored, ruby 18 | # friend names. 19 | def underscore 20 | self.gsub(/::/, '/'). 21 | gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). 22 | gsub(/([a-z\d])([A-Z])/,'\1_\2'). 23 | tr("-", "_"). 24 | downcase 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/xcode/deploy/ftp.rb: -------------------------------------------------------------------------------- 1 | require 'net/ftp' 2 | require 'xcode/deploy/web_assets' 3 | 4 | module Xcode 5 | module Deploy 6 | class Ftp 7 | attr_accessor :host, :username, :password, :dir 8 | 9 | def initialize(builder, options = {}) 10 | @builder = builder 11 | @username = options[:username] 12 | @password = options[:password] 13 | @dir = options[:dir] 14 | @host = options[:host] 15 | @base_url = options[:base_url] 16 | end 17 | 18 | # Support templating of member data. 19 | def get_binding 20 | binding 21 | end 22 | 23 | def remote_installation_path 24 | File.join(@dir, @builder.product_name) 25 | end 26 | 27 | def deploy 28 | WebAssets.generate @builder, @base_url do |dir| 29 | puts "Connecting to #{@remote_host} with username #{@username}" 30 | Net::FTP.open(@host, @username, @password) do |ftp| 31 | begin 32 | puts "Creating folder #{remote_installation_path}" 33 | ftp.mkdir(remote_installation_path) 34 | rescue Net::FTPError 35 | puts "It looks like the folder is already there." 36 | end 37 | 38 | puts "Changing to remote folder #{remote_installation_path}" 39 | files = ftp.chdir(remote_installation_path) 40 | 41 | Dir["#{dir}/*"].each do |f| 42 | filename = File.basename(f) 43 | puts "Uploading #{filename}" 44 | ftp.putbinaryfile(f, filename, 1024) 45 | end 46 | 47 | filename = File.basename("#{@builder.ipa_path}") 48 | puts "Uploading #{filename}" 49 | ftp.putbinaryfile("#{@builder.ipa_path}", filename, 1024) 50 | end 51 | end 52 | end 53 | 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/xcode/deploy/kickfolio.rb: -------------------------------------------------------------------------------- 1 | module Xcode 2 | module Deploy 3 | class Kickfolio 4 | 5 | def initialize(bundler, options={}) 6 | @bundler = bundler 7 | @options = options 8 | end 9 | 10 | def deploy 11 | RestClient.post "https://kickfolio.com/api/apps/#{@options[:app_id]}", 12 | {:bundle_url => @options[:url], :auth_token => @options[:api_key]}, 13 | :content_type => 'application/json' 14 | end 15 | 16 | end 17 | end 18 | end -------------------------------------------------------------------------------- /lib/xcode/deploy/s3.rb: -------------------------------------------------------------------------------- 1 | require 'xcode/deploy/web_assets' 2 | require 'aws-sdk' 3 | 4 | module Xcode 5 | module Deploy 6 | class S3 7 | def initialize(builder, options) 8 | @builder = builder 9 | @options = options 10 | end 11 | 12 | def bucket 13 | return @bucket unless @bucket.nil? 14 | s3 = AWS::S3.new @options 15 | @bucket = s3.buckets.create(@options[:bucket]) rescue s3.buckets[@options[:bucket]] 16 | @bucket 17 | end 18 | 19 | def upload(path) 20 | obj_path = File.join(@options[:dir]||'', File.basename(path)) 21 | puts "Uploading #{path} => #{bucket.name}/#{obj_path}" 22 | bucket.objects[obj_path]. 23 | write(File.open(path), :acl => :public_read) 24 | end 25 | 26 | def deploy 27 | remote_ipa = upload @builder.ipa_path 28 | base_url = remote_ipa.public_url(:secure => true).to_s.split("/")[0..-2].join("/") 29 | 30 | WebAssets.generate @builder, base_url do |dir| 31 | Dir["#{dir}/*"].each do |path| 32 | upload path 33 | end 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/xcode/deploy/ssh.rb: -------------------------------------------------------------------------------- 1 | require 'net/ssh' 2 | require 'net/scp' 3 | require 'xcode/deploy/web_assets' 4 | 5 | module Xcode 6 | module Deploy 7 | class Ssh 8 | attr_accessor :host, :username, :password, :dir 9 | 10 | def initialize(builder, options) 11 | @builder = builder 12 | @username = options[:username] 13 | @password = options[:password] 14 | @dir = options[:dir] 15 | @host = options[:host] 16 | @base_url = options[:base_url] 17 | end 18 | 19 | def remote_installation_path 20 | File.join(@dir, @builder.product_name) 21 | end 22 | 23 | def deploy 24 | WebAssets.generate @builder, @base_url do |dist_path| 25 | puts "Copying files to #{@remote_host}:#{remote_installation_path}" 26 | Net::SSH.start(@host, @username, :password => @password) do |ssh| 27 | puts "Creating folder with mkdir #{remote_installation_path}" 28 | ssh.exec!("mkdir #{remote_installation_path}") 29 | end 30 | Net::SCP.start(@host, @username, :password => @password) do |scp| 31 | puts "Copying files from folder #{dist_path}" 32 | Dir["#{dist_path}/*"].each do |f| 33 | puts "Copying #{f} to remote host in folder #{remote_installation_path}" 34 | scp.upload! "#{f}", "#{remote_installation_path}" 35 | end 36 | scp.upload! "#{@options[:ipa_path]}", "#{remote_installation_path}" 37 | end 38 | end 39 | end 40 | 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/xcode/deploy/templates/index.rhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Beta Download 7 | 15 | 16 | 17 |
18 | 19 |

Link didn't work?
20 | Make sure you're visiting this page on your device, not your computer.

21 | 22 | 23 | -------------------------------------------------------------------------------- /lib/xcode/deploy/templates/manifest.rhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | assets 9 | 10 | 11 | kind 12 | software-package 13 | url 14 | <%= deployment_url %> 15 | 16 | 17 | metadata 18 | 19 | bundle-identifier 20 | <%= bundle_identifier %> 21 | bundle-version 22 | <%= bundle_version %> 23 | kind 24 | software 25 | title 26 | <%= product_name %> 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /lib/xcode/deploy/testflight.rb: -------------------------------------------------------------------------------- 1 | require 'rest-client' 2 | 3 | module Xcode 4 | module Deploy 5 | class Testflight 6 | attr_accessor :api_token, :team_token, :notify, :proxy, :notes, :lists, :builder 7 | @@defaults = {} 8 | 9 | def self.defaults(defaults={}) 10 | @@defaults = defaults 11 | end 12 | 13 | def initialize(builder, options={}) 14 | @builder = builder 15 | @api_token = options[:api_token]||@@defaults[:api_token] 16 | @team_token = options[:team_token]||@@defaults[:team_token] 17 | @notify = options.has_key?(:notify) ? options[:notify] : true 18 | @notes = options[:notes] 19 | @lists = options[:lists]||[] 20 | @proxy = ENV['http_proxy'] || ENV['HTTP_PROXY'] 21 | end 22 | 23 | def deploy 24 | puts "Uploading to Testflight..." 25 | 26 | # RestClient.proxy = @proxy || ENV['http_proxy'] || ENV['HTTP_PROXY'] 27 | # RestClient.log = '/tmp/restclient.log' 28 | # 29 | # response = RestClient.post('http://testflightapp.com/api/builds.json', 30 | # :file => File.new(builder.ipa_path), 31 | # :dsym => File.new(builder.dsym_zip_path), 32 | # :api_token => @api_token, 33 | # :team_token => @team_token, 34 | # :notes => @notes, 35 | # :notify => @notify ? 'True' : 'False', 36 | # :distribution_lists => @lists.join(',') 37 | # ) 38 | # 39 | # json = JSON.parse(response) 40 | # puts " + Done, got: #{json.inspect}" 41 | # json 42 | 43 | cmd = Xcode::Shell::Command.new 'curl' 44 | cmd << "--proxy #{@proxy}" unless @proxy.nil? or @proxy=='' 45 | cmd << "-X POST http://testflightapp.com/api/builds.json" 46 | cmd << "-F file=@\"#{@builder.ipa_path}\"" 47 | cmd << "-F dsym=@\"#{@builder.dsym_zip_path}\"" unless @builder.dsym_zip_path.nil? 48 | cmd << "-F api_token='#{@api_token}'" 49 | cmd << "-F team_token='#{@team_token}'" 50 | cmd << "-F notes=\"#{@notes}\"" unless @notes.nil? 51 | cmd << "-F notify=#{@notify ? 'True' : 'False'}" 52 | cmd << "-F distribution_lists='#{@lists.join(',')}'" unless @lists.count==0 53 | 54 | response = cmd.execute 55 | 56 | json = MultiJson.load(response.join('')) 57 | puts " + Done, got: #{json.inspect}" 58 | 59 | yield(json) if block_given? 60 | 61 | json 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/xcode/deploy/web_assets.rb: -------------------------------------------------------------------------------- 1 | require 'erb' 2 | require 'ostruct' 3 | require 'tmpdir' 4 | 5 | module Xcode 6 | module Deploy 7 | module WebAssets 8 | class BindingContext < OpenStruct 9 | def get_binding 10 | return binding() 11 | end 12 | end 13 | 14 | def self.generate(builder, base_url, &block) 15 | Dir.mktmpdir do |dist_path| 16 | 17 | context = BindingContext.new 18 | context.product_name = builder.product_name 19 | context.manifest_url = "#{base_url}/manifest.plist" 20 | context.deployment_url = "#{base_url}/#{builder.ipa_name}" 21 | context.bundle_version = builder.bundle_version 22 | context.bundle_identifier = builder.bundle_identifier 23 | 24 | rhtml = ERB.new(File.read("#{File.dirname(__FILE__)}/templates/manifest.rhtml")) 25 | File.open("#{dist_path}/manifest.plist", "w") do |io| 26 | io.write(rhtml.result(context.get_binding)) 27 | end 28 | 29 | rhtml = ERB.new(File.read("#{File.dirname(__FILE__)}/templates/index.rhtml")) 30 | File.open("#{dist_path}/index.html", "w") do |io| 31 | io.write(rhtml.result(context.get_binding)) 32 | end 33 | 34 | yield dist_path 35 | end 36 | end 37 | end 38 | end 39 | end -------------------------------------------------------------------------------- /lib/xcode/info_plist.rb: -------------------------------------------------------------------------------- 1 | require 'plist' 2 | require 'pp' 3 | 4 | module Xcode 5 | 6 | # 7 | # @see https://developer.apple.com/library/ios/#documentation/general/Reference/InfoPlistKeyReference/Articles/AboutInformationPropertyListFiles.html 8 | # 9 | class InfoPlist 10 | def initialize(config, plist_location) 11 | @config = config 12 | 13 | @plist_location = File.expand_path("#{File.dirname(@config.target.project.path)}/#{plist_location}") 14 | unless File.exists?(@plist_location) 15 | puts 'Plist not found ' + @plist_location 16 | exit 1 17 | end 18 | @plist = Plist::parse_xml(@plist_location) 19 | end 20 | 21 | def marketing_version 22 | @plist['CFBundleShortVersionString'] 23 | end 24 | 25 | def marketing_version=(version) 26 | @plist['CFBundleShortVersionString'] = version 27 | end 28 | 29 | def version 30 | @plist['CFBundleVersion'] 31 | end 32 | 33 | def version=(version) 34 | @plist['CFBundleVersion'] = version.to_s 35 | end 36 | 37 | def identifier 38 | @plist['CFBundleIdentifier'] 39 | end 40 | 41 | def identifier=(identifier) 42 | @plist['CFBundleIdentifier'] = identifier 43 | end 44 | 45 | def display_name 46 | @plist['CFBundleDisplayName'] 47 | end 48 | 49 | def display_name=(name) 50 | @plist['CFBundleDisplayName'] = name 51 | end 52 | 53 | def save 54 | File.open(@plist_location, 'w') {|f| f << @plist.to_plist} 55 | end 56 | end 57 | end -------------------------------------------------------------------------------- /lib/xcode/parsers/plutil_project_parser.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'plist' 3 | 4 | module Xcode 5 | 6 | module PLUTILProjectParser 7 | extend self 8 | 9 | # 10 | # Using the sytem tool plutil, the specified project file is parsed and 11 | # converted to XML, and then converted into a ruby hash object. 12 | # 13 | def parse(path) 14 | registry = Plist.parse_xml open_project_file(path) 15 | 16 | raise "Failed to correctly parse the project file #{path}" unless registry 17 | 18 | registry 19 | end 20 | 21 | # 22 | # Save the outputed string data to the specified project file path and then 23 | # formats that data to be prettier than the default output. 24 | # 25 | def save(path,data) 26 | 27 | File.open(path,'w') do |file| 28 | file.puts data 29 | end 30 | 31 | if File.exists?(path) 32 | `pl -input #{path} -output #{path}` 33 | end 34 | 35 | end 36 | 37 | private 38 | 39 | # 40 | # @return [String] an XML version of the project file or the error message 41 | # that the file could not be found. 42 | # 43 | # @example Error Message 44 | # 45 | # Project.xcodeproj/project.pbproj file does not exist or is not 46 | # readable or is not a regular file. 47 | # 48 | def open_project_file(path) 49 | `plutil -convert xml1 -o - "#{path}"` 50 | end 51 | 52 | end 53 | 54 | end 55 | -------------------------------------------------------------------------------- /lib/xcode/platform.rb: -------------------------------------------------------------------------------- 1 | module Xcode 2 | 3 | module Platforms 4 | @@platforms = [] 5 | 6 | def self.[] sdk_name 7 | supported.find {|p| p.sdk==sdk_name} 8 | end 9 | 10 | def self.find platform, version = nil 11 | platform = supported.sort do 12 | |a,b| a.version.to_f <=> b.version.to_f 13 | end.find do |p| 14 | p.platform==platform and (version.nil? or p.version==version) 15 | end 16 | 17 | raise "Unable to find a platform #{platform},#{version} - available platforms are #{supported.map{|p| p.sdk}.join(', ')}" if platform.nil? 18 | 19 | platform 20 | end 21 | 22 | def self.supported 23 | return @@platforms unless @@platforms.count==0 24 | 25 | parsing = false 26 | `xcodebuild -showsdks`.split("\n").each do |l| 27 | l.strip! 28 | if l=~/(.*)\s+SDKs:/ 29 | parsing = true 30 | elsif l=~/^\s*$/ 31 | parsing = false 32 | elsif parsing 33 | l=~/([^\t]+)\t+\-sdk (.*)/ 34 | name = $1.strip 35 | $2.strip=~/([a-zA-Z]+)(\d+\.\d+)/ 36 | 37 | platform = Platform.new name, $1, $2 38 | @@platforms << platform 39 | end 40 | end 41 | 42 | @@platforms 43 | end 44 | 45 | end 46 | 47 | class Platform 48 | attr_reader :platform, :name, :version 49 | 50 | def initialize name, platform, version 51 | @platform = platform 52 | @name = name 53 | @version = version 54 | end 55 | 56 | def sdk 57 | "#{@platform}#{@version}" 58 | end 59 | 60 | def to_s 61 | "#{name} #{sdk}" 62 | end 63 | end 64 | 65 | end -------------------------------------------------------------------------------- /lib/xcode/project_reference.rb: -------------------------------------------------------------------------------- 1 | module Xcode 2 | 3 | module ProjectReference 4 | 5 | # 6 | # Returns the group specified. If any part of the group does not exist along 7 | # the path the group is created. Also paths can be specified to make the 8 | # traversing of the groups easier. 9 | # 10 | # @note this will attempt to find the paths specified, if it fails to find them 11 | # it will create one and then continue traversing. 12 | # 13 | # @example Traverse a path through the various sub-groups. 14 | # 15 | # project.group('Vendor/MyCode/Support Files') 16 | # # is equivalent to ... 17 | # project.group('Vendor').first.group('MyCode').first.group('Supporting Files') 18 | # 19 | # @note this path functionality current is only exercised from the project level 20 | # all groups will treat the path division `/` as simply a character. 21 | # 22 | # @param [String] name the group name to find/create 23 | # 24 | # @return [Group] the group with the specified name. 25 | # 26 | def group(name,options = {},&block) 27 | # By default create missing groups along the way 28 | options = { :create => true }.merge(options) 29 | 30 | current_group = main_group 31 | 32 | # @todo consider this traversing and find/create as a normal procedure when 33 | # traversing the project. 34 | 35 | name.split("/").each do |path_component| 36 | found_group = current_group.group(path_component).first 37 | 38 | if options[:create] and found_group.nil? 39 | found_group = current_group.create_group(path_component) 40 | end 41 | 42 | current_group = found_group 43 | 44 | break unless current_group 45 | end 46 | 47 | current_group.instance_eval(&block) if block_given? and current_group 48 | 49 | current_group 50 | end 51 | 52 | 53 | 54 | end 55 | end -------------------------------------------------------------------------------- /lib/xcode/provisioning_profile.rb: -------------------------------------------------------------------------------- 1 | module Xcode 2 | class ProvisioningProfile 3 | include Xcode::TerminalOutput 4 | attr_reader :path, :name, :uuid, :identifiers, :devices, :appstore 5 | def initialize(path) 6 | 7 | raise "Provisioning profile '#{path}' does not exist" unless File.exists? path 8 | 9 | @path = path 10 | @identifiers = [] 11 | @devices = [] 12 | @appstore = true 13 | @enterprise = false 14 | 15 | # TODO: im sure this could be done in a nicer way. maybe read out the XML-like stuff and use the plist -> json converter 16 | uuid = nil 17 | File.open(path, "rb") do |f| 18 | input = f.read 19 | 20 | if input=~/ProvisionedDevices/ 21 | @appstore = false 22 | end 23 | 24 | if input=~/ProvisionsAllDevices<\/key>/ 25 | @enterprise = true 26 | end 27 | 28 | if input=~/ProvisionedDevices<\/key>.*?(.*?)<\/array>/im 29 | $1.split(//).each do |id| 30 | next if id.nil? or id.strip=="" 31 | @devices << id.gsub(/<\/string>/,'').strip 32 | end 33 | end 34 | 35 | input=~/UUID<\/key>.*?(.*?)<\/string>/im 36 | @uuid = $1.strip 37 | 38 | input=~/Name<\/key>.*?(.*?)<\/string>/im 39 | @name = $1.strip 40 | 41 | input=~/ApplicationIdentifierPrefix<\/key>.*?(.*?)<\/array>/im 42 | $1.split(//).each do |id| 43 | next if id.nil? or id.strip=="" 44 | @identifiers << id.gsub(/<\/string>/,'').strip 45 | end 46 | end 47 | 48 | end 49 | 50 | def appstore? 51 | @appstore 52 | end 53 | 54 | def enterprise? 55 | @enterprise 56 | end 57 | 58 | def self.profiles_path 59 | File.expand_path "~/Library/MobileDevice/Provisioning\\ Profiles/" 60 | end 61 | 62 | def install_path 63 | "#{ProvisioningProfile.profiles_path}/#{self.uuid}.mobileprovision" 64 | end 65 | 66 | def install 67 | # Do not reinstall if profile is same and is already installed 68 | return if (self.path == self.install_path.gsub(/\\ /, ' ')) 69 | 70 | ProvisioningProfile.installed_profiles.each do |installed| 71 | if installed.identifiers==self.identifiers and installed.uuid==self.uuid 72 | installed.uninstall 73 | end 74 | end 75 | 76 | # print_task "profile", "installing #{self.path} with uuid #{self.uuid}", :info 77 | cmd = Xcode::Shell::Command.new 'cp' 78 | cmd << "\"" +self.path + "\"" 79 | cmd << self.install_path 80 | cmd.execute 81 | end 82 | 83 | def uninstall 84 | # print_task "profile", "removing #{self.install_path}", :info 85 | cmd = Xcode::Shell::Command.new 'rm' 86 | cmd << "-f #{self.install_path}" 87 | cmd.execute 88 | end 89 | 90 | def self.installed_profiles 91 | Dir["#{self.profiles_path}/*.mobileprovision"].map do |file| 92 | ProvisioningProfile.new(file) 93 | end 94 | end 95 | 96 | def self.find_installed_by_uuid uuid 97 | ProvisioningProfile.installed_profiles.each do |p| 98 | return p if p.uuid == uuid 99 | end 100 | end 101 | 102 | def self.installed_profile(name) 103 | self.installed_profiles.select {|p| p.name == name.to_s}.first; 104 | end 105 | 106 | end 107 | end -------------------------------------------------------------------------------- /lib/xcode/scheme.rb: -------------------------------------------------------------------------------- 1 | require 'nokogiri' 2 | 3 | module Xcode 4 | 5 | class SchemeAction 6 | attr_accessor :config, :name 7 | end 8 | 9 | # Schemes are an XML file that describe build, test, launch and profile actions 10 | # For the purposes of Xcoder, we want to be able to build and test 11 | class Scheme 12 | attr_reader :parent, :path, :name, :build_targets, :test_targets 13 | attr_accessor :build_config, :archive_config, :test_config 14 | 15 | # 16 | # Parse all the schemes given the current project. 17 | # 18 | def self.find_in_project(project) 19 | find_in_path(project, project.path) 20 | end 21 | 22 | # 23 | # Parse all the schemes given the current workspace. 24 | # 25 | def self.find_in_workspace(workspace) 26 | schemes = find_in_path(workspace, workspace.path) 27 | 28 | # Project level schemes 29 | workspace.projects.each do |project| 30 | schemes+=find_in_path(workspace, project.path) 31 | end 32 | 33 | schemes 34 | end 35 | 36 | # Parse all the scheme files that can be found at the given path. Schemes 37 | # can be defined as `shared` schemes and then `user` specific schemes. Parsing 38 | # the schemes will load the shared ones and then the current acting user's 39 | # schemes. 40 | # 41 | # 42 | # @param project or workspace in which the scheme is contained 43 | # @return [Array] the shared schemes and user specific schemes found 44 | # within the project/workspace at the path defined for schemes. 45 | # 46 | def self.find_in_path(parent, path) 47 | all_schemes_paths(path).map do |scheme_path| 48 | Xcode::Scheme.new(parent: parent, root: path, path: scheme_path) 49 | end 50 | end 51 | 52 | def initialize(params={}) 53 | @parent = params[:parent] 54 | @path = File.expand_path params[:path] 55 | @root = File.expand_path(File.join(params[:root],'..')) 56 | @name = File.basename(path).gsub(/\.xcscheme$/,'') 57 | doc = Nokogiri::XML(open(@path)) 58 | 59 | parse_build_actions(doc) 60 | end 61 | 62 | # 63 | # @return a builder for building this scheme 64 | # 65 | def builder 66 | Xcode::Builder::SchemeBuilder.new(self) 67 | end 68 | 69 | def to_s 70 | "#{name} (Scheme) in #{parent}" 71 | end 72 | 73 | # 74 | # @return true if the scheme is testable, false otherwise 75 | # 76 | def testable? 77 | !@test_config.nil? 78 | end 79 | 80 | private 81 | 82 | # 83 | # @return an array of all the scheme filepaths found within the project 84 | # or workspace path provided. 85 | # 86 | def self.all_schemes_paths(path) 87 | shared_schemes_paths(path) + current_user_schemes_paths(path) 88 | end 89 | 90 | # 91 | # @return an array of all the shared scheme filespaths found within the 92 | # project or workspace path provided. 93 | # 94 | def self.shared_schemes_paths(root) 95 | Dir["#{root}/xcshareddata/xcschemes/*.xcscheme"] 96 | end 97 | 98 | # 99 | # @return an array of all the current user's scheme filespaths found within the 100 | # project or workspace path provided. 101 | # 102 | def self.current_user_schemes_paths(root) 103 | Dir["#{root}/xcuserdata/#{ENV['USER']}.xcuserdatad/xcschemes/*.xcscheme"] 104 | end 105 | 106 | def target_from_build_reference(buildableReference) 107 | project_name = buildableReference['ReferencedContainer'].gsub(/^container:/,'') 108 | target_name = buildableReference['BlueprintName'] 109 | project_path = File.join @root, project_name 110 | project = Xcode.project project_path 111 | project.target(target_name) 112 | end 113 | 114 | def parse_build_actions(doc) 115 | # Build Config 116 | @build_targets = [] 117 | @test_targets = [] 118 | 119 | @build_config = doc.xpath("//LaunchAction").first['buildConfiguration'] 120 | @archive_config = doc.xpath("//ArchiveAction").first['buildConfiguration'] 121 | 122 | if doc.xpath("//TestAction/Testables/TestableReference/BuildableReference").children.count>0 123 | @test_config = doc.xpath("//TestAction").first['buildConfiguration'] 124 | doc.xpath("//TestAction/Testables/TestableReference/BuildableReference").each do |ref| 125 | @test_targets << target_from_build_reference(ref) 126 | end 127 | end 128 | 129 | build_action_entries = doc.xpath("//BuildAction//BuildableReference").each do |ref| 130 | @build_targets << target_from_build_reference(ref) 131 | end 132 | end 133 | 134 | end 135 | end 136 | -------------------------------------------------------------------------------- /lib/xcode/shell/command.rb: -------------------------------------------------------------------------------- 1 | require 'set' 2 | require 'tmpdir' 3 | require 'tempfile' 4 | require 'pty' 5 | 6 | module Xcode 7 | module Shell 8 | 9 | class ExecutionError < StandardError; 10 | attr_accessor :output 11 | def initialize(message, output=nil) 12 | super message 13 | @output = output 14 | end 15 | end 16 | 17 | class Command 18 | include Xcode::TerminalOutput 19 | attr_accessor :env, :cmd, :args, :show_output, :output_dir, :log_to_file, :output 20 | 21 | def initialize(cmd, environment={}) 22 | @cmd = cmd.to_s 23 | @args = [] 24 | @env = environment 25 | @show_output = true 26 | @pipe = nil 27 | @output = [] 28 | @output_dir = Dir.tmpdir 29 | @log_to_file = false 30 | end 31 | 32 | def <<(arg) 33 | @args << arg 34 | end 35 | 36 | def to_s 37 | "#{to_a.join(' ')}" 38 | end 39 | 40 | def to_a 41 | out = [] 42 | out << @cmd 43 | out+=@args 44 | out+=(@env.map {|k,v| "#{k}=#{v}"}) 45 | out 46 | end 47 | 48 | def ==(obj) 49 | return false unless obj.is_a? Xcode::Shell::Command 50 | # to_s==obj.to_s 51 | Set.new(obj.to_a) == Set.new(self.to_a) 52 | end 53 | 54 | # 55 | # Attach an output pipe. 56 | # 57 | # This can be any object which responds to puts and close 58 | def attach(pipe) 59 | @pipe = pipe 60 | @show_output = false 61 | end 62 | 63 | # 64 | # Execute the given command 65 | # 66 | def execute(&block) #:yield: output 67 | print_output self.to_s, :debug 68 | # print_task 'shell', self.to_s, :debug if show_output 69 | begin 70 | output_file_name = File.join(@output_dir, "xcoder-#{@cmd}-#{Time.now.strftime('%Y%m%d-%H%M%S')}") 71 | 72 | File.open(output_file_name, "w") do |file| 73 | PTY.spawn(to_s) do |r, w, child_pid| 74 | r.sync 75 | r.each_line do |line| 76 | file << line 77 | 78 | print_input line.gsub(/\n$/,''), :debug if @show_output 79 | 80 | if @pipe.nil? 81 | # DEPRECATED 82 | yield(line) if block_given? 83 | else 84 | @pipe << line 85 | end 86 | 87 | @output << line 88 | end 89 | Process.wait(child_pid) 90 | end 91 | end 92 | 93 | raise ExecutionError.new("Error (#{$?.exitstatus}) executing '#{to_s}'", @output) if $?.exitstatus>0 94 | 95 | if @log_to_file 96 | print_system "Captured output to #{output_file_name}", :notice 97 | else 98 | File.delete(output_file_name) 99 | end 100 | 101 | @output 102 | rescue Xcode::Shell::ExecutionError => e 103 | print_system "Captured output to #{output_file_name}", :notice 104 | print_system "Cropped #{e.output.count - 10} lines", :notice if @output.count>10 105 | @output.last(10).each do |line| 106 | print_output line.strip, :error 107 | end 108 | raise e 109 | ensure 110 | @pipe.close unless @pipe.nil? 111 | end 112 | end 113 | 114 | end 115 | end 116 | end -------------------------------------------------------------------------------- /lib/xcode/simple_identifier_generator.rb: -------------------------------------------------------------------------------- 1 | module SimpleIdentifierGenerator 2 | extend self 3 | 4 | MAX_IDENTIFIER_GENERATION_ATTEMPTS = 10 5 | 6 | # 7 | # Generate an identifier string 8 | # 9 | # @example generating a new identifier 10 | # 11 | # SimpleIdentifierGenerator.generate # => "E21EB9EE14E359840058122A" 12 | # 13 | # @example generating a new idenitier ensuring it does not match existing keys 14 | # 15 | # SimpleIdentifierGenerator.generate :existing_keys => [ "E21EB9EE14E359840058122A" ] # => "2574D65B0D2FFDB5D0372B4A" 16 | # 17 | # @param [Hash] options contains any additional parameters; namely the 18 | # `:existing_keys` parameters which will help ensure uniqueness. 19 | # 20 | # @return [String] a 24-length string that contains only hexadecimal characters. 21 | # 22 | def generate(options = {}) 23 | 24 | existing_keys = options[:existing_keys] || [] 25 | 26 | # Ensure that the identifier generated is unique 27 | identifier_generation_count = 0 28 | 29 | new_identifier = generate_new_key 30 | 31 | while existing_keys.include?(new_identifier) 32 | 33 | new_identifier = generate_new_key 34 | 35 | # Increment our identifier generation count and if we reach our max raise 36 | # an exception as something has gone horribly wrong. 37 | 38 | identifier_generation_count += 1 39 | if identifier_generation_count > MAX_IDENTIFIER_GENERATION_ATTEMPTS 40 | raise "SimpleIdentifierGenerator is unable to generate a unique identifier" 41 | end 42 | end 43 | 44 | new_identifier 45 | 46 | end 47 | 48 | private 49 | 50 | # 51 | # Generates a new identifier string 52 | # 53 | # @example identifier string 54 | # 55 | # "E21EB9EE14E359840058122A" 56 | # 57 | # @return [String] a new identifier string 58 | # 59 | def generate_new_key 60 | range = ('A'..'F').to_a + (0..9).to_a 61 | 24.times.inject("") {|ident| "#{ident}#{range.sample}" } 62 | end 63 | 64 | end -------------------------------------------------------------------------------- /lib/xcode/target_dependency.rb: -------------------------------------------------------------------------------- 1 | require 'xcode/container_item_proxy' 2 | 3 | module Xcode 4 | 5 | # 6 | # Targets, usually Aggregate targets, have dependencies on other targets. This 7 | # object manages that relationship. While the class-level default properties 8 | # method will return the properties necessary for a target it does not configure 9 | # it correctly until the method #create_dependency_on is called with the particular 10 | # target. 11 | # 12 | # @see Target#add_dependency 13 | # 14 | module TargetDependency 15 | 16 | # 17 | # Generate default properties for a Target Dependency 18 | # 19 | # /* Begin PBXTargetDependency section */ 20 | # 98A30E0414CDF2D800DF81EF /* PBXTargetDependency */ = { 21 | # isa = PBXTargetDependency; 22 | # target = 98A1E39414CDED2C00D4AB9D /* Facebook */; 23 | # targetProxy = 98A30E0314CDF2D800DF81EF /* PBXContainerItemProxy */; 24 | # }; 25 | # /* End PBXTargetDependency section */ 26 | # 27 | # @return [Hash] the properties default to a target dependency; however they 28 | # are all nil and this properties list is incomplete until the property 29 | # values are setup through #create_dependency_on 30 | # 31 | def self.default 32 | { 'isa' => 'PBXTargetDependency', 33 | 'target' => nil, 34 | 'targetProxy' => nil } 35 | end 36 | 37 | 38 | # 39 | # Establish the Target that this Target Dependency is dependent. 40 | # 41 | # @param [Target] target the target this dependency is based on 42 | # 43 | def create_dependency_on(target) 44 | 45 | @properties['target'] = target.identifier 46 | 47 | container_item_proxy = ContainerItemProxy.default target.project.project.identifier, target.identifier, target.name 48 | container_item_proxy = @registry.add_object(container_item_proxy) 49 | 50 | @properties['targetProxy'] = container_item_proxy.identifier 51 | 52 | save! 53 | end 54 | 55 | end 56 | end -------------------------------------------------------------------------------- /lib/xcode/terminal_output.rb: -------------------------------------------------------------------------------- 1 | require 'colorize' 2 | 3 | module Xcode 4 | module TerminalOutput 5 | @@colour_enabled = true 6 | @@log_level = :info 7 | 8 | LEVELS = [ 9 | :error, 10 | :warning, 11 | :notice, 12 | :info, 13 | :debug 14 | ] 15 | 16 | def log_level 17 | @@log_level 18 | end 19 | 20 | def self.log_level=(level) 21 | raise "Unknown log level #{level}, should be one of #{LEVELS.join(', ')}" unless LEVELS.include? level 22 | @@log_level = level 23 | end 24 | 25 | def self.included(base) 26 | @@colour_supported = terminal_supports_colors? 27 | end 28 | 29 | def color_output= color_output 30 | @@colour_enabled = color_output 31 | end 32 | 33 | def color_output? 34 | @@colour_supported and @@colour_enabled 35 | end 36 | 37 | # 38 | # Print an IO input interaction 39 | # 40 | def print_input message, level=:debug 41 | return if LEVELS.index(level) > LEVELS.index(@@log_level) 42 | puts format_lhs("", "", "<") + message, :default 43 | end 44 | 45 | # 46 | # Print an IO output interaction 47 | def print_output message, level=:debug 48 | return if LEVELS.index(level) > LEVELS.index(@@log_level) 49 | puts format_lhs("", "", ">") + message, :default 50 | end 51 | 52 | def print_system message, level=:debug 53 | return if LEVELS.index(level) > LEVELS.index(@@log_level) 54 | puts format_lhs("", "", "!") + message, :green 55 | end 56 | 57 | def format_lhs(left, right, terminator=":") 58 | # "#{left.to_s.ljust(10)} #{right.rjust(6)}#{terminator} " 59 | "#{right.to_s.rjust(7)}#{terminator} " 60 | end 61 | 62 | def print_task(task, message, level=:info, cr=true) 63 | return if LEVELS.index(level) > LEVELS.index(@@log_level) 64 | 65 | level_str = "" 66 | case level 67 | when :error 68 | level_str = "ERROR" 69 | color = :red 70 | when :warning 71 | level_str = "WARN" 72 | color = :yellow 73 | when :notice 74 | level_str = "NOTICE" 75 | color = :green 76 | when :info 77 | level_str = "" 78 | color = :blue 79 | else 80 | color = :default 81 | end 82 | 83 | print format_lhs(task, level_str), color 84 | print message, (level==:warning or level==:error or level==:notice) ? color : :default 85 | 86 | if block_given? 87 | yield 88 | end 89 | 90 | print "\n" if cr 91 | end 92 | 93 | def puts(text, color = :default) 94 | color_params = color_output? ? color : {} 95 | super(text.colorize(color_params)) 96 | end 97 | 98 | def print(text, color = :default) 99 | color_params = color_output? ? color : {} 100 | super(text.colorize(color_params)) 101 | end 102 | 103 | def self.terminal_supports_colors? 104 | # No colors unless we are being run via a TTY 105 | return false unless $stdout.isatty 106 | 107 | # Check if the terminal supports colors 108 | colors = `tput colors 2> /dev/null`.chomp 109 | if $?.exitstatus == 0 110 | colors.to_i >= 8 111 | else 112 | false 113 | end 114 | end 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /lib/xcode/test/formatters/junit_formatter.rb: -------------------------------------------------------------------------------- 1 | require 'builder' 2 | require 'socket' 3 | 4 | module Xcode 5 | module Test 6 | module Formatters 7 | class JunitFormatter 8 | def initialize(dir) 9 | @dir = File.expand_path(dir).to_s 10 | FileUtils.mkdir_p(@dir) 11 | end 12 | 13 | def after_suite(suite) 14 | write(suite) 15 | end 16 | 17 | def write(report) 18 | if report.end_time.nil? 19 | raise "Report #{report} #{report.name} has a nil end time!?" 20 | end 21 | xml = ::Builder::XmlMarkup.new( :indent => 2 ) 22 | xml.instruct! :xml, :encoding => "UTF-8" 23 | xml.testsuite(:errors => report.total_errors, 24 | :failures => report.total_failed_tests, 25 | :hostname => Socket.gethostname, 26 | :name => report.name, 27 | :tests => report.tests.count, 28 | :time => (report.end_time - report.start_time), 29 | :timestamp => report.end_time 30 | ) do |p| 31 | 32 | report.tests.each do |t| 33 | p.testcase(:classname => report.name, 34 | :name => t.name, 35 | :time => t.time 36 | ) do |testcase| 37 | 38 | t.errors.each do |error| 39 | testcase.failure error[:location], :message => error[:message], :type => 'Failure' 40 | end 41 | end 42 | end 43 | end 44 | 45 | File.open(File.join(@dir, sanitize_filename("TEST-#{report.name}.xml")), 'w') do |current_file| 46 | current_file.write xml.target! 47 | end 48 | 49 | end # write 50 | 51 | private 52 | def sanitize_filename(filename) 53 | filename.gsub(/[^0-9A-Za-z.\-]/, '_') 54 | end 55 | 56 | end # JUnitFormatter 57 | 58 | end # Formatters 59 | end # Test 60 | end # Xcode -------------------------------------------------------------------------------- /lib/xcode/test/formatters/stdout_formatter.rb: -------------------------------------------------------------------------------- 1 | module Xcode 2 | module Test 3 | module Formatters 4 | class StdoutFormatter 5 | include Xcode::TerminalOutput 6 | 7 | def initialize(options = {}) 8 | @errors = [] 9 | @test_count = 0 10 | options.each { |k,v| self.send("#{k}=", v) } 11 | end 12 | 13 | def before(report) 14 | print_task :test, "Begin tests", :info 15 | end 16 | 17 | def after(report) 18 | level = @errors.count>0 ? :error : :info 19 | if @errors.count>0 20 | print_task :test, "The following failures occured:", :warning 21 | @errors.each do |e| 22 | print_task :test, "[#{e.suite.name} #{e.name}]", :error 23 | e.errors.each do |error| 24 | print_task :test, " #{error[:message]}", :error 25 | print_task :test, " at #{error[:location]}", :error 26 | if error[:data].count>0 27 | print_task :test, "\n Test Output:", :error 28 | print_task :test, " > #{error[:data].join(" > ")}\n\n", :error 29 | end 30 | end 31 | 32 | # if there is left over data in the test report, show that 33 | if e.data.count>0 34 | print_task :test, "\n There was this trailing output after the above failures", :error 35 | print_task :test, " > #{e.data.join(" > ")}\n\n", :error 36 | end 37 | end 38 | end 39 | 40 | print_task :test, "Finished in #{report.duration} seconds", :info 41 | print_task :test, "#{@test_count} tests, #{@errors.count} failures", report.failed? ? :error : :info 42 | end 43 | 44 | def before_suite(suite) 45 | print_task :test, "#{suite.name}: ", :info, false 46 | end 47 | 48 | def after_suite(suite) 49 | color = (suite.total_passed_tests == suite.tests.count) ? :info : :error 50 | #print_task :test, "#{suite.total_passed_tests}/#{suite.tests.count}", color 51 | puts " [#{suite.total_passed_tests}/#{suite.tests.count}]", color 52 | end 53 | 54 | def before_test(test) 55 | # puts "[#{test.suite.name} #{test.name}] << BEGIN" 56 | end 57 | 58 | def after_test(test) 59 | @test_count += 1 60 | if test.passed? 61 | print ".", :green 62 | elsif test.failed? 63 | print "F", :red 64 | @errors << test 65 | end 66 | end 67 | 68 | end # StdoutFormatter 69 | end # Formatters 70 | end # Test 71 | end # Xcode -------------------------------------------------------------------------------- /lib/xcode/test/parsers/kif_parser.rb: -------------------------------------------------------------------------------- 1 | require 'xcode/test/report' 2 | require 'time' 3 | 4 | module Xcode 5 | module Test 6 | module Parsers 7 | 8 | class KIFParser 9 | attr_accessor :report, :builder 10 | 11 | def initialize(report = Xcode::Test::Report.new) 12 | @report = report 13 | @awaiting_scenario_name = false 14 | yield self if block_given? 15 | end 16 | 17 | def flush 18 | @report.finish 19 | end 20 | 21 | def <<(piped_row) 22 | if @awaiting_scenario_name 23 | if match = piped_row.match(/\[\d+\:.+\]\s(.+)/) 24 | name = match[1].strip 25 | @report.add_suite name, Time.now 26 | @awaiting_scenario_name = false 27 | end 28 | return 29 | end 30 | 31 | case piped_row.force_encoding("UTF-8") 32 | 33 | when /BEGIN KIF TEST RUN: (\d+) scenarios/ 34 | @report.start 35 | 36 | when /BEGIN SCENARIO (\d+)\/(\d+) \(\d+ steps\)/ 37 | @awaiting_scenario_name = true 38 | 39 | when /END OF SCENARIO \(duration (\d+\.\d+)s/ 40 | @report.in_current_suite do |suite| 41 | suite.finish(Time.now) 42 | end 43 | 44 | when /(PASS|FAIL) \((\d+\.\d+s)\): (.+)/ 45 | duration = $2.to_f 46 | name = $3.strip 47 | @report.in_current_suite do |suite| 48 | test = suite.add_test_case(name) 49 | 50 | if $1 == 'PASS' 51 | test.passed(duration) 52 | else 53 | test.failed(duration) 54 | end 55 | end 56 | 57 | when /KIF TEST RUN FINISHED: \d+ failures \(duration (\d+\.\d+)s\)/ 58 | @report.finish 59 | 60 | when /(.*): error: -\[(\S+) (\S+)\] : (.*)/ 61 | message = $4 62 | location = $1 63 | @report.in_current_test do |test| 64 | test.add_error(message, location) 65 | end 66 | 67 | # when /failed with exit code (\d+)/, 68 | when /BUILD FAILED/ 69 | @report.finish 70 | 71 | when /Segmentation fault/ 72 | @report.abort 73 | 74 | when /the iPhoneSimulator platform does not currently support application-hosted tests/ 75 | raise "Application tests are not currently supported by the iphone simulator. If these are logic tests, try unsetting TEST_HOST in your project config" 76 | else 77 | @report.in_current_test do |test| 78 | test << piped_row 79 | end 80 | end # case 81 | 82 | end # << 83 | 84 | end # OCUnitParser 85 | end # Parsers 86 | end # Test 87 | end # Xcode -------------------------------------------------------------------------------- /lib/xcode/test/parsers/ocunit_parser.rb: -------------------------------------------------------------------------------- 1 | require 'xcode/test/report' 2 | require 'time' 3 | 4 | module Xcode 5 | module Test 6 | module Parsers 7 | 8 | class OCUnitParser 9 | attr_accessor :report, :builder 10 | 11 | def initialize(report = Xcode::Test::Report.new) 12 | @report = report 13 | yield self if block_given? 14 | end 15 | 16 | def close 17 | @report.finish 18 | end 19 | 20 | def << piped_row 21 | case piped_row.force_encoding("UTF-8") 22 | 23 | when /Test Suite '(\S+)'.*started at\s+(.*)/ 24 | name = $1 25 | time = Time.parse($2) 26 | if name=~/\// 27 | @report.start 28 | else 29 | @report.add_suite name, time 30 | end 31 | 32 | when /Test Suite '(\S+)'.*finished at\s+(.*)./ 33 | time = Time.parse($2) 34 | name = $1 35 | if name=~/\// 36 | @report.finish 37 | else 38 | @report.in_current_suite do |suite| 39 | suite.finish(time) 40 | end 41 | end 42 | 43 | when /Test Case '-\[\S+\s+(\S+)\]' started./ 44 | name = $1 45 | @report.in_current_suite do |suite| 46 | suite.add_test_case name 47 | end 48 | 49 | when /Test Case '-\[\S+\s+(\S+)\]' passed \((.*) seconds\)/ 50 | duration = $2.to_f 51 | @report.in_current_test do |test| 52 | test.passed(duration) 53 | end 54 | 55 | when /(.*): error: -\[(\S+) (\S+)\] : (.*)/ 56 | message = $4 57 | location = $1 58 | @report.in_current_test do |test| 59 | test.add_error(message, location) 60 | end 61 | 62 | when /Test Case '-\[\S+ (\S+)\]' failed \((\S+) seconds\)/ 63 | duration = $2.to_f 64 | @report.in_current_test do |test| 65 | test.failed(duration) 66 | end 67 | 68 | # when /failed with exit code (\d+)/, 69 | when /BUILD FAILED/ 70 | @report.finish 71 | 72 | when /Segmentation fault/ 73 | @report.abort 74 | 75 | when /Run test case (\w+)/ 76 | # ignore 77 | when /Run test suite (\w+)/ 78 | # ignore 79 | when /Executed (\d+) test, with (\d+) failures \((\d+) unexpected\) in (\S+) \((\S+)\) seconds/ 80 | # ignore 81 | when /the iPhoneSimulator platform does not currently support application-hosted tests/ 82 | raise "Application tests are not currently supported by the iphone simulator. If these are logic tests, try unsetting TEST_HOST in your project config" 83 | else 84 | @report.in_current_test do |test| 85 | test << piped_row 86 | end 87 | end # case 88 | 89 | end # << 90 | 91 | end # OCUnitParser 92 | end # Parsers 93 | end # Test 94 | end # Xcode -------------------------------------------------------------------------------- /lib/xcode/test/report.rb: -------------------------------------------------------------------------------- 1 | require 'xcode/test/report/suite_result' 2 | require 'xcode/test/report/test_result' 3 | 4 | module Xcode 5 | module Test 6 | 7 | module Formatters 8 | end 9 | 10 | 11 | # The report is the abstract representation of a collection of suites of tests. Formatters can be attached to write output 12 | # in real time 13 | class Report 14 | attr_reader :suites, :observers 15 | attr_accessor :start_time, :end_time, :exit_code, :unexpected 16 | 17 | 18 | class InvalidStateException < StandardError; end 19 | 20 | def initialize 21 | @exit_code = 0 22 | @suites = [] 23 | @formatters = [] 24 | @start_time = nil 25 | @end_time = nil 26 | @unexpected = false 27 | @observers = [] 28 | 29 | yield self if block_given? 30 | end 31 | 32 | def add_formatter(format, *args) 33 | require "xcode/test/formatters/#{format.to_s}_formatter" 34 | formatter = Xcode::Test::Formatters.const_get("#{format.to_s.capitalize}Formatter").new(*args) 35 | @observers << formatter 36 | end 37 | 38 | def unexpected? 39 | @unexpected 40 | end 41 | 42 | def succeed? 43 | !self.failed? 44 | end 45 | 46 | def failed? 47 | return true if unexpected? 48 | 49 | @suites.each do |suite| 50 | suite.tests.each do |test| 51 | return true if test.failed? 52 | end 53 | end 54 | 55 | false 56 | end 57 | 58 | def start 59 | @start_time = Time.now 60 | notify_observers :before, self 61 | end 62 | 63 | def add_suite(name, time=Time.now) 64 | suite = Xcode::Test::Report::SuiteResult.new(self, name, time) 65 | @suites << suite 66 | end 67 | 68 | def finished? 69 | !@end_time.nil? 70 | end 71 | 72 | def duration 73 | return 0 if @start_time.nil? 74 | return Time.now - @start_time if @end_time.nil? 75 | @end_time - @start_time 76 | end 77 | 78 | def in_current_suite 79 | # raise InvalidStateException.new("There is no active suite") 80 | return if @suites.size==0 or !@suites.last.end_time.nil? 81 | yield @suites.last 82 | end 83 | 84 | def in_current_test 85 | in_current_suite do |suite| 86 | # raise InvalidStateException.new("There is no active test case") 87 | return if suite.tests.size==0 88 | yield suite.tests.last 89 | end 90 | end 91 | 92 | def finish 93 | return if finished? 94 | 95 | # if there is a current suite which isnt finished - finish it 96 | in_current_suite do |suite| 97 | unless suite.finished? 98 | @unexpected = true 99 | suite.finish 100 | end 101 | end 102 | 103 | @end_time = Time.now 104 | notify_observers :after, self 105 | end 106 | 107 | def abort 108 | @report.unexpected=true 109 | finish 110 | end 111 | 112 | def notify_observers(event, obj=nil) 113 | @observers.each do |f| 114 | f.send event, obj if f.respond_to? event 115 | end 116 | end 117 | 118 | end # Report 119 | end # Test 120 | end # Xcode -------------------------------------------------------------------------------- /lib/xcode/test/report/suite_result.rb: -------------------------------------------------------------------------------- 1 | module Xcode 2 | module Test 3 | class Report 4 | class SuiteResult 5 | attr_accessor :tests, :name, :start_time, :end_time, :report 6 | 7 | def initialize(report, name, start_time) 8 | @report = report 9 | @name = name 10 | @start_time = start_time 11 | @tests = [] 12 | 13 | @report.notify_observers :before_suite, self 14 | end 15 | 16 | def finish(time=Time.now) 17 | raise "Time is nil" if time.nil? 18 | 19 | # Fail any lingering test 20 | finish_current_test 21 | 22 | @end_time = time 23 | @report.notify_observers :after_suite, self 24 | end 25 | 26 | def add_test_case(name) 27 | finish_current_test 28 | 29 | test = Xcode::Test::Report::TestResult.new self, name 30 | @tests << test 31 | yield(test) if block_given? 32 | test 33 | end 34 | 35 | def finished? 36 | !@end_time.nil? 37 | end 38 | 39 | def total_errors 40 | errors = 0 41 | @tests.each do |t| 42 | errors = errors + t.errors.count if t.failed? 43 | end 44 | errors 45 | end 46 | 47 | def total_passed_tests 48 | @tests.select {|t| t.passed? }.count 49 | end 50 | 51 | def total_failed_tests 52 | @tests.select {|t| t.failed? }.count 53 | end 54 | 55 | private 56 | 57 | def finish_current_test 58 | # Fail any lingering test 59 | unless @tests.size==0 or @tests.last.passed? 60 | @tests.last.failed(0) 61 | end 62 | end 63 | 64 | end 65 | end 66 | end 67 | end -------------------------------------------------------------------------------- /lib/xcode/test/report/test_result.rb: -------------------------------------------------------------------------------- 1 | module Xcode 2 | module Test 3 | class Report 4 | class TestResult 5 | attr_reader :name, :time, :errors, :suite, :data 6 | 7 | def initialize(suite, name) 8 | @name = name 9 | @data = [] 10 | @suite = suite 11 | @errors = [] 12 | 13 | @suite.report.notify_observers :before_test, self 14 | end 15 | 16 | def passed? 17 | @passed 18 | end 19 | 20 | def failed? 21 | !@passed 22 | end 23 | 24 | def passed(time) 25 | @passed = true 26 | @time = time 27 | @suite.report.notify_observers :after_test, self 28 | end 29 | 30 | def failed(time) 31 | @passed = false 32 | @time = time 33 | @suite.report.notify_observers :after_test, self 34 | end 35 | 36 | def << (line) 37 | # puts "[#{@suite.name} #{@name}] << #{line}" 38 | return if @data.count==0 and line.strip.empty? 39 | @data << line 40 | end 41 | 42 | def add_error(error_message,error_location) 43 | @errors << {:message => error_message, :location => error_location, :data => @data} 44 | @data = [] 45 | end 46 | end 47 | end 48 | end 49 | end -------------------------------------------------------------------------------- /lib/xcode/variant_group.rb: -------------------------------------------------------------------------------- 1 | require_relative 'group' 2 | 3 | module Xcode 4 | 5 | # 6 | # A VariantGroup is generally a special group reserved for InfoPlist.strings 7 | # folders that contain additional files within it that are referenced. 8 | # 9 | module VariantGroup 10 | include Group ; extend Group 11 | 12 | # 13 | # # E21EB9DE14E357CF0058122A /* InfoPlist.strings */ = { 14 | # isa = PBXVariantGroup; 15 | # children = ( 16 | # E21EB9DF14E357CF0058122A /* en */, 17 | # ); 18 | # name = InfoPlist.strings; 19 | # sourceTree = ""; 20 | # }; 21 | # 22 | def self.info_plist properties 23 | default_properties = { 'isa' => 'PBXVariantGroup', 24 | 'children' => [], 25 | 'name' => name, 26 | 'sourceTree' => '' } 27 | 28 | default_properties.merge(properties) 29 | end 30 | 31 | end 32 | end -------------------------------------------------------------------------------- /lib/xcode/version.rb: -------------------------------------------------------------------------------- 1 | module Xcode 2 | VERSION = "0.1.19" 3 | end 4 | -------------------------------------------------------------------------------- /lib/xcode/workspace.rb: -------------------------------------------------------------------------------- 1 | require 'xcode/project' 2 | require 'nokogiri' 3 | 4 | module Xcode 5 | class Workspace 6 | attr_reader :projects, :name, :path 7 | def initialize(path) 8 | path = "#{path}.xcworkspace" unless path=~/\.xcworkspace/ 9 | 10 | @name = File.basename(path.gsub(/\.xcworkspace/,'')) 11 | @projects = [] 12 | @schemes = nil 13 | @path = File.expand_path path 14 | 15 | doc = Nokogiri::XML(open("#{@path}/contents.xcworkspacedata")) 16 | doc.search("FileRef").each do |file| 17 | location = file["location"] 18 | if (matcher = location.match(/^group:(.+\.xcodeproj)$/i)) 19 | project_path = "#{workspace_root}/#{matcher[1]}" 20 | begin 21 | @projects << Xcode::Project.new(project_path) 22 | rescue => e 23 | puts "Skipping project file #{project_path} referened by #{self} as it failed to parse" 24 | end 25 | end 26 | end 27 | end 28 | 29 | # 30 | # @return [Array] available schemes for the workspace 31 | # 32 | def schemes 33 | return @schemes unless @schemes.nil? 34 | @schemes = Xcode::Scheme.find_in_workspace(self) 35 | @schemes 36 | end 37 | 38 | # 39 | # Return the scheme with the specified name. Raises an error if no schemes 40 | # match the specified name. 41 | # 42 | # @note if two schemes match names, the first matching scheme is returned. 43 | # 44 | # @param [String] name of the specific scheme 45 | # @return [Scheme] the specific scheme that matches the name specified 46 | # 47 | def scheme(name) 48 | scheme = schemes.select {|t| t.name == name.to_s and t.parent == self}.first 49 | raise "No such scheme #{name} in #{self}, available schemes are #{schemes.map {|t| t.to_s}.join(', ')}" if scheme.nil? 50 | yield scheme if block_given? 51 | scheme 52 | end 53 | 54 | # 55 | # Return the names project. Raises an error if no projects 56 | # match the specified name. 57 | # 58 | # @note if two projects match names, the first matching scheme is returned. 59 | # 60 | # @param [String] name of the specific scheme 61 | # @return [Project] the specific project that matches the name specified 62 | # 63 | def project(name) 64 | project = @projects.select {|c| c.name == name.to_s}.first 65 | raise "No such project #{name} in #{self}, available projects are #{@projects.map {|c| c.name}.join(', ')}" if project.nil? 66 | yield project if block_given? 67 | project 68 | end 69 | 70 | def to_s 71 | "#{name} (Workspace)" 72 | end 73 | 74 | def describe 75 | puts "Workspace #{name} contains:" 76 | projects.each do |p| 77 | p.describe 78 | end 79 | end 80 | 81 | def workspace_root 82 | File.dirname(@path) 83 | end 84 | end 85 | end -------------------------------------------------------------------------------- /spec/Provisioning/AdHoc.mobileprovision: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayh/xcoder/0affa3e8f0a5c138ea25c004341d62b23c6b6711/spec/Provisioning/AdHoc.mobileprovision -------------------------------------------------------------------------------- /spec/Provisioning/AppStore.mobileprovision: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayh/xcoder/0affa3e8f0a5c138ea25c004341d62b23c6b6711/spec/Provisioning/AppStore.mobileprovision -------------------------------------------------------------------------------- /spec/Provisioning/Test.keychain: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayh/xcoder/0affa3e8f0a5c138ea25c004341d62b23c6b6711/spec/Provisioning/Test.keychain -------------------------------------------------------------------------------- /spec/Provisioning/TestUser.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayh/xcoder/0affa3e8f0a5c138ea25c004341d62b23c6b6711/spec/Provisioning/TestUser.p12 -------------------------------------------------------------------------------- /spec/TestProject/ApplicationTests/ApplicationTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | au.com.rayh.xcoder.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /spec/TestProject/ApplicationTests/ApplicationTests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'ApplicationTests' target in the 'ApplicationTests' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #endif 9 | -------------------------------------------------------------------------------- /spec/TestProject/ApplicationTests/ApplicationTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationTests.h 3 | // ApplicationTests 4 | // 5 | // Created by Ray Hilton on 18/04/12. 6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ApplicationTests : SenTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /spec/TestProject/ApplicationTests/ApplicationTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationTests.m 3 | // ApplicationTests 4 | // 5 | // Created by Ray Hilton on 18/04/12. 6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "ApplicationTests.h" 10 | 11 | @implementation ApplicationTests 12 | 13 | - (void)setUp 14 | { 15 | [super setUp]; 16 | 17 | // Set-up code here. 18 | } 19 | 20 | - (void)tearDown 21 | { 22 | // Tear-down code here. 23 | 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample 28 | { 29 | STFail(@"Unit tests are not implemented yet in ApplicationTests"); 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /spec/TestProject/ApplicationTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /spec/TestProject/Buildfile: -------------------------------------------------------------------------------- 1 | project :TestProject 2 | target :TestProject 3 | config :Release 4 | version "#{ENV['BUILD_NUMBER']}" 5 | #profile '' 6 | #identity '' 7 | 8 | #testflight_api_token '' 9 | #testflight_team_token '' 10 | #testflight_notes `git log -n 1` 11 | #testflight_list 'Internal' 12 | 13 | before :clean do |builder| 14 | `rm -Rf Local.vendorbackup*` 15 | end 16 | 17 | before :build do |builder| 18 | #`vendor install` 19 | `rm -Rf Local.vendorbackup*` 20 | end -------------------------------------------------------------------------------- /spec/TestProject/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayh/xcoder/0affa3e8f0a5c138ea25c004341d62b23c6b6711/spec/TestProject/Default-568h@2x.png -------------------------------------------------------------------------------- /spec/TestProject/LogicTests/AnotherTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // AnotherTest.h 3 | // TestProject 4 | // 5 | // Created by Ray Hilton on 1/12/11. 6 | // Copyright (c) 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | // Logic unit tests contain unit test code that is designed to be linked into an independent test executable. 10 | // See Also: http://developer.apple.com/iphone/library/documentation/Xcode/Conceptual/iphone_development/135-Unit_Testing_Applications/unit_testing_applications.html 11 | 12 | #import 13 | 14 | @interface AnotherTest : SenTestCase 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /spec/TestProject/LogicTests/AnotherTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // AnotherTest.m 3 | // TestProject 4 | // 5 | // Created by Ray Hilton on 1/12/11. 6 | // Copyright (c) 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "AnotherTest.h" 10 | 11 | @implementation AnotherTest 12 | 13 | // All code under test must be linked into the Unit Test bundle 14 | - (void)testMath 15 | { 16 | STAssertTrue((1 + 1) == 2, @"Compiler isn't feeling well today :-("); 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /spec/TestProject/LogicTests/TestProjectTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | au.com.rayh.xcoder.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /spec/TestProject/LogicTests/TestProjectTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestProjectTests.h 3 | // TestProjectTests 4 | // 5 | // Created by Ray Hilton on 10/11/11. 6 | // Copyright (c) 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TestProjectTests : SenTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /spec/TestProject/LogicTests/TestProjectTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestProjectTests.m 3 | // TestProjectTests 4 | // 5 | // Created by Ray Hilton on 10/11/11. 6 | // Copyright (c) 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "TestProjectTests.h" 10 | 11 | @implementation TestProjectTests 12 | 13 | - (void)setUp 14 | { 15 | [super setUp]; 16 | 17 | // Set-up code here. 18 | } 19 | 20 | - (void)tearDown 21 | { 22 | // Tear-down code here. 23 | 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testNilShouldAlwaysBeNil 28 | { 29 | STAssertNil(nil, @"This should definitely be nil"); 30 | } 31 | 32 | - (void)testShouldFail 33 | { 34 | STAssertEquals(1+1, 3, @"Something is broken"); 35 | } 36 | 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /spec/TestProject/LogicTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /spec/TestProject/TestProject.xcodeproj/xcshareddata/xcschemes/TestProject.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 71 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 89 | 90 | 96 | 97 | 98 | 99 | 101 | 102 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /spec/TestProject/TestProject/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TestProject 4 | // 5 | // Created by Ray Hilton on 10/11/11. 6 | // Copyright (c) 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /spec/TestProject/TestProject/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // TestProject 4 | // 5 | // Created by Ray Hilton on 10/11/11. 6 | // Copyright (c) 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | @synthesize window = _window; 14 | 15 | - (void)dealloc 16 | { 17 | [_window release]; 18 | [super dealloc]; 19 | } 20 | 21 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 22 | { 23 | self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; 24 | // Override point for customization after application launch. 25 | self.window.backgroundColor = [UIColor whiteColor]; 26 | [self.window makeKeyAndVisible]; 27 | return YES; 28 | } 29 | 30 | - (void)applicationWillResignActive:(UIApplication *)application 31 | { 32 | /* 33 | Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 34 | Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 35 | */ 36 | } 37 | 38 | - (void)applicationDidEnterBackground:(UIApplication *)application 39 | { 40 | /* 41 | Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 42 | If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 43 | */ 44 | } 45 | 46 | - (void)applicationWillEnterForeground:(UIApplication *)application 47 | { 48 | /* 49 | Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 50 | */ 51 | } 52 | 53 | - (void)applicationDidBecomeActive:(UIApplication *)application 54 | { 55 | /* 56 | Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 57 | */ 58 | } 59 | 60 | - (void)applicationWillTerminate:(UIApplication *)application 61 | { 62 | /* 63 | Called when the application is about to terminate. 64 | Save data if appropriate. 65 | See also applicationDidEnterBackground:. 66 | */ 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /spec/TestProject/TestProject/TestProject-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFiles 12 | 13 | CFBundleIdentifier 14 | au.com.rayh.xcoder.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | 27 | LSRequiresIPhoneOS 28 | 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /spec/TestProject/TestProject/TestProject-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'TestProject' target in the 'TestProject' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_3_0 8 | #warning "This project uses features only available in iOS SDK 3.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /spec/TestProject/TestProject/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /spec/TestProject/TestProject/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TestProject 4 | // 5 | // Created by Ray Hilton on 10/11/11. 6 | // Copyright (c) 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spec/TestWorkspace.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 9 | 10 | 11 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /spec/TestWorkspace.xcworkspace/xcshareddata/xcschemes/WorkspaceScheme.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 14 | 20 | 21 | 22 | 23 | 24 | 29 | 30 | 31 | 32 | 38 | 39 | 40 | 41 | 50 | 51 | 57 | 58 | 59 | 60 | 61 | 62 | 68 | 69 | 75 | 76 | 77 | 78 | 80 | 81 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /spec/TestWorkspace2.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /spec/build_phase_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe Xcode::BuildPhase do 4 | 5 | let(:project) { Xcode.project('TestProject') } 6 | let(:target) { project.target('TestProject') } 7 | 8 | describe "PBXSourcesBuildPhase" do 9 | 10 | let(:subject) { target.sources_build_phase } 11 | 12 | let(:first_build_file) { "main.m" } 13 | let(:second_build_file) { "AppDelegate.m" } 14 | 15 | describe "#files" do 16 | it "should return the correct number of build files" do 17 | subject.files.count.should == 2 18 | end 19 | 20 | it "should return BuildFiles with references to their files" do 21 | subject.files.each do |file| 22 | file.file_ref.should be_kind_of Xcode::FileReference 23 | end 24 | end 25 | end 26 | 27 | describe "#file" do 28 | it "should return the build file specified from the file name" do 29 | subject.file('main.m').should_not be_nil 30 | subject.file('main.m').file_ref.path.should == 'main.m' 31 | end 32 | end 33 | 34 | describe "#build_files" do 35 | it "should return the correct number of build files" do 36 | subject.build_files.count.should == 2 37 | end 38 | 39 | it "should return the correct build files " do 40 | subject.build_files.first.path.should == first_build_file 41 | subject.build_files.last.path.should == second_build_file 42 | end 43 | end 44 | 45 | describe "#build_file" do 46 | it "should return the correct file by name" do 47 | subject.build_file(first_build_file).should_not be_nil 48 | subject.build_file(first_build_file).path.should == subject.build_files.first.path 49 | end 50 | end 51 | 52 | describe "#add_build_file" do 53 | it "should add the specified file to the build phase" do 54 | source_file = project.groups.create_file 'NewFile.m' 55 | subject.add_build_file source_file 56 | subject.build_file('NewFile.m').should_not be_nil 57 | end 58 | end 59 | 60 | describe "#add_build_file_without_arc" do 61 | it "should add the specified file to the build phase with the correct parameters" do 62 | source_file = project.groups.create_file 'ArcLessSource.m' 63 | subject.add_build_file_without_arc source_file 64 | subject.build_file('ArcLessSource.m').should_not be_nil 65 | subject.file('ArcLessSource.m').settings.should == { 'COMPILER_FLAGS' => "-fno-objc-arc" } 66 | end 67 | end 68 | 69 | describe "#add_build_file_with_public_privacy" do 70 | it "should add the specified file to the build phase with the correct parameters" do 71 | source_file = project.groups.create_file 'publicheader.h' 72 | subject.add_build_file_with_public_privacy source_file 73 | subject.build_file('publicheader.h').should_not be_nil 74 | subject.file('publicheader.h').settings.should == { 'ATTRIBUTES' => ["Public"] } 75 | end 76 | end 77 | 78 | describe "#add_build_file_with_private_privacy" do 79 | it "should add the specified file to the build phase with the correct parameters" do 80 | source_file = project.groups.create_file 'privateheader.h' 81 | subject.add_build_file_with_private_privacy source_file 82 | subject.build_file('privateheader.h').should_not be_nil 83 | subject.file('privateheader.h').settings.should == { 'ATTRIBUTES' => ["Private"] } 84 | end 85 | end 86 | 87 | end 88 | 89 | 90 | describe "PBXFrameworksBuildPhase" do 91 | 92 | let(:subject) { target.framework_build_phase } 93 | 94 | let(:first_build_file) { "System/Library/Frameworks/UIKit.framework" } 95 | let(:second_build_file) { "System/Library/Frameworks/Foundation.framework" } 96 | let(:third_build_file) { "System/Library/Frameworks/CoreGraphics.framework" } 97 | 98 | describe "#build_files" do 99 | it "should return the correct number of build files" do 100 | subject.build_files.count.should == 4 101 | end 102 | 103 | it "should return the correct build files " do 104 | subject.build_files[0].path.should == first_build_file 105 | subject.build_files[1].path.should == second_build_file 106 | subject.build_files[2].path.should == third_build_file 107 | end 108 | 109 | end 110 | 111 | end 112 | 113 | 114 | describe "PBXResourcesBuildPhase" do 115 | 116 | let(:subject) { target.resources_build_phase } 117 | 118 | let(:first_build_file) { "InfoPlist.strings" } 119 | 120 | describe "#build_files" do 121 | it "should return the correct number of build files" do 122 | subject.build_files.count.should == 2 123 | end 124 | 125 | it "should return the correct build files " do 126 | subject.build_files.first.name.should == first_build_file 127 | end 128 | 129 | end 130 | 131 | end 132 | 133 | end -------------------------------------------------------------------------------- /spec/configuration_list_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe Xcode::ConfigurationList do 4 | 5 | let(:project) { Xcode.project 'TestProject' } 6 | let(:subject) { project.create_target('ConfigCreateTarget').build_configuration_list } 7 | 8 | describe "#configs" do 9 | it "should return all the configurations" do 10 | subject.build_configurations.count.should == 0 11 | end 12 | end 13 | 14 | describe "#create_config" do 15 | it "should add additional config" do 16 | subject.create_config('Debug2ElectricBoogaloo').should_not be_nil 17 | end 18 | end 19 | 20 | describe "#default_config" do 21 | context "when no default configuration has been specified" do 22 | it "should return a nil" do 23 | subject.default_config.should be_nil 24 | end 25 | end 26 | 27 | context "when a default configuration has been set" do 28 | it "it should return that configuration" do 29 | new_config = subject.create_config('Debug2ElectricBoogaloo') 30 | subject.set_default_config new_config.name 31 | subject.default_config_name.should == new_config.name 32 | subject.default_config.identifier.should == new_config.identifier 33 | end 34 | end 35 | 36 | end 37 | 38 | end -------------------------------------------------------------------------------- /spec/deploy_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | require 'ostruct' 3 | require 'xcode/deploy/testflight' 4 | require 'xcode/deploy/web_assets' 5 | require 'xcode/deploy/ftp' 6 | require 'xcode/deploy/ssh' 7 | 8 | describe Xcode::Deploy do 9 | 10 | let :builder do 11 | OpenStruct.new( 12 | :ipa_path => 'ipa path', 13 | :dsym_zip_path => 'dsym path', 14 | :bundle_version => '1.0', 15 | :bundle_identifier => 'test.bunlde.identifier' 16 | ) 17 | end 18 | 19 | describe Xcode::Deploy::WebAssets do 20 | it "should generate the assets" do 21 | Xcode::Deploy::WebAssets.generate builder, 'http://example.com/base-url' do |dir| 22 | File.exists?("#{dir}/index.html").should == true 23 | File.exists?("#{dir}/manifest.plist").should == true 24 | 25 | # TODO: more tests 26 | end 27 | end 28 | end 29 | 30 | describe Xcode::Deploy::Ftp do 31 | # TODO: tests! 32 | end 33 | 34 | describe Xcode::Deploy::Ssh do 35 | # TODO: tests! 36 | end 37 | 38 | describe Xcode::Deploy::Testflight do 39 | 40 | let(:testflight) do 41 | Xcode::Deploy::Testflight.new(builder, {:api_token => 'api token', :team_token => 'team token' }) 42 | end 43 | 44 | it "should be configured with api and team token" do 45 | testflight.api_token.should == 'api token' 46 | testflight.team_token.should == 'team token' 47 | end 48 | 49 | it "should call curl with correct bulld paths" do 50 | PTY.stub(:spawn) do |cmd, &block| 51 | cmd.should =~/^curl/ 52 | cmd.should =~/-F file=@"ipa path"/ 53 | cmd.should =~/-F dsym=@"dsym path"/ 54 | cmd.should =~/-F api_token='api token'/ 55 | cmd.should =~/-F team_token='team token'/ 56 | end 57 | 58 | testflight.deploy 59 | # response['response'].should == 'ok' 60 | end 61 | end 62 | 63 | end -------------------------------------------------------------------------------- /spec/group_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe Xcode::Group do 4 | 5 | let(:project) { Xcode.project('TestProject') } 6 | let(:subject) { project.groups } 7 | 8 | describe "#groups" do 9 | context "when a group matches the name specified" do 10 | it "should return the group" do 11 | subject.create_group('TestGroup') 12 | subject.group('TestGroup').should_not be_nil 13 | end 14 | end 15 | 16 | context "when multiple groups match the name specified" do 17 | it "should return all the matching groups" do 18 | subject.create_group('TestGroup') 19 | subject.group('TestGroup').length.should == 2 20 | end 21 | end 22 | 23 | context "when a group does not match the name specified" do 24 | it "should return an empty array" do 25 | subject.group('UnknownGroup').should be_empty 26 | end 27 | end 28 | end 29 | 30 | describe "#supergroup" do 31 | it "should return the owning group" do 32 | group = subject.create_group('Superman') 33 | group.supergroup.identifier.should == subject.identifier 34 | end 35 | end 36 | 37 | describe "#create_group" do 38 | it "should return the group" do 39 | subject.create_group('TestGroup').should_not be_nil 40 | end 41 | 42 | context "when adding a group within a group" do 43 | it "should successfully create the subgroup" do 44 | subgroup = subject.create_group('GroupWithSubGroup').create_group('Group MED') 45 | 46 | found_subgroup = subject.group('GroupWithSubGroup').first.group('Group MED').first 47 | subgroup.should_not be_nil 48 | found_subgroup.should_not be_nil 49 | 50 | subgroup.identifier.should == found_subgroup.identifier 51 | end 52 | end 53 | end 54 | 55 | describe "#remove!" do 56 | 57 | let!(:subject) { project.group('created/groups/to') } 58 | let!(:group_losing_a_child) { project.group('created/groups') } 59 | let!(:remove_grandchild) { project.group('created/groups/to/here') } 60 | 61 | it "should remove the group and all the children" do 62 | subject.supergroup.remove! 63 | group_losing_a_child.groups.should be_empty 64 | end 65 | 66 | end 67 | 68 | describe "Files" do 69 | let(:subject) { project.groups.group('TestProject').first } 70 | 71 | describe "#files" do 72 | it "should return the correct number of files within the group" do 73 | subject.files.count.should == 2 74 | end 75 | end 76 | 77 | describe "#file" do 78 | it "should return the files that match" do 79 | subject.file('AppDelegate.m').should_not be_empty 80 | end 81 | end 82 | 83 | describe "#create_file" do 84 | 85 | let(:new_file_params) { {'name' => 'NewFile.m', 'path' => 'NewFile.m'} } 86 | 87 | before(:each) do 88 | subject.create_file new_file_params 89 | end 90 | 91 | it "should create the files within the group" do 92 | subject.file(new_file_params['name']).should_not be_empty 93 | end 94 | 95 | it "should not duplicate the file within the group" do 96 | subject.create_file new_file_params 97 | subject.file(new_file_params['name']).count.should == 1 98 | end 99 | end 100 | 101 | describe "#create_system_framework" do 102 | 103 | let(:framework_name) { 'CFNetwork' } 104 | let(:full_framework_name) { 'CFNetwork.framework' } 105 | 106 | before(:each) do 107 | subject.create_system_framework framework_name 108 | end 109 | 110 | it "should create the framework within the group" do 111 | subject.file(full_framework_name).should_not be_nil 112 | end 113 | 114 | it "should not create the framework again if it exists" do 115 | expect(subject.exists?(full_framework_name)).to be_truthy 116 | subject.create_system_framework framework_name 117 | subject.file(full_framework_name).count.should == 1 118 | end 119 | 120 | end 121 | 122 | describe "#create_system_library" do 123 | 124 | let(:library_name) { 'libz.dylib' } 125 | 126 | it "should create the system library within the group" do 127 | subject.create_system_library library_name 128 | end 129 | 130 | it "should create a system library with the correct path" do 131 | subject.create_system_library(library_name).path.should == "usr/lib/libz.dylib" 132 | end 133 | 134 | it "should not create a system library twice" do 135 | subject.create_system_library library_name 136 | subject.create_system_library library_name 137 | subject.file(library_name).count.should == 1 138 | end 139 | 140 | end 141 | 142 | end 143 | 144 | end -------------------------------------------------------------------------------- /spec/integration/builder_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../spec_helper' 2 | 3 | describe Xcode::Builder, :integration => true do 4 | 5 | context "when using a builder built from a configuration" do 6 | 7 | describe "#build" do 8 | 9 | let(:configuration) { Xcode.project('TestProject').target('TestProject').config('Debug') } 10 | let(:subject) { configuration.builder } 11 | 12 | it "should be able to build" do 13 | subject.clean 14 | subject.build 15 | File.exists?(subject.app_path).should==true 16 | File.exists?(subject.dsym_path).should==true 17 | end 18 | 19 | it "should be able to package" do 20 | subject.clean 21 | subject.build 22 | subject.package 23 | File.exists?(subject.dsym_zip_path).should==true 24 | File.exists?(subject.ipa_path).should==true 25 | end 26 | 27 | end 28 | 29 | 30 | describe "logic tests" do 31 | 32 | let(:configuration) { Xcode.project('TestProject').target('LogicTests').config('Debug') } 33 | let(:subject) { configuration.builder } 34 | 35 | it "should be able to run unit tests" do 36 | subject.clean 37 | report = subject.test 38 | report.suites.count.should == 2 39 | 40 | tests = report.suites[1].tests 41 | tests.count.should==2 42 | tests[0].should be_passed 43 | tests[1].should be_failed 44 | 45 | tests = report.suites[0].tests 46 | tests.count.should==1 47 | tests[0].should be_passed 48 | end 49 | 50 | end 51 | 52 | 53 | describe "application tests" do 54 | 55 | # FIXME: known issue with xcodebuild and iphonesimulator application tests (when TEST_HOST is set) 56 | let(:configuration) { Xcode.project('TestProject').target('ApplicationTests').config('Debug') } 57 | let(:subject) { configuration.builder } 58 | 59 | it "should be able to run unit tests" do 60 | subject.clean 61 | report = subject.test 62 | report.should be_succeed 63 | end 64 | 65 | end 66 | 67 | end 68 | 69 | context "when using a builder built from a scheme" do 70 | 71 | let(:scheme) { Xcode.project('TestProject').scheme('TestProject') } 72 | 73 | let(:subject) { scheme.builder } 74 | 75 | describe "#build" do 76 | 77 | it "should be able to build" do 78 | subject.clean 79 | subject.build 80 | File.exists?(subject.app_path).should==true 81 | File.exists?(subject.dsym_path).should==true 82 | end 83 | 84 | it "should be able to package" do 85 | subject.clean 86 | subject.build 87 | subject.package 88 | File.exists?(subject.dsym_zip_path).should==true 89 | File.exists?(subject.ipa_path).should==true 90 | end 91 | 92 | end 93 | 94 | describe "#test" do 95 | 96 | # FIXME: cant seem to run tests when part of a scheme 97 | it "should be able to run unit tests" do 98 | subject.clean 99 | report = subject.test 100 | report.suites.count.should == 2 101 | 102 | tests = report.suites[1].tests 103 | tests.count.should==2 104 | tests[0].should be_passed 105 | tests[1].should be_failed 106 | 107 | tests = report.suites[0].tests 108 | tests.count.should==1 109 | tests[0].should be_passed 110 | end 111 | 112 | end 113 | 114 | end 115 | 116 | end 117 | -------------------------------------------------------------------------------- /spec/integration/cedar_install_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../spec_helper' 2 | 3 | describe "Cedar", :integration => true do 4 | 5 | let(:project) { Xcode.project 'TestProject' } 6 | 7 | it "should update a project with a working installation of Cedar" do 8 | 9 | # Copy the files necessary for the project to the correct destination 10 | 11 | FileUtils.cp_r "examples/Cedar/Vendor", "spec/TestProject" 12 | FileUtils.cp_r "examples/Cedar/Specs", "spec/TestProject" 13 | 14 | # 15 | # The following block of code will generate the target with all the neccessary 16 | # parameters and properties to get a full working 'Cedar' spec to build. 17 | # 18 | 19 | project.create_target 'Specs' do |target| 20 | 21 | target.name = 'Specs' 22 | target.product_name = 'Specs' 23 | 24 | application_file = target.create_product_reference 'Specs' 25 | 26 | # Create the Specs group 27 | 28 | supporting_group = project.group('Specs/SupportingFiles') 29 | 30 | # Create the Source File and add it to the sources build phase 31 | 32 | main_source = supporting_group.create_file 'name' => 'main.m', 'path' => 'Specs/main.m' 33 | 34 | target.create_build_phase :sources do |source| 35 | source.add_build_file main_source 36 | end 37 | 38 | # Create the InfoPlist file and add it to the Resrouces build phase 39 | 40 | supporting_group.create_file 'name' => 'Specs-Info.plist', 'path' => 'Specs/Specs-Info.plist' 41 | 42 | infoplist_file = supporting_group.create_infoplist 'name' => 'InfoPlist.strings', 'path' => 'Specs' 43 | infoplist_file.create_file 'name' => 'en', 'path' => 'en.lproj/InfoPlist.strings' 44 | 45 | target.create_build_phase :resources do |resources| 46 | resources.add_build_file infoplist_file 47 | end 48 | 49 | prefix_file = supporting_group.create_file 'name' => 'Specs-Prefix.pch', 'path' => 'Specs/Specs-Prefix.pch' 50 | 51 | # Create the custom framework and add it to the frameworks build phase 52 | 53 | cedar_framework = target.project.frameworks_group.create_framework 'name' => 'Cedar-iPhone.framework', 'path' => 'Vendor/Frameworks/Cedar-iPhone.framework', 'sourceTree' => '' 54 | 55 | target.create_build_phase :framework do |frameworks| 56 | frameworks.add_build_file cedar_framework 57 | frameworks.add_build_file project.file('Frameworks/UIKit.framework') 58 | frameworks.add_build_file project.file('Frameworks/Foundation.framework') 59 | frameworks.add_build_file project.file('Frameworks/CoreGraphics.framework') 60 | end 61 | 62 | # Add the necessary configurations 63 | 64 | target.create_configurations :debug, :release do |config| 65 | config.set 'GCC_PREFIX_HEADER', 'Specs/Specs-Prefix.pch' 66 | config.set 'INFOPLIST_FILE', 'Specs/Specs-Info.plist' 67 | config.set 'OTHER_LDFLAGS', '-ObjC -all_load -lstdc++' 68 | config.set 'FRAMEWORK_SEARCH_PATHS', [ "$(inherited)", "\"$(SRCROOT)/Vendor/Frameworks\"" ] 69 | config.save! 70 | end 71 | 72 | end 73 | 74 | project.save! 75 | 76 | expect { project.target('Specs').config('Debug').builder.build }.to_not raise_error 77 | 78 | project.save! 79 | 80 | end 81 | 82 | end -------------------------------------------------------------------------------- /spec/integration/pull_to_refresh_install_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../spec_helper' 2 | 3 | describe "EGORefreshTableHeaderView", :integration => true do 4 | 5 | let(:project) { Xcode.project 'TestProject' } 6 | 7 | it "should install without error" do 8 | 9 | # Copy the files necessary for the project to the correct destination 10 | 11 | FileUtils.cp_r "examples/EGORefreshTableHeaderView/Vendor", "spec/TestProject" 12 | 13 | # Add the source and header file to the project 14 | 15 | source_files = [ { 'name' => 'EGORefreshTableHeaderView.m', 'path' => 'Vendor/EGORefreshTableHeaderView/EGORefreshTableHeaderView.m' }, 16 | { 'name' => 'EGORefreshTableHeaderView.h', 'path' => 'Vendor/EGORefreshTableHeaderView/EGORefreshTableHeaderView.h' } ] 17 | 18 | # Create or traverse to the group to install the source files 19 | 20 | project.group('Vendor/EGORefreshTableHeaderView') do 21 | source_files.each {|source| create_file source } 22 | end 23 | 24 | ptr_source = project.file('Vendor/EGORefreshTableHeaderView/EGORefreshTableHeaderView.m') 25 | 26 | # Select the main target of the project and add the source file to the build 27 | # phase. 28 | 29 | project.target('TestProject').sources_build_phase do 30 | add_build_file ptr_source 31 | end 32 | 33 | project.save! 34 | end 35 | 36 | end -------------------------------------------------------------------------------- /spec/integration/reachability_install_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../spec_helper' 2 | 3 | describe "Reachability", :integration => true do 4 | 5 | let(:project) { Xcode.project 'TestProject' } 6 | 7 | it "should update a project with a working installation of Reachability" do 8 | 9 | # Copy the files necessary for the project to the correct destination 10 | 11 | FileUtils.cp_r "examples/Reachability/Vendor", "spec/TestProject" 12 | 13 | 14 | # Add the source and header file to the project 15 | 16 | source_files = [ { 'name' => 'Reachability.m', 'path' => 'Vendor/Reachability/Reachability.m' }, 17 | { 'name' => 'Reachability.h', 'path' => 'Vendor/Reachability/Reachability.h' } ] 18 | 19 | 20 | # Create or traverse to the group to install the source files 21 | project.group('Vendor/Reachability') do 22 | source_files.each {|source| create_file source } 23 | end 24 | 25 | source_file = project.file('Vendor/Reachability/Reachability.m') 26 | 27 | # Select the main target of the project and add the source file to the build phase. 28 | 29 | project.target('TestProject').sources_build_phase do 30 | add_build_file source_file 31 | end 32 | 33 | cfnetwork_framework = project.frameworks_group.create_system_framework 'CFNetwork' 34 | 35 | project.target('TestProject').framework_build_phase do 36 | add_build_file cfnetwork_framework 37 | end 38 | 39 | project.save! 40 | 41 | end 42 | 43 | end 44 | -------------------------------------------------------------------------------- /spec/integration/universal_framework_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../spec_helper' 2 | 3 | describe "Universal Framework", :integration => true do 4 | 5 | let(:project) { Xcode.project 'TestProject' } 6 | 7 | it "should install without error" do 8 | 9 | project.create_target 'Library', :bundle do |target| 10 | 11 | target.create_build_phase :sources do |source| 12 | source.add_build_file project.file('TestProject/AppDelegate.m') 13 | end 14 | 15 | target.create_build_phase :copy_headers do |headers| 16 | headers.add_build_file_with_public_privacy project.file('TestProject/AppDelegate.h') 17 | headers.add_build_file project.file('TestProject/Supporting Files/TestProject-Prefix.pch') 18 | end 19 | 20 | target.create_configurations :release do |config| 21 | config.always_search_user_paths = false 22 | config.architectures = [ "$(ARCHS_STANDARD_32_BIT)", 'armv6' ] 23 | config.copy_phase_strip = true 24 | config.dead_code_stripping = false 25 | config.debug_information_format = "dwarf-with-dsym" 26 | config.c_language_standard = 'gnu99' 27 | config.enable_objc_exceptions = true 28 | config.generate_debugging_symbols = false 29 | config.precompile_prefix_headers = false 30 | config.gcc_version = 'com.apple.compilers.llvm.clang.1_0' 31 | config.warn_64_to_32_bit_conversion = true 32 | config.warn_about_missing_prototypes = true 33 | config.install_path = "$(LOCAL_LIBRARY_DIR)/Bundles" 34 | config.link_with_standard_libraries = false 35 | config.mach_o_type = 'mh_object' 36 | config.macosx_deployment_target = '10.7' 37 | config.product_name = '$(TARGET_NAME)' 38 | config.sdkroot = 'iphoneos' 39 | config.valid_architectures = '$(ARCHS_STANDARD_32_BIT)' 40 | config.wrapper_extension = 'framework' 41 | config.info_plist_location = "" 42 | config.prefix_header = "" 43 | config.save! 44 | end 45 | 46 | end 47 | 48 | project.create_target('Universal Library',:aggregate) do |target| 49 | 50 | target.product_name = 'Univeral Library' 51 | 52 | target.add_dependency project.target('Library') 53 | 54 | target.create_configurations :release 55 | 56 | target.create_build_phase :run_script do |script| 57 | script.shell_script = "# Sets the target folders and the final framework product.\nFMK_NAME=Library\nFMK_VERSION=A\n\n# Install dir will be the final output to the framework.\n# The following line create it in the root folder of the current project.\nINSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework\n\n# Working dir will be deleted after the framework creation.\nWRK_DIR=build\nDEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework\nSIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework\n\n# Building both architectures.\nxcodebuild -configuration \"Release\" -target \"${FMK_NAME}\" -sdk iphoneos\nxcodebuild -configuration \"Release\" -target \"${FMK_NAME}\" -sdk iphonesimulator\n\n# Cleaning the oldest.\nif [ -d \"${INSTALL_DIR}\" ]\nthen\nrm -rf \"${INSTALL_DIR}\"\nfi\n\n# Creates and renews the final product folder.\nmkdir -p \"${INSTALL_DIR}\"\nmkdir -p \"${INSTALL_DIR}/Versions\"\nmkdir -p \"${INSTALL_DIR}/Versions/${FMK_VERSION}\"\nmkdir -p \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources\"\nmkdir -p \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers\"\n\n# Creates the internal links.\n# It MUST uses relative path, otherwise will not work when the folder is copied/moved.\nln -s \"${FMK_VERSION}\" \"${INSTALL_DIR}/Versions/Current\"\nln -s \"Versions/Current/Headers\" \"${INSTALL_DIR}/Headers\"\nln -s \"Versions/Current/Resources\" \"${INSTALL_DIR}/Resources\"\nln -s \"Versions/Current/${FMK_NAME}\" \"${INSTALL_DIR}/${FMK_NAME}\"\n\n# Copies the headers and resources files to the final product folder.\ncp -R \"${DEVICE_DIR}/Headers/\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers/\"\ncp -R \"${DEVICE_DIR}/\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/\"\n\n# Removes the binary and header from the resources folder.\nrm -r \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/Headers\" \"${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/${FMK_NAME}\"\n\n# Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.\nlipo -create \"${DEVICE_DIR}/${FMK_NAME}\" \"${SIMULATOR_DIR}/${FMK_NAME}\" -output \"${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}\"\n\nrm -r \"${WRK_DIR}\"" 58 | end 59 | 60 | end 61 | 62 | project.save! 63 | end 64 | 65 | end -------------------------------------------------------------------------------- /spec/keychain_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rspec' 2 | require 'xcoder' 3 | 4 | describe Xcode::Keychain do 5 | it "should create a keychain" do 6 | path = nil 7 | kc = Xcode::Keychain.temp do |kc| 8 | path = kc.path 9 | File.exists?(kc.path).should==true 10 | end 11 | File.exists?(path).should==false 12 | end 13 | 14 | it "should import a certificate" do 15 | Xcode::Keychain.temp do |kc| 16 | kc.import "#{File.dirname(__FILE__)}/Provisioning/TestUser.p12", 'testpassword' 17 | kc.identities.size.should==1 18 | kc.identities[0].should=="Test User" 19 | end 20 | end 21 | 22 | it "should open an existing keychain" do 23 | kc = Xcode::Keychain.new("#{File.dirname(__FILE__)}/Provisioning/Test.keychain") 24 | kc.unlock 'testpassword' 25 | kc.identities.size.should==1 26 | kc.identities[0].should=="Test User" 27 | end 28 | 29 | # FIXME: Need to surpress GUI dialog prompting to unlock keychain 30 | # it "should lock the keychain" do 31 | # kc = Xcode::Keychain.temp 32 | # kc.lock 33 | # kc.import "#{File.dirname(__FILE__)}/Provisioning/TestUser.p12", 'testpassword' 34 | # kc.identities.size.should==0 35 | # end 36 | 37 | it "should fetch the login keychain" do 38 | kc = Xcode::Keychain.login 39 | File.exists?(kc.path).should==true 40 | end 41 | end 42 | 43 | describe Xcode::Keychains do 44 | it "should read the current keychain search path" do 45 | Xcode::Keychains.search_path[0].path.should=~/login.keychain/ 46 | end 47 | 48 | it "should update the keychain search path" do 49 | keychains = Xcode::Keychains.search_path 50 | test_keychain = Xcode::Keychain.new("#{File.dirname(__FILE__)}/Provisioning/Test.keychain") 51 | begin 52 | Xcode::Keychains.search_path = [test_keychain] + keychains 53 | results = `security list-keychains` 54 | expect(results.include?(test_keychain.path)).to be_truthy 55 | ensure 56 | Xcode::Keychains.search_path = keychains 57 | end 58 | end 59 | 60 | it "should add the keychain to the search path and then remove it" do 61 | test_keychain = Xcode::Keychain.new("#{File.dirname(__FILE__)}/Provisioning/Test.keychain") 62 | Xcode::Keychains.with_keychain_in_search_path test_keychain do 63 | results = `security list-keychains` 64 | expect(results.include?(test_keychain.path)).to be_truthy 65 | end 66 | 67 | results = `security list-keychains` 68 | expect(results.include?(test_keychain.path)).to be_falsey 69 | end 70 | end -------------------------------------------------------------------------------- /spec/parsers/plutil_project_parser.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe Xcode::PLUTILProjectParser do 4 | 5 | let(:subject) { Xcode::PLUTILProjectParser } 6 | 7 | describe "#parse" do 8 | 9 | context "when the file exists" do 10 | 11 | let(:project_filet_that_exists) { "A Project File That Exists" } 12 | 13 | it "should return the parsed content" do 14 | 15 | subject.should_receive(:open_project_file).with(project_filet_that_exists).and_return(:raw_content) 16 | 17 | Plist.should_receive(:parse_xml).with(:raw_content).and_return(:valid_content) 18 | 19 | subject.parse(project_filet_that_exists).should eq(:valid_content) 20 | 21 | 22 | end 23 | end 24 | 25 | context "when the file does not exist" do 26 | 27 | let(:project_file_does_not_exist) { "A Project File Does Not Exists" } 28 | 29 | it "should raise an exception" do 30 | 31 | subject.should_receive(:open_project_file).with(project_file_does_not_exist) 32 | 33 | Plist.should_receive(:parse_xml).and_return(nil) 34 | 35 | expect { 36 | 37 | subject.parse(project_file_does_not_exist) 38 | 39 | }.to raise_error 40 | 41 | 42 | end 43 | end 44 | 45 | end 46 | 47 | end -------------------------------------------------------------------------------- /spec/parsers/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require_relative '../spec_helper' -------------------------------------------------------------------------------- /spec/provisioning_profile_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe Xcode::ProvisioningProfile do 4 | 5 | let(:adhoc) do 6 | path = "#{File.dirname(__FILE__)}/Provisioning/AdHoc.mobileprovision" 7 | Xcode::ProvisioningProfile.new(path) 8 | end 9 | 10 | let(:appstore) do 11 | path = "#{File.dirname(__FILE__)}/Provisioning/AppStore.mobileprovision" 12 | Xcode::ProvisioningProfile.new(path) 13 | end 14 | 15 | 16 | context "app store provisionig profile" do 17 | it "should read the uuid from the profile" do 18 | appstore.uuid.should=="81289CB8-4CC2-4A11-B3D6-82D8FA2BEC81" 19 | end 20 | it "should read the name from the profile" do 21 | appstore.name.should=="App Store" 22 | end 23 | it "should read identifiers from the profile" do 24 | appstore.identifiers.count.should==1 25 | appstore.identifiers.first.should=="ZKVD5XDZZZ" 26 | end 27 | it "should know its an app store profile" do 28 | appstore.appstore?.should==true 29 | appstore.devices.count.should==0 30 | end 31 | 32 | end 33 | 34 | context "ad hoc provisionig profile" do 35 | it "should read the uuid from the profile" do 36 | adhoc.uuid.should=="3FD4C48D-DD38-42E2-B535-C0F73198E52B" 37 | end 38 | it "should read the name from the profile" do 39 | adhoc.name.should=="AdHoc Distribution" 40 | end 41 | it "should read identifiers from the profile" do 42 | adhoc.identifiers.count.should==1 43 | adhoc.identifiers.first.should=="ZKVD5XDZZZ" 44 | end 45 | it "should know its an ad hoc profile" do 46 | adhoc.appstore?.should==false 47 | adhoc.devices.count.should==2 48 | adhoc.devices[0].should=="13cdd5c65c9c14f1e9a6f9885867d24fe77a2168" 49 | adhoc.devices[1].should=="b330b956d2b5a904a3da10224878d30ea96acaf4" 50 | end 51 | end 52 | 53 | end -------------------------------------------------------------------------------- /spec/registry_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe Xcode::Registry do 4 | 5 | def self.is_identifier? value 6 | value =~ /^[0-9A-F]{24,}$/ 7 | end 8 | 9 | describe "ClassMethods" do 10 | subject { described_class } 11 | 12 | describe "#is_identifier?" do 13 | context "when the value is valid hexadecimal 24 character length string" do 14 | context "when it is all uppercase" do 15 | let(:input) { "0123456789ABCDEF01234567" } 16 | 17 | it "should be an identifier" do 18 | expect(subject.is_identifier?(input)).to be_truthy 19 | end 20 | end 21 | 22 | context "when it uses lowercase characters" do 23 | let(:input) { "0123456789abcdef01234567" } 24 | 25 | it "should be an identifier" do 26 | expect(subject.is_identifier?(input)).to be_truthy 27 | end 28 | end 29 | end 30 | 31 | context "when the value is less than 24 characters" do 32 | let(:input) { "0123456789ABCDEF" } 33 | 34 | it "should not be an identifier" do 35 | expect(subject.is_identifier?(input)).to be_falsey 36 | end 37 | end 38 | 39 | context "when it contains other than hexadecimal characters" do 40 | let(:input) { "0123456789ABCDEFGHIJKLMNO" } 41 | 42 | it "should not be an identifier" do 43 | expect(subject.is_identifier?(input)).to be_falsey 44 | end 45 | end 46 | 47 | end 48 | end 49 | 50 | 51 | end -------------------------------------------------------------------------------- /spec/scheme_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe Xcode::Scheme do 4 | let :project do 5 | Xcode.project 'TestProject' 6 | end 7 | 8 | let :workspace do 9 | Xcode.workspace 'TestWorkspace' 10 | end 11 | 12 | context "Project schemes" do 13 | it "should parse project schemes" do 14 | scheme = project.scheme('TestProject') 15 | scheme.name.should=="TestProject" 16 | scheme.build_targets.first.name.should == 'TestProject' 17 | scheme.build_targets.first.project.name.should == 'TestProject' 18 | end 19 | 20 | it "should return an array of schemes" do 21 | project.schemes.size.should == 5 22 | end 23 | 24 | it "should complain that no such scheme exists" do 25 | lambda do 26 | project.scheme('BadScheme') 27 | end.should raise_error 28 | end 29 | end 30 | 31 | context "Workspace schemes" do 32 | it "should complain that no such scheme exists" do 33 | lambda do 34 | workspace.scheme('BadScheme') 35 | end.should raise_error 36 | end 37 | 38 | it "should return an array of schemes" do 39 | workspace.schemes.size.should == 6 40 | end 41 | 42 | it "should parse workspace schemes" do 43 | scheme = workspace.scheme('WorkspaceScheme') 44 | scheme.name.should=="WorkspaceScheme" 45 | scheme.build_targets.first.name.should == 'TestProject' 46 | scheme.build_targets.first.project.name.should == 'TestProject' 47 | end 48 | end 49 | 50 | 51 | end -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require_relative '../lib/xcoder' 2 | require 'fileutils' 3 | -------------------------------------------------------------------------------- /spec/workspace_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe Xcode::Workspace do 4 | it "should enumerate all workspaces in current directory" do 5 | workspaces = Xcode.workspaces 6 | workspaces.size.should == 2 7 | workspaces.first.name.should == "TestWorkspace" 8 | workspaces.first.projects.size.should == 1 9 | end 10 | 11 | it "should fetch workspace by name" do 12 | w = Xcode.workspace 'TestWorkspace' 13 | expect(w).not_to be_nil 14 | end 15 | 16 | it "should fetch workspace by name with extension and path" do 17 | w = Xcode.workspace "#{File.dirname(__FILE__)}/TestWorkspace.xcworkspace" 18 | expect(w).not_to be_nil 19 | end 20 | 21 | it "should handle workspace that use ' in the XML" do 22 | w = Xcode.workspace "#{File.dirname(__FILE__)}/TestWorkspace2.xcworkspace" 23 | expect(w).not_to be_nil 24 | end 25 | 26 | it "should have many projects" do 27 | w = Xcode.workspace "TestWorkspace" 28 | expect(w.projects.size).to eq 1 29 | expect(w.projects.first.name). to eq "TestProject" 30 | end 31 | 32 | it "should get project by name" do 33 | w = Xcode.workspace "TestWorkspace" 34 | p = w.project 'TestProject' 35 | expect(p.name.should).to eq "TestProject" 36 | end 37 | end -------------------------------------------------------------------------------- /spec/xcode_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe Xcode do 4 | 5 | let(:subject) { Xcode } 6 | 7 | describe "#projects" do 8 | 9 | context "when there is a project within the current directory" do 10 | 11 | let(:subject) { Xcode.projects } 12 | 13 | it "should find the project" do 14 | puts subject.map {|p| p.name} 15 | expect(subject.size).to eq 1 16 | expect(subject.first.name).to eq "TestProject" 17 | end 18 | 19 | end 20 | 21 | end 22 | 23 | describe "#project" do 24 | context "when the target project exists" do 25 | context "when fetching the project by name" do 26 | 27 | let(:subject) { Xcode.project 'TestProject' } 28 | 29 | it "should find the project" do 30 | expect(subject).not_to be_nil 31 | end 32 | 33 | end 34 | 35 | context "when fetching the project by path" do 36 | 37 | let(:subject) { Xcode.project "#{File.dirname(__FILE__)}/TestProject/TestProject.xcodeproj" } 38 | 39 | it "should find the project" do 40 | expect(subject).not_to be_nil 41 | end 42 | 43 | end 44 | end 45 | 46 | context "when the target project does not exist" do 47 | 48 | let(:subject) { Xcode.project 'DoesNotExistProject' } 49 | 50 | it "should raise an error" do 51 | expect { subject }.to raise_error 52 | end 53 | 54 | end 55 | 56 | end 57 | 58 | end -------------------------------------------------------------------------------- /xcoder.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "xcode/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "xcoder" 7 | s.version = Xcode::VERSION 8 | s.authors = ["Ray Hilton", "Frank Webber"] 9 | s.email = ["ray@wirestorm.net", "franklin.webber@gmail.com"] 10 | s.homepage = "https://github.com/rayh/xcoder" 11 | s.summary = %q{Ruby wrapper around xcodebuild, xcrun, agvtool and pbxproj files} 12 | s.description = %q{Provides a ruby based object-model for parsing project structures and invoking builds} 13 | 14 | s.rubyforge_project = "xcoder" 15 | 16 | s.files = `git ls-files`.split("\n").reject {|f| f=~/^(?:spec|examples)\//} # Ignore example files 17 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 18 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 19 | s.require_paths = ["lib"] 20 | 21 | s.add_runtime_dependency "multi_json" 22 | s.add_runtime_dependency "plist" 23 | s.add_runtime_dependency "nokogiri" 24 | s.add_runtime_dependency "builder" 25 | s.add_runtime_dependency "rest-client" 26 | s.add_runtime_dependency "colorize" 27 | s.add_runtime_dependency "aws-sdk" 28 | end 29 | --------------------------------------------------------------------------------