image_optim 0.17.1 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.gitignore +1 -0
- data/.travis.yml +5 -18
- data/CHANGELOG.markdown +10 -0
- data/README.markdown +31 -1
- data/bin/image_optim +3 -137
- data/image_optim.gemspec +6 -3
- data/lib/image_optim.rb +20 -3
- data/lib/image_optim/bin_resolver.rb +28 -1
- data/lib/image_optim/bin_resolver/bin.rb +17 -7
- data/lib/image_optim/cmd.rb +49 -0
- data/lib/image_optim/config.rb +64 -4
- data/lib/image_optim/image_path.rb +5 -0
- data/lib/image_optim/option_definition.rb +5 -3
- data/lib/image_optim/runner.rb +1 -2
- data/lib/image_optim/runner/option_parser.rb +216 -0
- data/lib/image_optim/worker.rb +32 -17
- data/lib/image_optim/worker/advpng.rb +7 -1
- data/lib/image_optim/worker/gifsicle.rb +16 -3
- data/lib/image_optim/worker/jhead.rb +15 -8
- data/lib/image_optim/worker/jpegoptim.rb +6 -2
- data/lib/image_optim/worker/jpegtran.rb +10 -3
- data/lib/image_optim/worker/optipng.rb +6 -1
- data/lib/image_optim/worker/pngcrush.rb +8 -1
- data/lib/image_optim/worker/pngout.rb +8 -1
- data/lib/image_optim/worker/svgo.rb +4 -1
- data/script/worker_analysis +523 -0
- data/script/worker_analysis.haml +153 -0
- data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +4 -5
- data/spec/image_optim/bin_resolver/simple_version_spec.rb +44 -21
- data/spec/image_optim/bin_resolver_spec.rb +63 -29
- data/spec/image_optim/cmd_spec.rb +66 -0
- data/spec/image_optim/config_spec.rb +38 -38
- data/spec/image_optim/handler_spec.rb +15 -12
- data/spec/image_optim/hash_helpers_spec.rb +14 -13
- data/spec/image_optim/image_path_spec.rb +22 -7
- data/spec/image_optim/runner/glob_helpers_spec.rb +6 -5
- data/spec/image_optim/runner/option_parser_spec.rb +99 -0
- data/spec/image_optim/space_spec.rb +5 -4
- data/spec/image_optim/worker_spec.rb +6 -5
- data/spec/image_optim_spec.rb +209 -237
- data/spec/spec_helper.rb +3 -0
- metadata +43 -11
@@ -0,0 +1,153 @@
|
|
1
|
+
!!! 5
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%meta(http-equiv="Content-Type" content="text/html; charset=utf-8")
|
5
|
+
%title Worker Analysis
|
6
|
+
:css
|
7
|
+
body {
|
8
|
+
font: 14px "Myriad Pro", "Helvetica", Arial, sans-serif;
|
9
|
+
margin: 20px;
|
10
|
+
padding: 0;
|
11
|
+
}
|
12
|
+
|
13
|
+
.formats {
|
14
|
+
display: block;
|
15
|
+
font-size: 1.5em;
|
16
|
+
margin: 1em 0;
|
17
|
+
font-weight: bold;
|
18
|
+
}
|
19
|
+
|
20
|
+
table {
|
21
|
+
border-collapse: collapse;
|
22
|
+
border-spacing: 0;
|
23
|
+
}
|
24
|
+
th, td {
|
25
|
+
border: 1px solid #ccc;
|
26
|
+
padding: 6px 8px 2px;
|
27
|
+
}
|
28
|
+
th {
|
29
|
+
text-align: left;
|
30
|
+
border-color: #999;
|
31
|
+
}
|
32
|
+
th:not([data-sortable="false"]) {
|
33
|
+
cursor: pointer;
|
34
|
+
padding-right: 18px;
|
35
|
+
}
|
36
|
+
th:after {
|
37
|
+
position: absolute;
|
38
|
+
width: 18px;
|
39
|
+
height: 14px;
|
40
|
+
line-height: 14px;
|
41
|
+
font-size: 0.8em;
|
42
|
+
color: #999;
|
43
|
+
visibility: hidden;
|
44
|
+
text-align: center;
|
45
|
+
}
|
46
|
+
th[data-sorted="true"]:after {
|
47
|
+
visibility: visible;
|
48
|
+
}
|
49
|
+
th[data-sorted-direction="descending"]:after {
|
50
|
+
content: "▼";
|
51
|
+
}
|
52
|
+
th[data-sorted-direction="ascending"]:after {
|
53
|
+
content: "▲";
|
54
|
+
}
|
55
|
+
table[data-sortable] tbody tr:hover {
|
56
|
+
background: #ddf;
|
57
|
+
}
|
58
|
+
tr.unused {
|
59
|
+
display: none;
|
60
|
+
}
|
61
|
+
|
62
|
+
input { margin-top: 1.5em; }
|
63
|
+
#toggle-show-unused:checked ~ table tr.unused { /* without js */
|
64
|
+
display: table-row;
|
65
|
+
}
|
66
|
+
|
67
|
+
.number {
|
68
|
+
text-align: right;
|
69
|
+
}
|
70
|
+
|
71
|
+
.warn-low {
|
72
|
+
background: rgba(255, 0, 0, 0.1);
|
73
|
+
}
|
74
|
+
.warn-medium {
|
75
|
+
background: rgba(255, 0, 0, 0.4);
|
76
|
+
}
|
77
|
+
.warn-high {
|
78
|
+
background: rgba(255, 0, 0, 0.8);
|
79
|
+
}
|
80
|
+
|
81
|
+
.worker-name,
|
82
|
+
.worker-success-count {
|
83
|
+
display: inline-block;
|
84
|
+
border-radius: 8px;
|
85
|
+
}
|
86
|
+
.worker-name {
|
87
|
+
background: #ddd;
|
88
|
+
padding: 2px 5px 0;
|
89
|
+
border: 1px solid #999;
|
90
|
+
margin: -3px 0 1px 0;
|
91
|
+
}
|
92
|
+
.worker-success-count {
|
93
|
+
font-size: 0.9em;
|
94
|
+
line-height: 1em;
|
95
|
+
background: #dfd;
|
96
|
+
padding: 1px 2px 0;
|
97
|
+
border: 1px solid #9b9;
|
98
|
+
}
|
99
|
+
.worker-name.unused {
|
100
|
+
text-decoration: line-through;
|
101
|
+
}
|
102
|
+
:javascript
|
103
|
+
/*! sortable.js 0.6.0 */
|
104
|
+
(function(){var a,b,c,d,e,f,g;a="table[data-sortable]",d=/^-?[£$¤]?[\d,.]+%?$/,g=/^\s+|\s+$/g,f="ontouchstart"in document.documentElement,c=f?"touchstart":"click",b=function(a,b,c){return null!=a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},e={init:function(b){var c,d,f,g,h;for(null==b&&(b={}),null==b.selector&&(b.selector=a),d=document.querySelectorAll(b.selector),h=[],f=0,g=d.length;g>f;f++)c=d[f],h.push(e.initTable(c));return h},initTable:function(a){var b,c,d,f,g,h;if(1===(null!=(h=a.tHead)?h.rows.length:void 0)&&"true"!==a.getAttribute("data-sortable-initialized")){for(a.setAttribute("data-sortable-initialized","true"),d=a.querySelectorAll("th"),b=f=0,g=d.length;g>f;b=++f)c=d[b],"false"!==c.getAttribute("data-sortable")&&e.setupClickableTH(a,c,b);return a}},setupClickableTH:function(a,d,f){var g;return g=e.getColumnType(a,f),"click"===c&&b(d,"mousedown",function(){return event.preventDefault?event.preventDefault():event.returnValue=!1}),b(d,c,function(){var b,c,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v;for("true"===this.getAttribute("data-sorted")?(l=this.getAttribute("data-sorted-direction"),b="ascending"===l?"descending":"ascending"):b=this.getAttribute("data-default-direction")||g.defaultSortDirection,n=this.parentNode.querySelectorAll("th"),o=0,r=n.length;r>o;o++)d=n[o],d.setAttribute("data-sorted","false"),d.removeAttribute("data-sorted-direction");for(this.setAttribute("data-sorted","true"),this.setAttribute("data-sorted-direction",b),m=a.tBodies[0],i=[],u=m.rows,c=p=0,s=u.length;s>p;c=++p)h=u[c],i.push([e.getNodeValue(h.cells[f]),h,c]);for(k="descending"===b?-1:1,i.sort(function(a,b){var c;return c=g.compare(a,b),0!==c?c*k:a[2]-b[2]}),v=[],q=0,t=i.length;t>q;q++)j=i[q],v.push(m.appendChild(j[1]));return v})},getColumnType:function(a,b){var c,f,g,h,i;for(i=a.tBodies[0].rows,g=0,h=i.length;h>g;g++)if(c=i[g],f=e.getNodeValue(c.cells[b]),""!==f){if(f.match(d))return e.types.numeric;if(!isNaN(Date.parse(f)))return e.types.date}return e.types.alpha},getNodeValue:function(a){return a?null!==a.getAttribute("data-value")?a.getAttribute("data-value"):"undefined"!=typeof a.innerText?a.innerText.replace(g,""):a.textContent.replace(g,""):""},types:{numeric:{defaultSortDirection:"descending",compare:function(a,b){var c,d;return c=parseFloat(a[0].replace(/[^0-9.-]/g,""),10),d=parseFloat(b[0].replace(/[^0-9.-]/g,""),10),isNaN(c)&&(c=0),isNaN(d)&&(d=0),c-d}},alpha:{defaultSortDirection:"ascending",compare:function(a,b){return a[0].localeCompare(b[0])}},date:{defaultSortDirection:"ascending",compare:function(a,b){var c,d;return c=Date.parse(a[0]),d=Date.parse(b[0]),isNaN(c)&&(c=0),isNaN(d)&&(d=0),c-d}}}},setTimeout(e.init,0),window.Sortable=e}).call(this);
|
105
|
+
%body
|
106
|
+
.formats
|
107
|
+
- format_links.each do |format, link|
|
108
|
+
- if stats_format == format
|
109
|
+
%span= format
|
110
|
+
- else
|
111
|
+
%a(href="#{link}")= format
|
112
|
+
%table
|
113
|
+
%tr
|
114
|
+
%td.warn-low Max difference >= 0.001
|
115
|
+
%td.warn-medium Max difference >= 0.01
|
116
|
+
%td.warn-high Max difference >= 0.1
|
117
|
+
%input#toggle-show-unused(type="checkbox")
|
118
|
+
%label(for="toggle-show-unused")
|
119
|
+
Show chains with workers failed for every image
|
120
|
+
%p
|
121
|
+
Images: #{stats.each_chain.first.entry_count},
|
122
|
+
size: #{stats.each_chain.first.original_size}
|
123
|
+
%table(data-sortable)
|
124
|
+
%thead
|
125
|
+
%tr
|
126
|
+
%th Chain
|
127
|
+
%th(data-default-direction="ascending"
|
128
|
+
data-sorted="true"
|
129
|
+
data-sorted-direction="ascending") Optimized size
|
130
|
+
%th(data-default-direction="ascending") Ratio
|
131
|
+
%th(data-default-direction="ascending") Avg ratio
|
132
|
+
%th(data-default-direction="ascending") Time (s)
|
133
|
+
%th(data-default-direction="ascending") Avg difference
|
134
|
+
%th(data-default-direction="ascending") Max difference
|
135
|
+
%th Speed (B/s)
|
136
|
+
%tbody
|
137
|
+
- stats.each_chain do |chain|
|
138
|
+
%tr{class: chain.unused_workers && 'unused'}
|
139
|
+
%td
|
140
|
+
- chain.worker_stats.each do |worker_stat|
|
141
|
+
.worker-name{:class => worker_stat.unused? && 'unused'}
|
142
|
+
= worker_stat.name
|
143
|
+
- unless worker_stat.unused?
|
144
|
+
.worker-success-count(title="successfull count")
|
145
|
+
= worker_stat.success_count
|
146
|
+
%td.number= chain.optimized_size
|
147
|
+
%td.number= format '%.4f', chain.ratio
|
148
|
+
%td.number= format '%.4f', chain.avg_ratio
|
149
|
+
%td.number= format '%.2f', chain.time
|
150
|
+
%td.number= format '%.5f', chain.avg_difference
|
151
|
+
%td.number{class: chain.warn_level && "warn-#{chain.warn_level}"}
|
152
|
+
= format '%.5f', chain.max_difference
|
153
|
+
%td.number= format '%.2f', chain.speed
|
@@ -1,11 +1,10 @@
|
|
1
|
-
|
2
|
-
require 'rspec'
|
1
|
+
require 'spec_helper'
|
3
2
|
require 'image_optim/bin_resolver/comparable_condition'
|
4
3
|
|
5
4
|
describe ImageOptim::BinResolver::ComparableCondition do
|
6
5
|
is = ImageOptim::BinResolver::ComparableCondition.is
|
7
6
|
|
8
|
-
it '
|
7
|
+
it 'builds conditions' do
|
9
8
|
expect(is.between?(10, 20).method).to eq(:between?)
|
10
9
|
expect(is.between?(10, 20).args).to eq([10, 20])
|
11
10
|
|
@@ -16,13 +15,13 @@ describe ImageOptim::BinResolver::ComparableCondition do
|
|
16
15
|
expect((is < 30).args).to eq([30])
|
17
16
|
end
|
18
17
|
|
19
|
-
it '
|
18
|
+
it 'stringifies conditions' do
|
20
19
|
expect(is.between?(10, 20).to_s).to eq('10..20')
|
21
20
|
expect((is >= 15).to_s).to eq('>= 15')
|
22
21
|
expect((is < 30).to_s).to eq('< 30')
|
23
22
|
end
|
24
23
|
|
25
|
-
it '
|
24
|
+
it 'matches conditions' do
|
26
25
|
expect(is.between?(10, 20)).not_to match 9
|
27
26
|
expect(is.between?(10, 20)).to match 15
|
28
27
|
expect(is.between?(10, 20)).not_to match 21
|
@@ -1,34 +1,57 @@
|
|
1
|
-
|
2
|
-
require 'rspec'
|
1
|
+
require 'spec_helper'
|
3
2
|
require 'image_optim/bin_resolver/simple_version'
|
4
3
|
|
5
4
|
describe ImageOptim::BinResolver::SimpleVersion do
|
6
|
-
|
7
|
-
|
5
|
+
helpers = Module.new do
|
6
|
+
def v(str)
|
7
|
+
ImageOptim::BinResolver::SimpleVersion.new(str)
|
8
|
+
end
|
8
9
|
end
|
10
|
+
include helpers
|
11
|
+
extend helpers
|
12
|
+
|
13
|
+
describe 'compares version 1.17' do
|
14
|
+
subject{ v '1.17' }
|
9
15
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
expect(v '1.17').to be < '2.1'
|
16
|
+
it{ is_expected.to be > '0' }
|
17
|
+
it{ is_expected.to be > '0.1' }
|
18
|
+
it{ is_expected.to be > '0.9' }
|
19
|
+
it{ is_expected.to be > '1.9' }
|
20
|
+
it{ is_expected.to be < '1.17.1' }
|
21
|
+
it{ is_expected.to be < '1.99' }
|
22
|
+
it{ is_expected.to be < '2.1' }
|
18
23
|
end
|
19
24
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
+
describe 'normalization' do
|
26
|
+
%w[
|
27
|
+
1
|
28
|
+
01
|
29
|
+
1.0
|
30
|
+
1.00
|
31
|
+
1.0.0
|
32
|
+
1.0.0.0
|
33
|
+
].each do |variation|
|
34
|
+
it "normalizes #{variation}" do
|
35
|
+
expect(v variation).to eq(1)
|
25
36
|
end
|
26
37
|
end
|
27
38
|
end
|
28
39
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
40
|
+
describe 'conversion' do
|
41
|
+
it 'converts Integer' do
|
42
|
+
expect(v 117).to eq('117')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'converts Float' do
|
46
|
+
expect(v 1.17).to eq('1.17')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'converts String' do
|
50
|
+
expect(v '1.17').to eq('1.17')
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'converts self' do
|
54
|
+
expect(v(v 1.17)).to eq('1.17')
|
55
|
+
end
|
33
56
|
end
|
34
57
|
end
|
@@ -1,20 +1,23 @@
|
|
1
|
-
|
2
|
-
require 'rspec'
|
1
|
+
require 'spec_helper'
|
3
2
|
require 'image_optim/bin_resolver'
|
4
|
-
|
5
|
-
def with_env(key, value)
|
6
|
-
saved, ENV[key] = ENV[key], value
|
7
|
-
yield
|
8
|
-
ensure
|
9
|
-
ENV[key] = saved
|
10
|
-
end
|
3
|
+
require 'image_optim/cmd'
|
11
4
|
|
12
5
|
describe ImageOptim::BinResolver do
|
13
|
-
|
14
|
-
|
15
|
-
|
6
|
+
def with_env(key, value)
|
7
|
+
saved, ENV[key] = ENV[key], value
|
8
|
+
yield
|
9
|
+
ensure
|
10
|
+
ENV[key] = saved
|
11
|
+
end
|
16
12
|
|
17
|
-
|
13
|
+
before do
|
14
|
+
stub_const('BinResolver', ImageOptim::BinResolver)
|
15
|
+
stub_const('Bin', BinResolver::Bin)
|
16
|
+
stub_const('SimpleVersion', BinResolver::SimpleVersion)
|
17
|
+
stub_const('Cmd', ImageOptim::Cmd)
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:image_optim){ double(:image_optim, :verbose => false, :pack => false) }
|
18
21
|
let(:resolver){ BinResolver.new(image_optim) }
|
19
22
|
|
20
23
|
describe :full_path do
|
@@ -23,25 +26,39 @@ describe ImageOptim::BinResolver do
|
|
23
26
|
end
|
24
27
|
|
25
28
|
def command_v(name)
|
26
|
-
path =
|
29
|
+
path = Cmd.capture("sh -c 'command -v #{name}' 2> /dev/null").strip
|
27
30
|
path unless path.empty?
|
28
31
|
end
|
29
32
|
|
30
|
-
it '
|
33
|
+
it 'finds binary in path' do
|
31
34
|
with_env 'PATH', 'bin' do
|
32
35
|
expect(full_path('image_optim')).
|
33
36
|
to eq(File.expand_path('bin/image_optim'))
|
34
37
|
end
|
35
38
|
end
|
36
39
|
|
37
|
-
it '
|
40
|
+
it 'finds bin in vendor' do
|
38
41
|
with_env 'PATH', nil do
|
39
42
|
expect(full_path('jpegrescan')).
|
40
43
|
to eq(File.expand_path('vendor/jpegrescan'))
|
41
44
|
end
|
42
45
|
end
|
43
46
|
|
44
|
-
it '
|
47
|
+
it 'finds bin in pack' do
|
48
|
+
allow(image_optim).to receive(:pack).and_return(true)
|
49
|
+
stub_const('ImageOptim::Pack', Class.new do
|
50
|
+
def self.path
|
51
|
+
'script'
|
52
|
+
end
|
53
|
+
end)
|
54
|
+
|
55
|
+
with_env 'PATH', nil do
|
56
|
+
expect(full_path('update_worker_options_in_readme')).
|
57
|
+
to eq(File.expand_path('script/update_worker_options_in_readme'))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'works with different path separator' do
|
45
62
|
stub_const('File::PATH_SEPARATOR', 'O_o')
|
46
63
|
with_env 'PATH', 'bin' do
|
47
64
|
expect(full_path('image_optim')).
|
@@ -49,20 +66,37 @@ describe ImageOptim::BinResolver do
|
|
49
66
|
end
|
50
67
|
end
|
51
68
|
|
52
|
-
it '
|
69
|
+
it 'returns nil on failure' do
|
53
70
|
with_env 'PATH', 'lib' do
|
54
71
|
expect(full_path('image_optim')).to be_nil
|
55
72
|
end
|
56
73
|
end
|
57
74
|
|
58
|
-
%w[ls sh which bash image_optim
|
59
|
-
it "
|
75
|
+
%w[ls sh which bash image_optim does_not_exist].each do |name|
|
76
|
+
it "returns same path as `command -v` for #{name}" do
|
60
77
|
expect(full_path(name)).to eq(command_v(name))
|
61
78
|
end
|
62
79
|
end
|
63
80
|
end
|
64
81
|
|
65
|
-
it '
|
82
|
+
it 'combines path in order dir:pack:path:vendor' do
|
83
|
+
allow(image_optim).to receive(:pack).and_return(true)
|
84
|
+
stub_const('ImageOptim::Pack', Class.new do
|
85
|
+
def self.path
|
86
|
+
'pack_path'
|
87
|
+
end
|
88
|
+
end)
|
89
|
+
allow(resolver).to receive(:dir).and_return('temp_dir')
|
90
|
+
|
91
|
+
expect(resolver.env_path).to eq([
|
92
|
+
'temp_dir',
|
93
|
+
'pack_path',
|
94
|
+
ENV['PATH'],
|
95
|
+
BinResolver::VENDOR_PATH,
|
96
|
+
].join(':'))
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'resolves bin in path and returns instance of Bin' do
|
66
100
|
with_env 'LS_BIN', nil do
|
67
101
|
expect(FSPath).not_to receive(:temp_dir)
|
68
102
|
expect(resolver).to receive(:full_path).with(:ls).and_return('/bin/ls')
|
@@ -71,7 +105,7 @@ describe ImageOptim::BinResolver do
|
|
71
105
|
expect(bin).to receive(:check!).exactly(5).times
|
72
106
|
|
73
107
|
5.times do
|
74
|
-
resolver.resolve!(:ls)
|
108
|
+
expect(resolver.resolve!(:ls)).to eq(bin)
|
75
109
|
end
|
76
110
|
expect(resolver.env_path).to eq([
|
77
111
|
ENV['PATH'],
|
@@ -80,7 +114,7 @@ describe ImageOptim::BinResolver do
|
|
80
114
|
end
|
81
115
|
end
|
82
116
|
|
83
|
-
it '
|
117
|
+
it 'raises on failure to resolve bin' do
|
84
118
|
with_env 'LS_BIN', nil do
|
85
119
|
expect(FSPath).not_to receive(:temp_dir)
|
86
120
|
expect(resolver).to receive(:full_path).with(:ls).and_return(nil)
|
@@ -98,7 +132,7 @@ describe ImageOptim::BinResolver do
|
|
98
132
|
end
|
99
133
|
end
|
100
134
|
|
101
|
-
it '
|
135
|
+
it 'resolves bin specified in ENV' do
|
102
136
|
path = 'bin/image_optim'
|
103
137
|
with_env 'IMAGE_OPTIM_BIN', path do
|
104
138
|
tmpdir = double(:tmpdir, :to_str => 'tmpdir')
|
@@ -137,11 +171,11 @@ describe ImageOptim::BinResolver do
|
|
137
171
|
end
|
138
172
|
|
139
173
|
{
|
140
|
-
'some/path/
|
174
|
+
'some/path/does/not/exist' => 'doesn\'t exist',
|
141
175
|
'.' => 'is not a file',
|
142
176
|
__FILE__ => 'is not executable',
|
143
177
|
}.each do |path, error_message|
|
144
|
-
it "
|
178
|
+
it "raises when bin specified in ENV #{error_message}" do
|
145
179
|
with_env 'IMAGE_OPTIM_BIN', path do
|
146
180
|
expect(FSPath).not_to receive(:temp_dir)
|
147
181
|
expect(resolver).not_to receive(:at_exit)
|
@@ -159,7 +193,7 @@ describe ImageOptim::BinResolver do
|
|
159
193
|
end
|
160
194
|
end
|
161
195
|
|
162
|
-
it '
|
196
|
+
it 'resolves bin only once, but checks every time' do
|
163
197
|
with_env 'LS_BIN', nil do
|
164
198
|
expect(resolver).to receive(:full_path).once.with(:ls) do
|
165
199
|
sleep 0.1
|
@@ -182,7 +216,7 @@ describe ImageOptim::BinResolver do
|
|
182
216
|
end
|
183
217
|
end
|
184
218
|
|
185
|
-
it '
|
219
|
+
it 'raises if did not got bin version' do
|
186
220
|
bin = Bin.new(:pngcrush, '/bin/pngcrush')
|
187
221
|
allow(bin).to receive(:version).and_return(nil)
|
188
222
|
|
@@ -193,7 +227,7 @@ describe ImageOptim::BinResolver do
|
|
193
227
|
end
|
194
228
|
end
|
195
229
|
|
196
|
-
it '
|
230
|
+
it 'raises on detection of problematic version' do
|
197
231
|
bin = Bin.new(:pngcrush, '/bin/pngcrush')
|
198
232
|
allow(bin).to receive(:version).and_return(SimpleVersion.new('1.7.60'))
|
199
233
|
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'image_optim/cmd'
|
3
|
+
|
4
|
+
describe ImageOptim::Cmd do
|
5
|
+
before do
|
6
|
+
stub_const('Cmd', ImageOptim::Cmd)
|
7
|
+
end
|
8
|
+
|
9
|
+
def expect_int_exception(&block)
|
10
|
+
expect(&block).to raise_error(SignalException) do |error|
|
11
|
+
expect(error.message.to_s).to match(/INT|#{Signal.list['INT']}/)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe :run do
|
16
|
+
it 'calls system and returns result' do
|
17
|
+
status = double
|
18
|
+
expect(Cmd).to receive(:system).with('cmd', 'arg').and_return(status)
|
19
|
+
allow(Cmd).to receive(:check_status!)
|
20
|
+
expect(Cmd.run('cmd', 'arg')).to eq(status)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns process success status' do
|
24
|
+
expect(Cmd.run('sh -c exit\ 0')).to eq(true)
|
25
|
+
expect($CHILD_STATUS.exitstatus).to eq(0)
|
26
|
+
|
27
|
+
expect(Cmd.run('sh -c exit\ 1')).to eq(false)
|
28
|
+
expect($CHILD_STATUS.exitstatus).to eq(1)
|
29
|
+
|
30
|
+
expect(Cmd.run('sh -c exit\ 66')).to eq(false)
|
31
|
+
expect($CHILD_STATUS.exitstatus).to eq(66)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'raises SignalException if process terminates after signal' do
|
35
|
+
expect_int_exception do
|
36
|
+
Cmd.run('kill -s INT $$')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe :capture do
|
42
|
+
it 'calls ` and returns result' do
|
43
|
+
output = double
|
44
|
+
expect(Cmd).to receive(:`).with('cmd arg arg+').and_return(output)
|
45
|
+
allow(Cmd).to receive(:check_status!)
|
46
|
+
expect(Cmd.capture('cmd arg arg+')).to eq(output)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns output' do
|
50
|
+
expect(Cmd.capture('echo test')).to eq("test\n")
|
51
|
+
expect($CHILD_STATUS.exitstatus).to eq(0)
|
52
|
+
|
53
|
+
expect(Cmd.capture('printf more; sh -c exit\ 1')).to eq('more')
|
54
|
+
expect($CHILD_STATUS.exitstatus).to eq(1)
|
55
|
+
|
56
|
+
expect(Cmd.capture('sh -c exit\ 66')).to eq('')
|
57
|
+
expect($CHILD_STATUS.exitstatus).to eq(66)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'raises SignalException if process terminates after signal' do
|
61
|
+
expect_int_exception do
|
62
|
+
Cmd.capture('kill -s INT $$')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|