/* * 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.Arrays; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.Map; import org.junit.Before; import org.junit.Test; /** * 测试<code>Map.entrySet()</code>, <code>Map.keySet()</code>, * <code>Map.values()</code> 返回的collection view对象. * * @author Michael Zhou */ public abstract class AbstractMapViewTests extends AbstractTests { private Map<Object, Object> map1; private Map<Object, Object> map2; private Map<Object, Object> map3; private Collection<?> view1; private Collection<?> view2; private Collection<?> view3; @Before public void init() { // map1测试一般情况. map1 = createMap(); map1.put("aaa", "111"); map1.put("bbb", "222"); map1.put("ccc", "333"); view1 = getView(map1); // map2测试key和value为null的情况. map2 = createMap(); map2.put(null, "111"); map2.put("aaa", null); view2 = getView(map2); // map3为空. map3 = createMap(); view3 = getView(map3); } private Collection<?> newCollection() { return createHashSet(); } private Object newItem(Object key, Object value) { return createItem(key, value); } @Test public void add() { assertAdd(view1); assertAdd(view2); assertAdd(view3); } @SuppressWarnings("unchecked") private void assertAdd(Collection<?> collection) { try { ((Collection<Object>) collection).add(newItem("key", "value")); fail("should throw an UnsupportedOperationException"); } catch (UnsupportedOperationException e) { } } @Test public void addAll() { assertAddAll(view1); assertAddAll(view2); assertAddAll(view3); } @SuppressWarnings("unchecked") private void assertAddAll(Collection<?> collection) { Collection<Object> items = (Collection<Object>) newCollection(); ((Collection<Object>) collection).addAll(items); // 加入空collection try { items.add(newItem("key", "value")); ((Collection<Object>) collection).addAll(items); // 加入非空collection fail("should throw an UnsupportedOperationException"); } catch (UnsupportedOperationException e) { } } @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 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 @SuppressWarnings("unchecked") public void containsAll() { Collection<Object> items; items = (Collection<Object>) newCollection(); items.add(newItem("aaa", "111")); items.add(newItem("bbb", "222")); items.add(newItem("ccc", "333")); assertTrue(view1.containsAll(items)); items = (Collection<Object>) newCollection(); items.add(newItem("aaa", null)); items.add(newItem(null, "111")); assertTrue(view2.containsAll(items)); items = (Collection<Object>) newCollection(); assertTrue(view1.containsAll(items)); assertTrue(view2.containsAll(items)); assertTrue(view3.containsAll(items)); } /** * 测试Object.equals()方法. 但并非所有的collection对象都定义了此方法. * 如果当前测试的collection对象不支持比较操作, 则 createCollectionToCompareWith()应该返回null. */ @Test @SuppressWarnings("unchecked") public void equals_() { Collection<Object> items; // 这个方法调用返回一个collection对象, 可以直接和当前的collection相比较. // 如果返回null, 表示当前collection不支持比较操作, 所以跳过此项测试. if (createCollectionToCompareWith() == null) { return; } items = (Collection<Object>) createCollectionToCompareWith(); items.add(newItem("aaa", "111")); items.add(newItem("bbb", "222")); items.add(newItem("ccc", "333")); assertTrue(view1.equals(items)); items = (Collection<Object>) createCollectionToCompareWith(); items.add(newItem("aaa", null)); items.add(newItem(null, "111")); assertTrue(view2.equals(items)); items = (Collection<Object>) createCollectionToCompareWith(); assertFalse(view1.equals(items)); assertFalse(view2.equals(items)); assertTrue(view3.equals(items)); } /** * 测试Object.hashCode()方法. 该方法总是和Object.equals()相关的(相等的对象有 相同的hashCode), * 因此,类似equals_(), hashCode_通过判断 * createCollectionToCompareWith()是否返回null来决定是否 做这项测试. */ @Test @SuppressWarnings("unchecked") public void hashCode_() { Collection<Object> items; // 这个方法调用返回一个collection对象, 可以直接和当前的collection相比较. // 如果返回null, 表示当前collection不支持比较操作, 所以不测试此项. if (createCollectionToCompareWith() == null) { return; } items = (Collection<Object>) createCollectionToCompareWith(); items.add(newItem("aaa", "111")); items.add(newItem("bbb", "222")); items.add(newItem("ccc", "333")); assertEquals(items.hashCode(), view1.hashCode()); items = (Collection<Object>) createCollectionToCompareWith(); items.add(newItem("aaa", null)); items.add(newItem(null, "111")); assertEquals(items.hashCode(), view2.hashCode()); items = (Collection<Object>) createCollectionToCompareWith(); assertFalse(items.hashCode() == view1.hashCode()); assertFalse(items.hashCode() == view2.hashCode()); assertEquals(items.hashCode(), view3.hashCode()); } @Test public void isEmpty() { assertFalse(view1.isEmpty()); assertFalse(view2.isEmpty()); assertTrue(view3.isEmpty()); } @Test public void iterator() { int count; count = 0; for (Iterator<?> i = view1.iterator(); i.hasNext(); count++) { Object entry = i.next(); assertTrue(isEqual(newItem("aaa", "111"), entry) || isEqual(newItem("bbb", "222"), entry) || isEqual(newItem("ccc", "333"), entry)); } assertEquals(3, count); count = 0; for (Iterator<?> i = view2.iterator(); i.hasNext(); count++) { Object entry = i.next(); assertTrue(isEqual(newItem("aaa", null), entry) || isEqual(newItem(null, "111"), entry)); } assertEquals(2, count); for (Object name : view3) { fail("should not go here: " + name); } } @Test public void iteratorRemove() { int count = view1.size() - 1; for (Iterator<?> i = view1.iterator(); i.hasNext(); count--) { i.next(); i.remove(); assertEquals(count, view1.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 @SuppressWarnings("unchecked") public void removeAll() { Collection<Object> items; items = (Collection<Object>) newCollection(); items.add(newItem("aaa", "111")); items.add(newItem("bbb", "222")); assertTrue(view1.removeAll(items)); assertTrue(view1.contains(newItem("ccc", "333"))); assertEquals(1, view1.size()); items = (Collection<Object>) newCollection(); items.add(newItem("aaa", null)); items.add(newItem(null, "111")); assertTrue(view2.removeAll(items)); assertEquals(0, view2.size()); items = (Collection<Object>) newCollection(); assertFalse(view3.removeAll(items)); assertEquals(0, view3.size()); } @Test @SuppressWarnings("unchecked") public void retainAll() { Collection<Object> items; items = (Collection<Object>) newCollection(); items.add(newItem("aaa", "111")); items.add(newItem("bbb", "222")); assertTrue(view1.retainAll(items)); assertTrue(view1.contains(newItem("aaa", "111"))); assertTrue(view1.contains(newItem("bbb", "222"))); assertFalse(view1.contains(newItem("ccc", "333"))); assertEquals(2, view1.size()); items = (Collection<Object>) newCollection(); items.add(newItem("aaa", null)); assertTrue(view2.retainAll(items)); assertTrue(view2.contains(newItem("aaa", null))); assertFalse(view2.contains(newItem(null, "111"))); assertEquals(1, view2.size()); items = (Collection<Object>) newCollection(); assertFalse(view3.retainAll(items)); assertEquals(0, view3.size()); } @Test public void size() { assertEquals(3, view1.size()); assertEquals(2, view2.size()); assertEquals(0, view3.size()); } @Test public void toArray() { Object[] array; // 不带参数的toArray(). array = view1.toArray(); view1.removeAll(Arrays.asList(array)); assertEquals(0, view1.size()); // 带参数的toArray(Object[]). array = new Object[2]; view2.toArray(array); view2.removeAll(Arrays.asList(array)); assertEquals(0, view2.size()); } @Test public void failFast() { Iterator<?> i; // 修改map以后, 试图i.next()导致异常. i = view1.iterator(); map1.put("aaa+aaa", "111+111"); try { i.next(); fail("should throw a ConcurrentModificationException"); } catch (ConcurrentModificationException e) { } // 修改map以后, 试图i.remove()导致异常. i = view2.iterator(); i.next(); map2.put("aaa+aaa", "111+111"); try { i.remove(); fail("should throw a ConcurrentModificationException"); } catch (ConcurrentModificationException e) { } } protected abstract Map<Object, Object> createMap(); /** * 从<code>Map</code>中取得view. 可能是<code>Map.entrySet()</code>, * <code>Map.keySet()</code> 和<code>Map.values()</code>等方法返回的结果. * * @param map 被测试的view所属的<code>Map</code> * @return view */ protected abstract Collection<?> getView(Map<Object, Object> map); /** * 创建一个可以和当前测试的view相比较的对象. 对于<code>Map.values()</code>返回的 * <code>Collection</code>对象, 没有定义<code>equals</code> 和 * <code>hashCode</code>方法, 所以不可比较. 这种情况返回<code>null</code>即可. * * @return <code>Collection</code>对象 */ protected abstract Collection<?> createCollectionToCompareWith(); /** * 创建一个和View中存放的对象可比较的对象. * * @param key key * @param value value * @return 对象 */ protected abstract Object createItem(Object key, Object value); }