require 'spec_helper'

class TestServer
  include Logging # Daemonizable should include this?
  include Daemonizable
  
  def stop
  end
  
  def name
    'Thin test server'
  end
end

describe 'Daemonizing' do
  before :all do
    @logfile = File.dirname(__FILE__) + '/../log/daemonizing_test.log'
    @pidfile = 'test.pid'
    File.delete(@logfile) if File.exist?(@logfile)
    File.delete(@pidfile) if File.exist?(@pidfile)
  end
  
  before :each do
    @server = TestServer.new
    @server.log_file = @logfile
    @server.pid_file = @pidfile
    @pid = nil
  end
  
  it 'should have a pid file' do
    @server.should respond_to(:pid_file)
    @server.should respond_to(:pid_file=)
  end
  
  it 'should create a pid file' do
    @pid = fork do
      @server.daemonize
      sleep 1
    end
    
    sleep 1
    Process.wait(@pid)
    File.exist?(@server.pid_file).should be_true
    @pid = @server.pid

    proc { sleep 0.1 while File.exist?(@server.pid_file) }.should take_less_then(5)
  end
  
  it 'should redirect stdio to a log file' do
    @pid = fork do
      @server.log_file = 'daemon_test.log'
      @server.daemonize

      puts "simple puts"
      STDERR.puts "STDERR.puts"
      STDOUT.puts "STDOUT.puts"
    end
    Process.wait(@pid)
    # Wait for the file to close and magical stuff to happen
    proc { sleep 0.1 until File.exist?('daemon_test.log') }.should take_less_then(3)
    sleep 0.5
    
    @pid = @server.pid

    log = File.read('daemon_test.log')
    log.should include('simple puts', 'STDERR.puts', 'STDOUT.puts')
    
    File.delete 'daemon_test.log'
  end
  
  it 'should change privilege' do
    @pid = fork do
      @server.daemonize
      @server.change_privilege('root', 'admin')
    end
    Process.wait(@pid)
    $?.should be_a_success
  end
  
  it 'should kill process in pid file' do
    @pid = fork do
      @server.daemonize
      loop { sleep 3 }
    end
  
    server_should_start_in_less_then 3
    
    @pid = @server.pid
  
    timeout(10) do
      silence_stream STDOUT do
        TestServer.kill(@server.pid_file, 1)
      end
    end
  
    File.exist?(@server.pid_file).should be_false
  end
  
  it 'should force kill process in pid file' do
    @pid = fork do
      @server.daemonize
      loop { sleep 3 }
    end
  
    server_should_start_in_less_then 3
    
    @pid = @server.pid
  
    timeout(10) do
      silence_stream STDOUT do
        TestServer.kill(@server.pid_file, 0)
      end
    end
  
    File.exist?(@server.pid_file).should be_false
  end
  
  it 'should send kill signal if timeout' do
    @pid = fork do
      @server.should_receive(:stop) # pretend we cannot handle the INT signal
      @server.daemonize
      sleep 5
    end
  
    server_should_start_in_less_then 10
    
    @pid = @server.pid
  
    silence_stream STDOUT do
      TestServer.kill(@server.pid_file, 1)
    end
    
    sleep 1
  
    File.exist?(@server.pid_file).should be_false
    Process.running?(@pid).should be_false
  end
  
  it "should restart" do
    @pid = fork do
      @server.on_restart {}
      @server.daemonize
      sleep 5
    end
    
    server_should_start_in_less_then 10
    
    @pid = @server.pid
  
    silence_stream STDOUT do
      TestServer.restart(@server.pid_file)
    end
    
    proc { sleep 0.1 while File.exist?(@server.pid_file) }.should take_less_then(10)
  end
  
  it "should ignore if no restart block specified" do
    TestServer.restart(@server.pid_file)
  end
  
  it "should not restart when not running" do
    silence_stream STDOUT do
      TestServer.restart(@server.pid_file)
    end
  end
  
  it "should exit and raise if pid file already exist" do
    @pid = fork do
      @server.daemonize
      sleep 5
    end
    server_should_start_in_less_then 10
    
    @pid = @server.pid

    proc { @server.daemonize }.should raise_error(PidFileExist)
    
    File.exist?(@server.pid_file).should be_true
  end
  
  it "should should delete pid file if stale" do
    # Create a file w/ a PID that does not exist
    File.open(@server.pid_file, 'w') { |f| f << 999999999 }
    
    @server.send(:remove_stale_pid_file)
    
    File.exist?(@server.pid_file).should be_false
  end
  
  after do
    Process.kill(9, @pid.to_i) if @pid && Process.running?(@pid.to_i)
    Process.kill(9, @server.pid) if @server.pid && Process.running?(@server.pid)
    File.delete(@server.pid_file) rescue nil
  end
  
  private
    def server_should_start_in_less_then(sec=10)
      proc { sleep 0.1 until File.exist?(@server.pid_file) }.should take_less_then(10)
    end
end
