image_optim 0.22.1 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +8 -8
  2. data/.appveyor.yml +95 -0
  3. data/.rubocop.yml +3 -0
  4. data/.travis.yml +27 -22
  5. data/CHANGELOG.markdown +10 -0
  6. data/CONTRIBUTING.markdown +2 -1
  7. data/Gemfile +1 -1
  8. data/README.markdown +10 -2
  9. data/image_optim.gemspec +4 -4
  10. data/lib/image_optim.rb +32 -16
  11. data/lib/image_optim/bin_resolver/bin.rb +11 -4
  12. data/lib/image_optim/cache.rb +71 -0
  13. data/lib/image_optim/cache_path.rb +16 -0
  14. data/lib/image_optim/config.rb +12 -2
  15. data/lib/image_optim/handler.rb +1 -1
  16. data/lib/image_optim/image_meta.rb +5 -10
  17. data/lib/image_optim/optimized_path.rb +25 -0
  18. data/lib/image_optim/path.rb +70 -0
  19. data/lib/image_optim/runner/option_parser.rb +13 -0
  20. data/lib/image_optim/worker.rb +5 -8
  21. data/lib/image_optim/worker/class_methods.rb +3 -1
  22. data/lib/image_optim/worker/jpegoptim.rb +3 -0
  23. data/lib/image_optim/worker/jpegrecompress.rb +3 -0
  24. data/lib/image_optim/worker/pngquant.rb +3 -0
  25. data/script/worker_analysis +10 -9
  26. data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +1 -1
  27. data/spec/image_optim/bin_resolver/simple_version_spec.rb +48 -40
  28. data/spec/image_optim/bin_resolver_spec.rb +190 -172
  29. data/spec/image_optim/cache_path_spec.rb +59 -0
  30. data/spec/image_optim/cache_spec.rb +159 -0
  31. data/spec/image_optim/cmd_spec.rb +11 -7
  32. data/spec/image_optim/config_spec.rb +92 -71
  33. data/spec/image_optim/handler_spec.rb +3 -6
  34. data/spec/image_optim/image_meta_spec.rb +61 -0
  35. data/spec/image_optim/optimized_path_spec.rb +58 -0
  36. data/spec/image_optim/option_helpers_spec.rb +25 -0
  37. data/spec/image_optim/path_spec.rb +105 -0
  38. data/spec/image_optim/railtie_spec.rb +6 -6
  39. data/spec/image_optim/runner/glob_helpers_spec.rb +2 -6
  40. data/spec/image_optim/runner/option_parser_spec.rb +3 -3
  41. data/spec/image_optim/space_spec.rb +16 -18
  42. data/spec/image_optim/worker/optipng_spec.rb +3 -3
  43. data/spec/image_optim/worker/pngquant_spec.rb +47 -7
  44. data/spec/image_optim/worker_spec.rb +114 -17
  45. data/spec/image_optim_spec.rb +58 -69
  46. data/spec/images/broken_jpeg +1 -0
  47. data/spec/spec_helper.rb +40 -10
  48. metadata +30 -8
  49. data/lib/image_optim/image_path.rb +0 -68
  50. data/spec/image_optim/image_path_spec.rb +0 -54
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
  require 'image_optim/bin_resolver/comparable_condition'
3
3
 
4
4
  describe ImageOptim::BinResolver::ComparableCondition do
5
- is = ImageOptim::BinResolver::ComparableCondition.is
5
+ let(:is){ ImageOptim::BinResolver::ComparableCondition.is }
6
6
 
7
7
  it 'builds conditions' do
8
8
  expect(is.between?(10, 20).method).to eq(:between?)
@@ -2,56 +2,64 @@ require 'spec_helper'
2
2
  require 'image_optim/bin_resolver/simple_version'
3
3
 
4
4
  describe ImageOptim::BinResolver::SimpleVersion do
5
- helpers = Module.new do
6
- def v(str)
7
- ImageOptim::BinResolver::SimpleVersion.new(str)
8
- end
9
- end
10
- include helpers
11
- extend helpers
12
-
13
- describe 'compares version 1.17' do
14
- subject{ v '1.17' }
15
-
16
- it{ is_expected.to be > '0' }
17
- it{ is_expected.to be > '0.1' }
18
- it{ is_expected.to be > '0.9' }
19
- it{ is_expected.to be > '1.9' }
20
- it{ is_expected.to be < '1.17.1' }
21
- it{ is_expected.to be < '1.99' }
22
- it{ is_expected.to be < '2.1' }
5
+ def v(str)
6
+ described_class.new(str)
23
7
  end
24
8
 
25
- describe 'normalization' do
26
- %w[
27
- 1
28
- 01
29
- 1.0
30
- 1.00
31
- 1.0.0
32
- 1.0.0.0
33
- ].each do |variation|
34
- it "normalizes #{variation}" do
35
- expect(v(variation)).to eq(1)
9
+ describe '#initialize' do
10
+ describe 'conversion' do
11
+ it 'converts Integer' do
12
+ expect(v(117)).to eq('117')
36
13
  end
37
- end
38
- end
39
14
 
40
- describe 'conversion' do
41
- it 'converts Integer' do
42
- expect(v(117)).to eq('117')
15
+ it 'converts Float' do
16
+ expect(v(1.17)).to eq('1.17')
17
+ end
18
+
19
+ it 'converts String' do
20
+ expect(v('1.17')).to eq('1.17')
21
+ end
22
+
23
+ it 'converts self' do
24
+ expect(v(v(1.17))).to eq('1.17')
25
+ end
43
26
  end
44
27
 
45
- it 'converts Float' do
46
- expect(v(1.17)).to eq('1.17')
28
+ describe 'normalization' do
29
+ %w[
30
+ 1
31
+ 01
32
+ 1.0
33
+ 1.00
34
+ 1.0.0
35
+ 1.0.0.0
36
+ ].each do |variation|
37
+ it "normalizes #{variation}" do
38
+ expect(v(variation)).to eq(1)
39
+ end
40
+ end
47
41
  end
42
+ end
48
43
 
49
- it 'converts String' do
50
- expect(v('1.17')).to eq('1.17')
44
+ describe '#to_s' do
45
+ it 'returns the original value converted to String' do
46
+ expect(v(117).to_s).to eq('117')
47
+ expect(v(1.17).to_s).to eq('1.17')
48
+ expect(v('0117').to_s).to eq('0117')
51
49
  end
50
+ end
51
+
52
+ describe '#<=>' do
53
+ describe 'comparing version 1.17' do
54
+ subject{ v '1.17' }
52
55
 
53
- it 'converts self' do
54
- expect(v(v(1.17))).to eq('1.17')
56
+ it{ is_expected.to be > '0' }
57
+ it{ is_expected.to be > '0.1' }
58
+ it{ is_expected.to be > '0.9' }
59
+ it{ is_expected.to be > '1.9' }
60
+ it{ is_expected.to be < '1.17.1' }
61
+ it{ is_expected.to be < '1.99' }
62
+ it{ is_expected.to be < '2.1' }
55
63
  end
56
64
  end
57
65
  end
@@ -1,13 +1,11 @@
1
1
  require 'spec_helper'
2
2
  require 'image_optim/bin_resolver'
3
3
  require 'image_optim/cmd'
4
+ require 'image_optim/path'
4
5
 
5
6
  describe ImageOptim::BinResolver do
6
- def with_env(key, value)
7
- saved, ENV[key] = ENV[key], value
8
- yield
9
- ensure
10
- ENV[key] = saved
7
+ def stub_env(key, value)
8
+ allow(ENV).to receive(:[]).with(key).and_return(value)
11
9
  end
12
10
 
13
11
  before do
@@ -15,71 +13,67 @@ describe ImageOptim::BinResolver do
15
13
  stub_const('Bin', BinResolver::Bin)
16
14
  stub_const('SimpleVersion', BinResolver::SimpleVersion)
17
15
  stub_const('Cmd', ImageOptim::Cmd)
16
+
17
+ allow(ENV).to receive(:[]).and_call_original
18
18
  end
19
19
 
20
20
  let(:image_optim){ double(:image_optim, :verbose => false, :pack => false) }
21
21
  let(:resolver){ BinResolver.new(image_optim) }
22
22
 
23
- describe :full_path do
23
+ describe '#full_path' do
24
24
  def full_path(name)
25
25
  resolver.instance_eval{ full_path(name) }
26
26
  end
27
27
 
28
- def command_v(name)
29
- path = Cmd.capture("sh -c 'command -v #{name}' 2> /dev/null").strip
30
- path unless path.empty?
31
- end
32
-
33
- it 'finds binary in path' do
34
- with_env 'PATH', 'bin' do
35
- expect(full_path('image_optim')).
36
- to eq(File.expand_path('bin/image_optim'))
37
- end
38
- end
28
+ context 'when PATHEXT is not set' do
29
+ it 'finds binary without ext in combined path' do
30
+ stub_env 'PATH', %w[/a /b /c /d].join(File::PATH_SEPARATOR)
31
+ stub_env 'PATHEXT', nil
32
+
33
+ [
34
+ [:file?, '/a/abc', false],
35
+ [:file?, '/b/abc', true],
36
+ [:executable?, '/b/abc', false],
37
+ [:file?, '/c/abc', true],
38
+ [:executable?, '/c/abc', true],
39
+ ].each do |method, path, result|
40
+ allow(File).to receive(method).
41
+ with(File.expand_path(path)).and_return(result)
42
+ end
39
43
 
40
- it 'finds bin in vendor' do
41
- with_env 'PATH', nil do
42
- expect(full_path('jpegrescan')).
43
- to eq(File.expand_path('vendor/jpegrescan'))
44
+ expect(full_path('abc')).
45
+ to eq(File.expand_path('/c/abc'))
44
46
  end
45
47
  end
46
48
 
47
- it 'finds bin in pack' do
48
- allow(image_optim).to receive(:pack).and_return(true)
49
- stub_const('ImageOptim::Pack', Class.new do
50
- def self.path
51
- 'script'
49
+ context 'when PATHEXT is set' do
50
+ it 'finds binary with ext in combined path' do
51
+ stub_env 'PATH', %w[/a /b].join(File::PATH_SEPARATOR)
52
+ stub_env 'PATHEXT', '.com;.bat'
53
+
54
+ [
55
+ [:file?, '/a/abc.com', false],
56
+ [:file?, '/a/abc.bat', true],
57
+ [:executable?, '/a/abc.bat', false],
58
+ [:file?, '/b/abc.com', true],
59
+ [:executable?, '/b/abc.com', true],
60
+ ].each do |method, path, result|
61
+ allow(File).to receive(method).
62
+ with(File.expand_path(path)).and_return(result)
52
63
  end
53
- end)
54
64
 
55
- with_env 'PATH', nil do
56
- expect(full_path('update_worker_options_in_readme')).
57
- to eq(File.expand_path('script/update_worker_options_in_readme'))
58
- end
59
- end
60
-
61
- it 'works with different path separator' do
62
- stub_const('File::PATH_SEPARATOR', 'O_o')
63
- with_env 'PATH', 'bin' do
64
- expect(full_path('image_optim')).
65
- to eq(File.expand_path('bin/image_optim'))
65
+ expect(full_path('abc')).
66
+ to eq(File.expand_path('/b/abc.com'))
66
67
  end
67
68
  end
68
69
 
69
70
  it 'returns nil on failure' do
70
- with_env 'PATH', 'lib' do
71
- expect(full_path('image_optim')).to be_nil
72
- end
73
- end
74
-
75
- %w[ls sh which bash image_optim does_not_exist].each do |name|
76
- it "returns same path as `command -v` for #{name}" do
77
- expect(full_path(name)).to eq(command_v(name))
78
- end
71
+ stub_env 'PATH', ''
72
+ expect(full_path('image_optim')).to be_nil
79
73
  end
80
74
  end
81
75
 
82
- it 'combines path in order dir:pack:path:vendor' do
76
+ it 'combines path in order dir, pack, path, vendor' do
83
77
  allow(image_optim).to receive(:pack).and_return(true)
84
78
  stub_const('ImageOptim::Pack', Class.new do
85
79
  def self.path
@@ -93,130 +87,157 @@ describe ImageOptim::BinResolver do
93
87
  'pack_path',
94
88
  ENV['PATH'],
95
89
  BinResolver::VENDOR_PATH,
96
- ].join(':'))
90
+ ].join(File::PATH_SEPARATOR))
97
91
  end
98
92
 
99
93
  it 'resolves bin in path and returns instance of Bin' do
100
- with_env 'LS_BIN', nil do
101
- expect(FSPath).not_to receive(:temp_dir)
102
- expect(resolver).to receive(:full_path).with(:ls).and_return('/bin/ls')
103
- bin = double
104
- expect(Bin).to receive(:new).with(:ls, '/bin/ls').and_return(bin)
105
- expect(bin).to receive(:check!).once
106
- expect(bin).to receive(:check_fail!).exactly(5).times
107
-
108
- 5.times do
109
- expect(resolver.resolve!(:ls)).to eq(bin)
110
- end
111
- expect(resolver.env_path).to eq([
112
- ENV['PATH'],
113
- BinResolver::VENDOR_PATH,
114
- ].join(':'))
94
+ stub_env 'LS_BIN', nil
95
+ expect(FSPath).not_to receive(:temp_dir)
96
+ expect(resolver).to receive(:full_path).with(:ls).and_return('/bin/ls')
97
+ bin = double
98
+ expect(Bin).to receive(:new).with(:ls, '/bin/ls').and_return(bin)
99
+ expect(bin).to receive(:check!).once
100
+ expect(bin).to receive(:check_fail!).exactly(5).times
101
+
102
+ 5.times do
103
+ expect(resolver.resolve!(:ls)).to eq(bin)
115
104
  end
105
+ expect(resolver.env_path).to eq([
106
+ ENV['PATH'],
107
+ BinResolver::VENDOR_PATH,
108
+ ].join(File::PATH_SEPARATOR))
116
109
  end
117
110
 
118
111
  it 'raises on failure to resolve bin' do
119
- with_env 'LS_BIN', nil do
112
+ stub_env 'LS_BIN', nil
113
+ expect(FSPath).not_to receive(:temp_dir)
114
+ expect(resolver).to receive(:full_path).with(:ls).and_return(nil)
115
+ expect(Bin).not_to receive(:new)
116
+
117
+ 5.times do
118
+ expect do
119
+ resolver.resolve!(:ls)
120
+ end.to raise_error BinResolver::BinNotFound
121
+ end
122
+ expect(resolver.env_path).to eq([
123
+ ENV['PATH'],
124
+ BinResolver::VENDOR_PATH,
125
+ ].join(File::PATH_SEPARATOR))
126
+ end
127
+
128
+ it 'resolves bin specified in ENV' do
129
+ path = 'bin/the_optimizer'
130
+ stub_env 'THE_OPTIMIZER_BIN', path
131
+ tmpdir = double(:tmpdir, :to_str => 'tmpdir')
132
+ symlink = double(:symlink)
133
+
134
+ full_path = File.expand_path(path)
135
+ allow(File).to receive(:exist?).with(full_path).and_return(true)
136
+ allow(File).to receive(:file?).with(full_path).and_return(true)
137
+ allow(File).to receive(:executable?).with(full_path).and_return(true)
138
+
139
+ expect(FSPath).to receive(:temp_dir).
140
+ once.and_return(tmpdir)
141
+ expect(tmpdir).to receive(:/).
142
+ with(:the_optimizer).once.and_return(symlink)
143
+ expect(symlink).to receive(:make_symlink).
144
+ with(File.expand_path(path)).once
145
+
146
+ expect(resolver).not_to receive(:full_path)
147
+ bin = double
148
+ expect(Bin).to receive(:new).
149
+ with(:the_optimizer, File.expand_path(path)).and_return(bin)
150
+ expect(bin).to receive(:check!).once
151
+ expect(bin).to receive(:check_fail!).exactly(5).times
152
+
153
+ at_exit_blocks = []
154
+ expect(resolver).to receive(:at_exit).once do |&block|
155
+ at_exit_blocks.unshift(block)
156
+ end
157
+
158
+ 5.times do
159
+ resolver.resolve!(:the_optimizer)
160
+ end
161
+ expect(resolver.env_path).to eq([
162
+ tmpdir,
163
+ ENV['PATH'],
164
+ BinResolver::VENDOR_PATH,
165
+ ].join(File::PATH_SEPARATOR))
166
+
167
+ expect(FileUtils).to receive(:remove_entry_secure).with(tmpdir)
168
+ at_exit_blocks.each(&:call)
169
+ end
170
+
171
+ describe 'checking bin' do
172
+ let(:path){ 'the_optimizer' }
173
+ let(:exist?){ true }
174
+ let(:file?){ true }
175
+ let(:executable?){ true }
176
+
177
+ before do
178
+ stub_env 'THE_OPTIMIZER_BIN', path
120
179
  expect(FSPath).not_to receive(:temp_dir)
121
- expect(resolver).to receive(:full_path).with(:ls).and_return(nil)
122
- expect(Bin).not_to receive(:new)
180
+ expect(resolver).not_to receive(:at_exit)
181
+ allow(File).to receive_messages(:exist? => exist?,
182
+ :file? => file?,
183
+ :executable? => executable?)
184
+ end
123
185
 
124
- 5.times do
125
- expect do
126
- resolver.resolve!(:ls)
127
- end.to raise_error BinResolver::BinNotFound
128
- end
186
+ after do
129
187
  expect(resolver.env_path).to eq([
130
188
  ENV['PATH'],
131
189
  BinResolver::VENDOR_PATH,
132
- ].join(':'))
190
+ ].join(File::PATH_SEPARATOR))
133
191
  end
134
- end
135
-
136
- it 'resolves bin specified in ENV' do
137
- path = 'bin/image_optim'
138
- with_env 'IMAGE_OPTIM_BIN', path do
139
- tmpdir = double(:tmpdir, :to_str => 'tmpdir')
140
- symlink = double(:symlink)
141
-
142
- expect(FSPath).to receive(:temp_dir).
143
- once.and_return(tmpdir)
144
- expect(tmpdir).to receive(:/).
145
- with(:image_optim).once.and_return(symlink)
146
- expect(symlink).to receive(:make_symlink).
147
- with(File.expand_path(path)).once
148
-
149
- expect(resolver).not_to receive(:full_path)
150
- bin = double
151
- expect(Bin).to receive(:new).
152
- with(:image_optim, File.expand_path(path)).and_return(bin)
153
- expect(bin).to receive(:check!).once
154
- expect(bin).to receive(:check_fail!).exactly(5).times
155
-
156
- at_exit_blocks = []
157
- expect(resolver).to receive(:at_exit).once do |&block|
158
- at_exit_blocks.unshift(block)
159
- end
160
192
 
193
+ def raises_error(error_message)
161
194
  5.times do
162
- resolver.resolve!(:image_optim)
195
+ expect do
196
+ resolver.resolve!(:the_optimizer)
197
+ end.to raise_error RuntimeError, /#{Regexp.escape(error_message)}/
163
198
  end
164
- expect(resolver.env_path).to eq([
165
- tmpdir,
166
- ENV['PATH'],
167
- BinResolver::VENDOR_PATH,
168
- ].join(':'))
199
+ end
169
200
 
170
- expect(FileUtils).to receive(:remove_entry_secure).with(tmpdir)
171
- at_exit_blocks.each(&:call)
201
+ context 'presence' do
202
+ let(:exist?){ false }
203
+
204
+ it{ raises_error('doesn\'t exist') }
172
205
  end
173
- end
174
206
 
175
- {
176
- 'some/path/does/not/exist' => 'doesn\'t exist',
177
- '.' => 'is not a file',
178
- __FILE__ => 'is not executable',
179
- }.each do |path, error_message|
180
- it "raises when bin specified in ENV #{error_message}" do
181
- with_env 'IMAGE_OPTIM_BIN', path do
182
- expect(FSPath).not_to receive(:temp_dir)
183
- expect(resolver).not_to receive(:at_exit)
184
-
185
- 5.times do
186
- expect do
187
- resolver.resolve!(:image_optim)
188
- end.to raise_error RuntimeError, /#{Regexp.escape(error_message)}/
189
- end
190
- expect(resolver.env_path).to eq([
191
- ENV['PATH'],
192
- BinResolver::VENDOR_PATH,
193
- ].join(':'))
194
- end
207
+ context 'been a file' do
208
+ let(:file?){ false }
209
+
210
+ it{ raises_error('is not a file') }
211
+ end
212
+
213
+ context 'been a file' do
214
+ let(:executable?){ false }
215
+
216
+ it{ raises_error('is not executable') }
195
217
  end
196
218
  end
197
219
 
198
220
  it 'resolves bin only once, but checks every time' do
199
- with_env 'LS_BIN', nil do
200
- expect(resolver).to receive(:full_path).once.with(:ls) do
201
- sleep 0.1
202
- '/bin/ls'
203
- end
204
- bin = double
205
- expect(Bin).to receive(:new).once.with(:ls, '/bin/ls').and_return(bin)
221
+ stub_env 'LS_BIN', nil
222
+ expect(resolver).to receive(:full_path).once.with(:ls) do
223
+ sleep 0.1
224
+ '/bin/ls'
225
+ end
226
+ bin = double
227
+ expect(Bin).to receive(:new).once.with(:ls, '/bin/ls').and_return(bin)
206
228
 
207
- count = 0
208
- mutex = Mutex.new
209
- allow(bin).to receive(:check!).once
210
- allow(bin).to receive(:check_fail!){ mutex.synchronize{ count += 1 } }
229
+ count = 0
230
+ mutex = Mutex.new
231
+ allow(bin).to receive(:check!).once
232
+ allow(bin).to receive(:check_fail!){ mutex.synchronize{ count += 1 } }
211
233
 
212
- Array.new(10) do
213
- Thread.new do
214
- resolver.resolve!(:ls)
215
- end
216
- end.each(&:join)
234
+ Array.new(10) do
235
+ Thread.new do
236
+ resolver.resolve!(:ls)
237
+ end
238
+ end.each(&:join)
217
239
 
218
- expect(count).to eq(10)
219
- end
240
+ expect(count).to eq(10)
220
241
  end
221
242
 
222
243
  describe 'checking version' do
@@ -225,47 +246,44 @@ describe ImageOptim::BinResolver do
225
246
  end
226
247
 
227
248
  it 'raises every time if did not get bin version' do
228
- with_env 'PNGCRUSH_BIN', nil do
229
- bin = Bin.new(:pngcrush, '/bin/pngcrush')
249
+ stub_env 'PNGCRUSH_BIN', nil
250
+ bin = Bin.new(:pngcrush, '/bin/pngcrush')
230
251
 
231
- expect(Bin).to receive(:new).and_return(bin)
232
- allow(bin).to receive(:version).and_return(nil)
252
+ expect(Bin).to receive(:new).and_return(bin)
253
+ allow(bin).to receive(:version).and_return(nil)
233
254
 
234
- 5.times do
235
- expect do
236
- resolver.resolve!(:pngcrush)
237
- end.to raise_error Bin::UnknownVersion
238
- end
255
+ 5.times do
256
+ expect do
257
+ resolver.resolve!(:pngcrush)
258
+ end.to raise_error Bin::UnknownVersion
239
259
  end
240
260
  end
241
261
 
242
262
  it 'raises every time on detection of misbehaving version' do
243
- with_env 'PNGCRUSH_BIN', nil do
244
- bin = Bin.new(:pngcrush, '/bin/pngcrush')
263
+ stub_env 'PNGCRUSH_BIN', nil
264
+ bin = Bin.new(:pngcrush, '/bin/pngcrush')
245
265
 
246
- expect(Bin).to receive(:new).and_return(bin)
247
- allow(bin).to receive(:version).and_return(SimpleVersion.new('1.7.60'))
266
+ expect(Bin).to receive(:new).and_return(bin)
267
+ allow(bin).to receive(:version).and_return(SimpleVersion.new('1.7.60'))
248
268
 
249
- 5.times do
250
- expect do
251
- resolver.resolve!(:pngcrush)
252
- end.to raise_error Bin::BadVersion
253
- end
269
+ 5.times do
270
+ expect do
271
+ resolver.resolve!(:pngcrush)
272
+ end.to raise_error Bin::BadVersion
254
273
  end
255
274
  end
256
275
 
257
276
  it 'warns once on detection of problematic version' do
258
- with_env 'ADVPNG_BIN', nil do
259
- bin = Bin.new(:advpng, '/bin/advpng')
277
+ stub_env 'ADVPNG_BIN', nil
278
+ bin = Bin.new(:advpng, '/bin/advpng')
260
279
 
261
- expect(Bin).to receive(:new).and_return(bin)
262
- allow(bin).to receive(:version).and_return(SimpleVersion.new('1.15'))
280
+ expect(Bin).to receive(:new).and_return(bin)
281
+ allow(bin).to receive(:version).and_return(SimpleVersion.new('1.15'))
263
282
 
264
- expect(bin).to receive(:warn).once
283
+ expect(bin).to receive(:warn).once
265
284
 
266
- 5.times do
267
- resolver.resolve!(:pngcrush)
268
- end
285
+ 5.times do
286
+ resolver.resolve!(:pngcrush)
269
287
  end
270
288
  end
271
289
  end