image_optim_pack 0.1.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 +15 -0
- data/.gitignore +7 -0
- data/.rubocop.yml +51 -0
- data/.travis.yml +22 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +27 -0
- data/Makefile +312 -0
- data/README.markdown +77 -0
- data/Vagrantfile +13 -0
- data/acknowledgements/7z.txt +56 -0
- data/acknowledgements/advancecomp.txt +674 -0
- data/acknowledgements/bmp2png.txt +8 -0
- data/acknowledgements/cexcept.txt +8 -0
- data/acknowledgements/gifread.txt +14 -0
- data/acknowledgements/gifsicle.txt +343 -0
- data/acknowledgements/jhead.txt +12 -0
- data/acknowledgements/jpegoptim.txt +339 -0
- data/acknowledgements/libjpeg.txt +64 -0
- data/acknowledgements/libpng.txt +90 -0
- data/acknowledgements/optipng-authors.txt +26 -0
- data/acknowledgements/optipng.txt +21 -0
- data/acknowledgements/pngcrush.txt +35 -0
- data/acknowledgements/pngquant.txt +56 -0
- data/acknowledgements/zlib.txt +34 -0
- data/acknowledgements/zopfli-contributors.txt +6 -0
- data/acknowledgements/zopfli.txt +201 -0
- data/image_optim_pack.gemspec +26 -0
- data/lib/image_optim/pack.rb +110 -0
- data/lib/image_optim_pack.rb +1 -0
- data/script/livecheck +72 -0
- data/script/run +26 -0
- data/script/update_versions +17 -0
- data/script/vm_init +30 -0
- data/spec/image_optim/pack_spec.rb +114 -0
- data/spec/image_optim_spec.rb +109 -0
- data/spec/spec_helper.rb +0 -0
- data/vendor/darwin/i386/advpng +0 -0
- data/vendor/darwin/i386/gifsicle +0 -0
- data/vendor/darwin/i386/jhead +0 -0
- data/vendor/darwin/i386/jpegoptim +0 -0
- data/vendor/darwin/i386/jpegtran +0 -0
- data/vendor/darwin/i386/libjpeg.dylib +0 -0
- data/vendor/darwin/i386/libpng.dylib +0 -0
- data/vendor/darwin/i386/libz.dylib +0 -0
- data/vendor/darwin/i386/optipng +0 -0
- data/vendor/darwin/i386/pngcrush +0 -0
- data/vendor/darwin/i386/pngquant +0 -0
- data/vendor/darwin/x86_64/advpng +0 -0
- data/vendor/darwin/x86_64/gifsicle +0 -0
- data/vendor/darwin/x86_64/jhead +0 -0
- data/vendor/darwin/x86_64/jpegoptim +0 -0
- data/vendor/darwin/x86_64/jpegtran +0 -0
- data/vendor/darwin/x86_64/libjpeg.dylib +0 -0
- data/vendor/darwin/x86_64/libpng.dylib +0 -0
- data/vendor/darwin/x86_64/libz.dylib +0 -0
- data/vendor/darwin/x86_64/optipng +0 -0
- data/vendor/darwin/x86_64/pngcrush +0 -0
- data/vendor/darwin/x86_64/pngquant +0 -0
- data/vendor/linux/i686/advpng +0 -0
- data/vendor/linux/i686/gifsicle +0 -0
- data/vendor/linux/i686/jhead +0 -0
- data/vendor/linux/i686/jpegoptim +0 -0
- data/vendor/linux/i686/jpegtran +0 -0
- data/vendor/linux/i686/libjpeg.so +0 -0
- data/vendor/linux/i686/libpng.so +0 -0
- data/vendor/linux/i686/libz.so +0 -0
- data/vendor/linux/i686/optipng +0 -0
- data/vendor/linux/i686/pngcrush +0 -0
- data/vendor/linux/i686/pngquant +0 -0
- data/vendor/linux/x86_64/advpng +0 -0
- data/vendor/linux/x86_64/gifsicle +0 -0
- data/vendor/linux/x86_64/jhead +0 -0
- data/vendor/linux/x86_64/jpegoptim +0 -0
- data/vendor/linux/x86_64/jpegtran +0 -0
- data/vendor/linux/x86_64/libjpeg.so +0 -0
- data/vendor/linux/x86_64/libpng.so +0 -0
- data/vendor/linux/x86_64/libz.so +0 -0
- data/vendor/linux/x86_64/optipng +0 -0
- data/vendor/linux/x86_64/pngcrush +0 -0
- data/vendor/linux/x86_64/pngquant +0 -0
- metadata +196 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |s|
|
|
4
|
+
s.name = 'image_optim_pack'
|
|
5
|
+
s.version = '0.1.0'
|
|
6
|
+
s.summary = %q{Precompiled binaries for image_optim: advpng, gifsicle, jhead, jpegoptim, jpegtran, optipng, pngcrush, pngquant}
|
|
7
|
+
s.homepage = "http://github.com/toy/#{s.name}"
|
|
8
|
+
s.authors = ['Ivan Kuchin']
|
|
9
|
+
s.license = 'MIT'
|
|
10
|
+
|
|
11
|
+
s.rubyforge_project = s.name
|
|
12
|
+
|
|
13
|
+
s.files = `git ls-files`.split("\n")
|
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
16
|
+
s.require_paths = %w[lib]
|
|
17
|
+
|
|
18
|
+
s.add_dependency 'image_optim', '~> 0.18'
|
|
19
|
+
s.add_dependency 'fspath', '~> 2.1'
|
|
20
|
+
|
|
21
|
+
s.add_development_dependency 'versionomy', '~> 0.4.4'
|
|
22
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
|
23
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9.3')
|
|
24
|
+
s.add_development_dependency 'rubocop', '~> 0.26.0'
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
require 'fspath'
|
|
2
|
+
require 'image_optim/bin_resolver/bin'
|
|
3
|
+
|
|
4
|
+
class ImageOptim
|
|
5
|
+
# Handle selection of directory with binaries most suitable for current
|
|
6
|
+
# operating system and architecture
|
|
7
|
+
module Pack
|
|
8
|
+
# Path to binary, last two parts are expect to be os/arch
|
|
9
|
+
class Path
|
|
10
|
+
# Path provided to initialize as FSPath
|
|
11
|
+
attr_reader :path
|
|
12
|
+
|
|
13
|
+
# Intended os
|
|
14
|
+
attr_reader :os
|
|
15
|
+
|
|
16
|
+
# Inteded architecture
|
|
17
|
+
attr_reader :arch
|
|
18
|
+
|
|
19
|
+
# Receive path, use last part for arch and last but one part for os
|
|
20
|
+
def initialize(path)
|
|
21
|
+
@path = FSPath(path)
|
|
22
|
+
@os = @path.dirname.basename.to_s
|
|
23
|
+
@arch = @path.basename.to_s
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Return path converted to string
|
|
27
|
+
def to_s
|
|
28
|
+
path.to_s
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Cached array of BinResolver::Bin instances for each bin
|
|
32
|
+
def bins
|
|
33
|
+
@bins ||= bin_paths.map do |bin_path|
|
|
34
|
+
BinResolver::Bin.new(bin_path.basename.to_s, bin_path.to_s)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Return true if all bins can execute and return version
|
|
39
|
+
def all_bins_working?
|
|
40
|
+
bins.all?(&:version)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Return true if all bins can't execute and return version
|
|
44
|
+
def all_bins_failing?
|
|
45
|
+
bins.none?(&:version)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# List of bins which can execute and return version
|
|
49
|
+
def working_bins
|
|
50
|
+
bins.select(&:version)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# List of bins which can't execute and return version
|
|
54
|
+
def failing_bins
|
|
55
|
+
bins.reject(&:version)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
# All children except those starting with 'lib'
|
|
61
|
+
def bin_paths
|
|
62
|
+
path.children.reject{ |child| child.basename.to_s =~ /^lib/ }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# downcased `uname -s`
|
|
67
|
+
OS = `uname -s`.strip.downcase
|
|
68
|
+
|
|
69
|
+
# downcased `uname -m`
|
|
70
|
+
ARCH = `uname -m`.strip.downcase
|
|
71
|
+
|
|
72
|
+
# Path to vendor at root of image_optim_pack
|
|
73
|
+
VENDOR_PATH = FSPath('../../../vendor').expand_path(__FILE__)
|
|
74
|
+
|
|
75
|
+
# List of paths
|
|
76
|
+
PATHS = VENDOR_PATH.glob('*/*').map{ |path| Path.new(path) }
|
|
77
|
+
|
|
78
|
+
class << self
|
|
79
|
+
# Return path to directory with binaries
|
|
80
|
+
# Yields debug messages if block given
|
|
81
|
+
def path
|
|
82
|
+
ordered_by_os_arch_match.find do |path|
|
|
83
|
+
yield "image_optim_pack: #{debug_message(path)}" if block_given?
|
|
84
|
+
path.all_bins_working?
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
# Order by match of os and architecture
|
|
91
|
+
def ordered_by_os_arch_match
|
|
92
|
+
PATHS.sort_by do |path|
|
|
93
|
+
[path.os == OS ? 0 : 1, path.arch == ARCH ? 0 : 1]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Messages based on success of getting versions of bins
|
|
98
|
+
def debug_message(path)
|
|
99
|
+
case
|
|
100
|
+
when path.all_bins_working?
|
|
101
|
+
"all bins from #{path} worked"
|
|
102
|
+
when path.all_bins_failing?
|
|
103
|
+
"all bins from #{path} failed"
|
|
104
|
+
else
|
|
105
|
+
"#{path.failing_bins.map(&:name).join(', ')} from #{path} failed"
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'image_optim/pack'
|
data/script/livecheck
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'net/http'
|
|
6
|
+
require 'versionomy'
|
|
7
|
+
|
|
8
|
+
bare = ARGV.delete('--bare')
|
|
9
|
+
|
|
10
|
+
unless [1, 2].include?(ARGV.length)
|
|
11
|
+
abort 'expected project name and optional current version'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def version(string)
|
|
15
|
+
Versionomy.parse(string)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
name = ARGV[0].downcase
|
|
19
|
+
config = YAML.load(DATA)[name]
|
|
20
|
+
abort "Livecheck for #{name} was not defined" unless config
|
|
21
|
+
|
|
22
|
+
uri = URI(config['url'])
|
|
23
|
+
version_regex = Regexp.new(config['regexp'])
|
|
24
|
+
current_version = ARGV[1] && version(ARGV[1])
|
|
25
|
+
|
|
26
|
+
data = Net::HTTP.get(uri)
|
|
27
|
+
versions = data.scan(version_regex).uniq.map{ |match| version(match[0]) }
|
|
28
|
+
abort 'No versions found' if versions.empty?
|
|
29
|
+
|
|
30
|
+
latest_version = versions.max
|
|
31
|
+
case
|
|
32
|
+
when bare
|
|
33
|
+
print latest_version
|
|
34
|
+
when current_version
|
|
35
|
+
if latest_version > current_version
|
|
36
|
+
puts "#{name} #{latest_version} > #{current_version}"
|
|
37
|
+
end
|
|
38
|
+
else
|
|
39
|
+
puts "#{name} #{latest_version}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
__END__
|
|
43
|
+
advancecomp:
|
|
44
|
+
url: http://sourceforge.net/projects/advancemame/rss?path=/advancecomp
|
|
45
|
+
regexp: /advancecomp/(\d+(?:\.\d+)*)/
|
|
46
|
+
gifsicle:
|
|
47
|
+
url: http://www.lcdf.org/gifsicle/
|
|
48
|
+
regexp: gifsicle-(\d+(?:\.\d+)*)\.tar\.gz
|
|
49
|
+
jhead:
|
|
50
|
+
url: http://www.sentex.net/~mwandel/jhead/
|
|
51
|
+
regexp: jhead-(\d+(?:\.\d+)*)
|
|
52
|
+
jpegoptim:
|
|
53
|
+
url: http://www.kokkonen.net/tjko/src/
|
|
54
|
+
regexp: jpegoptim-(\d+(?:\.\d+)*)
|
|
55
|
+
libjpeg:
|
|
56
|
+
url: http://www.ijg.org/files/
|
|
57
|
+
regexp: jpegsrc.v(.*?).tar.gz
|
|
58
|
+
libpng:
|
|
59
|
+
url: http://sourceforge.net/projects/libpng/rss
|
|
60
|
+
regexp: /libpng\d*/(\d+(?:\.\d+)*)/
|
|
61
|
+
libz:
|
|
62
|
+
url: http://sourceforge.net/projects/libpng/rss?path=/zlib
|
|
63
|
+
regexp: /zlib/(\d+(?:\.\d+)*)/
|
|
64
|
+
optipng:
|
|
65
|
+
url: http://sourceforge.net/projects/optipng/rss
|
|
66
|
+
regexp: /OptiPNG/optipng-(\d+(?:\.\d+)*)/
|
|
67
|
+
pngcrush:
|
|
68
|
+
url: http://sourceforge.net/projects/pmt/rss?path=/pngcrush
|
|
69
|
+
regexp: /pngcrush/(\d+(?:\.\d+)*)/
|
|
70
|
+
pngquant:
|
|
71
|
+
url: http://pngquant.org/releases.html
|
|
72
|
+
regexp: pngquant-([0-9.]+)-src.tar.bz2
|
data/script/run
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -xe
|
|
4
|
+
|
|
5
|
+
MAKE_COMMAND='make all -j'
|
|
6
|
+
|
|
7
|
+
for arg in "$@"; do
|
|
8
|
+
if [[ "$arg" =~ ^([A-Z_]+)=(.*)$ ]]; then
|
|
9
|
+
eval "$(printf %q=%q "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}")"
|
|
10
|
+
fi
|
|
11
|
+
done
|
|
12
|
+
|
|
13
|
+
if [ "$(uname -s | tr A-Z a-z)" != 'darwin' ]; then
|
|
14
|
+
echo 'expected to run on Mac OS X'
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
for arch in i386 x86_64; do
|
|
19
|
+
$MAKE_COMMAND ARCH=$arch
|
|
20
|
+
done
|
|
21
|
+
|
|
22
|
+
for vm in linux_{i686,x86_64}; do
|
|
23
|
+
vagrant up $vm
|
|
24
|
+
vagrant ssh $vm -c "cd /vagrant; $MAKE_COMMAND"
|
|
25
|
+
[ -n "$NO_HALT" ] || vagrant halt
|
|
26
|
+
done
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'English'
|
|
4
|
+
|
|
5
|
+
tty = $stdout.tty?
|
|
6
|
+
|
|
7
|
+
ARGF.each do |line|
|
|
8
|
+
if (name = line[/^([A-Z]+)_VER *:= *.*/, 1])
|
|
9
|
+
$stderr << name << ' := ' unless tty
|
|
10
|
+
latest_version = `script/livecheck --bare #{name}`.strip
|
|
11
|
+
abort unless $CHILD_STATUS.success?
|
|
12
|
+
$stderr << latest_version << "\n" unless tty
|
|
13
|
+
$stdout << "#{name}_VER := #{latest_version}\n"
|
|
14
|
+
else
|
|
15
|
+
$stdout << line
|
|
16
|
+
end
|
|
17
|
+
end
|
data/script/vm_init
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
present(){
|
|
4
|
+
local missing=0
|
|
5
|
+
for name in "$@"; do
|
|
6
|
+
if command -v "$name" >/dev/null 2>&1; then
|
|
7
|
+
echo "got $name"
|
|
8
|
+
else
|
|
9
|
+
echo "no $name"
|
|
10
|
+
missing=1
|
|
11
|
+
fi
|
|
12
|
+
done
|
|
13
|
+
[ $missing -eq 0 ] || return 127
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if ! present make wget gcc g++ chrpath perl pkg-config automake; then
|
|
17
|
+
if present apt-get; then
|
|
18
|
+
apt-get -y install make wget gcc g++ chrpath perl pkg-config automake
|
|
19
|
+
elif present yum; then
|
|
20
|
+
yum -y install make wget gcc gcc-c++ chrpath perl pkg-config automake
|
|
21
|
+
else
|
|
22
|
+
echo 'unknown package manager'
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
if ! make --version 2>&1 | grep -q 'GNU Make'; then
|
|
28
|
+
echo 'non gnu make'
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'image_optim/pack'
|
|
3
|
+
|
|
4
|
+
describe ImageOptim::Pack do
|
|
5
|
+
before do
|
|
6
|
+
stub_const('Pack', ImageOptim::Pack)
|
|
7
|
+
stub_const('Path', Pack::Path)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe :path do
|
|
11
|
+
it 'returns path' do
|
|
12
|
+
expect(Pack.path).to be
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'returns instance of Path' do
|
|
16
|
+
expect(Pack.path).to be_an(Path)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'yields debug messages' do
|
|
20
|
+
expect{ |block| Pack.path(&block) }.
|
|
21
|
+
to yield_with_args(/^image_optim_pack: all bins .* worked$/)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe ImageOptim::Pack::Path do
|
|
26
|
+
describe :path do
|
|
27
|
+
it 'returns FSPath with path' do
|
|
28
|
+
expect(Path.new('path').path).to eq(FSPath('path'))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe :to_s do
|
|
33
|
+
it 'returns stringified path' do
|
|
34
|
+
expect(Path.new(Pathname('path')).to_s).to eq('path')
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe :os do
|
|
39
|
+
it 'returns last but one part of path' do
|
|
40
|
+
expect(Path.new('path/futureos/Kqbit').os).to eq('futureos')
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe :arch do
|
|
45
|
+
it 'returns last but one part of path' do
|
|
46
|
+
expect(Path.new('path/futureos/Kqbit').arch).to eq('Kqbit')
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe :bins do
|
|
51
|
+
subject{ Pack.path.bins }
|
|
52
|
+
|
|
53
|
+
define :be_non_libraries do
|
|
54
|
+
match do |bin|
|
|
55
|
+
bin.name !~ /^lib/
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it{ should be_an(Array) }
|
|
60
|
+
|
|
61
|
+
it{ should_not be_empty }
|
|
62
|
+
|
|
63
|
+
it{ should all(be_an(ImageOptim::BinResolver::Bin)) }
|
|
64
|
+
|
|
65
|
+
it{ should all(have_attributes(:version => be)) }
|
|
66
|
+
|
|
67
|
+
it{ should all(be_non_libraries) }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe 'bin helpers' do
|
|
71
|
+
def bins_with_versions(*versions)
|
|
72
|
+
versions.map do |version|
|
|
73
|
+
double(:version => version, :inspect => version ? 'good' : 'bad')
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def path_with_bins(*bins)
|
|
78
|
+
path = Path.new('path')
|
|
79
|
+
allow(path).to receive(:bins).and_return(bins)
|
|
80
|
+
path
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe 'all good bins' do
|
|
84
|
+
let(:bins){ bins_with_versions('1.3', '6.1.6') }
|
|
85
|
+
subject{ path_with_bins(*bins) }
|
|
86
|
+
|
|
87
|
+
it{ should be_all_bins_working }
|
|
88
|
+
it{ should_not be_all_bins_failing }
|
|
89
|
+
it{ should have_attributes(:working_bins => eq(bins)) }
|
|
90
|
+
it{ should have_attributes(:failing_bins => be_empty) }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe 'mixed good/bad bins' do
|
|
94
|
+
let(:bins){ bins_with_versions('1.3', nil, '6.1.6', nil) }
|
|
95
|
+
subject{ path_with_bins(*bins) }
|
|
96
|
+
|
|
97
|
+
it{ should_not be_all_bins_working }
|
|
98
|
+
it{ should_not be_all_bins_failing }
|
|
99
|
+
it{ should have_attributes(:working_bins => bins.select(&:version)) }
|
|
100
|
+
it{ should have_attributes(:failing_bins => bins.reject(&:version)) }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
describe 'all bad bins' do
|
|
104
|
+
let(:bins){ bins_with_versions(nil, nil) }
|
|
105
|
+
subject{ path_with_bins(*bins) }
|
|
106
|
+
|
|
107
|
+
it{ should_not be_all_bins_working }
|
|
108
|
+
it{ should be_all_bins_failing }
|
|
109
|
+
it{ should have_attributes(:working_bins => be_empty) }
|
|
110
|
+
it{ should have_attributes(:failing_bins => eq(bins)) }
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'image_optim'
|
|
3
|
+
require 'image_optim/cmd'
|
|
4
|
+
require 'tempfile'
|
|
5
|
+
require 'English'
|
|
6
|
+
|
|
7
|
+
describe ImageOptim do
|
|
8
|
+
def self.temp_copy(image)
|
|
9
|
+
image.temp_path.tap{ |path| image.copy(path) }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
before do
|
|
13
|
+
stub_const('Cmd', ImageOptim::Cmd)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def flatten_animation(image)
|
|
17
|
+
if image.format == :gif
|
|
18
|
+
flattened = image.temp_path
|
|
19
|
+
flatten_command = %W[
|
|
20
|
+
convert
|
|
21
|
+
#{image.to_s.shellescape}
|
|
22
|
+
-coalesce
|
|
23
|
+
-append
|
|
24
|
+
#{flattened.to_s.shellescape}
|
|
25
|
+
].join(' ')
|
|
26
|
+
expect(Cmd.run(flatten_command)).to be_truthy
|
|
27
|
+
flattened
|
|
28
|
+
else
|
|
29
|
+
image
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def nrmse(image_a, image_b)
|
|
34
|
+
coalesce_a = flatten_animation(image_a)
|
|
35
|
+
coalesce_b = flatten_animation(image_b)
|
|
36
|
+
nrmse_command = %W[
|
|
37
|
+
compare
|
|
38
|
+
-metric RMSE
|
|
39
|
+
#{coalesce_a.to_s.shellescape}
|
|
40
|
+
#{coalesce_b.to_s.shellescape}
|
|
41
|
+
/dev/null
|
|
42
|
+
2>&1
|
|
43
|
+
].join(' ')
|
|
44
|
+
output = Cmd.capture(nrmse_command)
|
|
45
|
+
if [0, 1].include?($CHILD_STATUS.exitstatus)
|
|
46
|
+
output[/\((\d+(\.\d+)?)\)/, 1].to_f
|
|
47
|
+
else
|
|
48
|
+
fail "compare #{image_a} with #{image_b} failed with `#{output}`"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
define :have_same_data_as do |expected|
|
|
53
|
+
match{ |actual| actual.binread == expected.binread }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
define :have_size do
|
|
57
|
+
match(&:size?)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
define :be_smaller_than do |expected|
|
|
61
|
+
match{ |actual| actual.size < expected.size }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
define :be_pixel_identical_to do |expected|
|
|
65
|
+
match do |actual|
|
|
66
|
+
@diff = nrmse(actual, expected)
|
|
67
|
+
@diff == 0
|
|
68
|
+
end
|
|
69
|
+
failure_message do |actual|
|
|
70
|
+
"expected #{actual} to be pixel identical to #{expected}, got "\
|
|
71
|
+
"normalized root-mean-square error of #{@diff}"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
image_optim = ImageOptim.new
|
|
76
|
+
|
|
77
|
+
# grab images from image_optim gem
|
|
78
|
+
image_optim_root = Gem.loaded_specs['image_optim'].gem_dir
|
|
79
|
+
images_dir = ImageOptim::ImagePath.new(image_optim_root) / 'spec/images'
|
|
80
|
+
test_images = images_dir.glob('**/*.*')
|
|
81
|
+
|
|
82
|
+
# select images which for which there are workers
|
|
83
|
+
test_images = test_images.select do |image|
|
|
84
|
+
image_optim.workers_for_image(image)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
rotated = images_dir / 'orient/original.jpg'
|
|
88
|
+
rotate_images = images_dir.glob('orient/?.jpg')
|
|
89
|
+
|
|
90
|
+
copies = test_images.map{ |image| temp_copy(image) }
|
|
91
|
+
original_by_copy = Hash[copies.zip(test_images)]
|
|
92
|
+
|
|
93
|
+
image_optim.optimize_images(copies) do |copy, optimized|
|
|
94
|
+
fail 'expected copy to not be nil' if copy.nil?
|
|
95
|
+
original = original_by_copy[copy]
|
|
96
|
+
|
|
97
|
+
it "optimizes #{original}" do
|
|
98
|
+
expect(copy).to have_same_data_as(original)
|
|
99
|
+
|
|
100
|
+
expect(optimized).not_to be_nil
|
|
101
|
+
expect(optimized).to have_size
|
|
102
|
+
expect(optimized).to be_smaller_than(original)
|
|
103
|
+
expect(optimized).not_to have_same_data_as(original)
|
|
104
|
+
|
|
105
|
+
compare_to = rotate_images.include?(original) ? rotated : original
|
|
106
|
+
expect(optimized).to be_pixel_identical_to(compare_to)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|