image_optim 0.28.0 → 0.31.1
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 +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
|