/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.common.recycler;
import org.elasticsearch.common.recycler.Recycler.V;
import org.elasticsearch.test.ESTestCase;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public abstract class AbstractRecyclerTestCase extends ESTestCase {
// marker states for data
protected static final byte FRESH = 1;
protected static final byte RECYCLED = 2;
protected static final byte DEAD = 42;
protected static final Recycler.C<byte[]> RECYCLER_C = new AbstractRecyclerC<byte[]>() {
@Override
public byte[] newInstance(int sizing) {
byte[] value = new byte[10];
// "fresh" is intentionally not 0 to ensure we covered this code path
Arrays.fill(value, FRESH);
return value;
}
@Override
public void recycle(byte[] value) {
Arrays.fill(value, RECYCLED);
}
@Override
public void destroy(byte[] value) {
// we cannot really free the internals of a byte[], so mark it for verification
Arrays.fill(value, DEAD);
}
};
protected void assertFresh(byte[] data) {
assertNotNull(data);
for (int i = 0; i < data.length; ++i) {
assertEquals(FRESH, data[i]);
}
}
protected void assertRecycled(byte[] data) {
assertNotNull(data);
for (int i = 0; i < data.length; ++i) {
assertEquals(RECYCLED, data[i]);
}
}
protected void assertDead(byte[] data) {
assertNotNull(data);
for (int i = 0; i < data.length; ++i) {
assertEquals(DEAD, data[i]);
}
}
protected abstract Recycler<byte[]> newRecycler(int limit);
protected int limit = randomIntBetween(5, 10);
public void testReuse() {
Recycler<byte[]> r = newRecycler(limit);
Recycler.V<byte[]> o = r.obtain();
assertFalse(o.isRecycled());
final byte[] b1 = o.v();
assertFresh(b1);
o.close();
assertRecycled(b1);
o = r.obtain();
final byte[] b2 = o.v();
if (o.isRecycled()) {
assertRecycled(b2);
assertSame(b1, b2);
} else {
assertFresh(b2);
assertNotSame(b1, b2);
}
o.close();
r.close();
}
public void testRecycle() {
Recycler<byte[]> r = newRecycler(limit);
Recycler.V<byte[]> o = r.obtain();
assertFresh(o.v());
random().nextBytes(o.v());
o.close();
o = r.obtain();
assertRecycled(o.v());
o.close();
r.close();
}
public void testDoubleRelease() {
final Recycler<byte[]> r = newRecycler(limit);
final Recycler.V<byte[]> v1 = r.obtain();
v1.close();
try {
v1.close();
} catch (IllegalStateException e) {
// impl has protection against double release: ok
return;
}
// otherwise ensure that the impl may not be returned twice
final Recycler.V<byte[]> v2 = r.obtain();
final Recycler.V<byte[]> v3 = r.obtain();
assertNotSame(v2.v(), v3.v());
r.close();
}
public void testDestroyWhenOverCapacity() {
Recycler<byte[]> r = newRecycler(limit);
// get & keep reference to new/recycled data
Recycler.V<byte[]> o = r.obtain();
byte[] data = o.v();
assertFresh(data);
// now exhaust the recycler
List<V<byte[]>> vals = new ArrayList<>(limit);
for (int i = 0; i < limit ; ++i) {
vals.add(r.obtain());
}
// Recycler size increases on release, not on obtain!
for (V<byte[]> v: vals) {
v.close();
}
// release first ref, verify for destruction
o.close();
assertDead(data);
// close the rest
r.close();
}
public void testClose() {
Recycler<byte[]> r = newRecycler(limit);
// get & keep reference to pooled data
Recycler.V<byte[]> o = r.obtain();
byte[] data = o.v();
assertFresh(data);
// randomize & return to pool
random().nextBytes(data);
o.close();
// verify that recycle() ran
assertRecycled(data);
// closing the recycler should mark recycled instances via destroy()
r.close();
assertDead(data);
}
}