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 import java.io.Serializable; 27 import java.util.Arrays; 28 import java.util.Collection; 29 import java.util.Map; 30 import java.util.Map.Entry; 31 import java.util.RandomAccess; 32 import java.util.Set; 33 import junit.framework.Test; 34 import junit.framework.TestCase; 35 import junit.framework.TestSuite; 36 import org.checkerframework.checker.nullness.qual.Nullable; 37 38 /** 39 * Tests for {@code Synchronized#multimap}. 40 * 41 * @author Mike Bostock 42 */ 43 public class SynchronizedMultimapTest extends TestCase { 44 suite()45 public static Test suite() { 46 TestSuite suite = new TestSuite(); 47 suite.addTestSuite(SynchronizedMultimapTest.class); 48 suite.addTest( 49 SetMultimapTestSuiteBuilder.using( 50 new TestStringSetMultimapGenerator() { 51 @Override 52 protected SetMultimap<String, String> create(Entry<String, String>[] entries) { 53 TestMultimap<String, String> inner = new TestMultimap<>(); 54 SetMultimap<String, String> outer = 55 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( 64 MapFeature.GENERAL_PURPOSE, 65 CollectionSize.ANY, 66 CollectionFeature.SERIALIZABLE, 67 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 68 MapFeature.ALLOWS_NULL_KEYS, 69 MapFeature.ALLOWS_NULL_VALUES, 70 MapFeature.ALLOWS_ANY_NULL_QUERIES) 71 .createTestSuite()); 72 return suite; 73 } 74 75 private static final class TestMultimap<K, V> extends ForwardingSetMultimap<K, V> 76 implements Serializable { 77 final SetMultimap<K, V> delegate = HashMultimap.create(); 78 public final Object mutex = new Object[0]; // something Serializable 79 80 @Override delegate()81 protected SetMultimap<K, V> delegate() { 82 return delegate; 83 } 84 85 @Override toString()86 public String toString() { 87 assertTrue(Thread.holdsLock(mutex)); 88 return super.toString(); 89 } 90 91 @Override equals(@ullable Object o)92 public boolean equals(@Nullable Object o) { 93 assertTrue(Thread.holdsLock(mutex)); 94 return super.equals(o); 95 } 96 97 @Override hashCode()98 public int hashCode() { 99 assertTrue(Thread.holdsLock(mutex)); 100 return super.hashCode(); 101 } 102 103 @Override size()104 public int size() { 105 assertTrue(Thread.holdsLock(mutex)); 106 return super.size(); 107 } 108 109 @Override isEmpty()110 public boolean isEmpty() { 111 assertTrue(Thread.holdsLock(mutex)); 112 return super.isEmpty(); 113 } 114 115 @Override containsKey(@ullable Object key)116 public boolean containsKey(@Nullable Object key) { 117 assertTrue(Thread.holdsLock(mutex)); 118 return super.containsKey(key); 119 } 120 121 @Override containsValue(@ullable Object value)122 public boolean containsValue(@Nullable Object value) { 123 assertTrue(Thread.holdsLock(mutex)); 124 return super.containsValue(value); 125 } 126 127 @Override containsEntry(@ullable Object key, @Nullable Object value)128 public boolean containsEntry(@Nullable Object key, @Nullable Object value) { 129 assertTrue(Thread.holdsLock(mutex)); 130 return super.containsEntry(key, value); 131 } 132 133 @Override get(@ullable K key)134 public Set<V> get(@Nullable K key) { 135 assertTrue(Thread.holdsLock(mutex)); 136 /* TODO: verify that the Set is also synchronized? */ 137 return super.get(key); 138 } 139 140 @Override put(K key, V value)141 public boolean put(K key, V value) { 142 assertTrue(Thread.holdsLock(mutex)); 143 return super.put(key, value); 144 } 145 146 @Override putAll(@ullable K key, Iterable<? extends V> values)147 public boolean putAll(@Nullable K key, Iterable<? extends V> values) { 148 assertTrue(Thread.holdsLock(mutex)); 149 return super.putAll(key, values); 150 } 151 152 @Override putAll(Multimap<? extends K, ? extends V> map)153 public boolean putAll(Multimap<? extends K, ? extends V> map) { 154 assertTrue(Thread.holdsLock(mutex)); 155 return super.putAll(map); 156 } 157 158 @Override replaceValues(@ullable K key, Iterable<? extends V> values)159 public Set<V> replaceValues(@Nullable K key, Iterable<? extends V> values) { 160 assertTrue(Thread.holdsLock(mutex)); 161 return super.replaceValues(key, values); 162 } 163 164 @Override remove(@ullable Object key, @Nullable Object value)165 public boolean remove(@Nullable Object key, @Nullable Object value) { 166 assertTrue(Thread.holdsLock(mutex)); 167 return super.remove(key, value); 168 } 169 170 @Override removeAll(@ullable Object key)171 public Set<V> removeAll(@Nullable Object key) { 172 assertTrue(Thread.holdsLock(mutex)); 173 return super.removeAll(key); 174 } 175 176 @Override clear()177 public void clear() { 178 assertTrue(Thread.holdsLock(mutex)); 179 super.clear(); 180 } 181 182 @Override keySet()183 public Set<K> keySet() { 184 assertTrue(Thread.holdsLock(mutex)); 185 /* TODO: verify that the Set is also synchronized? */ 186 return super.keySet(); 187 } 188 189 @Override keys()190 public Multiset<K> keys() { 191 assertTrue(Thread.holdsLock(mutex)); 192 /* TODO: verify that the Multiset is also synchronized? */ 193 return super.keys(); 194 } 195 196 @Override values()197 public Collection<V> values() { 198 assertTrue(Thread.holdsLock(mutex)); 199 /* TODO: verify that the Collection is also synchronized? */ 200 return super.values(); 201 } 202 203 @Override entries()204 public Set<Entry<K, V>> entries() { 205 assertTrue(Thread.holdsLock(mutex)); 206 /* TODO: verify that the Set is also synchronized? */ 207 return super.entries(); 208 } 209 210 @Override asMap()211 public Map<K, Collection<V>> asMap() { 212 assertTrue(Thread.holdsLock(mutex)); 213 /* TODO: verify that the Map is also synchronized? */ 214 return super.asMap(); 215 } 216 217 private static final long serialVersionUID = 0; 218 } 219 testSynchronizedListMultimap()220 public void testSynchronizedListMultimap() { 221 ListMultimap<String, Integer> multimap = 222 Multimaps.synchronizedListMultimap(ArrayListMultimap.<String, Integer>create()); 223 multimap.putAll("foo", Arrays.asList(3, -1, 2, 4, 1)); 224 multimap.putAll("bar", Arrays.asList(1, 2, 3, 1)); 225 assertThat(multimap.removeAll("foo")).containsExactly(3, -1, 2, 4, 1).inOrder(); 226 assertFalse(multimap.containsKey("foo")); 227 assertThat(multimap.replaceValues("bar", Arrays.asList(6, 5))) 228 .containsExactly(1, 2, 3, 1) 229 .inOrder(); 230 assertThat(multimap.get("bar")).containsExactly(6, 5).inOrder(); 231 } 232 testSynchronizedSortedSetMultimap()233 public void testSynchronizedSortedSetMultimap() { 234 SortedSetMultimap<String, Integer> multimap = 235 Multimaps.synchronizedSortedSetMultimap(TreeMultimap.<String, Integer>create()); 236 multimap.putAll("foo", Arrays.asList(3, -1, 2, 4, 1)); 237 multimap.putAll("bar", Arrays.asList(1, 2, 3, 1)); 238 assertThat(multimap.removeAll("foo")).containsExactly(-1, 1, 2, 3, 4).inOrder(); 239 assertFalse(multimap.containsKey("foo")); 240 assertThat(multimap.replaceValues("bar", Arrays.asList(6, 5))) 241 .containsExactly(1, 2, 3) 242 .inOrder(); 243 assertThat(multimap.get("bar")).containsExactly(5, 6).inOrder(); 244 } 245 testSynchronizedArrayListMultimapRandomAccess()246 public void testSynchronizedArrayListMultimapRandomAccess() { 247 ListMultimap<String, Integer> delegate = ArrayListMultimap.create(); 248 delegate.put("foo", 1); 249 delegate.put("foo", 3); 250 ListMultimap<String, Integer> multimap = Multimaps.synchronizedListMultimap(delegate); 251 assertTrue(multimap.get("foo") instanceof RandomAccess); 252 assertTrue(multimap.get("bar") instanceof RandomAccess); 253 } 254 testSynchronizedLinkedListMultimapRandomAccess()255 public void testSynchronizedLinkedListMultimapRandomAccess() { 256 ListMultimap<String, Integer> delegate = LinkedListMultimap.create(); 257 delegate.put("foo", 1); 258 delegate.put("foo", 3); 259 ListMultimap<String, Integer> multimap = Multimaps.synchronizedListMultimap(delegate); 260 assertFalse(multimap.get("foo") instanceof RandomAccess); 261 assertFalse(multimap.get("bar") instanceof RandomAccess); 262 } 263 } 264