progress 2.4.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,321 +1,362 @@
1
- require File.dirname(__FILE__) + '/spec_helper.rb'
1
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
2
+ require 'rspec'
3
+ require 'progress'
2
4
 
3
5
  describe Progress do
4
- let(:count){ 165 }
5
6
 
6
- before :each do
7
- @io = StringIO.new
8
- Progress.instance_variable_set(:@io, @io)
9
- def Progress.time_to_print?; true; end
10
- end
11
-
12
- def io_pop
13
- @io.seek(0)
14
- s = @io.read
15
- @io.truncate(0)
16
- @io.seek(0)
17
- s
18
- end
7
+ before do
8
+ Progress.stay_on_line = true
9
+ Progress.highlight = true
10
+ Progress.set_terminal_title = true
19
11
 
20
- def io_pop_no_eta
21
- io_pop.sub(/ \(ETA: \d+.\)$/, '')
12
+ Progress.stub(:start_beeper)
13
+ Progress.stub(:time_to_print?).and_return(true)
14
+ Progress.stub(:eta)
15
+ Progress.stub(:elapsed).and_return('0s')
22
16
  end
23
17
 
24
- def verify_output_before_step(i, count)
25
- io_pop.should =~ /#{Regexp.quote(i == 0 ? '......' : '%5.1f' % (i / count.to_f * 100.0))}/
26
- end
27
- def verify_output_after_stop
28
- io_pop.should =~ /100\.0.*\n$/
29
- end
18
+ describe "integrity" do
30
19
 
31
- describe 'direct usage' do
32
- describe 'procedural' do
33
- it "should show valid output when called as Progress.start" do
34
- Progress.start('Test', count)
35
- count.times do |i|
36
- verify_output_before_step(i, count)
37
- Progress.step
38
- end
39
- Progress.stop
40
- verify_output_after_stop
41
- end
42
-
43
- it "should show valid output when called as Progress" do
44
- Progress('Test', count)
45
- count.times do |i|
46
- verify_output_before_step(i, count)
47
- Progress.step
48
- end
49
- Progress.stop
50
- verify_output_after_stop
51
- end
20
+ before do
21
+ @io = double(:<< => nil, :tty? => true)
22
+ Progress.stub(:io).and_return(@io)
23
+ end
52
24
 
53
- it "should show valid output when called without title" do
54
- Progress(count)
55
- count.times do |i|
56
- verify_output_before_step(i, count)
57
- Progress.step
58
- end
59
- Progress.stop
60
- verify_output_after_stop
61
- end
25
+ it "should return result from start block" do
26
+ Progress.start('Test') do
27
+ 'test'
28
+ end.should == 'test'
62
29
  end
63
30
 
64
- describe 'block' do
65
- it "should show valid output when called as Progress.start" do
66
- Progress.start('Test', count) do
67
- count.times do |i|
68
- verify_output_before_step(i, count)
69
- Progress.step
70
- end
71
- end
72
- verify_output_after_stop
31
+ it "should return result from step block" do
32
+ Progress.start 1 do
33
+ Progress.step{ 'test' }.should == 'test'
73
34
  end
35
+ end
74
36
 
75
- it "should show valid output when called as Progress" do
76
- Progress('Test', count) do
77
- count.times do |i|
78
- verify_output_before_step(i, count)
79
- Progress.step
80
- end
81
- end
82
- verify_output_after_stop
37
+ it "should return result from set block" do
38
+ Progress.start 1 do
39
+ Progress.set(1){ 'test' }.should == 'test'
83
40
  end
41
+ end
84
42
 
85
- it "should show valid output when called without title" do
86
- Progress(count) do
87
- count.times do |i|
88
- verify_output_before_step(i, count)
89
- Progress.step
90
- end
43
+ it "should return result from nested block" do
44
+ [1, 2, 3].with_progress.map do |a|
45
+ [1, 2, 3].with_progress.map do |b|
46
+ a * b
91
47
  end
92
- verify_output_after_stop
93
- end
48
+ end.should == [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
94
49
  end
95
- end
96
50
 
97
- describe 'integrity' do
98
- it "should not raise errors on extra Progress.stop" do
51
+ it "should not raise errors on extra step or stop" do
99
52
  proc{
100
- 10.times_with_progress('10') do
53
+ 3.times_with_progress do
101
54
  Progress.start 'simple' do
102
- Progress.start 'procedural'
103
- Progress.stop
104
- Progress.stop
55
+ Progress.step
56
+ Progress.step
57
+ Progress.step
105
58
  end
59
+ Progress.step
106
60
  Progress.stop
107
61
  end
62
+ Progress.step
108
63
  Progress.stop
109
64
  }.should_not raise_error
110
65
  end
111
66
 
112
- it "should return result from block" do
113
- Progress.start('Test') do
114
- 'qwerty'
115
- end.should == 'qwerty'
116
- end
67
+ describe Enumerable do
117
68
 
118
- it "should return result from step" do
119
- Progress.start do
120
- Progress.step{ 'qwerty' }.should == 'qwerty'
69
+ before :each do
70
+ @a = 0...1000
121
71
  end
122
- end
123
72
 
124
- it "should return result from set" do
125
- Progress.start do
126
- Progress.set(1){ 'qwerty' }.should == 'qwerty'
127
- end
128
- end
73
+ describe "with_progress" do
129
74
 
130
- it "should return result from nested block" do
131
- [1, 2, 3].with_progress('a').map do |a|
132
- [1, 2, 3].with_progress('b').map do |b|
133
- a * b
75
+ it "should not break each" do
76
+ reference = @a.each
77
+ @a.with_progress.each do |n|
78
+ n.should == reference.next
79
+ end
80
+ proc{ reference.next }.should raise_error(StopIteration)
134
81
  end
135
- end.should == [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
136
- end
137
82
 
138
- it "should kill progress on cycle break" do
139
- 2.times do
140
- catch(:lalala) do
141
- 2.times_with_progress('A') do |a|
142
- io_pop.should == "A: ......\n"
143
- 2.times_with_progress('B') do |b|
144
- io_pop.should == "A: ...... > B: ......\n"
145
- throw(:lalala)
146
- end
147
- end
83
+ it "should not break find" do
84
+ default = proc{ 'default' }
85
+ @a.with_progress.find{ |n| n == 100 }.should == @a.find{ |n| n == 100 }
86
+ @a.with_progress.find{ |n| n == 10000 }.should == @a.find{ |n| n == 10000 }
87
+ @a.with_progress.find(default){ |n| n == 10000 }.should == @a.find(default){ |n| n == 10000 }
88
+ end
89
+
90
+ it "should not break map" do
91
+ @a.with_progress.map{ |n| n * n }.should == @a.map{ |n| n * n }
148
92
  end
149
- io_pop.should == "A: ......\n\n"
150
- end
151
- end
152
93
 
153
- [[2, 200], [20, 20], [200, 2]].each do |_a, _b|
154
- it "should allow enclosed progress [#{_a}, #{_b}]" do
155
- _a.times_with_progress('A') do |a|
156
- io_pop_no_eta.should == "A: #{a == 0 ? '......' : '%5.1f%%'}\n" % [a / _a.to_f * 100.0]
157
- _b.times_with_progress('B') do |b|
158
- io_pop_no_eta.should == "A: #{a == 0 && b == 0 ? '......' : '%5.1f%%'} > B: #{b == 0 ? '......' : '%5.1f%%'}\n" % [(a + b / _b.to_f) / _a.to_f * 100.0, b / _b.to_f * 100.0]
94
+ it "should not break grep" do
95
+ @a.with_progress.grep(100).should == @a.grep(100)
96
+ end
97
+
98
+ it "should not break each_cons" do
99
+ reference = @a.each_cons(3)
100
+ @a.with_progress.each_cons(3) do |values|
101
+ values.should == reference.next
159
102
  end
160
- io_pop_no_eta.should == "A: %5.1f%% > B: 100.0%%\n" % [(a + 1) / _a.to_f * 100.0]
103
+ proc{ reference.next }.should raise_error(StopIteration)
161
104
  end
162
- io_pop.should == "A: 100.0%\nA: 100.0%\n\n"
163
- end
164
105
 
165
- it "should not overlap outer progress if inner exceeds [#{_a}, #{_b}]" do
166
- _a.times_with_progress('A') do |a|
167
- io_pop_no_eta.should == "A: #{a == 0 ? '......' : '%5.1f%%'}\n" % [a / _a.to_f * 100.0]
168
- Progress.start('B', _b) do
169
- (_b * 2).times do |b|
170
- io_pop_no_eta.should == "A: #{a == 0 && b == 0 ? '......' : '%5.1f%%'} > B: #{b == 0 ? '......' : '%5.1f%%'}\n" % [(a + [b / _b.to_f, 1].min) / _a.to_f * 100.0, b / _b.to_f * 100.0]
171
- Progress.step
172
- end
106
+ describe "with_progress.with_progress" do
107
+
108
+ it "should not change existing instance" do
109
+ wp = @a.with_progress('hello')
110
+ proc{ wp.with_progress('world') }.should_not change(wp, :title)
111
+ end
112
+
113
+ it "should create new instance with different title when called on WithProgress" do
114
+ wp = @a.with_progress('hello')
115
+ wp_wp = wp.with_progress('world')
116
+ wp.title.should == 'hello'
117
+ wp_wp.title.should == 'world'
118
+ wp_wp.should_not == wp
119
+ wp_wp.enumerable.should == wp.enumerable
173
120
  end
174
- io_pop_no_eta.should == "A: %5.1f%% > B: 200.0%%\n" % [(a + 1) / _a.to_f * 100.0]
121
+
175
122
  end
176
- io_pop.should == "A: 100.0%\nA: 100.0%\n\n"
177
- end
178
123
 
179
- it "should allow step with block to validly count custom progresses [#{_a}, #{_b}]" do
180
- a_step = 99
181
- Progress.start('A', _a * 100) do
182
- io_pop_no_eta.should == "A: ......\n"
183
- _a.times do |a|
184
- Progress.step(a_step) do
185
- _b.times_with_progress('B') do |b|
186
- io_pop_no_eta.should == "A: #{a == 0 && b == 0 ? '......' : '%5.1f%%'} > B: #{b == 0 ? '......' : '%5.1f%%'}\n" % [(a * a_step + b / _b.to_f * a_step) / (_a * 100).to_f * 100.0, b / _b.to_f * 100.0]
124
+ describe "calls to each" do
125
+
126
+ def without_warnings
127
+ verbosity = $VERBOSE
128
+ $VERBOSE = nil
129
+ result = yield
130
+ $VERBOSE = verbosity
131
+ result
132
+ end
133
+
134
+ it "should call each only once for Array" do
135
+ enum = [1, 2, 3]
136
+ enum.should_receive(:each).once
137
+ enum.with_progress.each{ }.should == enum
138
+ end
139
+
140
+ it "should call each only once for Hash" do
141
+ enum = {1 => 1, 2 => 2, 3 => 3}
142
+ enum.should_receive(:each).once
143
+ enum.with_progress.each{ }.should == enum
144
+ end
145
+
146
+ it "should call each only once for Set" do
147
+ enum = [1, 2, 3].to_set
148
+ enum.should_receive(:each).once
149
+ enum.with_progress.each{ }.should == enum
150
+ end
151
+
152
+ if ''.is_a?(Enumerable) # ruby1.8
153
+ it "should call each only once for String" do
154
+ enum = "a\nb\nc"
155
+ enum.should_receive(:each).once
156
+ without_warnings do
157
+ enum.with_progress.each{ }.should == enum
187
158
  end
188
- io_pop_no_eta.should == "A: %5.1f%% > B: 100.0%\n" % [(a + 1) * a_step.to_f / (100.0 * _a.to_f) * 100.0]
189
159
  end
190
- io_pop_no_eta.should == "A: %5.1f%%\n" % [(a + 1) * a_step.to_f / (100.0 * _a.to_f) * 100.0]
191
160
  end
192
- Progress.step _a
161
+
162
+ it "should call each only once for File (IO)" do
163
+ enum = File.open(__FILE__)
164
+ enum.should_receive(:each).once
165
+ without_warnings do
166
+ enum.with_progress.each{ }.should == enum
167
+ end
168
+ end
169
+
170
+ it "should call each only once for StringIO" do
171
+ enum = StringIO.new("a\nb\nc")
172
+ enum.should_receive(:each).once
173
+ without_warnings do
174
+ enum.with_progress.each{ }.should == enum
175
+ end
176
+ end
177
+
193
178
  end
194
- io_pop.should == "A: 100.0%\nA: 100.0%\n\n"
195
179
  end
196
180
  end
197
- end
198
181
 
199
- describe Enumerable do
200
- before :each do
201
- @a = 0...1000
182
+ describe Integer do
183
+
184
+ let(:count){ 108 }
185
+
186
+ it "should not break times_with_progress" do
187
+ reference = count.times
188
+ count.times_with_progress do |i|
189
+ i.should == reference.next
190
+ end
191
+ proc{ reference.next }.should raise_error(StopIteration)
192
+ end
193
+
194
+ it "should not break times.with_progress" do
195
+ reference = count.times
196
+ count.times.with_progress do |i|
197
+ i.should == reference.next
198
+ end
199
+ proc{ reference.next }.should raise_error(StopIteration)
200
+ end
201
+
202
202
  end
203
203
 
204
- describe 'with_progress' do
205
- it "should not break each" do
206
- with, without = [], []
207
- @a.with_progress.each{ |n| with << n }
208
- @a.each{ |n| without << n }
209
- with.should == without
204
+ end
205
+
206
+ describe "output" do
207
+
208
+ class ChunkIo
209
+ attr_reader :chunks
210
+ def initialize
211
+ @chunks = []
210
212
  end
211
213
 
212
- it "should not break find" do
213
- @a.with_progress('Hello').find{ |n| n == 100 }.should == @a.find{ |n| n == 100 }
214
- @a.with_progress('Hello').find{ |n| n == 10000 }.should == @a.find{ |n| n == 10000 }
215
- default = proc{ 'default' }
216
- @a.with_progress('Hello').find(default){ |n| n == 10000 }.should == @a.find(default){ |n| n == 10000 }
214
+ def <<(data)
215
+ @chunks << data.to_s
217
216
  end
217
+ end
218
218
 
219
- it "should not break map" do
220
- @a.with_progress('Hello').map{ |n| n * n }.should == @a.map{ |n| n * n }
219
+ def stub_progress_io(klass)
220
+ io = klass.new
221
+ io.stub(:tty?).and_return(true)
222
+ Progress.stub(:io).and_return(io)
223
+ io
224
+ end
225
+
226
+ describe "validity" do
227
+
228
+ def run_example_progress
229
+ Progress.start 5, 'Test' do
230
+ Progress.step 2, 'simle'
231
+
232
+ Progress.step 2, 'times' do
233
+ 3.times.with_progress {}
234
+ end
235
+
236
+ Progress.step 'enum' do
237
+ 3.times.to_a.with_progress {}
238
+ end
239
+ end
221
240
  end
222
241
 
223
- it "should not break grep" do
224
- @a.with_progress('Hello').grep(100).should == @a.grep(100)
242
+ def title(s)
243
+ "\e]0;#{s}\a"
225
244
  end
226
245
 
227
- it "should not break each_cons" do
228
- without_progress = []
229
- @a.each_cons(3){ |values| without_progress << values }
230
- with_progress = []
231
- @a.with_progress('Hello').each_cons(3){ |values| with_progress << values }
232
- without_progress.should == with_progress
246
+ def hl(s)
247
+ "\e[1m#{s}\e[0m"
233
248
  end
234
249
 
235
- describe "with_progress.with_progress" do
236
- it "should not change existing instance" do
237
- wp = @a.with_progress('hello')
238
- proc{ wp.with_progress('world') }.should_not change(wp, :title)
239
- end
250
+ def on_line(s)
251
+ "\r" + s + "\e[K"
252
+ end
240
253
 
241
- it "should create new instance with different title when called on WithProgress" do
242
- wp = @a.with_progress('hello')
243
- wp_wp = wp.with_progress('world')
244
- wp.title.should == 'hello'
245
- wp_wp.title.should == 'world'
246
- wp_wp.should_not == wp
247
- wp_wp.enumerable.should == wp.enumerable
248
- end
254
+ def line(s)
255
+ s + "\n"
249
256
  end
250
257
 
251
- describe "calls to each" do
252
- class CallsToEach
253
- include Enumerable
258
+ it "should produce valid output when staying on line" do
259
+ Progress.stay_on_line = true
260
+
261
+ @io = stub_progress_io(ChunkIo)
262
+ run_example_progress
263
+
264
+ @io.chunks.should == [
265
+ on_line("Test: #{hl '......'}"), title('Test: ......'),
266
+ on_line("Test: #{hl ' 40.0%'} - simle"), title('Test: 40.0% - simle'),
267
+ on_line("Test: #{hl ' 40.0%'} > #{hl '......'}"), title('Test: 40.0% > ......'),
268
+ on_line("Test: #{hl ' 53.3%'} > #{hl ' 33.3%'}"), title('Test: 53.3% > 33.3%'),
269
+ on_line("Test: #{hl ' 66.7%'} > #{hl ' 66.7%'}"), title('Test: 66.7% > 66.7%'),
270
+ on_line("Test: #{hl ' 80.0%'} > 100.0%"), title('Test: 80.0% > 100.0%'),
271
+ on_line("Test: #{hl ' 80.0%'} - times"), title('Test: 80.0% - times'),
272
+ on_line("Test: #{hl ' 80.0%'} > #{hl '......'}"), title('Test: 80.0% > ......'),
273
+ on_line("Test: #{hl ' 86.7%'} > #{hl ' 33.3%'}"), title('Test: 86.7% > 33.3%'),
274
+ on_line("Test: #{hl ' 93.3%'} > #{hl ' 66.7%'}"), title('Test: 93.3% > 66.7%'),
275
+ on_line("Test: 100.0% > 100.0%"), title('Test: 100.0% > 100.0%'),
276
+ on_line("Test: 100.0% - enum"), title('Test: 100.0% - enum'),
277
+ on_line("Test: 100.0% (elapsed: 0s) - enum") + "\n", title(''),
278
+ ]
279
+ end
254
280
 
255
- COUNT = 100
256
- end
281
+ it "should produce valid output when not staying on line" do
282
+ Progress.stay_on_line = false
283
+
284
+ @io = stub_progress_io(ChunkIo)
285
+ run_example_progress
286
+
287
+ @io.chunks.should == [
288
+ line("Test: #{hl '......'}"), title('Test: ......'),
289
+ line("Test: #{hl ' 40.0%'} - simle"), title('Test: 40.0% - simle'),
290
+ line("Test: #{hl ' 40.0%'} > #{hl '......'}"), title('Test: 40.0% > ......'),
291
+ line("Test: #{hl ' 53.3%'} > #{hl ' 33.3%'}"), title('Test: 53.3% > 33.3%'),
292
+ line("Test: #{hl ' 66.7%'} > #{hl ' 66.7%'}"), title('Test: 66.7% > 66.7%'),
293
+ line("Test: #{hl ' 80.0%'} > 100.0%"), title('Test: 80.0% > 100.0%'),
294
+ line("Test: #{hl ' 80.0%'} - times"), title('Test: 80.0% - times'),
295
+ line("Test: #{hl ' 80.0%'} > #{hl '......'}"), title('Test: 80.0% > ......'),
296
+ line("Test: #{hl ' 86.7%'} > #{hl ' 33.3%'}"), title('Test: 86.7% > 33.3%'),
297
+ line("Test: #{hl ' 93.3%'} > #{hl ' 66.7%'}"), title('Test: 93.3% > 66.7%'),
298
+ line("Test: 100.0% > 100.0%"), title('Test: 100.0% > 100.0%'),
299
+ line("Test: 100.0% - enum"), title('Test: 100.0% - enum'),
300
+ line("Test: 100.0% (elapsed: 0s) - enum"), title(''),
301
+ ]
302
+ end
257
303
 
258
- def init_calls_to_each
259
- @enum = CallsToEach.new
260
- @objects = 10.times.to_a
261
- @enum.should_receive(:each).once{ |&block|
262
- @objects.each(&block)
263
- }
264
- end
304
+ end
265
305
 
266
- it "should call each only one time for object with length" do
267
- init_calls_to_each
268
- @enum.should_receive(:length).and_return(10)
269
- got = []
270
- @enum.with_progress.each{ |o| got << o }.should == @enum
271
- got.should == @objects
272
- end
306
+ describe "different call styles" do
273
307
 
274
- it "should call each only one time for object without length" do
275
- init_calls_to_each
276
- got = []
277
- @enum.with_progress.each{ |o| got << o }.should == @enum
278
- got.should == @objects
279
- end
308
+ let(:count_a){ 13 }
309
+ let(:count_b){ 17 }
280
310
 
281
- it "should call each only one time for String" do
282
- @objects = ('a'..'z').map{ |c| "#{c}\n" }
283
- str = @objects.join('')
284
- str.should_not_receive(:length)
285
- str.should_receive(:each).once{ |&block|
286
- @objects.each(&block)
287
- }
288
- got = []
289
- str.with_progress.each{ |o| got << o }.should == str
290
- got.should == @objects
311
+ before do
312
+ reference_io = stub_progress_io(StringIO)
313
+ count_a.times.with_progress('Test') do
314
+ count_b.times.with_progress {}
291
315
  end
316
+ @reference_output = reference_io.string
317
+
318
+ @io = stub_progress_io(StringIO)
292
319
  end
293
- end
294
- end
295
320
 
296
- describe Integer do
297
- describe 'with times_with_progress' do
298
- it "should not break times" do
299
- ii = 0
300
- count.times_with_progress('Test') do |i|
301
- i.should == ii
302
- ii += 1
321
+ it "should output same when called without block" do
322
+ Progress(count_a, 'Test')
323
+ count_a.times do
324
+ Progress.step do
325
+ Progress.start(count_b)
326
+ count_b.times do
327
+ Progress.step
328
+ end
329
+ Progress.stop
330
+ end
303
331
  end
332
+ Progress.stop
333
+ @io.string.should == @reference_output
304
334
  end
305
335
 
306
- it "should show valid output for each_with_progress" do
307
- count.times_with_progress('Test') do |i|
308
- verify_output_before_step(i, count)
336
+ it "should output same when called with block" do
337
+ Progress(count_a, 'Test') do
338
+ count_a.times do
339
+ Progress.step do
340
+ Progress.start(count_b) do
341
+ count_b.times do
342
+ Progress.step
343
+ end
344
+ end
345
+ end
346
+ end
309
347
  end
310
- verify_output_after_stop
348
+ @io.string.should == @reference_output
311
349
  end
312
350
 
313
- it "should show valid output for each_with_progress without title" do
314
- count.times_with_progress do |i|
315
- verify_output_before_step(i, count)
351
+ it "should output same when called using with_progress on list" do
352
+ count_a.times.to_a.with_progress('Test') do
353
+ count_b.times.to_a.with_progress {}
316
354
  end
317
- verify_output_after_stop
355
+ @io.string.should == @reference_output
318
356
  end
357
+
319
358
  end
359
+
320
360
  end
361
+
321
362
  end