image_optim 0.17.1 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +1 -0
  3. data/.travis.yml +5 -18
  4. data/CHANGELOG.markdown +10 -0
  5. data/README.markdown +31 -1
  6. data/bin/image_optim +3 -137
  7. data/image_optim.gemspec +6 -3
  8. data/lib/image_optim.rb +20 -3
  9. data/lib/image_optim/bin_resolver.rb +28 -1
  10. data/lib/image_optim/bin_resolver/bin.rb +17 -7
  11. data/lib/image_optim/cmd.rb +49 -0
  12. data/lib/image_optim/config.rb +64 -4
  13. data/lib/image_optim/image_path.rb +5 -0
  14. data/lib/image_optim/option_definition.rb +5 -3
  15. data/lib/image_optim/runner.rb +1 -2
  16. data/lib/image_optim/runner/option_parser.rb +216 -0
  17. data/lib/image_optim/worker.rb +32 -17
  18. data/lib/image_optim/worker/advpng.rb +7 -1
  19. data/lib/image_optim/worker/gifsicle.rb +16 -3
  20. data/lib/image_optim/worker/jhead.rb +15 -8
  21. data/lib/image_optim/worker/jpegoptim.rb +6 -2
  22. data/lib/image_optim/worker/jpegtran.rb +10 -3
  23. data/lib/image_optim/worker/optipng.rb +6 -1
  24. data/lib/image_optim/worker/pngcrush.rb +8 -1
  25. data/lib/image_optim/worker/pngout.rb +8 -1
  26. data/lib/image_optim/worker/svgo.rb +4 -1
  27. data/script/worker_analysis +523 -0
  28. data/script/worker_analysis.haml +153 -0
  29. data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +4 -5
  30. data/spec/image_optim/bin_resolver/simple_version_spec.rb +44 -21
  31. data/spec/image_optim/bin_resolver_spec.rb +63 -29
  32. data/spec/image_optim/cmd_spec.rb +66 -0
  33. data/spec/image_optim/config_spec.rb +38 -38
  34. data/spec/image_optim/handler_spec.rb +15 -12
  35. data/spec/image_optim/hash_helpers_spec.rb +14 -13
  36. data/spec/image_optim/image_path_spec.rb +22 -7
  37. data/spec/image_optim/runner/glob_helpers_spec.rb +6 -5
  38. data/spec/image_optim/runner/option_parser_spec.rb +99 -0
  39. data/spec/image_optim/space_spec.rb +5 -4
  40. data/spec/image_optim/worker_spec.rb +6 -5
  41. data/spec/image_optim_spec.rb +209 -237
  42. data/spec/spec_helper.rb +3 -0
  43. metadata +43 -11
@@ -1,9 +1,10 @@
1
- $LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__)
2
- require 'rspec'
1
+ require 'spec_helper'
3
2
  require 'image_optim/space'
4
3
 
5
4
  describe ImageOptim::Space do
6
- Space = ImageOptim::Space
5
+ before do
6
+ stub_const('Space', ImageOptim::Space)
7
+ end
7
8
 
8
9
  {
9
10
  0 => ' ',
@@ -17,7 +18,7 @@ describe ImageOptim::Space do
17
18
  10_000_000 => ' 9.5M',
18
19
  100_000_000 => ' 95.4M',
19
20
  }.each do |size, space|
20
- it "should convert #{size} to #{space}" do
21
+ it "converts #{size} to #{space}" do
21
22
  expect(Space.space(size)).to eq(space)
22
23
  end
23
24
  end
@@ -1,12 +1,13 @@
1
- $LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__)
2
- require 'rspec'
1
+ require 'spec_helper'
3
2
  require 'image_optim/worker'
4
3
 
5
4
  describe ImageOptim::Worker do
6
- Worker = ImageOptim::Worker
5
+ before do
6
+ stub_const('Worker', ImageOptim::Worker)
7
+ end
7
8
 
8
- describe 'optimize' do
9
- it 'should raise NotImplementedError' do
9
+ describe :optimize do
10
+ it 'raises NotImplementedError' do
10
11
  image_optim = ImageOptim.new
11
12
  worker = Worker.new(image_optim, {})
12
13
 
@@ -1,52 +1,35 @@
1
- $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
- require 'rspec'
1
+ require 'spec_helper'
3
2
  require 'image_optim'
3
+ require 'image_optim/cmd'
4
4
  require 'tempfile'
5
+ require 'English'
5
6
 
6
- TEST_IMAGES = ImageOptim::ImagePath.new(__FILE__).dirname.glob('images/**/*.*')
7
-
8
- Fixnum.class_eval do
9
- def in_range?(range)
10
- range.include?(self)
11
- end
12
- end
13
-
14
- Tempfile.class_eval do
15
- def self.init_count
16
- class_variable_get(:@@init_count)
17
- end
18
-
19
- def self.init_count=(value)
20
- class_variable_set(:@@init_count, value)
21
- end
22
-
23
- def self.reset_init_count
24
- self.init_count = 0
25
- end
26
-
27
- reset_init_count
7
+ describe ImageOptim do
8
+ root_dir = ImageOptim::ImagePath.new(__FILE__).dirname.dirname
9
+ images_dir = root_dir / 'spec/images'
10
+ test_images = images_dir.glob('**/*.*').freeze
28
11
 
29
- alias_method :initialize_orig, :initialize
30
- def initialize(*args, &block)
31
- self.class.init_count += 1
32
- initialize_orig(*args, &block)
12
+ helpers = Module.new do
13
+ def temp_copy(image)
14
+ image.temp_path.tap{ |path| image.copy(path) }
15
+ end
33
16
  end
34
- end
17
+ include helpers
18
+ extend helpers
35
19
 
36
- ImageOptim::ImagePath.class_eval do
37
- def temp_copy
38
- temp_path.tap{ |path| copy(path) }
20
+ matcher :be_in_range do |expected|
21
+ match{ |actual| expected.include?(actual) }
39
22
  end
40
- end
41
23
 
42
- describe ImageOptim do
43
24
  before do
25
+ stub_const('Cmd', ImageOptim::Cmd)
26
+
44
27
  allow(ImageOptim::Config).to receive(:global).and_return({})
45
28
  allow(ImageOptim::Config).to receive(:local).and_return({})
46
29
  end
47
30
 
48
31
  describe 'workers' do
49
- it 'should be ordered by run_order' do
32
+ they 'are ordered by run_order' do
50
33
  image_optim = ImageOptim.new
51
34
  original_klasses = ImageOptim::Worker.klasses
52
35
  formats = original_klasses.map do |klass|
@@ -55,8 +38,8 @@ describe ImageOptim do
55
38
 
56
39
  [
57
40
  original_klasses,
58
- original_klasses.reverse,
59
- original_klasses.shuffle,
41
+ original_klasses.to_a.reverse,
42
+ original_klasses.to_a.shuffle,
60
43
  ].each do |klasses|
61
44
  expect(ImageOptim::Worker).to receive(:klasses).and_return(klasses)
62
45
 
@@ -75,265 +58,254 @@ describe ImageOptim do
75
58
  end
76
59
  end
77
60
 
78
- describe 'worker' do
79
- base_options = Hash[ImageOptim::Worker.klasses.map do |klass|
80
- [klass.bin_sym, false]
81
- end]
82
-
83
- ImageOptim::Worker.klasses.each do |worker_klass|
84
- describe worker_klass.bin_sym do
85
- it 'should optimize at least one test image' do
86
- options = base_options.merge(worker_klass.bin_sym => true)
87
- image_optim = ImageOptim.new(options)
88
- expect(TEST_IMAGES.any? do |original|
89
- image_optim.optimize_image(original.temp_copy)
90
- end).to be true
91
- end
61
+ disable_all_workers = Hash[ImageOptim::Worker.klasses.map do |klass|
62
+ [klass.bin_sym, false]
63
+ end]
64
+
65
+ ImageOptim::Worker.klasses.each do |worker_klass|
66
+ describe "#{worker_klass.bin_sym} worker" do
67
+ it 'optimizes at least one test image' do
68
+ options = disable_all_workers.merge(worker_klass.bin_sym => true)
69
+ image_optim = ImageOptim.new(options)
70
+ expect(test_images.any? do |original|
71
+ image_optim.optimize_image(temp_copy(original))
72
+ end).to be true
92
73
  end
93
74
  end
94
75
  end
95
76
 
96
- describe 'isolated' do
97
- describe 'optimize' do
98
- TEST_IMAGES.each do |original|
99
- it "should optimize #{original}" do
100
- copy = original.temp_copy
101
-
102
- Tempfile.reset_init_count
103
- image_optim = ImageOptim.new
104
- optimized_image = image_optim.optimize_image(copy)
105
- expect(optimized_image).to be_a(ImageOptim::ImagePath::Optimized)
106
- expect(optimized_image.size).to be_in_range(1...original.size)
107
- expect(optimized_image.read).not_to eq(original.read)
108
- expect(copy.read).to eq(original.read)
109
-
110
- if image_optim.workers_for_image(original).length > 1
111
- expect(Tempfile.init_count).to be_in_range(1..2)
112
- else
113
- expect(Tempfile.init_count).to eq(1)
114
- end
115
- end
77
+ describe :optimize_image do
78
+ def flatten_animation(image)
79
+ if image.format == :gif
80
+ flattened = image.temp_path
81
+ flatten_command = %W[
82
+ convert
83
+ #{image.to_s.shellescape}
84
+ -coalesce
85
+ -append
86
+ #{flattened.to_s.shellescape}
87
+ ].join(' ')
88
+ expect(Cmd.run(flatten_command)).to be_truthy
89
+ flattened
90
+ else
91
+ image
116
92
  end
117
93
  end
118
94
 
119
- describe 'optimize in place' do
120
- TEST_IMAGES.each do |original|
121
- it "should optimize #{original}" do
122
- copy = original.temp_copy
123
-
124
- Tempfile.reset_init_count
125
- image_optim = ImageOptim.new
126
- expect(image_optim.optimize_image!(copy)).to be_truthy
127
- expect(copy.size).to be_in_range(1...original.size)
128
- expect(copy.read).not_to eq(original.read)
129
-
130
- if image_optim.workers_for_image(original).length > 1
131
- expect(Tempfile.init_count).to be_in_range(2..3)
132
- else
133
- expect(Tempfile.init_count).to eq(2)
134
- end
135
- end
95
+ def nrmse(image_a, image_b)
96
+ coalesce_a = flatten_animation(image_a)
97
+ coalesce_b = flatten_animation(image_b)
98
+ nrmse_command = %W[
99
+ compare
100
+ -metric RMSE
101
+ #{coalesce_a.to_s.shellescape}
102
+ #{coalesce_b.to_s.shellescape}
103
+ /dev/null
104
+ 2>&1
105
+ ].join(' ')
106
+ output = Cmd.capture(nrmse_command)
107
+ if [0, 1].include?($CHILD_STATUS.exitstatus)
108
+ output[/\((\d+(\.\d+)?)\)/, 1].to_f
109
+ else
110
+ fail "compare #{image_a} with #{image_b} failed with `#{output}`"
136
111
  end
137
112
  end
138
113
 
139
- describe 'optimize image data' do
140
- TEST_IMAGES.each do |original|
141
- it "should optimize #{original}" do
142
- image_optim = ImageOptim.new
143
- optimized_data = image_optim.optimize_image_data(original.read)
144
- expect(optimized_data).not_to be_nil
114
+ define :have_same_data_as do |expected|
115
+ match{ |actual| actual.binread == expected.binread }
116
+ end
145
117
 
146
- expected_path = image_optim.optimize_image(original.temp_copy)
147
- expect(optimized_data).to eq(expected_path.open('rb', &:read))
118
+ define :have_size do
119
+ match(&:size?)
120
+ end
148
121
 
149
- expect(image_optim.optimize_image_data(optimized_data)).to be_nil
150
- end
122
+ define :be_smaller_than do |expected|
123
+ match{ |actual| actual.size < expected.size }
124
+ end
125
+
126
+ define :be_pixel_identical_to do |expected|
127
+ match do |actual|
128
+ @diff = nrmse(actual, expected)
129
+ @diff == 0
130
+ end
131
+ failure_message do |actual|
132
+ "expected #{actual} to be pixel identical to #{expected}, got "\
133
+ "normalized root-mean-square error of #{@diff}"
151
134
  end
152
135
  end
153
136
 
154
- describe 'stop optimizing' do
155
- TEST_IMAGES.each do |original|
156
- it "should stop optimizing #{original}" do
157
- copy = original.temp_copy
137
+ describe 'optimizing images' do
138
+ rotated = images_dir / 'orient/original.jpg'
139
+ rotate_images = images_dir.glob('orient/?.jpg')
158
140
 
159
- tries = 0
160
- 10.times do
161
- tries += 1
162
- break unless ImageOptim.optimize_image!(copy)
163
- end
164
- expect(tries).to be_in_range(2...3)
141
+ copies = test_images.map{ |image| temp_copy(image) }
142
+ original_by_copy = Hash[copies.zip(test_images)]
143
+
144
+ ImageOptim.optimize_images(copies) do |copy, optimized|
145
+ fail 'expected copy to not be nil' if copy.nil?
146
+ original = original_by_copy[copy]
147
+
148
+ it "optimizes #{original.relative_path_from(root_dir)}" do
149
+ expect(copy).to have_same_data_as(original)
150
+
151
+ expect(optimized).not_to be_nil
152
+ expect(optimized).to be_a(ImageOptim::ImagePath::Optimized)
153
+ expect(optimized).to have_size
154
+ expect(optimized).to be_smaller_than(original)
155
+ expect(optimized).not_to have_same_data_as(original)
156
+
157
+ compare_to = rotate_images.include?(original) ? rotated : original
158
+ expect(optimized).to be_pixel_identical_to(compare_to)
165
159
  end
166
160
  end
167
161
  end
168
- end
169
162
 
170
- describe 'bunch' do
171
- it 'should optimize' do
172
- copies = TEST_IMAGES.map(&:temp_copy)
173
- results = ImageOptim.optimize_images(copies)
174
- zipped = TEST_IMAGES.zip(copies, results)
175
- zipped.each do |original, copy, result|
176
- expect(result[0]).to eq(copy)
177
- expect(result[1]).to be_a(ImageOptim::ImagePath::Optimized)
178
- expect(result[1].size).to be_in_range(1...original.size)
179
- expect(copy.read).to eq(original.read)
180
- end
163
+ it 'ignores text file' do
164
+ original = ImageOptim::ImagePath.new(__FILE__)
165
+ copy = temp_copy(original)
166
+
167
+ expect(Tempfile).not_to receive(:new)
168
+ optimized_image = ImageOptim.optimize_image(copy)
169
+ expect(optimized_image).to be_nil
170
+ expect(copy.read).to eq(original.read)
181
171
  end
182
172
 
183
- it 'should optimize in place' do
184
- copies = TEST_IMAGES.map(&:temp_copy)
185
- results = ImageOptim.optimize_images!(copies)
186
- zipped = TEST_IMAGES.zip(copies, results)
187
- zipped.each do |original, copy, result|
188
- expect(result[0]).to eq(copy)
189
- expect(result[1]).to be_a(ImageOptim::ImagePath::Optimized)
190
- expect(copy.size).to be_in_range(1...original.size)
173
+ {
174
+ :png => "\211PNG\r\n\032\n",
175
+ :jpeg => "\377\330",
176
+ }.each do |type, data|
177
+ it "ingores broken #{type}" do
178
+ path = FSPath.temp_file_path
179
+ path.write(data)
180
+ expect(ImageOptim::ImageMeta).to receive(:warn)
181
+ expect(ImageOptim.optimize_image(path)).to be_nil
191
182
  end
192
183
  end
184
+ end
193
185
 
194
- it 'should optimize datas' do
195
- results = ImageOptim.optimize_images_data(TEST_IMAGES.map(&:read))
196
- zipped = TEST_IMAGES.zip(results)
197
- zipped.each do |original, result|
198
- expect(result[0]).to eq(original.read)
199
- expect(result[1]).to be_a(String)
200
- expect(result[1].size).to be_in_range(1...original.size)
186
+ describe :optimize_image! do
187
+ it 'optimizes image and replaces original' do
188
+ original = double
189
+ optimized = double(:original_size => 12_345)
190
+ optimized_wrap = double
191
+ image_optim = ImageOptim.new
201
192
 
202
- expected_path = ImageOptim.optimize_image(original.temp_copy)
203
- expect(result[1]).to eq(expected_path.open('rb', &:read))
204
- end
193
+ allow(ImageOptim::ImagePath).to receive(:convert).
194
+ with(original).and_return(original)
195
+
196
+ expect(image_optim).to receive(:optimize_image).
197
+ with(original).and_return(optimized)
198
+ expect(optimized).to receive(:replace).with(original)
199
+ expect(ImageOptim::ImagePath::Optimized).to receive(:new).
200
+ with(original, 12_345).and_return(optimized_wrap)
201
+
202
+ expect(image_optim.optimize_image!(original)).to eq(optimized_wrap)
203
+ end
204
+
205
+ it 'returns nil if optimization fails' do
206
+ original = double
207
+ image_optim = ImageOptim.new
208
+
209
+ allow(ImageOptim::ImagePath).to receive(:convert).
210
+ with(original).and_return(original)
211
+
212
+ expect(image_optim).to receive(:optimize_image).
213
+ with(original).and_return(nil)
214
+ expect(ImageOptim::ImagePath::Optimized).not_to receive(:new)
215
+
216
+ expect(image_optim.optimize_image!(original)).to eq(nil)
205
217
  end
206
218
  end
207
219
 
208
- describe 'unsupported' do
209
- let(:original){ ImageOptim::ImagePath.new(__FILE__) }
220
+ describe :optimize_image_data do
221
+ it 'create temp file, optimizes image and returns data' do
222
+ data = double
223
+ temp = double(:path => double)
224
+ optimized = double
225
+ optimized_data = double
226
+ image_optim = ImageOptim.new
210
227
 
211
- it 'should ignore' do
212
- copy = original.temp_copy
228
+ allow(ImageOptim::ImageMeta).to receive(:for_data).
229
+ with(data).and_return(double(:format => 'xxx'))
213
230
 
214
- Tempfile.reset_init_count
215
- optimized_image = ImageOptim.optimize_image(copy)
216
- expect(Tempfile.init_count).to eq(0)
217
- expect(optimized_image).to be_nil
218
- expect(copy.read).to eq(original.read)
231
+ expect(ImageOptim::ImagePath).to receive(:temp_file).and_yield(temp)
232
+ expect(temp).to receive(:binmode)
233
+ expect(temp).to receive(:write).with(data)
234
+ expect(temp).to receive(:close)
235
+ expect(image_optim).to receive(:optimize_image).
236
+ with(temp.path).and_return(optimized)
237
+ expect(optimized).to receive(:binread).and_return(optimized_data)
238
+
239
+ expect(image_optim.optimize_image_data(data)).to eq(optimized_data)
219
240
  end
220
241
 
221
- it 'should ignore in place' do
222
- copy = original.temp_copy
242
+ it 'returns nil if optimization fails' do
243
+ data = double
244
+ temp = double(:path => double)
245
+ image_optim = ImageOptim.new
223
246
 
224
- Tempfile.reset_init_count
225
- expect(ImageOptim.optimize_image!(copy)).not_to be_truthy
226
- expect(Tempfile.init_count).to eq(0)
227
- expect(copy.read).to eq(original.read)
247
+ allow(ImageOptim::ImageMeta).to receive(:for_data).
248
+ with(data).and_return(double(:format => 'xxx'))
249
+
250
+ expect(ImageOptim::ImagePath).to receive(:temp_file).and_yield(temp)
251
+ expect(temp).to receive(:binmode)
252
+ expect(temp).to receive(:write).with(data)
253
+ expect(temp).to receive(:close)
254
+ expect(image_optim).to receive(:optimize_image).
255
+ with(temp.path).and_return(nil)
256
+
257
+ expect(image_optim.optimize_image_data(data)).to eq(nil)
228
258
  end
229
259
 
230
- {
231
- :png => "\211PNG\r\n\032\n",
232
- :jpeg => "\377\330",
233
- }.each do |type, data|
234
- describe "broken #{type}" do
235
- before do
236
- expect(ImageOptim::ImageMeta).to receive(:warn)
237
- end
260
+ it 'returns nil if format can\'t be detected' do
261
+ data = double
262
+ image_optim = ImageOptim.new
238
263
 
239
- it 'should ignore path' do
240
- path = FSPath.temp_file_path
241
- path.write(data)
242
- expect(ImageOptim.optimize_image(path)).to be_nil
243
- end
264
+ allow(ImageOptim::ImageMeta).to receive(:for_data).
265
+ with(data).and_return(double(:format => nil))
244
266
 
245
- it 'should ignore data' do
246
- expect(ImageOptim.optimize_image_data(data)).to be_nil
247
- end
248
- end
267
+ expect(ImageOptim::ImagePath).not_to receive(:temp_file)
268
+ expect(image_optim).not_to receive(:optimize_image)
269
+
270
+ expect(image_optim.optimize_image_data(data)).to eq(nil)
249
271
  end
250
272
  end
251
273
 
252
274
  describe 'optimize multiple' do
253
- let(:srcs){ ('a'..'z').to_a }
254
-
255
- %w[optimize_images optimize_images!].each do |list_method|
275
+ %w[
276
+ optimize_images
277
+ optimize_images!
278
+ optimize_images_data
279
+ ].each do |list_method|
256
280
  describe list_method do
257
281
  method = list_method.sub('images', 'image')
258
282
  describe 'without block' do
259
- it 'should optimize images and return array of results' do
283
+ it 'optimizes images and returns array of results' do
260
284
  image_optim = ImageOptim.new
261
- dsts = srcs.map do |src|
262
- dst = "#{src}_"
285
+ results = test_images.map do |src|
286
+ dst = double
263
287
  expect(image_optim).to receive(method).with(src).and_return(dst)
264
- dst
288
+ [src, dst]
265
289
  end
266
- expect(image_optim.send(list_method, srcs)).to eq(srcs.zip(dsts))
290
+ expect(image_optim.send(list_method, test_images)).to eq(results)
267
291
  end
268
292
  end
269
293
 
270
294
  describe 'given block' do
271
- it 'should optimize images, yield path and result for each and '\
272
- 'return array of yield results' do
295
+ it 'optimizes images, yields path and result for each and '\
296
+ 'returns array of yield results' do
273
297
  image_optim = ImageOptim.new
274
- results = srcs.map do |src|
275
- dst = "#{src}_"
298
+ results = test_images.map do |src|
299
+ dst = double
276
300
  expect(image_optim).to receive(method).with(src).and_return(dst)
277
- "#{src} #{dst}"
301
+ [src, dst, :test]
278
302
  end
279
- expect(image_optim.send(list_method, srcs) do |src, dst|
280
- "#{src} #{dst}"
303
+ expect(image_optim.send(list_method, test_images) do |src, dst|
304
+ [src, dst, :test]
281
305
  end).to eq(results)
282
306
  end
283
307
  end
284
308
  end
285
309
  end
286
310
  end
287
-
288
- describe 'losslessness' do
289
- images_dir = ImageOptim::ImagePath.new(__FILE__).dirname / 'images'
290
- rotated = images_dir / 'orient/original.jpg'
291
- rotate_images = images_dir.glob('orient/?.jpg')
292
-
293
- def flatten_animation(image)
294
- if image.format == :gif
295
- flattened = image.temp_path
296
- flatten_command = %W[
297
- convert
298
- #{image.to_s.shellescape}
299
- -coalesce
300
- -append
301
- #{flattened.to_s.shellescape}
302
- ].join(' ')
303
- expect(system(flatten_command)).to be_truthy
304
- flattened
305
- else
306
- image
307
- end
308
- end
309
-
310
- def check_lossless_optimization(original, optimized)
311
- expect(optimized).not_to be_nil
312
- original = flatten_animation(original)
313
- optimized = flatten_animation(optimized)
314
- nrmse_command = %W[
315
- compare
316
- -metric RMSE
317
- #{original.to_s.shellescape}
318
- #{optimized.to_s.shellescape}
319
- /dev/null
320
- 2>&1
321
- ].join(' ')
322
- nrmse = `#{nrmse_command}`[/\((\d+(\.\d+)?)\)/, 1]
323
- expect(nrmse).not_to be_nil
324
- expect(nrmse.to_f).to eq(0)
325
- end
326
-
327
- rotate_images.each do |image|
328
- it "should rotate and optimize #{image} losslessly" do
329
- check_lossless_optimization(rotated, ImageOptim.optimize_image(image))
330
- end
331
- end
332
-
333
- (TEST_IMAGES - rotate_images).each do |image|
334
- it "should optimize #{image} losslessly" do
335
- check_lossless_optimization(image, ImageOptim.optimize_image(image))
336
- end
337
- end
338
- end
339
311
  end