image_optim 0.17.1 → 0.18.0
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 +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
|