image_optim 0.28.0 → 0.31.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/check.yml +89 -0
- data/.pre-commit-hooks.yaml +9 -0
- data/.rubocop.yml +6 -3
- data/CHANGELOG.markdown +20 -0
- data/CONTRIBUTING.markdown +1 -1
- data/Gemfile +1 -7
- data/LICENSE.txt +1 -1
- data/README.markdown +18 -9
- data/Vagrantfile +1 -1
- data/image_optim.gemspec +6 -3
- data/lib/image_optim/bin_resolver/bin.rb +9 -9
- data/lib/image_optim/cache.rb +6 -0
- data/lib/image_optim/cmd.rb +45 -6
- data/lib/image_optim/config.rb +11 -5
- data/lib/image_optim/elapsed_time.rb +26 -0
- data/lib/image_optim/errors.rb +9 -0
- data/lib/image_optim/path.rb +1 -1
- data/lib/image_optim/runner/option_parser.rb +21 -17
- data/lib/image_optim/runner.rb +1 -1
- data/lib/image_optim/timer.rb +25 -0
- data/lib/image_optim/worker/advpng.rb +7 -7
- data/lib/image_optim/worker/gifsicle.rb +11 -11
- data/lib/image_optim/worker/jhead.rb +2 -2
- data/lib/image_optim/worker/jpegoptim.rb +11 -11
- data/lib/image_optim/worker/jpegrecompress.rb +6 -6
- data/lib/image_optim/worker/jpegtran.rb +4 -4
- data/lib/image_optim/worker/optipng.rb +7 -7
- data/lib/image_optim/worker/oxipng.rb +53 -0
- data/lib/image_optim/worker/pngcrush.rb +6 -6
- data/lib/image_optim/worker/pngout.rb +7 -7
- data/lib/image_optim/worker/pngquant.rb +9 -9
- data/lib/image_optim/worker/svgo.rb +2 -2
- data/lib/image_optim/worker.rb +32 -29
- data/lib/image_optim.rb +16 -10
- data/script/update_worker_options_in_readme +1 -1
- data/script/worker_analysis +16 -18
- data/spec/image_optim/bin_resolver_spec.rb +5 -5
- data/spec/image_optim/cache_path_spec.rb +7 -10
- data/spec/image_optim/cache_spec.rb +7 -7
- data/spec/image_optim/cmd_spec.rb +64 -6
- data/spec/image_optim/config_spec.rb +36 -20
- data/spec/image_optim/elapsed_time_spec.rb +14 -0
- data/spec/image_optim/handler_spec.rb +1 -1
- data/spec/image_optim/hash_helpers_spec.rb +18 -18
- data/spec/image_optim/option_definition_spec.rb +6 -6
- data/spec/image_optim/path_spec.rb +8 -11
- data/spec/image_optim/runner/option_parser_spec.rb +4 -4
- data/spec/image_optim/timer_spec.rb +32 -0
- data/spec/image_optim/worker/jpegrecompress_spec.rb +2 -2
- data/spec/image_optim/worker/optipng_spec.rb +11 -11
- data/spec/image_optim/worker/oxipng_spec.rb +89 -0
- data/spec/image_optim/worker/pngquant_spec.rb +5 -5
- data/spec/image_optim/worker_spec.rb +17 -17
- data/spec/image_optim_spec.rb +47 -10
- data/spec/images/invisiblepixels/generate +1 -1
- data/spec/spec_helper.rb +24 -21
- metadata +35 -11
- data/.appveyor.yml +0 -53
- data/.travis.yml +0 -48
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'image_optim/timer'
|
5
|
+
|
6
|
+
describe ImageOptim::Timer do
|
7
|
+
let!(:timer){ described_class.new(1) }
|
8
|
+
|
9
|
+
describe '#elapsed' do
|
10
|
+
it 'returns elapsed time' do
|
11
|
+
sleep 0.01
|
12
|
+
|
13
|
+
expect(timer.elapsed).to be >= 0.01
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#left' do
|
18
|
+
it 'returns time left' do
|
19
|
+
sleep 0.01
|
20
|
+
|
21
|
+
expect(timer.left).to be <= 0.99
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#to_f' do
|
26
|
+
it 'returns time left' do
|
27
|
+
sleep 0.01
|
28
|
+
|
29
|
+
expect(timer.to_f).to be <= 0.99
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -14,7 +14,7 @@ describe ImageOptim::Worker::Jpegrecompress do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
context 'uses default when invalid' do
|
17
|
-
let(:method){ {:
|
17
|
+
let(:method){ {method: 'invalid'} }
|
18
18
|
|
19
19
|
it 'warns and keeps default' do
|
20
20
|
expect_any_instance_of(described_class).
|
@@ -24,7 +24,7 @@ describe ImageOptim::Worker::Jpegrecompress do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
context 'can use a valid option' do
|
27
|
-
let(:method){ {:
|
27
|
+
let(:method){ {method: 'smallfry'} }
|
28
28
|
|
29
29
|
it{ is_expected.to eq('smallfry') }
|
30
30
|
end
|
@@ -10,7 +10,7 @@ describe ImageOptim::Worker::Optipng do
|
|
10
10
|
|
11
11
|
let(:options){ {} }
|
12
12
|
let(:optipng_version){ '0.7' }
|
13
|
-
let(:src){ instance_double(ImageOptim::Path, :
|
13
|
+
let(:src){ instance_double(ImageOptim::Path, copy: nil) }
|
14
14
|
let(:dst){ instance_double(ImageOptim::Path) }
|
15
15
|
|
16
16
|
before do
|
@@ -34,7 +34,7 @@ describe ImageOptim::Worker::Optipng do
|
|
34
34
|
end
|
35
35
|
|
36
36
|
context 'when strip is disabled' do
|
37
|
-
let(:options){ {:
|
37
|
+
let(:options){ {strip: false} }
|
38
38
|
|
39
39
|
it 'should not add -strip all to arguments' do
|
40
40
|
expect(subject).to receive(:execute) do |_bin, *args|
|
@@ -61,42 +61,42 @@ describe ImageOptim::Worker::Optipng do
|
|
61
61
|
describe '#optimized?' do
|
62
62
|
let(:src){ instance_double(ImageOptim::Path, src_options) }
|
63
63
|
let(:dst){ instance_double(ImageOptim::Path, dst_options) }
|
64
|
-
let(:src_options){ {:
|
65
|
-
let(:dst_options){ {
|
64
|
+
let(:src_options){ {size: 10} }
|
65
|
+
let(:dst_options){ {size?: 9} }
|
66
66
|
let(:instance){ described_class.new(ImageOptim.new, instance_options) }
|
67
67
|
let(:instance_options){ {} }
|
68
68
|
|
69
69
|
subject{ instance.optimized?(src, dst) }
|
70
70
|
|
71
71
|
context 'when interlace option is enabled' do
|
72
|
-
let(:instance_options){ {:
|
72
|
+
let(:instance_options){ {interlace: true} }
|
73
73
|
|
74
74
|
context 'when dst is empty' do
|
75
|
-
let(:dst_options){ {
|
75
|
+
let(:dst_options){ {size?: nil} }
|
76
76
|
it{ is_expected.to be_falsy }
|
77
77
|
end
|
78
78
|
|
79
79
|
context 'when dst is not empty' do
|
80
|
-
let(:dst_options){ {
|
80
|
+
let(:dst_options){ {size?: 20} }
|
81
81
|
it{ is_expected.to be_truthy }
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
85
|
context 'when interlace option is disabled' do
|
86
|
-
let(:instance_options){ {:
|
86
|
+
let(:instance_options){ {interlace: false} }
|
87
87
|
|
88
88
|
context 'when dst is empty' do
|
89
|
-
let(:dst_options){ {
|
89
|
+
let(:dst_options){ {size?: nil} }
|
90
90
|
it{ is_expected.to be_falsy }
|
91
91
|
end
|
92
92
|
|
93
93
|
context 'when dst is greater than or equal to src' do
|
94
|
-
let(:dst_options){ {
|
94
|
+
let(:dst_options){ {size?: 10} }
|
95
95
|
it{ is_expected.to be_falsy }
|
96
96
|
end
|
97
97
|
|
98
98
|
context 'when dst is less than src' do
|
99
|
-
let(:dst_options){ {
|
99
|
+
let(:dst_options){ {size?: 9} }
|
100
100
|
it{ is_expected.to be_truthy }
|
101
101
|
end
|
102
102
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'image_optim/worker/oxipng'
|
5
|
+
require 'image_optim/path'
|
6
|
+
|
7
|
+
describe ImageOptim::Worker::Oxipng do
|
8
|
+
describe 'strip option' do
|
9
|
+
subject{ described_class.new(ImageOptim.new, options) }
|
10
|
+
|
11
|
+
let(:options){ {} }
|
12
|
+
let(:src){ instance_double(ImageOptim::Path, copy: nil) }
|
13
|
+
let(:dst){ instance_double(ImageOptim::Path) }
|
14
|
+
|
15
|
+
before do
|
16
|
+
oxipng_bin = instance_double(ImageOptim::BinResolver::Bin)
|
17
|
+
allow(subject).to receive(:resolve_bin!).
|
18
|
+
with(:oxipng).and_return(oxipng_bin)
|
19
|
+
|
20
|
+
allow(subject).to receive(:optimized?)
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'by default' do
|
24
|
+
it 'should add --strip all to arguments' do
|
25
|
+
expect(subject).to receive(:execute) do |_bin, *args|
|
26
|
+
expect(args.join(' ')).to match(/(^| )--strip all($| )/)
|
27
|
+
end
|
28
|
+
|
29
|
+
subject.optimize(src, dst)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when strip is disabled' do
|
34
|
+
let(:options){ {strip: false} }
|
35
|
+
|
36
|
+
it 'should not add --strip all to arguments' do
|
37
|
+
expect(subject).to receive(:execute) do |_bin, *args|
|
38
|
+
expect(args.join(' ')).not_to match(/(^| )--strip all($| )/)
|
39
|
+
end
|
40
|
+
|
41
|
+
subject.optimize(src, dst)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#optimized?' do
|
47
|
+
let(:src){ instance_double(ImageOptim::Path, src_options) }
|
48
|
+
let(:dst){ instance_double(ImageOptim::Path, dst_options) }
|
49
|
+
let(:src_options){ {size: 10} }
|
50
|
+
let(:dst_options){ {size?: 9} }
|
51
|
+
let(:instance){ described_class.new(ImageOptim.new, instance_options) }
|
52
|
+
let(:instance_options){ {} }
|
53
|
+
|
54
|
+
subject{ instance.optimized?(src, dst) }
|
55
|
+
|
56
|
+
context 'when interlace option is enabled' do
|
57
|
+
let(:instance_options){ {interlace: true} }
|
58
|
+
|
59
|
+
context 'when dst is empty' do
|
60
|
+
let(:dst_options){ {size?: nil} }
|
61
|
+
it{ is_expected.to be_falsy }
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when dst is not empty' do
|
65
|
+
let(:dst_options){ {size?: 20} }
|
66
|
+
it{ is_expected.to be_truthy }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when interlace option is disabled' do
|
71
|
+
let(:instance_options){ {interlace: false} }
|
72
|
+
|
73
|
+
context 'when dst is empty' do
|
74
|
+
let(:dst_options){ {size?: nil} }
|
75
|
+
it{ is_expected.to be_falsy }
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when dst is greater than or equal to src' do
|
79
|
+
let(:dst_options){ {size?: 10} }
|
80
|
+
it{ is_expected.to be_falsy }
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when dst is less than src' do
|
84
|
+
let(:dst_options){ {size?: 9} }
|
85
|
+
it{ is_expected.to be_truthy }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -22,7 +22,7 @@ describe ImageOptim::Worker::Pngquant do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
context 'when value is passed through options' do
|
25
|
-
let(:options){ {:
|
25
|
+
let(:options){ {quality: 10..90} }
|
26
26
|
|
27
27
|
it 'warns and keeps default' do
|
28
28
|
expect_any_instance_of(described_class).
|
@@ -34,13 +34,13 @@ describe ImageOptim::Worker::Pngquant do
|
|
34
34
|
|
35
35
|
context 'when lossy allowed' do
|
36
36
|
context 'by default' do
|
37
|
-
let(:options){ {:
|
37
|
+
let(:options){ {allow_lossy: true} }
|
38
38
|
|
39
39
|
it{ is_expected.to eq(0..100) }
|
40
40
|
end
|
41
41
|
|
42
42
|
context 'when value is passed through options' do
|
43
|
-
let(:options){ {:
|
43
|
+
let(:options){ {allow_lossy: true, quality: 10..90} }
|
44
44
|
|
45
45
|
it 'sets the value without warning' do
|
46
46
|
expect_any_instance_of(described_class).not_to receive(:warn)
|
@@ -49,7 +49,7 @@ describe ImageOptim::Worker::Pngquant do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
context 'when passed range begin is less than 0' do
|
52
|
-
let(:options){ {:
|
52
|
+
let(:options){ {allow_lossy: true, quality: -50..50} }
|
53
53
|
|
54
54
|
it 'sets begin to 0' do
|
55
55
|
is_expected.to eq(0..50)
|
@@ -57,7 +57,7 @@ describe ImageOptim::Worker::Pngquant do
|
|
57
57
|
end
|
58
58
|
|
59
59
|
context 'when passed range end is more than 100' do
|
60
|
-
let(:options){ {:
|
60
|
+
let(:options){ {allow_lossy: true, quality: 50..150} }
|
61
61
|
|
62
62
|
it 'sets end to 100' do
|
63
63
|
is_expected.to eq(50..100)
|
@@ -29,9 +29,9 @@ describe ImageOptim::Worker do
|
|
29
29
|
option(:three, 3, 'Three')
|
30
30
|
end
|
31
31
|
|
32
|
-
worker = worker_class.new(ImageOptim.new, :
|
32
|
+
worker = worker_class.new(ImageOptim.new, three: '...')
|
33
33
|
|
34
|
-
expect(worker.options).to eq(:
|
34
|
+
expect(worker.options).to eq(one: 1, two: 2, three: '...')
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -72,7 +72,7 @@ describe ImageOptim::Worker do
|
|
72
72
|
option(:three, 3, 'Three')
|
73
73
|
end)
|
74
74
|
|
75
|
-
worker = DefOptim.new(ImageOptim.new, :
|
75
|
+
worker = DefOptim.new(ImageOptim.new, three: '...')
|
76
76
|
|
77
77
|
expect(worker.inspect).to eq('#<DefOptim @one=1, @two=2, @three="...">')
|
78
78
|
end
|
@@ -104,17 +104,17 @@ describe ImageOptim::Worker do
|
|
104
104
|
|
105
105
|
it 'create hash by format' do
|
106
106
|
workers = [
|
107
|
-
double(:
|
108
|
-
double(:
|
109
|
-
double(:
|
107
|
+
double(image_formats: [:a]),
|
108
|
+
double(image_formats: [:a, :b]),
|
109
|
+
double(image_formats: [:b, :c]),
|
110
110
|
]
|
111
111
|
|
112
112
|
expect(Worker).to receive(:create_all).and_return(workers)
|
113
113
|
|
114
114
|
worker_by_format = {
|
115
|
-
:
|
116
|
-
:
|
117
|
-
:
|
115
|
+
a: [workers[0], workers[1]],
|
116
|
+
b: [workers[1], workers[2]],
|
117
|
+
c: [workers[2]],
|
118
118
|
}
|
119
119
|
|
120
120
|
expect(Worker.create_all_by_format(double)).to eq(worker_by_format)
|
@@ -123,21 +123,21 @@ describe ImageOptim::Worker do
|
|
123
123
|
|
124
124
|
describe '.create_all' do
|
125
125
|
def worker_double(override = {})
|
126
|
-
stubs = {
|
126
|
+
stubs = {resolve_used_bins!: nil, run_order: 0}.merge(override)
|
127
127
|
instance_double(Worker, stubs)
|
128
128
|
end
|
129
129
|
|
130
130
|
def worker_class_doubles(workers)
|
131
|
-
workers.map{ |worker| class_double(Worker, :
|
131
|
+
workers.map{ |worker| class_double(Worker, init: worker) }
|
132
132
|
end
|
133
133
|
|
134
|
-
let(:image_optim){ double(:
|
134
|
+
let(:image_optim){ double(allow_lossy: false) }
|
135
135
|
|
136
136
|
it 'creates all workers for which options_proc returns true' do
|
137
137
|
workers = Array.new(3){ worker_double }
|
138
138
|
klasses = worker_class_doubles(workers)
|
139
139
|
options_proc = proc do |klass|
|
140
|
-
klass == klasses[1] ? {:
|
140
|
+
klass == klasses[1] ? {disable: true} : {}
|
141
141
|
end
|
142
142
|
|
143
143
|
allow(Worker).to receive(:klasses).and_return(klasses)
|
@@ -206,7 +206,7 @@ describe ImageOptim::Worker do
|
|
206
206
|
it 'orders workers by run_order' do
|
207
207
|
run_orders = [10, -10, 0, 0, 0, 10, -10]
|
208
208
|
workers = run_orders.map do |run_order|
|
209
|
-
worker_double(:
|
209
|
+
worker_double(run_order: run_order)
|
210
210
|
end
|
211
211
|
klasses_list = worker_class_doubles(workers)
|
212
212
|
|
@@ -243,16 +243,16 @@ describe ImageOptim::Worker do
|
|
243
243
|
it 'allows overriding per worker' do
|
244
244
|
klasses = worker_class_doubles([worker_double, worker_double])
|
245
245
|
options_proc = proc do |klass|
|
246
|
-
klass == klasses[1] ? {:
|
246
|
+
klass == klasses[1] ? {allow_lossy: :b} : {}
|
247
247
|
end
|
248
248
|
|
249
249
|
allow(Worker).to receive(:klasses).and_return(klasses)
|
250
250
|
|
251
251
|
klasses.each{ |klass| klass.send(:attr_reader, :allow_lossy) }
|
252
252
|
expect(klasses[0]).to receive(:init).
|
253
|
-
with(image_optim, hash_including(:
|
253
|
+
with(image_optim, hash_including(allow_lossy: false))
|
254
254
|
expect(klasses[1]).to receive(:init).
|
255
|
-
with(image_optim, hash_including(:
|
255
|
+
with(image_optim, hash_including(allow_lossy: :b))
|
256
256
|
|
257
257
|
Worker.create_all(image_optim, &options_proc)
|
258
258
|
end
|
data/spec/image_optim_spec.rb
CHANGED
@@ -27,7 +27,7 @@ describe ImageOptim do
|
|
27
27
|
ImageOptim::Worker.klasses.map do |klass|
|
28
28
|
[klass.bin_sym, false]
|
29
29
|
end
|
30
|
-
].merge(:
|
30
|
+
].merge(skip_missing_workers: false)
|
31
31
|
|
32
32
|
ImageOptim::Worker.klasses.each do |worker_klass|
|
33
33
|
describe "#{worker_klass.bin_sym} worker" do
|
@@ -36,7 +36,7 @@ describe ImageOptim do
|
|
36
36
|
|
37
37
|
image_optim = ImageOptim.new(options)
|
38
38
|
if Array(worker_klass.init(image_optim)).empty?
|
39
|
-
image_optim = ImageOptim.new(options.merge(:
|
39
|
+
image_optim = ImageOptim.new(options.merge(allow_lossy: true))
|
40
40
|
end
|
41
41
|
|
42
42
|
expect(test_images.any? do |original|
|
@@ -59,10 +59,10 @@ describe ImageOptim do
|
|
59
59
|
rotated = images_dir / 'orient/original.jpg'
|
60
60
|
rotate_images = images_dir.glob('orient/?.jpg')
|
61
61
|
|
62
|
-
base_options = {:
|
62
|
+
base_options = {skip_missing_workers: false}
|
63
63
|
[
|
64
64
|
['lossless', base_options, 0],
|
65
|
-
['lossy', base_options.merge(:
|
65
|
+
['lossy', base_options.merge(allow_lossy: true), 0.001],
|
66
66
|
].each do |type, options, max_difference|
|
67
67
|
it "does it #{type}" do
|
68
68
|
image_optim = ImageOptim.new(options)
|
@@ -95,8 +95,8 @@ describe ImageOptim do
|
|
95
95
|
end
|
96
96
|
|
97
97
|
{
|
98
|
-
:
|
99
|
-
:
|
98
|
+
png: "\211PNG\r\n\032\n",
|
99
|
+
jpeg: "\377\330",
|
100
100
|
}.each do |type, data|
|
101
101
|
it "ingores broken #{type}" do
|
102
102
|
path = FSPath.temp_file_path
|
@@ -105,12 +105,49 @@ describe ImageOptim do
|
|
105
105
|
expect(ImageOptim.optimize_image(path)).to be_nil
|
106
106
|
end
|
107
107
|
end
|
108
|
+
|
109
|
+
context 'using timeout' do
|
110
|
+
let(:timeout){ 123 }
|
111
|
+
let(:image_optim){ ImageOptim.new(isolated_options_base.merge(timeout: timeout)) }
|
112
|
+
let(:timer){ instance_double(ImageOptim::Timer) }
|
113
|
+
let(:workers){ Array.new(3){ instance_double(ImageOptim::Worker) } }
|
114
|
+
let(:path){ test_images.first }
|
115
|
+
|
116
|
+
before do
|
117
|
+
allow(ImageOptim::Timer).to receive(:new).with(timeout).and_return(timer)
|
118
|
+
allow(image_optim).to receive(:workers_for_image).and_return(workers)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'sends timeout to every worker' do
|
122
|
+
some_path = instance_of(ImageOptim::Path)
|
123
|
+
|
124
|
+
expect(workers[0]).to receive(:optimize).with(some_path, some_path, timeout: timer)
|
125
|
+
expect(workers[1]).to receive(:optimize).with(some_path, some_path, timeout: timer)
|
126
|
+
expect(workers[2]).to receive(:optimize).with(some_path, some_path, timeout: timer)
|
127
|
+
|
128
|
+
image_optim.optimize_image(path)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'returns nil if there was no success before worker times out' do
|
132
|
+
allow(workers[0]).to receive(:optimize).and_return(false)
|
133
|
+
allow(workers[1]).to receive(:optimize).and_raise(ImageOptim::Errors::TimeoutExceeded)
|
134
|
+
|
135
|
+
expect(image_optim.optimize_image(path)).to be_nil
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'returns result if there was success before worker times out' do
|
139
|
+
allow(workers[0]).to receive(:optimize).and_return(true)
|
140
|
+
allow(workers[1]).to receive(:optimize).and_raise(ImageOptim::Errors::TimeoutExceeded)
|
141
|
+
|
142
|
+
expect(image_optim.optimize_image(path)).to be_an(ImageOptim::OptimizedPath)
|
143
|
+
end
|
144
|
+
end
|
108
145
|
end
|
109
146
|
|
110
147
|
describe '#optimize_image!' do
|
111
148
|
it 'optimizes image and replaces original' do
|
112
149
|
original = double
|
113
|
-
optimized = double(:
|
150
|
+
optimized = double(original_size: 12_345)
|
114
151
|
optimized_wrap = double
|
115
152
|
image_optim = ImageOptim.new
|
116
153
|
|
@@ -144,7 +181,7 @@ describe ImageOptim do
|
|
144
181
|
describe '#optimize_image_data' do
|
145
182
|
it 'create temp file, optimizes image and returns data' do
|
146
183
|
data = double
|
147
|
-
temp = double(:
|
184
|
+
temp = double(path: double)
|
148
185
|
optimized = double
|
149
186
|
optimized_data = double
|
150
187
|
image_optim = ImageOptim.new
|
@@ -165,7 +202,7 @@ describe ImageOptim do
|
|
165
202
|
|
166
203
|
it 'returns nil if optimization fails' do
|
167
204
|
data = double
|
168
|
-
temp = double(:
|
205
|
+
temp = double(path: double)
|
169
206
|
image_optim = ImageOptim.new
|
170
207
|
|
171
208
|
allow(ImageOptim::ImageMeta).to receive(:format_for_data).
|
@@ -216,7 +253,7 @@ describe ImageOptim do
|
|
216
253
|
|
217
254
|
describe 'given block' do
|
218
255
|
it 'optimizes images, yields path and result for each and '\
|
219
|
-
|
256
|
+
'returns array of yield results' do
|
220
257
|
image_optim = ImageOptim.new
|
221
258
|
results = test_images.map do |src|
|
222
259
|
dst = double
|
@@ -17,7 +17,7 @@ IO.popen(%W[
|
|
17
17
|
].shelljoin, 'w') do |f|
|
18
18
|
side.times do |a|
|
19
19
|
side.times do |b|
|
20
|
-
alpha = [0, 1, 0x7f, 0xff][(a / 8 + b / 8) % 4]
|
20
|
+
alpha = [0, 1, 0x7f, 0xff][((a / 8) + (b / 8)) % 4]
|
21
21
|
f << [rand(256), rand(256), rand(256), alpha].pack('C*')
|
22
22
|
end
|
23
23
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
if ENV['
|
3
|
+
if ENV['CC_TEST_REPORTER_ID']
|
4
4
|
require 'simplecov'
|
5
5
|
SimpleCov.start
|
6
6
|
end
|
@@ -23,16 +23,18 @@ RSpec.configure do |c|
|
|
23
23
|
c.order = :random
|
24
24
|
end
|
25
25
|
|
26
|
+
IMAGEMAGICK_PREFIX = ImageOptim::Cmd.capture('which magick').empty? ? [] : %w[magick]
|
27
|
+
|
26
28
|
def flatten_animation(image)
|
27
29
|
if image.image_format == :gif
|
28
30
|
flattened = image.temp_path
|
29
|
-
command = %W[
|
31
|
+
command = (IMAGEMAGICK_PREFIX + %W[
|
30
32
|
convert
|
31
33
|
#{image}
|
32
34
|
-coalesce
|
33
35
|
-append
|
34
36
|
#{flattened}
|
35
|
-
].shelljoin
|
37
|
+
]).shelljoin
|
36
38
|
expect(ImageOptim::Cmd.run(command)).to be_truthy
|
37
39
|
flattened
|
38
40
|
else
|
@@ -43,7 +45,7 @@ end
|
|
43
45
|
def mepp(image_a, image_b)
|
44
46
|
coalesce_a = flatten_animation(image_a)
|
45
47
|
coalesce_b = flatten_animation(image_b)
|
46
|
-
output = ImageOptim::Cmd.capture(%W[
|
48
|
+
output = ImageOptim::Cmd.capture((IMAGEMAGICK_PREFIX + %W[
|
47
49
|
compare
|
48
50
|
-metric MEPP
|
49
51
|
-alpha Background
|
@@ -51,7 +53,7 @@ def mepp(image_a, image_b)
|
|
51
53
|
#{coalesce_b.to_s.shellescape}
|
52
54
|
#{ImageOptim::Path::NULL}
|
53
55
|
2>&1
|
54
|
-
].join(' '))
|
56
|
+
]).join(' '))
|
55
57
|
unless [0, 1].include?($CHILD_STATUS.exitstatus)
|
56
58
|
fail "compare #{image_a} with #{image_b} failed with `#{output}`"
|
57
59
|
end
|
@@ -71,26 +73,27 @@ RSpec::Matchers.define :be_similar_to do |expected, max_difference|
|
|
71
73
|
end
|
72
74
|
failure_message do |actual|
|
73
75
|
"expected #{actual} to have at most #{max_difference} difference from "\
|
74
|
-
|
76
|
+
"#{expected}, got mean error per pixel of #{@diff}"
|
75
77
|
end
|
76
78
|
end
|
77
79
|
|
78
|
-
|
79
|
-
|
80
|
-
|
80
|
+
SkipConditions = Hash.new do |cache, name|
|
81
|
+
cache[name] = case name
|
82
|
+
when :any_file_mode_allowed
|
83
|
+
Tempfile.open('posix') do |f|
|
81
84
|
File.chmod(0, f.path)
|
82
|
-
|
85
|
+
'full file modes are not support' unless (File.stat(f.path).mode & 0o777).zero?
|
83
86
|
end
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
87
|
+
when :inodes_support
|
88
|
+
'inodes are not supported' if File.stat(__FILE__).ino.zero?
|
89
|
+
when :signals_support
|
90
|
+
begin
|
91
|
+
Process.kill(0, 0)
|
92
|
+
nil
|
93
|
+
rescue
|
94
|
+
'signals are not supported'
|
95
|
+
end
|
96
|
+
else
|
97
|
+
fail "Unknown check #{name}"
|
95
98
|
end
|
96
99
|
end
|