1 /* 2 * Copyright (C) 2007 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.collect; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import com.google.common.collect.testing.features.CollectionFeature; 22 import com.google.common.collect.testing.features.CollectionSize; 23 import com.google.common.collect.testing.features.MapFeature; 24 import com.google.common.collect.testing.google.SetMultimapTestSuiteBuilder; 25 import com.google.common.collect.testing.google.TestStringSetMultimapGenerator; 26 27 import junit.framework.Test; 28 import junit.framework.TestCase; 29 import junit.framework.TestSuite; 30 31 import java.io.Serializable; 32 import java.util.Arrays; 33 import java.util.Collection; 34 import java.util.Map; 35 import java.util.Map.Entry; 36 import java.util.RandomAccess; 37 import java.util.Set; 38 39 import javax.annotation.Nullable; 40 41 /** 42 * Tests for {@code Synchronized#multimap}. 43 * 44 * @author Mike Bostock 45 */ 46 public class SynchronizedMultimapTest extends TestCase { 47 suite()48 public static Test suite() { 49 TestSuite suite = new TestSuite(); 50 suite.addTestSuite(SynchronizedMultimapTest.class); 51 suite.addTest(SetMultimapTestSuiteBuilder.using(new TestStringSetMultimapGenerator() { 52 @Override 53 protected SetMultimap<String, String> create(Entry<String, String>[] entries) { 54 TestMultimap<String, String> inner = new TestMultimap<String, String>(); 55 SetMultimap<String, String> outer = Synchronized.setMultimap(inner, inner.mutex); 56 for (Entry<String, String> entry : entries) { 57 outer.put(entry.getKey(), entry.getValue()); 58 } 59 return outer; 60 } 61 }) 62 .named("Synchronized.setMultimap") 63 .withFeatures(MapFeature.GENERAL_PURPOSE, 64 CollectionSize.ANY, 65 CollectionFeature.SERIALIZABLE, 66 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 67 MapFeature.ALLOWS_NULL_KEYS, 68 MapFeature.ALLOWS_NULL_VALUES, 69 MapFeature.ALLOWS_ANY_NULL_QUERIES) 70 .createTestSuite()); 71 return suite; 72 } 73 74 private static final class TestMultimap<K, V> extends ForwardingSetMultimap<K, V> 75 implements Serializable { 76 final SetMultimap<K, V> delegate = HashMultimap.create(); 77 public final Object mutex = new Integer(1); // something Serializable 78 delegate()79 @Override protected SetMultimap<K, V> delegate() { 80 return delegate; 81 } 82 toString()83 @Override public String toString() { 84 assertTrue(Thread.holdsLock(mutex)); 85 return super.toString(); 86 } 87 equals(@ullable Object o)88 @Override public boolean equals(@Nullable Object o) { 89 assertTrue(Thread.holdsLock(mutex)); 90 return super.equals(o); 91 } 92 hashCode()93 @Override public int hashCode() { 94 assertTrue(Thread.holdsLock(mutex)); 95 return super.hashCode(); 96 } 97 size()98 @Override public int size() { 99 assertTrue(Thread.holdsLock(mutex)); 100 return super.size(); 101 } 102 isEmpty()103 @Override public boolean isEmpty() { 104 assertTrue(Thread.holdsLock(mutex)); 105 return super.isEmpty(); 106 } 107 containsKey(@ullable Object key)108 @Override public boolean containsKey(@Nullable Object key) { 109 assertTrue(Thread.holdsLock(mutex)); 110 return super.containsKey(key); 111 } 112 containsValue(@ullable Object value)113 @Override public boolean containsValue(@Nullable Object value) { 114 assertTrue(Thread.holdsLock(mutex)); 115 return super.containsValue(value); 116 } 117 containsEntry(@ullable Object key, @Nullable Object value)118 @Override public boolean containsEntry(@Nullable Object key, 119 @Nullable Object value) { 120 assertTrue(Thread.holdsLock(mutex)); 121 return super.containsEntry(key, value); 122 } 123 get(@ullable K key)124 @Override public Set<V> get(@Nullable K key) { 125 assertTrue(Thread.holdsLock(mutex)); 126 /* TODO: verify that the Collection is also synchronized? */ 127 return super.get(key); 128 } 129 put(K key, V value)130 @Override public boolean put(K key, V value) { 131 assertTrue(Thread.holdsLock(mutex)); 132 return super.put(key, value); 133 } 134 putAll(@ullable K key, Iterable<? extends V> values)135 @Override public boolean putAll(@Nullable K key, 136 Iterable<? extends V> values) { 137 assertTrue(Thread.holdsLock(mutex)); 138 return super.putAll(key, values); 139 } 140 putAll(Multimap<? extends K, ? extends V> map)141 @Override public boolean putAll(Multimap<? extends K, ? extends V> map) { 142 assertTrue(Thread.holdsLock(mutex)); 143 return super.putAll(map); 144 } 145 replaceValues(@ullable K key, Iterable<? extends V> values)146 @Override public Set<V> replaceValues(@Nullable K key, 147 Iterable<? extends V> values) { 148 assertTrue(Thread.holdsLock(mutex)); 149 return super.replaceValues(key, values); 150 } 151 remove(@ullable Object key, @Nullable Object value)152 @Override public boolean remove(@Nullable Object key, 153 @Nullable Object value) { 154 assertTrue(Thread.holdsLock(mutex)); 155 return super.remove(key, value); 156 } 157 removeAll(@ullable Object key)158 @Override public Set<V> removeAll(@Nullable Object key) { 159 assertTrue(Thread.holdsLock(mutex)); 160 return super.removeAll(key); 161 } 162 clear()163 @Override public void clear() { 164 assertTrue(Thread.holdsLock(mutex)); 165 super.clear(); 166 } 167 keySet()168 @Override public Set<K> keySet() { 169 assertTrue(Thread.holdsLock(mutex)); 170 /* TODO: verify that the Set is also synchronized? */ 171 return super.keySet(); 172 } 173 keys()174 @Override public Multiset<K> keys() { 175 assertTrue(Thread.holdsLock(mutex)); 176 /* TODO: verify that the Set is also synchronized? */ 177 return super.keys(); 178 } 179 values()180 @Override public Collection<V> values() { 181 assertTrue(Thread.holdsLock(mutex)); 182 /* TODO: verify that the Collection is also synchronized? */ 183 return super.values(); 184 } 185 entries()186 @Override public Set<Map.Entry<K, V>> entries() { 187 assertTrue(Thread.holdsLock(mutex)); 188 /* TODO: verify that the Collection is also synchronized? */ 189 return super.entries(); 190 } 191 asMap()192 @Override public Map<K, Collection<V>> asMap() { 193 assertTrue(Thread.holdsLock(mutex)); 194 /* TODO: verify that the Map is also synchronized? */ 195 return super.asMap(); 196 } 197 198 private static final long serialVersionUID = 0; 199 } 200 testSynchronizedListMultimap()201 public void testSynchronizedListMultimap() { 202 ListMultimap<String, Integer> multimap 203 = Multimaps.synchronizedListMultimap( 204 ArrayListMultimap.<String, Integer>create()); 205 multimap.putAll("foo", Arrays.asList(3, -1, 2, 4, 1)); 206 multimap.putAll("bar", Arrays.asList(1, 2, 3, 1)); 207 assertThat(multimap.removeAll("foo")).has().exactly(3, -1, 2, 4, 1).inOrder(); 208 assertFalse(multimap.containsKey("foo")); 209 assertThat(multimap.replaceValues("bar", Arrays.asList(6, 5))) 210 .has().exactly(1, 2, 3, 1).inOrder(); 211 assertThat(multimap.get("bar")).has().exactly(6, 5).inOrder(); 212 } 213 testSynchronizedSortedSetMultimap()214 public void testSynchronizedSortedSetMultimap() { 215 SortedSetMultimap<String, Integer> multimap 216 = Multimaps.synchronizedSortedSetMultimap( 217 TreeMultimap.<String, Integer>create()); 218 multimap.putAll("foo", Arrays.asList(3, -1, 2, 4, 1)); 219 multimap.putAll("bar", Arrays.asList(1, 2, 3, 1)); 220 assertThat(multimap.removeAll("foo")).has().exactly(-1, 1, 2, 3, 4).inOrder(); 221 assertFalse(multimap.containsKey("foo")); 222 assertThat(multimap.replaceValues("bar", Arrays.asList(6, 5))) 223 .has().exactly(1, 2, 3).inOrder(); 224 assertThat(multimap.get("bar")).has().exactly(5, 6).inOrder(); 225 } 226 testSynchronizedArrayListMultimapRandomAccess()227 public void testSynchronizedArrayListMultimapRandomAccess() { 228 ListMultimap<String, Integer> delegate = ArrayListMultimap.create(); 229 delegate.put("foo", 1); 230 delegate.put("foo", 3); 231 ListMultimap<String, Integer> multimap 232 = Multimaps.synchronizedListMultimap(delegate); 233 assertTrue(multimap.get("foo") instanceof RandomAccess); 234 assertTrue(multimap.get("bar") instanceof RandomAccess); 235 } 236 testSynchronizedLinkedListMultimapRandomAccess()237 public void testSynchronizedLinkedListMultimapRandomAccess() { 238 ListMultimap<String, Integer> delegate = LinkedListMultimap.create(); 239 delegate.put("foo", 1); 240 delegate.put("foo", 3); 241 ListMultimap<String, Integer> multimap 242 = Multimaps.synchronizedListMultimap(delegate); 243 assertFalse(multimap.get("foo") instanceof RandomAccess); 244 assertFalse(multimap.get("bar") instanceof RandomAccess); 245 } 246 } 247