summaryrefslogtreecommitdiff
path: root/spec/unicorn/unicorn_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/unicorn/unicorn_spec.rb')
-rw-r--r--spec/unicorn/unicorn_spec.rb98
1 files changed, 98 insertions, 0 deletions
diff --git a/spec/unicorn/unicorn_spec.rb b/spec/unicorn/unicorn_spec.rb
new file mode 100644
index 00000000000..8518c047a47
--- /dev/null
+++ b/spec/unicorn/unicorn_spec.rb
@@ -0,0 +1,98 @@
+require 'fileutils'
+
+require 'excon'
+
+require 'spec_helper'
+
+describe 'Unicorn' do
+ before(:all) do
+ config_lines = File.read('config/unicorn.rb.example').split("\n")
+
+ # Remove these because they make setup harder.
+ config_lines = config_lines.reject do |line|
+ %w[
+ working_directory
+ worker_processes
+ listen
+ pid
+ stderr_path
+ stdout_path
+ ].any? { |prefix| line.start_with?(prefix) }
+ end
+
+ config_lines << "working_directory '#{Rails.root}'"
+
+ # We want to have exactly 1 worker process because that makes it
+ # predictable which process will handle our requests.
+ config_lines << 'worker_processes 1'
+
+ @socket_path = File.join(Dir.pwd, 'tmp/tests/unicorn.socket')
+ config_lines << "listen '#{@socket_path}'"
+
+ ready_file = 'tmp/tests/unicorn-worker-ready'
+ FileUtils.rm_f(ready_file)
+ after_fork_index = config_lines.index { |l| l.start_with?('after_fork') }
+ config_lines.insert(after_fork_index + 1, "File.write('#{ready_file}', Process.pid)")
+
+ config_path = 'tmp/tests/unicorn.rb'
+ File.write(config_path, config_lines.join("\n") + "\n")
+
+ cmd = %W[unicorn -E test -c #{config_path} #{Rails.root.join('config.ru')}]
+ @unicorn_master_pid = spawn(*cmd)
+ wait_unicorn_boot!(@unicorn_master_pid, ready_file)
+ WebMock.allow_net_connect!
+ end
+
+ %w[SIGQUIT SIGTERM SIGKILL].each do |signal|
+ it "has a worker that self-terminates on signal #{signal}" do
+ response = Excon.get('unix:///unicorn_test/pid', socket: @socket_path)
+ expect(response.status).to eq(200)
+
+ worker_pid = response.body.to_i
+ expect(worker_pid).to be > 0
+
+ begin
+ Excon.post('unix:///unicorn_test/kill', socket: @socket_path, body: "signal=#{signal}")
+ rescue Excon::Error::Socket
+ # The connection may be closed abruptly
+ end
+
+ expect(pid_gone?(worker_pid)).to eq(true)
+ end
+ end
+
+ after(:all) do
+ WebMock.disable_net_connect!(allow_localhost: true)
+ Process.kill('TERM', @unicorn_master_pid)
+ end
+
+ def wait_unicorn_boot!(master_pid, ready_file)
+ # Unicorn should boot in under 60 seconds so 120 seconds seems like a good timeout.
+ timeout = 120
+ timeout.times do
+ return if File.exist?(ready_file)
+ pid = Process.waitpid(master_pid, Process::WNOHANG)
+ raise "unicorn failed to boot: #{$?}" unless pid.nil?
+
+ sleep 1
+ end
+
+ raise "unicorn boot timed out after #{timeout} seconds"
+ end
+
+ def pid_gone?(pid)
+ # Worker termination should take less than a second. That makes 10
+ # seconds a generous timeout.
+ 10.times do
+ begin
+ Process.kill(0, pid)
+ rescue Errno::ESRCH
+ return true
+ end
+
+ sleep 1
+ end
+
+ false
+ end
+end