image_optim 0.18.0 → 0.19.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/.travis.yml +14 -17
- data/CHANGELOG.markdown +10 -0
- data/README.markdown +13 -3
- data/image_optim.gemspec +3 -3
- data/lib/image_optim.rb +16 -31
- data/lib/image_optim/bin_resolver.rb +3 -1
- data/lib/image_optim/bin_resolver/bin.rb +33 -24
- data/lib/image_optim/config.rb +5 -0
- data/lib/image_optim/runner/option_parser.rb +12 -5
- data/lib/image_optim/worker.rb +10 -62
- data/lib/image_optim/worker/class_methods.rb +89 -0
- data/lib/image_optim/worker/gifsicle.rb +25 -2
- data/lib/image_optim/worker/jpegrecompress.rb +44 -0
- data/lib/image_optim/worker/optipng.rb +1 -1
- data/script/update_worker_options_in_readme +6 -7
- data/script/worker_analysis +15 -5
- data/script/worker_analysis.haml +31 -10
- data/spec/image_optim/bin_resolver_spec.rb +52 -20
- data/spec/image_optim/worker_spec.rb +139 -0
- data/spec/image_optim_spec.rb +35 -53
- metadata +9 -7
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'image_optim/worker'
|
3
|
+
require 'image_optim/bin_resolver'
|
3
4
|
|
4
5
|
describe ImageOptim::Worker do
|
5
6
|
before do
|
6
7
|
stub_const('Worker', ImageOptim::Worker)
|
8
|
+
stub_const('BinResolver', ImageOptim::BinResolver)
|
7
9
|
end
|
8
10
|
|
9
11
|
describe :optimize do
|
@@ -17,4 +19,141 @@ describe ImageOptim::Worker do
|
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
22
|
+
describe :create_all_by_format do
|
23
|
+
it 'passes arguments to create_all' do
|
24
|
+
image_optim = double
|
25
|
+
options_proc = proc{ true }
|
26
|
+
|
27
|
+
expect(Worker).to receive(:create_all) do |arg, &block|
|
28
|
+
expect(arg).to eq(image_optim)
|
29
|
+
expect(block).to eq(options_proc)
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
|
33
|
+
Worker.create_all_by_format(image_optim, &options_proc)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'create hash by format' do
|
37
|
+
workers = [
|
38
|
+
double(:image_formats => [:a]),
|
39
|
+
double(:image_formats => [:a, :b]),
|
40
|
+
double(:image_formats => [:b, :c]),
|
41
|
+
]
|
42
|
+
|
43
|
+
expect(Worker).to receive(:create_all).and_return(workers)
|
44
|
+
|
45
|
+
worker_by_format = {
|
46
|
+
:a => [workers[0], workers[1]],
|
47
|
+
:b => [workers[1], workers[2]],
|
48
|
+
:c => [workers[2]],
|
49
|
+
}
|
50
|
+
|
51
|
+
expect(Worker.create_all_by_format(double)).to eq(worker_by_format)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe :create_all do
|
56
|
+
def worker_double(override = {})
|
57
|
+
stubs = {:resolve_used_bins! => nil, :run_order => 0}.merge(override)
|
58
|
+
instance_double(Worker, stubs)
|
59
|
+
end
|
60
|
+
|
61
|
+
let(:image_optim){ double(:allow_lossy => false) }
|
62
|
+
|
63
|
+
it 'creates all workers for which options_proc returns true' do
|
64
|
+
workers = Array.new(3){ worker_double }
|
65
|
+
klasses = workers.map{ |worker| double(:init => worker) }
|
66
|
+
options_proc = proc{ |klass| klass != klasses[1] ? {} : false }
|
67
|
+
|
68
|
+
allow(Worker).to receive(:klasses).and_return(klasses)
|
69
|
+
|
70
|
+
expect(Worker.create_all(image_optim, &options_proc)).
|
71
|
+
to eq([workers[0], workers[2]])
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'handles workers initializing multiple instances' do
|
75
|
+
workers = [
|
76
|
+
worker_double,
|
77
|
+
[worker_double, worker_double, worker_double],
|
78
|
+
worker_double,
|
79
|
+
]
|
80
|
+
klasses = workers.map{ |worker| double(:init => worker) }
|
81
|
+
|
82
|
+
allow(Worker).to receive(:klasses).and_return(klasses)
|
83
|
+
|
84
|
+
expect(Worker.create_all(image_optim){ {} }).
|
85
|
+
to eq(workers.flatten)
|
86
|
+
end
|
87
|
+
|
88
|
+
describe 'with missing workers' do
|
89
|
+
let(:workers) do
|
90
|
+
Array.new(3) do |i|
|
91
|
+
worker = worker_double
|
92
|
+
unless i == 1
|
93
|
+
allow(worker).to receive(:resolve_used_bins!).
|
94
|
+
and_raise(BinResolver::BinNotFound, "not found #{i}")
|
95
|
+
end
|
96
|
+
worker
|
97
|
+
end
|
98
|
+
end
|
99
|
+
let(:klasses){ workers.map{ |worker| double(:init => worker) } }
|
100
|
+
|
101
|
+
before do
|
102
|
+
allow(Worker).to receive(:klasses).and_return(klasses)
|
103
|
+
end
|
104
|
+
|
105
|
+
describe 'if skip_missing_workers is true' do
|
106
|
+
define :bin_not_found do |message|
|
107
|
+
match do |error|
|
108
|
+
error.is_a?(BinResolver::BinNotFound) && error.message == message
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'shows warnings and returns resolved workers ' do
|
113
|
+
allow(image_optim).to receive(:skip_missing_workers).and_return(true)
|
114
|
+
|
115
|
+
expect(Worker).to receive(:warn).
|
116
|
+
once.with(bin_not_found('not found 0'))
|
117
|
+
expect(Worker).to receive(:warn).
|
118
|
+
once.with(bin_not_found('not found 2'))
|
119
|
+
|
120
|
+
expect(Worker.create_all(image_optim){ {} }).
|
121
|
+
to eq([workers[1]])
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe 'if skip_missing_workers is false' do
|
126
|
+
it 'fails with a joint exception' do
|
127
|
+
allow(image_optim).to receive(:skip_missing_workers).and_return(false)
|
128
|
+
|
129
|
+
expect do
|
130
|
+
Worker.create_all(image_optim){ {} }
|
131
|
+
end.to raise_error(BinResolver::Error, /not found 0\nnot found 2/)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'orders workers by run_order' do
|
137
|
+
image_optim = double(:allow_lossy => false)
|
138
|
+
run_orders = [10, -10, 0, 0, 0, 10, -10]
|
139
|
+
workers = run_orders.map do |run_order|
|
140
|
+
worker_double(:run_order => run_order)
|
141
|
+
end
|
142
|
+
klasses_list = workers.map{ |worker| double(:init => worker) }
|
143
|
+
|
144
|
+
[
|
145
|
+
klasses_list,
|
146
|
+
klasses_list.reverse,
|
147
|
+
klasses_list.shuffle,
|
148
|
+
].each do |klasses|
|
149
|
+
allow(Worker).to receive(:klasses).and_return(klasses)
|
150
|
+
|
151
|
+
expected_order = klasses.map(&:init).sort_by.with_index do |worker, i|
|
152
|
+
[worker.run_order, i]
|
153
|
+
end
|
154
|
+
|
155
|
+
expect(Worker.create_all(image_optim){ {} }).to eq(expected_order)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
20
159
|
end
|
data/spec/image_optim_spec.rb
CHANGED
@@ -28,36 +28,6 @@ describe ImageOptim do
|
|
28
28
|
allow(ImageOptim::Config).to receive(:local).and_return({})
|
29
29
|
end
|
30
30
|
|
31
|
-
describe 'workers' do
|
32
|
-
they 'are ordered by run_order' do
|
33
|
-
image_optim = ImageOptim.new
|
34
|
-
original_klasses = ImageOptim::Worker.klasses
|
35
|
-
formats = original_klasses.map do |klass|
|
36
|
-
klass.new(image_optim, {}).image_formats
|
37
|
-
end.flatten.uniq
|
38
|
-
|
39
|
-
[
|
40
|
-
original_klasses,
|
41
|
-
original_klasses.to_a.reverse,
|
42
|
-
original_klasses.to_a.shuffle,
|
43
|
-
].each do |klasses|
|
44
|
-
expect(ImageOptim::Worker).to receive(:klasses).and_return(klasses)
|
45
|
-
|
46
|
-
image_optim = ImageOptim.new
|
47
|
-
|
48
|
-
formats.each do |format|
|
49
|
-
path = ImageOptim::ImagePath.new("test.#{format}")
|
50
|
-
expect(path).to receive(:format).and_return(format)
|
51
|
-
|
52
|
-
workers = image_optim.workers_for_image(path)
|
53
|
-
expect(workers).to eq(workers.sort_by.with_index do |worker, i|
|
54
|
-
[worker.run_order, i]
|
55
|
-
end)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
31
|
disable_all_workers = Hash[ImageOptim::Worker.klasses.map do |klass|
|
62
32
|
[klass.bin_sym, false]
|
63
33
|
end]
|
@@ -65,8 +35,15 @@ describe ImageOptim do
|
|
65
35
|
ImageOptim::Worker.klasses.each do |worker_klass|
|
66
36
|
describe "#{worker_klass.bin_sym} worker" do
|
67
37
|
it 'optimizes at least one test image' do
|
68
|
-
options = disable_all_workers.
|
38
|
+
options = disable_all_workers.dup
|
39
|
+
options.merge!(worker_klass.bin_sym => true)
|
40
|
+
options.merge!(:skip_missing_workers => false)
|
41
|
+
|
69
42
|
image_optim = ImageOptim.new(options)
|
43
|
+
if Array(worker_klass.init(image_optim)).empty?
|
44
|
+
image_optim = ImageOptim.new(options.merge(:allow_lossy => true))
|
45
|
+
end
|
46
|
+
|
70
47
|
expect(test_images.any? do |original|
|
71
48
|
image_optim.optimize_image(temp_copy(original))
|
72
49
|
end).to be true
|
@@ -123,14 +100,14 @@ describe ImageOptim do
|
|
123
100
|
match{ |actual| actual.size < expected.size }
|
124
101
|
end
|
125
102
|
|
126
|
-
define :
|
103
|
+
define :be_similar_to do |expected, max_difference|
|
127
104
|
match do |actual|
|
128
105
|
@diff = nrmse(actual, expected)
|
129
|
-
@diff
|
106
|
+
@diff <= max_difference
|
130
107
|
end
|
131
108
|
failure_message do |actual|
|
132
|
-
"expected #{actual} to
|
133
|
-
"normalized root-mean-square error of #{@diff}"
|
109
|
+
"expected #{actual} to have at most #{max_difference} difference from "\
|
110
|
+
"#{expected}, got normalized root-mean-square error of #{@diff}"
|
134
111
|
end
|
135
112
|
end
|
136
113
|
|
@@ -138,24 +115,29 @@ describe ImageOptim do
|
|
138
115
|
rotated = images_dir / 'orient/original.jpg'
|
139
116
|
rotate_images = images_dir.glob('orient/?.jpg')
|
140
117
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
118
|
+
base_options = {:skip_missing_workers => false}
|
119
|
+
[
|
120
|
+
['lossless', base_options, 0],
|
121
|
+
['lossy', base_options.merge(:allow_lossy => true), 0.01],
|
122
|
+
].each do |type, options, max_difference|
|
123
|
+
image_optim = ImageOptim.new(options)
|
124
|
+
describe type do
|
125
|
+
copies = test_images.map{ |image| temp_copy(image) }
|
126
|
+
pairs = image_optim.optimize_images(copies)
|
127
|
+
test_images.zip(*pairs.transpose).each do |original, copy, optimized|
|
128
|
+
it "optimizes #{original.relative_path_from(root_dir)}" do
|
129
|
+
expect(copy).to have_same_data_as(original)
|
130
|
+
|
131
|
+
expect(optimized).not_to be_nil
|
132
|
+
expect(optimized).to be_a(ImageOptim::ImagePath::Optimized)
|
133
|
+
expect(optimized).to have_size
|
134
|
+
expect(optimized).to be_smaller_than(original)
|
135
|
+
expect(optimized).not_to have_same_data_as(original)
|
136
|
+
|
137
|
+
compare_to = rotate_images.include?(original) ? rotated : original
|
138
|
+
expect(optimized).to be_similar_to(compare_to, max_difference)
|
139
|
+
end
|
140
|
+
end
|
159
141
|
end
|
160
142
|
end
|
161
143
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: image_optim
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.19.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Kuchin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-11-
|
11
|
+
date: 2014-11-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fspath
|
@@ -98,14 +98,14 @@ dependencies:
|
|
98
98
|
requirements:
|
99
99
|
- - ~>
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version: '0.
|
101
|
+
version: '0.2'
|
102
102
|
type: :development
|
103
103
|
prerelease: false
|
104
104
|
version_requirements: !ruby/object:Gem::Requirement
|
105
105
|
requirements:
|
106
106
|
- - ~>
|
107
107
|
- !ruby/object:Gem::Version
|
108
|
-
version: '0.
|
108
|
+
version: '0.2'
|
109
109
|
- !ruby/object:Gem::Dependency
|
110
110
|
name: rspec
|
111
111
|
requirement: !ruby/object:Gem::Requirement
|
@@ -189,9 +189,11 @@ files:
|
|
189
189
|
- lib/image_optim/true_false_nil.rb
|
190
190
|
- lib/image_optim/worker.rb
|
191
191
|
- lib/image_optim/worker/advpng.rb
|
192
|
+
- lib/image_optim/worker/class_methods.rb
|
192
193
|
- lib/image_optim/worker/gifsicle.rb
|
193
194
|
- lib/image_optim/worker/jhead.rb
|
194
195
|
- lib/image_optim/worker/jpegoptim.rb
|
196
|
+
- lib/image_optim/worker/jpegrecompress.rb
|
195
197
|
- lib/image_optim/worker/jpegtran.rb
|
196
198
|
- lib/image_optim/worker/optipng.rb
|
197
199
|
- lib/image_optim/worker/pngcrush.rb
|
@@ -261,9 +263,9 @@ rubyforge_project: image_optim
|
|
261
263
|
rubygems_version: 2.4.1
|
262
264
|
signing_key:
|
263
265
|
specification_version: 4
|
264
|
-
summary: Optimize (lossless compress) images (jpeg, png, gif, svg)
|
265
|
-
utilities (advpng, gifsicle, jhead, jpegoptim, jpegrescan,
|
266
|
-
pngout, pngquant, svgo)
|
266
|
+
summary: Optimize (lossless compress, optionally lossy) images (jpeg, png, gif, svg)
|
267
|
+
using external utilities (advpng, gifsicle, jhead, jpeg-recompress, jpegoptim, jpegrescan,
|
268
|
+
jpegtran, optipng, pngcrush, pngout, pngquant, svgo)
|
267
269
|
test_files:
|
268
270
|
- spec/image_optim/bin_resolver/comparable_condition_spec.rb
|
269
271
|
- spec/image_optim/bin_resolver/simple_version_spec.rb
|