lossfully 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|