Current section

Files

Jump to
fixpoint test space thread_pool_test.exs
Raw

test/space/thread_pool_test.exs

defmodule CPSolverTest.Space.ThreadPool do
use ExUnit.Case
alias CPSolver.Space.ThreadPool
describe "The pool for space threads" do
test "checkouts" do
num_threads = 4
{:ok, thread_pool} = ThreadPool.new(num_threads)
{:ok, %{queue: q}} = ThreadPool.get_pool_state(thread_pool)
assert 0 = :queue.len(q)
## If number of checkouts does not exceed the pool capacity,
## the process queue is empty.
assert Enum.all?(1..num_threads, fn i ->
spawn(fn ->
ThreadPool.run_task(
fn -> :timer.sleep(100) end,
thread_pool)
end)
## Give it a bit of time for the process to check out.
:timer.sleep(10)
{:ok, %{queue: q, available: available_threads}} = ThreadPool.get_pool_state(thread_pool)
## No queue, available thread count is down with every checkout
:queue.len(q) == 0 && available_threads == num_threads - i
end)
## No available threads now
{:ok, %{available: available}} = ThreadPool.get_pool_state(thread_pool)
assert 0 = available
## This process will be added to pool queue
my_pid = self()
waiting_process = spawn(fn ->
ThreadPool.run_task(
fn ->
send(my_pid, {:completed, self()})
end,
thread_pool)
end)
## Give it a bit of time for the process to be added to the thread pool.
:timer.sleep(10)
{:ok, %{queue: q}} = ThreadPool.get_pool_state(thread_pool)
assert :queue.len(q) == 1
## Wait for any of the processes previously checked out to complete and check in
:timer.sleep(100)
## The waiting process should be removed from the queue...
{:ok, %{queue: q}} = ThreadPool.get_pool_state(thread_pool)
assert :queue.len(q) == 0
## ...and processed
assert_receive {:completed, ^waiting_process}
## The pool is now at full capacity
{:ok, %{available: available}} = ThreadPool.get_pool_state(thread_pool)
assert num_threads == available
end
test "checkins" do
num_threads = 4
{:ok, thread_pool} = ThreadPool.new(num_threads)
## Checking in while the pool is at capacity
sleep_interval = 100
spawn(fn ->
ThreadPool.checkin(thread_pool)
:timer.sleep(sleep_interval)
end)
## Give some time for the process to check in, but not to complete
:timer.sleep(div(sleep_interval, 2))
{:ok, %{available: available}} = ThreadPool.get_pool_state(thread_pool)
## No effect on the pool
assert num_threads == available
## Check out all the capacity
Enum.each(1..num_threads, fn _ ->
ThreadPool.checkout(thread_pool)
:timer.sleep(10)
end)
{:ok, %{available: available, queue: queue}} = ThreadPool.get_pool_state(thread_pool)
assert 0 = available
assert 0 = :queue.len(queue)
## This process will wait until first check-in
waiting_process = spawn(fn ->
ThreadPool.checkout(thread_pool)
end)
:timer.sleep(10)
{:ok, %{queue: queue, available: available}} = ThreadPool.get_pool_state(thread_pool)
assert 0 = available
{:value, {process_pid, _ref}} = :queue.peek(queue)
## Process is in the queue
assert waiting_process == process_pid
## Process is alive
assert Process.alive?(waiting_process)
## Now, do a check-in
ThreadPool.checkin(thread_pool)
:timer.sleep(10)
## The process has completed
refute Process.alive?(waiting_process)
## The queue has been cleaned, but there is still no available threads
{:ok, %{queue: queue, available: available}} = ThreadPool.get_pool_state(thread_pool)
assert (0 = available)
assert :queue.len(queue) == 0
## Another check-in to free up one thread
ThreadPool.checkin(thread_pool)
:timer.sleep(10)
{:ok, %{available: available}} = ThreadPool.get_pool_state(thread_pool)
assert 1 = available
end
end
end