/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* 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 com.alibaba.citrus.util.collection;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static org.junit.Assert.*;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import org.junit.Before;
import org.junit.Test;
/**
* 测试<code>ListMap.entryList()</code>, <code>ListMap.keyList()</code>,
* <code>ListMap.valueList()</code> 返回的list view对象.
*
* @author Michael Zhou
*/
public abstract class AbstractListMapViewTests extends AbstractTests {
private ListMap<Object, Object> map1;
private ListMap<Object, Object> map2;
private ListMap<Object, Object> map3;
private List<?> view1;
private List<?> view2;
private List<?> view3;
@Before
public void init() {
// map1测试一般情况.
map1 = createListMap();
map1.put("aaa", "111");
map1.put("bbb", "222");
map1.put("ccc", "333");
view1 = getView(map1);
// map2测试key和value为null的情况.
map2 = createListMap();
map2.put(null, "111");
map2.put("aaa", null);
view2 = getView(map2);
// map3为空.
map3 = createListMap();
view3 = getView(map3);
}
private Object newItem(Object key, Object value) {
return createItem(key, value);
}
@Test
public void size() {
assertEquals(3, view1.size());
assertEquals(2, view2.size());
assertEquals(0, view3.size());
}
@Test
public void isEmpty() {
assertFalse(view1.isEmpty());
assertFalse(view2.isEmpty());
assertTrue(view3.isEmpty());
}
@Test
public void clear() {
view1.clear();
assertEquals(0, map1.size());
assertTrue(map1.isEmpty());
assertTrue(view1.isEmpty());
view2.clear();
assertEquals(0, map2.size());
assertTrue(map2.isEmpty());
assertTrue(view2.isEmpty());
view3.clear();
assertEquals(0, map3.size());
assertTrue(map3.isEmpty());
assertTrue(view3.isEmpty());
}
@Test
public void removeByIndex() {
view1.remove(0);
assertFalse(map1.containsKey("aaa"));
assertFalse(view1.contains(newItem("aaa", "111")));
assertEquals(2, map1.size());
assertEquals(2, view1.size());
view2.remove(1);
assertFalse(map2.containsKey("aaa"));
assertFalse(view2.contains(newItem("aaa", null)));
view2.remove(0);
assertFalse(map2.containsKey(null));
assertFalse(view2.contains(newItem(null, "111")));
assertEquals(0, map2.size());
assertEquals(0, view2.size());
try {
view3.remove(0);
fail("Should throw IndexOutOfBoundsException");
} catch (IndexOutOfBoundsException e) {
assertFalse(map3.containsKey("not exists"));
assertFalse(view3.contains(newItem("not exists", null)));
assertEquals(0, map3.size());
assertEquals(0, view3.size());
}
}
@Test
public void remove() {
view1.remove(newItem("aaa", "111"));
assertFalse(map1.containsKey("aaa"));
assertFalse(view1.contains(newItem("aaa", "111")));
assertEquals(2, map1.size());
assertEquals(2, view1.size());
view2.remove(newItem("aaa", null));
assertFalse(map2.containsKey("aaa"));
assertFalse(view2.contains(newItem("aaa", null)));
view2.remove(newItem(null, "111"));
assertFalse(map2.containsKey(null));
assertFalse(view2.contains(newItem(null, "111")));
assertEquals(0, map2.size());
assertEquals(0, view2.size());
view3.remove(newItem("not exists", null));
assertFalse(map3.containsKey("not exists"));
assertFalse(view3.contains(newItem("not exists", null)));
assertEquals(0, map3.size());
assertEquals(0, view3.size());
}
@Test
public void indexOf() {
assertEquals(0, view1.indexOf(newItem("aaa", "111")));
assertEquals(1, view1.indexOf(newItem("bbb", "222")));
assertEquals(2, view1.indexOf(newItem("ccc", "333")));
assertEquals(-1, view1.indexOf(newItem(null, null)));
assertEquals(0, view2.indexOf(newItem(null, "111")));
assertEquals(1, view2.indexOf(newItem("aaa", null)));
assertEquals(-1, view2.indexOf(newItem("bbb", "222")));
assertEquals(-1, view3.indexOf(newItem("aaa", "111")));
}
@Test
public void lastIndexOf() {
assertEquals(0, view1.lastIndexOf(newItem("aaa", "111")));
assertEquals(1, view1.lastIndexOf(newItem("bbb", "222")));
assertEquals(2, view1.lastIndexOf(newItem("ccc", "333")));
assertEquals(-1, view1.lastIndexOf(newItem(null, null)));
assertEquals(0, view2.lastIndexOf(newItem(null, "111")));
assertEquals(1, view2.lastIndexOf(newItem("aaa", null)));
assertEquals(-1, view2.lastIndexOf(newItem("bbb", "222")));
assertEquals(-1, view3.lastIndexOf(newItem("aaa", "111")));
}
@Test
public void contains() {
assertTrue(view1.contains(newItem("aaa", "111")));
assertTrue(view1.contains(newItem("bbb", "222")));
assertTrue(view1.contains(newItem("ccc", "333")));
assertTrue(view2.contains(newItem("aaa", null)));
assertTrue(view2.contains(newItem(null, "111")));
}
@Test
public void containsAll() {
List<?> items;
items = createArrayList(newItem("aaa", "111"), newItem("bbb", "222"), newItem("ccc", "333"));
assertTrue(view1.containsAll(items));
items = createArrayList(newItem("aaa", null), newItem(null, "111"));
assertTrue(view2.containsAll(items));
items = createArrayList();
assertTrue(view1.containsAll(items));
assertTrue(view2.containsAll(items));
assertTrue(view3.containsAll(items));
}
@Test
public void get() {
assertEquals(newItem("aaa", "111"), view1.get(0));
assertEquals(newItem("bbb", "222"), view1.get(1));
assertEquals(newItem("ccc", "333"), view1.get(2));
assertEquals(newItem(null, "111"), view2.get(0));
assertEquals(newItem("aaa", null), view2.get(1));
try {
view3.get(0);
fail("Should throw IndexOutOfBoundsException");
} catch (IndexOutOfBoundsException e) {
}
}
@Test
public void iterator() {
Iterator<?> i = view1.iterator();
assertTrue(i.hasNext());
assertEquals(newItem("aaa", "111"), i.next());
assertTrue(i.hasNext());
assertEquals(newItem("bbb", "222"), i.next());
assertTrue(i.hasNext());
assertEquals(newItem("ccc", "333"), i.next());
assertFalse(i.hasNext());
try {
i.next();
fail("Should throw NoSuchElementException");
} catch (NoSuchElementException e) {
}
i = view2.iterator();
i.next();
i.remove();
assertEquals(1, map2.size());
i.next();
i.remove();
assertEquals(0, map2.size());
try {
i.next();
fail("Should throw NoSuchElementException");
} catch (NoSuchElementException e) {
}
}
@Test
@SuppressWarnings("unchecked")
public void listIterator() {
ListIterator<Object> i = (ListIterator<Object>) view1.listIterator();
assertTrue(i.hasNext());
assertEquals(newItem("aaa", "111"), i.next());
assertTrue(i.hasNext());
assertEquals(newItem("bbb", "222"), i.next());
assertTrue(i.hasNext());
assertEquals(newItem("ccc", "333"), i.next());
assertFalse(i.hasNext());
try {
i.next();
fail("Should throw NoSuchElementException");
} catch (NoSuchElementException e) {
}
assertTrue(i.hasPrevious());
assertEquals(newItem("ccc", "333"), i.previous());
assertTrue(i.hasPrevious());
assertEquals(newItem("bbb", "222"), i.previous());
assertTrue(i.hasPrevious());
assertEquals(newItem("aaa", "111"), i.previous());
assertFalse(i.hasPrevious());
try {
i.previous();
fail("Should throw NoSuchElementException");
} catch (NoSuchElementException e) {
}
i.remove();
assertEquals(2, map1.size());
i.next();
i.remove();
assertEquals(1, map1.size());
i.next();
i.remove();
assertEquals(0, map1.size());
try {
i.next();
fail("Should throw NoSuchElementException");
} catch (NoSuchElementException e) {
}
i = (ListIterator<Object>) view2.listIterator();
i.next();
// 此操作只有valueList支持.
try {
i.set(newItem("hello", "world"));
assertEquals(newItem("hello", "world"), map2.get(0));
} catch (UnsupportedOperationException e) {
}
}
@Test
@SuppressWarnings("unchecked")
public void failFast() {
Iterator<Object> i;
ListIterator<Object> j;
// 修改map以后, 试图i.next()导致异常.
i = (Iterator<Object>) view1.iterator();
map1.put("aaa+aaa", "111+111");
try {
i.next();
fail("should throw a ConcurrentModificationException");
} catch (ConcurrentModificationException e) {
}
// 修改map以后, 试图i.remove()导致异常.
j = (ListIterator<Object>) view2.listIterator();
j.next();
map2.put("aaa+aaa", "111+111");
try {
j.remove();
fail("should throw a ConcurrentModificationException");
} catch (ConcurrentModificationException e) {
}
}
protected abstract ListMap<Object, Object> createListMap();
/**
* 从<code>ListMap</code>中取得view. 可能是<code>ListMap.entryList()</code>,
* <code>ListMap.keyList()</code> 和<code>ListMap.valueList()</code>
* 等方法返回的结果.
*
* @param map 被测试的view所属的<code>ListMap</code>
* @return view
*/
protected abstract List<?> getView(ListMap<Object, Object> map);
/**
* 创建一个和View中存放的对象可比较的对象.
*
* @param key key
* @param value value
* @return 对象
*/
protected abstract Object createItem(Object key, Object value);
}