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.
- checksums.yaml +8 -8
- data/.appveyor.yml +95 -0
- data/.rubocop.yml +3 -0
- data/.travis.yml +27 -22
- data/CHANGELOG.markdown +10 -0
- data/CONTRIBUTING.markdown +2 -1
- data/Gemfile +1 -1
- data/README.markdown +10 -2
- data/image_optim.gemspec +4 -4
- data/lib/image_optim.rb +32 -16
- data/lib/image_optim/bin_resolver/bin.rb +11 -4
- data/lib/image_optim/cache.rb +71 -0
- data/lib/image_optim/cache_path.rb +16 -0
- data/lib/image_optim/config.rb +12 -2
- data/lib/image_optim/handler.rb +1 -1
- data/lib/image_optim/image_meta.rb +5 -10
- data/lib/image_optim/optimized_path.rb +25 -0
- data/lib/image_optim/path.rb +70 -0
- data/lib/image_optim/runner/option_parser.rb +13 -0
- data/lib/image_optim/worker.rb +5 -8
- data/lib/image_optim/worker/class_methods.rb +3 -1
- data/lib/image_optim/worker/jpegoptim.rb +3 -0
- data/lib/image_optim/worker/jpegrecompress.rb +3 -0
- data/lib/image_optim/worker/pngquant.rb +3 -0
- data/script/worker_analysis +10 -9
- data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +1 -1
- data/spec/image_optim/bin_resolver/simple_version_spec.rb +48 -40
- data/spec/image_optim/bin_resolver_spec.rb +190 -172
- data/spec/image_optim/cache_path_spec.rb +59 -0
- data/spec/image_optim/cache_spec.rb +159 -0
- data/spec/image_optim/cmd_spec.rb +11 -7
- data/spec/image_optim/config_spec.rb +92 -71
- data/spec/image_optim/handler_spec.rb +3 -6
- 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_helpers_spec.rb +25 -0
- data/spec/image_optim/path_spec.rb +105 -0
- data/spec/image_optim/railtie_spec.rb +6 -6
- data/spec/image_optim/runner/glob_helpers_spec.rb +2 -6
- data/spec/image_optim/runner/option_parser_spec.rb +3 -3
- data/spec/image_optim/space_spec.rb +16 -18
- data/spec/image_optim/worker/optipng_spec.rb +3 -3
- data/spec/image_optim/worker/pngquant_spec.rb +47 -7
- data/spec/image_optim/worker_spec.rb +114 -17
- data/spec/image_optim_spec.rb +58 -69
- data/spec/images/broken_jpeg +1 -0
- data/spec/spec_helper.rb +40 -10
- metadata +30 -8
- data/lib/image_optim/image_path.rb +0 -68
- data/spec/image_optim/image_path_spec.rb +0 -54
@@ -6,12 +6,36 @@ describe ImageOptim::Worker do
|
|
6
6
|
before do
|
7
7
|
stub_const('Worker', ImageOptim::Worker)
|
8
8
|
stub_const('BinResolver', ImageOptim::BinResolver)
|
9
|
+
|
10
|
+
# don't add to list of wokers
|
11
|
+
allow(ImageOptim::Worker).to receive(:inherited)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#initialize' do
|
15
|
+
it 'expects first argument to be an instanace of ImageOptim' do
|
16
|
+
expect do
|
17
|
+
Worker.new(double)
|
18
|
+
end.to raise_error ArgumentError
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#options' do
|
23
|
+
it 'returns a Hash with options' do
|
24
|
+
worker_class = Class.new(Worker) do
|
25
|
+
option(:one, 1, 'One')
|
26
|
+
option(:two, 2, 'Two')
|
27
|
+
option(:three, 3, 'Three')
|
28
|
+
end
|
29
|
+
|
30
|
+
worker = worker_class.new(ImageOptim.new, :three => '...')
|
31
|
+
|
32
|
+
expect(worker.options).to eq(:one => 1, :two => 2, :three => '...')
|
33
|
+
end
|
9
34
|
end
|
10
35
|
|
11
|
-
describe
|
36
|
+
describe '#optimize' do
|
12
37
|
it 'raises NotImplementedError' do
|
13
|
-
|
14
|
-
worker = Worker.new(image_optim, {})
|
38
|
+
worker = Worker.new(ImageOptim.new, {})
|
15
39
|
|
16
40
|
expect do
|
17
41
|
worker.optimize(double, double)
|
@@ -19,7 +43,50 @@ describe ImageOptim::Worker do
|
|
19
43
|
end
|
20
44
|
end
|
21
45
|
|
22
|
-
describe
|
46
|
+
describe '#image_formats' do
|
47
|
+
{
|
48
|
+
'GifOptim' => :gif,
|
49
|
+
'JpegOptim' => :jpeg,
|
50
|
+
'PngOptim' => :png,
|
51
|
+
'SvgOptim' => :svg,
|
52
|
+
}.each do |class_name, image_format|
|
53
|
+
it "detects if class name contains #{image_format}" do
|
54
|
+
worker = stub_const(class_name, Class.new(Worker)).new(ImageOptim.new)
|
55
|
+
expect(worker.image_formats).to eq([image_format])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'fails if class name does not contain known type' do
|
60
|
+
worker = stub_const('TiffOptim', Class.new(Worker)).new(ImageOptim.new)
|
61
|
+
expect{ worker.image_formats }.to raise_error(/can't guess/)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#inspect' do
|
66
|
+
it 'returns inspect String containing options' do
|
67
|
+
stub_const('DefOptim', Class.new(Worker) do
|
68
|
+
option(:one, 1, 'One')
|
69
|
+
option(:two, 2, 'Two')
|
70
|
+
option(:three, 3, 'Three')
|
71
|
+
end)
|
72
|
+
|
73
|
+
worker = DefOptim.new(ImageOptim.new, :three => '...')
|
74
|
+
|
75
|
+
expect(worker.inspect).to eq('#<DefOptim @one=1, @two=2, @three="...">')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '.inherited' do
|
80
|
+
it 'adds subclasses to klasses' do
|
81
|
+
base_class = Class.new{ extend ImageOptim::Worker::ClassMethods }
|
82
|
+
expect(base_class.klasses.to_a).to eq([])
|
83
|
+
|
84
|
+
worker_class = Class.new(base_class)
|
85
|
+
expect(base_class.klasses.to_a).to eq([worker_class])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '.create_all_by_format' do
|
23
90
|
it 'passes arguments to create_all' do
|
24
91
|
image_optim = double
|
25
92
|
options_proc = proc{ true }
|
@@ -52,17 +119,21 @@ describe ImageOptim::Worker do
|
|
52
119
|
end
|
53
120
|
end
|
54
121
|
|
55
|
-
describe
|
122
|
+
describe '.create_all' do
|
56
123
|
def worker_double(override = {})
|
57
124
|
stubs = {:resolve_used_bins! => nil, :run_order => 0}.merge(override)
|
58
125
|
instance_double(Worker, stubs)
|
59
126
|
end
|
60
127
|
|
128
|
+
def worker_class_doubles(workers)
|
129
|
+
workers.map{ |worker| class_double(Worker, :init => worker) }
|
130
|
+
end
|
131
|
+
|
61
132
|
let(:image_optim){ double(:allow_lossy => false) }
|
62
133
|
|
63
134
|
it 'creates all workers for which options_proc returns true' do
|
64
135
|
workers = Array.new(3){ worker_double }
|
65
|
-
klasses = workers
|
136
|
+
klasses = worker_class_doubles(workers)
|
66
137
|
options_proc = proc do |klass|
|
67
138
|
klass == klasses[1] ? {:disable => true} : {}
|
68
139
|
end
|
@@ -79,7 +150,7 @@ describe ImageOptim::Worker do
|
|
79
150
|
[worker_double, worker_double, worker_double],
|
80
151
|
worker_double,
|
81
152
|
]
|
82
|
-
klasses = workers
|
153
|
+
klasses = worker_class_doubles(workers)
|
83
154
|
|
84
155
|
allow(Worker).to receive(:klasses).and_return(klasses)
|
85
156
|
|
@@ -98,7 +169,7 @@ describe ImageOptim::Worker do
|
|
98
169
|
worker
|
99
170
|
end
|
100
171
|
end
|
101
|
-
let(:klasses){ workers
|
172
|
+
let(:klasses){ worker_class_doubles(workers) }
|
102
173
|
|
103
174
|
before do
|
104
175
|
allow(Worker).to receive(:klasses).and_return(klasses)
|
@@ -136,12 +207,11 @@ describe ImageOptim::Worker do
|
|
136
207
|
end
|
137
208
|
|
138
209
|
it 'orders workers by run_order' do
|
139
|
-
image_optim = double(:allow_lossy => false)
|
140
210
|
run_orders = [10, -10, 0, 0, 0, 10, -10]
|
141
211
|
workers = run_orders.map do |run_order|
|
142
212
|
worker_double(:run_order => run_order)
|
143
213
|
end
|
144
|
-
klasses_list = workers
|
214
|
+
klasses_list = worker_class_doubles(workers)
|
145
215
|
|
146
216
|
[
|
147
217
|
klasses_list,
|
@@ -157,13 +227,43 @@ describe ImageOptim::Worker do
|
|
157
227
|
expect(Worker.create_all(image_optim){ {} }).to eq(expected_order)
|
158
228
|
end
|
159
229
|
end
|
230
|
+
|
231
|
+
describe 'passing allow_lossy' do
|
232
|
+
it 'passes allow_lossy if worker has such attribute' do
|
233
|
+
klasses = worker_class_doubles([worker_double, worker_double])
|
234
|
+
|
235
|
+
allow(Worker).to receive(:klasses).and_return(klasses)
|
236
|
+
|
237
|
+
klasses[0].send(:attr_reader, :allow_lossy)
|
238
|
+
expect(klasses[0]).to receive(:init).
|
239
|
+
with(image_optim, hash_including(:allow_lossy))
|
240
|
+
expect(klasses[1]).to receive(:init).
|
241
|
+
with(image_optim, hash_not_including(:allow_lossy))
|
242
|
+
|
243
|
+
Worker.create_all(image_optim){ {} }
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'allows overriding per worker' do
|
247
|
+
klasses = worker_class_doubles([worker_double, worker_double])
|
248
|
+
options_proc = proc do |klass|
|
249
|
+
klass == klasses[1] ? {:allow_lossy => :b} : {}
|
250
|
+
end
|
251
|
+
|
252
|
+
allow(Worker).to receive(:klasses).and_return(klasses)
|
253
|
+
|
254
|
+
klasses.each{ |klass| klass.send(:attr_reader, :allow_lossy) }
|
255
|
+
expect(klasses[0]).to receive(:init).
|
256
|
+
with(image_optim, hash_including(:allow_lossy => false))
|
257
|
+
expect(klasses[1]).to receive(:init).
|
258
|
+
with(image_optim, hash_including(:allow_lossy => :b))
|
259
|
+
|
260
|
+
Worker.create_all(image_optim, &options_proc)
|
261
|
+
end
|
262
|
+
end
|
160
263
|
end
|
161
264
|
|
162
|
-
describe
|
265
|
+
describe '.option' do
|
163
266
|
it 'runs option block in context of worker' do
|
164
|
-
# don't add Abc to list of wokers
|
165
|
-
allow(ImageOptim::Worker).to receive(:inherited)
|
166
|
-
|
167
267
|
stub_const('Abc', Class.new(Worker) do
|
168
268
|
option(:test, 1, 'Test context') do |_v|
|
169
269
|
some_instance_method
|
@@ -176,9 +276,6 @@ describe ImageOptim::Worker do
|
|
176
276
|
end
|
177
277
|
|
178
278
|
it 'returns instance of OptionDefinition' do
|
179
|
-
# don't add Abc to list of wokers
|
180
|
-
allow(ImageOptim::Worker).to receive(:inherited)
|
181
|
-
|
182
279
|
definition = nil
|
183
280
|
Class.new(Worker) do
|
184
281
|
definition = option(:test, 1, 'Test'){ |v| v }
|
data/spec/image_optim_spec.rb
CHANGED
@@ -5,7 +5,7 @@ require 'tempfile'
|
|
5
5
|
require 'English'
|
6
6
|
|
7
7
|
describe ImageOptim do
|
8
|
-
root_dir = ImageOptim::
|
8
|
+
root_dir = ImageOptim::Path.new(__FILE__).dirname.dirname
|
9
9
|
images_dir = root_dir / 'spec/images'
|
10
10
|
test_images = images_dir.glob('**/*.*').freeze
|
11
11
|
|
@@ -17,15 +17,8 @@ describe ImageOptim do
|
|
17
17
|
include helpers
|
18
18
|
extend helpers
|
19
19
|
|
20
|
-
matcher :be_in_range do |expected|
|
21
|
-
match{ |actual| expected.include?(actual) }
|
22
|
-
end
|
23
|
-
|
24
20
|
before do
|
25
21
|
stub_const('Cmd', ImageOptim::Cmd)
|
26
|
-
|
27
|
-
allow(ImageOptim::Config).to receive(:global).and_return({})
|
28
|
-
allow(ImageOptim::Config).to receive(:local).and_return({})
|
29
22
|
end
|
30
23
|
|
31
24
|
isolated_options_base = {:skip_missing_workers => false}
|
@@ -50,7 +43,7 @@ describe ImageOptim do
|
|
50
43
|
end
|
51
44
|
end
|
52
45
|
|
53
|
-
describe
|
46
|
+
describe '#optimize_image' do
|
54
47
|
define :have_same_data_as do |expected|
|
55
48
|
match{ |actual| actual.binread == expected.binread }
|
56
49
|
end
|
@@ -68,30 +61,28 @@ describe ImageOptim do
|
|
68
61
|
['lossless', base_options, 0],
|
69
62
|
['lossy', base_options.merge(:allow_lossy => true), 0.001],
|
70
63
|
].each do |type, options, max_difference|
|
71
|
-
|
72
|
-
|
64
|
+
it "does it #{type}" do
|
65
|
+
image_optim = ImageOptim.new(options)
|
73
66
|
copies = test_images.map{ |image| temp_copy(image) }
|
74
67
|
pairs = image_optim.optimize_images(copies)
|
75
68
|
test_images.zip(*pairs.transpose).each do |original, copy, optimized|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
expect(optimized).to be_similar_to(compare_to, max_difference)
|
87
|
-
end
|
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)
|
88
79
|
end
|
89
80
|
end
|
90
81
|
end
|
91
82
|
end
|
92
83
|
|
93
84
|
it 'ignores text file' do
|
94
|
-
original = ImageOptim::
|
85
|
+
original = ImageOptim::Path.new(__FILE__)
|
95
86
|
copy = temp_copy(original)
|
96
87
|
|
97
88
|
expect(Tempfile).not_to receive(:new)
|
@@ -106,27 +97,27 @@ describe ImageOptim do
|
|
106
97
|
}.each do |type, data|
|
107
98
|
it "ingores broken #{type}" do
|
108
99
|
path = FSPath.temp_file_path
|
109
|
-
path.
|
100
|
+
path.binwrite(data)
|
110
101
|
expect(ImageOptim::ImageMeta).to receive(:warn)
|
111
102
|
expect(ImageOptim.optimize_image(path)).to be_nil
|
112
103
|
end
|
113
104
|
end
|
114
105
|
end
|
115
106
|
|
116
|
-
describe
|
107
|
+
describe '#optimize_image!' do
|
117
108
|
it 'optimizes image and replaces original' do
|
118
109
|
original = double
|
119
110
|
optimized = double(:original_size => 12_345)
|
120
111
|
optimized_wrap = double
|
121
112
|
image_optim = ImageOptim.new
|
122
113
|
|
123
|
-
allow(ImageOptim::
|
114
|
+
allow(ImageOptim::Path).to receive(:convert).
|
124
115
|
with(original).and_return(original)
|
125
116
|
|
126
117
|
expect(image_optim).to receive(:optimize_image).
|
127
118
|
with(original).and_return(optimized)
|
128
119
|
expect(optimized).to receive(:replace).with(original)
|
129
|
-
expect(ImageOptim::
|
120
|
+
expect(ImageOptim::OptimizedPath).to receive(:new).
|
130
121
|
with(original, 12_345).and_return(optimized_wrap)
|
131
122
|
|
132
123
|
expect(image_optim.optimize_image!(original)).to eq(optimized_wrap)
|
@@ -136,18 +127,18 @@ describe ImageOptim do
|
|
136
127
|
original = double
|
137
128
|
image_optim = ImageOptim.new
|
138
129
|
|
139
|
-
allow(ImageOptim::
|
130
|
+
allow(ImageOptim::Path).to receive(:convert).
|
140
131
|
with(original).and_return(original)
|
141
132
|
|
142
133
|
expect(image_optim).to receive(:optimize_image).
|
143
134
|
with(original).and_return(nil)
|
144
|
-
expect(ImageOptim::
|
135
|
+
expect(ImageOptim::OptimizedPath).not_to receive(:new)
|
145
136
|
|
146
137
|
expect(image_optim.optimize_image!(original)).to eq(nil)
|
147
138
|
end
|
148
139
|
end
|
149
140
|
|
150
|
-
describe
|
141
|
+
describe '#optimize_image_data' do
|
151
142
|
it 'create temp file, optimizes image and returns data' do
|
152
143
|
data = double
|
153
144
|
temp = double(:path => double)
|
@@ -155,10 +146,10 @@ describe ImageOptim do
|
|
155
146
|
optimized_data = double
|
156
147
|
image_optim = ImageOptim.new
|
157
148
|
|
158
|
-
allow(ImageOptim::ImageMeta).to receive(:
|
159
|
-
with(data).and_return(
|
149
|
+
allow(ImageOptim::ImageMeta).to receive(:format_for_data).
|
150
|
+
with(data).and_return('xxx')
|
160
151
|
|
161
|
-
expect(ImageOptim::
|
152
|
+
expect(ImageOptim::Path).to receive(:temp_file).and_yield(temp)
|
162
153
|
expect(temp).to receive(:binmode)
|
163
154
|
expect(temp).to receive(:write).with(data)
|
164
155
|
expect(temp).to receive(:close)
|
@@ -174,10 +165,10 @@ describe ImageOptim do
|
|
174
165
|
temp = double(:path => double)
|
175
166
|
image_optim = ImageOptim.new
|
176
167
|
|
177
|
-
allow(ImageOptim::ImageMeta).to receive(:
|
178
|
-
with(data).and_return(
|
168
|
+
allow(ImageOptim::ImageMeta).to receive(:format_for_data).
|
169
|
+
with(data).and_return('xxx')
|
179
170
|
|
180
|
-
expect(ImageOptim::
|
171
|
+
expect(ImageOptim::Path).to receive(:temp_file).and_yield(temp)
|
181
172
|
expect(temp).to receive(:binmode)
|
182
173
|
expect(temp).to receive(:write).with(data)
|
183
174
|
expect(temp).to receive(:close)
|
@@ -191,49 +182,47 @@ describe ImageOptim do
|
|
191
182
|
data = double
|
192
183
|
image_optim = ImageOptim.new
|
193
184
|
|
194
|
-
allow(ImageOptim::ImageMeta).to receive(:
|
195
|
-
with(data).and_return(
|
185
|
+
allow(ImageOptim::ImageMeta).to receive(:format_for_data).
|
186
|
+
with(data).and_return(nil)
|
196
187
|
|
197
|
-
expect(ImageOptim::
|
188
|
+
expect(ImageOptim::Path).not_to receive(:temp_file)
|
198
189
|
expect(image_optim).not_to receive(:optimize_image)
|
199
190
|
|
200
191
|
expect(image_optim.optimize_image_data(data)).to eq(nil)
|
201
192
|
end
|
202
193
|
end
|
203
194
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
[src, dst]
|
219
|
-
end
|
220
|
-
expect(image_optim.send(list_method, test_images)).to eq(results)
|
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
|
+
expect(image_optim).to receive(method).with(src).and_return(dst)
|
208
|
+
[src, dst]
|
221
209
|
end
|
210
|
+
expect(image_optim.send(list_method, test_images)).to eq(results)
|
222
211
|
end
|
212
|
+
end
|
223
213
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
end
|
233
|
-
expect(image_optim.send(list_method, test_images) do |src, dst|
|
234
|
-
[src, dst, :test]
|
235
|
-
end).to eq(results)
|
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
|
+
expect(image_optim).to receive(method).with(src).and_return(dst)
|
221
|
+
[src, dst, :test]
|
236
222
|
end
|
223
|
+
expect(image_optim.send(list_method, test_images) do |src, dst|
|
224
|
+
[src, dst, :test]
|
225
|
+
end).to eq(results)
|
237
226
|
end
|
238
227
|
end
|
239
228
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
��
|
data/spec/spec_helper.rb
CHANGED
@@ -1,18 +1,28 @@
|
|
1
|
-
if ENV['CODECLIMATE_REPO_TOKEN']
|
2
|
-
|
3
|
-
|
4
|
-
CodeClimate::TestReporter.start
|
5
|
-
rescue LoadError => e
|
6
|
-
$stderr.puts "Got following while loading codeclimate-test-reporter: #{e}"
|
7
|
-
end
|
1
|
+
if ENV['CODECLIMATE'] && ENV['CODECLIMATE_REPO_TOKEN']
|
2
|
+
require 'codeclimate-test-reporter'
|
3
|
+
CodeClimate::TestReporter.start
|
8
4
|
end
|
9
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
|
+
|
10
14
|
RSpec.configure do |c|
|
11
|
-
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
|
12
22
|
end
|
13
23
|
|
14
24
|
def flatten_animation(image)
|
15
|
-
if image.
|
25
|
+
if image.image_format == :gif
|
16
26
|
flattened = image.temp_path
|
17
27
|
command = %W[
|
18
28
|
convert
|
@@ -37,7 +47,7 @@ def mepp(image_a, image_b)
|
|
37
47
|
-alpha Background
|
38
48
|
#{coalesce_a.to_s.shellescape}
|
39
49
|
#{coalesce_b.to_s.shellescape}
|
40
|
-
|
50
|
+
#{ImageOptim::Path::NULL}
|
41
51
|
2>&1
|
42
52
|
].join(' ')
|
43
53
|
output = ImageOptim::Cmd.capture(command)
|
@@ -63,3 +73,23 @@ RSpec::Matchers.define :be_similar_to do |expected, max_difference|
|
|
63
73
|
"#{expected}, got normalized root-mean-square error of #{@diff}"
|
64
74
|
end
|
65
75
|
end
|
76
|
+
|
77
|
+
module CapabilityCheckHelpers
|
78
|
+
def any_file_modes_allowed?
|
79
|
+
Tempfile.open 'posix' do |f|
|
80
|
+
File.chmod(0, f.path)
|
81
|
+
File.stat(f.path).mode & 0o777 == 0
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def inodes_supported?
|
86
|
+
File.stat(__FILE__).ino != 0
|
87
|
+
end
|
88
|
+
|
89
|
+
def signals_supported?
|
90
|
+
Process.kill(0, 0)
|
91
|
+
true
|
92
|
+
rescue
|
93
|
+
false
|
94
|
+
end
|
95
|
+
end
|