require 'spec_helper'

unless defined?(::JRUBY_VERSION)
  describe %[forked connection] do
    let(:path) { "/_zktest_" }
    let(:pids_root) { "#{path}/pids" }
    let(:data) { "underpants" }
    let(:connection_string) { Zookeeper.default_cnx_str }

    def process_alive?(pid)
      Process.kill(0, @pid)
      true
    rescue Errno::ESRCH
      false
    end

    LBORDER = ('-' * 35) << '< '
    RBORDER = ' >' << ('-' * 35)

    def mark(thing)
      logger << "\n#{LBORDER}#{thing}#{RBORDER}\n\n"
    end

    before do
      mark "BEFORE: START"
      if defined?(::Rubinius)
        pending("this test is currently broken in rbx")
      else
        @zk = Zookeeper.new(connection_string)
        rm_rf(@zk, path)
      end
      mark "BEFORE: END"
    end

    after do
      mark "AFTER: START"

      if @pid and process_alive?(@pid)
        begin
          Process.kill('KILL', @pid)
          p Process.wait2(@pid)
        rescue Errno::ESRCH
        end
      end

      @zk.close if @zk and !@zk.closed?
      with_open_zk(connection_string) { |z| rm_rf(z, path) }

      mark "AFTER: END"
    end

    def wait_for_child_safely(pid, timeout=5)
      time_to_stop = Time.now + timeout

      until Time.now > time_to_stop
        if a = Process.wait2(@pid, Process::WNOHANG)
          return a.last
        else
          sleep(0.01)
        end
      end

      nil
    end

    it %[should do the right thing and not fail] do
      mark "TEST: START"

      @zk.wait_until_connected

      mkdir_p(@zk, pids_root)

      # the parent's pid path
      @zk.create(:path => "#{pids_root}/#{$$}", :data => $$.to_s)

      @latch = Zookeeper::Latch.new
      @event = nil

      cb = proc do |h|
        logger.debug { "watcher called back: #{h.inspect}" }
        @event = h
        @latch.release
      end

      @zk.stat(:path => "#{pids_root}/child", :watcher => cb)

      @zk.pause_before_fork_in_parent

      mark "FORK"

      @pid = fork do
        logger.debug { "reopening connection in child: #{$$}" }
        @zk.reopen
        logger.debug { "creating path" }
        rv = @zk.create(:path => "#{pids_root}/child", :data => $$.to_s)
        logger.debug { "created path #{rv[:path]}" }
        @zk.close

        logger.debug { "close finished" }
        exit!(0)
      end

      @zk.resume_after_fork_in_parent

      event_waiter_th = Thread.new do
        @latch.await(5) unless @event
        @event
      end

      logger.debug { "waiting on child #{@pid}" }

      status = wait_for_child_safely(@pid)
      raise "Child process did not exit, likely hung"  unless status

      expect(status).not_to be_signaled
      expect(status).to be_success

      expect(event_waiter_th.join(5)).to eq(event_waiter_th)
      expect(@event).not_to be_nil
    end
  end
end
