mac_bacon 1.2.1 → 1.3
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/ChangeLog +18 -0
- data/RDOX +43 -15
- data/{README → README.md} +142 -48
- data/Rakefile +4 -4
- data/TODO +2 -0
- data/lib/mac_bacon.rb +124 -21
- data/test/spec_helper.rb +26 -0
- data/test/spec_mac_bacon.rb +195 -10
- metadata +8 -19
data/ChangeLog
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
Sat Mar 12 15:47:27 2011 +0100 Eloy Duran <eloy.de.enige@gmail.com>
|
2
|
+
* Filter the backtraces.
|
3
|
+
|
4
|
+
Sat Mar 12 15:34:41 2011 +0100 Eloy Duran <eloy.de.enige@gmail.com>
|
5
|
+
* Postponing blocks until a KVO message is received should work from before and after filters as well.
|
6
|
+
|
7
|
+
Sat Mar 12 15:06:26 2011 +0100 Eloy Duran <eloy.de.enige@gmail.com>
|
8
|
+
* Postponing blocks until Context#resume is called should work from before and after filters as well.
|
9
|
+
|
10
|
+
Sat Mar 12 14:52:41 2011 +0100 Eloy Duran <eloy.de.enige@gmail.com>
|
11
|
+
* Postponing blocks for a fixed period of time should work from before and after filters as well.
|
12
|
+
|
13
|
+
Sat Mar 12 13:57:01 2011 +0100 Eloy Duran <eloy.de.enige@gmail.com>
|
14
|
+
* Add Context#wait_for_change(object_to_observe, key_path, timeout = 1), which resumes the postponed block until a KVO message is received.
|
15
|
+
|
16
|
+
Sat Mar 12 12:41:10 2011 +0100 Eloy Duran <eloy.de.enige@gmail.com>
|
17
|
+
* Postpone a block until either Context#resume is called or the timeout has exceeded. The default timeout is 1s, or can be given by using Context#wait_max instead of Context#wait.
|
18
|
+
|
1
19
|
Mon Feb 28 12:34:55 2011 +0100 Klaas Speller <klaasspeller@gmail.com>
|
2
20
|
* Exit with other status than 0 on fails or errors
|
3
21
|
|
data/RDOX
CHANGED
@@ -55,8 +55,36 @@ describe arguments
|
|
55
55
|
- should work with multiple arguments
|
56
56
|
|
57
57
|
NSRunloop aware Bacon
|
58
|
+
|
59
|
+
concerning `wait' with a fixed time
|
58
60
|
- allows the user to postpone execution of a block for n seconds, which will halt any further execution of specs
|
59
61
|
|
62
|
+
concerning `wait' without a fixed time
|
63
|
+
- allows the user to postpone execution of a block until Context#resume is called, from for instance a delegate callback
|
64
|
+
- has a default timeout of 1 second after which the spec will fail and further scheduled calls to the Context are cancelled [FAILED]
|
65
|
+
- takes an explicit timeout [FAILED]
|
66
|
+
|
67
|
+
concerning `wait_for_change'
|
68
|
+
- resumes the postponed block once an observed value changes
|
69
|
+
- has a default timeout of 1 second [FAILED]
|
70
|
+
- takes an explicit timeout [FAILED]
|
71
|
+
|
72
|
+
postponing blocks should work from before/after filters as well
|
73
|
+
|
74
|
+
with `wait'
|
75
|
+
|
76
|
+
and an explicit time
|
77
|
+
- starts later because of postponed blocks in the before filter
|
78
|
+
- starts even later because of the postponed blocks in the after filter
|
79
|
+
|
80
|
+
and without explicit time
|
81
|
+
- starts later because of postponed blocks in the before filter
|
82
|
+
- starts even later because of the postponed blocks in the after filter
|
83
|
+
|
84
|
+
with `wait_for_change'
|
85
|
+
- starts later because of postponed blocks in the before filter
|
86
|
+
- starts even later because of the postponed blocks in the after filter
|
87
|
+
|
60
88
|
Nib helper
|
61
89
|
- takes a NIB path and instantiates the NIB with the given `owner' object
|
62
90
|
- also returns an array or other top level objects
|
@@ -84,18 +112,18 @@ Bacon::Context
|
|
84
112
|
|
85
113
|
Bacon::Context empty
|
86
114
|
|
87
|
-
Bacon::Error: not #<Proc:
|
88
|
-
/Users/eloy/
|
89
|
-
/Users/eloy/
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
/Users/eloy/
|
97
|
-
|
98
|
-
|
99
|
-
/Users/eloy/
|
100
|
-
|
101
|
-
|
115
|
+
Bacon::Error: not #<Proc:0x200485980 (lambda)>.raise?(Bacon::Error) failed
|
116
|
+
/Users/eloy/code/MacRuby/MacBacon/test/spec_bacon.rb:9:in `block': Bacon - should have should.be.close
|
117
|
+
/Users/eloy/code/MacRuby/MacBacon/test/spec_bacon.rb:182:in `block'
|
118
|
+
|
119
|
+
Bacon::Error: timeout exceeded: concerning `wait' without a fixed time - has a default timeout of 1 second after which the spec will fail and further scheduled calls to the Context are cancelled
|
120
|
+
|
121
|
+
Bacon::Error: timeout exceeded: concerning `wait' without a fixed time - takes an explicit timeout
|
122
|
+
|
123
|
+
Bacon::Error: timeout exceeded: concerning `wait_for_change' - has a default timeout of 1 second
|
124
|
+
/Users/eloy/code/MacRuby/MacBacon/test/spec_helper.rb:23:in `finish_spec': concerning `wait_for_change' - has a default timeout of 1 second
|
125
|
+
|
126
|
+
Bacon::Error: timeout exceeded: concerning `wait_for_change' - takes an explicit timeout
|
127
|
+
/Users/eloy/code/MacRuby/MacBacon/test/spec_helper.rb:23:in `finish_spec': concerning `wait_for_change' - takes an explicit timeout
|
128
|
+
|
129
|
+
64 specifications (411 requirements), 1 failures, 0 errors
|
data/{README → README.md}
RENAMED
@@ -1,6 +1,7 @@
|
|
1
|
-
|
1
|
+
MacBacon -- small RSpec clone.
|
2
|
+
------------------------------
|
2
3
|
|
3
|
-
|
4
|
+
"Truth will sooner come out from error than from confusion."
|
4
5
|
---Francis Bacon
|
5
6
|
|
6
7
|
Bacon is a small RSpec clone weighing less than 350 LoC but
|
@@ -9,9 +10,10 @@ nevertheless providing all essential features.
|
|
9
10
|
This MacBacon fork is created and maintained by Eloy Durán (@alloy).
|
10
11
|
It differs with regular Bacon in that it operates properly in a
|
11
12
|
NSRunloop based environment. I.e. MacRuby/Objective-C. See the
|
12
|
-
|
13
|
+
Objective-C runloop macros section for more info.
|
13
14
|
|
14
|
-
|
15
|
+
Whirl-wind tour
|
16
|
+
===============
|
15
17
|
|
16
18
|
require 'mac_bacon'
|
17
19
|
|
@@ -135,7 +137,8 @@ As of Bacon 1.1, it also supports Knock:
|
|
135
137
|
(knock is available from http://github.com/chneukirchen/knock/)
|
136
138
|
|
137
139
|
|
138
|
-
|
140
|
+
Implemented assertions
|
141
|
+
======================
|
139
142
|
|
140
143
|
* should.<predicate> and should.be.<predicate>
|
141
144
|
* should.equal
|
@@ -147,7 +150,8 @@ As of Bacon 1.1, it also supports Knock:
|
|
147
150
|
* should.satisfy { |object| }
|
148
151
|
|
149
152
|
|
150
|
-
|
153
|
+
Added core predicates
|
154
|
+
=====================
|
151
155
|
|
152
156
|
* Object#true?
|
153
157
|
* Object#false?
|
@@ -157,7 +161,8 @@ As of Bacon 1.1, it also supports Knock:
|
|
157
161
|
* Numeric#close?
|
158
162
|
|
159
163
|
|
160
|
-
|
164
|
+
before/after
|
165
|
+
============
|
161
166
|
|
162
167
|
before and after need to be defined before the first specification in
|
163
168
|
a context and are run before and after each specification.
|
@@ -165,7 +170,8 @@ a context and are run before and after each specification.
|
|
165
170
|
As of Bacon 1.1, before and after do nest in nested contexts.
|
166
171
|
|
167
172
|
|
168
|
-
|
173
|
+
Shared contexts
|
174
|
+
===============
|
169
175
|
|
170
176
|
You can define shared contexts in Bacon like this:
|
171
177
|
|
@@ -186,59 +192,132 @@ behaves_like in other contexts. You can use shared contexts to
|
|
186
192
|
structure suites with many recurring specifications.
|
187
193
|
|
188
194
|
|
189
|
-
|
195
|
+
Matchers
|
196
|
+
========
|
190
197
|
|
191
198
|
Custom matchers are simply lambdas returning a boolean value, for
|
192
199
|
example:
|
193
200
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
201
|
+
def shorter_than(max_size)
|
202
|
+
lambda { |obj| obj.size < max_size }
|
203
|
+
end
|
204
|
+
|
205
|
+
[1,2,3].should.be shorter_than(5)
|
199
206
|
|
200
207
|
You can use modules and extend to group matchers for use in multiple
|
201
208
|
contexts.
|
202
209
|
|
203
210
|
|
204
|
-
|
211
|
+
Objective-C runloop macros
|
212
|
+
==========================
|
213
|
+
|
205
214
|
|
206
215
|
Often in Objective-C apps, code will *not* execute immediately, but
|
207
216
|
scheduled on a runloop for later execution. Therefor a mechanism is
|
208
|
-
|
209
|
-
time. This is where the `wait` macro comes in:
|
217
|
+
provided that will postpone execution of blocks for a period of time.
|
210
218
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
219
|
+
You can event nest these blocks. However, with the exception of `wait`
|
220
|
+
with an explicit time, you can *not* have multiple at the same time.
|
221
|
+
|
222
|
+
All these macros may be used in before and after filters as well.
|
223
|
+
|
224
|
+
|
225
|
+
### `wait` with fixed period of time
|
226
|
+
|
227
|
+
it 'should perform a long running operation' do
|
228
|
+
# Here a method call is scheduled to be performed ~0.5 seconds in the future
|
229
|
+
@ary.performSelector("addObject:", withObject:"soup", afterDelay:0.5)
|
230
|
+
wait 0.6 do
|
231
|
+
# This block is executed ~0.6 seconds in the future
|
232
|
+
@ary.size.should.be 1
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
|
237
|
+
### `wait` without fixed period of time, until `resume` is called
|
238
|
+
|
239
|
+
By default this usage of `wait` will wait for 1 second. If `resume`
|
240
|
+
has not been called by that time, the spec fails. If you want to
|
241
|
+
specify the timeout use `wait_max(timeout, &block)` instead.
|
242
|
+
|
243
|
+
def aDelegateCallbackMethod(sender)
|
244
|
+
@delegateCallbackMethodCalled = true
|
245
|
+
resume
|
246
|
+
end
|
247
|
+
|
248
|
+
it 'should wait until notified' do
|
249
|
+
# Here a method is called that in the near future will result in the object calling back the delegate
|
250
|
+
@object.delegate = self
|
251
|
+
@object.startLongRunningMethod
|
252
|
+
wait do
|
253
|
+
# This block is executed once aDelegateCallbackMethod is called
|
254
|
+
@delegateCallbackMethodCalled.should == true
|
218
255
|
end
|
256
|
+
end
|
257
|
+
|
219
258
|
|
220
|
-
|
221
|
-
the runloop as well. This means that your runloop based code will have
|
222
|
-
a chance to perform its job before the assertions in the block are
|
223
|
-
executed.
|
259
|
+
### `wait_for_change` (Key-Value Observing)
|
224
260
|
|
225
|
-
|
261
|
+
This macro makes the specification an observer of the key path of the
|
262
|
+
given object for the duration of the specification.
|
226
263
|
|
264
|
+
By default this usage of `wait_for_change` will wait for 1 second. If
|
265
|
+
the KVO message has not arrived by that time, the spec fails. If you
|
266
|
+
want to specify the timeout use
|
267
|
+
`wait_for_change(observable, key_path, timeout)` instead.
|
227
268
|
|
228
|
-
|
269
|
+
class AKeyValueObservableClass
|
270
|
+
attr_accessor :an_attribute
|
229
271
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
-o, --output FORMAT do FORMAT (SpecDox/TestUnit/Tap) output
|
235
|
-
-Q, --no-backtrace don't print backtraces
|
236
|
-
-a, --automatic gather tests from ./test/, include ./lib/
|
237
|
-
-n, --name NAME runs tests matching regexp NAME
|
238
|
-
-t, --testcase TESTCASE runs tests in TestCases matching regexp TESTCASE
|
272
|
+
def compute_an_attribute
|
273
|
+
# trust me, this takes a few ms
|
274
|
+
end
|
275
|
+
end
|
239
276
|
|
277
|
+
it 'should wait until AKeyValueObservableClass#an_attribute changes' do
|
278
|
+
# Here a method is called that in the near future will update the 'an_attribute' value of the object
|
279
|
+
observable.compute_an_attribute
|
280
|
+
wait_for_change observable, 'an_attribute' do
|
281
|
+
# This block is executed once 'an_attribute' has changed value
|
282
|
+
observable.an_attribute.should == 'changed'
|
283
|
+
end
|
284
|
+
end
|
240
285
|
|
241
|
-
|
286
|
+
|
287
|
+
Load NIBs
|
288
|
+
=========
|
289
|
+
|
290
|
+
In case you have a NIB that defines the UI for the controller you're testing,
|
291
|
+
then you can use the `load_nib` method to easily do so:
|
292
|
+
|
293
|
+
describe "PreferencesController" do
|
294
|
+
before do
|
295
|
+
@controller = PreferencesController.new
|
296
|
+
nib_path = File.join(SRC_ROOT, 'app/views/PreferencesWindow.xib')
|
297
|
+
@top_level_objects = load_nib(nib_path, @controller)
|
298
|
+
end
|
299
|
+
|
300
|
+
# tests...
|
301
|
+
|
302
|
+
end
|
303
|
+
|
304
|
+
|
305
|
+
bacon standalone runner
|
306
|
+
=======================
|
307
|
+
|
308
|
+
-s, --specdox do AgileDox-like output (default)
|
309
|
+
-q, --quiet do Test::Unit-like non-verbose output
|
310
|
+
-p, --tap do TAP (Test Anything Protocol) output
|
311
|
+
-k, --knock do Knock output
|
312
|
+
-o, --output FORMAT do FORMAT (SpecDox/TestUnit/Tap) output
|
313
|
+
-Q, --no-backtrace don't print backtraces
|
314
|
+
-a, --automatic gather tests from ./test/, include ./lib/
|
315
|
+
-n, --name NAME runs tests matching regexp NAME
|
316
|
+
-t, --testcase TESTCASE runs tests in TestCases matching regexp TESTCASE
|
317
|
+
|
318
|
+
|
319
|
+
Object#should
|
320
|
+
=============
|
242
321
|
|
243
322
|
You can use Object#should outside of contexts, where the result of
|
244
323
|
assertion will be returned as a boolean. This is nice for
|
@@ -251,7 +330,8 @@ demonstrations, quick checks and doctest tests.
|
|
251
330
|
=> false
|
252
331
|
|
253
332
|
|
254
|
-
|
333
|
+
Converting specs
|
334
|
+
================
|
255
335
|
|
256
336
|
spec-converter is a simple tool to convert test-unit or dust style
|
257
337
|
tests to test/spec specs.
|
@@ -259,7 +339,8 @@ tests to test/spec specs.
|
|
259
339
|
It can be found at http://opensource.thinkrelevance.com/wiki/spec_converter.
|
260
340
|
|
261
341
|
|
262
|
-
|
342
|
+
Thanks to
|
343
|
+
=========
|
263
344
|
|
264
345
|
* Michael Fellinger, for fixing Bacon for 1.9 and various improvements.
|
265
346
|
* Gabriele Renzi, for implementing Context#should.
|
@@ -268,7 +349,8 @@ It can be found at http://opensource.thinkrelevance.com/wiki/spec_converter.
|
|
268
349
|
* everyone contributing bug fixes.
|
269
350
|
|
270
351
|
|
271
|
-
|
352
|
+
History
|
353
|
+
=======
|
272
354
|
|
273
355
|
* January 7, 2008: First public release 0.9.
|
274
356
|
|
@@ -288,10 +370,17 @@ It can be found at http://opensource.thinkrelevance.com/wiki/spec_converter.
|
|
288
370
|
|
289
371
|
* January 10th, 2011: MacBacon fork release 1.1
|
290
372
|
* Make it work in a NSRunloop environment
|
291
|
-
* Add `wait
|
373
|
+
* Add `wait`
|
292
374
|
* Remove extras, for now
|
293
375
|
|
294
|
-
|
376
|
+
* March 12th, 2011: MacBacon fork release 1.3
|
377
|
+
* Add NIB helper
|
378
|
+
* exit with non-zero status when there were failures/errors
|
379
|
+
* Add `wait` without explicit time
|
380
|
+
* Add `wait_for_change`
|
381
|
+
|
382
|
+
Contact
|
383
|
+
=======
|
295
384
|
|
296
385
|
Please mail bugs, suggestions and patches for Bacon to
|
297
386
|
<mailto:chneukirchen@gmail.com>
|
@@ -307,18 +396,23 @@ And repository location:
|
|
307
396
|
https://github.com/alloy/MacBacon
|
308
397
|
git://github.com/alloy/MacBacon.git
|
309
398
|
|
310
|
-
|
399
|
+
Copying
|
400
|
+
=======
|
311
401
|
|
312
|
-
Copyright (C) 2007
|
402
|
+
Copyright (C) 2007 - 2011 Christian Neukirchen <purl.org/net/chneukirchen>
|
403
|
+
Copyright (C) 2011 Eloy Durán <eloy.de.enige@gmail.com>
|
313
404
|
|
314
405
|
Bacon is freely distributable under the terms of an MIT-style license.
|
315
406
|
See COPYING or http://www.opensource.org/licenses/mit-license.php.
|
316
407
|
|
317
408
|
|
318
|
-
|
409
|
+
Links
|
410
|
+
=====
|
319
411
|
|
320
412
|
Behavior-Driven Development:: <http://behaviour-driven.org/>
|
321
413
|
RSpec:: <http://rspec.rubyforge.org/>
|
322
414
|
test/spec:: <http://test-spec.rubyforge.org/>
|
323
415
|
|
324
416
|
Christian Neukirchen:: <http://chneukirchen.org/>
|
417
|
+
Eloy Durán:: <http://soup.superalloy.nl/>
|
418
|
+
|
data/Rakefile
CHANGED
@@ -27,7 +27,7 @@ def git_tree_version
|
|
27
27
|
#$: << "lib"
|
28
28
|
#require 'mac_bacon'
|
29
29
|
#@tree_version = Bacon::VERSION
|
30
|
-
@tree_version = "1.
|
30
|
+
@tree_version = "1.3"
|
31
31
|
end
|
32
32
|
@tree_version
|
33
33
|
end
|
@@ -116,7 +116,7 @@ https://github.com/alloy/MacBacon
|
|
116
116
|
s.executables << 'macbacon'
|
117
117
|
s.require_path = 'lib'
|
118
118
|
s.has_rdoc = true
|
119
|
-
s.extra_rdoc_files = ['README', 'RDOX']
|
119
|
+
s.extra_rdoc_files = ['README.md', 'RDOX']
|
120
120
|
s.test_files = []
|
121
121
|
|
122
122
|
s.author = 'Eloy Durán'
|
@@ -137,11 +137,11 @@ end
|
|
137
137
|
desc "Generate RDoc documentation"
|
138
138
|
Rake::RDocTask.new(:rdoc) do |rdoc|
|
139
139
|
rdoc.options << '--line-numbers' << '--inline-source' <<
|
140
|
-
'--main' << 'README' <<
|
140
|
+
'--main' << 'README.md' <<
|
141
141
|
'--title' << 'Bacon Documentation' <<
|
142
142
|
'--charset' << 'utf-8'
|
143
143
|
rdoc.rdoc_dir = "doc"
|
144
|
-
rdoc.rdoc_files.include 'README'
|
144
|
+
rdoc.rdoc_files.include 'README.md'
|
145
145
|
rdoc.rdoc_files.include 'COPYING'
|
146
146
|
rdoc.rdoc_files.include 'RDOX'
|
147
147
|
rdoc.rdoc_files.include('lib/mac_bacon.rb')
|
data/lib/mac_bacon.rb
CHANGED
@@ -11,7 +11,7 @@ framework "Cocoa"
|
|
11
11
|
require "mac_bacon/helpers"
|
12
12
|
|
13
13
|
module Bacon
|
14
|
-
VERSION = "1.
|
14
|
+
VERSION = "1.3"
|
15
15
|
|
16
16
|
Counter = Hash.new(0)
|
17
17
|
ErrorLog = ""
|
@@ -137,31 +137,43 @@ module Bacon
|
|
137
137
|
@before_filters, @after_filters = before_filters.dup, after_filters.dup
|
138
138
|
|
139
139
|
@postponed_blocks_count = 0
|
140
|
+
@ran_spec_block = false
|
141
|
+
@ran_after_filters = false
|
140
142
|
@exception_occurred = false
|
141
143
|
@error = ""
|
142
144
|
end
|
143
145
|
|
146
|
+
def postponed?
|
147
|
+
@postponed_blocks_count != 0
|
148
|
+
end
|
149
|
+
|
144
150
|
def run_before_filters
|
145
|
-
@before_filters.each { |f| @context.instance_eval(&f) }
|
151
|
+
execute_block { @before_filters.each { |f| @context.instance_eval(&f) } }
|
152
|
+
end
|
153
|
+
|
154
|
+
def run_spec_block
|
155
|
+
@ran_spec_block = true
|
156
|
+
# If an exception occurred, we definitely don't need to perform the actual spec anymore
|
157
|
+
unless @exception_occurred
|
158
|
+
execute_block { @context.instance_eval(&@block) }
|
159
|
+
end
|
160
|
+
finish_spec unless postponed?
|
146
161
|
end
|
147
162
|
|
148
163
|
def run_after_filters
|
149
|
-
@
|
164
|
+
@ran_after_filters = true
|
165
|
+
execute_block { @after_filters.each { |f| @context.instance_eval(&f) } }
|
150
166
|
end
|
151
167
|
|
152
168
|
def run
|
153
169
|
Bacon.handle_requirement_begin(@description)
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
@context.instance_eval(&@block)
|
159
|
-
end
|
160
|
-
|
161
|
-
finalize if @postponed_blocks_count == 0
|
170
|
+
Counter[:depth] += 1
|
171
|
+
run_before_filters
|
172
|
+
@number_of_requirements_before = Counter[:requirements]
|
173
|
+
run_spec_block unless postponed?
|
162
174
|
end
|
163
175
|
|
164
|
-
def
|
176
|
+
def schedule_block(seconds, &block)
|
165
177
|
# If an exception occurred, we definitely don't need to schedule any more blocks
|
166
178
|
unless @exception_occurred
|
167
179
|
@postponed_blocks_count += 1
|
@@ -169,22 +181,97 @@ module Bacon
|
|
169
181
|
end
|
170
182
|
end
|
171
183
|
|
184
|
+
def postpone_block(timeout = 1, &block)
|
185
|
+
# If an exception occurred, we definitely don't need to schedule any more blocks
|
186
|
+
unless @exception_occurred
|
187
|
+
if @postponed_block
|
188
|
+
raise "Only one indefinite `wait' block at the same time is allowed!"
|
189
|
+
else
|
190
|
+
@postponed_blocks_count += 1
|
191
|
+
@postponed_block = block
|
192
|
+
performSelector("postponed_block_timeout_exceeded", withObject:nil, afterDelay:timeout)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def postpone_block_until_change(object_to_observe, key_path, timeout = 1, &block)
|
198
|
+
# If an exception occurred, we definitely don't need to schedule any more blocks
|
199
|
+
unless @exception_occurred
|
200
|
+
if @postponed_block
|
201
|
+
raise "Only one indefinite `wait' block at the same time is allowed!"
|
202
|
+
else
|
203
|
+
@postponed_blocks_count += 1
|
204
|
+
@postponed_block = block
|
205
|
+
@observed_object_and_key_path = [object_to_observe, key_path]
|
206
|
+
object_to_observe.addObserver(self, forKeyPath:key_path, options:0, context:nil)
|
207
|
+
performSelector("postponed_change_block_timeout_exceeded", withObject:nil, afterDelay:timeout)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def observeValueForKeyPath(key_path, ofObject:object, change:_, context:__)
|
213
|
+
resume
|
214
|
+
end
|
215
|
+
|
216
|
+
def postponed_change_block_timeout_exceeded
|
217
|
+
remove_observer!
|
218
|
+
postponed_block_timeout_exceeded
|
219
|
+
end
|
220
|
+
|
221
|
+
def remove_observer!
|
222
|
+
if @observed_object_and_key_path
|
223
|
+
object, key_path = @observed_object_and_key_path
|
224
|
+
object.removeObserver(self, forKeyPath:key_path)
|
225
|
+
@observed_object_and_key_path = nil
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def postponed_block_timeout_exceeded
|
230
|
+
cancel_scheduled_requests!
|
231
|
+
execute_block { raise Error.new(:failed, "timeout exceeded: #{@context.name} - #{@description}") }
|
232
|
+
@postponed_blocks_count = 0
|
233
|
+
finish_spec
|
234
|
+
end
|
235
|
+
|
236
|
+
def resume
|
237
|
+
NSObject.cancelPreviousPerformRequestsWithTarget(self, selector:'postponed_block_timeout_exceeded', object:nil)
|
238
|
+
NSObject.cancelPreviousPerformRequestsWithTarget(self, selector:'postponed_change_block_timeout_exceeded', object:nil)
|
239
|
+
remove_observer!
|
240
|
+
block, @postponed_block = @postponed_block, nil
|
241
|
+
run_postponed_block(block)
|
242
|
+
end
|
243
|
+
|
172
244
|
def run_postponed_block(block)
|
173
245
|
# If an exception occurred, we definitely don't need execute any more blocks
|
174
246
|
execute_block(&block) unless @exception_occurred
|
175
247
|
@postponed_blocks_count -= 1
|
176
|
-
|
248
|
+
unless postponed?
|
249
|
+
if @ran_after_filters
|
250
|
+
exit_spec
|
251
|
+
elsif @ran_spec_block
|
252
|
+
finish_spec
|
253
|
+
else
|
254
|
+
run_spec_block
|
255
|
+
end
|
256
|
+
end
|
177
257
|
end
|
178
258
|
|
179
|
-
def
|
180
|
-
if Counter[:requirements] == @number_of_requirements_before
|
259
|
+
def finish_spec
|
260
|
+
if !@exception_occurred && Counter[:requirements] == @number_of_requirements_before
|
181
261
|
# the specification did not contain any requirements, so it flunked
|
182
|
-
# TODO ugh, exceptions for control flow, need to clean this up
|
183
262
|
execute_block { raise Error.new(:missing, "empty specification: #{@context.name} #{@description}") }
|
184
263
|
end
|
264
|
+
run_after_filters
|
265
|
+
exit_spec unless postponed?
|
266
|
+
end
|
185
267
|
|
186
|
-
|
268
|
+
def cancel_scheduled_requests!
|
269
|
+
NSObject.cancelPreviousPerformRequestsWithTarget(@context)
|
270
|
+
NSObject.cancelPreviousPerformRequestsWithTarget(self)
|
271
|
+
end
|
187
272
|
|
273
|
+
def exit_spec
|
274
|
+
cancel_scheduled_requests!
|
188
275
|
Counter[:depth] -= 1
|
189
276
|
Bacon.handle_requirement_end(@error)
|
190
277
|
@context.specification_did_finish(self)
|
@@ -197,8 +284,8 @@ module Bacon
|
|
197
284
|
@exception_occurred = true
|
198
285
|
|
199
286
|
ErrorLog << "#{e.class}: #{e.message}\n"
|
200
|
-
e.backtrace.find_all { |line| line !~ /bin\/
|
201
|
-
|
287
|
+
lines = $DEBUG ? e.backtrace : e.backtrace.find_all { |line| line !~ /bin\/macbacon|\/mac_bacon\.rb:\d+/ }
|
288
|
+
lines.each_with_index { |line, i|
|
202
289
|
ErrorLog << "\t#{line}#{i==0 ? ": #{@context.name} - #{@description}" : ""}\n"
|
203
290
|
}
|
204
291
|
ErrorLog << "\n"
|
@@ -315,8 +402,24 @@ module Bacon
|
|
315
402
|
context
|
316
403
|
end
|
317
404
|
|
318
|
-
def wait(seconds, &block)
|
319
|
-
|
405
|
+
def wait(seconds = nil, &block)
|
406
|
+
if seconds
|
407
|
+
current_specification.schedule_block(seconds, &block)
|
408
|
+
else
|
409
|
+
current_specification.postpone_block(&block)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
def wait_max(timeout, &block)
|
414
|
+
current_specification.postpone_block(timeout, &block)
|
415
|
+
end
|
416
|
+
|
417
|
+
def wait_for_change(object_to_observe, key_path, timeout = 1, &block)
|
418
|
+
current_specification.postpone_block_until_change(object_to_observe, key_path, timeout, &block)
|
419
|
+
end
|
420
|
+
|
421
|
+
def resume
|
422
|
+
current_specification.resume
|
320
423
|
end
|
321
424
|
|
322
425
|
def raise?(*args, &block); block.raise?(*args); end
|
data/test/spec_helper.rb
CHANGED
@@ -1,2 +1,28 @@
|
|
1
1
|
$:.unshift File.expand_path('../../lib', __FILE__)
|
2
2
|
require 'mac_bacon'
|
3
|
+
|
4
|
+
module Bacon
|
5
|
+
class Specification
|
6
|
+
alias_method :_real_finish_spec, :finish_spec
|
7
|
+
end
|
8
|
+
|
9
|
+
class Context
|
10
|
+
def failures_before
|
11
|
+
@failures_before
|
12
|
+
end
|
13
|
+
|
14
|
+
def expect_spec_to_fail!
|
15
|
+
@failures_before = Bacon::Counter[:failed]
|
16
|
+
Bacon::Specification.class_eval do
|
17
|
+
def finish_spec
|
18
|
+
@exception_occurred.should == true
|
19
|
+
@exception_occurred = nil
|
20
|
+
Bacon::Counter[:failed].should == @context.failures_before + 1
|
21
|
+
Bacon::Counter[:failed] = @context.failures_before
|
22
|
+
self.class.class_eval { alias_method :finish_spec, :_real_finish_spec }
|
23
|
+
_real_finish_spec
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/test/spec_mac_bacon.rb
CHANGED
@@ -1,19 +1,204 @@
|
|
1
1
|
require File.expand_path('../spec_helper', __FILE__)
|
2
2
|
|
3
|
+
class MockObservable
|
4
|
+
attr_accessor :an_attribute
|
5
|
+
end
|
6
|
+
|
3
7
|
describe "NSRunloop aware Bacon" do
|
4
|
-
|
5
|
-
|
6
|
-
|
8
|
+
describe "concerning `wait' with a fixed time" do
|
9
|
+
it "allows the user to postpone execution of a block for n seconds, which will halt any further execution of specs" do
|
10
|
+
started_at_1 = started_at_2 = started_at_3 = Time.now
|
11
|
+
number_of_specs_before = Bacon::Counter[:specifications]
|
12
|
+
|
13
|
+
wait 0.5 do
|
14
|
+
(Time.now - started_at_1).should.be.close(0.5, 0.5)
|
15
|
+
end
|
16
|
+
wait 1 do
|
17
|
+
(Time.now - started_at_2).should.be.close(1, 0.5)
|
18
|
+
wait 1.5 do
|
19
|
+
(Time.now - started_at_3).should.be.close(2.5, 0.5)
|
20
|
+
Bacon::Counter[:specifications].should == number_of_specs_before
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "concerning `wait' without a fixed time" do
|
27
|
+
def delegateCallbackMethod
|
28
|
+
@delegateCallbackCalled = true
|
29
|
+
resume
|
30
|
+
end
|
31
|
+
|
32
|
+
it "allows the user to postpone execution of a block until Context#resume is called, from for instance a delegate callback" do
|
33
|
+
performSelector('delegateCallbackMethod', withObject:nil, afterDelay:0.1)
|
34
|
+
@delegateCallbackCalled.should == nil
|
35
|
+
wait do
|
36
|
+
@delegateCallbackCalled.should == true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def delegateCallbackTookTooLongMethod
|
41
|
+
raise "Oh noes, I must never be called!"
|
42
|
+
end
|
43
|
+
|
44
|
+
# This spec adds a failure to the ErrorLog!
|
45
|
+
it "has a default timeout of 1 second after which the spec will fail and further scheduled calls to the Context are cancelled" do
|
46
|
+
expect_spec_to_fail!
|
47
|
+
performSelector('delegateCallbackTookTooLongMethod', withObject:nil, afterDelay:1.2)
|
48
|
+
wait do
|
49
|
+
# we must never arrive here, because the default timeout of 1 second will have passed
|
50
|
+
raise "Oh noes, we shouldn't have arrived in this postponed block!"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# This spec adds a failure to the ErrorLog!
|
55
|
+
it "takes an explicit timeout" do
|
56
|
+
expect_spec_to_fail!
|
57
|
+
performSelector('delegateCallbackTookTooLongMethod', withObject:nil, afterDelay:0.8)
|
58
|
+
wait_max 0.3 do
|
59
|
+
# we must never arrive here, because the default timeout of 1 second will have passed
|
60
|
+
raise "Oh noes, we shouldn't have arrived in this postponed block!"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "concerning `wait_for_change'" do
|
66
|
+
before do
|
67
|
+
@observable = MockObservable.new
|
68
|
+
end
|
69
|
+
|
70
|
+
def triggerChange
|
71
|
+
@observable.an_attribute = 'changed'
|
72
|
+
end
|
73
|
+
|
74
|
+
it "resumes the postponed block once an observed value changes" do
|
75
|
+
wait_for_change @observable, 'an_attribute' do
|
76
|
+
@value = @observable.an_attribute
|
77
|
+
end
|
78
|
+
@value.should == nil
|
79
|
+
performSelector('triggerChange', withObject:nil, afterDelay:0.1)
|
80
|
+
wait 0.2 do
|
81
|
+
@value.should == 'changed'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# This spec adds a failure to the ErrorLog!
|
86
|
+
it "has a default timeout of 1 second" do
|
87
|
+
expect_spec_to_fail!
|
88
|
+
wait_for_change(@observable, 'an_attribute') do
|
89
|
+
raise "Oh noes, I must never be called!"
|
90
|
+
end
|
91
|
+
performSelector('triggerChange', withObject:nil, afterDelay:1.1)
|
92
|
+
wait 1.2 do
|
93
|
+
# we must never arrive here, because the default timeout of 1 second will have passed
|
94
|
+
raise "Oh noes, we shouldn't have arrived in this postponed block!"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# This spec adds a failure to the ErrorLog!
|
99
|
+
it "takes an explicit timeout" do
|
100
|
+
expect_spec_to_fail!
|
101
|
+
wait_for_change(@observable, 'an_attribute', 0.3) do
|
102
|
+
raise "Oh noes, I must never be called!"
|
103
|
+
end
|
104
|
+
performSelector('triggerChange', withObject:nil, afterDelay:0.8)
|
105
|
+
wait 0.9 do
|
106
|
+
# we must never arrive here, because the default timeout of 1 second will have passed
|
107
|
+
raise "Oh noes, we shouldn't have arrived in this postponed block!"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "postponing blocks should work from before/after filters as well" do
|
113
|
+
shared "waiting in before/after filters" do
|
114
|
+
it "starts later because of postponed blocks in the before filter" do
|
115
|
+
(Time.now - @started_at).should.be.close(1, 0.5)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "starts even later because of the postponed blocks in the after filter" do
|
119
|
+
(Time.now - @started_at).should.be.close(3, 0.5)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "with `wait'" do
|
124
|
+
describe "and an explicit time" do
|
125
|
+
before do
|
126
|
+
@started_at ||= Time.now
|
127
|
+
wait 0.5 do
|
128
|
+
wait 0.5 do
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
after do
|
134
|
+
wait 0.5 do
|
135
|
+
wait 0.5 do
|
136
|
+
@time ||= 0
|
137
|
+
@time += 2
|
138
|
+
(Time.now - @started_at).should.be.close(@time, 0.2)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
behaves_like "waiting in before/after filters"
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "and without explicit time" do
|
147
|
+
before do
|
148
|
+
@started_at ||= Time.now
|
149
|
+
performSelector('resume', withObject:nil, afterDelay:0.5)
|
150
|
+
wait do
|
151
|
+
performSelector('resume', withObject:nil, afterDelay:0.5)
|
152
|
+
wait do
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
after do
|
158
|
+
performSelector('resume', withObject:nil, afterDelay:0.5)
|
159
|
+
wait do
|
160
|
+
performSelector('resume', withObject:nil, afterDelay:0.5)
|
161
|
+
wait do
|
162
|
+
@time ||= 0
|
163
|
+
@time += 2
|
164
|
+
(Time.now - @started_at).should.be.close(@time, 0.2)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
7
168
|
|
8
|
-
|
9
|
-
|
169
|
+
behaves_like "waiting in before/after filters"
|
170
|
+
end
|
10
171
|
end
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
172
|
+
|
173
|
+
describe "with `wait_for_change'" do
|
174
|
+
before do
|
175
|
+
@observable = MockObservable.new
|
176
|
+
@started_at ||= Time.now
|
177
|
+
performSelector('triggerChange', withObject:nil, afterDelay:0.5)
|
178
|
+
wait_for_change @observable, 'an_attribute' do
|
179
|
+
performSelector('triggerChange', withObject:nil, afterDelay:0.5)
|
180
|
+
wait_for_change @observable, 'an_attribute' do
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
after do
|
186
|
+
performSelector('triggerChange', withObject:nil, afterDelay:0.5)
|
187
|
+
wait_for_change @observable, 'an_attribute' do
|
188
|
+
performSelector('triggerChange', withObject:nil, afterDelay:0.5)
|
189
|
+
wait_for_change @observable, 'an_attribute' do
|
190
|
+
@time ||= 0
|
191
|
+
@time += 2
|
192
|
+
(Time.now - @started_at).should.be.close(@time, 1)
|
193
|
+
end
|
194
|
+
end
|
16
195
|
end
|
196
|
+
|
197
|
+
def triggerChange
|
198
|
+
@observable.an_attribute = 'changed'
|
199
|
+
end
|
200
|
+
|
201
|
+
behaves_like "waiting in before/after filters"
|
17
202
|
end
|
18
203
|
end
|
19
204
|
end
|
metadata
CHANGED
@@ -1,13 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mac_bacon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease: false
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 2
|
9
|
-
- 1
|
10
|
-
version: 1.2.1
|
4
|
+
version: "1.3"
|
11
5
|
platform: ruby
|
12
6
|
authors:
|
13
7
|
- "Eloy Dur\xC3\xA1n"
|
@@ -15,7 +9,7 @@ autorequire:
|
|
15
9
|
bindir: bin
|
16
10
|
cert_chain: []
|
17
11
|
|
18
|
-
date: 2011-03-
|
12
|
+
date: 2011-03-12 00:00:00 +01:00
|
19
13
|
default_executable:
|
20
14
|
dependencies: []
|
21
15
|
|
@@ -34,12 +28,13 @@ executables:
|
|
34
28
|
extensions: []
|
35
29
|
|
36
30
|
extra_rdoc_files:
|
37
|
-
- README
|
31
|
+
- README.md
|
38
32
|
- RDOX
|
39
33
|
files:
|
40
34
|
- COPYING
|
41
|
-
- README
|
35
|
+
- README.md
|
42
36
|
- Rakefile
|
37
|
+
- TODO
|
43
38
|
- bin/macbacon
|
44
39
|
- lib/mac_bacon.rb
|
45
40
|
- lib/mac_bacon/helpers.rb
|
@@ -63,27 +58,21 @@ rdoc_options: []
|
|
63
58
|
require_paths:
|
64
59
|
- lib
|
65
60
|
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
-
none: false
|
67
61
|
requirements:
|
68
62
|
- - ">="
|
69
63
|
- !ruby/object:Gem::Version
|
70
|
-
hash: 3
|
71
|
-
segments:
|
72
|
-
- 0
|
73
64
|
version: "0"
|
65
|
+
version:
|
74
66
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
-
none: false
|
76
67
|
requirements:
|
77
68
|
- - ">="
|
78
69
|
- !ruby/object:Gem::Version
|
79
|
-
hash: 3
|
80
|
-
segments:
|
81
|
-
- 0
|
82
70
|
version: "0"
|
71
|
+
version:
|
83
72
|
requirements: []
|
84
73
|
|
85
74
|
rubyforge_project:
|
86
|
-
rubygems_version: 1.3.
|
75
|
+
rubygems_version: 1.3.5
|
87
76
|
signing_key:
|
88
77
|
specification_version: 3
|
89
78
|
summary: a small RSpec clone for MacRuby
|