progressbar 0.21.0 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/LICENSE.txt +19 -0
  5. data/README.md +38 -0
  6. data/Rakefile +2 -14
  7. data/lib/progressbar.rb +14 -284
  8. data/lib/ruby-progressbar/base.rb +161 -0
  9. data/lib/ruby-progressbar/calculators/length.rb +88 -0
  10. data/lib/ruby-progressbar/calculators/length_spec.rb +9 -0
  11. data/lib/ruby-progressbar/calculators/running_average.rb +9 -0
  12. data/lib/ruby-progressbar/components.rb +5 -0
  13. data/lib/ruby-progressbar/components/bar.rb +96 -0
  14. data/lib/ruby-progressbar/components/percentage.rb +29 -0
  15. data/lib/ruby-progressbar/components/rate.rb +43 -0
  16. data/lib/ruby-progressbar/components/time.rb +103 -0
  17. data/lib/ruby-progressbar/components/title.rb +13 -0
  18. data/lib/ruby-progressbar/errors/invalid_progress_error.rb +4 -0
  19. data/lib/ruby-progressbar/format.rb +3 -0
  20. data/lib/ruby-progressbar/format/formatter.rb +27 -0
  21. data/lib/ruby-progressbar/format/molecule.rb +58 -0
  22. data/lib/ruby-progressbar/format/string.rb +36 -0
  23. data/lib/ruby-progressbar/output.rb +61 -0
  24. data/lib/ruby-progressbar/outputs/non_tty.rb +47 -0
  25. data/lib/ruby-progressbar/outputs/tty.rb +32 -0
  26. data/lib/ruby-progressbar/progress.rb +114 -0
  27. data/lib/ruby-progressbar/throttle.rb +25 -0
  28. data/lib/ruby-progressbar/time.rb +30 -0
  29. data/lib/ruby-progressbar/timer.rb +72 -0
  30. data/lib/ruby-progressbar/version.rb +3 -0
  31. data/spec/fixtures/benchmark.rb +28 -0
  32. data/spec/ruby-progressbar/base_spec.rb +949 -0
  33. data/spec/ruby-progressbar/calculators/length_calculator_spec.rb +17 -0
  34. data/spec/ruby-progressbar/calculators/running_average_spec.rb +19 -0
  35. data/spec/ruby-progressbar/components/bar_spec.rb +234 -0
  36. data/spec/ruby-progressbar/components/percentage_spec.rb +9 -0
  37. data/spec/ruby-progressbar/components/rate_spec.rb +9 -0
  38. data/spec/ruby-progressbar/components/throttle_spec.rb +157 -0
  39. data/spec/ruby-progressbar/components/time_spec.rb +307 -0
  40. data/spec/ruby-progressbar/components/title_spec.rb +12 -0
  41. data/spec/ruby-progressbar/format/formatter_spec.rb +9 -0
  42. data/spec/ruby-progressbar/format/molecule_spec.rb +30 -0
  43. data/spec/ruby-progressbar/format/string_spec.rb +9 -0
  44. data/spec/ruby-progressbar/output_spec.rb +7 -0
  45. data/spec/ruby-progressbar/outputs/non_tty_spec.rb +9 -0
  46. data/spec/ruby-progressbar/outputs/tty_spec.rb +9 -0
  47. data/spec/ruby-progressbar/progress_spec.rb +156 -0
  48. data/spec/ruby-progressbar/time_spec.rb +45 -0
  49. data/spec/ruby-progressbar/timer_spec.rb +7 -0
  50. data/spec/spec_helper.rb +6 -0
  51. data/spec/support/time.rb +17 -0
  52. metadata +134 -69
  53. metadata.gz.sig +3 -0
  54. data/.gitignore +0 -23
  55. data/.ruby-version +0 -1
  56. data/.travis.yml +0 -6
  57. data/ChangeLog +0 -113
  58. data/Gemfile +0 -4
  59. data/Gemfile.lock +0 -27
  60. data/LICENSE +0 -1
  61. data/README.rdoc +0 -116
  62. data/progressbar.gemspec +0 -29
  63. data/test/test.rb +0 -125
@@ -0,0 +1,3 @@
1
+ class ProgressBar
2
+ VERSION = '1.8.1'.freeze
3
+ end
@@ -0,0 +1,28 @@
1
+ # bundle exec ruby-prof --printer=graph_html
2
+ # --file=../results.html
3
+ # --require 'ruby-progressbar'
4
+ # --sort=total ./spec/fixtures/benchmark.rb
5
+
6
+ total = 100_000
7
+ # output = File.open('/Users/jfelchner/Downloads/benchmark.txt', 'w+')
8
+ output = $stdout
9
+
10
+ # Progressbar gem
11
+ # bar = ProgressBar.new('Progress', total)
12
+ #
13
+ # total.times do |i|
14
+ # bar.inc
15
+ # end
16
+ #
17
+ # bar.finish
18
+
19
+ # Ruby/ProgressBar
20
+ bar = ProgressBar.create(:output => output,
21
+ :length => 80,
22
+ :start => 0,
23
+ :total => total)
24
+
25
+ total.times do |_i|
26
+ # bar.log i
27
+ bar.increment
28
+ end
@@ -0,0 +1,949 @@
1
+ require 'spec_helper'
2
+ require 'support/time'
3
+ require 'stringio'
4
+
5
+ # rubocop:disable Metrics/LineLength, Style/UnneededInterpolation
6
+ RSpec.describe ProgressBar::Base do
7
+ let(:output) do
8
+ StringIO.new('', 'w+').tap do |io|
9
+ allow(io).to receive(:tty?).and_return true
10
+ end
11
+ end
12
+
13
+ let(:non_tty_output) do
14
+ StringIO.new('', 'w+').tap do |io|
15
+ allow(io).to receive(:tty?).and_return false
16
+ end
17
+ end
18
+
19
+ let(:progressbar) { ProgressBar::Base.new(:output => output, :length => 80, :throttle_rate => 0.0) }
20
+
21
+ context 'when the terminal width is shorter than the string being output' do
22
+ it 'can properly handle outputting the bar when the length changes on the fly to less than the minimum width' do
23
+ progressbar = ProgressBar::Base.new(:output => output, :title => 'a' * 25, :format => '%t%B', :throttle_rate => 0.0)
24
+
25
+ allow(progressbar.send(:output).send(:length_calculator)).to receive(:terminal_width).
26
+ and_return 30
27
+
28
+ progressbar.start
29
+
30
+ allow(progressbar.send(:output).send(:length_calculator)).to receive(:terminal_width).
31
+ and_return 20
32
+
33
+ progressbar.increment
34
+
35
+ output.rewind
36
+ expect(output.read).to match(/\raaaaaaaaaaaaaaaaaaaaaaaaa \r\s+\raaaaaaaaaaaaaaaaaaaaaaaaa\r\z/)
37
+ end
38
+
39
+ context 'and the bar length is calculated' do
40
+ it 'returns the proper string' do
41
+ progressbar = ProgressBar::Base.new(:output => output, :title => ('*' * 21), :starting_at => 5, :total => 10, :autostart => false)
42
+
43
+ allow(progressbar.send(:output).send(:length_calculator)).to receive(:terminal_width).
44
+ and_return 20
45
+
46
+ expect(progressbar.to_s('%t%w')).to eql '*********************'
47
+ end
48
+ end
49
+
50
+ context 'and the incomplete bar length is calculated' do
51
+ it 'returns the proper string' do
52
+ progressbar = ProgressBar::Base.new(:output => output, :title => ('*' * 21), :autostart => false)
53
+
54
+ allow(progressbar.send(:output).send(:length_calculator)).to receive(:terminal_width).
55
+ and_return 20
56
+
57
+ expect(progressbar.to_s('%t%i')).to eql '*********************'
58
+ end
59
+
60
+ it 'returns the proper string' do
61
+ progressbar = ProgressBar::Base.new(:output => output, :title => ('*' * 21), :starting_at => 5, :total => 10, :autostart => false)
62
+
63
+ allow(progressbar.send(:output).send(:length_calculator)).to receive(:terminal_width).
64
+ and_return 20
65
+
66
+ expect(progressbar.to_s('%t%i')).to eql '*********************'
67
+ end
68
+ end
69
+
70
+ context 'and the full bar length is calculated (but lacks the space to output the entire bar)' do
71
+ it 'returns the proper string' do
72
+ progressbar = ProgressBar::Base.new(:output => output, :title => ('*' * 19), :starting_at => 5, :total => 10, :autostart => false)
73
+
74
+ allow(progressbar.send(:output).send(:length_calculator)).to receive(:terminal_width).
75
+ and_return 20
76
+
77
+ expect(progressbar.to_s('%t%B')).to eql '******************* '
78
+ end
79
+
80
+ it 'returns the proper string' do
81
+ progressbar = ProgressBar::Base.new(:output => output, :title => ('*' * 19), :starting_at => 5, :total => 10, :autostart => false)
82
+
83
+ allow(progressbar.send(:output).send(:length_calculator)).to receive(:terminal_width).
84
+ and_return 20
85
+
86
+ expect(progressbar.to_s('%t%w%i')).to eql '******************* '
87
+ end
88
+ end
89
+ end
90
+
91
+ context 'when a new bar is created' do
92
+ context 'and no options are passed' do
93
+ let(:progressbar) { ProgressBar::Base.new }
94
+
95
+ describe '#title' do
96
+ it 'returns the default title' do
97
+ expect(progressbar.send(:title).to_s).to eql ProgressBar::Components::Title::DEFAULT_TITLE
98
+ end
99
+ end
100
+
101
+ describe '#output' do
102
+ it 'returns the default output stream' do
103
+ expect(progressbar.send(:output).send(:stream)).to eql ProgressBar::Output::DEFAULT_OUTPUT_STREAM
104
+ end
105
+ end
106
+
107
+ describe '#length' do
108
+ context 'when the RUBY_PROGRESS_BAR_LENGTH environment variable exists' do
109
+ before { ENV['RUBY_PROGRESS_BAR_LENGTH'] = '44' }
110
+ after { ENV['RUBY_PROGRESS_BAR_LENGTH'] = nil }
111
+
112
+ it 'returns the length of the environment variable as an integer' do
113
+ progressbar = ProgressBar::Base.new
114
+ expect(progressbar.send(:output).send(:length_calculator).send(:length)).to eql 44
115
+ end
116
+ end
117
+
118
+ context 'when the RUBY_PROGRESS_BAR_LENGTH environment variable does not exist' do
119
+ before { ENV['RUBY_PROGRESS_BAR_LENGTH'] = nil }
120
+
121
+ context 'but the length option was passed in' do
122
+ it 'returns the length specified in the option' do
123
+ progressbar = ProgressBar::Base.new(:length => 88)
124
+ expect(progressbar.send(:output).send(:length_calculator).send(:length)).to eql 88
125
+ end
126
+ end
127
+
128
+ context 'and no length option was passed in' do
129
+ it 'returns the width of the terminal if it is a Unix environment' do
130
+ allow(progressbar.send(:output).send(:length_calculator)).to receive(:terminal_width).and_return(99)
131
+ progressbar.send(:output).send(:length_calculator).send(:reset_length)
132
+ expect(progressbar.send(:output).send(:length_calculator).send(:length)).to eql 99
133
+ end
134
+
135
+ it 'returns 80 if it is not a Unix environment' do
136
+ allow(progressbar.send(:output).send(:length_calculator)).to receive(:unix?).and_return(false)
137
+ progressbar.send(:output).send(:length_calculator).send(:reset_length)
138
+ expect(progressbar.send(:output).send(:length_calculator).send(:length)).to eql 80
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ context 'and options are passed' do
146
+ let(:progressbar) { ProgressBar::Base.new(:title => 'We All Float', :total => 12, :output => STDOUT, :progress_mark => 'x', :length => 88, :starting_at => 5) }
147
+
148
+ describe '#title' do
149
+ it 'returns the overridden title' do
150
+ expect(progressbar.send(:title).to_s).to eql 'We All Float'
151
+ end
152
+ end
153
+
154
+ describe '#output' do
155
+ it 'returns the overridden output stream' do
156
+ expect(progressbar.send(:output).send(:stream)).to eql STDOUT
157
+ end
158
+ end
159
+
160
+ describe '#length' do
161
+ it 'returns the overridden length' do
162
+ expect(progressbar.send(:output).send(:length_calculator).send(:length)).to eql 88
163
+ end
164
+ end
165
+ end
166
+
167
+ context 'if the bar was started 4 minutes ago' do
168
+ before do
169
+ Timecop.travel(-240) do
170
+ progressbar.start
171
+ end
172
+ end
173
+
174
+ context 'and within 2 minutes it was halfway done' do
175
+ before do
176
+ Timecop.travel(-120) do
177
+ 50.times { progressbar.increment }
178
+ end
179
+ end
180
+
181
+ describe '#finish' do
182
+ before do
183
+ Timecop.travel(-120) do
184
+ progressbar.finish
185
+ end
186
+ end
187
+
188
+ it 'completes the bar' do
189
+ output.rewind
190
+ expect(output.read).to match(/Progress: \|#{'=' * 68}\|\n/)
191
+ end
192
+
193
+ it 'shows the elapsed time instead of the estimated time since the bar is completed' do
194
+ expect(progressbar.to_s('%e')).to eql 'Time: 00:02:00'
195
+ end
196
+
197
+ it 'calculates the elapsed time to 00:02:00' do
198
+ expect(progressbar.to_s('%a')).to eql 'Time: 00:02:00'
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ context 'which includes ANSI SGR codes in the format string' do
205
+ it 'properly calculates the length of the bar by removing the long version of the ANSI codes from the calculated length' do
206
+ @color_code = "\e[0m\e[32m\e[7m\e[1m"
207
+ @reset_code = "\e[0m"
208
+ @progress_mark = "#{@color_code} #{@reset_code}"
209
+ progressbar = ProgressBar::Base.new(:format => "#{@color_code}Processing... %b%i#{@reset_code}#{@color_code} %p%%#{@reset_code}",
210
+ :progress_mark => @progress_mark,
211
+ :output => output,
212
+ :length => 24,
213
+ :starting_at => 3,
214
+ :total => 6,
215
+ :throttle_rate => 0.0)
216
+
217
+ progressbar.increment
218
+ progressbar.increment
219
+
220
+ output.rewind
221
+ expect(output.read).to include "#{@color_code}Processing... #{@progress_mark * 3}#{' ' * 3}#{@reset_code}#{@color_code} 50%#{@reset_code}\r#{@color_code}Processing... #{@progress_mark * 3}#{' ' * 3}#{@reset_code}#{@color_code} 66%#{@reset_code}\r#{@color_code}Processing... #{@progress_mark * 4}#{' ' * 2}#{@reset_code}#{@color_code} 83%#{@reset_code}\r"
222
+ end
223
+
224
+ it 'properly calculates the length of the bar by removing the short version of the ANSI codes from the calculated length' do
225
+ @color_code = "\e[0;32;7;1m"
226
+ @reset_code = "\e[0m"
227
+ @progress_mark = "#{@color_code} #{@reset_code}"
228
+ progressbar = ProgressBar::Base.new(:format => "#{@color_code}Processing... %b%i#{@reset_code}#{@color_code} %p%%#{@reset_code}",
229
+ :progress_mark => @progress_mark,
230
+ :output => output,
231
+ :length => 24,
232
+ :starting_at => 3,
233
+ :total => 6,
234
+ :throttle_rate => 0.0)
235
+
236
+ progressbar.increment
237
+ progressbar.increment
238
+
239
+ output.rewind
240
+ expect(output.read).to include "#{@color_code}Processing... #{@progress_mark * 3}#{' ' * 3}#{@reset_code}#{@color_code} 50%#{@reset_code}\r#{@color_code}Processing... #{@progress_mark * 3}#{' ' * 3}#{@reset_code}#{@color_code} 66%#{@reset_code}\r#{@color_code}Processing... #{@progress_mark * 4}#{' ' * 2}#{@reset_code}#{@color_code} 83%#{@reset_code}\r"
241
+ end
242
+ end
243
+
244
+ context 'for a TTY enabled device' do
245
+ it 'can log messages' do
246
+ progressbar = ProgressBar::Base.new(:output => output, :length => 20, :starting_at => 3, :total => 6, :throttle_rate => 0.0)
247
+ progressbar.increment
248
+ progressbar.log 'We All Float'
249
+ progressbar.increment
250
+
251
+ output.rewind
252
+ expect(output.read).to include "Progress: |==== |\rProgress: |===== |\r \rWe All Float\nProgress: |===== |\rProgress: |====== |\r"
253
+ end
254
+ end
255
+
256
+ context 'for a non-TTY enabled device' do
257
+ it 'can log messages' do
258
+ progressbar = ProgressBar::Base.new(:output => non_tty_output, :length => 20, :starting_at => 4, :total => 6, :throttle_rate => 0.0)
259
+ progressbar.increment
260
+ progressbar.log 'We All Float'
261
+ progressbar.increment
262
+ progressbar.finish
263
+
264
+ non_tty_output.rewind
265
+ expect(non_tty_output.read).to include "We All Float\nProgress: |========|\n"
266
+ end
267
+
268
+ it 'can output the bar properly so that it does not spam the screen' do
269
+ progressbar = ProgressBar::Base.new(:output => non_tty_output, :length => 20, :starting_at => 0, :total => 6, :throttle_rate => 0.0)
270
+
271
+ 6.times { progressbar.increment }
272
+
273
+ non_tty_output.rewind
274
+ expect(non_tty_output.read).to eql "\n\nProgress: |========|\n"
275
+ end
276
+
277
+ it 'can output the bar properly if finished in the middle of its progress' do
278
+ progressbar = ProgressBar::Base.new(:output => non_tty_output, :length => 20, :starting_at => 0, :total => 6, :throttle_rate => 0.0)
279
+
280
+ 3.times { progressbar.increment }
281
+
282
+ progressbar.finish
283
+
284
+ non_tty_output.rewind
285
+ expect(non_tty_output.read).to eql "\n\nProgress: |========|\n"
286
+ end
287
+
288
+ it 'can output the bar properly if stopped in the middle of its progress' do
289
+ progressbar = ProgressBar::Base.new(:output => non_tty_output, :length => 20, :starting_at => 0, :total => 6, :throttle_rate => 0.0)
290
+
291
+ 3.times { progressbar.increment }
292
+
293
+ progressbar.stop
294
+
295
+ non_tty_output.rewind
296
+ expect(non_tty_output.read).to eql "\n\nProgress: |====\n"
297
+ end
298
+
299
+ it 'ignores changes to the title due to the fact that the bar length cannot change' do
300
+ progressbar = ProgressBar::Base.new(:output => non_tty_output, :length => 20, :starting_at => 0, :total => 6, :throttle_rate => 0.0)
301
+
302
+ 3.times { progressbar.increment }
303
+
304
+ progressbar.title = 'Testing'
305
+ progressbar.stop
306
+
307
+ non_tty_output.rewind
308
+
309
+ expect(non_tty_output.read).to eql "\n\nProgress: |====\n"
310
+ end
311
+
312
+ it 'allows the title to be customized when the bar is created' do
313
+ progressbar = ProgressBar::Base.new(:output => non_tty_output, :title => 'Custom', :length => 20, :starting_at => 0, :total => 6, :throttle_rate => 0.0)
314
+
315
+ 3.times { progressbar.increment }
316
+
317
+ progressbar.stop
318
+
319
+ non_tty_output.rewind
320
+
321
+ expect(non_tty_output.read).to eql "\n\nCustom: |=====\n"
322
+ end
323
+ end
324
+ end
325
+
326
+ context 'when a bar is about to be completed' do
327
+ let(:progressbar) { ProgressBar::Base.new(:starting_at => 5, :total => 6, :output => output, :length => 20, :throttle_rate => 0.0) }
328
+
329
+ context 'and it is incremented' do
330
+ before { progressbar.increment }
331
+
332
+ it 'registers as being "finished"' do
333
+ expect(progressbar).to be_finished
334
+ end
335
+
336
+ it 'prints a new line' do
337
+ output.rewind
338
+ expect(output.read.end_with?("\n")).to eql true
339
+ end
340
+
341
+ it 'does not continue to print bars if finish is subsequently called' do
342
+ progressbar.finish
343
+
344
+ output.rewind
345
+ expect(output.read).to end_with " \rProgress: |====== |\rProgress: |========|\n"
346
+ end
347
+ end
348
+ end
349
+
350
+ context 'when a bar with autofinish=false is about to be completed' do
351
+ let(:progressbar) { ProgressBar::Base.new(:autofinish => false, :starting_at => 5, :total => 6, :output => output, :length => 20, :throttle_rate => 0.0) }
352
+
353
+ context 'and it is incremented' do
354
+ before { progressbar.increment }
355
+
356
+ it 'does not automatically finish' do
357
+ expect(progressbar).not_to be_finished
358
+ end
359
+
360
+ it 'does not prints a new line' do
361
+ output.rewind
362
+
363
+ expect(output.read.end_with?("\n")).to eql false
364
+ end
365
+
366
+ it 'allows reset' do
367
+ progressbar.finish
368
+ expect(progressbar).to be_finished
369
+
370
+ progressbar.reset
371
+
372
+ expect(progressbar).not_to be_finished
373
+ end
374
+
375
+ it 'does prints a new line when manually finished' do
376
+ progressbar.finish
377
+ expect(progressbar).to be_finished
378
+
379
+ output.rewind
380
+
381
+ expect(output.read.end_with?("\n")).to eql true
382
+ end
383
+
384
+ it 'does not continue to print bars if finish is subsequently called' do
385
+ progressbar.finish
386
+
387
+ output.rewind
388
+
389
+ expect(output.read).to end_with " \rProgress: |====== |\rProgress: |========|\rProgress: |========|\n"
390
+ end
391
+ end
392
+ end
393
+
394
+ context 'when a bar has an unknown amount to completion' do
395
+ let(:progressbar) { ProgressBar::Base.new(:total => nil, :output => output, :length => 80, :unknown_progress_animation_steps => ['=--', '-=-', '--=']) }
396
+
397
+ it 'is represented correctly' do
398
+ expect(progressbar.to_s('%i')).to eql '=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=-'
399
+ end
400
+
401
+ it 'is represented after being incremented once' do
402
+ progressbar.increment
403
+ expect(progressbar.to_s('%i')).to eql '-=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--='
404
+ end
405
+
406
+ it 'is represented after being incremented twice' do
407
+ progressbar.increment
408
+ progressbar.increment
409
+ expect(progressbar.to_s('%i')).to eql '--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--'
410
+ end
411
+
412
+ it 'displays the proper ETA' do
413
+ progressbar.increment
414
+
415
+ expect(progressbar.to_s('%i%e')).to eql '-=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=- ETA: ??:??:??'
416
+ expect(progressbar.to_s('%i%E')).to eql '-=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=--=- ETA: ??:??:??'
417
+ end
418
+ end
419
+
420
+ context 'when a bar is started' do
421
+ let(:progressbar) { ProgressBar::Base.new(:starting_at => 0, :total => 100, :output => output, :length => 80, :throttle_rate => 0.0) }
422
+
423
+ context 'and it is incremented any number of times' do
424
+ before { 10.times { progressbar.increment } }
425
+
426
+ describe '#progress_mark=' do
427
+ it 'changes the mark used to represent progress and updates the output' do
428
+ progressbar.progress_mark = 'x'
429
+
430
+ output.rewind
431
+ expect(output.read).to match(/\rProgress: \|xxxxxx#{' ' * 62}\|\r\z/)
432
+ end
433
+ end
434
+
435
+ describe '#remainder_mark=' do
436
+ it 'changes the mark used to represent the remaining part of the bar and updates the output' do
437
+ progressbar.remainder_mark = 'x'
438
+
439
+ output.rewind
440
+ expect(output.read).to match(/\rProgress: \|======#{'x' * 62}\|\r\z/)
441
+ end
442
+ end
443
+
444
+ describe '#title=' do
445
+ it 'changes the title used to represent the items being progressed and updates the output' do
446
+ progressbar.title = 'Items'
447
+
448
+ output.rewind
449
+ expect(output.read).to match(/\rItems: \|=======#{' ' * 64}\|\r\z/)
450
+ end
451
+ end
452
+
453
+ describe '#reset' do
454
+ before { progressbar.reset }
455
+
456
+ it 'resets the bar back to the starting value' do
457
+ output.rewind
458
+ expect(output.read).to match(/\rProgress: \|#{' ' * 68}\|\r\z/)
459
+ end
460
+ end
461
+
462
+ describe '#stop' do
463
+ before { progressbar.stop }
464
+
465
+ it 'forcibly halts the bar wherever it is and cancels it' do
466
+ output.rewind
467
+ expect(output.read).to match(/\rProgress: \|======#{' ' * 62}\|\n\z/)
468
+ end
469
+
470
+ it 'does not output the bar multiple times if the bar is already stopped' do
471
+ output.rewind
472
+ progressbar.stop
473
+ output.rewind
474
+
475
+ expect(output.read).to start_with "#{' ' * 80}"
476
+ end
477
+ end
478
+
479
+ describe '#resume' do
480
+ it 'does not output the bar multiple times' do
481
+ output.rewind
482
+ progressbar.resume
483
+ output.rewind
484
+
485
+ expect(output.read).to start_with "#{' ' * 80}"
486
+ end
487
+ end
488
+ end
489
+ end
490
+
491
+ context 'when a bar is started from 10/100' do
492
+ let(:progressbar) { ProgressBar::Base.new(:starting_at => 10, :total => 100, :output => output, :length => 112) }
493
+
494
+ context 'and it is incremented any number of times' do
495
+ before { 10.times { progressbar.increment } }
496
+
497
+ describe '#reset' do
498
+ before { progressbar.reset }
499
+
500
+ it 'resets the bar back to the starting value' do
501
+ output.rewind
502
+ expect(output.read).to match(/\rProgress: \|==========#{' ' * 90}\|\r\z/)
503
+ end
504
+ end
505
+ end
506
+ end
507
+
508
+ describe '#clear' do
509
+ it 'clears the current terminal line and/or bar text' do
510
+ progressbar.clear
511
+
512
+ output.rewind
513
+ expect(output.read).to match(/^#{progressbar.send(:output).send(:clear_string)}/)
514
+ end
515
+ end
516
+
517
+ describe '#start' do
518
+ it 'clears the current terminal line' do
519
+ progressbar.start
520
+
521
+ output.rewind
522
+ expect(output.read).to match(/^#{progressbar.send(:output).send(:clear_string)}/)
523
+ end
524
+
525
+ it 'prints the bar for the first time' do
526
+ progressbar.start
527
+
528
+ output.rewind
529
+ expect(output.read).to match(/Progress: \| \|\r\z/)
530
+ end
531
+
532
+ it 'prints correctly if passed a position to start at' do
533
+ progressbar.start(:at => 20)
534
+
535
+ output.rewind
536
+ expect(output.read).to match(/Progress: \|============= \|\r\z/)
537
+ end
538
+ end
539
+
540
+ context 'when the bar has not been completed' do
541
+ let(:progressbar) { ProgressBar::Base.new(:length => 112, :starting_at => 0, :total => 50, :output => output, :throttle_rate => 0.0) }
542
+
543
+ describe '#increment' do
544
+ before { progressbar.increment }
545
+
546
+ it 'displays the bar with the correct formatting' do
547
+ output.rewind
548
+ expect(output.read).to match(/Progress: \|== \|\r\z/)
549
+ end
550
+ end
551
+ end
552
+
553
+ context 'when a new bar is created with a specific format' do
554
+ context '#format' do
555
+ let(:progressbar) { ProgressBar::Base.new(:format => '%B %p%%') }
556
+
557
+ context 'if called with no arguments' do
558
+ before { progressbar.format = nil }
559
+
560
+ it 'resets the format back to the default' do
561
+ expect(progressbar.to_s).to match(/^Progress: \|\s+\|\z/)
562
+ end
563
+ end
564
+
565
+ context 'if called with a specific format string' do
566
+ before { progressbar.format = '%t' }
567
+
568
+ it 'sets it as the new format for the bar' do
569
+ expect(progressbar.to_s).to match(/^Progress\z/)
570
+ end
571
+ end
572
+ end
573
+
574
+ context '#to_s' do
575
+ context 'when no time has elapsed' do
576
+ it 'displays zero for the rate' do
577
+ Timecop.freeze do
578
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 0)
579
+
580
+ expect(progressbar.to_s('%r')).to match(/^0\z/)
581
+ end
582
+ end
583
+ end
584
+
585
+ context 'when any time has elasped' do
586
+ context 'and the standard rate is applied' do
587
+ it 'displays zero for %r if no progress has been made' do
588
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 20)
589
+
590
+ Timecop.travel(2) do
591
+ expect(progressbar.to_s('%r')).to match(/^0\z/)
592
+ end
593
+ end
594
+
595
+ it 'displays zero for %R if no progress has been made' do
596
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 20)
597
+
598
+ Timecop.travel(2) do
599
+ expect(progressbar.to_s('%R')).to match(/^0.00\z/)
600
+ end
601
+ end
602
+
603
+ it 'takes into account the starting position when calculating %r' do
604
+ Timecop.freeze do
605
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 20)
606
+ progressbar.start
607
+ progressbar.progress += 20
608
+
609
+ Timecop.travel(2) do
610
+ expect(progressbar.to_s('%r')).to match(/^10\z/)
611
+ end
612
+ end
613
+ end
614
+
615
+ it 'takes into account the starting position when calculating %R' do
616
+ Timecop.freeze do
617
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 20)
618
+ progressbar.start
619
+ progressbar.progress += 13
620
+
621
+ Timecop.travel(2) do
622
+ expect(progressbar.to_s('%R')).to match(/^6.50\z/)
623
+ end
624
+ end
625
+ end
626
+
627
+ it 'displays the rate when passed the "%r" format flag' do
628
+ Timecop.freeze do
629
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 0)
630
+ progressbar.start
631
+ progressbar.progress += 20
632
+
633
+ Timecop.travel(2) do
634
+ expect(progressbar.to_s('%r')).to match(/^10\z/)
635
+ end
636
+ end
637
+ end
638
+
639
+ it 'displays the rate when passed the "%R" format flag' do
640
+ Timecop.freeze do
641
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 0)
642
+ progressbar.start
643
+ progressbar.progress += 10
644
+
645
+ Timecop.travel(6) do
646
+ expect(progressbar.to_s('%R')).to match(/^1.67\z/)
647
+ end
648
+ end
649
+ end
650
+ end
651
+
652
+ context 'and the a custom rate is applied' do
653
+ it 'displays zero for %r if no progress has been made' do
654
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 20, :rate_scale => lambda { |rate| rate / 2 })
655
+
656
+ Timecop.travel(2) do
657
+ expect(progressbar.to_s('%r')).to match(/^0\z/)
658
+ end
659
+ end
660
+
661
+ it 'displays zero for %R if no progress has been made' do
662
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 20, :rate_scale => lambda { |rate| rate / 2 })
663
+
664
+ Timecop.travel(2) do
665
+ expect(progressbar.to_s('%R')).to match(/^0.00\z/)
666
+ end
667
+ end
668
+
669
+ it 'takes into account the starting position when calculating %r' do
670
+ Timecop.freeze do
671
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 20, :rate_scale => lambda { |rate| rate / 2 })
672
+ progressbar.start
673
+ progressbar.progress += 20
674
+
675
+ Timecop.travel(2) do
676
+ expect(progressbar.to_s('%r')).to match(/^5\z/)
677
+ end
678
+ end
679
+ end
680
+
681
+ it 'takes into account the starting position when calculating %R' do
682
+ Timecop.freeze do
683
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 20, :rate_scale => lambda { |rate| rate / 2 })
684
+ progressbar.start
685
+ progressbar.progress += 13
686
+
687
+ Timecop.travel(2) do
688
+ expect(progressbar.to_s('%R')).to match(/^3.25\z/)
689
+ end
690
+ end
691
+ end
692
+
693
+ it 'displays the rate when passed the "%r" format flag' do
694
+ Timecop.freeze do
695
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 0, :rate_scale => lambda { |rate| rate / 2 })
696
+ progressbar.start
697
+ progressbar.progress += 20
698
+
699
+ Timecop.travel(2) do
700
+ expect(progressbar.to_s('%r')).to match(/^5\z/)
701
+ end
702
+ end
703
+ end
704
+
705
+ it 'displays the rate when passed the "%R" format flag' do
706
+ Timecop.freeze do
707
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 0, :rate_scale => lambda { |rate| rate / 2 })
708
+ progressbar.start
709
+ progressbar.progress += 10
710
+
711
+ Timecop.travel(6) do
712
+ expect(progressbar.to_s('%R')).to match(/^0.83\z/)
713
+ end
714
+ end
715
+ end
716
+ end
717
+ end
718
+
719
+ it 'displays the title when passed the "%t" format flag' do
720
+ expect(progressbar.to_s('%t')).to match(/^Progress\z/)
721
+ end
722
+
723
+ it 'displays the title when passed the "%T" format flag' do
724
+ expect(progressbar.to_s('%T')).to match(/^Progress\z/)
725
+ end
726
+
727
+ it 'displays the bar when passed the "%B" format flag (including empty space)' do
728
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 20)
729
+ expect(progressbar.to_s('%B')).to match(/^#{'=' * 20}#{' ' * 80}\z/)
730
+ end
731
+
732
+ it 'displays the bar when passed the combined "%b%i" format flags' do
733
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 20)
734
+ expect(progressbar.to_s('%b%i')).to match(/^#{'=' * 20}#{' ' * 80}\z/)
735
+ end
736
+
737
+ it 'displays the bar when passed the "%b" format flag (excluding empty space)' do
738
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 20)
739
+ expect(progressbar.to_s('%b')).to match(/^#{'=' * 20}\z/)
740
+ end
741
+
742
+ it 'displays the incomplete space when passed the "%i" format flag' do
743
+ progressbar = ProgressBar::Base.new(:length => 100, :starting_at => 20)
744
+ expect(progressbar.to_s('%i')).to match(/^#{' ' * 80}\z/)
745
+ end
746
+
747
+ it 'displays the bar when passed the "%w" format flag' do
748
+ progressbar = ProgressBar::Base.new(:output => output, :length => 100, :starting_at => 0)
749
+
750
+ expect(progressbar.to_s('%w')).to match(/^\z/)
751
+ 4.times { progressbar.increment }
752
+ expect(progressbar.to_s('%w')).to match(/^====\z/)
753
+ progressbar.increment
754
+ expect(progressbar.to_s('%w')).to match(/^= 5 =\z/)
755
+ 5.times { progressbar.increment }
756
+ expect(progressbar.to_s('%w')).to match(/^=== 10 ===\z/)
757
+ progressbar.decrement
758
+ expect(progressbar.to_s('%w')).to match(/^=== 9 ===\z/)
759
+ 91.times { progressbar.increment }
760
+ expect(progressbar.to_s('%w')).to match(/^#{'=' * 47} 100 #{'=' * 48}\z/)
761
+ end
762
+
763
+ it 'calculates the remaining negative space properly with an integrated percentage bar of 0 percent' do
764
+ progressbar = ProgressBar::Base.new(:output => output, :length => 100, :total => 200, :starting_at => 0)
765
+
766
+ expect(progressbar.to_s('%w%i')).to match(/^\s{100}\z/)
767
+ 9.times { progressbar.increment }
768
+ expect(progressbar.to_s('%w%i')).to match(/^====\s{96}\z/)
769
+ progressbar.increment
770
+ expect(progressbar.to_s('%w%i')).to match(/^= 5 =\s{95}\z/)
771
+ end
772
+
773
+ it 'can display a percentage, even if the total is unknown' do
774
+ progressbar = ProgressBar::Base.new(:output => output, :length => 100, :total => nil, :starting_at => 0)
775
+
776
+ expect(progressbar.to_s('%p')).to match(/\A0\z/)
777
+ expect(progressbar.to_s('%P')).to match(/\A0\.0\z/)
778
+ end
779
+
780
+ it 'can display a percentage, even if the total is zero' do
781
+ progressbar = ProgressBar::Base.new(:output => output, :length => 100, :total => 0, :starting_at => 0)
782
+
783
+ expect(progressbar.to_s('%p')).to match(/\A100\z/)
784
+ expect(progressbar.to_s('%P')).to match(/\A100\.0\z/)
785
+ end
786
+
787
+ it 'displays the current capacity when passed the "%c" format flag' do
788
+ progressbar = ProgressBar::Base.new(:output => output, :starting_at => 0)
789
+
790
+ expect(progressbar.to_s('%c')).to match(/^0\z/)
791
+ progressbar.increment
792
+ expect(progressbar.to_s('%c')).to match(/^1\z/)
793
+ progressbar.decrement
794
+ expect(progressbar.to_s('%c')).to match(/^0\z/)
795
+ end
796
+
797
+ it 'displays the total capacity when passed the "%C" format flag' do
798
+ progressbar = ProgressBar::Base.new(:total => 100)
799
+
800
+ expect(progressbar.to_s('%C')).to match(/^100\z/)
801
+ end
802
+
803
+ it 'displays the percentage complete when passed the "%p" format flag' do
804
+ progressbar = ProgressBar::Base.new(:starting_at => 33, :total => 200)
805
+
806
+ expect(progressbar.to_s('%p')).to match(/^16\z/)
807
+ end
808
+
809
+ it 'displays the justified percentage complete when passed the "%j" format flag' do
810
+ progressbar = ProgressBar::Base.new(:starting_at => 33, :total => 200)
811
+
812
+ expect(progressbar.to_s('%j')).to match(/^ 16\z/)
813
+ end
814
+
815
+ it 'displays the percentage complete when passed the "%P" format flag' do
816
+ progressbar = ProgressBar::Base.new(:starting_at => 33, :total => 200)
817
+
818
+ expect(progressbar.to_s('%P')).to match(/^16.50\z/)
819
+ end
820
+
821
+ it 'displays the justified percentage complete when passed the "%J" format flag' do
822
+ progressbar = ProgressBar::Base.new(:starting_at => 33, :total => 200)
823
+
824
+ expect(progressbar.to_s('%J')).to match(/^ 16.50\z/)
825
+ end
826
+
827
+ it 'displays only up to 2 decimal places when using the "%P" flag' do
828
+ progressbar = ProgressBar::Base.new(:starting_at => 66, :total => 99)
829
+
830
+ expect(progressbar.to_s('%P')).to match(/^66.66\z/)
831
+ end
832
+
833
+ it 'displays a literal percent sign when using the "%%" flag' do
834
+ progressbar = ProgressBar::Base.new(:starting_at => 66, :total => 99)
835
+
836
+ expect(progressbar.to_s('%%')).to match(/^%\z/)
837
+ end
838
+
839
+ it 'displays a literal percent sign when using the "%%" flag' do
840
+ progressbar = ProgressBar::Base.new(:starting_at => 66, :total => 99)
841
+
842
+ expect(progressbar.to_s('%%')).to match(/^%\z/)
843
+ end
844
+
845
+ context 'when called after #start' do
846
+ before do
847
+ Timecop.travel(-3_723) do
848
+ progressbar.start
849
+ end
850
+ end
851
+
852
+ context 'and the bar is reset' do
853
+ before { progressbar.reset }
854
+
855
+ it 'displays "??:??:??" until finished when passed the %e flag' do
856
+ expect(progressbar.to_s('%a')).to match(/^Time: --:--:--\z/)
857
+ end
858
+ end
859
+
860
+ it 'displays the time elapsed when using the "%a" flag' do
861
+ expect(progressbar.to_s('%a')).to match(/^Time: 01:02:03\z/)
862
+ end
863
+ end
864
+
865
+ context 'when called before #start' do
866
+ it 'displays unknown time until finished when passed the "%e" flag' do
867
+ progressbar = ProgressBar::Base.new
868
+ expect(progressbar.to_s('%e')).to match(/^ ETA: \?\?:\?\?:\?\?\z/)
869
+ end
870
+
871
+ context 'when started_at is set to a value greater than 0' do
872
+ it 'displays unknown time until finished when passed the "%e" flag' do
873
+ progressbar = ProgressBar::Base.new(:starting_at => 1)
874
+ expect(progressbar.to_s('%e')).to match(/^ ETA: \?\?:\?\?:\?\?\z/)
875
+ end
876
+ end
877
+ end
878
+
879
+ context 'when called after #start' do
880
+ let(:progressbar) do
881
+ Timecop.travel(-3_723) do
882
+ progressbar = ProgressBar::Base.new(:starting_at => 0, :output => output, :smoothing => 0.0)
883
+ progressbar.start
884
+ progressbar.progress = 50
885
+ progressbar
886
+ end
887
+ end
888
+
889
+ context 'and the bar is reset' do
890
+ before { progressbar.reset }
891
+
892
+ it 'displays "??:??:??" until finished when passed the "%e" flag' do
893
+ expect(progressbar.to_s('%e')).to match(/^ ETA: \?\?:\?\?:\?\?\z/)
894
+ end
895
+ end
896
+
897
+ it 'displays the estimated time remaining when using the "%e" flag' do
898
+ expect(progressbar.to_s('%e')).to match(/^ ETA: 01:02:03\z/)
899
+ end
900
+ end
901
+
902
+ context 'when it could take 100 hours or longer to finish' do
903
+ let(:progressbar) do
904
+ Timecop.travel(-120_000) do
905
+ progressbar = ProgressBar::Base.new(:starting_at => 0, :total => 100, :output => output, :smoothing => 0.0)
906
+ progressbar.start
907
+ progressbar.progress = 25
908
+ progressbar
909
+ end
910
+ end
911
+
912
+ it 'displays "> 4 Days" until finished when passed the "%E" flag' do
913
+ expect(progressbar.to_s('%E')).to match(/^ ETA: > 4 Days\z/)
914
+ end
915
+
916
+ it 'displays "??:??:??" until finished when passed the "%e" flag' do
917
+ expect(progressbar.to_s('%e')).to match(/^ ETA: \?\?:\?\?:\?\?\z/)
918
+ end
919
+
920
+ it 'displays the exact estimated time until finished when passed the "%f" flag' do
921
+ expect(progressbar.to_s('%f')).to match(/^ ETA: 100:00:00\z/)
922
+ end
923
+ end
924
+ end
925
+ end
926
+
927
+ context 'when the bar is started after having total set to 0' do
928
+ let(:progressbar) { ProgressBar::Base.new(:output => output, :autostart => false) }
929
+
930
+ it 'does not throw an error' do
931
+ progressbar.total = 0
932
+
933
+ expect { progressbar.start }.not_to raise_error
934
+ end
935
+ end
936
+
937
+ context 'when the bar has no items to process' do
938
+ context 'and it has not been started' do
939
+ let(:progressbar) { ProgressBar::Base.new(:started_at => 0, :total => 0, :autostart => false, :smoothing => 0.0, :format => ' %c/%C |%w>%i| %e ', :output => output) }
940
+
941
+ it 'does not throw an error if told to stop' do
942
+ progressbar.stop
943
+
944
+ expect { progressbar.start }.not_to raise_error
945
+ end
946
+ end
947
+ end
948
+ end
949
+ # rubocop:enable Metrics/LineLength