innertube 1.0.1 → 1.0.2
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/lib/innertube.rb +1 -1
- data/lib/innertube/version.rb +1 -1
- data/spec/innertube_spec.rb +252 -152
- data/spec/spec_helper.rb +5 -2
- data/spec/support/timeout.rb +15 -0
- data/spec/support/verbose_formatter.rb +112 -0
- metadata +6 -2
data/lib/innertube.rb
CHANGED
@@ -153,6 +153,7 @@ module Innertube
|
|
153
153
|
@iterator.synchronize do
|
154
154
|
until targets.empty?
|
155
155
|
@lock.synchronize do
|
156
|
+
@element_released.wait(@iterator) if targets.all? {|e| e.locked? }
|
156
157
|
unlocked, targets = targets.partition {|e| e.unlocked? }
|
157
158
|
unlocked.each {|e| e.lock }
|
158
159
|
end
|
@@ -164,7 +165,6 @@ module Innertube
|
|
164
165
|
e.unlock
|
165
166
|
end
|
166
167
|
end
|
167
|
-
@element_released.wait(@iterator) unless targets.empty?
|
168
168
|
end
|
169
169
|
end
|
170
170
|
end
|
data/lib/innertube/version.rb
CHANGED
data/spec/innertube_spec.rb
CHANGED
@@ -1,103 +1,102 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'thread'
|
3
|
+
require 'thwait'
|
3
4
|
|
4
5
|
describe Innertube::Pool do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
described_class.new(lambda { [0] }, lambda { |x| })
|
6
|
+
def wait_all(threads)
|
7
|
+
message "Waiting on #{threads.size} threads: "
|
8
|
+
ThreadsWait.all_waits(*threads) do |t|
|
9
|
+
message "<#{threads.index(t) + 1}> "
|
10
10
|
end
|
11
|
+
message "\n"
|
12
|
+
end
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
let(:pool_members) { pool.instance_variable_get(:@pool) }
|
15
|
+
|
16
|
+
let(:pool) { described_class.new(lambda { [0] }, lambda { |x| }) }
|
17
|
+
|
18
|
+
it 'yields a new object when the pool is empty' do
|
19
|
+
pool.take do |x|
|
20
|
+
x.should == [0]
|
16
21
|
end
|
22
|
+
end
|
17
23
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
24
|
+
it 'retains a single object for serial access' do
|
25
|
+
n = 100
|
26
|
+
n.times do |i|
|
27
|
+
pool.take do |x|
|
28
|
+
x.should == [i]
|
29
|
+
x[0] += 1
|
25
30
|
end
|
26
|
-
subject.size.should == 1
|
27
31
|
end
|
32
|
+
pool.size.should == 1
|
33
|
+
end
|
28
34
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
35
|
+
it 'should be re-entrant' do
|
36
|
+
n = 10
|
37
|
+
n.times do |i|
|
38
|
+
pool.take do |x|
|
39
|
+
x.replace [1]
|
40
|
+
pool.take do |y|
|
41
|
+
y.replace [2]
|
42
|
+
pool.take do |z|
|
43
|
+
z.replace [3]
|
44
|
+
pool.take do |t|
|
45
|
+
t.replace [4]
|
41
46
|
end
|
42
47
|
end
|
43
48
|
end
|
44
49
|
end
|
45
|
-
subject.instance_variable_get(:@pool).map { |e| e.object.first }.sort.should == [1,2,3,4]
|
46
50
|
end
|
51
|
+
pool_members.map { |e| e.object.first }.sort.should == [1,2,3,4]
|
52
|
+
end
|
47
53
|
|
48
54
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
55
|
+
it 'should unlock when exceptions are raised' do
|
56
|
+
begin
|
57
|
+
pool.take do |x|
|
58
|
+
x << 1
|
59
|
+
pool.take do |y|
|
60
|
+
x << 2
|
61
|
+
y << 3
|
62
|
+
raise
|
58
63
|
end
|
59
|
-
rescue
|
60
64
|
end
|
61
|
-
|
62
|
-
pool_members.map { |e| e.object }.should =~ [[0,1,2],[0,3]]
|
65
|
+
rescue
|
63
66
|
end
|
67
|
+
pool_members.should be_all {|e| not e.owner }
|
68
|
+
pool_members.map { |e| e.object }.should =~ [[0,1,2],[0,3]]
|
69
|
+
end
|
64
70
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
m
|
71
|
-
end,
|
72
|
-
lambda do |res|
|
73
|
-
res.close
|
74
|
-
end)
|
75
|
-
end
|
71
|
+
context 'when BadResource is raised' do
|
72
|
+
let(:pool) do
|
73
|
+
described_class.new(lambda { mock('resource').tap {|m| m.should_receive(:close) } },
|
74
|
+
lambda { |res| res.close })
|
75
|
+
end
|
76
76
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
end
|
77
|
+
it 'should remove the member from the pool' do
|
78
|
+
lambda do
|
79
|
+
pool.take do |x|
|
80
|
+
raise Innertube::Pool::BadResource
|
81
|
+
end
|
82
|
+
end.should raise_error(Innertube::Pool::BadResource)
|
83
|
+
pool_members.size.should == 0
|
85
84
|
end
|
86
85
|
end
|
87
86
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
}
|
87
|
+
|
88
|
+
context 'threaded access' do
|
89
|
+
let!(:pool) { described_class.new(lambda { [] }, lambda { |x| }) }
|
92
90
|
|
93
91
|
it 'should allocate n objects for n concurrent operations' do
|
94
92
|
# n threads concurrently allocate and sign objects from the pool
|
95
93
|
n = 10
|
96
94
|
readyq = Queue.new
|
97
95
|
finishq = Queue.new
|
96
|
+
|
98
97
|
threads = (0...n).map do
|
99
98
|
Thread.new do
|
100
|
-
|
99
|
+
pool.take do |x|
|
101
100
|
readyq << 1
|
102
101
|
x << Thread.current
|
103
102
|
finishq.pop
|
@@ -105,16 +104,18 @@ describe Innertube::Pool do
|
|
105
104
|
end
|
106
105
|
end
|
107
106
|
|
107
|
+
# Give the go-ahead to all threads
|
108
108
|
n.times { readyq.pop }
|
109
|
+
|
110
|
+
# Let all threads finish
|
109
111
|
n.times { finishq << 1 }
|
110
112
|
|
111
113
|
# Wait for completion
|
112
|
-
threads
|
113
|
-
t.join
|
114
|
-
end
|
114
|
+
ThreadsWait.all_waits(*threads)
|
115
115
|
|
116
116
|
# Should have taken exactly n objects to do this
|
117
|
-
|
117
|
+
pool_members.size.should == n
|
118
|
+
|
118
119
|
# And each one should be signed exactly once
|
119
120
|
pool_members.map do |e|
|
120
121
|
e.object.size.should == 1
|
@@ -124,16 +125,17 @@ describe Innertube::Pool do
|
|
124
125
|
|
125
126
|
it 'take with filter and default' do
|
126
127
|
n = 10
|
127
|
-
|
128
|
+
pool = described_class.new(lambda { [] }, lambda { |x| })
|
128
129
|
|
129
130
|
# Allocate several elements of the pool
|
130
131
|
q = Queue.new
|
132
|
+
finishq = Queue.new
|
131
133
|
threads = (0...n).map do |i|
|
132
134
|
Thread.new do
|
133
|
-
|
134
|
-
a << i
|
135
|
+
pool.take do |a|
|
135
136
|
q << 1
|
136
|
-
|
137
|
+
a << i
|
138
|
+
finishq.pop
|
137
139
|
end
|
138
140
|
end
|
139
141
|
end
|
@@ -141,29 +143,32 @@ describe Innertube::Pool do
|
|
141
143
|
# Wait for all threads to have acquired an element
|
142
144
|
n.times { q.pop }
|
143
145
|
|
144
|
-
threads
|
145
|
-
|
146
|
-
|
146
|
+
# Let all threads finish
|
147
|
+
n.times { finishq << 1 }
|
148
|
+
|
149
|
+
# Wait for completion
|
150
|
+
# threads.each {|t| t.join }
|
151
|
+
ThreadsWait.all_waits(*threads)
|
147
152
|
|
148
153
|
# Get and delete existing even elements
|
149
|
-
got =
|
154
|
+
got = []
|
150
155
|
(n / 2).times do
|
151
156
|
begin
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
157
|
+
pool.take(
|
158
|
+
:filter => lambda { |x| x.first.even? },
|
159
|
+
:default => [:default]
|
160
|
+
) do |x|
|
156
161
|
got << x.first
|
157
162
|
raise Innertube::Pool::BadResource
|
158
163
|
end
|
159
164
|
rescue Innertube::Pool::BadResource
|
160
165
|
end
|
161
166
|
end
|
162
|
-
got.should
|
167
|
+
got.should =~ (0...n).select(&:even?)
|
163
168
|
|
164
169
|
# This time, no even elements exist, so we should get the default.
|
165
|
-
|
166
|
-
|
170
|
+
pool.take(:filter => lambda { |x| x.first.even? },
|
171
|
+
:default => :default) do |x|
|
167
172
|
x.should == :default
|
168
173
|
end
|
169
174
|
end
|
@@ -174,7 +179,7 @@ describe Innertube::Pool do
|
|
174
179
|
threads = (0..n).map do
|
175
180
|
Thread.new do
|
176
181
|
psleep = 0.75 * rand # up to 50ms sleep
|
177
|
-
|
182
|
+
pool.take do |a|
|
178
183
|
started << 1
|
179
184
|
a << rand
|
180
185
|
sleep psleep
|
@@ -185,114 +190,209 @@ describe Innertube::Pool do
|
|
185
190
|
n.times { started.pop }
|
186
191
|
touched = []
|
187
192
|
|
188
|
-
|
189
|
-
touched << e
|
190
|
-
end
|
193
|
+
pool.each {|e| touched << e }
|
191
194
|
|
192
|
-
threads
|
193
|
-
t.join
|
194
|
-
end
|
195
|
+
wait_all threads
|
195
196
|
|
196
|
-
touched.should be_all {|item| pool_members.
|
197
|
+
touched.should be_all {|item| pool_members.any? {|e| e.object == item } }
|
197
198
|
end
|
198
199
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
)
|
200
|
+
context 'clearing the pool' do
|
201
|
+
let(:pool) do
|
202
|
+
described_class.new(lambda { mock('connection').tap {|m| m.should_receive(:teardown) }},
|
203
|
+
lambda { |b| b.teardown })
|
204
|
+
end
|
205
205
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
206
|
+
it 'should remove all elements' do
|
207
|
+
n = 10
|
208
|
+
q, fq = Queue.new, Queue.new
|
209
|
+
|
210
|
+
# Allocate several elements of the pool
|
211
|
+
threads = (0...n).map do |i|
|
212
|
+
Thread.new do
|
213
|
+
pool.take do |a|
|
214
|
+
q << i
|
215
|
+
sleep(rand * 0.5)
|
216
|
+
message "W<#{i}> "
|
217
|
+
fq.pop
|
218
|
+
message "X<#{i}> "
|
219
|
+
end
|
213
220
|
end
|
214
221
|
end
|
215
|
-
end
|
216
222
|
|
217
|
-
|
218
|
-
|
223
|
+
# Wait for all threads to have acquired an element
|
224
|
+
n.times { message "S<#{q.pop}> " }
|
225
|
+
|
226
|
+
# Start a thread to push stuff onto the finish queue, allowing
|
227
|
+
# the worker threads to exit
|
228
|
+
pusher = Thread.new do
|
229
|
+
n.times do |i|
|
230
|
+
message "R<#{i}> "
|
231
|
+
fq << 1
|
232
|
+
sleep(rand * 0.1)
|
233
|
+
end
|
234
|
+
end
|
219
235
|
|
220
|
-
|
221
|
-
|
222
|
-
|
236
|
+
# Clear the pool while threads still have elements checked out
|
237
|
+
message "S<C> "
|
238
|
+
pool.clear
|
239
|
+
message "X<C> "
|
223
240
|
|
224
|
-
|
225
|
-
|
226
|
-
|
241
|
+
# Wait for threads to complete
|
242
|
+
wait_all(threads + [pusher])
|
243
|
+
pool_members.should be_empty
|
227
244
|
end
|
228
245
|
end
|
229
246
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
247
|
+
context 'conditionally deleting members' do
|
248
|
+
let(:pool) { described_class.new( lambda { [] }, lambda { |x| } ) }
|
249
|
+
it 'should remove them from the pool' do
|
250
|
+
n = 10
|
251
|
+
|
252
|
+
# Allocate several elements of the pool
|
253
|
+
q = Queue.new
|
254
|
+
threads = (0...n).map do |i|
|
255
|
+
Thread.new do
|
256
|
+
pool.take do |a|
|
257
|
+
message "S<#{i}> "
|
258
|
+
a << i
|
259
|
+
q << i
|
260
|
+
Thread.pass
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
236
264
|
|
237
|
-
|
238
|
-
|
265
|
+
# Wait for all threads to have acquired an element
|
266
|
+
n.times { message "X<#{q.pop}> " }
|
267
|
+
|
268
|
+
# Delete odd elements
|
269
|
+
pool.delete_if do |x|
|
270
|
+
x.first.odd?
|
271
|
+
end
|
272
|
+
|
273
|
+
# Verify odds are gone.
|
274
|
+
pool_members.all? do |x|
|
275
|
+
x.object.first.even?
|
276
|
+
end.should == true
|
277
|
+
|
278
|
+
# Wait for threads
|
279
|
+
wait_all threads
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'iteration race-condition regression', :timeout => 60 do
|
284
|
+
# This simulates a race-condition where the condition variable
|
285
|
+
# waited on by the iterator until an element is released might
|
286
|
+
# be signaled before the iterator begins waiting, thus dropping
|
287
|
+
# the signal and sending the iterator into an infinite wait.
|
288
|
+
|
289
|
+
# First we pick a largish random thread count, and split it into
|
290
|
+
# threads that release before the iterator starts (split) and
|
291
|
+
# ones that release while the iterator is busy (rest).
|
292
|
+
n = rand(250)
|
293
|
+
split = rand(n)
|
294
|
+
rest = n - split
|
295
|
+
|
296
|
+
message "[#{n}:#{split}] "
|
297
|
+
# We use two queues to signal between the main thread and the
|
298
|
+
# workers, and a queue to communicate with the iterator thread
|
299
|
+
sq, fq, iq = Queue.new, Queue.new, Queue.new
|
300
|
+
|
301
|
+
# Start all the worker threads
|
239
302
|
threads = (0...n).map do |i|
|
240
303
|
Thread.new do
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
304
|
+
pool.take do |e|
|
305
|
+
# Signal to the main thread that we're inside the take
|
306
|
+
sq << i+1
|
307
|
+
# Block waiting on the main thread. When reactivated, log
|
308
|
+
# the exit of the thread
|
309
|
+
fq.pop
|
310
|
+
message "X<#{i+1}> "
|
311
|
+
sq << Thread.current
|
245
312
|
end
|
246
313
|
end
|
247
314
|
end
|
248
315
|
|
249
|
-
# Wait for all
|
250
|
-
n.times {
|
316
|
+
# Wait for all workers to start up, log their startup to the console
|
317
|
+
n.times { message "S<#{sq.pop}> " }
|
251
318
|
|
252
|
-
|
253
|
-
subject.delete_if do |x|
|
254
|
-
x.first.odd?
|
255
|
-
end
|
319
|
+
message "[all started] "
|
256
320
|
|
257
|
-
#
|
258
|
-
|
259
|
-
|
260
|
-
|
321
|
+
# Now signal for the first group to continue
|
322
|
+
finished = []
|
323
|
+
split.times { fq << 1; finished << sq.pop }
|
324
|
+
wait_all finished
|
261
325
|
|
262
|
-
|
263
|
-
|
264
|
-
|
326
|
+
message "[first group #{split}] "
|
327
|
+
|
328
|
+
# Start the iterator thread
|
329
|
+
iterator = Thread.new do
|
330
|
+
Thread.current[:wait] = true
|
331
|
+
pool.each do |e|
|
332
|
+
# Block in the first iteration so the other workers can exit
|
333
|
+
# while the iterator is not waiting on the condition variable
|
334
|
+
if Thread.current[:wait]
|
335
|
+
sq << 'i'
|
336
|
+
iq.pop
|
337
|
+
Thread.current[:wait] = false
|
338
|
+
end
|
339
|
+
# Make sure we've touched every element of the pool by
|
340
|
+
# modifying every entry.
|
341
|
+
e << 1
|
342
|
+
end
|
343
|
+
message "X<i> "
|
265
344
|
end
|
345
|
+
|
346
|
+
# Wait on the iterator thread to start
|
347
|
+
message "S<#{sq.pop}> "
|
348
|
+
|
349
|
+
# Now signal the remaining workers to finish, and wait on all
|
350
|
+
# workers to exit (even ones that exited in the first pass)
|
351
|
+
finished.clear
|
352
|
+
rest.times { fq << 1; finished << sq.pop }
|
353
|
+
wait_all(finished)
|
354
|
+
|
355
|
+
message "[second group #{rest}] "
|
356
|
+
|
357
|
+
# Now signal the iterator to continue, and wait for it to exit
|
358
|
+
iq << 1
|
359
|
+
wait_all([ iterator ])
|
360
|
+
|
361
|
+
# Finally, verify that all elements of the pool were touched by
|
362
|
+
# the iterator
|
363
|
+
pool_members.each {|e| e.object.size.should == 1 }
|
266
364
|
end
|
267
365
|
|
268
|
-
it 'stress test', :
|
269
|
-
n =
|
270
|
-
|
271
|
-
|
272
|
-
|
366
|
+
it 'stress test', :timeout => 60 do
|
367
|
+
n = rand(400)
|
368
|
+
passes = rand(20)
|
369
|
+
rounds = rand(200)
|
370
|
+
breaker = rand
|
371
|
+
message "[#{n}t:#{rounds}r:#{passes}p:#{'%0.5f' % breaker}b] "
|
273
372
|
|
274
373
|
threads = (0...n).map do
|
275
374
|
Thread.new do
|
276
375
|
rounds.times do |i|
|
277
|
-
|
376
|
+
pool.take do |a|
|
278
377
|
a.should == []
|
279
378
|
a << Thread.current
|
280
379
|
a.should == [Thread.current]
|
281
380
|
|
282
|
-
#
|
283
|
-
|
284
|
-
|
381
|
+
# Pass and check
|
382
|
+
passes.times do
|
383
|
+
Thread.pass
|
384
|
+
# Nobody else should get ahold of this while I'm idle
|
285
385
|
a.should == [Thread.current]
|
386
|
+
break if rand > breaker
|
286
387
|
end
|
287
388
|
|
288
389
|
a.delete Thread.current
|
390
|
+
message "."
|
289
391
|
end
|
290
392
|
end
|
291
393
|
end
|
292
394
|
end
|
293
|
-
threads
|
294
|
-
t.join
|
295
|
-
end
|
395
|
+
wait_all threads
|
296
396
|
end
|
297
397
|
end
|
298
398
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,13 +4,16 @@ require 'rubygems'
|
|
4
4
|
require 'rspec'
|
5
5
|
require 'innertube'
|
6
6
|
|
7
|
+
require 'support/verbose_formatter'
|
8
|
+
require 'support/timeout'
|
9
|
+
|
7
10
|
RSpec.configure do |config|
|
8
11
|
config.mock_with :rspec
|
9
12
|
config.filter_run :focus => true
|
10
13
|
config.run_all_when_everything_filtered = true
|
11
|
-
|
12
14
|
if defined?(::Java)
|
13
|
-
|
15
|
+
seed = Time.now.utc
|
16
|
+
config.seed = seed
|
14
17
|
else
|
15
18
|
config.order = :random
|
16
19
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
RSpec.configure do |config|
|
4
|
+
config.include Timeout
|
5
|
+
config.around(:each) do |example|
|
6
|
+
time = example.metadata[:timeout] || 30
|
7
|
+
begin
|
8
|
+
timeout(time, Timeout::Error) do
|
9
|
+
example.run
|
10
|
+
end
|
11
|
+
rescue Timeout::Error => e
|
12
|
+
example.send :set_exception, e
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'rspec/core/formatters/base_text_formatter'
|
2
|
+
|
3
|
+
class VerboseFormatter < RSpec::Core::Formatters::BaseTextFormatter
|
4
|
+
attr_reader :column, :current_indentation
|
5
|
+
def initialize(*args)
|
6
|
+
super
|
7
|
+
@mutex = Mutex.new
|
8
|
+
@column = @current_indentation = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
def start(count)
|
12
|
+
super
|
13
|
+
output.puts
|
14
|
+
output.puts "Running suite with seed #{RSpec.configuration.seed}\n"
|
15
|
+
output.puts
|
16
|
+
end
|
17
|
+
|
18
|
+
def example_group_started(example_group)
|
19
|
+
super
|
20
|
+
# $stderr.puts example_group.metadata.inspect
|
21
|
+
output.puts "#{padding}#{example_group.metadata[:example_group][:description_args].first}"
|
22
|
+
indent!
|
23
|
+
end
|
24
|
+
|
25
|
+
def example_group_finished(example_group)
|
26
|
+
super
|
27
|
+
outdent!
|
28
|
+
end
|
29
|
+
|
30
|
+
def example_started(example)
|
31
|
+
output.puts "#{padding}#{example.description}:"
|
32
|
+
indent!
|
33
|
+
end
|
34
|
+
|
35
|
+
def message(m)
|
36
|
+
@mutex.synchronize do
|
37
|
+
messages = m.split(/\r?\n/).reject {|s| s.empty? }
|
38
|
+
messages.each do |message|
|
39
|
+
if column + message.length > max_columns
|
40
|
+
output.puts
|
41
|
+
@column = current_indentation
|
42
|
+
end
|
43
|
+
if at_left_margin?
|
44
|
+
output.print "#{padding}#{message}"
|
45
|
+
else
|
46
|
+
output.print message
|
47
|
+
end
|
48
|
+
@column += message.length
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def example_passed(example)
|
54
|
+
super
|
55
|
+
print_example_result green("PASS")
|
56
|
+
end
|
57
|
+
|
58
|
+
def example_failed(example)
|
59
|
+
super
|
60
|
+
print_example_result red("FAIL")
|
61
|
+
end
|
62
|
+
|
63
|
+
def example_pending(example)
|
64
|
+
super
|
65
|
+
print_example_result yellow("PENDING: #{example.metadata[:execution_result][:pending_message]}")
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
def print_example_result(text)
|
70
|
+
output.puts unless at_left_margin?
|
71
|
+
output.puts "#{padding}#{text}"
|
72
|
+
output.puts
|
73
|
+
outdent!
|
74
|
+
end
|
75
|
+
|
76
|
+
def at_left_margin?
|
77
|
+
column == current_indentation
|
78
|
+
end
|
79
|
+
|
80
|
+
def max_columns
|
81
|
+
@max_columns ||= ENV.include?('COLUMNS') ? ENV['COLUMNS'].to_i : 72
|
82
|
+
end
|
83
|
+
|
84
|
+
def indent_width
|
85
|
+
2
|
86
|
+
end
|
87
|
+
|
88
|
+
def padding
|
89
|
+
' ' * current_indentation
|
90
|
+
end
|
91
|
+
|
92
|
+
def indent!
|
93
|
+
@current_indentation += indent_width
|
94
|
+
@column = @current_indentation
|
95
|
+
end
|
96
|
+
|
97
|
+
def outdent!
|
98
|
+
@current_indentation -= indent_width
|
99
|
+
@column = @current_indentation
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
module ExposeFormatter
|
104
|
+
def message(string)
|
105
|
+
RSpec.configuration.formatters.first.message(string)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
RSpec.configure do |config|
|
110
|
+
config.include ExposeFormatter
|
111
|
+
config.add_formatter VerboseFormatter
|
112
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: innertube
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-06
|
13
|
+
date: 2012-07-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rspec
|
@@ -44,6 +44,8 @@ files:
|
|
44
44
|
- README.md
|
45
45
|
- spec/innertube_spec.rb
|
46
46
|
- spec/spec_helper.rb
|
47
|
+
- spec/support/timeout.rb
|
48
|
+
- spec/support/verbose_formatter.rb
|
47
49
|
- .gitignore
|
48
50
|
homepage: http://github.com/basho/innertube
|
49
51
|
licenses: []
|
@@ -72,4 +74,6 @@ summary: A thread-safe resource pool, originally borne in riak-client (Ripple).
|
|
72
74
|
test_files:
|
73
75
|
- spec/innertube_spec.rb
|
74
76
|
- spec/spec_helper.rb
|
77
|
+
- spec/support/timeout.rb
|
78
|
+
- spec/support/verbose_formatter.rb
|
75
79
|
- .gitignore
|