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.
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