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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/check.yml +89 -0
  3. data/.pre-commit-hooks.yaml +9 -0
  4. data/.rubocop.yml +6 -3
  5. data/CHANGELOG.markdown +20 -0
  6. data/CONTRIBUTING.markdown +1 -1
  7. data/Gemfile +1 -7
  8. data/LICENSE.txt +1 -1
  9. data/README.markdown +18 -9
  10. data/Vagrantfile +1 -1
  11. data/image_optim.gemspec +6 -3
  12. data/lib/image_optim/bin_resolver/bin.rb +9 -9
  13. data/lib/image_optim/cache.rb +6 -0
  14. data/lib/image_optim/cmd.rb +45 -6
  15. data/lib/image_optim/config.rb +11 -5
  16. data/lib/image_optim/elapsed_time.rb +26 -0
  17. data/lib/image_optim/errors.rb +9 -0
  18. data/lib/image_optim/path.rb +1 -1
  19. data/lib/image_optim/runner/option_parser.rb +21 -17
  20. data/lib/image_optim/runner.rb +1 -1
  21. data/lib/image_optim/timer.rb +25 -0
  22. data/lib/image_optim/worker/advpng.rb +7 -7
  23. data/lib/image_optim/worker/gifsicle.rb +11 -11
  24. data/lib/image_optim/worker/jhead.rb +2 -2
  25. data/lib/image_optim/worker/jpegoptim.rb +11 -11
  26. data/lib/image_optim/worker/jpegrecompress.rb +6 -6
  27. data/lib/image_optim/worker/jpegtran.rb +4 -4
  28. data/lib/image_optim/worker/optipng.rb +7 -7
  29. data/lib/image_optim/worker/oxipng.rb +53 -0
  30. data/lib/image_optim/worker/pngcrush.rb +6 -6
  31. data/lib/image_optim/worker/pngout.rb +7 -7
  32. data/lib/image_optim/worker/pngquant.rb +9 -9
  33. data/lib/image_optim/worker/svgo.rb +2 -2
  34. data/lib/image_optim/worker.rb +32 -29
  35. data/lib/image_optim.rb +16 -10
  36. data/script/update_worker_options_in_readme +1 -1
  37. data/script/worker_analysis +16 -18
  38. data/spec/image_optim/bin_resolver_spec.rb +5 -5
  39. data/spec/image_optim/cache_path_spec.rb +7 -10
  40. data/spec/image_optim/cache_spec.rb +7 -7
  41. data/spec/image_optim/cmd_spec.rb +64 -6
  42. data/spec/image_optim/config_spec.rb +36 -20
  43. data/spec/image_optim/elapsed_time_spec.rb +14 -0
  44. data/spec/image_optim/handler_spec.rb +1 -1
  45. data/spec/image_optim/hash_helpers_spec.rb +18 -18
  46. data/spec/image_optim/option_definition_spec.rb +6 -6
  47. data/spec/image_optim/path_spec.rb +8 -11
  48. data/spec/image_optim/runner/option_parser_spec.rb +4 -4
  49. data/spec/image_optim/timer_spec.rb +32 -0
  50. data/spec/image_optim/worker/jpegrecompress_spec.rb +2 -2
  51. data/spec/image_optim/worker/optipng_spec.rb +11 -11
  52. data/spec/image_optim/worker/oxipng_spec.rb +89 -0
  53. data/spec/image_optim/worker/pngquant_spec.rb +5 -5
  54. data/spec/image_optim/worker_spec.rb +17 -17
  55. data/spec/image_optim_spec.rb +47 -10
  56. data/spec/images/invisiblepixels/generate +1 -1
  57. data/spec/spec_helper.rb +24 -21
  58. metadata +35 -11
  59. data/.appveyor.yml +0 -53
  60. 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){ {:method => 'invalid'} }
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){ {:method => 'smallfry'} }
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, :copy => nil) }
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){ {:strip => false} }
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){ {:size => 10} }
65
- let(:dst_options){ {:size? => 9} }
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){ {:interlace => true} }
72
+ let(:instance_options){ {interlace: true} }
73
73
 
74
74
  context 'when dst is empty' do
75
- let(:dst_options){ {:size? => nil} }
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){ {:size? => 20} }
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){ {:interlace => false} }
86
+ let(:instance_options){ {interlace: false} }
87
87
 
88
88
  context 'when dst is empty' do
89
- let(:dst_options){ {:size? => nil} }
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){ {:size? => 10} }
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){ {:size? => 9} }
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){ {:quality => 10..90} }
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){ {:allow_lossy => true} }
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){ {:allow_lossy => true, :quality => 10..90} }
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){ {:allow_lossy => true, :quality => -50..50} }
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){ {:allow_lossy => true, :quality => 50..150} }
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, :three => '...')
32
+ worker = worker_class.new(ImageOptim.new, three: '...')
33
33
 
34
- expect(worker.options).to eq(:one => 1, :two => 2, :three => '...')
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, :three => '...')
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(:image_formats => [:a]),
108
- double(:image_formats => [:a, :b]),
109
- double(:image_formats => [:b, :c]),
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
- :a => [workers[0], workers[1]],
116
- :b => [workers[1], workers[2]],
117
- :c => [workers[2]],
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 = {:resolve_used_bins! => nil, :run_order => 0}.merge(override)
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, :init => worker) }
131
+ workers.map{ |worker| class_double(Worker, init: worker) }
132
132
  end
133
133
 
134
- let(:image_optim){ double(:allow_lossy => false) }
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] ? {:disable => true} : {}
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(:run_order => run_order)
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] ? {:allow_lossy => :b} : {}
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(:allow_lossy => false))
253
+ with(image_optim, hash_including(allow_lossy: false))
254
254
  expect(klasses[1]).to receive(:init).
255
- with(image_optim, hash_including(:allow_lossy => :b))
255
+ with(image_optim, hash_including(allow_lossy: :b))
256
256
 
257
257
  Worker.create_all(image_optim, &options_proc)
258
258
  end
@@ -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(:skip_missing_workers => false)
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(:allow_lossy => true))
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 = {:skip_missing_workers => false}
62
+ base_options = {skip_missing_workers: false}
63
63
  [
64
64
  ['lossless', base_options, 0],
65
- ['lossy', base_options.merge(:allow_lossy => true), 0.001],
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
- :png => "\211PNG\r\n\032\n",
99
- :jpeg => "\377\330",
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(:original_size => 12_345)
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(:path => 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(:path => 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
- 'returns array of yield results' do
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['CODECLIMATE']
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
- "#{expected}, got normalized root-mean-square error of #{@diff}"
76
+ "#{expected}, got mean error per pixel of #{@diff}"
75
77
  end
76
78
  end
77
79
 
78
- module CapabilityCheckHelpers
79
- def any_file_modes_allowed?
80
- Tempfile.open 'posix' do |f|
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
- return (File.stat(f.path).mode & 0o777).zero?
85
+ 'full file modes are not support' unless (File.stat(f.path).mode & 0o777).zero?
83
86
  end
84
- end
85
-
86
- def inodes_supported?
87
- !File.stat(__FILE__).ino.zero?
88
- end
89
-
90
- def signals_supported?
91
- Process.kill(0, 0)
92
- true
93
- rescue
94
- false
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