describe Wisper::TemporaryListeners do
  let(:listener_1) { double('listener', :to_a => nil) } # [1]
  let(:listener_2) { double('listener', :to_a => nil) }

  let(:publisher)  { publisher_class.new }

  describe '.subscribe' do
    it 'globally subscribes listener for duration of given block' do

      expect(listener_1).to receive(:success)
      expect(listener_1).to_not receive(:failure)

      Wisper::TemporaryListeners.subscribe(listener_1) do
        publisher.instance_eval { broadcast(:success) }
      end

      publisher.instance_eval { broadcast(:failure) }
    end

    it 'globally subscribes listeners for duration of given block' do

      expect(listener_1).to receive(:success)
      expect(listener_1).to_not receive(:failure)

      expect(listener_2).to receive(:success)
      expect(listener_2).to_not receive(:failure)

      Wisper::TemporaryListeners.subscribe(listener_1, listener_2) do
        publisher.instance_eval { broadcast(:success) }
      end

      publisher.instance_eval { broadcast(:failure) }
    end

    it 'globally subscribes listeners for duration of nested block' do

      expect(listener_1).to receive(:success)
      expect(listener_1).to receive(:failure)

      expect(listener_2).to receive(:success)
      expect(listener_2).to_not receive(:failure)

      Wisper::TemporaryListeners.subscribe(listener_1) do
        Wisper::TemporaryListeners.subscribe(listener_2) do
          publisher.instance_eval { broadcast(:success) }
        end
        publisher.instance_eval { broadcast(:failure) }
      end
    end

    it 'clears registrations for the block which exits' do

      # listener_1 is subscribed twice hence it's supposed to get the message twice
      expect(listener_1).to receive(:success).twice
      expect(listener_1).to receive(:failure)
      expect(listener_1).to_not receive(:ignored)

      expect(listener_2).to receive(:success)
      expect(listener_2).to_not receive(:failure)
      expect(listener_2).to_not receive(:ignored)

      Wisper::TemporaryListeners.subscribe(listener_1) do
        Wisper::TemporaryListeners.subscribe(listener_1, listener_2) do
          publisher.instance_eval { broadcast(:success) }
        end
        publisher.instance_eval { broadcast(:failure) }
      end
      publisher.instance_eval { broadcast(:ignored) }
    end

    it 'is thread safe' do
      num_threads = 20
      (1..num_threads).to_a.map do
        Thread.new do
          Wisper::TemporaryListeners.registrations << Object.new
          expect(Wisper::TemporaryListeners.registrations.size).to eq 1
        end
      end.each(&:join)

      expect(Wisper::TemporaryListeners.registrations).to be_empty
    end

    it 'clears registrations when an exception occurs' do
      MyError = Class.new(StandardError)

      begin
        Wisper::TemporaryListeners.subscribe(listener_1) do
          raise MyError
        end
      rescue MyError
      end

      expect(Wisper::TemporaryListeners.registrations).to be_empty
    end

    it 'returns self' do
      expect(Wisper::TemporaryListeners.subscribe {}).to be_an_instance_of(Wisper::TemporaryListeners)
    end
  end
end

# [1] stubbing `to_a` prevents `Double "listener" received unexpected message
# :to_a with (no args)` on MRI 1.9.2 when a double is passed to `Array()`.