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.testers.ReflectionFreeAssertThrows.assertThrows;
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 com.google.common.collect.testing.testers.TestExceptions.SomeUncheckedException;
030import java.util.Map;
031import junit.framework.AssertionFailedError;
032import org.junit.Ignore;
033
034/**
035 * A generic JUnit test which tests {@link Map#computeIfAbsent}. Can't be invoked directly; please
036 * see {@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 MapComputeIfAbsentTester<K, V> extends AbstractMapTester<K, V> {
046
047  @MapFeature.Require(SUPPORTS_PUT)
048  public void testComputeIfAbsent_supportedAbsent() {
049    assertEquals(
050        "computeIfAbsent(notPresent, function) should return new value",
051        v3(),
052        getMap()
053            .computeIfAbsent(
054                k3(),
055                k -> {
056                  assertEquals(k3(), k);
057                  return v3();
058                }));
059    expectAdded(e3());
060  }
061
062  @MapFeature.Require(SUPPORTS_PUT)
063  @CollectionSize.Require(absent = ZERO)
064  public void testComputeIfAbsent_supportedPresent() {
065    assertEquals(
066        "computeIfAbsent(present, function) should return existing value",
067        v0(),
068        getMap()
069            .computeIfAbsent(
070                k0(),
071                k -> {
072                  throw new AssertionFailedError();
073                }));
074    expectUnchanged();
075  }
076
077  @MapFeature.Require(SUPPORTS_PUT)
078  public void testComputeIfAbsent_functionReturnsNullNotInserted() {
079    assertNull(
080        "computeIfAbsent(absent, returnsNull) should return null",
081        getMap()
082            .computeIfAbsent(
083                k3(),
084                k -> {
085                  assertEquals(k3(), k);
086                  return null;
087                }));
088    expectUnchanged();
089  }
090
091  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
092  @CollectionSize.Require(absent = ZERO)
093  public void testComputeIfAbsent_nullTreatedAsAbsent() {
094    initMapWithNullValue();
095    assertEquals(
096        "computeIfAbsent(presentAssignedToNull, function) should return newValue",
097        getValueForNullKey(),
098        getMap()
099            .computeIfAbsent(
100                getKeyForNullValue(),
101                k -> {
102                  assertEquals(getKeyForNullValue(), k);
103                  return getValueForNullKey();
104                }));
105    expectReplacement(entry(getKeyForNullValue(), getValueForNullKey()));
106  }
107
108  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS})
109  public void testComputeIfAbsent_nullKeySupported() {
110    getMap()
111        .computeIfAbsent(
112            null,
113            k -> {
114              assertNull(k);
115              return v3();
116            });
117    expectAdded(entry(null, v3()));
118  }
119
120  @MapFeature.Require(SUPPORTS_PUT)
121  public void testComputeIfAbsent_functionThrows() {
122    assertThrows(
123        SomeUncheckedException.class,
124        () ->
125            getMap()
126                .computeIfAbsent(
127                    k3(),
128                    k -> {
129                      assertEquals(k3(), k);
130                      throw new SomeUncheckedException();
131                    }));
132    expectUnchanged();
133  }
134
135  @MapFeature.Require(absent = SUPPORTS_PUT)
136  public void testComputeIfAbsent_unsupportedAbsent() {
137    assertThrows(
138        UnsupportedOperationException.class,
139        () ->
140            getMap()
141                .computeIfAbsent(
142                    k3(),
143                    k -> {
144                      // allowed to be called
145                      assertEquals(k3(), k);
146                      return v3();
147                    }));
148    expectUnchanged();
149  }
150
151  @MapFeature.Require(absent = SUPPORTS_PUT)
152  @CollectionSize.Require(absent = ZERO)
153  public void testComputeIfAbsent_unsupportedPresentExistingValue() {
154    try {
155      assertEquals(
156          "computeIfAbsent(present, returnsCurrentValue) should return present or throw",
157          v0(),
158          getMap()
159              .computeIfAbsent(
160                  k0(),
161                  k -> {
162                    assertEquals(k0(), k);
163                    return v0();
164                  }));
165    } catch (UnsupportedOperationException tolerated) {
166    }
167    expectUnchanged();
168  }
169
170  @MapFeature.Require(absent = SUPPORTS_PUT)
171  @CollectionSize.Require(absent = ZERO)
172  public void testComputeIfAbsent_unsupportedPresentDifferentValue() {
173    try {
174      assertEquals(
175          "computeIfAbsent(present, returnsDifferentValue) should return present or throw",
176          v0(),
177          getMap()
178              .computeIfAbsent(
179                  k0(),
180                  k -> {
181                    assertEquals(k0(), k);
182                    return v3();
183                  }));
184    } catch (UnsupportedOperationException tolerated) {
185    }
186    expectUnchanged();
187  }
188
189  @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_KEYS)
190  public void testComputeIfAbsent_nullKeyUnsupported() {
191    assertThrows(
192        NullPointerException.class,
193        () ->
194            getMap()
195                .computeIfAbsent(
196                    null,
197                    k -> {
198                      assertNull(k);
199                      return v3();
200                    }));
201    expectUnchanged();
202    expectNullKeyMissingWhenNullKeysUnsupported(
203        "Should not contain null key after unsupported computeIfAbsent(null, function)");
204  }
205}