test/unit/process_tests.rb in qs-0.1.0 vs test/unit/process_tests.rb in qs-0.2.0
- old
+ new
@@ -16,13 +16,13 @@
end
class InitTests < UnitTests
desc "when init"
setup do
- @current_env_process_name = ENV['QS_PROCESS_NAME']
+ @current_env_process_label = ENV['QS_PROCESS_LABEL']
@current_env_skip_daemonize = ENV['QS_SKIP_DAEMONIZE']
- ENV.delete('QS_PROCESS_NAME')
+ ENV.delete('QS_PROCESS_LABEL')
ENV.delete('QS_SKIP_DAEMONIZE')
@daemon_spy = DaemonSpy.new
@pid_file_spy = PIDFileSpy.new(Factory.integer)
@@ -35,38 +35,40 @@
@process = @process_class.new(@daemon_spy)
end
teardown do
ENV['QS_SKIP_DAEMONIZE'] = @current_env_skip_daemonize
- ENV['QS_PROCESS_NAME'] = @current_env_process_name
+ ENV['QS_PROCESS_LABEL'] = @current_env_process_label
end
subject{ @process }
- should have_readers :daemon, :name, :pid_file, :restart_cmd
- should have_imeths :run, :daemonize?, :restart?
+ should have_readers :daemon, :name
+ should have_readers :pid_file, :signal_io, :restart_cmd
+ should have_imeths :run, :daemonize?
should "know its daemon" do
assert_equal @daemon_spy, subject.daemon
end
- should "know its name, pid file and restart cmd" do
- assert_equal "qs-#{@daemon_spy.name}", subject.name
+ should "know its name, pid file, signal io and restart cmd" do
+ assert_equal "qs: #{@daemon_spy.name}", subject.name
assert_equal @pid_file_spy, subject.pid_file
+ assert_instance_of Qs::IOPipe, subject.signal_io
assert_equal @restart_cmd_spy, subject.restart_cmd
end
should "set its name using env vars" do
- ENV['QS_PROCESS_NAME'] = Factory.string
+ ENV['QS_PROCESS_LABEL'] = Factory.string
process = @process_class.new(@daemon_spy)
- assert_equal ENV['QS_PROCESS_NAME'], process.name
+ assert_equal "qs: #{ENV['QS_PROCESS_LABEL']}", process.name
end
should "ignore blank env values for its name" do
- ENV['QS_PROCESS_NAME'] = ''
+ ENV['QS_PROCESS_LABEL'] = ''
process = @process_class.new(@daemon_spy)
- assert_equal "qs-#{@daemon_spy.name}", process.name
- end
+ assert_equal "qs: #{@daemon_spy.name}", process.name
+ end
should "not daemonize by default" do
process = @process_class.new(@daemon_spy)
assert_false process.daemonize?
end
@@ -88,134 +90,197 @@
ENV['QS_SKIP_DAEMONIZE'] = ''
process = @process_class.new(@daemon_spy, :daemonize => true)
assert_true process.daemonize?
end
- should "not restart by default" do
- assert_false subject.restart?
- end
-
end
class RunSetupTests < InitTests
setup do
@daemonize_called = false
Assert.stub(::Process, :daemon).with(true){ @daemonize_called = true }
@current_process_name = $0
- @term_signal_trap_block = nil
- @term_signal_trap_called = false
- Assert.stub(::Signal, :trap).with("TERM") do |&block|
- @term_signal_trap_block = block
- @term_signal_trap_called = true
+ @signal_traps = []
+ Assert.stub(::Signal, :trap) do |signal, &block|
+ @signal_traps << SignalTrap.new(signal, block)
end
-
- @int_signal_trap_block = nil
- @int_signal_trap_called = false
- Assert.stub(::Signal, :trap).with("INT") do |&block|
- @int_signal_trap_block = block
- @int_signal_trap_called = true
- end
-
- @usr2_signal_trap_block = nil
- @usr2_signal_trap_called = false
- Assert.stub(::Signal, :trap).with("USR2") do |&block|
- @usr2_signal_trap_block = block
- @usr2_signal_trap_called = true
- end
end
teardown do
+ @process.signal_io.write(HALT)
+ @thread.join if @thread
$0 = @current_process_name
end
end
class RunTests < RunSetupTests
desc "and run"
setup do
- @process.run
+ @thread = Thread.new{ @process.run }
+ @thread.join(0.1)
end
- should "not have daemonized the process" do
+ should "not daemonize the process" do
assert_false @daemonize_called
end
- should "have set the process name" do
+ should "set the process name" do
assert_equal $0, subject.name
end
- should "have written the PID file" do
+ should "write its PID file" do
assert_true @pid_file_spy.write_called
end
- should "have trapped signals" do
- assert_true @term_signal_trap_called
- assert_false @daemon_spy.stop_called
- @term_signal_trap_block.call
- assert_true @daemon_spy.stop_called
-
- assert_true @int_signal_trap_called
- assert_false @daemon_spy.halt_called
- @int_signal_trap_block.call
- assert_true @daemon_spy.halt_called
-
- @daemon_spy.stop_called = false
-
- assert_true @usr2_signal_trap_called
- assert_false subject.restart?
- @usr2_signal_trap_block.call
- assert_true @daemon_spy.stop_called
- assert_true subject.restart?
+ should "trap signals" do
+ assert_equal 3, @signal_traps.size
+ assert_equal ['INT', 'TERM', 'USR2'], @signal_traps.map(&:signal)
end
- should "have started the daemon" do
+ should "start the daemon" do
assert_true @daemon_spy.start_called
end
- should "have joined the daemon thread" do
- assert_true @daemon_spy.thread.join_called
+ should "sleep its thread waiting for signals" do
+ assert_equal 'sleep', @thread.status
end
should "not run the restart cmd" do
assert_false @restart_cmd_spy.run_called
end
- should "have removed the PID file" do
- assert_true @pid_file_spy.remove_called
+ end
+
+ class SignalTrapsTests < RunSetupTests
+ desc "signal traps"
+ setup do
+ # setup the io pipe so we can see whats written to it
+ @process.signal_io.setup
end
+ teardown do
+ @process.signal_io.teardown
+ end
+ should "write the signals to processes signal IO" do
+ @signal_traps.each do |signal_trap|
+ signal_trap.block.call
+ assert_equal signal_trap.signal, subject.signal_io.read
+ end
+ end
+
end
class RunWithDaemonizeTests < RunSetupTests
- desc "that should daemonize is run"
+ desc "and run when it should daemonize"
setup do
Assert.stub(@process, :daemonize?){ true }
- @process.run
+ @thread = Thread.new{ @process.run }
+ @thread.join(0.1)
end
- should "have daemonized the process" do
+ should "daemonize the process" do
assert_true @daemonize_called
end
end
- class RunAndDaemonPausedTests < RunSetupTests
- desc "then run and sent a restart signal"
+ class RunAndHaltTests < RunSetupTests
+ desc "and run with a halt signal"
setup do
- # mimicing pause being called by a signal, after the thread is joined
- @daemon_spy.thread.on_join{ @usr2_signal_trap_block.call }
- @process.run
+ @thread = Thread.new{ @process.run }
+ @process.signal_io.write(HALT)
+ @thread.join(0.1)
end
- should "set env vars for restarting and run the restart cmd" do
+ should "halt its daemon" do
+ assert_true @daemon_spy.halt_called
+ assert_equal [true], @daemon_spy.halt_args
+ end
+
+ should "not set the env var to skip daemonize" do
+ assert_equal @current_env_skip_daemonize, ENV['QS_SKIP_DAEMONIZE']
+ end
+
+ should "not run the restart cmd" do
+ assert_false @restart_cmd_spy.run_called
+ end
+
+ should "remove the PID file" do
+ assert_true @pid_file_spy.remove_called
+ end
+
+ end
+
+ class RunAndStopTests < RunSetupTests
+ desc "and run with a stop signal"
+ setup do
+ @thread = Thread.new{ @process.run }
+ @process.signal_io.write(STOP)
+ @thread.join(0.1)
+ end
+
+ should "stop its daemon" do
+ assert_true @daemon_spy.stop_called
+ assert_equal [true], @daemon_spy.stop_args
+ end
+
+ should "not set the env var to skip daemonize" do
+ assert_equal @current_env_skip_daemonize, ENV['QS_SKIP_DAEMONIZE']
+ end
+
+ should "not run the restart cmd" do
+ assert_false @restart_cmd_spy.run_called
+ end
+
+ should "remove the PID file" do
+ assert_true @pid_file_spy.remove_called
+ end
+
+ end
+
+ class RunAndRestartTests < RunSetupTests
+ desc "and run with a restart signal"
+ setup do
+ @thread = Thread.new{ @process.run }
+ @process.signal_io.write(RESTART)
+ @thread.join(0.1)
+ end
+
+ should "stop its daemon" do
+ assert_true @daemon_spy.stop_called
+ assert_equal [true], @daemon_spy.stop_args
+ end
+
+ should "set the env var to skip daemonize" do
assert_equal 'yes', ENV['QS_SKIP_DAEMONIZE']
+ end
+
+ should "run the restart cmd" do
assert_true @restart_cmd_spy.run_called
end
end
+ class RunWithInvalidSignalTests < RunSetupTests
+ desc "and run with unsupported signals"
+ setup do
+ # ruby throws an argument error if the OS doesn't support a signal
+ Assert.stub(::Signal, :trap){ raise ArgumentError }
+
+ @thread = Thread.new{ @process.run }
+ @thread.join(0.1)
+ end
+
+ should "start normally" do
+ assert_true @daemon_spy.start_called
+ assert_equal 'sleep', @thread.status
+ end
+
+ end
+
class RestartCmdTests < UnitTests
desc "RestartCmd"
setup do
@current_pwd = ENV['PWD']
ENV['PWD'] = Factory.path
@@ -260,11 +325,11 @@
end
end
class RestartCmdWithPWDEnvNoMatchTests < RestartCmdTests
- desc "when init with a PWD env variable that doesn't point to ruby working dir"
+ desc "when init with a PWD env variable that's not the ruby working dir"
setup do
@restart_cmd = @cmd_class.new
end
should "know its dir" do
@@ -272,11 +337,11 @@
end
end
class RestartCmdWithPWDEnvInitTests < RestartCmdTests
- desc "when init with a PWD env variable that points to the ruby working dir"
+ desc "when init with a PWD env variable that's the ruby working dir"
setup do
# make ENV['PWD'] point to the same file as Dir.pwd
Assert.stub(File, :stat).with(ENV['PWD']){ @ruby_pwd_stat }
@restart_cmd = @cmd_class.new
end
@@ -298,62 +363,45 @@
assert_equal Dir.pwd, subject.dir
end
end
+ SignalTrap = Struct.new(:signal, :block)
+
class DaemonSpy
include Qs::Daemon
name Factory.string
pid_file Factory.file_path
queue Qs::Queue.new{ name Factory.string }
attr_accessor :start_called, :stop_called, :halt_called
- attr_reader :start_args
- attr_reader :thread
+ attr_reader :start_args, :stop_args, :halt_args
def initialize(*args)
super
+ @start_args = nil
@start_called = false
- @stop_called = false
- @halt_called = false
-
- @start_args = nil
-
- @thread = ThreadSpy.new
+ @stop_args = nil
+ @stop_called = false
+ @halt_args = nil
+ @halt_called = false
end
def start(*args)
- @start_args = args
+ @start_args = args
@start_called = true
- @thread
end
def stop(*args)
+ @stop_args = args
@stop_called = true
end
def halt(*args)
+ @halt_args = args
@halt_called = true
- end
- end
-
- class ThreadSpy
- attr_reader :join_called, :on_join_proc
-
- def initialize
- @join_called = false
- @on_join_proc = proc{ }
- end
-
- def on_join(&block)
- @on_join_proc = block
- end
-
- def join
- @join_called = true
- @on_join_proc.call
end
end
class RestartCmdSpy
attr_reader :run_called