xcoder 0.1.11 → 0.1.12
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.
- data/Rakefile +2 -0
- data/lib/xcode/builder.rb +32 -19
- data/lib/xcode/shell.rb +3 -1
- data/lib/xcode/test/parsers/ocunit_parser.rb +94 -0
- data/lib/xcode/test/report.rb +121 -0
- data/lib/xcode/test/report/suite_result.rb +67 -0
- data/lib/xcode/test/report/test_result.rb +49 -0
- data/lib/xcode/version.rb +1 -1
- data/lib/xcode/workspace.rb +6 -3
- data/spec/TestProject/{TestProjectTests/TestProjectTests-Info.plist → ApplicationTests/ApplicationTests-Info.plist} +0 -0
- data/spec/TestProject/ApplicationTests/ApplicationTests-Prefix.pch +8 -0
- data/spec/TestProject/ApplicationTests/ApplicationTests.h +13 -0
- data/spec/TestProject/ApplicationTests/ApplicationTests.m +32 -0
- data/spec/TestProject/{TestProjectTests → ApplicationTests}/en.lproj/InfoPlist.strings +0 -0
- data/spec/TestProject/{TestProjectTests → LogicTests}/AnotherTest.h +0 -0
- data/spec/TestProject/{TestProjectTests → LogicTests}/AnotherTest.m +0 -0
- data/spec/TestProject/LogicTests/TestProjectTests-Info.plist +22 -0
- data/spec/TestProject/{TestProjectTests → LogicTests}/TestProjectTests.h +0 -0
- data/spec/TestProject/{TestProjectTests → LogicTests}/TestProjectTests.m +0 -0
- data/spec/TestProject/LogicTests/en.lproj/InfoPlist.strings +2 -0
- data/spec/TestProject/TestProject.xcodeproj/project.pbxproj +1329 -556
- data/spec/TestProject/TestProject.xcodeproj/xcshareddata/xcschemes/TestProject.xcscheme +13 -2
- data/spec/TestWorkspace2.xcworkspace/contents.xcworkspacedata +7 -0
- data/spec/TestWorkspace2.xcworkspace/xcuserdata/ray.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- data/spec/build_phase_spec.rb +2 -2
- data/spec/builder_spec.rb +51 -10
- data/spec/integration/builder_spec.rb +60 -4
- data/spec/ocunit_parser_spec.rb +164 -0
- data/spec/project_spec.rb +1 -1
- data/spec/workspace_spec.rb +8 -3
- metadata +44 -29
- data/lib/xcode/test/ocunit_report_parser.rb +0 -174
- data/lib/xcode/test/suite_result.rb +0 -39
- data/lib/xcode/test/test_result.rb +0 -43
- 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
|
data/lib/xcode/builder.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'xcode/shell'
|
2
2
|
require 'xcode/provisioning_profile'
|
3
|
-
require 'xcode/test/
|
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
|
30
|
-
cmd = build_command(
|
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
|
-
|
38
|
-
|
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
|
-
|
43
|
-
|
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
|
-
|
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(
|
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=\"#{@
|
219
|
-
cmd << "SYMROOT=\"#{@
|
231
|
+
cmd << "OBJROOT=\"#{@objroot}\""
|
232
|
+
cmd << "SYMROOT=\"#{@symroot}\""
|
220
233
|
cmd << "PROVISIONING_PROFILE=#{profile.uuid}" unless profile.nil?
|
221
234
|
cmd
|
222
235
|
end
|
data/lib/xcode/shell.rb
CHANGED
@@ -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
|