Beispiel #1
0
def test_no_resources_eternal_timeout() -> None:
    """Test behaviour with an infinite timeout when there are no resources available.

    Given:
        - a default-initialized resource pool
        - there are no resources currently available in the pool
        - and the pool has no way of creating a new
          resource (i. e., `alloc` was not given on construction)
    When:
        - client code attempts to get a resurce with an infinite timeout (=0)
    Then:
        - no exception will be raised
        - the `pop` method will block until a resource is made available
          by another thread
    """
    pool = ResourcePool()
    timeout = 1

    threading.Timer(timeout, lambda: pool.push(R())).start()

    start = now()
    with pool(timeout=0) as r:
        assert r == 0

    duration = now() - start
    assert duration == pytest.approx(timeout, abs=1e-2)
Beispiel #2
0
def test_minmax_size() -> None:
    """Check that resources are destroyed when the pool overflows.

    Given:
        - a resource pool initialized with a min and a max poolsize and a dealloc function
    When:
        - more resources than the max poolsize are acquired
        - and the resources are all put back into the pool
    Then:
        - the surplus resources will be reset using the dealloc function
    """
    pool = ResourcePool(alloc=R, dealloc=R.close, minsize=1, maxsize=2)
    a, b, c = pool.pop(), pool.pop(), pool.pop()
    pool.push(a)
    pool.push(b)
    pool.push(c)

    sleep(1)  # wait for pool's gc thread

    assert len(R.deallocated) == 2
    assert a in R.deallocated
    assert not a.alive
    assert b in R.deallocated
    assert not b.alive
    assert c.alive
Beispiel #3
0
def test_maxage() -> None:
    """Test maxage parameter."""
    size = 5
    pool = ResourcePool(alloc=now, maxage=1)

    resources = [pool.pop() for _ in range(size)]
    for r in resources:
        pool.push(r)

    assert len(pool) == size
    sleep(1.1)
    assert len(pool) == 0
Beispiel #4
0
def test_call_allocator() -> None:
    """Allocator is called without arguments.

    Given:
        - a resource pool with an `alloc` function, enabling
          creation of new resources
    When:
        - client code attempts to get a resurce
    Then:
        - the allocation method had been called
    """
    pool = ResourcePool(alloc=R)

    resource = pool.pop()

    assert resource == 0
Beispiel #5
0
def test_limited_resources_with_timeout() -> None:
    """Test mltiple threads trying to allocate from a limited resource pool.

    Given:
        - a resource pool with al limited set of resources
    When:
        - multiple threads try to get a resurce with a timeout
        - and thread receiving the resource return them to the pool
          immediately, thus not triggering a timeout for other threads
    Then:
        - all threads will get a resource in time
    """
    def use(x: int) -> int:
        """Allocate a resource, ignore it, and return the given argument."""
        with pool(timeout=timeout):
            return x

    pool = ResourcePool(init=R.create(1), check=lambda r: r.alive)
    timeout = 2

    with ThreadPoolExecutor(max_workers=20) as executor:
        futures = executor.map(use, range(10))
        results = set(futures)

    assert len(results) == 10
Beispiel #6
0
def test_no_resources() -> None:
    """Test exception raised when there are no resources available.

    Given:
        - a default-initialized resource pool
        - there are no resources currently available in the pool
        - and the pool has no way of creating a new
          resource (i. e., `alloc` was not given on construction)
    When:
        - client code attempts to get a resurce without giving
          the `timeout` argument
    Then:
        - an exception of type `ResourcePoolEmpty` will be raised
    """
    pool = ResourcePool()

    with pytest.raises(ResourcePoolEmpty):
        pool.pop()
Beispiel #7
0
def test_reuse() -> None:
    """Resources are actually reused after returning."""
    allocate = MagicMock()
    allocate.side_effect = [1, 2, 3, 4]
    pool = ResourcePool(alloc=allocate)

    resources = []
    for _ in range(3):
        with pool() as r:
            resources.append(r)

    pool.push(100)

    resources.append(pool.pop())
    for _ in range(2):
        with pool() as r:
            resources.append(r)

    allocate.assert_called_once()
    assert resources == [1, 1, 1, 100, 1, 1]
Beispiel #8
0
def test_no_resources_with_timeout() -> None:
    """Test exception raised with a timeout when there are no resources available.

    Given:
        - a default-initialized resource pool
        - there are no resources currently available in the pool
        - and the pool has no way of creating a new
          resource (i. e., `alloc` was not given on construction)
    When:
        - client code attempts to get a resurce with a timeout
    Then:
        - an exception of type `ResourcePoolEmpty` will be raised
        - the exception will be raised after the timeout period
    """
    pool = ResourcePool()
    timeout = 1

    start = now()
    with pytest.raises(ResourcePoolEmpty):
        pool.pop(timeout=timeout)

    duration = now() - start
    assert duration == pytest.approx(timeout, abs=1e-2)
Beispiel #9
0
def test_init() -> None:
    """Test using an initial resource list.

    Given:
        - a resource pool with an initial list of resources
        - and the pool has no way of creating a new
          resource (i. e., `alloc` was not given on construction)
    When:
        - client code attempts to get multiple resurces
    Then:
        - the resources will be the ones from the original pool
    """
    init = R.create(10)

    pool = ResourcePool(init=init)

    for x in range(100):
        with pool() as r:
            assert r == 0
Beispiel #10
0
def test_provide_additional_resource() -> None:
    """Resources are actually reused after returning."""
    allocate = MagicMock()
    allocate.side_effect = R.create(4)
    pool = ResourcePool(alloc=allocate)

    resources = [pool.pop() for _ in range(3)]

    R.set(100)
    pool.push(R())

    resources.append(pool.pop())
    resources.append(pool.pop())

    allocate.assert_has_calls([call(), call(), call(), call()])
    assert resources == [0, 1, 2, 100, 3]
Beispiel #11
0
def test_closed_resources_with_timeout_exception() -> None:
    """Test multiple threads trying to allocate from a limited resource pool.

    Given:
        - a resource pool with al limited set of a single resource
        - the single resource is invalid
    When:
        - multiple threads try to get a resurce with a timeout
    Then:
        - an exception of type `ResourcePoolEmpty` will be raised
    """
    def use(x: int) -> int:
        """Allocate a resource, ignore it, and return the given argument."""
        with pool(timeout=timeout):
            sleep(sleeptime)
            return x

    def push(resource: R) -> None:
        """Push one more resource."""
        sleep(sleeptime)
        pool.push(resource)

    dead = R()
    dead.close()
    pool = ResourcePool(init=[], check=lambda r: r.alive)
    timeout = 2
    sleeptime = 0.1

    start = now()

    with ThreadPoolExecutor(max_workers=20) as executor:
        futures = executor.map(use, range(10))
        executor.submit(push, dead)
        with pytest.raises(ResourcePoolEmpty):
            set(futures)

    duration = now() - start
    assert duration == pytest.approx(timeout, abs=1e-2)
Beispiel #12
0
def test_closed_resources_with_timeout() -> None:
    """Test multiple threads trying to allocate from a limited resource pool.

    Given:
        - a resource pool with al limited set of a single resource
        - the single resource is invalid
    When:
        - multiple threads try to get a resurce with a timeout
        - another thread pushes a valid resource
        - and thread receiving the resource return them to the pool
          immediately, thus not triggering a timeout for other threads
    Then:
        - all threads will get a resource in time
    """
    def use(x: int) -> int:
        """Allocate a resource, ignore it, and return the given argument."""
        with pool(timeout=timeout):
            sleep(sleeptime)
            return x

    def push(resource: R) -> None:
        """Push one more resource."""
        sleep(sleeptime)
        pool.push(resource)

    alive, dead = R.create(2)
    dead.close()
    pool = ResourcePool(init=[], check=lambda r: r.alive)
    timeout = 2
    sleeptime = 0.1

    with ThreadPoolExecutor(max_workers=20) as executor:
        futures = executor.map(use, range(10))
        executor.submit(push, dead)
        executor.submit(push, alive)
        results = set(futures)

    assert len(results) == 10
Beispiel #13
0
def test_maxage_minsize() -> None:
    """Test maxage parameter with minimum size."""
    size = 5
    pool = ResourcePool(alloc=now, maxage=1, minsize=1)

    resources = [pool.pop() for _ in range(size)]
    assert len(pool) == 0

    for r in resources:
        sleep(0.1)
        pool.push(r)

    assert len(pool) == size
    sleep(2)
    assert len(pool) == 1
    expected = max(resources)
    actual = pool.pop()
    assert actual == expected
Beispiel #14
0
def test_max_size_no_overflow() -> None:
    """Check that resources are not destroyed unless the pool overflows.

    Given:
        - a resource pool initialized with a max poolsize and a dealloc function
    When:
        - the maxsize amount of resources are allocated
        - and the resources are all put back into the pool
    Then:
        - no resources will be reset using the dealloc function
    """
    pool = ResourcePool(alloc=R, dealloc=R.close, minsize=0, maxsize=2)
    a, b = pool.pop(), pool.pop()
    pool.push(a)
    pool.push(b)

    sleep(1)  # wait for pool's gc thread
    assert len(R.deallocated) == 0
    assert a.alive
    assert b.alive
Beispiel #15
0
def test_minsize_init() -> None:
    """Test generation of initial values."""
    pool = ResourcePool(init=[1, 2, 3], alloc=R, minsize=6)
    assert len(pool) == 6
Beispiel #16
0
def test_minsize() -> None:
    """Test generation of initial values."""
    pool = ResourcePool(alloc=R, minsize=3)
    assert len(pool) == 3