xcodebuild-rb 0.1.0
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/LICENSE +19 -0
- data/README.md +59 -0
- data/bin/rbxcb +3 -0
- data/lib/xcode_build.rb +27 -0
- data/lib/xcode_build/build_action.rb +72 -0
- data/lib/xcode_build/build_step.rb +47 -0
- data/lib/xcode_build/formatters.rb +6 -0
- data/lib/xcode_build/formatters/progress_formatter.rb +99 -0
- data/lib/xcode_build/output_translator.rb +82 -0
- data/lib/xcode_build/reporter.rb +22 -0
- data/lib/xcode_build/reporting/build_reporting.rb +61 -0
- data/lib/xcode_build/reporting/clean_reporting.rb +59 -0
- data/lib/xcode_build/tasks/build_task.rb +84 -0
- data/lib/xcode_build/translations.rb +20 -0
- data/lib/xcode_build/translations/building.rb +101 -0
- data/lib/xcode_build/translations/cleaning.rb +96 -0
- data/lib/xcode_build/utilities/colorize.rb +45 -0
- data/lib/xcodebuild.rb +1 -0
- data/spec/build_task_spec.rb +142 -0
- data/spec/output_translator_spec.rb +52 -0
- data/spec/reporting/build_reporting_spec.rb +305 -0
- data/spec/reporting/clean_reporting_spec.rb +276 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/translations/building_translations_spec.rb +167 -0
- data/spec/translations/cleaning_translations_spec.rb +144 -0
- metadata +164 -0
@@ -0,0 +1,276 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe XcodeBuild::Reporting::CleanReporting do
|
4
|
+
let(:reporter) { XcodeBuild::Reporter.new }
|
5
|
+
|
6
|
+
shared_examples_for "any clean" do
|
7
|
+
it "reports the clean target" do
|
8
|
+
reporter.clean.target.should == "ExampleProject"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "reports the project name" do
|
12
|
+
reporter.clean.project_name.should == "ExampleProject"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "reports the clean configuration" do
|
16
|
+
reporter.clean.configuration.should == "Release"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "reports if the clean configuration was the default" do
|
20
|
+
reporter.clean.should be_default_configuration
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when receiving events" do
|
25
|
+
let(:delegate) { mock('reporter delegate').as_null_object }
|
26
|
+
|
27
|
+
before do
|
28
|
+
reporter.delegate = delegate
|
29
|
+
|
30
|
+
# let's assume it responds to all delegate methods
|
31
|
+
delegate.stub(:respond_to?).with(anything).and_return(true)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "notifies it's delegate that a clean has started" do
|
35
|
+
delegate.should_receive(:clean_started).with instance_of(XcodeBuild::Reporting::CleanReporting::Clean)
|
36
|
+
|
37
|
+
event({:clean_started=>
|
38
|
+
{:target=>"ExampleProject",
|
39
|
+
:project=>"ExampleProject",
|
40
|
+
:configuration=>"Release",
|
41
|
+
:default=>true}})
|
42
|
+
end
|
43
|
+
|
44
|
+
it "notifies it's delegate when a clean step begins" do
|
45
|
+
assume_clean_started
|
46
|
+
|
47
|
+
delegate.should_receive(:clean_step_started).with instance_of(XcodeBuild::BuildStep)
|
48
|
+
|
49
|
+
event({:clean_step=>
|
50
|
+
{:type=>"Clean.Remove",
|
51
|
+
:arguments=>
|
52
|
+
["clean",
|
53
|
+
"build/Release-iphoneos/ExampleProject.app"]}})
|
54
|
+
end
|
55
|
+
|
56
|
+
it "notifies it's delegate when a previous clean step finishes" do
|
57
|
+
assume_clean_started
|
58
|
+
|
59
|
+
event({:clean_step=>
|
60
|
+
{:type=>"Clean.Remove",
|
61
|
+
:arguments=>
|
62
|
+
["clean",
|
63
|
+
"build/Release-iphoneos/ExampleProject.app"]}})
|
64
|
+
|
65
|
+
delegate.should_receive(:clean_step_finished).with reporter.clean.last_step
|
66
|
+
|
67
|
+
event({:clean_step=>
|
68
|
+
{:type=>"Clean.Remove",
|
69
|
+
:arguments=>
|
70
|
+
["clean",
|
71
|
+
"build/Release-iphoneos/ExampleProject.app"]}})
|
72
|
+
end
|
73
|
+
|
74
|
+
it "notifies it's delegate when the last clean step finishes and the clean is successful" do
|
75
|
+
assume_clean_started
|
76
|
+
|
77
|
+
event({:clean_step=>
|
78
|
+
{:type=>"Clean.Remove",
|
79
|
+
:arguments=>
|
80
|
+
["clean",
|
81
|
+
"build/Release-iphoneos/ExampleProject.app"]}})
|
82
|
+
|
83
|
+
delegate.should_receive(:clean_step_finished).with reporter.clean.last_step
|
84
|
+
|
85
|
+
event({:clean_succeeded=>{}})
|
86
|
+
end
|
87
|
+
|
88
|
+
it "notifies it's delegate when the last clean step finishes and the clean fails" do
|
89
|
+
assume_clean_started
|
90
|
+
|
91
|
+
event({:clean_step=>
|
92
|
+
{:type=>"Clean.Remove",
|
93
|
+
:arguments=>
|
94
|
+
["clean",
|
95
|
+
"build/Release-iphoneos/ExampleProject.app"]}})
|
96
|
+
|
97
|
+
delegate.should_receive(:clean_step_finished).with reporter.clean.last_step
|
98
|
+
|
99
|
+
event({:clean_failed=>{}})
|
100
|
+
end
|
101
|
+
|
102
|
+
it "notifies it's delegate that the clean has finished when it is successful" do
|
103
|
+
assume_clean_started
|
104
|
+
delegate.should_receive(:clean_finished).with(reporter.clean)
|
105
|
+
event({:clean_succeeded=>{}})
|
106
|
+
end
|
107
|
+
|
108
|
+
it "notifies it's delegate that the clean has finished when it fails" do
|
109
|
+
assume_clean_started
|
110
|
+
delegate.should_receive(:clean_finished).with(reporter.clean)
|
111
|
+
event({:clean_failed=>{}})
|
112
|
+
end
|
113
|
+
|
114
|
+
it "tracks the time a clean takes" do
|
115
|
+
Timecop.travel(Chronic.parse("10 seconds ago")) do
|
116
|
+
event({:clean_started=>
|
117
|
+
{:target=>"ExampleProject",
|
118
|
+
:project=>"ExampleProject",
|
119
|
+
:configuration=>"Release",
|
120
|
+
:default=>true}})
|
121
|
+
|
122
|
+
Timecop.travel(Chronic.parse("5 seconds from now")) do
|
123
|
+
event({:clean_succeeded=>{}})
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
reporter.clean.duration.should be_within(0.01).of(5)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "once a clean has started" do
|
132
|
+
before do
|
133
|
+
event({:clean_started=>
|
134
|
+
{:target=>"ExampleProject",
|
135
|
+
:project=>"ExampleProject",
|
136
|
+
:configuration=>"Release",
|
137
|
+
:default=>true}})
|
138
|
+
end
|
139
|
+
|
140
|
+
it "reports that the clean is running" do
|
141
|
+
reporter.clean.should be_running
|
142
|
+
end
|
143
|
+
|
144
|
+
it "reports that the clean is not finished" do
|
145
|
+
reporter.clean.should_not be_finished
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context "once a simple, successful clean has finished" do
|
150
|
+
before do
|
151
|
+
event({:clean_started=>
|
152
|
+
{:target=>"ExampleProject",
|
153
|
+
:project=>"ExampleProject",
|
154
|
+
:configuration=>"Release",
|
155
|
+
:default=>true}})
|
156
|
+
|
157
|
+
event({:clean_step=>
|
158
|
+
{:type=>"Clean.Remove",
|
159
|
+
:arguments=>
|
160
|
+
["clean",
|
161
|
+
"build/Release-iphoneos/FileOne"]}})
|
162
|
+
|
163
|
+
event({:clean_step=>
|
164
|
+
{:type=>"Clean.Remove",
|
165
|
+
:arguments=>
|
166
|
+
["clean",
|
167
|
+
"build/Release-iphoneos/FileTwo"]}})
|
168
|
+
|
169
|
+
event({:clean_succeeded=>{}})
|
170
|
+
end
|
171
|
+
|
172
|
+
it_behaves_like "any clean"
|
173
|
+
|
174
|
+
it "reports that the clean was successful" do
|
175
|
+
reporter.clean.should be_successful
|
176
|
+
end
|
177
|
+
|
178
|
+
it "reports the total number of completed clean steps" do
|
179
|
+
reporter.clean.should have(2).steps_completed
|
180
|
+
end
|
181
|
+
|
182
|
+
it "reports that the clean is not running" do
|
183
|
+
reporter.clean.should_not be_running
|
184
|
+
end
|
185
|
+
|
186
|
+
it "reports that the clean is finished" do
|
187
|
+
reporter.clean.should be_finished
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context "once a simple, failed build has finished" do
|
192
|
+
before do
|
193
|
+
event({:clean_started=>
|
194
|
+
{:target=>"ExampleProject",
|
195
|
+
:project=>"ExampleProject",
|
196
|
+
:configuration=>"Release",
|
197
|
+
:default=>true}})
|
198
|
+
|
199
|
+
event({:clean_step=>
|
200
|
+
{:type=>"Clean.Remove",
|
201
|
+
:arguments=>
|
202
|
+
["clean",
|
203
|
+
"build/Release-iphoneos/FileOne"]}})
|
204
|
+
|
205
|
+
event({:clean_error_detected=>
|
206
|
+
{:message=>"Error Domain=NSCocoaErrorDomain Code=513 ExampleProject couldn't be removed"}})
|
207
|
+
|
208
|
+
event({:clean_step=>
|
209
|
+
{:type=>"Clean.Remove",
|
210
|
+
:arguments=>
|
211
|
+
["clean",
|
212
|
+
"build/Release-iphoneos/FileTwo"]}})
|
213
|
+
|
214
|
+
event({:clean_failed=>{}})
|
215
|
+
|
216
|
+
event({:clean_step_failed=>
|
217
|
+
{:type=>"Clean.Remove",
|
218
|
+
:arguments=>
|
219
|
+
["clean",
|
220
|
+
"build/Release-iphoneos/FileOne"]}})
|
221
|
+
end
|
222
|
+
|
223
|
+
it_behaves_like "any clean"
|
224
|
+
|
225
|
+
it "reports that the clean was a failure" do
|
226
|
+
reporter.clean.should be_failed
|
227
|
+
end
|
228
|
+
|
229
|
+
it "reports the total number of completed clean steps" do
|
230
|
+
reporter.clean.should have(2).steps_completed
|
231
|
+
end
|
232
|
+
|
233
|
+
it "reports the total number of failed clean steps" do
|
234
|
+
reporter.clean.should have(1).failed_steps
|
235
|
+
reporter.clean.failed_steps.first.tap do |step|
|
236
|
+
step.type.should == "Clean.Remove"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
it "reports the errors for each failed clean step" do
|
241
|
+
reporter.clean.failed_steps.first.should have(1).errors
|
242
|
+
reporter.clean.failed_steps.first.errors.first.tap do |error|
|
243
|
+
error.message.should == "Error Domain=NSCocoaErrorDomain Code=513 ExampleProject couldn't be removed"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
it "reports that the clean is not running" do
|
248
|
+
reporter.clean.should_not be_running
|
249
|
+
end
|
250
|
+
|
251
|
+
it "reports that the clean is finished" do
|
252
|
+
reporter.clean.should be_finished
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
private
|
257
|
+
|
258
|
+
def event(event_data)
|
259
|
+
message = event_data.keys.first
|
260
|
+
params = event_data.values.first
|
261
|
+
|
262
|
+
if params.any?
|
263
|
+
reporter.send(message, params)
|
264
|
+
else
|
265
|
+
reporter.send(message)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def assume_clean_started
|
270
|
+
event({:clean_started=>
|
271
|
+
{:target=>"ExampleProject",
|
272
|
+
:project=>"ExampleProject",
|
273
|
+
:configuration=>"Release",
|
274
|
+
:default=>true}})
|
275
|
+
end
|
276
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
ENV["LC_CTYPE"] = "en_US.UTF-8"
|
2
|
+
|
3
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
4
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
5
|
+
# Require this file using `require "spec_helper.rb"` to ensure that it is only
|
6
|
+
# loaded once.
|
7
|
+
#
|
8
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
11
|
+
config.run_all_when_everything_filtered = true
|
12
|
+
config.filter_run :focus
|
13
|
+
end
|
14
|
+
|
15
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
16
|
+
|
17
|
+
require 'xcodebuild'
|
18
|
+
require 'chronic'
|
19
|
+
require 'timecop'
|
20
|
+
|
21
|
+
def delegate_should_respond_to(method)
|
22
|
+
mock_should_respond?(delegate, method, true)
|
23
|
+
end
|
24
|
+
|
25
|
+
def delegate_should_not_respond_to(method)
|
26
|
+
mock_should_respond?(delegate, method, false)
|
27
|
+
end
|
28
|
+
|
29
|
+
def mock_should_respond?(mock, method, should_respond)
|
30
|
+
mock.stub(:respond_to?).with(method).and_return(should_respond)
|
31
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe XcodeBuild::Translations::Building do
|
4
|
+
let(:delegate) { mock('delegate', :respond_to? => true) }
|
5
|
+
let(:translator) { XcodeBuild::OutputTranslator.new(delegate, ignore_global_translations: true) }
|
6
|
+
let(:translation) { translator.translations[0] }
|
7
|
+
|
8
|
+
before do
|
9
|
+
translator.use_translation XcodeBuild::Translations::Building
|
10
|
+
|
11
|
+
delegate_should_respond_to(:beginning_translation_of_line)
|
12
|
+
delegate.stub(:beginning_translation_of_line).and_return(true)
|
13
|
+
|
14
|
+
translator.should have(1).translations
|
15
|
+
end
|
16
|
+
|
17
|
+
context "before it detects that a build has started" do
|
18
|
+
it "reports that it is not building" do
|
19
|
+
translation.should_not be_building
|
20
|
+
end
|
21
|
+
|
22
|
+
it "notifies the delegate of the start of a build with the default configuration" do
|
23
|
+
delegate.stub(:beginning_translation_of_line)
|
24
|
+
delegate.should_receive(:build_started).with(
|
25
|
+
target: "ExampleProject",
|
26
|
+
project: "ExampleProject",
|
27
|
+
configuration: "Release",
|
28
|
+
default: true
|
29
|
+
)
|
30
|
+
translator << "=== BUILD NATIVE TARGET ExampleProject OF PROJECT ExampleProject WITH THE DEFAULT CONFIGURATION (Release) ==="
|
31
|
+
end
|
32
|
+
|
33
|
+
it "notifies the delegate of the start of a build with a non-default configuration" do
|
34
|
+
delegate.stub(:beginning_translation_of_line)
|
35
|
+
delegate.should_receive(:build_started).with(
|
36
|
+
target: "ExampleProject",
|
37
|
+
project: "ExampleProject",
|
38
|
+
configuration: "Debug",
|
39
|
+
default: false
|
40
|
+
)
|
41
|
+
translator << "=== BUILD NATIVE TARGET ExampleProject OF PROJECT ExampleProject WITH THE CONFIGURATION Debug ==="
|
42
|
+
end
|
43
|
+
|
44
|
+
it "treats :build_started as a required delegate message and raise if it doesn't respond" do
|
45
|
+
delegate_should_not_respond_to(:build_started)
|
46
|
+
-> {
|
47
|
+
translator << "=== BUILD NATIVE TARGET ExampleProject OF PROJECT ExampleProject WITH THE CONFIGURATION Debug ==="
|
48
|
+
|
49
|
+
}.should raise_error(XcodeBuild::OutputTranslator::MissingDelegateMethodError)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "once a build start has been detected" do
|
54
|
+
before do
|
55
|
+
translation.stub(:building?).and_return(true)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "notifies the delegate of a single build step" do
|
59
|
+
delegate.stub(:beginning_translation_of_line)
|
60
|
+
delegate.should_receive(:build_step).with(
|
61
|
+
type: "CodeSign",
|
62
|
+
arguments: ["build/Debug-iphoneos/ExampleProject.app"]
|
63
|
+
)
|
64
|
+
translator << "\n"
|
65
|
+
translator << "CodeSign build/Debug-iphoneos/ExampleProject.app"
|
66
|
+
end
|
67
|
+
|
68
|
+
it "treats :beginning_translation_of_line as an optional delegate message" do
|
69
|
+
delegate_should_not_respond_to(:build_step)
|
70
|
+
delegate.should_not_receive(:build_step)
|
71
|
+
translator << "\n"
|
72
|
+
translator << "CodeSign build/Debug-iphoneos/ExampleProject.app"
|
73
|
+
end
|
74
|
+
|
75
|
+
it "notifies the delegate when the build failed" do
|
76
|
+
delegate.stub(:beginning_translation_of_line)
|
77
|
+
delegate.should_receive(:build_failed)
|
78
|
+
translator << "\n\n\n"
|
79
|
+
translator << "** BUILD FAILED **"
|
80
|
+
end
|
81
|
+
|
82
|
+
it "treats :build_failed as a required delegate message and raise if it doesn't respond" do
|
83
|
+
delegate_should_not_respond_to(:build_failed)
|
84
|
+
-> {
|
85
|
+
translator << "** BUILD FAILED **"
|
86
|
+
|
87
|
+
}.should raise_error(XcodeBuild::OutputTranslator::MissingDelegateMethodError)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "notifies the delegate when the build succeeded" do
|
91
|
+
delegate.stub(:beginning_translation_of_line)
|
92
|
+
delegate.should_receive(:build_succeeded)
|
93
|
+
translator << "\n\n\n"
|
94
|
+
translator << "** BUILD SUCCEEDED **"
|
95
|
+
end
|
96
|
+
|
97
|
+
it "treats :build_succeeded as a required delegate message and raise if it doesn't respond" do
|
98
|
+
delegate_should_not_respond_to(:build_succeeded)
|
99
|
+
-> {
|
100
|
+
translator << "** BUILD SUCCEEDED **"
|
101
|
+
|
102
|
+
}.should raise_error(XcodeBuild::OutputTranslator::MissingDelegateMethodError)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "notifies the delegate of build step failures" do
|
106
|
+
delegate.should_receive(:build_step_failed).with(
|
107
|
+
type: "CodeSign",
|
108
|
+
arguments: ["build/Debug-iphoneos/ExampleProject.app"]
|
109
|
+
)
|
110
|
+
translator << "The following build commands failed:"
|
111
|
+
translator << "\tCodeSign build/Debug-iphoneos/ExampleProject.app"
|
112
|
+
translator << "(2 failures)"
|
113
|
+
end
|
114
|
+
|
115
|
+
it "treats :build_step_failed as an optional delegate message" do
|
116
|
+
delegate_should_not_respond_to(:build_step_failed)
|
117
|
+
delegate.should_not_receive(:build_step_failed)
|
118
|
+
translator << "The following build commands failed:"
|
119
|
+
translator << "\tCodeSign build/Debug-iphoneos/ExampleProject.app"
|
120
|
+
end
|
121
|
+
|
122
|
+
it "notifies the delegate of errors that occur throughout the build" do
|
123
|
+
delegate.should_receive(:build_error_detected).with(
|
124
|
+
file: "/ExampleProject/main.m",
|
125
|
+
line: 16,
|
126
|
+
char: 42,
|
127
|
+
message: "expected ';' after expression [1]"
|
128
|
+
)
|
129
|
+
translator << "/ExampleProject/main.m:16:42: error: expected ';' after expression [1]"
|
130
|
+
end
|
131
|
+
|
132
|
+
it "notifies the delegate of errors for different build steps" do
|
133
|
+
delegate.should_receive(:build_error_detected).with(
|
134
|
+
file: "/ExampleProject/main.m",
|
135
|
+
line: 16,
|
136
|
+
char: 42,
|
137
|
+
message: "expected ';' after expression [1]"
|
138
|
+
)
|
139
|
+
|
140
|
+
translator << "CompileC ExampleProject/main.m normal"
|
141
|
+
translator << "/ExampleProject/main.m:16:42: error: expected ';' after expression [1]"
|
142
|
+
translator << "1 error generated."
|
143
|
+
end
|
144
|
+
|
145
|
+
it "notifies the delegate of multiple errors for the same build step" do
|
146
|
+
delegate.should_receive(:build_error_detected).with(
|
147
|
+
file: "/ExampleProject/main.m",
|
148
|
+
line: 16,
|
149
|
+
char: 42,
|
150
|
+
message: "expected ';' after expression [1]"
|
151
|
+
).twice
|
152
|
+
|
153
|
+
translator << "CompileC ExampleProject/main.m normal"
|
154
|
+
translator << "/ExampleProject/main.m:16:42: error: expected ';' after expression [1]"
|
155
|
+
translator << ""
|
156
|
+
translator << "/ExampleProject/main.m:16:42: error: expected ';' after expression [1]"
|
157
|
+
translator << ""
|
158
|
+
translator << "2 errors generated."
|
159
|
+
end
|
160
|
+
|
161
|
+
it "treats :build_error_detected as an optional delegate message" do
|
162
|
+
delegate_should_not_respond_to(:build_error_detected)
|
163
|
+
delegate.should_not_receive(:build_error_detected)
|
164
|
+
translator << "/ExampleProject/main.m:16:42: error: expected ';' after expression [1]"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|