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.
@@ -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
@@ -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.merge(worker_klass.bin_sym => true)
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 :be_pixel_identical_to do |expected|
103
+ define :be_similar_to do |expected, max_difference|
127
104
  match do |actual|
128
105
  @diff = nrmse(actual, expected)
129
- @diff == 0
106
+ @diff <= max_difference
130
107
  end
131
108
  failure_message do |actual|
132
- "expected #{actual} to be pixel identical to #{expected}, got "\
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
- copies = test_images.map{ |image| temp_copy(image) }
142
- original_by_copy = Hash[copies.zip(test_images)]
143
-
144
- ImageOptim.optimize_images(copies) do |copy, optimized|
145
- fail 'expected copy to not be nil' if copy.nil?
146
- original = original_by_copy[copy]
147
-
148
- it "optimizes #{original.relative_path_from(root_dir)}" do
149
- expect(copy).to have_same_data_as(original)
150
-
151
- expect(optimized).not_to be_nil
152
- expect(optimized).to be_a(ImageOptim::ImagePath::Optimized)
153
- expect(optimized).to have_size
154
- expect(optimized).to be_smaller_than(original)
155
- expect(optimized).not_to have_same_data_as(original)
156
-
157
- compare_to = rotate_images.include?(original) ? rotated : original
158
- expect(optimized).to be_pixel_identical_to(compare_to)
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.18.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-01 00:00:00.000000000 Z
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.1'
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.1'
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) using external
265
- utilities (advpng, gifsicle, jhead, jpegoptim, jpegrescan, jpegtran, optipng, pngcrush,
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