xcoder 0.0.17 → 0.0.18

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  gemspec
4
- gem 'rspec'
4
+ gem 'rspec'
5
+ gem 'builder'
data/bin/xcoder-build CHANGED
File without changes
data/lib/xcode/builder.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  require 'xcode/shell'
2
2
  require 'xcode/provisioning_profile'
3
+ require 'xcode/test/report_parser.rb'
4
+ require 'xcode/testflight'
3
5
 
4
6
  module Xcode
5
7
  class Builder
6
- attr_accessor :profile, :identity, :build_path, :keychain
8
+ attr_accessor :profile, :identity, :build_path, :keychain, :sdk
7
9
 
8
10
  def initialize(config)
9
11
  if config.is_a? Xcode::Scheme
@@ -11,6 +13,7 @@ module Xcode
11
13
  config = config.launch
12
14
  end
13
15
  @target = config.target
16
+ @sdk = @target.project.sdk
14
17
  @config = config
15
18
  @build_path = "#{File.dirname(@target.project.path)}/build/"
16
19
  end
@@ -30,40 +33,33 @@ module Xcode
30
33
  p
31
34
  end
32
35
 
33
- def build
34
- profile = install_profile
35
-
36
- cmd = []
37
- cmd << "xcodebuild"
38
- cmd << "-sdk #{@target.project.sdk}" unless @target.project.sdk.nil?
39
- cmd << "-project \"#{@target.project.path}\""
40
-
41
- cmd << "-scheme #{@scheme.name}" unless @scheme.nil?
42
- cmd << "-target \"#{@target.name}\"" if @scheme.nil?
43
- cmd << "-configuration \"#{@config.name}\"" if @scheme.nil?
44
-
45
- cmd << "OTHER_CODE_SIGN_FLAGS=\"--keychain #{@keychain.path}\"" unless @keychain.nil?
46
- cmd << "CODE_SIGN_IDENTITY=\"#{@identity}\"" unless @identity.nil?
47
- cmd << "OBJROOT=\"#{@build_path}\""
48
- cmd << "SYMROOT=\"#{@build_path}\""
49
- cmd << "PROVISIONING_PROFILE=#{profile.uuid}" unless profile.nil?
50
- yield(cmd) if block_given?
51
-
36
+ def build(sdk=@sdk)
37
+ cmd = build_command(@sdk)
52
38
  Xcode::Shell.execute(cmd)
53
39
  end
54
40
 
55
41
  def test
56
- build do |cmd|
57
- cmd.select! do |line|
58
- !line=~/\^-sdk/
59
- end
60
-
61
- cmd << "TEST_AFTER_BUILD=YES"
62
- cmd << "TEST_HOST=''"
63
- cmd << "-sdk iphonesimulator5.0" # FIXME: hardcoded version, should be smarter
42
+ cmd = build_command('iphonesimulator')
43
+ cmd << "TEST_AFTER_BUILD=YES"
44
+ cmd << "TEST_HOST=''"
45
+
46
+ parser = Xcode::Test::ReportParser.new
47
+ Xcode::Shell.execute(cmd, false) do |line|
48
+ puts line
49
+ parser << line
64
50
  end
65
51
 
66
- Xcode::Shell.execute(cmd)
52
+ yield(parser) if block_given?
53
+
54
+ exit parser.exit_code if parser.exit_code!=0
55
+
56
+ parser
57
+ end
58
+
59
+ def upload(api_token, team_token)
60
+ testflight = Xcode::Testflight.new(api_token, team_token)
61
+ yield(testflight) if block_given?
62
+ testflight.upload(ipa_path, dsym_zip_path)
67
63
  end
68
64
 
69
65
  def clean
@@ -158,5 +154,27 @@ module Xcode
158
154
  "#{product_version_basename}.dSYM.zip"
159
155
  end
160
156
 
157
+
158
+ private
159
+
160
+ def build_command(sdk=@sdk)
161
+ profile = install_profile
162
+ cmd = []
163
+ cmd << "xcodebuild"
164
+ cmd << "-sdk #{sdk}" unless sdk.nil?
165
+ cmd << "-project \"#{@target.project.path}\""
166
+
167
+ cmd << "-scheme #{@scheme.name}" unless @scheme.nil?
168
+ cmd << "-target \"#{@target.name}\"" if @scheme.nil?
169
+ cmd << "-configuration \"#{@config.name}\"" if @scheme.nil?
170
+
171
+ cmd << "OTHER_CODE_SIGN_FLAGS=\"--keychain #{@keychain.path}\"" unless @keychain.nil?
172
+ cmd << "CODE_SIGN_IDENTITY=\"#{@identity}\"" unless @identity.nil?
173
+ cmd << "OBJROOT=\"#{@build_path}\""
174
+ cmd << "SYMROOT=\"#{@build_path}\""
175
+ cmd << "PROVISIONING_PROFILE=#{profile.uuid}" unless profile.nil?
176
+ cmd
177
+ end
178
+
161
179
  end
162
180
  end
@@ -37,7 +37,7 @@ module Xcode
37
37
  end
38
38
 
39
39
  def info_plist
40
- puts @json.inspect
40
+ # puts @json.inspect
41
41
  info = Xcode::InfoPlist.new(self, info_plist_location)
42
42
  yield info if block_given?
43
43
  info.save
data/lib/xcode/shell.rb CHANGED
@@ -8,6 +8,7 @@ module Xcode
8
8
  IO.popen (cmd) do |f|
9
9
  f.each do |line|
10
10
  puts line if show_output
11
+ yield(line) if block_given?
11
12
  out << line
12
13
  end
13
14
  end
@@ -0,0 +1,172 @@
1
+ require 'time'
2
+ require 'FileUtils'
3
+ require 'socket'
4
+ require 'builder'
5
+
6
+ module Xcode
7
+ module Test
8
+ module Formatters
9
+ class JunitFormatter
10
+ def initialize(dir)
11
+ @dir = dir
12
+ end
13
+
14
+ def string_to_xml(s)
15
+ s.gsub(/&/, '&amp;').gsub(/'/, '&quot;').gsub(/</, '&lt;')
16
+ end
17
+
18
+ def write(report)
19
+ if report.end_time.nil?
20
+ raise "Report #{report} #{report.name} has a nil end time!?"
21
+ end
22
+ xml = ::Builder::XmlMarkup.new( :indent => 2 )
23
+ xml.instruct! :xml, :encoding => "UTF-8"
24
+ xml.testsuite(:errors => report.total_error_tests,
25
+ :failures => report.total_failed_tests,
26
+ :hostname => Socket.gethostname,
27
+ :name => report.name,
28
+ :tests => report.tests.count,
29
+ :time => (report.end_time - report.start_time),
30
+ :timestamp => report.end_time
31
+ ) do |p|
32
+
33
+ report.tests.each do |t|
34
+ p.testcase(:classname => report.name,
35
+ :name => t.name,
36
+ :time => t.time
37
+ ) do |e|
38
+
39
+ if t.error?
40
+ e.failure t.error_location, :message => t.error_message, :type => 'Failure'
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ File.open("#{@dir}/TEST-#{report.name}.xml", 'w') do |current_file|
47
+ current_file.write xml.target!
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+
54
+ class SuiteReport
55
+ attr_accessor :tests, :name, :start_time, :end_time
56
+
57
+ def initialize(name, start_time)
58
+ @name = name
59
+ @start_time = start_time
60
+ @tests = []
61
+ end
62
+
63
+ def finish(time)
64
+ raise "Time is nil" if time.nil?
65
+ @end_time = time
66
+ end
67
+
68
+ def total_error_tests
69
+ @tests.select {|t| t.error? }.count
70
+ end
71
+
72
+ def total_passed_tests
73
+ @tests.select {|t| t.passed? }.count
74
+ end
75
+
76
+ def total_failed_tests
77
+ @tests.select {|t| t.failed? }.count
78
+ end
79
+
80
+ end
81
+
82
+ class CaseReport
83
+ attr_reader :name, :time, :error_message, :error_location
84
+
85
+ def initialize(name)
86
+ @name = name
87
+ end
88
+
89
+ def passed?
90
+ @passed
91
+ end
92
+
93
+ def failed?
94
+ error? or !@passed
95
+ end
96
+
97
+ def error?
98
+ !@error_message.nil?
99
+ end
100
+
101
+ def passed(time)
102
+ @passed = true
103
+ @time = time
104
+ end
105
+
106
+ def failed(time)
107
+ @passed = false
108
+ @time = time
109
+ end
110
+
111
+ def error(error_message,error_location)
112
+ @error_message = error_message
113
+ @error_location = error_location
114
+ end
115
+ end
116
+
117
+ class ReportParser
118
+
119
+ attr_reader :exit_code, :reports
120
+
121
+ def initialize
122
+ @exit_code = 0
123
+ @reports = []
124
+ end
125
+
126
+ def write(dir, format=:junit)
127
+ dir = File.expand_path(dir)
128
+ FileUtils.mkdir_p(dir)
129
+
130
+ formatter = Formatters.const_get("#{format.capitalize}Formatter").new(dir)
131
+ @reports.each do |r|
132
+ formatter.write(r)
133
+ end
134
+ end
135
+
136
+ def <<(piped_row)
137
+ case piped_row
138
+
139
+ when /Test Suite '(\S+)'.*started at\s+(.*)/
140
+ name = $1
141
+ time = Time.parse($2)
142
+ @reports << SuiteReport.new(name, time) unless name=~/\// # ignore if its a file path
143
+
144
+ when /Test Suite '(\S+)'.*finished at\s+(.*)./
145
+ @reports.last.finish(Time.parse($2))
146
+
147
+ when /Test Case '-\[\S+\s+(\S+)\]' started./
148
+ test = CaseReport.new($1)
149
+ @reports.last.tests << test
150
+
151
+ when /Test Case '-\[\S+\s+(\S+)\]' passed \((.*) seconds\)/
152
+ @reports.last.tests.last.passed($2.to_f)
153
+
154
+ when /(.*): error: -\[(\S+) (\S+)\] : (.*)/
155
+ @reports.last.tests.last.error(error_message,error_location)
156
+ @exit_code = 1 # should terminate
157
+
158
+ when /Test Case '-\[\S+ (\S+)\]' failed \((\S+) seconds\)/
159
+ @reports.last.tests.last.failed($2.to_f)
160
+ @exit_code = 1 # should terminate
161
+
162
+ when /failed with exit code (\d+)/
163
+ @exit_code = $1.to_i
164
+
165
+ when
166
+ /BUILD FAILED/
167
+ @exit_code = -1;
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,37 @@
1
+ require 'rest-client'
2
+ require 'json'
3
+
4
+ module Xcode
5
+ class Testflight
6
+ attr_accessor :api_token, :team_token, :notify, :proxy, :notes, :lists
7
+
8
+ def initialize(api_token, team_token)
9
+ @api_token = api_token
10
+ @team_token = team_token
11
+ @notify = true
12
+ @notes = nil
13
+ @lists = []
14
+ end
15
+
16
+ def upload(ipa_path, dsymzip_path=nil)
17
+ # cmd = []
18
+ RestClient.proxy = @proxy || ENV['http_proxy'] || ENV['HTTP_PROXY']
19
+ RestClient.log = '/tmp/restclient.log'
20
+
21
+ puts "Uploading to Testflight..."
22
+ response = RestClient.post('http://testflightapp.com/api/builds.json',
23
+ :file => File.new(ipa_path),
24
+ :dsym => File.new(dsymzip_path),
25
+ :api_token => @api_token,
26
+ :team_token => @team_token,
27
+ :notes => @notes,
28
+ :notify => @notify ? 'True' : 'False',
29
+ :distribution_lists => @lists.join(',')
30
+ )
31
+
32
+ json = JSON.parse(response)
33
+ puts " + Done, got: #{json.inspect}"
34
+ json
35
+ end
36
+ end
37
+ end
data/lib/xcode/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Xcode
2
- VERSION = "0.0.17"
2
+ VERSION = "0.0.18"
3
3
  end
@@ -0,0 +1,85 @@
1
+ require 'rspec'
2
+ require 'xcoder'
3
+
4
+ describe Xcode::Test::ReportParser do
5
+
6
+ def example_report
7
+ t = Xcode::Test::ReportParser.new
8
+ t << "Run test suite AnExampleTestSuite"
9
+ t << "Test Suite 'AnExampleTestSuite' started at 2012-02-10 00:37:04 +0000"
10
+
11
+ t << "Run test case anExampleTest1"
12
+ t << "Test Case '-[AnExampleTestSuite anExampleTest1]' started."
13
+ t << "Test Case '-[AnExampleTestSuite anExampleTest1]' passed (0.003 seconds)."
14
+
15
+ t << "Run test case anExampleTest2"
16
+ t << "Test Case '-[AnExampleTestSuite anExampleTest2]' started."
17
+ t << "Test Case '-[AnExampleTestSuite anExampleTest2]' passed (0.003 seconds)."
18
+
19
+ yield(t) if block_given?
20
+
21
+ t << "Test Suite 'AnExampleTestSuite' finished at 2012-02-10 00:37:04 +0000."
22
+ t << "Executed 1 test, with 0 failures (0 unexpected) in 0.000 (0.000) seconds"
23
+
24
+ t
25
+ end
26
+
27
+ def example_failing_report
28
+ example_report do |t|
29
+ t << "Test Case '-[AnExampleTestSuite aFailingTest]' started."
30
+ t << "Test Case '-[AnExampleTestSuite aFailingTest]' failed (2 seconds)."
31
+ end
32
+ end
33
+
34
+ it "should create a test case" do
35
+ t = example_report
36
+ t.reports.count.should==1
37
+ t.reports.first.name.should=="AnExampleTestSuite"
38
+ t.reports.first.start_time.should==Time.parse("2012-02-10 00:37:04 +0000")
39
+ t.reports.first.end_time.should==Time.parse("2012-02-10 00:37:04 +0000")
40
+ end
41
+
42
+ it "should set the exist status to 0" do
43
+ t = example_report
44
+ t.exit_code.should==0
45
+ end
46
+
47
+ it "should set the exit status to non 0" do
48
+ t = example_failing_report
49
+ t.exit_code.should_not==0
50
+ end
51
+
52
+ it "should record a failure" do
53
+ t = example_failing_report
54
+ t.reports.first.total_failed_tests.should==1
55
+ end
56
+
57
+ it "should create a test case with some tests" do
58
+ t = example_report
59
+
60
+ t.reports.count.should==1
61
+ t.reports.first.tests.count.should==2
62
+ t.reports.first.tests[0].name.should=='anExampleTest1'
63
+ t.reports.first.tests[0].time.should==0.003
64
+ t.reports.first.tests[0].passed?.should==true
65
+
66
+ t.reports.first.tests[1].name.should=='anExampleTest2'
67
+ t.reports.first.tests[1].time.should==0.003
68
+ t.reports.first.tests[1].passed?.should==true
69
+ end
70
+
71
+ it "should write out reports in junit format" do
72
+ report_dir = "#{File.dirname(__FILE__)}/test-reports"
73
+ FileUtils.rm_rf report_dir
74
+
75
+ t = example_report
76
+ t.write(report_dir, :junit)
77
+
78
+ files = Dir["#{report_dir}/*.xml"]
79
+ files.count.should==1
80
+ files.first.should=~/TEST-AnExampleTestSuite.xml$/
81
+
82
+ # FIXME: parse the report
83
+ end
84
+
85
+ end
data/xcoder.gemspec CHANGED
@@ -21,4 +21,6 @@ Gem::Specification.new do |s|
21
21
  s.add_runtime_dependency "json"
22
22
  s.add_runtime_dependency "plist"
23
23
  s.add_runtime_dependency "nokogiri"
24
+ s.add_runtime_dependency "builder"
25
+ s.add_runtime_dependency "rest-client"
24
26
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xcoder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.17
4
+ version: 0.0.18
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-06 00:00:00.000000000Z
12
+ date: 2012-02-10 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
16
- requirement: &70168698844980 !ruby/object:Gem::Requirement
16
+ requirement: &70260058216360 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70168698844980
24
+ version_requirements: *70260058216360
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: plist
27
- requirement: &70168698844560 !ruby/object:Gem::Requirement
27
+ requirement: &70260058215940 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70168698844560
35
+ version_requirements: *70260058215940
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: nokogiri
38
- requirement: &70168698844140 !ruby/object:Gem::Requirement
38
+ requirement: &70260058215520 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,29 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70168698844140
46
+ version_requirements: *70260058215520
47
+ - !ruby/object:Gem::Dependency
48
+ name: builder
49
+ requirement: &70260058215100 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70260058215100
58
+ - !ruby/object:Gem::Dependency
59
+ name: rest-client
60
+ requirement: &70260058214680 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *70260058214680
47
69
  description: Provides a ruby based object-model for parsing project structures and
48
70
  invoking builds
49
71
  email:
@@ -69,6 +91,8 @@ files:
69
91
  - lib/xcode/scheme.rb
70
92
  - lib/xcode/shell.rb
71
93
  - lib/xcode/target.rb
94
+ - lib/xcode/test/report_parser.rb
95
+ - lib/xcode/testflight.rb
72
96
  - lib/xcode/version.rb
73
97
  - lib/xcode/workspace.rb
74
98
  - lib/xcoder.rb
@@ -96,6 +120,7 @@ files:
96
120
  - spec/project_spec.rb
97
121
  - spec/scheme_spec.rb
98
122
  - spec/target_spec.rb
123
+ - spec/test_report_spec.rb
99
124
  - spec/workspace_spec.rb
100
125
  - xcoder.gemspec
101
126
  homepage: https://github.com/rayh/xcoder
@@ -147,4 +172,5 @@ test_files:
147
172
  - spec/project_spec.rb
148
173
  - spec/scheme_spec.rb
149
174
  - spec/target_spec.rb
175
+ - spec/test_report_spec.rb
150
176
  - spec/workspace_spec.rb