/*
* Copyright (c) 2010 Red Hat, Inc.
*
* Licensed 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.ovirt.engine.api.common.util;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.Reference;
import org.easymock.classextension.EasyMock;
import org.easymock.classextension.IMocksControl;
import org.easymock.IExpectationSetters;
import static org.easymock.classextension.EasyMock.expect;
public class ReapableMapTest extends Assert {
private static final String[] NUMBERS = { "one", "two", "three", "four", "five" };
private static final NumberMapper MAPPER = new NumberMapper();
private IMocksControl control;
private ReapedMap<String, Integer> map;
@Before
public void setUp() {
control = EasyMock.createNiceControl();
}
@Test
public void testReapingWithoutGC() throws Exception {
map = new ReapedMap<String, Integer>(1000);
populate(1, 2, 3);
assertSizes(3, 0);
assertExpected(1, 2, 3);
map.reapable("one");
map.reapable("three");
assertSizes(1, 2);
map.reapable("one");
assertSizes(1, 2);
assertExpected(1, 2, 3);
assertEquals(new Integer(3), map.remove("three"));
assertSizes(1, 1);
assertExpected(1, 2);
assertNull(map.get("three"));
Thread.sleep(1100);
assertExpected(1);
assertNull(map.get("one"));
assertExpected(2);
assertSizes(1, 0);
populate(4, 5);
map.clear();
}
@Test
public void testReapingOnGetWithGC() throws Exception {
setUpGCExpectations(5);
populate(1, 2, 3);
map.reapable("one");
map.reapable("three");
assertSizes(1, 2);
assertExpected(3);
assertSizes(1, 1);
assertNull(map.get("three"));
control.verify();
}
@Test
public void testReapingOnGetWithAccessBasedAging() throws Exception {
setUpAccessBaseAgingExpectations();
populate(1, 2, 3);
assertSizes(3, 0);
map.reapable("one");
map.reapable("three");
assertSizes(1, 2);
for (int i = 0 ; i < 6 ; i++) {
Thread.sleep(250);
assertExpected(i == 0 ? 1 : 3);
}
assertSizes(1, 1);
assertNull(map.get("one"));
assertExpected(3);
Thread.sleep(500);
assertExpected(3);
assertSizes(1, 1);
Thread.sleep(1200);
assertExpected(2);
assertSizes(1, 0);
assertNull(map.get("three"));
control.verify();
}
@Test
public void testReapingOnPutWithGC() throws Exception {
setUpGCExpectations(5);
populate(1, 2, 3);
map.reapable("one");
map.reapable("three");
assertSizes(1, 2);
populate(4);
assertSizes(2, 1);
assertNull(map.get("three"));
control.verify();
}
@Test
public void testReapingOnRemoveWithGC() throws Exception {
setUpGCExpectations(5);
populate(1, 2, 3);
map.reapable("one");
map.reapable("three");
assertSizes(1, 2);
map.remove("two");
assertSizes(0, 1);
assertNull(map.get("three"));
control.verify();
}
@SuppressWarnings("unchecked")
private void setUpGCExpectations(int gcAfter) {
ReferenceQueue<Integer> queue = (ReferenceQueue<Integer>)control.createMock(ReferenceQueue.class);
map = new ReapedMap<String, Integer>(10000, false, queue);
for (int i = 0 ; i < gcAfter ; i++) {
expect(queue.poll()).andReturn(null);
}
// the gcAfter^th queue poll simulates a GC event and triggers deletion
// on the reapable map
ReapedMap.IdAwareReference<String, Integer> ref = control.createMock(ReapedMap.IdAwareReference.class);
// awkward syntax required to work around compilation error
// on Reference<capture#nnn ? extends Integer> mismatch
IExpectationSetters<? extends Reference<? extends Integer>> qSetter = expect(queue.poll());
((IExpectationSetters<Reference<? extends Integer>>)qSetter).andReturn(ref);
IExpectationSetters<? extends String> refSetter = expect(ref.getKey());
((IExpectationSetters<String>)refSetter).andReturn("three");
control.replay();
}
@SuppressWarnings("unchecked")
private void setUpAccessBaseAgingExpectations() {
ReferenceQueue<Integer> queue = (ReferenceQueue<Integer>)control.createMock(ReferenceQueue.class);
map = new ReapedMap<String, Integer>(1000, true, queue);
expect(queue.poll()).andReturn(null).anyTimes();
control.replay();
}
private void populate(Integer ... values) {
for (Integer v: values) {
map.put(MAPPER.getKey(v), v);
}
}
private void assertSizes(int i, int j) {
assertEquals("unexpected primary map size", i, map.size());
assertEquals("unexpected secondary map size", j, map.reapableSize());
}
private void assertExpected(Integer ... values) {
for (Integer v: values) {
assertEquals(v, map.get(MAPPER.getKey(v)));
}
}
private static class NumberMapper {
public String getKey(Integer i) {
return i <= NUMBERS.length ? NUMBERS[i-1] : null;
}
};
}