image_optim 0.28.0 → 0.31.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/check.yml +89 -0
- data/.pre-commit-hooks.yaml +9 -0
- data/.rubocop.yml +6 -3
- data/CHANGELOG.markdown +20 -0
- data/CONTRIBUTING.markdown +1 -1
- data/Gemfile +1 -7
- data/LICENSE.txt +1 -1
- data/README.markdown +18 -9
- data/Vagrantfile +1 -1
- data/image_optim.gemspec +6 -3
- data/lib/image_optim/bin_resolver/bin.rb +9 -9
- data/lib/image_optim/cache.rb +6 -0
- data/lib/image_optim/cmd.rb +45 -6
- data/lib/image_optim/config.rb +11 -5
- data/lib/image_optim/elapsed_time.rb +26 -0
- data/lib/image_optim/errors.rb +9 -0
- data/lib/image_optim/path.rb +1 -1
- data/lib/image_optim/runner/option_parser.rb +21 -17
- data/lib/image_optim/runner.rb +1 -1
- data/lib/image_optim/timer.rb +25 -0
- data/lib/image_optim/worker/advpng.rb +7 -7
- data/lib/image_optim/worker/gifsicle.rb +11 -11
- data/lib/image_optim/worker/jhead.rb +2 -2
- data/lib/image_optim/worker/jpegoptim.rb +11 -11
- data/lib/image_optim/worker/jpegrecompress.rb +6 -6
- data/lib/image_optim/worker/jpegtran.rb +4 -4
- data/lib/image_optim/worker/optipng.rb +7 -7
- data/lib/image_optim/worker/oxipng.rb +53 -0
- data/lib/image_optim/worker/pngcrush.rb +6 -6
- data/lib/image_optim/worker/pngout.rb +7 -7
- data/lib/image_optim/worker/pngquant.rb +9 -9
- data/lib/image_optim/worker/svgo.rb +2 -2
- data/lib/image_optim/worker.rb +32 -29
- data/lib/image_optim.rb +16 -10
- data/script/update_worker_options_in_readme +1 -1
- data/script/worker_analysis +16 -18
- data/spec/image_optim/bin_resolver_spec.rb +5 -5
- data/spec/image_optim/cache_path_spec.rb +7 -10
- data/spec/image_optim/cache_spec.rb +7 -7
- data/spec/image_optim/cmd_spec.rb +64 -6
- data/spec/image_optim/config_spec.rb +36 -20
- data/spec/image_optim/elapsed_time_spec.rb +14 -0
- data/spec/image_optim/handler_spec.rb +1 -1
- data/spec/image_optim/hash_helpers_spec.rb +18 -18
- data/spec/image_optim/option_definition_spec.rb +6 -6
- data/spec/image_optim/path_spec.rb +8 -11
- data/spec/image_optim/runner/option_parser_spec.rb +4 -4
- data/spec/image_optim/timer_spec.rb +32 -0
- data/spec/image_optim/worker/jpegrecompress_spec.rb +2 -2
- data/spec/image_optim/worker/optipng_spec.rb +11 -11
- data/spec/image_optim/worker/oxipng_spec.rb +89 -0
- data/spec/image_optim/worker/pngquant_spec.rb +5 -5
- data/spec/image_optim/worker_spec.rb +17 -17
- data/spec/image_optim_spec.rb +47 -10
- data/spec/images/invisiblepixels/generate +1 -1
- data/spec/spec_helper.rb +24 -21
- metadata +35 -11
- data/.appveyor.yml +0 -53
- data/.travis.yml +0 -48
@@ -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
|