lossfully 0.0.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.
- data/.bnsignore +19 -0
- data/.gitmodules +3 -0
- data/CHANGELOG +4 -0
- data/COPYING +674 -0
- data/README.rdoc +96 -0
- data/Rakefile +27 -0
- data/lib/lossfully/audio_file.rb +134 -0
- data/lib/lossfully/generator.rb +614 -0
- data/lib/lossfully/input_rules.rb +176 -0
- data/lib/lossfully/thread_pool.rb +162 -0
- data/lib/lossfully.rb +79 -0
- data/test/test_audio_file.rb +47 -0
- data/test/test_input_rules.rb +125 -0
- data/test/test_thread_pool.rb +77 -0
- data/version.txt +1 -0
- metadata +100 -0
@@ -0,0 +1,176 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2011 Don March
|
3
|
+
#
|
4
|
+
# This file is part of Lossfully.
|
5
|
+
#
|
6
|
+
# Lossfully is free software: you can redistribute it and/or modify it
|
7
|
+
# under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Lossfully is distributed in the hope that it will be useful, but
|
12
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program. If not, see
|
18
|
+
# <http://www.gnu.org/licenses/>.
|
19
|
+
#++
|
20
|
+
|
21
|
+
module Lossfully
|
22
|
+
LOSSLESS_TYPES = %w(wav flac wv sox).map(&:to_sym)
|
23
|
+
|
24
|
+
# The InputRules class wraps up conditions and provides a way to
|
25
|
+
# test if a file meets those conditions. It also allows the
|
26
|
+
# conditions to be sorted so that more restrictive conditions are
|
27
|
+
# tried before less restrictive. The sorting hopefully does what
|
28
|
+
# seems natural; it looks at first at the regexp, then the file type
|
29
|
+
# (as returned by soxi -t), the file extension, the bitrate
|
30
|
+
# threshold, and finally if a block is given. For example, the
|
31
|
+
# following encode rules are shown in the order that they would be
|
32
|
+
# tested against every file (even though the rules would be checked
|
33
|
+
# in this order even if the below encode statements were in a
|
34
|
+
# different order):
|
35
|
+
#
|
36
|
+
# encode [:mp3, 128, /bach/] do
|
37
|
+
# ...
|
38
|
+
# end
|
39
|
+
# encode [:mp3, 128, /bach/] => ...
|
40
|
+
# encode [:mp3, /bach/] => ...
|
41
|
+
# encode [:mp3, 128] => ...
|
42
|
+
# encode :mp3 => ...
|
43
|
+
# encode :lossy => ...
|
44
|
+
# encode :audio => ...
|
45
|
+
# encode :everything => ...
|
46
|
+
#
|
47
|
+
# It's obviously only a partial order; see the code for
|
48
|
+
# compare_strictness if you need to know exactly what it's doing.
|
49
|
+
#
|
50
|
+
class InputRules
|
51
|
+
include Comparable
|
52
|
+
|
53
|
+
def initialize array=[], &block
|
54
|
+
raise unless array.kind_of? Array
|
55
|
+
|
56
|
+
@block = block
|
57
|
+
|
58
|
+
array.each do |x|
|
59
|
+
@type = x if x.kind_of? Symbol
|
60
|
+
@max_bitrate = x if x.kind_of? Numeric
|
61
|
+
if x.kind_of? String
|
62
|
+
@extension = (x[0..0] == '.') || x== '' ? x : '.' + x
|
63
|
+
end
|
64
|
+
@regexp = x if x.kind_of? Regexp
|
65
|
+
end
|
66
|
+
@type ||= :everything
|
67
|
+
@max_bitrate ||= 0
|
68
|
+
@extension ||= ''
|
69
|
+
@regexp ||= //
|
70
|
+
end
|
71
|
+
|
72
|
+
attr_reader :block, :extension, :regexp, :type, :max_bitrate
|
73
|
+
|
74
|
+
def test file_or_path
|
75
|
+
file = if file_or_path.kind_of? AudioFile
|
76
|
+
file_or_path
|
77
|
+
else
|
78
|
+
AudioFile.new(file_or_path)
|
79
|
+
end
|
80
|
+
# unless file.is_audio?
|
81
|
+
# return false unless [:everything, :nonaudio].include?(@type)
|
82
|
+
# return false unless file.path =~ @regexp
|
83
|
+
# (return block.call(file.path)) if @block
|
84
|
+
# return true
|
85
|
+
# end
|
86
|
+
|
87
|
+
if @type != :everything
|
88
|
+
if [:audio, :lossy, :lossless].include? @type
|
89
|
+
return false unless file.is_audio?
|
90
|
+
end
|
91
|
+
|
92
|
+
if @type == :lossy
|
93
|
+
return false if LOSSLESS_TYPES.include? file.type
|
94
|
+
elsif @type == :lossless
|
95
|
+
return false unless LOSSLESS_TYPES.include? file.type
|
96
|
+
elsif @type == :nonaudio
|
97
|
+
return false if file.is_audio?
|
98
|
+
elsif @type != :audio
|
99
|
+
v = [:vorbis, :ogg]
|
100
|
+
return false unless (file.type == @type) ||
|
101
|
+
(v.include?(file.type) && v.include?(@type))
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
if @max_bitrate > 0
|
106
|
+
return false unless file.bitrate_kbps > @max_bitrate
|
107
|
+
end
|
108
|
+
|
109
|
+
if @extension != ''
|
110
|
+
return false unless File.extname(file.path) == @extension
|
111
|
+
end
|
112
|
+
|
113
|
+
if @regexp != //
|
114
|
+
return false unless file.path =~ @regexp
|
115
|
+
end
|
116
|
+
|
117
|
+
if @block
|
118
|
+
# return block.call(file.path)
|
119
|
+
# TODO: decide if this should be file or file.path
|
120
|
+
return block.call(file)
|
121
|
+
end
|
122
|
+
|
123
|
+
return true
|
124
|
+
end
|
125
|
+
|
126
|
+
# Order by strictness, which is the proper order to test things in
|
127
|
+
def <=> x
|
128
|
+
-1 * compare_strictness(x)
|
129
|
+
end
|
130
|
+
|
131
|
+
# return -1 if self is less strict, 1 if self is more strict
|
132
|
+
def compare_strictness x
|
133
|
+
return nil unless x.class == self.class
|
134
|
+
|
135
|
+
if @regexp != x.regexp
|
136
|
+
return -1 if @regexp == //
|
137
|
+
return 1 if x.regexp == //
|
138
|
+
return nil
|
139
|
+
end
|
140
|
+
|
141
|
+
if @type != x.type
|
142
|
+
return -1 if @type == :everything
|
143
|
+
return 1 if x.type == :everything
|
144
|
+
return -1 if @type == :audio
|
145
|
+
return 1 if x.type == :audio
|
146
|
+
# these don't have to be comparable since they're mutual exclusive
|
147
|
+
return -1 if @type == :nonaudio
|
148
|
+
return 1 if x.type == :nonaudio
|
149
|
+
return -1 if @type == :lossless
|
150
|
+
return 1 if x.type == :lossless
|
151
|
+
return -1 if @type == :lossy
|
152
|
+
return 1 if x.type == :lossy
|
153
|
+
return -1 * (@type.to_s <=> x.type.to_s)
|
154
|
+
end
|
155
|
+
|
156
|
+
if @extension != x.extension
|
157
|
+
return -1 if @extension == ''
|
158
|
+
return 1 if x.extension == ''
|
159
|
+
return -1 * (@extension <=> x.extension)
|
160
|
+
end
|
161
|
+
|
162
|
+
b = @max_bitrate <=> x.max_bitrate
|
163
|
+
return b unless b == 0
|
164
|
+
|
165
|
+
if @block || x.block
|
166
|
+
return nil if @block && x.block
|
167
|
+
return -1 if ! @block
|
168
|
+
return 1 if ! x.block
|
169
|
+
end
|
170
|
+
|
171
|
+
return 0
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
@@ -0,0 +1,162 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2011 Don March
|
3
|
+
#
|
4
|
+
# This file is part of Lossfully.
|
5
|
+
#
|
6
|
+
# Lossfully is free software: you can redistribute it and/or modify it
|
7
|
+
# under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Lossfully is distributed in the hope that it will be useful, but
|
12
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program. If not, see
|
18
|
+
# <http://www.gnu.org/licenses/>.
|
19
|
+
#++
|
20
|
+
|
21
|
+
require 'thread'
|
22
|
+
require 'timeout'
|
23
|
+
|
24
|
+
module Lossfully
|
25
|
+
|
26
|
+
# There's (at least) two ways to do this: 1) the Ruby Recipes way,
|
27
|
+
# which is to make a thread for every incoming task, but put it to
|
28
|
+
# sleep until there's room in the pool for another task, or 2) have a
|
29
|
+
# pool of threads that eat tasks from a queue. This implements (2),
|
30
|
+
# mainly because it seemed more fun to me. But also because it
|
31
|
+
# doesn't require the explicit use of Mutexes at all; it uses them,
|
32
|
+
# for the sake of sending signals with ConditionVaribles, but if those
|
33
|
+
# signals aren't received there would be a delay of at most 1 second.
|
34
|
+
#
|
35
|
+
# Another useful thing about this implementation is for the case when
|
36
|
+
# every task you anticipate adding to the ThreadPool of the same
|
37
|
+
# general form. Then then ThreadPool can be initialized with a block
|
38
|
+
# and you can just add objects to the task queue.
|
39
|
+
#
|
40
|
+
class ThreadPool
|
41
|
+
|
42
|
+
DEFAULT_BLOCK = lambda {|block, &blk| block = blk if block_given? ; block.call}
|
43
|
+
|
44
|
+
def initialize(max_size = 1, block=nil, &blk)
|
45
|
+
@running = true
|
46
|
+
@joining = false
|
47
|
+
|
48
|
+
@mutex = Mutex.new
|
49
|
+
@cv = ConditionVariable.new
|
50
|
+
@max_size = max_size
|
51
|
+
block = blk if block_given?
|
52
|
+
@block = block.nil? ? DEFAULT_BLOCK : block
|
53
|
+
@queue = Queue.new
|
54
|
+
@workers = []
|
55
|
+
@master = master_thread
|
56
|
+
@completed = 0
|
57
|
+
@total = 0
|
58
|
+
end
|
59
|
+
|
60
|
+
def process (block_or_item=nil, &blk)
|
61
|
+
block_or_item = blk if block_given?
|
62
|
+
if block_or_item.respond_to?(:call)
|
63
|
+
@queue << block_or_item
|
64
|
+
else
|
65
|
+
@queue << lambda { @block.call(block_or_item) }
|
66
|
+
end
|
67
|
+
# @mutex.synchronize { @total +=1 }
|
68
|
+
@total += 1
|
69
|
+
signal_master
|
70
|
+
end
|
71
|
+
|
72
|
+
def current
|
73
|
+
@total - @queue.size
|
74
|
+
end
|
75
|
+
|
76
|
+
attr_reader :max_size, :mutex, :completed, :total
|
77
|
+
alias :enq :process
|
78
|
+
alias :dispatch :process
|
79
|
+
|
80
|
+
def max_size=(size)
|
81
|
+
@max_size = size
|
82
|
+
signal_master
|
83
|
+
end
|
84
|
+
|
85
|
+
def << block_or_item
|
86
|
+
process block_or_item
|
87
|
+
end
|
88
|
+
|
89
|
+
def join
|
90
|
+
@running = false
|
91
|
+
@joining = true
|
92
|
+
signal_master
|
93
|
+
# A weird bug happens on this next line if you don't test
|
94
|
+
# @master.alive?, but only sometimes. I don't care enough to
|
95
|
+
# figure it out right now.
|
96
|
+
@master.join if @master.alive?
|
97
|
+
end
|
98
|
+
|
99
|
+
def size
|
100
|
+
@workers.size
|
101
|
+
end
|
102
|
+
|
103
|
+
def queue_size
|
104
|
+
@queue.size
|
105
|
+
end
|
106
|
+
|
107
|
+
def stop
|
108
|
+
@queue.clear
|
109
|
+
join
|
110
|
+
end
|
111
|
+
|
112
|
+
def kill
|
113
|
+
@queue.clear
|
114
|
+
@workers.each(&:kill)
|
115
|
+
join
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def signal_master
|
121
|
+
@mutex.synchronize { @cv.signal }
|
122
|
+
end
|
123
|
+
|
124
|
+
def master_thread
|
125
|
+
Thread.new do
|
126
|
+
while @running || ! @queue.empty?
|
127
|
+
|
128
|
+
@workers ||= []
|
129
|
+
@workers.delete_if { |w| ! w.alive? }
|
130
|
+
|
131
|
+
while @workers.size < @max_size && @queue.size > 0
|
132
|
+
@workers << Thread.new do
|
133
|
+
begin
|
134
|
+
if task = @queue.pop(true) rescue nil
|
135
|
+
task.call
|
136
|
+
@mutex.synchronize { @completed +=1 }
|
137
|
+
end
|
138
|
+
ensure
|
139
|
+
signal_master
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
@mutex.synchronize do
|
145
|
+
# @cv.wait(@mutex, 1) # can't do this in 1.8.7
|
146
|
+
begin
|
147
|
+
Timeout::timeout(2) { @cv.wait(@mutex) }
|
148
|
+
rescue Timeout::Error
|
149
|
+
end
|
150
|
+
end
|
151
|
+
# This needs to come after the critical section above,
|
152
|
+
# otherwise the main thread will have to wait for the timeout
|
153
|
+
# before continuing when the ThreadPool is joined. The rescue
|
154
|
+
# below handles exceptions that might have happened in the
|
155
|
+
# threads, which will stop the main thread now that they're
|
156
|
+
# being joined.
|
157
|
+
@workers.each(&:join) if @joining rescue nil
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
data/lib/lossfully.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2011 Don March
|
3
|
+
#
|
4
|
+
# This file is part of Lossfully.
|
5
|
+
#
|
6
|
+
# Lossfully is free software: you can redistribute it and/or modify it
|
7
|
+
# under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Lossfully is distributed in the hope that it will be useful, but
|
12
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program. If not, see
|
18
|
+
# <http://www.gnu.org/licenses/>.
|
19
|
+
#++
|
20
|
+
|
21
|
+
module Lossfully
|
22
|
+
|
23
|
+
# :stopdoc:
|
24
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
25
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
26
|
+
# :startdoc:
|
27
|
+
VERSION = ::File.read(PATH + 'version.txt').strip
|
28
|
+
|
29
|
+
# Returns the library path for the module. If any arguments are given,
|
30
|
+
# they will be joined to the end of the libray path using
|
31
|
+
# <tt>File.join</tt>.
|
32
|
+
#
|
33
|
+
def self.libpath( *args )
|
34
|
+
rv = args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
35
|
+
if block_given?
|
36
|
+
begin
|
37
|
+
$LOAD_PATH.unshift LIBPATH
|
38
|
+
rv = yield
|
39
|
+
ensure
|
40
|
+
$LOAD_PATH.shift
|
41
|
+
end
|
42
|
+
end
|
43
|
+
return rv
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the lpath for the module. If any arguments are given,
|
47
|
+
# they will be joined to the end of the path using
|
48
|
+
# <tt>File.join</tt>.
|
49
|
+
#
|
50
|
+
def self.path( *args )
|
51
|
+
rv = args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
52
|
+
if block_given?
|
53
|
+
begin
|
54
|
+
$LOAD_PATH.unshift PATH
|
55
|
+
rv = yield
|
56
|
+
ensure
|
57
|
+
$LOAD_PATH.shift
|
58
|
+
end
|
59
|
+
end
|
60
|
+
return rv
|
61
|
+
end
|
62
|
+
|
63
|
+
# Utility method used to require all files ending in .rb that lie in the
|
64
|
+
# directory below this file that has the same name as the filename passed
|
65
|
+
# in. Optionally, a specific _directory_ name can be passed in such that
|
66
|
+
# the _filename_ does not have to be equivalent to the directory.
|
67
|
+
#
|
68
|
+
def self.require_all_libs_relative_to( fname, dir = nil )
|
69
|
+
dir ||= ::File.basename(fname, '.*')
|
70
|
+
search_me = ::File.expand_path(
|
71
|
+
::File.join(::File.dirname(fname), dir, '**', '*.rb'))
|
72
|
+
|
73
|
+
Dir.glob(search_me).sort.each {|rb| require rb}
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
Lossfully.require_all_libs_relative_to(__FILE__)
|
79
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
4
|
+
require 'lossfully'
|
5
|
+
|
6
|
+
module TestLossfully
|
7
|
+
class TestAudioFile < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def test_encoding
|
10
|
+
assert_raise RuntimeError do Lossfully::AudioFile.encoding('test/data/this_file_does_not_exist') end
|
11
|
+
assert ! Lossfully::AudioFile.is_audio?('test/data/text.txt')
|
12
|
+
assert Lossfully::AudioFile.encoding('test/data/so_sad.ogg')
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_bitrate
|
16
|
+
assert_equal '114k', Lossfully::AudioFile.bitrate('test/data/so_sad.ogg')
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_bitrate_kbps
|
20
|
+
assert_equal 114, Lossfully::AudioFile.bitrate_kbps('test/data/so_sad.ogg')
|
21
|
+
assert_equal 2820, Lossfully::AudioFile.bitrate_kbps('test/data/so_sad.sox')
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_duration
|
25
|
+
f = Lossfully::AudioFile.new('test/data/so_sad.ogg')
|
26
|
+
assert_equal 6.047959, f.duration
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_class_encode
|
30
|
+
input = 'test/data/so_sad.flac'
|
31
|
+
output = 'test/data/so_sad.wav'
|
32
|
+
FileUtils.rm output if File.exist? output
|
33
|
+
Lossfully::AudioFile.encode input, output
|
34
|
+
assert_equal :wav, Lossfully::AudioFile.type(output)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_encode
|
38
|
+
input = 'test/data/so_sad.flac'
|
39
|
+
f = Lossfully::AudioFile.new input
|
40
|
+
output = 'test/data/so_sad.wav'
|
41
|
+
FileUtils.rm output if File.exist? output
|
42
|
+
f.encode output
|
43
|
+
assert_equal :wav, Lossfully::AudioFile.type(output)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
4
|
+
require 'lossfully'
|
5
|
+
|
6
|
+
module TestLossfully
|
7
|
+
class TestInputRules < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def test_test
|
10
|
+
r1 = Lossfully::InputRules.new [:flac]
|
11
|
+
r2 = Lossfully::InputRules.new [:everything]
|
12
|
+
r2 = Lossfully::InputRules.new [:nonaudio]
|
13
|
+
f1 = 'test/data/text.txt'
|
14
|
+
assert !(r1.test f1)
|
15
|
+
assert r2.test f1
|
16
|
+
|
17
|
+
r1 = Lossfully::InputRules.new [:flac]
|
18
|
+
r2 = Lossfully::InputRules.new [:ogg]
|
19
|
+
r3 = Lossfully::InputRules.new [:everything]
|
20
|
+
r4 = Lossfully::InputRules.new [:lossy]
|
21
|
+
r5 = Lossfully::InputRules.new [:lossless]
|
22
|
+
r6 = Lossfully::InputRules.new [:audio]
|
23
|
+
r7 = Lossfully::InputRules.new [:nonaudio]
|
24
|
+
f1 = 'test/data/so_sad.flac'
|
25
|
+
f2 = 'test/data/so_sad.ogg'
|
26
|
+
assert r1.test f1
|
27
|
+
assert r2.test f2
|
28
|
+
assert r3.test f1
|
29
|
+
assert r3.test f2
|
30
|
+
assert r4.test f2
|
31
|
+
assert r5.test f1
|
32
|
+
assert r6.test f1
|
33
|
+
assert r6.test f2
|
34
|
+
assert ! (r4.test f1)
|
35
|
+
assert ! (r5.test f2)
|
36
|
+
|
37
|
+
r1 = Lossfully::InputRules.new [100]
|
38
|
+
r2 = Lossfully::InputRules.new [128]
|
39
|
+
f1 = 'test/data/so_sad.ogg'
|
40
|
+
assert r1.test f1
|
41
|
+
assert !(r2.test f1)
|
42
|
+
|
43
|
+
r1 = Lossfully::InputRules.new ['.ogg']
|
44
|
+
r2 = Lossfully::InputRules.new ['.flac']
|
45
|
+
f1 = 'test/data/so_sad.ogg'
|
46
|
+
f2 = 'test/data/so_sad.flac'
|
47
|
+
assert r1.test f1
|
48
|
+
assert r2.test f2
|
49
|
+
assert !(r1.test f2)
|
50
|
+
assert !(r2.test f1)
|
51
|
+
|
52
|
+
r1 = Lossfully::InputRules.new [/sad/]
|
53
|
+
r2 = Lossfully::InputRules.new [/happy/]
|
54
|
+
f1 = Lossfully::AudioFile.new 'test/data/so_sad.ogg'
|
55
|
+
assert r1.test f1
|
56
|
+
assert !(r2.test f1)
|
57
|
+
|
58
|
+
r1 = Lossfully::InputRules.new do |f|
|
59
|
+
[:mp3] if File.dirname(f.path) == 'test/data'
|
60
|
+
end
|
61
|
+
f1 = Lossfully::AudioFile.new 'test/data/so_sad.ogg'
|
62
|
+
assert r1.test f1
|
63
|
+
|
64
|
+
r1 = Lossfully::InputRules.new [:ogg, 100] do |f|
|
65
|
+
[:mp3] if File.dirname(f.path) == 'test/data'
|
66
|
+
end
|
67
|
+
assert r1.test f1
|
68
|
+
|
69
|
+
r1 = Lossfully::InputRules.new [:ogg, 128] do |f|
|
70
|
+
[:mp3] if File.dirname(f.path) == 'test/data'
|
71
|
+
end
|
72
|
+
assert !(r1.test f1)
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_comparison
|
76
|
+
r1 = Lossfully::InputRules.new [:mp3, /mp3/]
|
77
|
+
r2 = Lossfully::InputRules.new [:mp3, /mp3/]
|
78
|
+
r3 = Lossfully::InputRules.new [:mp3]
|
79
|
+
r4 = Lossfully::InputRules.new { false }
|
80
|
+
a = [r1]
|
81
|
+
assert a.include? r2
|
82
|
+
assert !(a.include? r3)
|
83
|
+
assert !(a.include? r4)
|
84
|
+
|
85
|
+
r1 = Lossfully::InputRules.new { false }
|
86
|
+
r2 = Lossfully::InputRules.new
|
87
|
+
r3 = Lossfully::InputRules.new [:mp3, /regexp/, 192]
|
88
|
+
r4 = Lossfully::InputRules.new [:mp3, /regexp/, 192] do false end
|
89
|
+
assert r1 < r2
|
90
|
+
assert r3 < r1
|
91
|
+
assert r4 < r3
|
92
|
+
|
93
|
+
r1 = Lossfully::InputRules.new [:everything]
|
94
|
+
r2 = Lossfully::InputRules.new [:lossy]
|
95
|
+
r3 = Lossfully::InputRules.new [:lossless]
|
96
|
+
r4 = Lossfully::InputRules.new [:audio]
|
97
|
+
r5 = Lossfully::InputRules.new [:nonaudio]
|
98
|
+
assert r2 < r1
|
99
|
+
assert r3 < r1
|
100
|
+
assert r4 < r1
|
101
|
+
assert r5 < r1
|
102
|
+
assert r2 < r4
|
103
|
+
assert r3 < r4
|
104
|
+
|
105
|
+
r1 = Lossfully::InputRules.new ['']
|
106
|
+
r2 = Lossfully::InputRules.new ['ogg']
|
107
|
+
assert r2 < r1
|
108
|
+
|
109
|
+
r1 = Lossfully::InputRules.new [192]
|
110
|
+
r2 = Lossfully::InputRules.new [128]
|
111
|
+
assert r1 < r2
|
112
|
+
|
113
|
+
r1 = Lossfully::InputRules.new [//]
|
114
|
+
r2 = Lossfully::InputRules.new [/a/]
|
115
|
+
r3 = Lossfully::InputRules.new [/b/]
|
116
|
+
assert r2 < r1
|
117
|
+
assert_not_equal r2, r3
|
118
|
+
|
119
|
+
r1 = Lossfully::InputRules.new [:ogg, 192]
|
120
|
+
r2 = Lossfully::InputRules.new [:ogg, /a/]
|
121
|
+
assert r2 < r1
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
4
|
+
require 'lossfully'
|
5
|
+
|
6
|
+
module TestLossfully
|
7
|
+
class TestThreadPool < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def test_everything
|
10
|
+
a = []
|
11
|
+
tp = Lossfully::ThreadPool.new(2)
|
12
|
+
tp.max_size = 3
|
13
|
+
assert_equal 3, tp.max_size
|
14
|
+
tp.process { a << 1}
|
15
|
+
tp.process { a << 2 }
|
16
|
+
tp.process { a << 3 }
|
17
|
+
tp.join
|
18
|
+
assert a.include? 1
|
19
|
+
assert a.include? 2
|
20
|
+
assert a.include? 3
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_everything_with_auto_blocks
|
24
|
+
a = []
|
25
|
+
tp = Lossfully::ThreadPool.new(2) do |x|
|
26
|
+
a << x
|
27
|
+
end
|
28
|
+
tp << 1
|
29
|
+
tp << 2
|
30
|
+
tp << 3
|
31
|
+
tp.join
|
32
|
+
assert a.include? 1
|
33
|
+
assert a.include? 2
|
34
|
+
assert a.include? 3
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_stop
|
38
|
+
tp = Lossfully::ThreadPool.new(2)
|
39
|
+
r1 = false
|
40
|
+
r2 = false
|
41
|
+
r3 = false
|
42
|
+
|
43
|
+
tp.process { sleep 0.3; r1 = true}
|
44
|
+
tp.process { sleep 0.3; r2 = true}
|
45
|
+
tp.process { sleep 0.3; r3 = true}
|
46
|
+
2.times { Thread.pass } and sleep 0.1
|
47
|
+
assert_equal 1, tp.queue_size
|
48
|
+
assert_equal 2, tp.size
|
49
|
+
tp.stop
|
50
|
+
sleep 0.4
|
51
|
+
|
52
|
+
assert r1, 'first task finished'
|
53
|
+
assert r2, 'second task finished'
|
54
|
+
assert ! r3, 'third task did not finish'
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_kill
|
58
|
+
tp = Lossfully::ThreadPool.new(2)
|
59
|
+
r1 = false
|
60
|
+
r2 = false
|
61
|
+
r3 = false
|
62
|
+
|
63
|
+
tp.process { sleep 0.3; r1 = true}
|
64
|
+
tp.process { sleep 0.3; r2 = true}
|
65
|
+
tp.process { sleep 0.3; r3 = true}
|
66
|
+
2.times { Thread.pass } and sleep 0.1
|
67
|
+
|
68
|
+
tp.kill
|
69
|
+
sleep 0.4
|
70
|
+
|
71
|
+
assert ! r1, 'first task did not finished'
|
72
|
+
assert ! r2, 'second task did not finished'
|
73
|
+
assert ! r3, 'third task did not finish'
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
data/version.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|