xcoder 0.1.11 → 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/Rakefile +2 -0
  2. data/lib/xcode/builder.rb +32 -19
  3. data/lib/xcode/shell.rb +3 -1
  4. data/lib/xcode/test/parsers/ocunit_parser.rb +94 -0
  5. data/lib/xcode/test/report.rb +121 -0
  6. data/lib/xcode/test/report/suite_result.rb +67 -0
  7. data/lib/xcode/test/report/test_result.rb +49 -0
  8. data/lib/xcode/version.rb +1 -1
  9. data/lib/xcode/workspace.rb +6 -3
  10. data/spec/TestProject/{TestProjectTests/TestProjectTests-Info.plist → ApplicationTests/ApplicationTests-Info.plist} +0 -0
  11. data/spec/TestProject/ApplicationTests/ApplicationTests-Prefix.pch +8 -0
  12. data/spec/TestProject/ApplicationTests/ApplicationTests.h +13 -0
  13. data/spec/TestProject/ApplicationTests/ApplicationTests.m +32 -0
  14. data/spec/TestProject/{TestProjectTests → ApplicationTests}/en.lproj/InfoPlist.strings +0 -0
  15. data/spec/TestProject/{TestProjectTests → LogicTests}/AnotherTest.h +0 -0
  16. data/spec/TestProject/{TestProjectTests → LogicTests}/AnotherTest.m +0 -0
  17. data/spec/TestProject/LogicTests/TestProjectTests-Info.plist +22 -0
  18. data/spec/TestProject/{TestProjectTests → LogicTests}/TestProjectTests.h +0 -0
  19. data/spec/TestProject/{TestProjectTests → LogicTests}/TestProjectTests.m +0 -0
  20. data/spec/TestProject/LogicTests/en.lproj/InfoPlist.strings +2 -0
  21. data/spec/TestProject/TestProject.xcodeproj/project.pbxproj +1329 -556
  22. data/spec/TestProject/TestProject.xcodeproj/xcshareddata/xcschemes/TestProject.xcscheme +13 -2
  23. data/spec/TestWorkspace2.xcworkspace/contents.xcworkspacedata +7 -0
  24. data/spec/TestWorkspace2.xcworkspace/xcuserdata/ray.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  25. data/spec/build_phase_spec.rb +2 -2
  26. data/spec/builder_spec.rb +51 -10
  27. data/spec/integration/builder_spec.rb +60 -4
  28. data/spec/ocunit_parser_spec.rb +164 -0
  29. data/spec/project_spec.rb +1 -1
  30. data/spec/workspace_spec.rb +8 -3
  31. metadata +44 -29
  32. data/lib/xcode/test/ocunit_report_parser.rb +0 -174
  33. data/lib/xcode/test/suite_result.rb +0 -39
  34. data/lib/xcode/test/test_result.rb +0 -43
  35. data/spec/test_report_spec.rb +0 -147
data/Rakefile CHANGED
@@ -4,10 +4,12 @@ require "yard/rake/yardoc_task"
4
4
 
5
5
  task :default => [:specs, :build]
6
6
 
7
+ desc "Run specs"
7
8
  task :specs do
8
9
  system "rspec --color --format d --tag ~integration"
9
10
  end
10
11
 
12
+ desc "Run integration tests"
11
13
  task :integration do
12
14
  system "rspec --color --format d --tag integration"
13
15
  end
@@ -1,6 +1,6 @@
1
1
  require 'xcode/shell'
2
2
  require 'xcode/provisioning_profile'
3
- require 'xcode/test/ocunit_report_parser.rb'
3
+ require 'xcode/test/parsers/ocunit_parser.rb'
4
4
  require 'xcode/testflight'
5
5
 
6
6
  module Xcode
@@ -10,7 +10,7 @@ module Xcode
10
10
  # project build tasks.
11
11
  #
12
12
  class Builder
13
- attr_accessor :profile, :identity, :build_path, :keychain, :sdk
13
+ attr_accessor :profile, :identity, :build_path, :keychain, :sdk, :objroot, :symroot
14
14
 
15
15
  def initialize(config)
16
16
  if config.is_a? Xcode::Scheme
@@ -23,37 +23,49 @@ module Xcode
23
23
  @sdk = @target.project.sdk
24
24
  @config = config
25
25
  @build_path = "#{File.dirname(@target.project.path)}/build/"
26
+ @objroot = @build_path
27
+ @symroot = @build_path
26
28
  end
27
29
 
28
30
 
29
- def build(sdk=@sdk)
30
- cmd = build_command(@sdk)
31
+ def build(options = {:sdk => @sdk})
32
+ cmd = build_command(options)
31
33
  with_keychain do
32
34
  Xcode::Shell.execute(cmd)
33
35
  end
34
36
  self
35
37
  end
36
-
37
- def test
38
- cmd = build_command('iphonesimulator')
38
+
39
+ #
40
+ # Invoke the configuration's test target and parse the resulting output
41
+ #
42
+ # If a block is provided, the report is yielded for configuration before the test is run
43
+ #
44
+ def test(options = {:sdk => 'iphonesimulator'}) #, :parser => :OCUnit })
45
+ cmd = build_command(options)
39
46
  cmd << "TEST_AFTER_BUILD=YES"
40
- cmd << "TEST_HOST=''"
41
47
 
42
- parser = Xcode::Test::OCUnitReportParser.new
43
- yield(parser) if block_given?
48
+ report = Xcode::Test::Report.new
49
+ if block_given?
50
+ yield(report)
51
+ else
52
+ report.add_formatter :stdout
53
+ report.add_formatter :junit, 'test-reports'
54
+ end
55
+
56
+ parser = Xcode::Test::Parsers::OCUnitParser.new report
44
57
 
45
58
  begin
46
59
  Xcode::Shell.execute(cmd, false) do |line|
47
60
  parser << line
48
61
  end
49
- rescue => e
62
+ rescue Xcode::Shell::ExecutionError => e
63
+ puts "Test platform exited: #{e.message}"
64
+ ensure
50
65
  parser.flush
51
- # Let the failure bubble up unless parser has got an error from the output
52
- raise e unless parser.failed?
53
66
  end
54
- exit 1 if parser.failed?
55
67
 
56
- self
68
+ report
57
69
  end
58
70
 
59
71
  def testflight(api_token, team_token)
@@ -202,11 +214,12 @@ module Xcode
202
214
  p
203
215
  end
204
216
 
205
- def build_command(sdk=@sdk)
217
+ def build_command(options = {})
218
+ options = {:sdk => @sdk}.merge options
206
219
  profile = install_profile
207
220
  cmd = []
208
221
  cmd << "xcodebuild"
209
- cmd << "-sdk #{sdk}" unless sdk.nil?
222
+ cmd << "-sdk #{options[:sdk]}" unless options[:sdk].nil?
210
223
  cmd << "-project \"#{@target.project.path}\""
211
224
 
212
225
  cmd << "-scheme \"#{@scheme.name}\"" unless @scheme.nil?
@@ -215,8 +228,8 @@ module Xcode
215
228
 
216
229
  cmd << "OTHER_CODE_SIGN_FLAGS='--keychain #{@keychain.path}'" unless @keychain.nil?
217
230
  cmd << "CODE_SIGN_IDENTITY=\"#{@identity}\"" unless @identity.nil?
218
- cmd << "OBJROOT=\"#{@build_path}\""
219
- cmd << "SYMROOT=\"#{@build_path}\""
231
+ cmd << "OBJROOT=\"#{@objroot}\""
232
+ cmd << "SYMROOT=\"#{@symroot}\""
220
233
  cmd << "PROVISIONING_PROFILE=#{profile.uuid}" unless profile.nil?
221
234
  cmd
222
235
  end
@@ -1,6 +1,8 @@
1
1
  module Xcode
2
2
  module Shell
3
3
 
4
+ class ExecutionError < StandardError; end
5
+
4
6
  def self.execute(bits, show_output=true)
5
7
  out = []
6
8
  cmd = bits.is_a?(Array) ? bits.join(' ') : bits
@@ -14,7 +16,7 @@ module Xcode
14
16
  end
15
17
  end
16
18
  #Process.wait
17
- raise "Error (#{$?.exitstatus}) executing '#{cmd}'\n\n #{out.join(" ")}" if $?.exitstatus>0
19
+ raise ExecutionError.new("Error (#{$?.exitstatus}) executing '#{cmd}'\n\n #{out.join(" ")}") if $?.exitstatus>0
18
20
  #puts "RETURN: #{out.inspect}"
19
21
  out
20
22
  end
@@ -0,0 +1,94 @@
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 flush
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
@@ -0,0 +1,121 @@
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
+ @debug = false
22
+ @exit_code = 0
23
+ @suites = []
24
+ @formatters = []
25
+ @start_time = nil
26
+ @end_time = nil
27
+ @unexpected = false
28
+ @observers = []
29
+
30
+ yield self if block_given?
31
+ end
32
+
33
+ def add_formatter(format, *args)
34
+ require "xcode/test/formatters/#{format.to_s}_formatter"
35
+ formatter = Xcode::Test::Formatters.const_get("#{format.to_s.capitalize}Formatter").new(*args)
36
+ @observers << formatter
37
+ end
38
+
39
+ def unexpected?
40
+ @unexpected
41
+ end
42
+
43
+ def succeed?
44
+ !self.failed?
45
+ end
46
+
47
+ def failed?
48
+ return true if unexpected?
49
+
50
+ @suites.each do |suite|
51
+ suite.tests.each do |test|
52
+ return true if test.failed?
53
+ end
54
+ end
55
+
56
+ false
57
+ end
58
+
59
+ def start
60
+ @start_time = Time.now
61
+ notify_observers :before, self
62
+ end
63
+
64
+ def add_suite(name, time=Time.now)
65
+ suite = Xcode::Test::Report::SuiteResult.new(self, name, time)
66
+ @suites << suite
67
+ end
68
+
69
+ def finished?
70
+ !@end_time.nil?
71
+ end
72
+
73
+ def duration
74
+ return 0 if @start_time.nil?
75
+ return Time.now - @start_time if @end_time.nil?
76
+ @end_time - @start_time
77
+ end
78
+
79
+ def in_current_suite
80
+ # raise InvalidStateException.new("There is no active suite")
81
+ return if @suites.size==0 or !@suites.last.end_time.nil?
82
+ yield @suites.last
83
+ end
84
+
85
+ def in_current_test
86
+ in_current_suite do |suite|
87
+ # raise InvalidStateException.new("There is no active test case")
88
+ return if suite.tests.size==0
89
+ yield suite.tests.last
90
+ end
91
+ end
92
+
93
+ def finish
94
+ return if finished?
95
+
96
+ # if there is a current suite which isnt finished - finish it
97
+ in_current_suite do |suite|
98
+ unless suite.finished?
99
+ @unexpected = true
100
+ suite.finish
101
+ end
102
+ end
103
+
104
+ @end_time = Time.now
105
+ notify_observers :after, self
106
+ end
107
+
108
+ def abort
109
+ @report.unexpected=true
110
+ finish
111
+ end
112
+
113
+ def notify_observers(event, obj=nil)
114
+ @observers.each do |f|
115
+ f.send event, obj if f.respond_to? event
116
+ end
117
+ end
118
+
119
+ end # Report
120
+ end # Test
121
+ end # Xcode
@@ -0,0 +1,67 @@
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
@@ -0,0 +1,49 @@
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