/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.common.objectpool;
import java.util.Vector;
import com.facebook.common.testing.FakeClock;
import com.facebook.testing.robolectric.v2.WithTestDefaultsRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import static org.junit.Assert.*;
/**
* Test for {@link ObjectPool}, {@link ObjectPoolManager}.
*/
@RunWith(WithTestDefaultsRunner.class)
public class ObjectPoolTest {
private FakeClock fakeClock;
@Before
public void setUp() throws Exception {
this.fakeClock = new FakeClock();
}
@Test
public void testPoolConstruction() {
ObjectPoolManager manager = new ObjectPoolManager(fakeClock);
TestAllocator alloc = new TestAllocator();
ObjectPool<String> pool = manager.createPoolBuilder(String.class)
.setMinimumSize(101)
.setMaximumSize(303)
.setIncrementSize(18)
.setCompactionDelay(805)
.setAllocator(alloc)
.build();
assertEquals(101, pool.getMinimumSize());
assertEquals(303, pool.getMaximumSize());
assertEquals(805, pool.getCompactionDelayMs());
assertEquals(18, pool.getIncrementSize());
// make sure the allocator we specified is actually the one attached to the pool
String s = pool.allocate();
assertEquals(1, alloc.allocateCallCount);
pool.release(s);
assertEquals(1, alloc.releaseCallCount);
}
@Test
public void testPool() {
ObjectPoolManager manager = new ObjectPoolManager(fakeClock);
TestAllocator alloc = new TestAllocator();
ObjectPool<String> pool = manager.createPoolBuilder(String.class)
.setMinimumSize(16)
.setMaximumSize(100)
.setIncrementSize(16)
.setCompactionDelay(100)
.setAllocator(alloc)
.build();
// The new pool we got should be the same pool we get from the manager afterwards
assertEquals(pool, manager.getPool(String.class));
String s = pool.allocate();
// Only check that create must be called at least this many times, to account for the pool
// possibly eagerly creating objects to fill the pool
assertTrue(alloc.createCallCount >= 1);
// Our allocator should be notified whenever there is an allocation
assertEquals(1, alloc.allocateCallCount);
assertTrue(s != null);
// Allocating one object should not have changed the backing size of the array
assertEquals(pool.getMinimumSize(), pool.getPoolSize());
int poolSize = pool.getPooledObjectCount();
pool.release(s);
// ... or a release
assertEquals(1, alloc.releaseCallCount);
// The object should have returned to the pool
assertEquals(poolSize + 1, pool.getPooledObjectCount());
// Our minimum size was enough that we should not have had to increase the pool size
assertEquals(pool.getMinimumSize(), pool.getPoolSize());
int prevCallCount = alloc.createCallCount;
s = pool.allocate();
// Reallocating from the pool should call our allocation function again even though it should
// be serviced from the pool
assertEquals(prevCallCount, alloc.createCallCount);
assertEquals(2, alloc.allocateCallCount);
alloc.resetCallCounts();
Vector<String> store = new Vector<String>();
for (int i = 0; i < pool.getMinimumSize() + 1; ++i) {
store.add(pool.allocate());
}
// We shouldn't have increased the pool size during allocation
assertEquals(pool.getMinimumSize(), pool.getPoolSize());
for (String str : store) {
pool.release(str);
}
store.clear();
// ... but we should have upon releasing
assertEquals(pool.getMinimumSize() + pool.getIncrementSize(), pool.getPoolSize());
alloc.resetCallCounts();
for (int i = 0; i < pool.getIncrementSize() * 2 + 1; ++i) {
store.add(pool.allocate());
}
for (String str : store) {
pool.release(str);
}
store.clear();
pool.checkUsage();
// We should not have resized our pool yet because the clock has not advanced
assertEquals(pool.getMinimumSize() + pool.getIncrementSize() * 2, pool.getPoolSize());
fakeClock.incrementBy(pool.getCompactionDelayMs() + 1);
pool.checkUsage();
// We should have lopped off incrementSize number of elements in our pool
assertEquals(pool.getMinimumSize() + pool.getIncrementSize(), pool.getPoolSize());
// If we force a compaction, then it must shrink
pool.compactUsage();
assertEquals(pool.getMinimumSize(), pool.getPoolSize());
for (int i = 0; i < 2 * pool.getMaximumSize(); ++i) {
store.add(pool.allocate());
}
for (String str : store) {
pool.release(str);
}
store.clear();
// Regardless of how many objects we allocate, we should only have up to the maximum
assertEquals(pool.getMaximumSize(), pool.getPoolSize());
}
public class TestAllocator extends ObjectPool.BasicAllocator<String> {
public int createCallCount;
public int allocateCallCount;
public int releaseCallCount;
public TestAllocator() {
super(String.class);
resetCallCounts();
}
public void resetCallCounts() {
createCallCount = 0;
allocateCallCount = 0;
releaseCallCount = 0;
}
@Override
public String create() {
++createCallCount;
String s = super.create();
assertNotNull(s);
return s;
}
@Override
public void onAllocate(String obj) {
++allocateCallCount;
assertNotNull(obj);
super.onAllocate(obj);
}
@Override
public void onRelease(String obj) {
++releaseCallCount;
assertNotNull(obj);
super.onRelease(obj);
}
}
}