image_optim 0.17.1 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/.gitignore +1 -0
- data/.travis.yml +5 -18
- data/CHANGELOG.markdown +10 -0
- data/README.markdown +31 -1
- data/bin/image_optim +3 -137
- data/image_optim.gemspec +6 -3
- data/lib/image_optim.rb +20 -3
- data/lib/image_optim/bin_resolver.rb +28 -1
- data/lib/image_optim/bin_resolver/bin.rb +17 -7
- data/lib/image_optim/cmd.rb +49 -0
- data/lib/image_optim/config.rb +64 -4
- data/lib/image_optim/image_path.rb +5 -0
- data/lib/image_optim/option_definition.rb +5 -3
- data/lib/image_optim/runner.rb +1 -2
- data/lib/image_optim/runner/option_parser.rb +216 -0
- data/lib/image_optim/worker.rb +32 -17
- data/lib/image_optim/worker/advpng.rb +7 -1
- data/lib/image_optim/worker/gifsicle.rb +16 -3
- data/lib/image_optim/worker/jhead.rb +15 -8
- data/lib/image_optim/worker/jpegoptim.rb +6 -2
- data/lib/image_optim/worker/jpegtran.rb +10 -3
- data/lib/image_optim/worker/optipng.rb +6 -1
- data/lib/image_optim/worker/pngcrush.rb +8 -1
- data/lib/image_optim/worker/pngout.rb +8 -1
- data/lib/image_optim/worker/svgo.rb +4 -1
- data/script/worker_analysis +523 -0
- data/script/worker_analysis.haml +153 -0
- data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +4 -5
- data/spec/image_optim/bin_resolver/simple_version_spec.rb +44 -21
- data/spec/image_optim/bin_resolver_spec.rb +63 -29
- data/spec/image_optim/cmd_spec.rb +66 -0
- data/spec/image_optim/config_spec.rb +38 -38
- data/spec/image_optim/handler_spec.rb +15 -12
- data/spec/image_optim/hash_helpers_spec.rb +14 -13
- data/spec/image_optim/image_path_spec.rb +22 -7
- data/spec/image_optim/runner/glob_helpers_spec.rb +6 -5
- data/spec/image_optim/runner/option_parser_spec.rb +99 -0
- data/spec/image_optim/space_spec.rb +5 -4
- data/spec/image_optim/worker_spec.rb +6 -5
- data/spec/image_optim_spec.rb +209 -237
- data/spec/spec_helper.rb +3 -0
- metadata +43 -11
@@ -1,9 +1,10 @@
|
|
1
|
-
|
2
|
-
require 'rspec'
|
1
|
+
require 'spec_helper'
|
3
2
|
require 'image_optim/space'
|
4
3
|
|
5
4
|
describe ImageOptim::Space do
|
6
|
-
|
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 "
|
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
|
-
|
2
|
-
require 'rspec'
|
1
|
+
require 'spec_helper'
|
3
2
|
require 'image_optim/worker'
|
4
3
|
|
5
4
|
describe ImageOptim::Worker do
|
6
|
-
|
5
|
+
before do
|
6
|
+
stub_const('Worker', ImageOptim::Worker)
|
7
|
+
end
|
7
8
|
|
8
|
-
describe
|
9
|
-
it '
|
9
|
+
describe :optimize do
|
10
|
+
it 'raises NotImplementedError' do
|
10
11
|
image_optim = ImageOptim.new
|
11
12
|
worker = Worker.new(image_optim, {})
|
12
13
|
|
data/spec/image_optim_spec.rb
CHANGED
@@ -1,52 +1,35 @@
|
|
1
|
-
|
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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
17
|
+
include helpers
|
18
|
+
extend helpers
|
35
19
|
|
36
|
-
|
37
|
-
|
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
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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
|
-
|
140
|
-
|
141
|
-
|
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
|
-
|
147
|
-
|
118
|
+
define :have_size do
|
119
|
+
match(&:size?)
|
120
|
+
end
|
148
121
|
|
149
|
-
|
150
|
-
|
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 '
|
155
|
-
|
156
|
-
|
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
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
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
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
expect(
|
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
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
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
|
-
|
203
|
-
|
204
|
-
|
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
|
209
|
-
|
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
|
-
|
212
|
-
|
228
|
+
allow(ImageOptim::ImageMeta).to receive(:for_data).
|
229
|
+
with(data).and_return(double(:format => 'xxx'))
|
213
230
|
|
214
|
-
|
215
|
-
|
216
|
-
expect(
|
217
|
-
expect(
|
218
|
-
expect(
|
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 '
|
222
|
-
|
242
|
+
it 'returns nil if optimization fails' do
|
243
|
+
data = double
|
244
|
+
temp = double(:path => double)
|
245
|
+
image_optim = ImageOptim.new
|
223
246
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
expect(
|
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
|
-
|
232
|
-
|
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
|
-
|
240
|
-
|
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
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
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
|
-
|
254
|
-
|
255
|
-
|
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 '
|
283
|
+
it 'optimizes images and returns array of results' do
|
260
284
|
image_optim = ImageOptim.new
|
261
|
-
|
262
|
-
dst =
|
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,
|
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 '
|
272
|
-
'
|
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 =
|
275
|
-
dst =
|
298
|
+
results = test_images.map do |src|
|
299
|
+
dst = double
|
276
300
|
expect(image_optim).to receive(method).with(src).and_return(dst)
|
277
|
-
|
301
|
+
[src, dst, :test]
|
278
302
|
end
|
279
|
-
expect(image_optim.send(list_method,
|
280
|
-
|
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
|