progress 3.0.2 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,9 +1,11 @@
1
1
  require 'enumerator'
2
2
  require 'progress/with_progress'
3
3
 
4
+ # Add with_progress method to Enumerable
4
5
  module Enumerable
5
6
  # run any Enumerable method with progress
6
- # methods which don't necessarily go through all items (like find, any? or all?) will not show 100%
7
+ # methods which don't necessarily go through all items (like find, any? or
8
+ # all?) will not show 100%
7
9
  # ==== Example
8
10
  # [1, 2, 3].with_progress('Numbers').each do |number|
9
11
  # # code
@@ -1,4 +1,5 @@
1
1
  class Progress
2
+ # Estimate time of arrival
2
3
  class Eta
3
4
  def initialize
4
5
  @started_at = Time.now
@@ -6,9 +7,8 @@ class Progress
6
7
 
7
8
  def left(completed)
8
9
  seconds = seconds_left(completed)
9
- if seconds && seconds > 0
10
- seconds_to_string(seconds)
11
- end
10
+ return unless seconds && seconds > 0
11
+ seconds_to_string(seconds)
12
12
  end
13
13
 
14
14
  def elapsed
@@ -18,31 +18,29 @@ class Progress
18
18
  private
19
19
 
20
20
  def seconds_to_string(seconds)
21
- if seconds
22
- case seconds
23
- when 0...60
24
- '%.0fs' % seconds
25
- when 60...3600
26
- '%.1fm' % (seconds / 60)
27
- when 3600...86400
28
- '%.1fh' % (seconds / 3600)
29
- else
30
- '%.1fd' % (seconds / 86400)
31
- end
21
+ return unless seconds
22
+ case seconds
23
+ when 0...60
24
+ format '%.0fs', seconds
25
+ when 60...3600
26
+ format '%.1fm', seconds / 60
27
+ when 3600...86_400
28
+ format '%.1fh', seconds / 3600
29
+ else
30
+ format '%.1fd', seconds / 86_400
32
31
  end
33
32
  end
34
33
 
35
34
  def seconds_left(completed)
36
35
  now = Time.now
37
- if completed > 0 && now - @started_at >= 1
38
- current_eta = @started_at + (now - @started_at) / completed
39
- @left = if @left
40
- @left + (current_eta - @left) * (1 + completed) * 0.5
41
- else
42
- current_eta
43
- end
44
- @left - now
36
+ return unless completed > 0 && now - @started_at >= 1
37
+ current_eta = @started_at + (now - @started_at) / completed
38
+ @left = if @left
39
+ @left + (current_eta - @left) * (1 + completed) * 0.5
40
+ else
41
+ current_eta
45
42
  end
43
+ @left - now
46
44
  end
47
45
  end
48
46
  end
@@ -1,5 +1,6 @@
1
1
  require 'progress'
2
2
 
3
+ # Add times_with_progress method to Integer
3
4
  class Integer
4
5
  # run `times` with progress
5
6
  # 100.times_with_progress('Numbers') do |number|
@@ -0,0 +1,9 @@
1
+ require 'progress'
2
+
3
+ # Add Progress method as alias to Progress.start
4
+ module Kernel
5
+ define_method :Progress do |*args, &block|
6
+ Progress.start(*args, &block)
7
+ end
8
+ private :Progress
9
+ end
@@ -1,30 +1,37 @@
1
1
  require 'progress'
2
+ require 'stringio'
2
3
 
3
4
  class Progress
5
+ # Handling with_progress
4
6
  class WithProgress
5
- attr_reader :enumerable, :title
7
+ attr_reader :enum, :title
8
+ alias_method :enumerable, :enum
9
+
10
+ # If block given run each on instance otherwise return instance
11
+ def self.new(*args, &block)
12
+ block ? super.each(&block) : super
13
+ end
6
14
 
7
15
  # initialize with object responding to each, title and optional length
8
16
  # if block is provided, it is passed to each
9
- def initialize(enumerable, title, length = nil, &block)
10
- @enumerable, @title, @length = enumerable, title, length
11
- each(&block) if block
17
+ def initialize(enum, title = nil, length = nil)
18
+ @enum, @title, @length = enum, title, length
12
19
  end
13
20
 
14
21
  # returns self but changes title
15
22
  def with_progress(title = nil, length = nil, &block)
16
- self.class.new(@enumerable, title, length || @length, &block)
23
+ self.class.new(@enum, title, length || @length, &block)
17
24
  end
18
25
 
19
26
  # befriend with in_threads gem
20
27
  def in_threads(*args, &block)
21
- @enumerable.in_threads(*args).with_progress(@title, @length, &block)
28
+ @enum.in_threads(*args).with_progress(@title, @length, &block)
22
29
  rescue
23
30
  super
24
31
  end
25
32
 
26
- def respond_to?(sym, include_private = false)
27
- enumerable_method?(method) || super(sym, include_private)
33
+ def respond_to?(method, include_private = false)
34
+ enumerable_method?(method) || super
28
35
  end
29
36
 
30
37
  def method_missing(method, *args, &block)
@@ -42,54 +49,95 @@ class Progress
42
49
  end
43
50
 
44
51
  def run(method, *args, &block)
45
- enumerable = case
52
+ case
53
+ when !block
54
+ run_without_block(@enum, method, *args)
46
55
  when @length
47
- @enumerable
48
- when
49
- @enumerable.is_a?(String),
50
- @enumerable.is_a?(IO),
51
- Object.const_defined?(:StringIO) && @enumerable.is_a?(StringIO),
52
- Object.const_defined?(:TempFile) && @enumerable.is_a?(TempFile)
53
- warn "Progress: collecting elements for instance of class #{@enumerable.class}"
54
- lines = []
55
- @enumerable.each{ |line| lines << line }
56
- lines
56
+ run_with_length(@enum, @length, method, *args, &block)
57
+ when @enum.is_a?(String)
58
+ run_for_string(method, *args, &block)
59
+ when io?
60
+ run_for_io(method, *args, &block)
61
+ when defined?(CSV::Reader) && @enum.is_a?(CSV::Reader)
62
+ run_for_ruby18_csv(method, *args, &block)
57
63
  else
58
- @enumerable
64
+ run_with_length(@enum, enum_length(@enum), method, *args, &block)
59
65
  end
66
+ end
60
67
 
61
- length = case
62
- when @length
63
- @length
64
- when enumerable.respond_to?(:size)
65
- enumerable.size
66
- when enumerable.respond_to?(:length)
67
- enumerable.length
68
- else
69
- enumerable.count
68
+ def run_for_string(method, *args, &block)
69
+ with_substitute(StringIO.new(@enum)) do |io|
70
+ run_with_pos(io, method, *args, &block)
70
71
  end
72
+ end
71
73
 
72
- if block
73
- result = Progress.start(@title, length) do
74
- enumerable.send(method, *args) do |*block_args|
75
- Progress.step do
76
- block.call(*block_args)
77
- end
78
- end
74
+ def run_for_io(method, *args, &block)
75
+ if io_pos?
76
+ run_with_pos(@enum, method, *args, &block)
77
+ else
78
+ warn "Progress: can't get #{@enum.class} pos, collecting elements"
79
+ with_substitute(@enum.to_a) do |lines|
80
+ run_with_length(lines, lines.length, method, *args, &block)
79
81
  end
80
- if result.eql?(enumerable)
81
- @enumerable
82
- else
83
- result
82
+ end
83
+ end
84
+
85
+ def run_for_ruby18_csv(method, *args, &block)
86
+ warn "Progress: #{@enum.class} doesn't expose IO, collecting elements"
87
+ with_substitute(@enum.to_a) do |lines|
88
+ run_with_length(lines, lines.length, method, *args, &block)
89
+ end
90
+ end
91
+
92
+ def run_without_block(enum, method, *args)
93
+ Progress.start(@title) do
94
+ Progress.step do
95
+ enum.send(method, *args)
84
96
  end
85
- else
86
- Progress.start(@title) do
97
+ end
98
+ end
99
+
100
+ def run_with_length(enum, length, method, *args, &block)
101
+ Progress.start(@title, length) do
102
+ enum.send(method, *args) do |*block_args|
87
103
  Progress.step do
88
- enumerable.send(method, *args)
104
+ block.call(*block_args)
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ def run_with_pos(io, method, *args, &block)
111
+ size = io.respond_to?(:size) ? io.size : io.stat.size
112
+ Progress.start(@title, size) do
113
+ io.send(method, *args) do |*block_args|
114
+ Progress.set(io.pos) do
115
+ block.call(*block_args)
89
116
  end
90
117
  end
91
118
  end
92
119
  end
93
120
 
121
+ def with_substitute(enum)
122
+ result = yield enum
123
+ result.eql?(enum) ? @enum : result
124
+ end
125
+
126
+ def io?
127
+ @enum.respond_to?(:pos) &&
128
+ (@enum.respond_to?(:size) || @enum.respond_to?(:stat))
129
+ end
130
+
131
+ def io_pos?
132
+ @enum.pos; true
133
+ rescue Errno::ESPIPE
134
+ false
135
+ end
136
+
137
+ def enum_length(enum)
138
+ enum.respond_to?(:size) && enum.size ||
139
+ enum.respond_to?(:length) && enum.length ||
140
+ enum.count
141
+ end
94
142
  end
95
143
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'progress'
5
- s.version = '3.0.2'
5
+ s.version = '3.1.0'
6
6
  s.summary = %q{Show progress of long running tasks}
7
7
  s.homepage = "http://github.com/toy/#{s.name}"
8
8
  s.authors = ['Ivan Kuchin']
@@ -15,5 +15,8 @@ Gem::Specification.new do |s|
15
15
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
16
  s.require_paths = %w[lib]
17
17
 
18
- s.add_development_dependency 'rspec'
18
+ s.add_development_dependency 'rspec', '~> 3.0'
19
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9.3')
20
+ s.add_development_dependency 'rubocop', '~> 0.27'
21
+ end
19
22
  end
@@ -1,55 +1,56 @@
1
- $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
2
1
  require 'rspec'
3
2
  require 'progress'
3
+ require 'tempfile'
4
+ require 'shellwords'
5
+ require 'csv'
4
6
 
5
7
  describe Progress do
6
-
7
8
  before do
8
9
  Progress.stay_on_line = true
9
10
  Progress.highlight = true
10
- Progress.set_terminal_title = true
11
+ Progress.terminal_title = true
11
12
 
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')
16
- end
13
+ allow(Progress).to receive(:start_beeper)
14
+ allow(Progress).to receive(:time_to_print?).and_return(true)
17
15
 
18
- describe "integrity" do
16
+ eta = instance_double(Progress::Eta, :left => nil, :elapsed => '0s')
17
+ allow(Progress).to receive(:eta).and_return(eta)
18
+ end
19
19
 
20
+ describe 'integrity' do
20
21
  before do
21
- @io = double(:<< => nil, :tty? => true)
22
- Progress.stub(:io).and_return(@io)
22
+ io = double(:<< => nil, :tty? => true)
23
+ allow(Progress).to receive(:io).and_return(io)
23
24
  end
24
25
 
25
- it "should return result from start block" do
26
- Progress.start('Test') do
26
+ it 'returns result from start block' do
27
+ expect(Progress.start('Test') do
27
28
  'test'
28
- end.should == 'test'
29
+ end).to eq('test')
29
30
  end
30
31
 
31
- it "should return result from step block" do
32
+ it 'returns result from step block' do
32
33
  Progress.start 1 do
33
- Progress.step{ 'test' }.should == 'test'
34
+ expect(Progress.step{ 'test' }).to eq('test')
34
35
  end
35
36
  end
36
37
 
37
- it "should return result from set block" do
38
+ it 'returns result from set block' do
38
39
  Progress.start 1 do
39
- Progress.set(1){ 'test' }.should == 'test'
40
+ expect(Progress.set(1){ 'test' }).to eq('test')
40
41
  end
41
42
  end
42
43
 
43
- it "should return result from nested block" do
44
- [1, 2, 3].with_progress.map do |a|
44
+ it 'returns result from nested block' do
45
+ expect([1, 2, 3].with_progress.map do |a|
45
46
  [1, 2, 3].with_progress.map do |b|
46
47
  a * b
47
48
  end
48
- end.should == [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
49
+ end).to eq([[1, 2, 3], [2, 4, 6], [3, 6, 9]])
49
50
  end
50
51
 
51
- it "should not raise errors on extra step or stop" do
52
- proc{
52
+ it 'does not raise errors on extra step or stop' do
53
+ expect do
53
54
  3.times_with_progress do
54
55
  Progress.start 'simple' do
55
56
  Progress.step
@@ -61,180 +62,235 @@ describe Progress do
61
62
  end
62
63
  Progress.step
63
64
  Progress.stop
64
- }.should_not raise_error
65
+ end.not_to raise_error
65
66
  end
66
67
 
67
68
  describe Enumerable do
69
+ let(:enum){ 0...1000 }
68
70
 
69
- before :each do
70
- @a = 0...1000
71
- end
72
-
73
- describe "with_progress" do
71
+ describe 'with_progress' do
72
+ it 'returns with block same as when called with each' do
73
+ expect(enum.with_progress{}).to eq(enum.with_progress.each{})
74
+ end
74
75
 
75
- it "should not break each" do
76
- reference = @a.each
77
- @a.with_progress.each do |n|
78
- n.should == reference.next
76
+ it 'does not break each' do
77
+ reference = enum.each
78
+ enum.with_progress.each do |n|
79
+ expect(n).to eq(reference.next)
79
80
  end
80
- proc{ reference.next }.should raise_error(StopIteration)
81
+ expect{ reference.next }.to raise_error(StopIteration)
81
82
  end
82
83
 
83
- it "should not break find" do
84
+ it 'does not break find' do
84
85
  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 }
86
+ expect(enum.with_progress.find{ |n| n == 100 }).
87
+ to eq(enum.find{ |n| n == 100 })
88
+ expect(enum.with_progress.find{ |n| n == 10_000 }).
89
+ to eq(enum.find{ |n| n == 10_000 })
90
+ expect(enum.with_progress.find(default){ |n| n == 10_000 }).
91
+ to eq(enum.find(default){ |n| n == 10_000 })
88
92
  end
89
93
 
90
- it "should not break map" do
91
- @a.with_progress.map{ |n| n * n }.should == @a.map{ |n| n * n }
94
+ it 'does not break map' do
95
+ expect(enum.with_progress.map{ |n| n**2 }).to eq(enum.map{ |n| n**2 })
92
96
  end
93
97
 
94
- it "should not break grep" do
95
- @a.with_progress.grep(100).should == @a.grep(100)
98
+ it 'does not break grep' do
99
+ expect(enum.with_progress.grep(100)).to eq(enum.grep(100))
96
100
  end
97
101
 
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
102
+ it 'does not break each_cons' do
103
+ reference = enum.each_cons(3)
104
+ enum.with_progress.each_cons(3) do |values|
105
+ expect(values).to eq(reference.next)
102
106
  end
103
- proc{ reference.next }.should raise_error(StopIteration)
107
+ expect{ reference.next }.to raise_error(StopIteration)
104
108
  end
105
109
 
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)
110
+ describe 'with_progress.with_progress' do
111
+ it 'does not change existing instance' do
112
+ wp = enum.with_progress('hello')
113
+ expect{ wp.with_progress('world') }.not_to change(wp, :title)
111
114
  end
112
115
 
113
- it "should create new instance with different title when called on WithProgress" do
114
- wp = @a.with_progress('hello')
116
+ it 'returns new instance with different title' do
117
+ wp = enum.with_progress('hello')
115
118
  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
119
+ expect(wp.title).to eq('hello')
120
+ expect(wp_wp.title).to eq('world')
121
+ expect(wp_wp).not_to eq(wp)
122
+ expect(wp_wp.enumerable).to eq(wp.enumerable)
120
123
  end
121
-
122
124
  end
123
125
 
124
- describe "calls to each" do
126
+ describe 'collections' do
127
+ [
128
+ [1, 2, 3],
129
+ {1 => 1, 2 => 2, 3 => 3},
130
+ [1, 2, 3].to_set,
131
+ ].each do |enum|
132
+ it "calls each only once for #{enum.class}" do
133
+ expect(enum).to receive(:each).once.and_call_original
134
+ expect(enum.with_progress.each{}).to eq(enum)
135
+ end
125
136
 
126
- def without_warnings
127
- verbosity = $VERBOSE
128
- $VERBOSE = nil
129
- result = yield
130
- $VERBOSE = verbosity
131
- result
137
+ it "yields same objects for #{enum.class}" do
138
+ expect(enum.with_progress.entries).to eq(enum.entries)
139
+ end
132
140
  end
133
141
 
134
- it "should call each only once for Array" do
135
- enum = [1, 2, 3]
136
- enum.should_receive(:each).once.and_return(enum)
137
- enum.with_progress.each{ }.should == enum
142
+ [
143
+ 100.times,
144
+ 'a'..'z',
145
+ ].each do |enum|
146
+ it "calls each twice for #{enum.class}" do
147
+ enum_each = enum.each{}
148
+ expect(enum).to receive(:each).at_most(:twice).and_call_original
149
+ expect(enum.with_progress.each{}).to eq(enum_each)
150
+ end
151
+
152
+ it "yields same objects for #{enum.class}" do
153
+ expect(enum.with_progress.entries).to eq(enum.entries)
154
+ end
138
155
  end
156
+ end
139
157
 
140
- it "should call each only once for Hash" do
141
- enum = {1 => 1, 2 => 2, 3 => 3}
142
- enum.should_receive(:each).once.and_return(enum)
143
- enum.with_progress.each{ }.should == enum
158
+ describe String do
159
+ it 'calls each only once on StringIO' do
160
+ enum = "a\nb\nc"
161
+ expect(enum).not_to receive(:each)
162
+ io = StringIO.new(enum)
163
+ expect(StringIO).to receive(:new).with(enum).and_return(io)
164
+ expect(io).to receive(:each).once.and_call_original
165
+
166
+ with_progress = Progress::WithProgress.new(enum)
167
+ expect(with_progress).not_to receive(:warn)
168
+ expect(with_progress.each{}).to eq(enum)
144
169
  end
145
170
 
146
- it "should call each only once for Set" do
147
- enum = [1, 2, 3].to_set
148
- enum.should_receive(:each).once.and_return(enum)
149
- enum.with_progress.each{ }.should == enum
171
+ it 'yields same lines' do
172
+ enum = "a\nb\nc"
173
+ lines = []
174
+ Progress::WithProgress.new(enum).each{ |line| lines << line }
175
+ expect(lines).to eq(enum.lines.to_a)
150
176
  end
177
+ end
151
178
 
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.and_return(enum)
156
- without_warnings do
157
- enum.with_progress.each{ }.should == enum
158
- end
179
+ describe IO do
180
+ [
181
+ File.open(__FILE__),
182
+ StringIO.new(File.read(__FILE__)),
183
+ ].each do |enum|
184
+ it "calls each only once for #{enum.class}" do
185
+ expect(enum).to receive(:each).once.and_call_original
186
+
187
+ with_progress = enum.with_progress
188
+ expect(with_progress).not_to receive(:warn)
189
+ expect(with_progress.each{}).to eq(enum)
159
190
  end
160
191
  end
161
192
 
162
- it "should call each only once for File (IO)" do
163
- enum = File.open(__FILE__)
164
- enum.should_receive(:each).once.and_return(enum)
165
- without_warnings do
166
- enum.with_progress.each{ }.should == enum
193
+ it 'calls each only once for Tempfile' do
194
+ enum = Tempfile.open('progress')
195
+ enum_each = enum.each{} # returns underlying File
196
+ expect(enum_each).to receive(:each).once.and_call_original
197
+
198
+ with_progress = enum.with_progress
199
+ expect(with_progress).not_to receive(:warn)
200
+ expect(with_progress.each{}).to eq(enum_each)
201
+ end
202
+
203
+ it 'calls each only once for IO and shows warning' do
204
+ enum = IO.popen("cat #{__FILE__.shellescape}")
205
+ expect(enum).to receive(:each).once.and_call_original
206
+
207
+ with_progress = enum.with_progress
208
+ expect(with_progress).to receive(:warn)
209
+ expect(with_progress.each{}).to eq(enum)
210
+ end
211
+
212
+ [
213
+ File.open(__FILE__),
214
+ StringIO.new(File.read(__FILE__)),
215
+ Tempfile.open('progress').tap do |f|
216
+ f.write(File.read(__FILE__))
217
+ f.rewind
218
+ end,
219
+ IO.popen("cat #{__FILE__.shellescape}"),
220
+ ].each do |enum|
221
+ it "yields same lines for #{enum.class}" do
222
+ expect(enum.with_progress.entries).to eq(File.readlines(__FILE__))
167
223
  end
168
224
  end
225
+ end
169
226
 
170
- it "should call each only once for StringIO" do
171
- enum = StringIO.new("a\nb\nc")
172
- enum.should_receive(:each).once.and_return(enum)
173
- without_warnings do
174
- enum.with_progress.each{ }.should == enum
227
+ describe CSV do
228
+ if CSV.method_defined?(:pos)
229
+ it 'calls each only once for CSV' do
230
+ enum = CSV.open('spec/test.csv')
231
+ expect(enum).to receive(:each).once.and_call_original
232
+
233
+ with_progress = enum.with_progress
234
+ expect(with_progress).not_to receive(:warn)
235
+ expect(with_progress.each{}).to eq(nil)
236
+ end
237
+ else
238
+ it 'calls each only once for CSV and shows warning' do
239
+ enum = CSV.open('spec/test.csv', 'r')
240
+ expect(enum).to receive(:each).once.and_call_original
241
+
242
+ with_progress = enum.with_progress
243
+ expect(with_progress).to receive(:warn)
244
+ expect(with_progress.each{}).to eq(enum)
175
245
  end
176
246
  end
177
247
 
248
+ it 'yields same lines for CSV' do
249
+ csv = proc{ CSV.open('spec/test.csv', 'r') }
250
+ expect(csv[].with_progress.entries).to eq(csv[].entries)
251
+ end
178
252
  end
179
253
  end
180
254
  end
181
255
 
182
256
  describe Integer do
183
-
184
257
  let(:count){ 108 }
185
258
 
186
- it "should not break times_with_progress" do
259
+ it 'does not break times_with_progress' do
187
260
  reference = count.times
188
261
  count.times_with_progress do |i|
189
- i.should == reference.next
262
+ expect(i).to eq(reference.next)
190
263
  end
191
- proc{ reference.next }.should raise_error(StopIteration)
264
+ expect{ reference.next }.to raise_error(StopIteration)
192
265
  end
193
266
 
194
- it "should not break times.with_progress" do
267
+ it 'does not break times.with_progress' do
195
268
  reference = count.times
196
269
  count.times.with_progress do |i|
197
- i.should == reference.next
270
+ expect(i).to eq(reference.next)
198
271
  end
199
- proc{ reference.next }.should raise_error(StopIteration)
272
+ expect{ reference.next }.to raise_error(StopIteration)
200
273
  end
201
-
202
274
  end
203
-
204
275
  end
205
276
 
206
- describe "output" do
207
-
208
- class ChunkIo
209
- attr_reader :chunks
210
- def initialize
211
- @chunks = []
212
- end
213
-
214
- def <<(data)
215
- @chunks << data.to_s
216
- end
277
+ describe 'output' do
278
+ def stub_progress_io(io)
279
+ allow(io).to receive(:tty?).and_return(true)
280
+ allow(Progress).to receive(:io).and_return(io)
217
281
  end
218
282
 
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
-
283
+ describe 'validity' do
228
284
  def run_example_progress
229
285
  Progress.start 5, 'Test' do
230
286
  Progress.step 2, 'simle'
231
287
 
232
288
  Progress.step 2, 'times' do
233
- 3.times.with_progress {}
289
+ 3.times.with_progress{}
234
290
  end
235
291
 
236
292
  Progress.step 'enum' do
237
- 3.times.to_a.with_progress {}
293
+ 3.times.to_a.with_progress{}
238
294
  end
239
295
  end
240
296
  end
@@ -247,6 +303,10 @@ describe Progress do
247
303
  "\e[1m#{s}\e[0m"
248
304
  end
249
305
 
306
+ def unhl(s)
307
+ s.gsub(/\e\[\dm/, '')
308
+ end
309
+
250
310
  def on_line(s)
251
311
  "\r" + s + "\e[K"
252
312
  end
@@ -255,70 +315,80 @@ describe Progress do
255
315
  s + "\n"
256
316
  end
257
317
 
258
- it "should produce valid output when staying on line" do
318
+ def on_line_n_title(s)
319
+ [on_line(s), title(unhl(s))]
320
+ end
321
+
322
+ def line_n_title(s)
323
+ [line(s), title(unhl(s))]
324
+ end
325
+
326
+ it 'produces valid output when staying on line' do
259
327
  Progress.stay_on_line = true
260
328
 
261
- @io = stub_progress_io(ChunkIo)
329
+ stub_progress_io(io = StringIO.new)
262
330
  run_example_progress
263
331
 
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
- ]
332
+ expect(io.string).to eq([
333
+ on_line_n_title("Test: #{hl '......'}"),
334
+ on_line_n_title("Test: #{hl ' 40.0%'} - simle"),
335
+ on_line_n_title("Test: #{hl ' 40.0%'} > #{hl '......'}"),
336
+ on_line_n_title("Test: #{hl ' 53.3%'} > #{hl ' 33.3%'}"),
337
+ on_line_n_title("Test: #{hl ' 66.7%'} > #{hl ' 66.7%'}"),
338
+ on_line_n_title("Test: #{hl ' 80.0%'} > 100.0%"),
339
+ on_line_n_title("Test: #{hl ' 80.0%'} - times"),
340
+ on_line_n_title("Test: #{hl ' 80.0%'} > #{hl '......'}"),
341
+ on_line_n_title("Test: #{hl ' 86.7%'} > #{hl ' 33.3%'}"),
342
+ on_line_n_title("Test: #{hl ' 93.3%'} > #{hl ' 66.7%'}"),
343
+ on_line_n_title('Test: 100.0% > 100.0%'),
344
+ on_line_n_title('Test: 100.0% - enum'),
345
+ on_line('Test: 100.0% (elapsed: 0s) - enum') + "\n",
346
+ title(''),
347
+ ].flatten.join)
279
348
  end
280
349
 
281
- it "should produce valid output when not staying on line" do
350
+ it 'produces valid output when not staying on line' do
282
351
  Progress.stay_on_line = false
283
352
 
284
- @io = stub_progress_io(ChunkIo)
353
+ stub_progress_io(io = StringIO.new)
285
354
  run_example_progress
286
355
 
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
- ]
356
+ expect(io.string).to eq([
357
+ line_n_title("Test: #{hl '......'}"),
358
+ line_n_title("Test: #{hl ' 40.0%'} - simle"),
359
+ line_n_title("Test: #{hl ' 40.0%'} > #{hl '......'}"),
360
+ line_n_title("Test: #{hl ' 53.3%'} > #{hl ' 33.3%'}"),
361
+ line_n_title("Test: #{hl ' 66.7%'} > #{hl ' 66.7%'}"),
362
+ line_n_title("Test: #{hl ' 80.0%'} > 100.0%"),
363
+ line_n_title("Test: #{hl ' 80.0%'} - times"),
364
+ line_n_title("Test: #{hl ' 80.0%'} > #{hl '......'}"),
365
+ line_n_title("Test: #{hl ' 86.7%'} > #{hl ' 33.3%'}"),
366
+ line_n_title("Test: #{hl ' 93.3%'} > #{hl ' 66.7%'}"),
367
+ line_n_title('Test: 100.0% > 100.0%'),
368
+ line_n_title('Test: 100.0% - enum'),
369
+ line('Test: 100.0% (elapsed: 0s) - enum'),
370
+ title(''),
371
+ ].flatten.join)
302
372
  end
303
-
304
373
  end
305
374
 
306
- describe "different call styles" do
307
-
375
+ describe 'different call styles' do
308
376
  let(:count_a){ 13 }
309
377
  let(:count_b){ 17 }
310
-
311
- before do
312
- reference_io = stub_progress_io(StringIO)
378
+ let(:reference_output) do
379
+ stub_progress_io(reference_io = StringIO.new)
313
380
  count_a.times.with_progress('Test') do
314
- count_b.times.with_progress {}
381
+ count_b.times.with_progress{}
315
382
  end
316
- @reference_output = reference_io.string
383
+ reference_io.string
384
+ end
385
+ let(:io){ StringIO.new }
317
386
 
318
- @io = stub_progress_io(StringIO)
387
+ before do
388
+ stub_progress_io(io)
319
389
  end
320
390
 
321
- it "should output same when called without block" do
391
+ it 'outputs same when called without block' do
322
392
  Progress(count_a, 'Test')
323
393
  count_a.times do
324
394
  Progress.step do
@@ -330,10 +400,10 @@ describe Progress do
330
400
  end
331
401
  end
332
402
  Progress.stop
333
- @io.string.should == @reference_output
403
+ expect(io.string).to eq(reference_output)
334
404
  end
335
405
 
336
- it "should output same when called with block" do
406
+ it 'outputs same when called with block' do
337
407
  Progress(count_a, 'Test') do
338
408
  count_a.times do
339
409
  Progress.step do
@@ -345,18 +415,15 @@ describe Progress do
345
415
  end
346
416
  end
347
417
  end
348
- @io.string.should == @reference_output
418
+ expect(io.string).to eq(reference_output)
349
419
  end
350
420
 
351
- it "should output same when called using with_progress on list" do
421
+ it 'outputs same when called using with_progress on list' do
352
422
  count_a.times.to_a.with_progress('Test') do
353
- count_b.times.to_a.with_progress {}
423
+ count_b.times.to_a.with_progress{}
354
424
  end
355
- @io.string.should == @reference_output
425
+ expect(io.string).to eq(reference_output)
356
426
  end
357
-
358
427
  end
359
-
360
428
  end
361
-
362
429
  end