discourse_image_optim 0.24.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.appveyor.yml +46 -0
- data/.gitignore +18 -0
- data/.rubocop.yml +110 -0
- data/.travis.yml +42 -0
- data/CHANGELOG.markdown +316 -0
- data/CONTRIBUTING.markdown +11 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +358 -0
- data/Vagrantfile +38 -0
- data/bin/image_optim +28 -0
- data/image_optim.gemspec +34 -0
- data/lib/image_optim.rb +267 -0
- data/lib/image_optim/bin_resolver.rb +142 -0
- data/lib/image_optim/bin_resolver/bin.rb +115 -0
- data/lib/image_optim/bin_resolver/comparable_condition.rb +60 -0
- data/lib/image_optim/bin_resolver/error.rb +6 -0
- data/lib/image_optim/bin_resolver/simple_version.rb +31 -0
- data/lib/image_optim/cache.rb +72 -0
- data/lib/image_optim/cache_path.rb +16 -0
- data/lib/image_optim/cmd.rb +122 -0
- data/lib/image_optim/config.rb +219 -0
- data/lib/image_optim/configuration_error.rb +3 -0
- data/lib/image_optim/handler.rb +57 -0
- data/lib/image_optim/hash_helpers.rb +45 -0
- data/lib/image_optim/image_meta.rb +20 -0
- data/lib/image_optim/non_negative_integer_range.rb +11 -0
- data/lib/image_optim/optimized_path.rb +25 -0
- data/lib/image_optim/option_definition.rb +38 -0
- data/lib/image_optim/option_helpers.rb +17 -0
- data/lib/image_optim/path.rb +70 -0
- data/lib/image_optim/runner.rb +139 -0
- data/lib/image_optim/runner/glob_helpers.rb +45 -0
- data/lib/image_optim/runner/option_parser.rb +246 -0
- data/lib/image_optim/space.rb +29 -0
- data/lib/image_optim/true_false_nil.rb +16 -0
- data/lib/image_optim/worker.rb +170 -0
- data/lib/image_optim/worker/advpng.rb +37 -0
- data/lib/image_optim/worker/class_methods.rb +107 -0
- data/lib/image_optim/worker/gifsicle.rb +65 -0
- data/lib/image_optim/worker/jhead.rb +47 -0
- data/lib/image_optim/worker/jpegoptim.rb +63 -0
- data/lib/image_optim/worker/jpegrecompress.rb +49 -0
- data/lib/image_optim/worker/jpegtran.rb +48 -0
- data/lib/image_optim/worker/optipng.rb +53 -0
- data/lib/image_optim/worker/pngcrush.rb +56 -0
- data/lib/image_optim/worker/pngout.rb +40 -0
- data/lib/image_optim/worker/pngquant.rb +61 -0
- data/lib/image_optim/worker/svgo.rb +34 -0
- data/script/template/jquery-2.1.3.min.js +4 -0
- data/script/template/sortable-0.6.0.min.js +2 -0
- data/script/template/worker_analysis.erb +254 -0
- data/script/update_worker_options_in_readme +59 -0
- data/script/worker_analysis +589 -0
- data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +37 -0
- data/spec/image_optim/bin_resolver/simple_version_spec.rb +65 -0
- data/spec/image_optim/bin_resolver_spec.rb +290 -0
- data/spec/image_optim/cache_path_spec.rb +57 -0
- data/spec/image_optim/cache_spec.rb +162 -0
- data/spec/image_optim/cmd_spec.rb +93 -0
- data/spec/image_optim/config_spec.rb +254 -0
- data/spec/image_optim/handler_spec.rb +90 -0
- data/spec/image_optim/hash_helpers_spec.rb +74 -0
- data/spec/image_optim/image_meta_spec.rb +61 -0
- data/spec/image_optim/optimized_path_spec.rb +58 -0
- data/spec/image_optim/option_definition_spec.rb +138 -0
- data/spec/image_optim/option_helpers_spec.rb +25 -0
- data/spec/image_optim/path_spec.rb +103 -0
- data/spec/image_optim/runner/glob_helpers_spec.rb +21 -0
- data/spec/image_optim/runner/option_parser_spec.rb +105 -0
- data/spec/image_optim/space_spec.rb +23 -0
- data/spec/image_optim/worker/optipng_spec.rb +102 -0
- data/spec/image_optim/worker/pngquant_spec.rb +67 -0
- data/spec/image_optim/worker_spec.rb +303 -0
- data/spec/image_optim_spec.rb +259 -0
- data/spec/images/broken_jpeg +1 -0
- data/spec/images/comparison.png +0 -0
- data/spec/images/decompressed.jpeg +0 -0
- data/spec/images/icecream.gif +0 -0
- data/spec/images/image.jpg +0 -0
- data/spec/images/invisiblepixels/generate +24 -0
- data/spec/images/invisiblepixels/image.png +0 -0
- data/spec/images/lena.jpg +0 -0
- data/spec/images/orient/0.jpg +0 -0
- data/spec/images/orient/1.jpg +0 -0
- data/spec/images/orient/2.jpg +0 -0
- data/spec/images/orient/3.jpg +0 -0
- data/spec/images/orient/4.jpg +0 -0
- data/spec/images/orient/5.jpg +0 -0
- data/spec/images/orient/6.jpg +0 -0
- data/spec/images/orient/7.jpg +0 -0
- data/spec/images/orient/8.jpg +0 -0
- data/spec/images/orient/generate +23 -0
- data/spec/images/orient/original.jpg +0 -0
- data/spec/images/quant/64.png +0 -0
- data/spec/images/quant/generate +25 -0
- data/spec/images/rails.png +0 -0
- data/spec/images/test.svg +3 -0
- data/spec/images/transparency1.png +0 -0
- data/spec/images/transparency2.png +0 -0
- data/spec/images/vergroessert.jpg +0 -0
- data/spec/spec_helper.rb +93 -0
- metadata +281 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'image_optim/bin_resolver/comparable_condition'
|
3
|
+
|
4
|
+
describe ImageOptim::BinResolver::ComparableCondition do
|
5
|
+
let(:is){ ImageOptim::BinResolver::ComparableCondition.is }
|
6
|
+
|
7
|
+
it 'builds conditions' do
|
8
|
+
expect(is.between?(10, 20).method).to eq(:between?)
|
9
|
+
expect(is.between?(10, 20).args).to eq([10, 20])
|
10
|
+
|
11
|
+
expect((is >= 15).method).to eq(:>=)
|
12
|
+
expect((is >= 15).args).to eq([15])
|
13
|
+
|
14
|
+
expect((is < 30).method).to eq(:<)
|
15
|
+
expect((is < 30).args).to eq([30])
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'stringifies conditions' do
|
19
|
+
expect(is.between?(10, 20).to_s).to eq('10..20')
|
20
|
+
expect((is >= 15).to_s).to eq('>= 15')
|
21
|
+
expect((is < 30).to_s).to eq('< 30')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'matches conditions' do
|
25
|
+
expect(is.between?(10, 20)).not_to match 9
|
26
|
+
expect(is.between?(10, 20)).to match 15
|
27
|
+
expect(is.between?(10, 20)).not_to match 21
|
28
|
+
|
29
|
+
expect(is >= 15).not_to match 14
|
30
|
+
expect(is >= 15).to match 15
|
31
|
+
expect(is >= 15).to match 16
|
32
|
+
|
33
|
+
expect(is < 30).to match 29
|
34
|
+
expect(is < 30).not_to match 30
|
35
|
+
expect(is < 30).not_to match 31
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'image_optim/bin_resolver/simple_version'
|
3
|
+
|
4
|
+
describe ImageOptim::BinResolver::SimpleVersion do
|
5
|
+
def v(str)
|
6
|
+
described_class.new(str)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#initialize' do
|
10
|
+
describe 'conversion' do
|
11
|
+
it 'converts Integer' do
|
12
|
+
expect(v(117)).to eq('117')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'converts Float' do
|
16
|
+
expect(v(1.17)).to eq('1.17')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'converts String' do
|
20
|
+
expect(v('1.17')).to eq('1.17')
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'converts self' do
|
24
|
+
expect(v(v(1.17))).to eq('1.17')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'normalization' do
|
29
|
+
%w[
|
30
|
+
1
|
31
|
+
01
|
32
|
+
1.0
|
33
|
+
1.00
|
34
|
+
1.0.0
|
35
|
+
1.0.0.0
|
36
|
+
].each do |variation|
|
37
|
+
it "normalizes #{variation}" do
|
38
|
+
expect(v(variation)).to eq(1)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#to_s' do
|
45
|
+
it 'returns the original value converted to String' do
|
46
|
+
expect(v(117).to_s).to eq('117')
|
47
|
+
expect(v(1.17).to_s).to eq('1.17')
|
48
|
+
expect(v('0117').to_s).to eq('0117')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#<=>' do
|
53
|
+
describe 'comparing version 1.17' do
|
54
|
+
subject{ v '1.17' }
|
55
|
+
|
56
|
+
it{ is_expected.to be > '0' }
|
57
|
+
it{ is_expected.to be > '0.1' }
|
58
|
+
it{ is_expected.to be > '0.9' }
|
59
|
+
it{ is_expected.to be > '1.9' }
|
60
|
+
it{ is_expected.to be < '1.17.1' }
|
61
|
+
it{ is_expected.to be < '1.99' }
|
62
|
+
it{ is_expected.to be < '2.1' }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,290 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'image_optim/bin_resolver'
|
3
|
+
require 'image_optim/cmd'
|
4
|
+
require 'image_optim/path'
|
5
|
+
|
6
|
+
describe ImageOptim::BinResolver do
|
7
|
+
def stub_env(key, value)
|
8
|
+
allow(ENV).to receive(:[]).with(key).and_return(value)
|
9
|
+
end
|
10
|
+
|
11
|
+
before do
|
12
|
+
stub_const('BinResolver', ImageOptim::BinResolver)
|
13
|
+
stub_const('Bin', BinResolver::Bin)
|
14
|
+
stub_const('SimpleVersion', BinResolver::SimpleVersion)
|
15
|
+
stub_const('Cmd', ImageOptim::Cmd)
|
16
|
+
|
17
|
+
allow(ENV).to receive(:[]).and_call_original
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:image_optim){ double(:image_optim, :verbose => false, :pack => false) }
|
21
|
+
let(:resolver){ BinResolver.new(image_optim) }
|
22
|
+
|
23
|
+
describe '#full_path' do
|
24
|
+
def full_path(name)
|
25
|
+
resolver.instance_eval{ full_path(name) }
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when PATHEXT is not set' do
|
29
|
+
it 'finds binary without ext in combined path' do
|
30
|
+
stub_env 'PATH', %w[/a /b /c /d].join(File::PATH_SEPARATOR)
|
31
|
+
stub_env 'PATHEXT', nil
|
32
|
+
|
33
|
+
[
|
34
|
+
[:file?, '/a/abc', false],
|
35
|
+
[:file?, '/b/abc', true],
|
36
|
+
[:executable?, '/b/abc', false],
|
37
|
+
[:file?, '/c/abc', true],
|
38
|
+
[:executable?, '/c/abc', true],
|
39
|
+
].each do |method, path, result|
|
40
|
+
allow(File).to receive(method).
|
41
|
+
with(File.expand_path(path)).and_return(result)
|
42
|
+
end
|
43
|
+
|
44
|
+
expect(full_path('abc')).
|
45
|
+
to eq(File.expand_path('/c/abc'))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when PATHEXT is set' do
|
50
|
+
it 'finds binary with ext in combined path' do
|
51
|
+
stub_env 'PATH', %w[/a /b].join(File::PATH_SEPARATOR)
|
52
|
+
stub_env 'PATHEXT', '.com;.bat'
|
53
|
+
|
54
|
+
[
|
55
|
+
[:file?, '/a/abc.com', false],
|
56
|
+
[:file?, '/a/abc.bat', true],
|
57
|
+
[:executable?, '/a/abc.bat', false],
|
58
|
+
[:file?, '/b/abc.com', true],
|
59
|
+
[:executable?, '/b/abc.com', true],
|
60
|
+
].each do |method, path, result|
|
61
|
+
allow(File).to receive(method).
|
62
|
+
with(File.expand_path(path)).and_return(result)
|
63
|
+
end
|
64
|
+
|
65
|
+
expect(full_path('abc')).
|
66
|
+
to eq(File.expand_path('/b/abc.com'))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'returns nil on failure' do
|
71
|
+
stub_env 'PATH', ''
|
72
|
+
expect(full_path('image_optim')).to be_nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'combines path in order dir, pack, path, vendor' do
|
77
|
+
allow(image_optim).to receive(:pack).and_return(true)
|
78
|
+
stub_const('ImageOptim::Pack', Class.new do
|
79
|
+
def self.path
|
80
|
+
'pack_path'
|
81
|
+
end
|
82
|
+
end)
|
83
|
+
allow(resolver).to receive(:dir).and_return('temp_dir')
|
84
|
+
|
85
|
+
expect(resolver.env_path).to eq([
|
86
|
+
'temp_dir',
|
87
|
+
'pack_path',
|
88
|
+
ENV['PATH'],
|
89
|
+
BinResolver::VENDOR_PATH,
|
90
|
+
].join(File::PATH_SEPARATOR))
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'resolves bin in path and returns instance of Bin' do
|
94
|
+
stub_env 'LS_BIN', nil
|
95
|
+
expect(FSPath).not_to receive(:temp_dir)
|
96
|
+
expect(resolver).to receive(:full_path).with(:ls).and_return('/bin/ls')
|
97
|
+
bin = double
|
98
|
+
expect(Bin).to receive(:new).with(:ls, '/bin/ls').and_return(bin)
|
99
|
+
expect(bin).to receive(:check!).once
|
100
|
+
expect(bin).to receive(:check_fail!).exactly(5).times
|
101
|
+
|
102
|
+
5.times do
|
103
|
+
expect(resolver.resolve!(:ls)).to eq(bin)
|
104
|
+
end
|
105
|
+
expect(resolver.env_path).to eq([
|
106
|
+
ENV['PATH'],
|
107
|
+
BinResolver::VENDOR_PATH,
|
108
|
+
].join(File::PATH_SEPARATOR))
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'raises on failure to resolve bin' do
|
112
|
+
stub_env 'LS_BIN', nil
|
113
|
+
expect(FSPath).not_to receive(:temp_dir)
|
114
|
+
expect(resolver).to receive(:full_path).with(:ls).and_return(nil)
|
115
|
+
expect(Bin).not_to receive(:new)
|
116
|
+
|
117
|
+
5.times do
|
118
|
+
expect do
|
119
|
+
resolver.resolve!(:ls)
|
120
|
+
end.to raise_error BinResolver::BinNotFound
|
121
|
+
end
|
122
|
+
expect(resolver.env_path).to eq([
|
123
|
+
ENV['PATH'],
|
124
|
+
BinResolver::VENDOR_PATH,
|
125
|
+
].join(File::PATH_SEPARATOR))
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'resolves bin specified in ENV' do
|
129
|
+
path = 'bin/the_optimizer'
|
130
|
+
stub_env 'THE_OPTIMIZER_BIN', path
|
131
|
+
tmpdir = double(:tmpdir, :to_str => 'tmpdir')
|
132
|
+
symlink = double(:symlink)
|
133
|
+
|
134
|
+
full_path = File.expand_path(path)
|
135
|
+
allow(File).to receive(:exist?).with(full_path).and_return(true)
|
136
|
+
allow(File).to receive(:file?).with(full_path).and_return(true)
|
137
|
+
allow(File).to receive(:executable?).with(full_path).and_return(true)
|
138
|
+
|
139
|
+
expect(FSPath).to receive(:temp_dir).
|
140
|
+
once.and_return(tmpdir)
|
141
|
+
expect(tmpdir).to receive(:/).
|
142
|
+
with(:the_optimizer).once.and_return(symlink)
|
143
|
+
expect(symlink).to receive(:make_symlink).
|
144
|
+
with(File.expand_path(path)).once
|
145
|
+
|
146
|
+
expect(resolver).not_to receive(:full_path)
|
147
|
+
bin = double
|
148
|
+
expect(Bin).to receive(:new).
|
149
|
+
with(:the_optimizer, File.expand_path(path)).and_return(bin)
|
150
|
+
expect(bin).to receive(:check!).once
|
151
|
+
expect(bin).to receive(:check_fail!).exactly(5).times
|
152
|
+
|
153
|
+
at_exit_blocks = []
|
154
|
+
expect(resolver).to receive(:at_exit).once do |&block|
|
155
|
+
at_exit_blocks.unshift(block)
|
156
|
+
end
|
157
|
+
|
158
|
+
5.times do
|
159
|
+
resolver.resolve!(:the_optimizer)
|
160
|
+
end
|
161
|
+
expect(resolver.env_path).to eq([
|
162
|
+
tmpdir,
|
163
|
+
ENV['PATH'],
|
164
|
+
BinResolver::VENDOR_PATH,
|
165
|
+
].join(File::PATH_SEPARATOR))
|
166
|
+
|
167
|
+
expect(FileUtils).to receive(:remove_entry_secure).with(tmpdir)
|
168
|
+
at_exit_blocks.each(&:call)
|
169
|
+
end
|
170
|
+
|
171
|
+
describe 'checking bin' do
|
172
|
+
let(:path){ 'the_optimizer' }
|
173
|
+
let(:exist?){ true }
|
174
|
+
let(:file?){ true }
|
175
|
+
let(:executable?){ true }
|
176
|
+
|
177
|
+
before do
|
178
|
+
stub_env 'THE_OPTIMIZER_BIN', path
|
179
|
+
expect(FSPath).not_to receive(:temp_dir)
|
180
|
+
expect(resolver).not_to receive(:at_exit)
|
181
|
+
allow(File).to receive_messages(:exist? => exist?,
|
182
|
+
:file? => file?,
|
183
|
+
:executable? => executable?)
|
184
|
+
end
|
185
|
+
|
186
|
+
after do
|
187
|
+
expect(resolver.env_path).to eq([
|
188
|
+
ENV['PATH'],
|
189
|
+
BinResolver::VENDOR_PATH,
|
190
|
+
].join(File::PATH_SEPARATOR))
|
191
|
+
end
|
192
|
+
|
193
|
+
def raises_error(error_message)
|
194
|
+
5.times do
|
195
|
+
expect do
|
196
|
+
resolver.resolve!(:the_optimizer)
|
197
|
+
end.to raise_error RuntimeError, /#{Regexp.escape(error_message)}/
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context 'presence' do
|
202
|
+
let(:exist?){ false }
|
203
|
+
|
204
|
+
it{ raises_error('doesn\'t exist') }
|
205
|
+
end
|
206
|
+
|
207
|
+
context 'been a file' do
|
208
|
+
let(:file?){ false }
|
209
|
+
|
210
|
+
it{ raises_error('is not a file') }
|
211
|
+
end
|
212
|
+
|
213
|
+
context 'been a file' do
|
214
|
+
let(:executable?){ false }
|
215
|
+
|
216
|
+
it{ raises_error('is not executable') }
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'resolves bin only once, but checks every time' do
|
221
|
+
stub_env 'LS_BIN', nil
|
222
|
+
expect(resolver).to receive(:full_path).once.with(:ls) do
|
223
|
+
sleep 0.1
|
224
|
+
'/bin/ls'
|
225
|
+
end
|
226
|
+
bin = double
|
227
|
+
expect(Bin).to receive(:new).once.with(:ls, '/bin/ls').and_return(bin)
|
228
|
+
|
229
|
+
count = 0
|
230
|
+
mutex = Mutex.new
|
231
|
+
allow(bin).to receive(:check!).once
|
232
|
+
allow(bin).to receive(:check_fail!){ mutex.synchronize{ count += 1 } }
|
233
|
+
|
234
|
+
Array.new(10) do
|
235
|
+
Thread.new do
|
236
|
+
resolver.resolve!(:ls)
|
237
|
+
end
|
238
|
+
end.each(&:join)
|
239
|
+
|
240
|
+
expect(count).to eq(10)
|
241
|
+
end
|
242
|
+
|
243
|
+
describe 'checking version' do
|
244
|
+
before do
|
245
|
+
allow(resolver).to receive(:full_path){ |name| "/bin/#{name}" }
|
246
|
+
end
|
247
|
+
|
248
|
+
it 'raises every time if did not get bin version' do
|
249
|
+
stub_env 'PNGCRUSH_BIN', nil
|
250
|
+
bin = Bin.new(:pngcrush, '/bin/pngcrush')
|
251
|
+
|
252
|
+
expect(Bin).to receive(:new).and_return(bin)
|
253
|
+
allow(bin).to receive(:version).and_return(nil)
|
254
|
+
|
255
|
+
5.times do
|
256
|
+
expect do
|
257
|
+
resolver.resolve!(:pngcrush)
|
258
|
+
end.to raise_error Bin::UnknownVersion, %r{pngcrush at /bin/pngcrush}
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'raises every time on detection of misbehaving version' do
|
263
|
+
stub_env 'PNGCRUSH_BIN', nil
|
264
|
+
bin = Bin.new(:pngcrush, '/bin/pngcrush')
|
265
|
+
|
266
|
+
expect(Bin).to receive(:new).and_return(bin)
|
267
|
+
allow(bin).to receive(:version).and_return(SimpleVersion.new('1.7.60'))
|
268
|
+
|
269
|
+
5.times do
|
270
|
+
expect do
|
271
|
+
resolver.resolve!(:pngcrush)
|
272
|
+
end.to raise_error Bin::BadVersion, /is known to produce broken pngs/
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'warns once on detection of problematic version' do
|
277
|
+
stub_env 'ADVPNG_BIN', nil
|
278
|
+
bin = Bin.new(:advpng, '/bin/advpng')
|
279
|
+
|
280
|
+
expect(Bin).to receive(:new).and_return(bin)
|
281
|
+
allow(bin).to receive(:version).and_return(SimpleVersion.new('1.15'))
|
282
|
+
|
283
|
+
expect(bin).to receive(:warn).once.with(match(/does not use zopfli/))
|
284
|
+
|
285
|
+
5.times do
|
286
|
+
resolver.resolve!(:pngcrush)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'image_optim/cache_path'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
describe ImageOptim::CachePath do
|
6
|
+
include CapabilityCheckHelpers
|
7
|
+
|
8
|
+
before do
|
9
|
+
stub_const('CachePath', ImageOptim::CachePath)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#replace' do
|
13
|
+
let(:src){ CachePath.temp_file_path }
|
14
|
+
let(:dst){ CachePath.temp_file_path }
|
15
|
+
|
16
|
+
it 'moves data to destination' do
|
17
|
+
src.write('src')
|
18
|
+
|
19
|
+
src.replace(dst)
|
20
|
+
|
21
|
+
expect(dst.read).to eq('src')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'does not remove original file' do
|
25
|
+
src.replace(dst)
|
26
|
+
|
27
|
+
expect(src).to exist
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'preserves attributes of destination file' do
|
31
|
+
skip 'full file modes are not support' unless any_file_modes_allowed?
|
32
|
+
mode = 0o666
|
33
|
+
|
34
|
+
dst.chmod(mode)
|
35
|
+
|
36
|
+
src.replace(dst)
|
37
|
+
|
38
|
+
got = dst.stat.mode & 0o777
|
39
|
+
expect(got).to eq(mode), format('expected %04o, got %04o', mode, got)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'does not preserve mtime of destination file' do
|
43
|
+
time = src.mtime
|
44
|
+
|
45
|
+
dst.utime(time - 1000, time - 1000)
|
46
|
+
|
47
|
+
src.replace(dst)
|
48
|
+
|
49
|
+
expect(dst.mtime).to be >= time
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'changes inode of destination' do
|
53
|
+
skip 'inodes are not supported' unless inodes_supported?
|
54
|
+
expect{ src.replace(dst) }.to change{ dst.stat.ino }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|