progress 2.4.0 → 3.0.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.
@@ -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