albanpeignier-alsa-backup 0.0.1 → 0.0.3

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/.autotest CHANGED
@@ -1,3 +1,4 @@
1
1
  Autotest.add_hook :initialize do |autotest|
2
2
  autotest.add_exception(/tmp/)
3
+ autotest.add_exception(/log/)
3
4
  end
data/History.txt CHANGED
@@ -1,4 +1,13 @@
1
+ == 0.0.3
2
+
3
+ * Support --background to daemonize process
4
+ * Support --pidfile option
5
+ * Rename existing file
6
+
7
+ == 0.0.2 2009-05-19
8
+
9
+ * Improve capture by support partial reads and recovering errors
10
+
1
11
  == 0.0.1 2009-05-14
2
12
 
3
- * 1 major enhancement:
4
- * Initial release
13
+ * Initial release
data/Manifest.txt CHANGED
@@ -4,20 +4,26 @@ Manifest.txt
4
4
  PostInstall.txt
5
5
  README.rdoc
6
6
  Rakefile
7
+ TODO
7
8
  alsa-backup.gemspec
8
9
  bin/alsa.backup
9
10
  config.sample
10
11
  lib/alsa.rb
11
12
  lib/alsa_backup.rb
12
13
  lib/alsa_backup/cli.rb
14
+ lib/alsa_backup/core_ext.rb
15
+ lib/alsa_backup/length_controller.rb
13
16
  lib/alsa_backup/recorder.rb
17
+ lib/alsa_backup/writer.rb
14
18
  lib/sndfile.rb
15
19
  script/console
16
20
  script/destroy
17
21
  script/generate
18
22
  spec/alsa/pcm_spec.rb
19
23
  spec/alsa_backup/cli_spec.rb
24
+ spec/alsa_backup/core_ext_spec.rb
20
25
  spec/alsa_backup/recorder_spec.rb
26
+ spec/alsa_backup/writer_spec.rb
21
27
  spec/alsa_backup_spec.rb
22
28
  spec/fixtures/config_test.rb
23
29
  spec/sndfile/info_spec.rb
data/README.rdoc CHANGED
@@ -32,7 +32,43 @@ will load the specified configuration
32
32
 
33
33
  == CONFIGURATION:
34
34
 
35
- See config.sample for the moment.
35
+ The configuration file is a Ruby file. This piece of code can
36
+ configurate the AlsaBacup recorder :
37
+
38
+ AlsaBackup.config do |recorder|
39
+ # configure recorder here :
40
+ recorder.file = "record.wav"
41
+ end
42
+
43
+ === Recorder File
44
+
45
+ The recorder file can be specified as a simple string :
46
+
47
+ recorder.file = "record.wav"
48
+
49
+ The recorder file can be specified by a Proc which returns the string :
50
+
51
+ recorder.file = Proc.new {
52
+ Time.now.strftime("%Y/%m-%b/%d-%a/%Hh.wav")
53
+ }
54
+
55
+ will use the current time to create file names like these :
56
+
57
+ 2009/05-May/17-Sun/19h.wav
58
+ 2009/05-May/17-Sun/20h.wav
59
+
60
+ To use different files every 15 minutes :
61
+
62
+ recorder.file = Proc.new {
63
+ Time.now.floor(:min, 15).strftime("%Y/%m-%b/%d-%a/%Hh%M.wav")
64
+ }
65
+
66
+ will create files like these :
67
+
68
+ 2009/05-May/17-Sun/19h00.wav
69
+ 2009/05-May/17-Sun/19h15.wav
70
+
71
+ See config.sample.
36
72
 
37
73
  == REQUIREMENTS:
38
74
 
@@ -41,16 +77,14 @@ See config.sample for the moment.
41
77
 
42
78
  == INSTALL:
43
79
 
44
- * sudo apt-get install libasound2 libsndfile1
45
- * sudo gem install ruby-ffi
46
-
47
- * sudo gem install --source http://gems.github.com albanpeignier-alsa-backup
80
+ sudo apt-get install libasound2 libsndfile1
81
+ sudo gem install --source http://gems.github.com albanpeignier-alsa-backup
48
82
 
49
83
  == LICENSE:
50
84
 
51
85
  (The MIT License)
52
86
 
53
- Copyright (c) 2009 FIXME Alban Peignier
87
+ Copyright (c) 2009 Alban Peignier
54
88
 
55
89
  Permission is hereby granted, free of charge, to any person obtaining
56
90
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ $hoe = Hoe.new('alsa-backup', AlsaBackup::VERSION) do |p|
9
9
  p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
10
10
  p.rubyforge_name = p.name # TODO this is default value
11
11
  p.extra_deps = [
12
- ['ffi','>= 0.3.5'],
12
+ ['ffi','>= 0.3.5'], ['newgem', ">= #{::Newgem::VERSION}"]
13
13
  ]
14
14
  p.extra_dev_deps = [
15
15
  ['newgem', ">= #{::Newgem::VERSION}"]
@@ -25,3 +25,10 @@ require 'newgem/tasks' # load /tasks/*.rake
25
25
  Dir['tasks/**/*.rake'].each { |t| load t }
26
26
 
27
27
  task :default => :spec
28
+
29
+ namespace :gems do
30
+ task :install do
31
+ gems = %w{activesupport ffi rspec}
32
+ sh "sudo gem install #{gems.join(' ')}"
33
+ end
34
+ end
data/TODO ADDED
@@ -0,0 +1,4 @@
1
+ * configurable channels
2
+ * configurable sample rate
3
+ * configurable alsa device
4
+ * improve spec coverage
data/alsa-backup.gemspec CHANGED
@@ -2,17 +2,17 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{alsa-backup}
5
- s.version = "0.0.1"
5
+ s.version = "0.0.3"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Alban Peignier"]
9
- s.date = %q{2009-05-18}
9
+ s.date = %q{2009-05-21}
10
10
  s.default_executable = %q{alsa.backup}
11
11
  s.description = %q{ALSA client to perform continuous recording}
12
12
  s.email = ["alban.peignier@free.fr"]
13
13
  s.executables = ["alsa.backup"]
14
14
  s.extra_rdoc_files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc"]
15
- s.files = [".autotest", "History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "Rakefile", "alsa-backup.gemspec", "bin/alsa.backup", "config.sample", "lib/alsa.rb", "lib/alsa_backup.rb", "lib/alsa_backup/cli.rb", "lib/alsa_backup/recorder.rb", "lib/sndfile.rb", "script/console", "script/destroy", "script/generate", "spec/alsa/pcm_spec.rb", "spec/alsa_backup/cli_spec.rb", "spec/alsa_backup/recorder_spec.rb", "spec/alsa_backup_spec.rb", "spec/fixtures/config_test.rb", "spec/sndfile/info_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/rspec.rake"]
15
+ s.files = [".autotest", "History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "Rakefile", "TODO", "alsa-backup.gemspec", "bin/alsa.backup", "config.sample", "lib/alsa.rb", "lib/alsa_backup.rb", "lib/alsa_backup/cli.rb", "lib/alsa_backup/core_ext.rb", "lib/alsa_backup/length_controller.rb", "lib/alsa_backup/recorder.rb", "lib/alsa_backup/writer.rb", "lib/sndfile.rb", "script/console", "script/destroy", "script/generate", "spec/alsa/pcm_spec.rb", "spec/alsa_backup/cli_spec.rb", "spec/alsa_backup/core_ext_spec.rb", "spec/alsa_backup/recorder_spec.rb", "spec/alsa_backup/writer_spec.rb", "spec/alsa_backup_spec.rb", "spec/fixtures/config_test.rb", "spec/sndfile/info_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/rspec.rake"]
16
16
  s.has_rdoc = true
17
17
  s.homepage = %q{http://github.com/alban.peignier/alsa-backup}
18
18
  s.rdoc_options = ["--main", "README.rdoc"]
@@ -27,16 +27,19 @@ Gem::Specification.new do |s|
27
27
 
28
28
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
29
29
  s.add_runtime_dependency(%q<ffi>, [">= 0.3.5"])
30
+ s.add_runtime_dependency(%q<newgem>, [">= 1.4.1"])
30
31
  s.add_development_dependency(%q<newgem>, [">= 1.4.1"])
31
32
  s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
32
33
  else
33
34
  s.add_dependency(%q<ffi>, [">= 0.3.5"])
34
35
  s.add_dependency(%q<newgem>, [">= 1.4.1"])
36
+ s.add_dependency(%q<newgem>, [">= 1.4.1"])
35
37
  s.add_dependency(%q<hoe>, [">= 1.8.0"])
36
38
  end
37
39
  else
38
40
  s.add_dependency(%q<ffi>, [">= 0.3.5"])
39
41
  s.add_dependency(%q<newgem>, [">= 1.4.1"])
42
+ s.add_dependency(%q<newgem>, [">= 1.4.1"])
40
43
  s.add_dependency(%q<hoe>, [">= 1.8.0"])
41
44
  end
42
45
  end
data/config.sample CHANGED
@@ -38,6 +38,25 @@ AlsaBackup.config do |recorder|
38
38
  # Define where the files are created
39
39
  #
40
40
  #recorder.directory = "/var/lib/alsa.backup"
41
+
42
+ #
43
+ # Customize error handler during record
44
+ #
45
+
46
+ # To sleep 30 seconds on error
47
+ #recorder.error_handler = Proc.new do |exception|
48
+ # 30
49
+ #end
50
+
51
+ # To fail on any error
52
+ #recorder.error_handler = Proc.new do |exception|
53
+ # false
54
+ #end
55
+
56
+ # To sleep the default time
57
+ #recorder.error_handler = Proc.new do |exception|
58
+ # true
59
+ #end
41
60
  end
42
61
 
43
62
 
data/lib/alsa.rb CHANGED
@@ -18,7 +18,7 @@ module ALSA
18
18
 
19
19
  def self.try_to(message, &block)
20
20
  logger.debug('alsa') { message }
21
- if (response = yield) < 0
21
+ if ALSA::Native::error_code?(response = yield)
22
22
  raise "cannot #{message} (#{ALSA::Native::strerror(response)})"
23
23
  else
24
24
  response
@@ -30,6 +30,10 @@ module ALSA
30
30
  ffi_lib "libasound.so"
31
31
 
32
32
  attach_function :strerror, :snd_strerror, [:int], :string
33
+
34
+ def self.error_code?(response)
35
+ response < 0
36
+ end
33
37
  end
34
38
 
35
39
  module PCM
@@ -77,6 +81,7 @@ module ALSA
77
81
  def hardware_parameters
78
82
  HwParameters.new(self).current_for_device
79
83
  end
84
+ alias_method :hw_params, :hardware_parameters
80
85
 
81
86
  def hardware_parameters=(attributes= {})
82
87
  attributes = {:access => :rw_interleaved}.update(attributes)
@@ -86,21 +91,33 @@ module ALSA
86
91
  end
87
92
 
88
93
  def read
89
- frame_count = hardware_parameters.sample_rate
90
- buffer = FFI::MemoryPointer.new(hardware_parameters.buffer_size_for(frame_count))
94
+ frame_count = hw_params.sample_rate / 2
95
+
96
+ FFI::MemoryPointer.new(:char, hw_params.buffer_size_for(frame_count)) do |buffer|
97
+ begin
98
+ read_buffer buffer, frame_count
99
+ end while yield buffer, frame_count
100
+ end
101
+ end
91
102
 
92
- continue = true
93
- while continue
94
- read_count = ALSA::try_to "read from audio interface" do
95
- ALSA::PCM::Native::readi(self.handle, buffer, frame_count)
103
+ def read_buffer(buffer, frame_count)
104
+ read_count = ALSA::try_to "read from audio interface" do
105
+ response = ALSA::PCM::Native::readi(self.handle, buffer, frame_count)
106
+ if ALSA::Native::error_code?(response)
107
+ ALSA.logger.debug('alsa') { "try to recover '#{ALSA::Native::strerror(response)}' on read"}
108
+ ALSA::PCM::Native::pcm_recover(self.handle, response, 1)
109
+ else
110
+ response
96
111
  end
97
-
98
- raise "can't read expected frame count (#{read_count}/#{frame_count})" unless read_count == frame_count
99
-
100
- continue = yield buffer, read_count
101
112
  end
102
113
 
103
- buffer.free
114
+ missing_frame_count = frame_count - read_count
115
+ if missing_frame_count > 0
116
+ ALSA.logger.debug('alsa') { "re-read missing frame count: #{missing_frame_count}"}
117
+ read_buffer_size = hw_params.buffer_size_for(read_count)
118
+ # buffer[read_buffer_size] doesn't return a MemoryPointer
119
+ read_buffer(buffer + read_buffer_size, missing_frame_count)
120
+ end
104
121
  end
105
122
 
106
123
  def close
@@ -242,6 +259,8 @@ module ALSA
242
259
 
243
260
  attach_function :readi, :snd_pcm_readi, [ :pointer, :pointer, :ulong ], :long
244
261
 
262
+ attach_function :pcm_recover, :snd_pcm_recover, [ :pointer, :int, :int ], :int
263
+
245
264
  attach_function :hw_params_malloc, :snd_pcm_hw_params_malloc, [:pointer], :int
246
265
  attach_function :hw_params_free, :snd_pcm_hw_params_free, [:pointer], :int
247
266
 
data/lib/alsa_backup.rb CHANGED
@@ -10,7 +10,7 @@ require 'activesupport'
10
10
  require 'logger'
11
11
 
12
12
  module AlsaBackup
13
- VERSION = '0.0.1'
13
+ VERSION = '0.0.3'
14
14
 
15
15
  def self.recorder
16
16
  @recorder ||= AlsaBackup::Recorder.new
@@ -33,15 +33,9 @@ module AlsaBackup
33
33
 
34
34
  end
35
35
 
36
- class Time
37
-
38
- def floor(attribute, modulo)
39
- actual = self.send(attribute)
40
- self.change(attribute => actual - actual%modulo)
41
- end
42
-
43
- end
44
-
36
+ require 'alsa_backup/core_ext'
37
+ require 'alsa_backup/length_controller'
38
+ require 'alsa_backup/writer'
45
39
  require 'alsa_backup/recorder'
46
40
 
47
41
 
@@ -1,4 +1,5 @@
1
1
  require 'optparse'
2
+ require 'daemons'
2
3
 
3
4
  module AlsaBackup
4
5
  class CLI
@@ -23,6 +24,10 @@ module AlsaBackup
23
24
  "Base directory") { |arg| options[:directory] = arg }
24
25
  opts.on("-c", "--config=CONFIG", String,
25
26
  "Configuration file") { |arg| options[:config] = arg }
27
+ opts.on("-p", "--pid=PID_FILE", String,
28
+ "File to write the process pid") { |arg| options[:pid] = arg }
29
+ opts.on("-b", "--background", nil,
30
+ "Daemonize the process") { |arg| options[:daemonize] = true }
26
31
  opts.on("-h", "--help",
27
32
  "Show this help message.") { stdout.puts opts; exit }
28
33
  opts.parse!(arguments)
@@ -36,9 +41,16 @@ module AlsaBackup
36
41
 
37
42
  AlsaBackup.recorder.file = options[:file] if options[:file]
38
43
  AlsaBackup.recorder.directory = options[:directory] if options[:directory]
44
+
45
+ pid_file = File.expand_path(options[:pid]) if options[:pid]
46
+
47
+ Daemonize.daemonize(nil, "alsa.backup") if options[:daemonize]
48
+ File.write(pid_file, $$) if pid_file
39
49
 
40
50
  length = options[:length].to_i if options[:length]
41
51
  AlsaBackup.recorder.start(length)
52
+ rescue Exception => e
53
+ AlsaBackup.logger.fatal(e)
42
54
  end
43
55
  end
44
56
  end
@@ -0,0 +1,42 @@
1
+ class Time
2
+
3
+ def floor(attribute, modulo)
4
+ actual = self.send(attribute)
5
+ self.change(attribute => actual - actual%modulo)
6
+ end
7
+
8
+ end
9
+
10
+ class File
11
+
12
+ def self.extension(file)
13
+ if file =~ /(\.[^.]*)$/
14
+ $1
15
+ else
16
+ ""
17
+ end
18
+ end
19
+
20
+ def self.suffix_basename(file, suffix)
21
+ dirname = File.dirname(file)
22
+
23
+ dirname =
24
+ case dirname
25
+ when "/": "/"
26
+ when ".": ""
27
+ else
28
+ dirname + "/"
29
+ end
30
+
31
+ extension = File.extension(file)
32
+ dirname +
33
+ File.basename(file, extension) +
34
+ suffix +
35
+ extension
36
+ end
37
+
38
+ def self.write(file, content)
39
+ File.open(file, "w") { |f| f.puts content }
40
+ end
41
+
42
+ end
@@ -0,0 +1,19 @@
1
+ module AlsaBackup
2
+ module LengthController
3
+ class Loop
4
+ def continue_after?(frame_count)
5
+ true
6
+ end
7
+ end
8
+
9
+ class FrameCount
10
+ def initialize(frame_count)
11
+ @frame_count = frame_count
12
+ end
13
+
14
+ def continue_after?(frame_count)
15
+ (@frame_count -= frame_count) > 0
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,71 +1,65 @@
1
1
  require 'alsa'
2
- require 'sndfile'
3
-
4
- require 'fileutils'
5
2
 
6
3
  module AlsaBackup
7
4
  class Recorder
8
5
 
9
6
  def initialize(file = "record.wav")
10
- @file = file
11
- @directory = "."
7
+ @file = File.basename(file)
8
+ @directory = File.dirname(file)
9
+
10
+ @error_handler = Proc.new { |e| true }
12
11
  end
13
12
 
14
- attr_accessor :file, :directory
13
+ attr_accessor :file, :directory, :error_handler
15
14
 
16
15
  def start(seconds_to_record = nil)
17
- frames_to_record = format[:sample_rate] * seconds_to_record if seconds_to_record
18
-
19
- # prepare sndfile
20
- self.sndfile
16
+ length_controller = self.length_controller(seconds_to_record)
21
17
 
22
- ALSA::PCM::Capture.open("hw:0", self.format(:sample_format => :s16_le)) do |capture|
23
- capture.read do |buffer, frame_count|
24
- self.sndfile.write buffer, frame_count
25
- if frames_to_record
26
- (frames_to_record -= frame_count) > 0
27
- else
28
- true
18
+ Writer.open(directory, file, format(:format => "wav pcm_16")) do |writer|
19
+ ALSA::PCM::Capture.open("hw:0", self.format(:sample_format => :s16_le)) do |capture|
20
+ capture.read do |buffer, frame_count|
21
+ writer.write buffer, frame_count
22
+ length_controller.continue_after? frame_count
29
23
  end
30
24
  end
31
25
  end
26
+ rescue Interrupt
27
+ AlsaBackup.logger.debug('recorder interrupted')
32
28
  rescue Exception => e
33
29
  AlsaBackup.logger.error(e)
34
- raise e
35
- ensure
36
- @sndfile.close if @sndfile
37
- end
30
+ AlsaBackup.logger.debug { e.backtrace.join("\n") }
38
31
 
39
- def file
40
- case @file
41
- when Proc
42
- @file.call
32
+ if seconds_to_record.nil? and continue_on_error?(e)
33
+ retry
43
34
  else
44
- @file
35
+ raise e
45
36
  end
46
37
  end
47
38
 
48
- def target_file
49
- File.join self.directory, self.file
39
+ def continue_on_error?(e)
40
+ error_handler_response = @error_handler.call(e) if @error_handler
41
+
42
+ if error_handler_response
43
+ sleep_time = Numeric === error_handler_response ? error_handler_response : 5
44
+ AlsaBackup.logger.warn("sleep #{sleep_time}s before retrying")
45
+ sleep sleep_time
46
+ end
47
+
48
+ error_handler_response
50
49
  end
51
50
 
52
51
  def format(additional_parameters = {})
53
52
  {:sample_rate => 44100, :channels => 2}.merge(additional_parameters)
54
53
  end
55
54
 
56
- def sndfile
57
- target_file = self.target_file
58
- raise "no recording file" unless target_file
59
-
60
- unless @sndfile and @sndfile.path == target_file
61
- @sndfile.close if @sndfile
62
- AlsaBackup.logger.info "new file #{target_file}"
63
-
64
- FileUtils.mkdir_p File.dirname(target_file)
65
- @sndfile = Sndfile::File.new(target_file, "w", self.format(:format => "wav pcm_16"))
55
+ def length_controller(seconds_to_record)
56
+ if seconds_to_record
57
+ AlsaBackup::LengthController::FrameCount.new format[:sample_rate] * seconds_to_record
58
+ else
59
+ AlsaBackup::LengthController::Loop.new
66
60
  end
67
- @sndfile
68
61
  end
69
-
62
+
70
63
  end
64
+
71
65
  end
@@ -0,0 +1,92 @@
1
+ require 'sndfile'
2
+ require 'fileutils'
3
+
4
+ module AlsaBackup
5
+ class Writer
6
+
7
+ attr_accessor :directory, :file, :format
8
+
9
+ def self.default_format
10
+ {:sample_rate => 44100, :channels => 2, :format => "wav pcm_16"}
11
+ end
12
+
13
+ def initialize(directory, file, format = Writer.default_format)
14
+ @directory = directory
15
+ @file = file
16
+ @format = format
17
+ end
18
+
19
+ def self.open(directory, file, format, &block)
20
+ writer = Writer.new(directory, file, format).prepare
21
+
22
+ begin
23
+ yield writer
24
+ ensure
25
+ writer.close
26
+ end
27
+ end
28
+
29
+ def prepare
30
+ # prepare sndfile
31
+ self.sndfile
32
+ self
33
+ end
34
+
35
+ def write(*arguments)
36
+ self.sndfile.write *arguments
37
+ end
38
+
39
+ def close
40
+ if @sndfile
41
+ AlsaBackup.logger.info('close current file')
42
+ @sndfile.close
43
+ end
44
+ @sndfile = nil
45
+ end
46
+
47
+ def file
48
+ case @file
49
+ when Proc
50
+ @file.call
51
+ else
52
+ @file
53
+ end
54
+ end
55
+
56
+ def target_file
57
+ File.join self.directory, self.file
58
+ end
59
+
60
+ def sndfile
61
+ target_file = self.target_file
62
+ raise "no recording file" unless target_file
63
+
64
+ unless @sndfile and @sndfile.path == target_file
65
+ @sndfile.close if @sndfile
66
+
67
+ Writer.rename_existing_file(target_file)
68
+ AlsaBackup.logger.info{"new file #{File.expand_path target_file}"}
69
+
70
+ FileUtils.mkdir_p File.dirname(target_file)
71
+ @sndfile = Sndfile::File.new(target_file, "w", self.format)
72
+ end
73
+ @sndfile
74
+ end
75
+
76
+ def self.rename_existing_file(file)
77
+ if File.exists?(file)
78
+ index = 1
79
+
80
+ while File.exists?(new_file = File.suffix_basename(file, "-#{index}"))
81
+ index += 1
82
+
83
+ raise "can't find a free file for #{file}" if index > 1000
84
+ end
85
+
86
+ AlsaBackup.logger.warn "rename existing file #{File.basename(file)} into #{new_file}"
87
+ File.rename(file, new_file)
88
+ end
89
+ end
90
+
91
+ end
92
+ end
@@ -15,7 +15,12 @@ describe AlsaBackup::CLI, "execute" do
15
15
  def execute_cli(options = {})
16
16
  options = { :file => @file, :length => 2 }.update(options)
17
17
  arguments = options.collect do |key,value|
18
- "--#{key}=#{value}" if value
18
+ case value
19
+ when true: "--#{key}"
20
+ when nil: nil
21
+ else
22
+ "--#{key}=#{value}"
23
+ end
19
24
  end.compact
20
25
 
21
26
  AlsaBackup::CLI.execute(@stdout_io, *arguments)
@@ -59,4 +64,16 @@ describe AlsaBackup::CLI, "execute" do
59
64
  AlsaBackup.recorder.file.should == argument_file
60
65
  end
61
66
 
67
+ it "should write pid in specified file" do
68
+ pid_file = test_file("pid")
69
+ execute_cli :pid => pid_file
70
+
71
+ IO.read(pid_file).strip.should == $$.to_s
72
+ end
73
+
74
+ it "should write pid in specified file" do
75
+ Daemonize.should_receive(:daemonize)
76
+ execute_cli :background => true
77
+ end
78
+
62
79
  end
@@ -0,0 +1,58 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe Time do
4
+
5
+ describe "floor" do
6
+
7
+ it "should change xh41 on xh49 for min with a modulo of 10" do
8
+ time = Time.now.change(:min => 49)
9
+ time.floor(:min, 10).should == time.change(:min => 40)
10
+ end
11
+
12
+ end
13
+
14
+ end
15
+
16
+ describe File do
17
+
18
+ describe "extension" do
19
+
20
+ it "should be .rb for test.rb" do
21
+ File.extension('test.rb').should == '.rb'
22
+ end
23
+
24
+ it "should be '.' for 'test.'" do
25
+ File.extension('test.').should == '.'
26
+ end
27
+
28
+ it "should be blank for 'test'" do
29
+ File.extension('test').should be_blank
30
+ end
31
+
32
+ end
33
+
34
+ describe "suffix_basename" do
35
+
36
+ def take_this_form(expected_file)
37
+ simple_matcher("have this #{expected_file}") do |actual|
38
+ File.suffix_basename(actual,'<prefix>') == expected_file
39
+ end
40
+ end
41
+
42
+ it { 'test.rb'.should take_this_form('test<prefix>.rb') }
43
+
44
+ it "should return test<prefix> for test" do
45
+ File.suffix_basename('test','<prefix>').should == 'test<prefix>'
46
+ end
47
+
48
+ it "should return /path/test<prefix>.rb for /patest" do
49
+ File.suffix_basename('/path/test.rb','<prefix>').should == '/path/test<prefix>.rb'
50
+ end
51
+
52
+ it "should return /test<prefix>.rb for /test.rb" do
53
+ File.suffix_basename('/path/test.rb','<prefix>').should == '/path/test<prefix>.rb'
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -13,19 +13,72 @@ describe AlsaBackup::Recorder do
13
13
  lambda { @recorder.start(2) }.should_not raise_error
14
14
  end
15
15
 
16
- describe "file" do
16
+ describe "error handler" do
17
+
18
+ class TestErrorHandler
19
+
20
+ def initialize(proc)
21
+ proc = Proc.new { |e| proc } unless Proc == proc
22
+ @proc = proc
23
+ end
24
+
25
+ def call(e)
26
+ if @proc
27
+ response = @proc.call(e)
28
+ @proc = nil
29
+ response
30
+ end
31
+ end
32
+
33
+ end
17
34
 
18
- it "should accept file as string" do
19
- @recorder.file = file_name = "dummy"
20
- @recorder.file.should == file_name
35
+ before(:each) do
36
+ AlsaBackup::Writer.stub!(:open).and_raise("dummy")
37
+ @recorder.stub!(:sleep)
21
38
  end
22
39
 
23
- it "should accept file as Proc" do
24
- file_name = "dummy"
25
- @recorder.file = Proc.new { file_name }
26
- @recorder.file.should == file_name
40
+ it "should raise error when error handler is nil" do
41
+ @recorder.error_handler = nil
42
+ lambda { @recorder.start }.should raise_error
27
43
  end
28
-
44
+
45
+ it "should raise error when error handler returns nil or false" do
46
+ @recorder.error_handler = TestErrorHandler.new(nil)
47
+ lambda { @recorder.start }.should raise_error
48
+ end
49
+
50
+ def start_recorder(limit = nil)
51
+ @recorder.start(limit)
52
+ rescue RuntimeError
53
+
54
+ end
55
+
56
+ it "should retry when error handler returns something (not false or nil)" do
57
+ @recorder.error_handler = TestErrorHandler.new(true)
58
+ AlsaBackup::Writer.should_receive(:open).twice().and_raise("dummy")
59
+
60
+ start_recorder
61
+ end
62
+
63
+ it "should use the error handler response as sleep time if numerical" do
64
+ @recorder.error_handler = TestErrorHandler.new(error_handler_response = 5)
65
+ @recorder.should_receive(:sleep).with(error_handler_response)
66
+ start_recorder
67
+ end
68
+
69
+ it "should sleep 5 seconds when the error handler response is a number" do
70
+ @recorder.error_handler = TestErrorHandler.new(true)
71
+ @recorder.should_receive(:sleep).with(5)
72
+ start_recorder
73
+ end
74
+
75
+ it "should not use error handler when recorder is started with a time length" do
76
+ @recorder.error_handler = mock("error_handler")
77
+ @recorder.error_handler.should_not_receive(:call)
78
+
79
+ start_recorder(2)
80
+ end
81
+
29
82
  end
30
83
 
31
84
  end
@@ -0,0 +1,53 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe AlsaBackup::Writer do
4
+
5
+ before(:each) do
6
+ @file = "test.wav"
7
+ @directory = test_directory
8
+ @recorder = AlsaBackup::Writer.new(@file, @directory)
9
+ end
10
+
11
+ describe "file" do
12
+
13
+ it "should accept file as string" do
14
+ @recorder.file = file_name = "dummy"
15
+ @recorder.file.should == file_name
16
+ end
17
+
18
+ it "should accept file as Proc" do
19
+ file_name = "dummy"
20
+ @recorder.file = Proc.new { file_name }
21
+ @recorder.file.should == file_name
22
+ end
23
+
24
+ end
25
+
26
+ describe "rename_existing_file" do
27
+
28
+ it "should keep file if not exists" do
29
+ File.should_receive(:exists?).with(@file).and_return(false)
30
+ File.should_not_receive(:rename)
31
+ AlsaBackup::Writer.rename_existing_file(@file)
32
+ end
33
+
34
+ it "should try to suffix with '-n' to find a free name" do
35
+ File.stub!(:exists?).and_return(true)
36
+
37
+ free_file = File.suffix_basename(@file, "-99")
38
+ File.should_receive(:exists?).with(free_file).and_return(false)
39
+
40
+ File.should_receive(:rename).with(@file, free_file)
41
+ AlsaBackup::Writer.rename_existing_file(@file)
42
+ end
43
+
44
+ it "should raise an error when no free file is found" do
45
+ File.stub!(:exists?).and_return(true)
46
+ lambda do
47
+ AlsaBackup::Writer.rename_existing_file(@file)
48
+ end.should raise_error
49
+ end
50
+
51
+ end
52
+
53
+ end
data/spec/spec_helper.rb CHANGED
@@ -9,13 +9,20 @@ end
9
9
  $:.unshift(File.dirname(__FILE__) + '/../lib')
10
10
  require 'alsa_backup'
11
11
 
12
+ def test_directory
13
+ File.dirname(__FILE__) + '/../tmp'
14
+ end
15
+
12
16
  def test_file(name = 'test.wav')
13
- File.dirname(__FILE__) + '/../tmp/' + name
17
+ File.join(test_directory, name)
14
18
  end
15
19
 
16
20
  def fixture_file(name)
17
21
  File.dirname(__FILE__) + '/fixtures/' + name
18
22
  end
19
23
 
24
+ log_dir = File.dirname(__FILE__) + '/../log/'
25
+ Dir.mkdir(log_dir) unless File.exists?(log_dir)
26
+
20
27
  ALSA::logger = AlsaBackup.logger =
21
- Logger.new(File.dirname(__FILE__) + '/../log/test.log')
28
+ Logger.new(File.join(log_dir,'test.log'))
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: albanpeignier-alsa-backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alban Peignier
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-18 00:00:00 -07:00
12
+ date: 2009-05-21 00:00:00 -07:00
13
13
  default_executable: alsa.backup
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -22,6 +22,16 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: 0.3.5
24
24
  version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: newgem
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.4.1
34
+ version:
25
35
  - !ruby/object:Gem::Dependency
26
36
  name: newgem
27
37
  type: :development
@@ -61,20 +71,26 @@ files:
61
71
  - PostInstall.txt
62
72
  - README.rdoc
63
73
  - Rakefile
74
+ - TODO
64
75
  - alsa-backup.gemspec
65
76
  - bin/alsa.backup
66
77
  - config.sample
67
78
  - lib/alsa.rb
68
79
  - lib/alsa_backup.rb
69
80
  - lib/alsa_backup/cli.rb
81
+ - lib/alsa_backup/core_ext.rb
82
+ - lib/alsa_backup/length_controller.rb
70
83
  - lib/alsa_backup/recorder.rb
84
+ - lib/alsa_backup/writer.rb
71
85
  - lib/sndfile.rb
72
86
  - script/console
73
87
  - script/destroy
74
88
  - script/generate
75
89
  - spec/alsa/pcm_spec.rb
76
90
  - spec/alsa_backup/cli_spec.rb
91
+ - spec/alsa_backup/core_ext_spec.rb
77
92
  - spec/alsa_backup/recorder_spec.rb
93
+ - spec/alsa_backup/writer_spec.rb
78
94
  - spec/alsa_backup_spec.rb
79
95
  - spec/fixtures/config_test.rb
80
96
  - spec/sndfile/info_spec.rb