xcoder 0.1.4 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,17 +1,64 @@
1
1
  module SimpleIdentifierGenerator
2
2
  extend self
3
3
 
4
+ MAX_IDENTIFIER_GENERATION_ATTEMPTS = 10
5
+
4
6
  #
5
7
  # Generate an identifier string
6
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
+ #
7
53
  # @example identifier string
8
54
  #
9
55
  # "E21EB9EE14E359840058122A"
10
56
  #
11
- # @return [String] a 24-length string that contains only hexadecimal characters.
57
+ # @return [String] a new identifier string
12
58
  #
13
- def generate
59
+ def generate_new_key
14
60
  range = ('A'..'F').to_a + (0..9).to_a
15
61
  24.times.inject("") {|ident| "#{ident}#{range.sample}" }
16
62
  end
63
+
17
64
  end
data/lib/xcode/target.rb CHANGED
@@ -1,4 +1,4 @@
1
- require_relative 'build_file'
1
+ require 'xcode/build_file'
2
2
 
3
3
  module Xcode
4
4
 
@@ -49,13 +49,13 @@ module Xcode
49
49
  'productType' => 'com.apple.product-type.application' }
50
50
  end
51
51
 
52
- # A reference to the project for which these targets reside.
52
+ # @return [Project] the reference to the project for which these targets reside.
53
53
  attr_accessor :project
54
54
 
55
55
  #
56
- # @return [PBXBuildConfiguration] the configurations that this target supports.
57
- # these are generally 'Debug' or 'Release' but may be custom designed
58
- # configurations.
56
+ # @return [Array<BuildConfiguration>] the configurations that this target
57
+ # supports. These are generally 'Debug' or 'Release' but may be custom
58
+ # created configurations.
59
59
  #
60
60
  def configs
61
61
  build_configuration_list.build_configurations.map do |config|
@@ -65,11 +65,14 @@ module Xcode
65
65
  end
66
66
 
67
67
  #
68
- # Return a specific build configuration. When one is not found to match,
69
- # an exception is raised.
68
+ # Return a specific build configuration.
69
+ #
70
+ # @note an exception is raised if no configuration matches the specified name.
71
+ #
70
72
  #
71
73
  # @param [String] name of a configuration to return
72
- # @return [PBXBuildConfiguration] a specific build configuration that
74
+ #
75
+ # @return [BuildConfiguration] a specific build configuration that
73
76
  # matches the specified name.
74
77
  #
75
78
  def config(name)
@@ -6,7 +6,12 @@ module Xcode
6
6
  module Formatters
7
7
  class JunitFormatter
8
8
  def initialize(dir)
9
- @dir = dir
9
+ @dir = File.expand_path(dir)
10
+ FileUtils.mkdir_p(@dir)
11
+ end
12
+
13
+ def after_suite(suite)
14
+ write(suite)
10
15
  end
11
16
 
12
17
  def write(report)
@@ -15,7 +20,7 @@ module Xcode
15
20
  end
16
21
  xml = ::Builder::XmlMarkup.new( :indent => 2 )
17
22
  xml.instruct! :xml, :encoding => "UTF-8"
18
- xml.testsuite(:errors => report.total_error_tests,
23
+ xml.testsuite(:errors => report.total_errors,
19
24
  :failures => report.total_failed_tests,
20
25
  :hostname => Socket.gethostname,
21
26
  :name => report.name,
@@ -28,10 +33,10 @@ module Xcode
28
33
  p.testcase(:classname => report.name,
29
34
  :name => t.name,
30
35
  :time => t.time
31
- ) do |e|
36
+ ) do |testcase|
32
37
 
33
- if t.error?
34
- e.failure t.error_location, :message => t.error_message, :type => 'Failure'
38
+ t.errors.each do |error|
39
+ testcase.failure error[:location], :message => error[:message], :type => 'Failure'
35
40
  end
36
41
  end
37
42
  end
@@ -0,0 +1,62 @@
1
+ module Xcode
2
+ module Test
3
+ module Formatters
4
+ class StdoutFormatter
5
+
6
+ def initialize
7
+ @errors = []
8
+ end
9
+
10
+ def before(report)
11
+ puts "Begin tests"
12
+ end
13
+
14
+ def after(report)
15
+ puts "\n\nThe following failures occured:" if @errors.count>0
16
+ @errors.each do |e|
17
+ puts "[#{e.suite.name} #{e.name}]"
18
+ e.errors.each do |error|
19
+ puts " #{error[:message]}"
20
+ puts " at #{error[:location]}"
21
+ if error[:data].count>0
22
+ puts "\n Test Output:"
23
+ puts " > #{error[:data].join(" > ")}\n\n"
24
+ end
25
+ end
26
+
27
+ # if there is left over data in the test report, show that
28
+ if e.data.count>0
29
+ puts "\n There was this trailing output after the above failures"
30
+ puts " > #{e.data.join(" > ")}\n\n"
31
+ end
32
+ end
33
+
34
+ puts "\n\nEnd tests (#{report.failed? ? 'FAILED' : 'PASSED'}). Took #{report.duration}s"
35
+ end
36
+
37
+ def before_suite(suite)
38
+ print "#{suite.name}: "
39
+ end
40
+
41
+ def after_suite(suite)
42
+ puts " [#{suite.total_passed_tests}/#{suite.tests.count}]"
43
+ end
44
+
45
+ def before_test(test)
46
+ # puts "[#{test.suite.name} #{test.name}] << BEGIN"
47
+ end
48
+
49
+ def after_test(test)
50
+ if test.passed?
51
+ print "."
52
+ elsif test.failed?
53
+ print "F"
54
+ @errors << test
55
+ end
56
+ # puts "[#{test.suite.name} #{test.name}] << END"
57
+ end
58
+
59
+ end # StdoutFormatter
60
+ end # Formatters
61
+ end # Test
62
+ end # Xcode
@@ -11,58 +11,155 @@ module Xcode
11
11
 
12
12
  class OCUnitReportParser
13
13
 
14
- attr_reader :exit_code, :reports
14
+ attr_reader :reports, :end_time, :start_time
15
+ attr_accessor :debug, :formatters
15
16
 
16
17
  def initialize
18
+ @debug = false
17
19
  @exit_code = 0
18
20
  @reports = []
21
+ @formatters = []
22
+ @failed = false
23
+ @start_time = nil
24
+ @end_time = nil
25
+
26
+ add_formatter :junit, 'test-reports'
27
+ add_formatter :stdout
28
+ end
29
+
30
+ def failed?
31
+ @failed
32
+ end
33
+
34
+ def finished?
35
+ !@end_time.nil?
36
+ end
37
+
38
+ def duration
39
+ return Time.now - @start_time if @end_time.nil?
40
+ @end_time - @start_time
19
41
  end
20
-
21
- def write(dir, format=:junit)
22
- dir = File.expand_path(dir)
23
- FileUtils.mkdir_p(dir)
24
42
 
43
+ def add_formatter(format, *args)
25
44
  require "xcode/test/formatters/#{format.to_s}_formatter"
26
- formatter = Formatters.const_get("#{format.to_s.capitalize}Formatter").new(dir)
27
- @reports.each do |r|
28
- formatter.write(r)
45
+ formatter = Formatters.const_get("#{format.to_s.capitalize}Formatter").new(*args)
46
+ @formatters << formatter
47
+ end
48
+
49
+ def flush
50
+ return if finished?
51
+
52
+ # if there is a current, unfinished test - fail it
53
+ unless current_test.nil? or current_test.passed?
54
+ fail_current_test(0)
55
+ @failed = true
56
+ end
57
+
58
+ # if there is a current suite which isnt finished - finish it
59
+ unless current_suite.nil? or current_suite.finished?
60
+ @failed = true # It may not have failed, but we want to indicate an unexpected end
61
+ current_suite.finish
62
+ notify_formatters(:after_suite, current_suite)
29
63
  end
64
+
65
+ # finish all tests
66
+ @end_time = Time.now
67
+ notify_formatters(:after, self)
30
68
  end
31
69
 
32
70
  def <<(piped_row)
71
+ puts piped_row if @debug
72
+
33
73
  case piped_row
34
74
 
35
75
  when /Test Suite '(\S+)'.*started at\s+(.*)/
36
76
  name = $1
37
77
  time = Time.parse($2)
38
- @reports << SuiteResult.new(name, time) unless name=~/\// # ignore if its a file path
39
-
78
+ if name=~/\//
79
+ # all tests begin
80
+ @start_time = Time.now
81
+ notify_formatters(:before, self)
82
+ else
83
+ @reports << SuiteResult.new(name, time)
84
+ notify_formatters(:before_suite, current_suite)
85
+ end
86
+
40
87
  when /Test Suite '(\S+)'.*finished at\s+(.*)./
41
- @reports.last.finish(Time.parse($2))
88
+ time = Time.parse($2)
89
+ name = $1
90
+ if name=~/\//
91
+ # all tests ended
92
+ @end_time = Time.now
93
+ notify_formatters(:after, self)
94
+ else
95
+ @reports.last.finish(time)
96
+ notify_formatters(:after_suite, current_suite)
97
+ end
42
98
 
43
99
  when /Test Case '-\[\S+\s+(\S+)\]' started./
44
- test = TestResult.new($1)
100
+ test = TestResult.new($1, current_suite)
45
101
  @reports.last.tests << test
102
+ notify_formatters(:before_test, test)
46
103
 
47
104
  when /Test Case '-\[\S+\s+(\S+)\]' passed \((.*) seconds\)/
48
105
  @reports.last.tests.last.passed($2.to_f)
106
+ notify_formatters(:after_test, current_test)
49
107
 
50
108
  when /(.*): error: -\[(\S+) (\S+)\] : (.*)/
51
- @reports.last.tests.last.error(error_message,error_location)
52
- @exit_code = 1 # should terminate
109
+ current_test.add_error($4,$1)
110
+ @failed = true
111
+ # notify_formatters(:after_test, @reports.last.tests.last)
53
112
 
54
113
  when /Test Case '-\[\S+ (\S+)\]' failed \((\S+) seconds\)/
55
- @reports.last.tests.last.failed($2.to_f)
56
- @exit_code = 1 # should terminate
57
-
58
- when /failed with exit code (\d+)/
59
- @exit_code = $1.to_i
114
+ fail_current_test($2.to_f)
115
+ @failed = true
116
+
117
+ # when /failed with exit code (\d+)/,
118
+ when /BUILD FAILED/
119
+ flush
120
+
121
+ when /Run test case (\w+)/
122
+ # ignore
123
+ when /Run test suite (\w+)/
124
+ # ignore
125
+ when /Executed (\d+) test, with (\d+) failures \((\d+) unexpected\) in (\S+) \((\S+)\) seconds/
126
+ # ignore
127
+ else
128
+ append_line_to_current_test piped_row
129
+ end # case
130
+
131
+ end # <<
60
132
 
61
- when
62
- /BUILD FAILED/
63
- @exit_code = -1;
133
+ private
134
+
135
+ def notify_formatters(event, obj=nil)
136
+ @formatters.each do |f|
137
+ f.send event, obj if f.respond_to? event
64
138
  end
65
139
  end
66
- end
67
- end
68
- end
140
+
141
+ def current_suite
142
+ @reports.last
143
+ end
144
+
145
+ def current_test
146
+ @reports.last.tests.last unless current_suite.nil?
147
+ end
148
+
149
+ def fail_current_test(duration=0)
150
+ return if current_test.nil?
151
+
152
+ current_test.failed(duration)
153
+ notify_formatters(:after_test, current_test)
154
+ end
155
+
156
+ def append_line_to_current_test(line)
157
+ return if current_suite.nil? or !current_suite.end_time.nil?
158
+ return if current_test.nil?
159
+ current_test << line
160
+ end
161
+
162
+
163
+ end # OCUnitReportParser
164
+ end # Test
165
+ end # Xcode
@@ -9,13 +9,21 @@ module Xcode
9
9
  @tests = []
10
10
  end
11
11
 
12
- def finish(time)
12
+ def finish(time=Time.now)
13
13
  raise "Time is nil" if time.nil?
14
14
  @end_time = time
15
15
  end
16
-
17
- def total_error_tests
18
- @tests.select {|t| t.error? }.count
16
+
17
+ def finished?
18
+ !@end_time.nil?
19
+ end
20
+
21
+ def total_errors
22
+ errors = 0
23
+ @tests.each do |t|
24
+ errors = errors + t.errors.count if t.failed?
25
+ end
26
+ errors
19
27
  end
20
28
 
21
29
  def total_passed_tests
@@ -1,10 +1,13 @@
1
1
  module Xcode
2
2
  module Test
3
3
  class TestResult
4
- attr_reader :name, :time, :error_message, :error_location
4
+ attr_reader :name, :time, :errors, :suite, :data
5
5
 
6
- def initialize(name)
6
+ def initialize(name, suite)
7
7
  @name = name
8
+ @data = []
9
+ @suite = suite
10
+ @errors = []
8
11
  end
9
12
 
10
13
  def passed?
@@ -12,11 +15,7 @@ module Xcode
12
15
  end
13
16
 
14
17
  def failed?
15
- error? or !@passed
16
- end
17
-
18
- def error?
19
- !@error_message.nil?
18
+ !@passed
20
19
  end
21
20
 
22
21
  def passed(time)
@@ -28,10 +27,16 @@ module Xcode
28
27
  @passed = false
29
28
  @time = time
30
29
  end
30
+
31
+ def << (line)
32
+ # puts "[#{@suite.name} #{@name}] << #{line}"
33
+ return if @data.count==0 and line.strip.empty?
34
+ @data << line
35
+ end
31
36
 
32
- def error(error_message,error_location)
33
- @error_message = error_message
34
- @error_location = error_location
37
+ def add_error(error_message,error_location)
38
+ @errors << {:message => error_message, :location => error_location, :data => @data}
39
+ @data = []
35
40
  end
36
41
  end
37
42
  end
@@ -2,6 +2,10 @@ require_relative 'group'
2
2
 
3
3
  module Xcode
4
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
+ #
5
9
  module VariantGroup
6
10
  include Group ; extend Group
7
11
 
data/lib/xcode/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Xcode
2
- VERSION = "0.1.4"
2
+ VERSION = "0.1.6"
3
3
  end
data/lib/xcoder.rb CHANGED
@@ -10,20 +10,43 @@ require 'xcode/workspace'
10
10
  require 'xcode/buildfile'
11
11
 
12
12
  module Xcode
13
+
13
14
  @@projects = nil
14
15
  @@workspaces = nil
15
16
  @@sdks = nil
16
17
 
18
+ #
19
+ # Find all the projects within the current working directory.
20
+ #
21
+ # @return [Array<Project>] an array of the all the Projects found.
22
+ #
17
23
  def self.projects
18
24
  @@projects = parse_projects if @@projects.nil?
19
25
  @@projects
20
26
  end
21
27
 
28
+ #
29
+ # Find all the workspaces within the current working directory.
30
+ #
31
+ # @return [Array<Workspaces>] an array of all the Workspaces found.
32
+ #
22
33
  def self.workspaces
23
34
  @@workspaces = parse_workspaces if @@workspaces.nil?
24
35
  @@workspaces
25
36
  end
26
37
 
38
+ #
39
+ # Find the project with the specified name within the current working directory.
40
+ #
41
+ # @note this method will raise an error when it is unable to find the project
42
+ # specified.
43
+ #
44
+ # @param [String] name of the project (e.g. NAME.xcodeproj) that is attempting
45
+ # to be found.
46
+ #
47
+ # @return [Project] the project found; an error is raise if a project is unable
48
+ # to be found.
49
+ #
27
50
  def self.project(name)
28
51
  name = name.to_s
29
52
 
@@ -35,6 +58,18 @@ module Xcode
35
58
  raise "Unable to find a project named #{name}. However, I did find these projects: #{self.projects.map {|p| p.name}.join(', ') }"
36
59
  end
37
60
 
61
+ #
62
+ # Find the workspace with the specified name within the current working directory.
63
+ #
64
+ # @note this method will raise an error when it is unable to find the workspace
65
+ # specified.
66
+ #
67
+ # @param [String] name of the workspace (e.g. NAME.xcworkspace) that is attempting
68
+ # to be found.
69
+ #
70
+ # @return [Project] the workspace found; an error is raise if a workspace is unable
71
+ # to be found.
72
+ #
38
73
  def self.workspace(name)
39
74
  name = name.to_s
40
75
 
@@ -46,15 +81,30 @@ module Xcode
46
81
  raise "Unable to find a workspace named #{name}. However, I did find these workspaces: #{self.workspaces.map {|p| p.name}.join(', ') }"
47
82
  end
48
83
 
84
+ #
85
+ # @param [String] dir the path to search for projects; defaults to using
86
+ # the current working directory.
87
+ #
88
+ # @return [Array<Project>] the projects found at the specified directory.
89
+ #
49
90
  def self.find_projects(dir='.')
50
91
  parse_projects(dir)
51
92
  end
52
93
 
94
+ #
95
+ # @param [String] sdk name of the sdk that is being asked to see if available.
96
+ # @return [TrueClass,FalseClass] true if the sdk is available; false otherwise.
97
+ #
53
98
  def self.is_sdk_available?(sdk)
54
99
  parse_sdks if @@sdks.nil?
55
100
  @@sdks.values.include? sdk
56
101
  end
57
102
 
103
+ #
104
+ # Available SDKs available on this particular system.
105
+ #
106
+ # @return [Array<String>] the available SDKs on the current system.
107
+ #
58
108
  def self.available_sdks
59
109
  parse_sdks if @@sdks.nil?
60
110
  @@sdks
data/spec/builder_spec.rb CHANGED
@@ -86,6 +86,7 @@ describe Xcode::Builder do
86
86
  let(:default_clean_parameters) do
87
87
  [ "xcodebuild",
88
88
  "-project \"#{configuration.target.project.path}\"",
89
+ "-sdk iphoneos",
89
90
  "-target \"#{configuration.target.name}\"",
90
91
  "-configuration \"#{configuration.name}\"",
91
92
  "OBJROOT=\"#{File.dirname(configuration.target.project.path)}/build/\"",
@@ -132,6 +133,7 @@ describe Xcode::Builder do
132
133
  let(:default_clean_parameters) do
133
134
  [ "xcodebuild",
134
135
  "-project \"#{scheme.project.path}\"",
136
+ "-sdk iphoneos",
135
137
  "-scheme #{scheme.name}",
136
138
  "OBJROOT=\"#{File.dirname(scheme.project.path)}/build/\"",
137
139
  "SYMROOT=\"#{File.dirname(scheme.project.path)}/build/\"",