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