require_relative "setup"

RSpec.describe XPool do
  let(:pool_size) do
    2
  end

  let(:pool) do
    xpool size: pool_size
  end

  let(:tmpdir) do
    File.join __dir__, "tmp"
  end

  after do
    pool.shutdown
  end

  describe "#broadcast" do
    it "broadcasts a callable across a pool" do
      processes = pool.broadcast Sleep, 1
      processes.each { |process| expect(process.call_count).to eq(1) }
    end
  end

  describe "#size" do
    it "returns the number of processes in a pool" do
      expect(pool.size).to eq(pool_size)
    end
  end

  describe "#shutdown" do
    let(:paths) { Array.new(pool_size) { File.join(tmpdir, "xpool_test_#{_1}") } }

    after do
      paths.each { File.unlink(_1) if File.exist?(_1) }
    end

    it "allows callables to clear the queue before shutting down" do
      paths.each { pool.schedule(SlowFileWrite, _1) }
      pool.shutdown
      paths.each { expect(File.read(_1)).to eq("123") }
    end
  end

  describe "#schedule" do
    it "distrubutes a callable across the pool" do
      child_proccesses = Array.new(pool_size) { pool.schedule Sleep, 0.1 }
      child_proccesses.each { |subprocess| expect(subprocess.call_count).to eq(1) }
    end

    it "schedules a nested module" do
      path = File.join(tmpdir, "nested_module_test")
      pool.schedule XChan::SlowFileWrite, path
      pool.shutdown
      expect(File.read(path)).to eq('123')
    ensure
      File.unlink(path) if File.exist?(path)
    end

    it "raises when given an object other than Module" do
      expect { pool.schedule(::Object.new).to raise_error(ArgumentError) }
    end

    it "raises when given an anonymous module" do
      expect { pool.schedule Module.new { } }.to raise_error(ArgumentError)
    end
  end

  describe "#resize" do
    it "resizes the pool to the given number" do
      pool.resize(1)
      expect(pool.size).to eq(1)
    end
  end

  describe "#expand" do
    it "expands the pool size by 1" do
      pool.expand(1)
      expect(pool.size).to eq(pool_size + 1)
    end
  end

  describe "#shrink" do
    it "shrinks the pool size by 1 (graceful shutdown)" do
      pool.shrink(1)
      expect(pool.size).to eq(pool_size - 1)
    end

    it "decreases size to zero when given a size larger than the pool size" do
      pool.shrink pool.size + 1
      expect(pool.size).to eq(0)
    end
  end
end
