/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.apache.flink.runtime.util;
import org.junit.Test;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.apache.flink.util.Preconditions.checkNotNull;
public class EvictingBoundedListTest {
@Test
public void testAddGet() {
int insertSize = 17;
int boundSize = 5;
Integer defaultElement = 4711;
EvictingBoundedList<Integer> list = new EvictingBoundedList<>(boundSize, defaultElement);
assertTrue(list.isEmpty());
for (int i = 0; i < insertSize; ++i) {
list.add(i);
}
assertEquals(17, list.size());
for (int i = 0; i < insertSize; ++i) {
int exp = i < (insertSize - boundSize) ? defaultElement : i;
int act = list.get(i);
assertEquals(exp, act);
}
}
@Test
public void testSet() {
int insertSize = 17;
int boundSize = 5;
Integer defaultElement = 4711;
List<Integer> reference = new ArrayList<>(insertSize);
EvictingBoundedList<Integer> list = new EvictingBoundedList<>(boundSize, defaultElement);
for (int i = 0; i < insertSize; ++i) {
reference.add(i);
list.add(i);
}
assertEquals(reference.size(), list.size());
list.set(0, 123);
list.set(insertSize - boundSize - 1, 123);
list.set(insertSize - boundSize, 42);
reference.set(insertSize - boundSize, 42);
list.set(13, 43);
reference.set(13, 43);
list.set(16, 44);
reference.set(16, 44);
try {
list.set(insertSize, 23);
fail("Illegal index in set not detected.");
} catch (IllegalArgumentException ignored) {
}
for (int i = 0; i < insertSize; ++i) {
int exp = i < (insertSize - boundSize) ? defaultElement : reference.get(i);
int act = list.get(i);
assertEquals(exp, act);
}
assertEquals(reference.size(), list.size());
}
@Test
public void testClear() {
int insertSize = 17;
int boundSize = 5;
Integer defaultElement = 4711;
EvictingBoundedList<Integer> list = new EvictingBoundedList<>(boundSize, defaultElement);
for (int i = 0; i < insertSize; ++i) {
list.add(i);
}
list.clear();
assertEquals(0, list.size());
assertTrue(list.isEmpty());
try {
list.get(0);
fail();
} catch (IndexOutOfBoundsException ignore) {
}
}
@Test
public void testIterator() {
int insertSize = 17;
int boundSize = 5;
Integer defaultElement = 4711;
EvictingBoundedList<Integer> list = new EvictingBoundedList<>(boundSize, defaultElement);
assertTrue(list.isEmpty());
for (int i = 0; i < insertSize; ++i) {
list.add(i);
}
Iterator<Integer> iterator = list.iterator();
for (int i = 0; i < insertSize; ++i) {
assertTrue(iterator.hasNext());
int exp = i < (insertSize - boundSize) ? defaultElement : i;
int act = iterator.next();
assertEquals(exp, act);
}
assertFalse(iterator.hasNext());
try {
iterator.next();
fail("Next on exhausted iterator did not trigger exception.");
} catch (NoSuchElementException ignored) {
}
iterator = list.iterator();
assertTrue(iterator.hasNext());
iterator.next();
list.add(123);
assertTrue(iterator.hasNext());
try {
iterator.next();
fail("Concurrent modification not detected.");
} catch (ConcurrentModificationException ignored) {
}
}
@Test
public void testMapWithHalfFullList() {
final Object[] originals = { new Object(), new Object(), new Object() };
final Object defaultValue = new Object();
final EvictingBoundedList<Object> original = new EvictingBoundedList<>(5, defaultValue);
for (Object o : originals) {
original.add(o);
}
final EvictingBoundedList<TransformedObject> transformed = original.map(new Mapper());
assertEquals(original.size(), transformed.size());
assertEquals(original.getSizeLimit(), transformed.getSizeLimit());
assertEquals(defaultValue, transformed.getDefaultElement().original);
int i = 0;
for (TransformedObject to : transformed) {
assertEquals(originals[i++], to.original);
}
try {
transformed.get(originals.length);
fail("should have failed with an exception");
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testMapWithEvictedElements() {
final Object[] originals = { new Object(), new Object(), new Object(), new Object(), new Object() };
final Object defaultValue = new Object();
final EvictingBoundedList<Object> original = new EvictingBoundedList<>(2, defaultValue);
for (Object o : originals) {
original.add(o);
}
final EvictingBoundedList<TransformedObject> transformed = original.map(new Mapper());
assertEquals(originals.length, transformed.size());
assertEquals(original.size(), transformed.size());
assertEquals(original.getSizeLimit(), transformed.getSizeLimit());
assertEquals(defaultValue, transformed.getDefaultElement().original);
for (int i = 0; i < originals.length; i++) {
if (i < originals.length - transformed.getSizeLimit()) {
assertEquals(transformed.getDefaultElement(), transformed.get(i));
} else {
assertEquals(originals[i], transformed.get(i).original);
}
}
try {
transformed.get(originals.length);
fail("should have failed with an exception");
} catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testMapWithNullDefault() {
final EvictingBoundedList<Object> original = new EvictingBoundedList<>(5, null);
final EvictingBoundedList<TransformedObject> transformed = original.map(new Mapper());
assertEquals(original.size(), transformed.size());
assertNull(transformed.getDefaultElement());
}
// ------------------------------------------------------------------------
private static final class TransformedObject {
final Object original;
TransformedObject(Object original) {
this.original = checkNotNull(original);
}
}
// ------------------------------------------------------------------------
private static final class Mapper implements EvictingBoundedList.Function<Object, TransformedObject> {
@Override
public TransformedObject apply(Object value) {
return new TransformedObject(value);
}
}
}