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
@@ -29,7 +29,7 @@ end
|
|
29
29
|
def write_marked(io)
|
30
30
|
io.puts BEGIN_MARKER
|
31
31
|
io.puts '<!-- markdown for worker options is generated by '\
|
32
|
-
|
32
|
+
"`#{Pathname($PROGRAM_NAME).cleanpath}` -->"
|
33
33
|
io.puts
|
34
34
|
|
35
35
|
ImageOptim::Worker.klasses.sort_by(&:name).each do |klass|
|
data/script/worker_analysis
CHANGED
@@ -358,20 +358,18 @@ class Analyser
|
|
358
358
|
end
|
359
359
|
|
360
360
|
def flatten_animation(image)
|
361
|
-
run_cache[:flatten][image.digest] ||=
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
image
|
374
|
-
end
|
361
|
+
run_cache[:flatten][image.digest] ||= if image.image_format == :gif
|
362
|
+
flattened = image.temp_path
|
363
|
+
Cmd.run(*%W[
|
364
|
+
convert
|
365
|
+
#{image.image_format}:#{image}
|
366
|
+
-coalesce
|
367
|
+
-append
|
368
|
+
#{image.image_format}:#{flattened}
|
369
|
+
]) || fail("failed flattening of #{image}")
|
370
|
+
flattened
|
371
|
+
else
|
372
|
+
image
|
375
373
|
end
|
376
374
|
end
|
377
375
|
|
@@ -527,10 +525,10 @@ class Analyser
|
|
527
525
|
stats = Stats.new('all', by_format[format], worker_ids2names)
|
528
526
|
path = FSPath("#{DIR}/#{basenames[format]}")
|
529
527
|
model = {
|
530
|
-
:
|
531
|
-
:
|
532
|
-
:
|
533
|
-
:
|
528
|
+
stats_format: format,
|
529
|
+
stats: stats,
|
530
|
+
format_links: basenames,
|
531
|
+
template_dir: template_path.dirname.relative_path_from(path.dirname),
|
534
532
|
}
|
535
533
|
html = template.result(OpenStruct.new(model).instance_eval{ binding })
|
536
534
|
path.write(html)
|
@@ -19,7 +19,7 @@ describe ImageOptim::BinResolver do
|
|
19
19
|
allow(ENV).to receive(:[]).and_call_original
|
20
20
|
end
|
21
21
|
|
22
|
-
let(:image_optim){ double(:image_optim, :
|
22
|
+
let(:image_optim){ double(:image_optim, verbose: false, pack: false) }
|
23
23
|
let(:resolver){ BinResolver.new(image_optim) }
|
24
24
|
|
25
25
|
describe '#full_path' do
|
@@ -130,7 +130,7 @@ describe ImageOptim::BinResolver do
|
|
130
130
|
it 'resolves bin specified in ENV' do
|
131
131
|
path = 'bin/the_optimizer'
|
132
132
|
stub_env 'THE_OPTIMIZER_BIN', path
|
133
|
-
tmpdir = double(:tmpdir, :
|
133
|
+
tmpdir = double(:tmpdir, to_str: 'tmpdir')
|
134
134
|
symlink = double(:symlink)
|
135
135
|
|
136
136
|
full_path = File.expand_path(path)
|
@@ -180,9 +180,9 @@ describe ImageOptim::BinResolver do
|
|
180
180
|
stub_env 'THE_OPTIMIZER_BIN', path
|
181
181
|
expect(FSPath).not_to receive(:temp_dir)
|
182
182
|
expect(resolver).not_to receive(:at_exit)
|
183
|
-
allow(File).to receive_messages(
|
184
|
-
|
185
|
-
|
183
|
+
allow(File).to receive_messages(exist?: exist?,
|
184
|
+
file?: file?,
|
185
|
+
executable?: executable?)
|
186
186
|
end
|
187
187
|
|
188
188
|
after do
|
@@ -5,8 +5,6 @@ require 'image_optim/cache_path'
|
|
5
5
|
require 'tempfile'
|
6
6
|
|
7
7
|
describe ImageOptim::CachePath do
|
8
|
-
include CapabilityCheckHelpers
|
9
|
-
|
10
8
|
before do
|
11
9
|
stub_const('Path', ImageOptim::Path)
|
12
10
|
stub_const('CachePath', ImageOptim::CachePath)
|
@@ -32,8 +30,7 @@ describe ImageOptim::CachePath do
|
|
32
30
|
expect(src).to exist
|
33
31
|
end
|
34
32
|
|
35
|
-
it 'preserves attributes of destination file' do
|
36
|
-
skip 'full file modes are not support' unless any_file_modes_allowed?
|
33
|
+
it 'preserves attributes of destination file', skip: SkipConditions[:any_file_mode_allowed] do
|
37
34
|
mode = 0o666
|
38
35
|
|
39
36
|
dst.chmod(mode)
|
@@ -45,22 +42,22 @@ describe ImageOptim::CachePath do
|
|
45
42
|
end
|
46
43
|
|
47
44
|
it 'does not preserve mtime of destination file' do
|
48
|
-
time = src.mtime
|
45
|
+
time = src.mtime - 1000
|
46
|
+
dst.utime(time, time)
|
49
47
|
|
50
|
-
|
48
|
+
time = dst.mtime
|
51
49
|
|
52
50
|
src.replace(dst)
|
53
51
|
|
54
|
-
expect(dst.mtime).
|
52
|
+
expect(dst.mtime).to_not eq(time)
|
55
53
|
end
|
56
54
|
|
57
|
-
it 'changes inode of destination' do
|
58
|
-
skip 'inodes are not supported' unless inodes_supported?
|
55
|
+
it 'changes inode of destination', skip: SkipConditions[:inodes_support] do
|
59
56
|
expect{ src.replace(dst) }.to change{ dst.stat.ino }
|
60
57
|
end
|
61
58
|
|
62
59
|
it 'is using temporary file with .tmp extension' do
|
63
|
-
expect(src).to receive(:copy).with(having_attributes(:
|
60
|
+
expect(src).to receive(:copy).with(having_attributes(extname: '.tmp')).at_least(:once)
|
64
61
|
|
65
62
|
src.replace(dst)
|
66
63
|
end
|
@@ -11,7 +11,7 @@ describe ImageOptim::Cache do
|
|
11
11
|
stub_const('CachePath', ImageOptim::CachePath)
|
12
12
|
end
|
13
13
|
|
14
|
-
let(:tmp_file){ double('/somewhere/tmp/foo/bar', :
|
14
|
+
let(:tmp_file){ double('/somewhere/tmp/foo/bar', rename: 0) }
|
15
15
|
|
16
16
|
let(:cache_dir) do
|
17
17
|
dir = '/somewhere/cache'
|
@@ -24,7 +24,7 @@ describe ImageOptim::Cache do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
let(:original) do
|
27
|
-
original = double('/somewhere/original', :
|
27
|
+
original = double('/somewhere/original', image_format: :ext)
|
28
28
|
allow(Digest::SHA1).to receive(:file).with(original) do
|
29
29
|
Digest::SHA1.new << 'some content!'
|
30
30
|
end
|
@@ -32,7 +32,7 @@ describe ImageOptim::Cache do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
let(:optimized) do
|
35
|
-
double('/somewhere/optimized', :
|
35
|
+
double('/somewhere/optimized', format: :ext, basename: 'optimized')
|
36
36
|
end
|
37
37
|
|
38
38
|
let(:cached) do
|
@@ -45,7 +45,7 @@ describe ImageOptim::Cache do
|
|
45
45
|
|
46
46
|
context 'when cache is disabled (default)' do
|
47
47
|
let(:image_optim) do
|
48
|
-
double(:image_optim, :
|
48
|
+
double(:image_optim, cache_dir: nil, cache_worker_digests: false, timeout: nil)
|
49
49
|
end
|
50
50
|
let(:cache){ Cache.new(image_optim, double) }
|
51
51
|
|
@@ -122,7 +122,7 @@ describe ImageOptim::Cache do
|
|
122
122
|
context 'when cache is enabled (without worker digests)' do
|
123
123
|
let(:image_optim) do
|
124
124
|
double(:image_optim,
|
125
|
-
:cache_dir
|
125
|
+
cache_dir: cache_dir, cache_worker_digests: false, timeout: nil)
|
126
126
|
end
|
127
127
|
let(:cache) do
|
128
128
|
cache = Cache.new(image_optim, {})
|
@@ -144,8 +144,8 @@ describe ImageOptim::Cache do
|
|
144
144
|
context 'when cache is enabled (with worker digests)' do
|
145
145
|
let(:image_optim) do
|
146
146
|
double(:image_optim,
|
147
|
-
:
|
148
|
-
:
|
147
|
+
cache_dir: cache_dir,
|
148
|
+
cache_worker_digests: true, timeout: nil)
|
149
149
|
end
|
150
150
|
let(:cache) do
|
151
151
|
cache = Cache.new(image_optim, {})
|
@@ -2,10 +2,9 @@
|
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
require 'image_optim/cmd'
|
5
|
+
require 'image_optim/timer'
|
5
6
|
|
6
7
|
describe ImageOptim::Cmd do
|
7
|
-
include CapabilityCheckHelpers
|
8
|
-
|
9
8
|
before do
|
10
9
|
stub_const('Cmd', ImageOptim::Cmd)
|
11
10
|
end
|
@@ -35,12 +34,72 @@ describe ImageOptim::Cmd do
|
|
35
34
|
expect($CHILD_STATUS.exitstatus).to eq(66)
|
36
35
|
end
|
37
36
|
|
38
|
-
it 'raises SignalException if process terminates after signal' do
|
39
|
-
skip 'signals are not supported' unless signals_supported?
|
37
|
+
it 'raises SignalException if process terminates after signal', skip: SkipConditions[:signals_support] do
|
40
38
|
expect_int_exception do
|
41
39
|
Cmd.run('kill -s INT $$')
|
42
40
|
end
|
43
41
|
end
|
42
|
+
|
43
|
+
context 'with timeout' do
|
44
|
+
it 'returns process success status' do
|
45
|
+
expect(Cmd.run('sh -c "exit 0"', timeout: 1)).to eq(true)
|
46
|
+
|
47
|
+
expect(Cmd.run('sh -c "exit 1"', timeout: 1)).to eq(false)
|
48
|
+
|
49
|
+
expect(Cmd.run('sh -c "exit 66"', timeout: 1)).to eq(false)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns process success status when timeout is instance of ImageOptim::Timer' do
|
53
|
+
timeout = ImageOptim::Timer.new(1.0)
|
54
|
+
expect(Cmd.run('sh -c "exit 0"', timeout: timeout)).to eq(true)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'raises SignalException if process terminates after signal', skip: SkipConditions[:signals_support] do
|
58
|
+
expect_int_exception do
|
59
|
+
Cmd.run('kill -s INT $$', timeout: 1)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'raises TimeoutExceeded if process does not exit until timeout' do
|
64
|
+
expect do
|
65
|
+
Cmd.run('sleep 10', timeout: 0)
|
66
|
+
end.to raise_error(ImageOptim::Errors::TimeoutExceeded)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'does not leave zombie threads' do
|
70
|
+
expect do
|
71
|
+
begin
|
72
|
+
Cmd.run('sleep 10', timeout: 0)
|
73
|
+
rescue ImageOptim::Errors::TimeoutExceeded
|
74
|
+
# noop
|
75
|
+
end
|
76
|
+
end.not_to change{ Thread.list }
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'receives TERM', skip: SkipConditions[:signals_support] do
|
80
|
+
waiter = double
|
81
|
+
allow(Process).to receive(:detach).once{ |pid| @pid = pid; waiter }
|
82
|
+
allow(waiter).to receive(:join){ sleep 0.1; nil }
|
83
|
+
|
84
|
+
expect do
|
85
|
+
Cmd.run('sleep 5', timeout: 0.1)
|
86
|
+
end.to raise_error(ImageOptim::Errors::TimeoutExceeded)
|
87
|
+
|
88
|
+
expect(Process.wait2(@pid).last.termsig).to eq(Signal.list['TERM'])
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'receives KILL if it does not react on TERM', skip: SkipConditions[:signals_support] do
|
92
|
+
waiter = double
|
93
|
+
allow(Process).to receive(:detach).once{ |pid| @pid = pid; waiter }
|
94
|
+
allow(waiter).to receive(:join){ sleep 0.1; nil }
|
95
|
+
|
96
|
+
expect do
|
97
|
+
Cmd.run('trap "" TERM; sleep 5', timeout: 0.1)
|
98
|
+
end.to raise_error(ImageOptim::Errors::TimeoutExceeded)
|
99
|
+
|
100
|
+
expect(Process.wait2(@pid).last.termsig).to eq(Signal.list['KILL'])
|
101
|
+
end
|
102
|
+
end
|
44
103
|
end
|
45
104
|
|
46
105
|
describe '.capture' do
|
@@ -62,8 +121,7 @@ describe ImageOptim::Cmd do
|
|
62
121
|
expect($CHILD_STATUS.exitstatus).to eq(66)
|
63
122
|
end
|
64
123
|
|
65
|
-
it 'raises SignalException if process terminates after signal' do
|
66
|
-
skip 'signals are not supported' unless signals_supported?
|
124
|
+
it 'raises SignalException if process terminates after signal', skip: SkipConditions[:signals_support] do
|
67
125
|
expect_int_exception do
|
68
126
|
Cmd.capture('kill -s INT $$')
|
69
127
|
end
|
@@ -19,7 +19,7 @@ describe ImageOptim::Config do
|
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'raises when there are unused options' do
|
22
|
-
config = IOConfig.new(:
|
22
|
+
config = IOConfig.new(unused: true)
|
23
23
|
expect do
|
24
24
|
config.assert_no_unused_options!
|
25
25
|
end.to raise_error(ImageOptim::ConfigurationError)
|
@@ -37,12 +37,12 @@ describe ImageOptim::Config do
|
|
37
37
|
end
|
38
38
|
|
39
39
|
it 'is 0 if disabled' do
|
40
|
-
config = IOConfig.new(:
|
40
|
+
config = IOConfig.new(nice: false)
|
41
41
|
expect(config.nice).to eq(0)
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'converts value to number' do
|
45
|
-
config = IOConfig.new(:
|
45
|
+
config = IOConfig.new(nice: '13')
|
46
46
|
expect(config.nice).to eq(13)
|
47
47
|
end
|
48
48
|
end
|
@@ -59,16 +59,32 @@ describe ImageOptim::Config do
|
|
59
59
|
end
|
60
60
|
|
61
61
|
it 'is 1 if disabled' do
|
62
|
-
config = IOConfig.new(:
|
62
|
+
config = IOConfig.new(threads: false)
|
63
63
|
expect(config.threads).to eq(1)
|
64
64
|
end
|
65
65
|
|
66
66
|
it 'converts value to number' do
|
67
|
-
config = IOConfig.new(:
|
67
|
+
config = IOConfig.new(threads: '616')
|
68
68
|
expect(config.threads).to eq(616)
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
+
describe '#timeout' do
|
73
|
+
before do
|
74
|
+
allow(IOConfig).to receive(:read_options).and_return({})
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'is nil by default' do
|
78
|
+
config = IOConfig.new({})
|
79
|
+
expect(config.timeout).to eq(nil)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'converts value to a float' do
|
83
|
+
config = IOConfig.new(timeout: '15.1')
|
84
|
+
expect(config.timeout).to eq(15.1)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
72
88
|
describe '#cache_dir' do
|
73
89
|
before do
|
74
90
|
allow(IOConfig).to receive(:read_options).and_return({})
|
@@ -80,7 +96,7 @@ describe ImageOptim::Config do
|
|
80
96
|
end
|
81
97
|
|
82
98
|
it 'is nil if set to the empty string' do
|
83
|
-
config = IOConfig.new(:
|
99
|
+
config = IOConfig.new(cache_dir: '')
|
84
100
|
expect(config.cache_dir).to be nil
|
85
101
|
end
|
86
102
|
end
|
@@ -112,17 +128,17 @@ describe ImageOptim::Config do
|
|
112
128
|
end
|
113
129
|
|
114
130
|
it 'returns passed hash' do
|
115
|
-
config = IOConfig.new(:
|
116
|
-
expect(config.for_worker(Abc)).to eq(:
|
131
|
+
config = IOConfig.new(abc: {option: true})
|
132
|
+
expect(config.for_worker(Abc)).to eq(option: true)
|
117
133
|
end
|
118
134
|
|
119
135
|
it 'returns {:disable => true} for false' do
|
120
|
-
config = IOConfig.new(:
|
121
|
-
expect(config.for_worker(Abc)).to eq(:
|
136
|
+
config = IOConfig.new(abc: false)
|
137
|
+
expect(config.for_worker(Abc)).to eq(disable: true)
|
122
138
|
end
|
123
139
|
|
124
140
|
it 'raises on unknown option' do
|
125
|
-
config = IOConfig.new(:
|
141
|
+
config = IOConfig.new(abc: 13)
|
126
142
|
expect do
|
127
143
|
config.for_worker(Abc)
|
128
144
|
end.to raise_error(ImageOptim::ConfigurationError)
|
@@ -132,11 +148,11 @@ describe ImageOptim::Config do
|
|
132
148
|
describe '#initialize' do
|
133
149
|
it 'reads options from default locations' do
|
134
150
|
expect(IOConfig).to receive(:read_options).
|
135
|
-
with(IOConfig::GLOBAL_PATH).and_return(:
|
151
|
+
with(IOConfig::GLOBAL_PATH).and_return(a: 1, b: 2, c: 3)
|
136
152
|
expect(IOConfig).to receive(:read_options).
|
137
|
-
with(IOConfig::LOCAL_PATH).and_return(:
|
153
|
+
with(IOConfig::LOCAL_PATH).and_return(a: 10, b: 20)
|
138
154
|
|
139
|
-
config = IOConfig.new(:
|
155
|
+
config = IOConfig.new(a: 100)
|
140
156
|
expect(config.get!(:a)).to eq(100)
|
141
157
|
expect(config.get!(:b)).to eq(20)
|
142
158
|
expect(config.get!(:c)).to eq(3)
|
@@ -146,17 +162,17 @@ describe ImageOptim::Config do
|
|
146
162
|
it 'does not read options with empty config_paths' do
|
147
163
|
expect(IOConfig).not_to receive(:read_options)
|
148
164
|
|
149
|
-
config = IOConfig.new(:
|
165
|
+
config = IOConfig.new(config_paths: [])
|
150
166
|
config.assert_no_unused_options!
|
151
167
|
end
|
152
168
|
|
153
169
|
it 'reads options from specified paths' do
|
154
170
|
expect(IOConfig).to receive(:read_options).
|
155
|
-
with('/etc/image_optim.yml').and_return(:
|
171
|
+
with('/etc/image_optim.yml').and_return(a: 1, b: 2, c: 3)
|
156
172
|
expect(IOConfig).to receive(:read_options).
|
157
|
-
with('config/image_optim.yml').and_return(:
|
173
|
+
with('config/image_optim.yml').and_return(a: 10, b: 20)
|
158
174
|
|
159
|
-
config = IOConfig.new(:
|
175
|
+
config = IOConfig.new(a: 100, config_paths: %w[
|
160
176
|
/etc/image_optim.yml
|
161
177
|
config/image_optim.yml
|
162
178
|
])
|
@@ -170,7 +186,7 @@ describe ImageOptim::Config do
|
|
170
186
|
expect(IOConfig).to receive(:read_options).
|
171
187
|
with('config/image_optim.yml').and_return({})
|
172
188
|
|
173
|
-
config = IOConfig.new(:
|
189
|
+
config = IOConfig.new(config_paths: 'config/image_optim.yml')
|
174
190
|
config.assert_no_unused_options!
|
175
191
|
end
|
176
192
|
end
|
@@ -200,7 +216,7 @@ describe ImageOptim::Config do
|
|
200
216
|
|
201
217
|
it 'returns hash with deep symbolised keys from reader' do
|
202
218
|
stringified = {'config' => {'this' => true}}
|
203
|
-
symbolized = {:
|
219
|
+
symbolized = {config: {this: true}}
|
204
220
|
|
205
221
|
expect(IOConfig).not_to receive(:warn)
|
206
222
|
expect(File).to receive(:expand_path).
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'image_optim/elapsed_time'
|
5
|
+
|
6
|
+
describe ImageOptim::ElapsedTime do
|
7
|
+
let(:timeout){ 0.01 }
|
8
|
+
|
9
|
+
describe '.now' do
|
10
|
+
it 'returns incrementing value' do
|
11
|
+
expect{ sleep timeout }.to change{ described_class.now }.by_at_least(timeout)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -9,7 +9,7 @@ describe ImageOptim::Handler do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'uses original as source for first conversion '\
|
12
|
-
|
12
|
+
'and two temp files for further conversions' do
|
13
13
|
original = double(:original)
|
14
14
|
allow(original).to receive(:respond_to?).with(:temp_path).and_return(true)
|
15
15
|
|
@@ -10,10 +10,10 @@ describe ImageOptim::HashHelpers do
|
|
10
10
|
|
11
11
|
context 'stringify/simbolyze' do
|
12
12
|
symbol_keys = {
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
13
|
+
a: 1,
|
14
|
+
b: {
|
15
|
+
c: [:a, 'a'],
|
16
|
+
d: {},
|
17
17
|
},
|
18
18
|
}
|
19
19
|
|
@@ -38,22 +38,22 @@ describe ImageOptim::HashHelpers do
|
|
38
38
|
|
39
39
|
it 'deep merges hashes' do
|
40
40
|
merge_a = {
|
41
|
-
:
|
42
|
-
:
|
43
|
-
:
|
44
|
-
:
|
45
|
-
:
|
41
|
+
a: {
|
42
|
+
b: 1,
|
43
|
+
c: {
|
44
|
+
d: 2,
|
45
|
+
e: {f: true},
|
46
46
|
},
|
47
47
|
},
|
48
|
-
:
|
48
|
+
y: 10,
|
49
49
|
}
|
50
50
|
|
51
51
|
merge_b = {
|
52
52
|
:a => {
|
53
|
-
:
|
54
|
-
:
|
55
|
-
:
|
56
|
-
:
|
53
|
+
b: 2,
|
54
|
+
c: {
|
55
|
+
d: 3,
|
56
|
+
e: false,
|
57
57
|
},
|
58
58
|
},
|
59
59
|
'z' => 20,
|
@@ -61,10 +61,10 @@ describe ImageOptim::HashHelpers do
|
|
61
61
|
|
62
62
|
merge_result = {
|
63
63
|
:a => {
|
64
|
-
:
|
65
|
-
:
|
66
|
-
:
|
67
|
-
:
|
64
|
+
b: 2,
|
65
|
+
c: {
|
66
|
+
d: 3,
|
67
|
+
e: false,
|
68
68
|
},
|
69
69
|
},
|
70
70
|
:y => 10,
|
@@ -58,13 +58,13 @@ describe ImageOptim::OptionDefinition do
|
|
58
58
|
|
59
59
|
context 'when option is nil' do
|
60
60
|
it 'returns nil' do
|
61
|
-
expect(subject.value(nil, :
|
61
|
+
expect(subject.value(nil, abc: nil)).to eq(nil)
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
65
|
context 'when option is set' do
|
66
66
|
it 'returns value' do
|
67
|
-
expect(subject.value(nil, :
|
67
|
+
expect(subject.value(nil, abc: 123)).to eq(123)
|
68
68
|
end
|
69
69
|
end
|
70
70
|
end
|
@@ -84,13 +84,13 @@ describe ImageOptim::OptionDefinition do
|
|
84
84
|
|
85
85
|
context 'when option is nil' do
|
86
86
|
it 'returns nil passed through proc' do
|
87
|
-
expect(subject.value(nil, :
|
87
|
+
expect(subject.value(nil, abc: nil)).to eq('nil')
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
91
|
context 'when option is set' do
|
92
92
|
it 'returns value passed through proc' do
|
93
|
-
expect(subject.value(nil, :
|
93
|
+
expect(subject.value(nil, abc: 123)).to eq('123')
|
94
94
|
end
|
95
95
|
end
|
96
96
|
end
|
@@ -108,13 +108,13 @@ describe ImageOptim::OptionDefinition do
|
|
108
108
|
|
109
109
|
context 'when option is nil' do
|
110
110
|
it 'returns nil passed through proc' do
|
111
|
-
expect(subject.value(nil, :
|
111
|
+
expect(subject.value(nil, abc: nil)).to eq(['nil', subject])
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
115
115
|
context 'when option is set' do
|
116
116
|
it 'returns value passed through proc' do
|
117
|
-
expect(subject.value(nil, :
|
117
|
+
expect(subject.value(nil, abc: 123)).to eq(['123', subject])
|
118
118
|
end
|
119
119
|
end
|
120
120
|
end
|
@@ -5,8 +5,6 @@ require 'image_optim/path'
|
|
5
5
|
require 'tempfile'
|
6
6
|
|
7
7
|
describe ImageOptim::Path do
|
8
|
-
include CapabilityCheckHelpers
|
9
|
-
|
10
8
|
before do
|
11
9
|
stub_const('Path', ImageOptim::Path)
|
12
10
|
end
|
@@ -76,8 +74,7 @@ describe ImageOptim::Path do
|
|
76
74
|
expect(src).to_not exist
|
77
75
|
end
|
78
76
|
|
79
|
-
it 'preserves attributes of destination file' do
|
80
|
-
skip 'full file modes are not support' unless any_file_modes_allowed?
|
77
|
+
it 'preserves attributes of destination file', skip: SkipConditions[:any_file_mode_allowed] do
|
81
78
|
mode = 0o666
|
82
79
|
|
83
80
|
dst.chmod(mode)
|
@@ -89,17 +86,17 @@ describe ImageOptim::Path do
|
|
89
86
|
end
|
90
87
|
|
91
88
|
it 'does not preserve mtime of destination file' do
|
92
|
-
time = src.mtime
|
89
|
+
time = src.mtime - 1000
|
90
|
+
dst.utime(time, time)
|
93
91
|
|
94
|
-
|
92
|
+
time = dst.mtime
|
95
93
|
|
96
94
|
src.replace(dst)
|
97
95
|
|
98
|
-
expect(dst.mtime).
|
96
|
+
expect(dst.mtime).to_not eq(time)
|
99
97
|
end
|
100
98
|
|
101
|
-
it 'changes inode of destination' do
|
102
|
-
skip 'inodes are not supported' unless inodes_supported?
|
99
|
+
it 'changes inode of destination', skip: SkipConditions[:inodes_support] do
|
103
100
|
expect{ src.replace(dst) }.to change{ dst.stat.ino }
|
104
101
|
end
|
105
102
|
end
|
@@ -120,7 +117,7 @@ describe ImageOptim::Path do
|
|
120
117
|
include_examples 'replaces file'
|
121
118
|
|
122
119
|
it 'is using temporary file with .tmp extension' do
|
123
|
-
expect(src).to receive(:move).with(having_attributes(:
|
120
|
+
expect(src).to receive(:move).with(having_attributes(extname: '.tmp'))
|
124
121
|
|
125
122
|
src.replace(dst)
|
126
123
|
end
|
@@ -135,7 +132,7 @@ describe ImageOptim::Path do
|
|
135
132
|
include_examples 'replaces file'
|
136
133
|
|
137
134
|
it 'is using temporary file with .tmp extension' do
|
138
|
-
expect(src).to receive(:move).with(having_attributes(:
|
135
|
+
expect(src).to receive(:move).with(having_attributes(extname: '.tmp'))
|
139
136
|
|
140
137
|
src.replace(dst)
|
141
138
|
end
|
@@ -37,7 +37,7 @@ describe ImageOptim::Runner::OptionParser do
|
|
37
37
|
%w[-r -R --recursive].each do |flag|
|
38
38
|
it "is parsed from #{flag}" do
|
39
39
|
args = %W[#{flag} foo bar]
|
40
|
-
expect(OptionParser.parse!(args)).to eq(:
|
40
|
+
expect(OptionParser.parse!(args)).to eq(recursive: true)
|
41
41
|
expect(args).to eq(%w[foo bar])
|
42
42
|
end
|
43
43
|
end
|
@@ -46,19 +46,19 @@ describe ImageOptim::Runner::OptionParser do
|
|
46
46
|
describe 'numeric option threads' do
|
47
47
|
it 'is parsed with space separator' do
|
48
48
|
args = %w[--threads 616 foo bar]
|
49
|
-
expect(OptionParser.parse!(args)).to eq(:
|
49
|
+
expect(OptionParser.parse!(args)).to eq(threads: 616)
|
50
50
|
expect(args).to eq(%w[foo bar])
|
51
51
|
end
|
52
52
|
|
53
53
|
it 'is parsed with equal separator' do
|
54
54
|
args = %w[--threads=616 foo bar]
|
55
|
-
expect(OptionParser.parse!(args)).to eq(:
|
55
|
+
expect(OptionParser.parse!(args)).to eq(threads: 616)
|
56
56
|
expect(args).to eq(%w[foo bar])
|
57
57
|
end
|
58
58
|
|
59
59
|
it 'is parsed with no- prefix' do
|
60
60
|
args = %w[--no-threads 616 foo bar]
|
61
|
-
expect(OptionParser.parse!(args)).to eq(:
|
61
|
+
expect(OptionParser.parse!(args)).to eq(threads: false)
|
62
62
|
expect(args).to eq(%w[616 foo bar])
|
63
63
|
end
|
64
64
|
end
|