xcoder 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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/\"",