001/* 002 * Copyright (C) 2012 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.google.common.collect.testing.google; 017 018import static com.google.common.base.Preconditions.checkState; 019import static com.google.common.collect.Lists.newArrayList; 020import static com.google.common.collect.testing.Helpers.assertContainsAllOf; 021import static com.google.common.collect.testing.features.CollectionSize.ZERO; 022import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; 023import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; 024import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; 025import static java.util.Collections.emptyList; 026import static java.util.Collections.singletonList; 027import static org.junit.Assert.assertThrows; 028 029import com.google.common.annotations.GwtCompatible; 030import com.google.common.collect.Iterators; 031import com.google.common.collect.Multimap; 032import com.google.common.collect.testing.features.CollectionSize; 033import com.google.common.collect.testing.features.MapFeature; 034import java.util.ArrayList; 035import java.util.Collection; 036import java.util.Collections; 037import java.util.Iterator; 038 039/** 040 * Tests for {@link Multimap#putAll(Object, Iterable)}. 041 * 042 * @author Louis Wasserman 043 */ 044@GwtCompatible 045// We use ::iterator so that we test passing a plain Iterable, not a Collection. 046@SuppressWarnings("UnnecessaryMethodReference") 047public class MultimapPutIterableTester<K, V> extends AbstractMultimapTester<K, V, Multimap<K, V>> { 048 @CollectionSize.Require(absent = ZERO) 049 @MapFeature.Require(SUPPORTS_PUT) 050 public void testPutAllNonEmptyIterableOnPresentKey() { 051 assertTrue(multimap().putAll(k0(), newArrayList(v3(), v4())::iterator)); 052 assertGet(k0(), v0(), v3(), v4()); 053 } 054 055 @CollectionSize.Require(absent = ZERO) 056 @MapFeature.Require(SUPPORTS_PUT) 057 public void testPutAllNonEmptyCollectionOnPresentKey() { 058 assertTrue(multimap().putAll(k0(), newArrayList(v3(), v4()))); 059 assertGet(k0(), v0(), v3(), v4()); 060 } 061 062 @MapFeature.Require(SUPPORTS_PUT) 063 public void testPutAllNonEmptyIterableOnAbsentKey() { 064 assertTrue(multimap().putAll(k3(), newArrayList(v3(), v4())::iterator)); 065 assertGet(k3(), v3(), v4()); 066 } 067 068 @MapFeature.Require(SUPPORTS_PUT) 069 public void testPutAllNonEmptyCollectionOnAbsentKey() { 070 assertTrue(multimap().putAll(k3(), newArrayList(v3(), v4()))); 071 assertGet(k3(), v3(), v4()); 072 } 073 074 @CollectionSize.Require(absent = ZERO) 075 @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES}) 076 public void testPutAllNullValueOnPresentKey_supported() { 077 assertTrue(multimap().putAll(k0(), newArrayList(v3(), null))); 078 assertGet(k0(), v0(), v3(), null); 079 } 080 081 @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES}) 082 public void testPutAllNullValueOnAbsentKey_supported() { 083 assertTrue(multimap().putAll(k3(), newArrayList(v3(), null))); 084 assertGet(k3(), v3(), null); 085 } 086 087 @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) 088 public void testPutAllNullValueSingle_unsupported() { 089 multimap().putAll(k1(), newArrayList((V) null)); 090 expectUnchanged(); 091 } 092 093 // In principle, it would be nice to apply these two tests to keys with existing values, too. 094 095 @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) 096 public void testPutAllNullValueNullLast_unsupported() { 097 int size = getNumElements(); 098 099 assertThrows( 100 NullPointerException.class, () -> multimap().putAll(k3(), newArrayList(v3(), null))); 101 102 Collection<V> values = multimap().get(k3()); 103 if (values.size() == 0) { 104 expectUnchanged(); 105 // Be extra thorough in case internal state was corrupted by the expected null. 106 assertEquals(new ArrayList<>(), new ArrayList<>(values)); 107 assertEquals(size, multimap().size()); 108 } else { 109 assertEquals(newArrayList(v3()), new ArrayList<>(values)); 110 assertEquals(size + 1, multimap().size()); 111 } 112 } 113 114 @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) 115 public void testPutAllNullValueNullFirst_unsupported() { 116 int size = getNumElements(); 117 118 assertThrows( 119 NullPointerException.class, () -> multimap().putAll(k3(), newArrayList(null, v3()))); 120 121 /* 122 * In principle, a Multimap implementation could add e3 first before failing on the null. But 123 * that seems unlikely enough to be worth complicating the test over, especially if there's any 124 * chance that a permissive test could mask a bug. 125 */ 126 expectUnchanged(); 127 // Be extra thorough in case internal state was corrupted by the expected null. 128 assertEquals(new ArrayList<>(), new ArrayList<>(multimap().get(k3()))); 129 assertEquals(size, multimap().size()); 130 } 131 132 @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS}) 133 public void testPutAllOnPresentNullKey() { 134 assertTrue(multimap().putAll(null, newArrayList(v3(), v4()))); 135 assertGet(null, v3(), v4()); 136 } 137 138 @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_KEYS) 139 public void testPutAllNullForbidden() { 140 assertThrows(NullPointerException.class, () -> multimap().putAll(null, singletonList(v3()))); 141 } 142 143 @SuppressWarnings("EmptyList") // ImmutableList doesn't support nullable element types 144 @MapFeature.Require(SUPPORTS_PUT) 145 public void testPutAllEmptyCollectionOnAbsentKey() { 146 assertFalse(multimap().putAll(k3(), emptyList())); 147 expectUnchanged(); 148 } 149 150 @MapFeature.Require(SUPPORTS_PUT) 151 public void testPutAllEmptyIterableOnAbsentKey() { 152 assertFalse(multimap().putAll(k3(), Collections::emptyIterator)); 153 expectUnchanged(); 154 } 155 156 @SuppressWarnings("EmptyList") // ImmutableList doesn't support nullable element types 157 @CollectionSize.Require(absent = ZERO) 158 @MapFeature.Require(SUPPORTS_PUT) 159 public void testPutAllEmptyIterableOnPresentKey() { 160 multimap().putAll(k0(), emptyList()); 161 expectUnchanged(); 162 } 163 164 @MapFeature.Require(SUPPORTS_PUT) 165 public void testPutAllOnlyCallsIteratorOnce() { 166 Iterable<V> iterable = 167 new Iterable<V>() { 168 private boolean calledIteratorAlready = false; 169 170 @Override 171 public Iterator<V> iterator() { 172 checkState(!calledIteratorAlready); 173 calledIteratorAlready = true; 174 return Iterators.forArray(v3()); 175 } 176 }; 177 178 multimap().putAll(k3(), iterable); 179 } 180 181 @MapFeature.Require(SUPPORTS_PUT) 182 public void testPutAllPropagatesToGet() { 183 Collection<V> getCollection = multimap().get(k0()); 184 int getCollectionSize = getCollection.size(); 185 assertTrue(multimap().putAll(k0(), newArrayList(v3(), v4()))); 186 assertEquals(getCollectionSize + 2, getCollection.size()); 187 assertContainsAllOf(getCollection, v3(), v4()); 188 } 189}