discourse_image_optim 0.24.4
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 +7 -0
- data/.appveyor.yml +46 -0
- data/.gitignore +18 -0
- data/.rubocop.yml +110 -0
- data/.travis.yml +42 -0
- data/CHANGELOG.markdown +316 -0
- data/CONTRIBUTING.markdown +11 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +358 -0
- data/Vagrantfile +38 -0
- data/bin/image_optim +28 -0
- data/image_optim.gemspec +34 -0
- data/lib/image_optim.rb +267 -0
- data/lib/image_optim/bin_resolver.rb +142 -0
- data/lib/image_optim/bin_resolver/bin.rb +115 -0
- data/lib/image_optim/bin_resolver/comparable_condition.rb +60 -0
- data/lib/image_optim/bin_resolver/error.rb +6 -0
- data/lib/image_optim/bin_resolver/simple_version.rb +31 -0
- data/lib/image_optim/cache.rb +72 -0
- data/lib/image_optim/cache_path.rb +16 -0
- data/lib/image_optim/cmd.rb +122 -0
- data/lib/image_optim/config.rb +219 -0
- data/lib/image_optim/configuration_error.rb +3 -0
- data/lib/image_optim/handler.rb +57 -0
- data/lib/image_optim/hash_helpers.rb +45 -0
- data/lib/image_optim/image_meta.rb +20 -0
- data/lib/image_optim/non_negative_integer_range.rb +11 -0
- data/lib/image_optim/optimized_path.rb +25 -0
- data/lib/image_optim/option_definition.rb +38 -0
- data/lib/image_optim/option_helpers.rb +17 -0
- data/lib/image_optim/path.rb +70 -0
- data/lib/image_optim/runner.rb +139 -0
- data/lib/image_optim/runner/glob_helpers.rb +45 -0
- data/lib/image_optim/runner/option_parser.rb +246 -0
- data/lib/image_optim/space.rb +29 -0
- data/lib/image_optim/true_false_nil.rb +16 -0
- data/lib/image_optim/worker.rb +170 -0
- data/lib/image_optim/worker/advpng.rb +37 -0
- data/lib/image_optim/worker/class_methods.rb +107 -0
- data/lib/image_optim/worker/gifsicle.rb +65 -0
- data/lib/image_optim/worker/jhead.rb +47 -0
- data/lib/image_optim/worker/jpegoptim.rb +63 -0
- data/lib/image_optim/worker/jpegrecompress.rb +49 -0
- data/lib/image_optim/worker/jpegtran.rb +48 -0
- data/lib/image_optim/worker/optipng.rb +53 -0
- data/lib/image_optim/worker/pngcrush.rb +56 -0
- data/lib/image_optim/worker/pngout.rb +40 -0
- data/lib/image_optim/worker/pngquant.rb +61 -0
- data/lib/image_optim/worker/svgo.rb +34 -0
- data/script/template/jquery-2.1.3.min.js +4 -0
- data/script/template/sortable-0.6.0.min.js +2 -0
- data/script/template/worker_analysis.erb +254 -0
- data/script/update_worker_options_in_readme +59 -0
- data/script/worker_analysis +589 -0
- data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +37 -0
- data/spec/image_optim/bin_resolver/simple_version_spec.rb +65 -0
- data/spec/image_optim/bin_resolver_spec.rb +290 -0
- data/spec/image_optim/cache_path_spec.rb +57 -0
- data/spec/image_optim/cache_spec.rb +162 -0
- data/spec/image_optim/cmd_spec.rb +93 -0
- data/spec/image_optim/config_spec.rb +254 -0
- data/spec/image_optim/handler_spec.rb +90 -0
- data/spec/image_optim/hash_helpers_spec.rb +74 -0
- data/spec/image_optim/image_meta_spec.rb +61 -0
- data/spec/image_optim/optimized_path_spec.rb +58 -0
- data/spec/image_optim/option_definition_spec.rb +138 -0
- data/spec/image_optim/option_helpers_spec.rb +25 -0
- data/spec/image_optim/path_spec.rb +103 -0
- data/spec/image_optim/runner/glob_helpers_spec.rb +21 -0
- data/spec/image_optim/runner/option_parser_spec.rb +105 -0
- data/spec/image_optim/space_spec.rb +23 -0
- data/spec/image_optim/worker/optipng_spec.rb +102 -0
- data/spec/image_optim/worker/pngquant_spec.rb +67 -0
- data/spec/image_optim/worker_spec.rb +303 -0
- data/spec/image_optim_spec.rb +259 -0
- data/spec/images/broken_jpeg +1 -0
- data/spec/images/comparison.png +0 -0
- data/spec/images/decompressed.jpeg +0 -0
- data/spec/images/icecream.gif +0 -0
- data/spec/images/image.jpg +0 -0
- data/spec/images/invisiblepixels/generate +24 -0
- data/spec/images/invisiblepixels/image.png +0 -0
- data/spec/images/lena.jpg +0 -0
- data/spec/images/orient/0.jpg +0 -0
- data/spec/images/orient/1.jpg +0 -0
- data/spec/images/orient/2.jpg +0 -0
- data/spec/images/orient/3.jpg +0 -0
- data/spec/images/orient/4.jpg +0 -0
- data/spec/images/orient/5.jpg +0 -0
- data/spec/images/orient/6.jpg +0 -0
- data/spec/images/orient/7.jpg +0 -0
- data/spec/images/orient/8.jpg +0 -0
- data/spec/images/orient/generate +23 -0
- data/spec/images/orient/original.jpg +0 -0
- data/spec/images/quant/64.png +0 -0
- data/spec/images/quant/generate +25 -0
- data/spec/images/rails.png +0 -0
- data/spec/images/test.svg +3 -0
- data/spec/images/transparency1.png +0 -0
- data/spec/images/transparency2.png +0 -0
- data/spec/images/vergroessert.jpg +0 -0
- data/spec/spec_helper.rb +93 -0
- metadata +281 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'image_optim'
|
|
3
|
+
require 'image_optim/cmd'
|
|
4
|
+
require 'tempfile'
|
|
5
|
+
require 'English'
|
|
6
|
+
|
|
7
|
+
describe ImageOptim do
|
|
8
|
+
root_dir = ImageOptim::Path.new(__FILE__).dirname.dirname
|
|
9
|
+
images_dir = root_dir / 'spec/images'
|
|
10
|
+
test_images = images_dir.glob('**/*.*').freeze
|
|
11
|
+
|
|
12
|
+
helpers = Module.new do
|
|
13
|
+
def temp_copy(image)
|
|
14
|
+
image.temp_path.tap{ |path| image.copy(path) }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
include helpers
|
|
18
|
+
extend helpers
|
|
19
|
+
|
|
20
|
+
before do
|
|
21
|
+
stub_const('Cmd', ImageOptim::Cmd)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
isolated_options_base = {:skip_missing_workers => false}
|
|
25
|
+
ImageOptim::Worker.klasses.each do |klass|
|
|
26
|
+
isolated_options_base[klass.bin_sym] = false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
ImageOptim::Worker.klasses.each do |worker_klass|
|
|
30
|
+
describe "#{worker_klass.bin_sym} worker" do
|
|
31
|
+
it 'optimizes at least one test image' do
|
|
32
|
+
options = isolated_options_base.merge(worker_klass.bin_sym => true)
|
|
33
|
+
|
|
34
|
+
image_optim = ImageOptim.new(options)
|
|
35
|
+
if Array(worker_klass.init(image_optim)).empty?
|
|
36
|
+
image_optim = ImageOptim.new(options.merge(:allow_lossy => true))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
expect(test_images.any? do |original|
|
|
40
|
+
image_optim.optimize_image(temp_copy(original))
|
|
41
|
+
end).to be true
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe '#optimize_image' do
|
|
47
|
+
define :have_same_data_as do |expected|
|
|
48
|
+
match{ |actual| actual.binread == expected.binread }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
define :have_size do
|
|
52
|
+
match(&:size?)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe 'optimizing images' do
|
|
56
|
+
rotated = images_dir / 'orient/original.jpg'
|
|
57
|
+
rotate_images = images_dir.glob('orient/?.jpg')
|
|
58
|
+
|
|
59
|
+
base_options = {:skip_missing_workers => false}
|
|
60
|
+
[
|
|
61
|
+
['lossless', base_options, 0],
|
|
62
|
+
['lossy', base_options.merge(:allow_lossy => true), 0.001],
|
|
63
|
+
].each do |type, options, max_difference|
|
|
64
|
+
it "does it #{type}" do
|
|
65
|
+
image_optim = ImageOptim.new(options)
|
|
66
|
+
copies = test_images.map{ |image| temp_copy(image) }
|
|
67
|
+
pairs = image_optim.optimize_images(copies)
|
|
68
|
+
test_images.zip(*pairs.transpose).each do |original, copy, optimized|
|
|
69
|
+
expect(copy).to have_same_data_as(original)
|
|
70
|
+
|
|
71
|
+
expect(optimized).not_to be_nil
|
|
72
|
+
expect(optimized).to be_a(ImageOptim::OptimizedPath)
|
|
73
|
+
expect(optimized).to have_size
|
|
74
|
+
expect(optimized).to be_smaller_than(original)
|
|
75
|
+
expect(optimized).not_to have_same_data_as(original)
|
|
76
|
+
|
|
77
|
+
compare_to = rotate_images.include?(original) ? rotated : original
|
|
78
|
+
expect(optimized).to be_similar_to(compare_to, max_difference)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'ignores text file' do
|
|
85
|
+
original = ImageOptim::Path.new(__FILE__)
|
|
86
|
+
copy = temp_copy(original)
|
|
87
|
+
|
|
88
|
+
expect(Tempfile).not_to receive(:new)
|
|
89
|
+
optimized_image = ImageOptim.optimize_image(copy)
|
|
90
|
+
expect(optimized_image).to be_nil
|
|
91
|
+
expect(copy.read).to eq(original.read)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
{
|
|
95
|
+
:png => "\211PNG\r\n\032\n",
|
|
96
|
+
:jpeg => "\377\330",
|
|
97
|
+
}.each do |type, data|
|
|
98
|
+
it "ingores broken #{type}" do
|
|
99
|
+
path = FSPath.temp_file_path
|
|
100
|
+
path.binwrite(data)
|
|
101
|
+
expect(ImageOptim::ImageMeta).to receive(:warn)
|
|
102
|
+
expect(ImageOptim.optimize_image(path)).to be_nil
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
describe '#optimize_image!' do
|
|
108
|
+
it 'optimizes image and replaces original' do
|
|
109
|
+
original = double
|
|
110
|
+
optimized = double(:original_size => 12_345)
|
|
111
|
+
optimized_wrap = double
|
|
112
|
+
image_optim = ImageOptim.new
|
|
113
|
+
|
|
114
|
+
allow(ImageOptim::Path).to receive(:convert).
|
|
115
|
+
with(original).and_return(original)
|
|
116
|
+
|
|
117
|
+
expect(image_optim).to receive(:optimize_image).
|
|
118
|
+
with(original).and_return(optimized)
|
|
119
|
+
expect(optimized).to receive(:replace).with(original)
|
|
120
|
+
expect(ImageOptim::OptimizedPath).to receive(:new).
|
|
121
|
+
with(original, 12_345).and_return(optimized_wrap)
|
|
122
|
+
|
|
123
|
+
expect(image_optim.optimize_image!(original)).to eq(optimized_wrap)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'returns nil if optimization fails' do
|
|
127
|
+
original = double
|
|
128
|
+
image_optim = ImageOptim.new
|
|
129
|
+
|
|
130
|
+
allow(ImageOptim::Path).to receive(:convert).
|
|
131
|
+
with(original).and_return(original)
|
|
132
|
+
|
|
133
|
+
expect(image_optim).to receive(:optimize_image).
|
|
134
|
+
with(original).and_return(nil)
|
|
135
|
+
expect(ImageOptim::OptimizedPath).not_to receive(:new)
|
|
136
|
+
|
|
137
|
+
expect(image_optim.optimize_image!(original)).to eq(nil)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
describe '#optimize_image_data' do
|
|
142
|
+
it 'create temp file, optimizes image and returns data' do
|
|
143
|
+
data = double
|
|
144
|
+
temp = double(:path => double)
|
|
145
|
+
optimized = double
|
|
146
|
+
optimized_data = double
|
|
147
|
+
image_optim = ImageOptim.new
|
|
148
|
+
|
|
149
|
+
allow(ImageOptim::ImageMeta).to receive(:format_for_data).
|
|
150
|
+
with(data).and_return('xxx')
|
|
151
|
+
|
|
152
|
+
expect(ImageOptim::Path).to receive(:temp_file).and_yield(temp)
|
|
153
|
+
expect(temp).to receive(:binmode)
|
|
154
|
+
expect(temp).to receive(:write).with(data)
|
|
155
|
+
expect(temp).to receive(:close)
|
|
156
|
+
expect(image_optim).to receive(:optimize_image).
|
|
157
|
+
with(temp.path).and_return(optimized)
|
|
158
|
+
expect(optimized).to receive(:binread).and_return(optimized_data)
|
|
159
|
+
|
|
160
|
+
expect(image_optim.optimize_image_data(data)).to eq(optimized_data)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
it 'returns nil if optimization fails' do
|
|
164
|
+
data = double
|
|
165
|
+
temp = double(:path => double)
|
|
166
|
+
image_optim = ImageOptim.new
|
|
167
|
+
|
|
168
|
+
allow(ImageOptim::ImageMeta).to receive(:format_for_data).
|
|
169
|
+
with(data).and_return('xxx')
|
|
170
|
+
|
|
171
|
+
expect(ImageOptim::Path).to receive(:temp_file).and_yield(temp)
|
|
172
|
+
expect(temp).to receive(:binmode)
|
|
173
|
+
expect(temp).to receive(:write).with(data)
|
|
174
|
+
expect(temp).to receive(:close)
|
|
175
|
+
expect(image_optim).to receive(:optimize_image).
|
|
176
|
+
with(temp.path).and_return(nil)
|
|
177
|
+
|
|
178
|
+
expect(image_optim.optimize_image_data(data)).to eq(nil)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it 'returns nil if format can\'t be detected' do
|
|
182
|
+
data = double
|
|
183
|
+
image_optim = ImageOptim.new
|
|
184
|
+
|
|
185
|
+
allow(ImageOptim::ImageMeta).to receive(:format_for_data).
|
|
186
|
+
with(data).and_return(nil)
|
|
187
|
+
|
|
188
|
+
expect(ImageOptim::Path).not_to receive(:temp_file)
|
|
189
|
+
expect(image_optim).not_to receive(:optimize_image)
|
|
190
|
+
|
|
191
|
+
expect(image_optim.optimize_image_data(data)).to eq(nil)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
%w[
|
|
196
|
+
optimize_images
|
|
197
|
+
optimize_images!
|
|
198
|
+
optimize_images_data
|
|
199
|
+
].each do |list_method|
|
|
200
|
+
describe "##{list_method}" do
|
|
201
|
+
method = list_method.sub('images', 'image')
|
|
202
|
+
describe 'without block' do
|
|
203
|
+
it 'optimizes images and returns array of results' do
|
|
204
|
+
image_optim = ImageOptim.new
|
|
205
|
+
results = test_images.map do |src|
|
|
206
|
+
dst = double
|
|
207
|
+
allow(image_optim).to receive(method).with(src).and_return(dst)
|
|
208
|
+
[src, dst]
|
|
209
|
+
end
|
|
210
|
+
expect(image_optim.send(list_method, test_images)).to eq(results)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
describe 'given block' do
|
|
215
|
+
it 'optimizes images, yields path and result for each and '\
|
|
216
|
+
'returns array of yield results' do
|
|
217
|
+
image_optim = ImageOptim.new
|
|
218
|
+
results = test_images.map do |src|
|
|
219
|
+
dst = double
|
|
220
|
+
allow(image_optim).to receive(method).with(src).and_return(dst)
|
|
221
|
+
[src, dst, :test]
|
|
222
|
+
end
|
|
223
|
+
expect(image_optim.send(list_method, test_images) do |src, dst|
|
|
224
|
+
[src, dst, :test]
|
|
225
|
+
end).to eq(results)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
%w[
|
|
232
|
+
optimize_image
|
|
233
|
+
optimize_image!
|
|
234
|
+
optimize_image_data
|
|
235
|
+
optimize_images
|
|
236
|
+
optimize_images!
|
|
237
|
+
optimize_images_data
|
|
238
|
+
].each do |method|
|
|
239
|
+
describe ".#{method}" do
|
|
240
|
+
it 'is checkable by respond_to?' do
|
|
241
|
+
expect(ImageOptim).to respond_to(method)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
it 'calls same method on a new ImageOptim instance' do
|
|
245
|
+
image_optim = instance_double(ImageOptim)
|
|
246
|
+
method_arg = double
|
|
247
|
+
method_block = proc{ :x }
|
|
248
|
+
|
|
249
|
+
allow(ImageOptim).to receive(:new).and_return(image_optim)
|
|
250
|
+
expect(image_optim).to receive(method) do |arg, &block|
|
|
251
|
+
expect(arg).to eq(method_arg)
|
|
252
|
+
expect(block).to eq(method_block)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
ImageOptim.send(method, method_arg, &method_block)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
��
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
Dir.chdir(File.dirname(__FILE__))
|
|
4
|
+
|
|
5
|
+
require 'shellwords'
|
|
6
|
+
|
|
7
|
+
side = 64
|
|
8
|
+
|
|
9
|
+
IO.popen(%W[
|
|
10
|
+
convert
|
|
11
|
+
-depth 8
|
|
12
|
+
-size #{side}x#{side}
|
|
13
|
+
-strip
|
|
14
|
+
rgba:-
|
|
15
|
+
PNG32:image.png
|
|
16
|
+
].shelljoin, 'w') do |f|
|
|
17
|
+
side.times do |a|
|
|
18
|
+
side.times do |b|
|
|
19
|
+
alpha = [0, 1, 0x7f, 0xff][(a / 8 + b / 8) % 4]
|
|
20
|
+
f << [rand(256), rand(256), rand(256), alpha].pack('C*')
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
system 'image_optim --pngcrush-blacken=n image.png'
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
cd "$(dirname "$0")"
|
|
4
|
+
|
|
5
|
+
convert -size 2x2 xc:black -fill '#f00' -draw 'point 0,1' -fill '#0f0' -draw 'point 1,0' -fill '#00f' -draw 'point 1,1' -scale 64x64 original.png
|
|
6
|
+
|
|
7
|
+
convert original.png 0.jpg
|
|
8
|
+
convert original.png 1.jpg
|
|
9
|
+
convert original.png -flop 2.jpg
|
|
10
|
+
convert original.png -rotate 180 3.jpg
|
|
11
|
+
convert original.png -flip 4.jpg
|
|
12
|
+
convert original.png -transpose 5.jpg
|
|
13
|
+
convert original.png -rotate 270 6.jpg
|
|
14
|
+
convert original.png -transverse 7.jpg
|
|
15
|
+
convert original.png -rotate 90 8.jpg
|
|
16
|
+
|
|
17
|
+
convert original.png original.jpg
|
|
18
|
+
rm original.png
|
|
19
|
+
|
|
20
|
+
exiv2 -M"add Exif.Photo.UserComment image_optim-spec" *.jpg
|
|
21
|
+
for i in {1..8}; do
|
|
22
|
+
exiv2 -M"add Exif.Image.Orientation Short $i" $i.jpg
|
|
23
|
+
done
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
Dir.chdir(File.dirname(__FILE__))
|
|
4
|
+
|
|
5
|
+
require 'shellwords'
|
|
6
|
+
|
|
7
|
+
palettes = [64]
|
|
8
|
+
side = 256
|
|
9
|
+
|
|
10
|
+
palettes.each do |palette|
|
|
11
|
+
IO.popen(%W[
|
|
12
|
+
convert
|
|
13
|
+
-depth 8
|
|
14
|
+
-size #{side}x#{side}
|
|
15
|
+
-strip
|
|
16
|
+
rgb:-
|
|
17
|
+
PNG24:#{palette}.png
|
|
18
|
+
].shelljoin, 'w') do |f|
|
|
19
|
+
(side * side).times do |i|
|
|
20
|
+
color = i * palette / (side * side) * 0x10000 / palette
|
|
21
|
+
f << [color / 0x100, color % 0x100, 0].pack('C*')
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
system "identify -format 'Wrote %f with %k unique colors\n' #{palette}.png"
|
|
25
|
+
end
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
if ENV['CODECLIMATE']
|
|
2
|
+
require 'simplecov'
|
|
3
|
+
SimpleCov.start
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
require 'image_optim/pack'
|
|
7
|
+
require 'image_optim/path'
|
|
8
|
+
|
|
9
|
+
ENV['PATH'] = [
|
|
10
|
+
ImageOptim::Pack.path,
|
|
11
|
+
ENV['PATH'],
|
|
12
|
+
].compact.join File::PATH_SEPARATOR
|
|
13
|
+
|
|
14
|
+
RSpec.configure do |c|
|
|
15
|
+
c.before do
|
|
16
|
+
stub_const('ImageOptim::Config::GLOBAL_PATH', ImageOptim::Path::NULL)
|
|
17
|
+
stub_const('ImageOptim::Config::LOCAL_PATH', ImageOptim::Path::NULL)
|
|
18
|
+
ImageOptim.class_eval{ def pack; end }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
c.order = :random
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def flatten_animation(image)
|
|
25
|
+
if image.image_format == :gif
|
|
26
|
+
flattened = image.temp_path
|
|
27
|
+
command = %W[
|
|
28
|
+
convert
|
|
29
|
+
#{image}
|
|
30
|
+
-coalesce
|
|
31
|
+
-append
|
|
32
|
+
#{flattened}
|
|
33
|
+
].shelljoin
|
|
34
|
+
expect(ImageOptim::Cmd.run(command)).to be_truthy
|
|
35
|
+
flattened
|
|
36
|
+
else
|
|
37
|
+
image
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def mepp(image_a, image_b)
|
|
42
|
+
coalesce_a = flatten_animation(image_a)
|
|
43
|
+
coalesce_b = flatten_animation(image_b)
|
|
44
|
+
output = ImageOptim::Cmd.capture(%W[
|
|
45
|
+
compare
|
|
46
|
+
-metric MEPP
|
|
47
|
+
-alpha Background
|
|
48
|
+
#{coalesce_a.to_s.shellescape}
|
|
49
|
+
#{coalesce_b.to_s.shellescape}
|
|
50
|
+
#{ImageOptim::Path::NULL}
|
|
51
|
+
2>&1
|
|
52
|
+
].join(' '))
|
|
53
|
+
unless [0, 1].include?($CHILD_STATUS.exitstatus)
|
|
54
|
+
fail "compare #{image_a} with #{image_b} failed with `#{output}`"
|
|
55
|
+
end
|
|
56
|
+
num_r = '\d+(?:\.\d+(?:[eE][-+]?\d+)?)?'
|
|
57
|
+
output[/\((#{num_r}), #{num_r}\)/, 1].to_f
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
RSpec::Matchers.define :be_smaller_than do |expected|
|
|
61
|
+
match{ |actual| actual.size < expected.size }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
RSpec::Matchers.define :be_similar_to do |expected, max_difference|
|
|
65
|
+
match do |actual|
|
|
66
|
+
@diff = mepp(actual, expected)
|
|
67
|
+
@diff <= max_difference
|
|
68
|
+
end
|
|
69
|
+
failure_message do |actual|
|
|
70
|
+
"expected #{actual} to have at most #{max_difference} difference from "\
|
|
71
|
+
"#{expected}, got normalized root-mean-square error of #{@diff}"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
module CapabilityCheckHelpers
|
|
76
|
+
def any_file_modes_allowed?
|
|
77
|
+
Tempfile.open 'posix' do |f|
|
|
78
|
+
File.chmod(0, f.path)
|
|
79
|
+
return (File.stat(f.path).mode & 0o777).zero?
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def inodes_supported?
|
|
84
|
+
!File.stat(__FILE__).ino.zero?
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def signals_supported?
|
|
88
|
+
Process.kill(0, 0)
|
|
89
|
+
true
|
|
90
|
+
rescue
|
|
91
|
+
false
|
|
92
|
+
end
|
|
93
|
+
end
|