001/*
002 * Copyright (C) 2015 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 */
016
017package com.google.common.collect.testing.testers;
018
019import static com.google.common.collect.testing.features.CollectionSize.ZERO;
020import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS;
021import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES;
022import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT;
023import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE;
024
025import com.google.common.annotations.GwtCompatible;
026import com.google.common.collect.testing.AbstractMapTester;
027import com.google.common.collect.testing.features.CollectionSize;
028import com.google.common.collect.testing.features.MapFeature;
029import java.util.Map;
030import org.junit.Ignore;
031
032/**
033 * A generic JUnit test which tests {@link Map#compute}. Can't be invoked directly; please see
034 * {@link com.google.common.collect.testing.MapTestSuiteBuilder}.
035 *
036 * @author Louis Wasserman
037 */
038@GwtCompatible
039@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests.
040public class MapComputeTester<K, V> extends AbstractMapTester<K, V> {
041  @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE})
042  public void testCompute_absentToPresent() {
043    assertEquals(
044        "Map.compute(absent, functionReturningValue) should return value",
045        v3(),
046        getMap()
047            .compute(
048                k3(),
049                (k, v) -> {
050                  assertEquals(k3(), k);
051                  assertNull(v);
052                  return v3();
053                }));
054    expectAdded(e3());
055    assertEquals(getNumElements() + 1, getMap().size());
056  }
057
058  @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE})
059  public void testCompute_absentToAbsent() {
060    assertNull(
061        "Map.compute(absent, functionReturningNull) should return null",
062        getMap()
063            .compute(
064                k3(),
065                (k, v) -> {
066                  assertEquals(k3(), k);
067                  assertNull(v);
068                  return null;
069                }));
070    expectUnchanged();
071    assertEquals(getNumElements(), getMap().size());
072  }
073
074  @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE})
075  @CollectionSize.Require(absent = ZERO)
076  public void testCompute_presentToPresent() {
077    assertEquals(
078        "Map.compute(present, functionReturningValue) should return new value",
079        v3(),
080        getMap()
081            .compute(
082                k0(),
083                (k, v) -> {
084                  assertEquals(k0(), k);
085                  assertEquals(v0(), v);
086                  return v3();
087                }));
088    expectReplacement(entry(k0(), v3()));
089    assertEquals(getNumElements(), getMap().size());
090  }
091
092  @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE})
093  @CollectionSize.Require(absent = ZERO)
094  public void testCompute_presentToAbsent() {
095    assertNull(
096        "Map.compute(present, functionReturningNull) should return null",
097        getMap()
098            .compute(
099                k0(),
100                (k, v) -> {
101                  assertEquals(k0(), k);
102                  assertEquals(v0(), v);
103                  return null;
104                }));
105    expectMissing(e0());
106    expectMissingKeys(k0());
107    assertEquals(getNumElements() - 1, getMap().size());
108  }
109
110  @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE, ALLOWS_NULL_VALUES})
111  @CollectionSize.Require(absent = ZERO)
112  public void testCompute_presentNullToPresentNonnull() {
113    initMapWithNullValue();
114    V value = getValueForNullKey();
115    assertEquals(
116        "Map.compute(presentMappedToNull, functionReturningValue) should return new value",
117        value,
118        getMap()
119            .compute(
120                getKeyForNullValue(),
121                (k, v) -> {
122                  assertEquals(getKeyForNullValue(), k);
123                  assertNull(v);
124                  return value;
125                }));
126    expectReplacement(entry(getKeyForNullValue(), value));
127    assertEquals(getNumElements(), getMap().size());
128  }
129
130  @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE, ALLOWS_NULL_VALUES})
131  @CollectionSize.Require(absent = ZERO)
132  public void testCompute_presentNullToNull() {
133    // The spec is somewhat ambiguous about this case, but the actual default implementation
134    // in Map will remove a present null.
135    initMapWithNullValue();
136    assertNull(
137        "Map.compute(presentMappedToNull, functionReturningNull) should return null",
138        getMap()
139            .compute(
140                getKeyForNullValue(),
141                (k, v) -> {
142                  assertEquals(getKeyForNullValue(), k);
143                  assertNull(v);
144                  return null;
145                }));
146    expectMissingKeys(getKeyForNullValue());
147    assertEquals(getNumElements() - 1, getMap().size());
148  }
149
150  @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE, ALLOWS_NULL_KEYS})
151  @CollectionSize.Require(absent = ZERO)
152  public void testCompute_nullKeyPresentToPresent() {
153    initMapWithNullKey();
154    assertEquals(
155        "Map.compute(present, functionReturningValue) should return new value",
156        v3(),
157        getMap()
158            .compute(
159                null,
160                (k, v) -> {
161                  assertNull(k);
162                  assertEquals(getValueForNullKey(), v);
163                  return v3();
164                }));
165    assertEquals(getNumElements(), getMap().size());
166  }
167
168  static class ExpectedException extends RuntimeException {}
169
170  @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE})
171  @CollectionSize.Require(absent = ZERO)
172  public void testCompute_presentFunctionThrows() {
173    try {
174      getMap()
175          .compute(
176              k0(),
177              (k, v) -> {
178                assertEquals(k0(), k);
179                assertEquals(v0(), v);
180                throw new ExpectedException();
181              });
182      fail("Expected ExpectedException");
183    } catch (ExpectedException expected) {
184    }
185    expectUnchanged();
186  }
187
188  @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE})
189  public void testCompute_absentFunctionThrows() {
190    try {
191      getMap()
192          .compute(
193              k3(),
194              (k, v) -> {
195                assertEquals(k3(), k);
196                assertNull(v);
197                throw new ExpectedException();
198              });
199      fail("Expected ExpectedException");
200    } catch (ExpectedException expected) {
201    }
202    expectUnchanged();
203  }
204}