Repository: google/guava (Java)
Date: 2018-06-28 15:42:23+00:00
Task type: Testing

Issue:
Test setValue(null) (both success and failure cases)



Issue discussion:
We should add this to `MapEntrySetTester` so that all our collections benefit. This is similar to what CL 116244562 did for "normal" `setValue` calls.

Hi,

Can I work on this?

Thanks,
Keerthi Korivi.
Sure, that would be terrific.
Thanks @cpovirk 
Could you please point me to the change CL 116244562, so that i can get an idea?
Is this issue still open?
Yes, we'd be happy for a pull request, thanks.
[PR #3176]
Please let me know if any changes are required. Thanks.
Hi, Do i need to add a custom entry implementation which checks for null values in the test case builder?
> Hi, Do i need to add a custom entry implementation which checks for null values in the test case builder?

I think the implementation you added for `testsForHashMapNullValuesForbidden` is the right thing to do, yes. Sorry for not reviewing this sooner.
Thanks Chris for the reply. I was just wondering if there's any other way to provide custom implementation for all entry points to a Map, like `put` and `entry.setValue`, which reduces the boilerplate code.
Not to my knowledge, unfortunately.

Solution patch:
diff --git a/android/guava/src/com/google/common/base/CaseFormat.java b/android/guava/src/com/google/common/base/CaseFormat.java
--- a/android/guava/src/com/google/common/base/CaseFormat.java
+++ b/android/guava/src/com/google/common/base/CaseFormat.java
@@ -202,7 +202,7 @@ private String normalizeFirstWord(String word) {
   }
 
   private static String firstCharOnlyToUpper(String word) {
-    return (word.isEmpty())
+    return word.isEmpty()
         ? word
         : Ascii.toUpperCase(word.charAt(0)) + Ascii.toLowerCase(word.substring(1));
   }
diff --git a/android/guava/src/com/google/common/collect/CompactLinkedHashMap.java b/android/guava/src/com/google/common/collect/CompactLinkedHashMap.java
--- a/android/guava/src/com/google/common/collect/CompactLinkedHashMap.java
+++ b/android/guava/src/com/google/common/collect/CompactLinkedHashMap.java
@@ -124,7 +124,7 @@ private void setSuccessor(int entry, int succ) {
   }
 
   private void setPredecessor(int entry, int pred) {
-    long predMask = (~0L) << 32;
+    long predMask = ~0L << 32;
     links[entry] = (links[entry] & ~predMask) | ((long) pred << 32);
   }
 
diff --git a/android/guava/src/com/google/common/collect/MapMakerInternalMap.java b/android/guava/src/com/google/common/collect/MapMakerInternalMap.java
--- a/android/guava/src/com/google/common/collect/MapMakerInternalMap.java
+++ b/android/guava/src/com/google/common/collect/MapMakerInternalMap.java
@@ -2189,7 +2189,7 @@ public WeakKeyWeakValueEntry<K, V> castForTesting(InternalEntry<K, V, ?> entry)
     @Override
     public WeakValueReference<K, V, WeakKeyWeakValueEntry<K, V>> getWeakValueReferenceForTesting(
         InternalEntry<K, V, ?> e) {
-      return (castForTesting(e)).getValueReference();
+      return castForTesting(e).getValueReference();
     }
 
     @Override
diff --git a/android/guava/src/com/google/common/collect/RegularContiguousSet.java b/android/guava/src/com/google/common/collect/RegularContiguousSet.java
--- a/android/guava/src/com/google/common/collect/RegularContiguousSet.java
+++ b/android/guava/src/com/google/common/collect/RegularContiguousSet.java
@@ -41,7 +41,7 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C>
   }
 
   private ContiguousSet<C> intersectionInCurrentDomain(Range<C> other) {
-    return (range.isConnected(other))
+    return range.isConnected(other)
         ? ContiguousSet.create(range.intersection(other), domain)
         : new EmptyContiguousSet<C>(domain);
   }
diff --git a/android/guava/src/com/google/common/hash/BloomFilter.java b/android/guava/src/com/google/common/hash/BloomFilter.java
--- a/android/guava/src/com/google/common/hash/BloomFilter.java
+++ b/android/guava/src/com/google/common/hash/BloomFilter.java
@@ -225,11 +225,11 @@ long bitSize() {
    */
   public boolean isCompatible(BloomFilter<T> that) {
     checkNotNull(that);
-    return (this != that)
-        && (this.numHashFunctions == that.numHashFunctions)
-        && (this.bitSize() == that.bitSize())
-        && (this.strategy.equals(that.strategy))
-        && (this.funnel.equals(that.funnel));
+    return this != that
+        && this.numHashFunctions == that.numHashFunctions
+        && this.bitSize() == that.bitSize()
+        && this.strategy.equals(that.strategy)
+        && this.funnel.equals(that.funnel);
   }
 
   /**
diff --git a/android/guava/src/com/google/common/hash/Hashing.java b/android/guava/src/com/google/common/hash/Hashing.java
--- a/android/guava/src/com/google/common/hash/Hashing.java
+++ b/android/guava/src/com/google/common/hash/Hashing.java
@@ -669,7 +669,7 @@ public LinearCongruentialGenerator(long seed) {
 
     public double nextDouble() {
       state = 2862933555777941757L * state + 1;
-      return ((double) ((int) (state >>> 33) + 1)) / (0x1.0p31);
+      return ((double) ((int) (state >>> 33) + 1)) / 0x1.0p31;
     }
   }
 
diff --git a/android/guava/src/com/google/common/math/DoubleUtils.java b/android/guava/src/com/google/common/math/DoubleUtils.java
--- a/android/guava/src/com/google/common/math/DoubleUtils.java
+++ b/android/guava/src/com/google/common/math/DoubleUtils.java
@@ -116,7 +116,7 @@ static double bigToDouble(BigInteger x) {
     boolean increment =
         (twiceSignifFloor & 1) != 0 && ((signifFloor & 1) != 0 || absX.getLowestSetBit() < shift);
     long signifRounded = increment ? signifFloor + 1 : signifFloor;
-    long bits = (long) ((exponent + EXPONENT_BIAS)) << SIGNIFICAND_BITS;
+    long bits = (long) (exponent + EXPONENT_BIAS) << SIGNIFICAND_BITS;
     bits += signifRounded;
     /*
      * If signifRounded == 2^53, we'd need to set all of the significand bits to zero and add 1 to
diff --git a/android/guava/src/com/google/common/math/PairedStats.java b/android/guava/src/com/google/common/math/PairedStats.java
--- a/android/guava/src/com/google/common/math/PairedStats.java
+++ b/android/guava/src/com/google/common/math/PairedStats.java
@@ -221,10 +221,10 @@ public boolean equals(@NullableDecl Object obj) {
       return false;
     }
     PairedStats other = (PairedStats) obj;
-    return (xStats.equals(other.xStats))
-        && (yStats.equals(other.yStats))
-        && (doubleToLongBits(sumOfProductsOfDeltas)
-            == doubleToLongBits(other.sumOfProductsOfDeltas));
+    return xStats.equals(other.xStats)
+        && yStats.equals(other.yStats)
+        && doubleToLongBits(sumOfProductsOfDeltas)
+            == doubleToLongBits(other.sumOfProductsOfDeltas);
   }
 
   /**
diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java b/android/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java
--- a/android/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java
+++ b/android/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java
@@ -21,7 +21,6 @@
 import com.google.common.annotations.GwtCompatible;
 import com.google.common.base.Function;
 import com.google.errorprone.annotations.ForOverride;
-import java.lang.reflect.UndeclaredThrowableException;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
@@ -109,10 +108,6 @@ public final void run() {
     T transformResult;
     try {
       transformResult = doTransform(localFunction, sourceResult);
-    } catch (UndeclaredThrowableException e) {
-      // Set the cause of the exception as this future's exception.
-      setException(e.getCause());
-      return;
     } catch (Throwable t) {
       // This exception is irrelevant in this thread, but useful for the client.
       setException(t);
@@ -237,7 +232,6 @@ private static final class TransformFuture<I, O>
     @NullableDecl
     O doTransform(Function<? super I, ? extends O> function, @NullableDecl I input) {
       return function.apply(input);
-      // TODO(lukes): move the UndeclaredThrowable catch block here?
     }
 
     @Override
diff --git a/android/pom.xml b/android/pom.xml
--- a/android/pom.xml
+++ b/android/pom.xml
@@ -18,7 +18,7 @@
   <properties>
     <!-- Override this with -Dtest.include="**/SomeTest.java" on the CLI -->
     <test.include>%regex[.*.class]</test.include>
-    <truth.version>0.35</truth.version>
+    <truth.version>0.41</truth.version>
     <animal.sniffer.version>1.14</animal.sniffer.version>
     <maven-javadoc-plugin.version>3.0.0</maven-javadoc-plugin.version>
   </properties>
@@ -226,7 +226,7 @@
       <dependency>
         <groupId>org.checkerframework</groupId>
         <artifactId>checker-compat-qual</artifactId>
-        <version>2.0.0</version>
+        <version>2.5.2</version>
       </dependency>
       <dependency>
         <groupId>com.google.errorprone</groupId>
diff --git a/guava-gwt/src-super/java/lang/super/java/lang/reflect/UndeclaredThrowableException.java b/guava-gwt/src-super/java/lang/super/java/lang/reflect/UndeclaredThrowableException.java
deleted file mode 100644
--- a/guava-gwt/src-super/java/lang/super/java/lang/reflect/UndeclaredThrowableException.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2015 The Guava Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package java.lang.reflect;
-
-/**
- * GWT emulation of UndeclaredThrowableException.
- */
-public class UndeclaredThrowableException extends RuntimeException {
-  public UndeclaredThrowableException(Throwable undeclaredThrowable) {
-    super(undeclaredThrowable);
-  }
-
-  public UndeclaredThrowableException(Throwable undeclaredThrowable, String message) {
-    super(message, undeclaredThrowable);
-  }
-
-  public Throwable getUndeclaredThrowable() {
-    return getCause();
-  }
-}
diff --git a/guava/src/com/google/common/base/CaseFormat.java b/guava/src/com/google/common/base/CaseFormat.java
--- a/guava/src/com/google/common/base/CaseFormat.java
+++ b/guava/src/com/google/common/base/CaseFormat.java
@@ -202,7 +202,7 @@ private String normalizeFirstWord(String word) {
   }
 
   private static String firstCharOnlyToUpper(String word) {
-    return (word.isEmpty())
+    return word.isEmpty()
         ? word
         : Ascii.toUpperCase(word.charAt(0)) + Ascii.toLowerCase(word.substring(1));
   }
diff --git a/guava/src/com/google/common/collect/CollectSpliterators.java b/guava/src/com/google/common/collect/CollectSpliterators.java
--- a/guava/src/com/google/common/collect/CollectSpliterators.java
+++ b/guava/src/com/google/common/collect/CollectSpliterators.java
@@ -45,7 +45,7 @@ static <T> Spliterator<T> indexed(
       IntFunction<T> function,
       Comparator<? super T> comparator) {
     if (comparator != null) {
-      checkArgument((extraCharacteristics & (Spliterator.SORTED)) != 0);
+      checkArgument((extraCharacteristics & Spliterator.SORTED) != 0);
     }
     class WithCharacteristics implements Spliterator<T> {
       private final Spliterator.OfInt delegate;
diff --git a/guava/src/com/google/common/collect/CompactLinkedHashMap.java b/guava/src/com/google/common/collect/CompactLinkedHashMap.java
--- a/guava/src/com/google/common/collect/CompactLinkedHashMap.java
+++ b/guava/src/com/google/common/collect/CompactLinkedHashMap.java
@@ -135,7 +135,7 @@ private void setSuccessor(int entry, int succ) {
   }
 
   private void setPredecessor(int entry, int pred) {
-    long predMask = (~0L) << 32;
+    long predMask = ~0L << 32;
     links[entry] = (links[entry] & ~predMask) | ((long) pred << 32);
   }
 
diff --git a/guava/src/com/google/common/collect/MapMakerInternalMap.java b/guava/src/com/google/common/collect/MapMakerInternalMap.java
--- a/guava/src/com/google/common/collect/MapMakerInternalMap.java
+++ b/guava/src/com/google/common/collect/MapMakerInternalMap.java
@@ -2184,7 +2184,7 @@ public WeakKeyWeakValueEntry<K, V> castForTesting(InternalEntry<K, V, ?> entry)
     @Override
     public WeakValueReference<K, V, WeakKeyWeakValueEntry<K, V>> getWeakValueReferenceForTesting(
         InternalEntry<K, V, ?> e) {
-      return (castForTesting(e)).getValueReference();
+      return castForTesting(e).getValueReference();
     }
 
     @Override
diff --git a/guava/src/com/google/common/collect/RegularContiguousSet.java b/guava/src/com/google/common/collect/RegularContiguousSet.java
--- a/guava/src/com/google/common/collect/RegularContiguousSet.java
+++ b/guava/src/com/google/common/collect/RegularContiguousSet.java
@@ -41,7 +41,7 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C>
   }
 
   private ContiguousSet<C> intersectionInCurrentDomain(Range<C> other) {
-    return (range.isConnected(other))
+    return range.isConnected(other)
         ? ContiguousSet.create(range.intersection(other), domain)
         : new EmptyContiguousSet<C>(domain);
   }
diff --git a/guava/src/com/google/common/hash/BloomFilter.java b/guava/src/com/google/common/hash/BloomFilter.java
--- a/guava/src/com/google/common/hash/BloomFilter.java
+++ b/guava/src/com/google/common/hash/BloomFilter.java
@@ -226,11 +226,11 @@ long bitSize() {
    */
   public boolean isCompatible(BloomFilter<T> that) {
     checkNotNull(that);
-    return (this != that)
-        && (this.numHashFunctions == that.numHashFunctions)
-        && (this.bitSize() == that.bitSize())
-        && (this.strategy.equals(that.strategy))
-        && (this.funnel.equals(that.funnel));
+    return this != that
+        && this.numHashFunctions == that.numHashFunctions
+        && this.bitSize() == that.bitSize()
+        && this.strategy.equals(that.strategy)
+        && this.funnel.equals(that.funnel);
   }
 
   /**
diff --git a/guava/src/com/google/common/hash/Hashing.java b/guava/src/com/google/common/hash/Hashing.java
--- a/guava/src/com/google/common/hash/Hashing.java
+++ b/guava/src/com/google/common/hash/Hashing.java
@@ -669,7 +669,7 @@ public LinearCongruentialGenerator(long seed) {
 
     public double nextDouble() {
       state = 2862933555777941757L * state + 1;
-      return ((double) ((int) (state >>> 33) + 1)) / (0x1.0p31);
+      return ((double) ((int) (state >>> 33) + 1)) / 0x1.0p31;
     }
   }
 
diff --git a/guava/src/com/google/common/math/DoubleUtils.java b/guava/src/com/google/common/math/DoubleUtils.java
--- a/guava/src/com/google/common/math/DoubleUtils.java
+++ b/guava/src/com/google/common/math/DoubleUtils.java
@@ -116,7 +116,7 @@ static double bigToDouble(BigInteger x) {
     boolean increment =
         (twiceSignifFloor & 1) != 0 && ((signifFloor & 1) != 0 || absX.getLowestSetBit() < shift);
     long signifRounded = increment ? signifFloor + 1 : signifFloor;
-    long bits = (long) ((exponent + EXPONENT_BIAS)) << SIGNIFICAND_BITS;
+    long bits = (long) (exponent + EXPONENT_BIAS) << SIGNIFICAND_BITS;
     bits += signifRounded;
     /*
      * If signifRounded == 2^53, we'd need to set all of the significand bits to zero and add 1 to
diff --git a/guava/src/com/google/common/math/PairedStats.java b/guava/src/com/google/common/math/PairedStats.java
--- a/guava/src/com/google/common/math/PairedStats.java
+++ b/guava/src/com/google/common/math/PairedStats.java
@@ -221,10 +221,10 @@ public boolean equals(@Nullable Object obj) {
       return false;
     }
     PairedStats other = (PairedStats) obj;
-    return (xStats.equals(other.xStats))
-        && (yStats.equals(other.yStats))
-        && (doubleToLongBits(sumOfProductsOfDeltas)
-            == doubleToLongBits(other.sumOfProductsOfDeltas));
+    return xStats.equals(other.xStats)
+        && yStats.equals(other.yStats)
+        && doubleToLongBits(sumOfProductsOfDeltas)
+            == doubleToLongBits(other.sumOfProductsOfDeltas);
   }
 
   /**
diff --git a/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java b/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java
--- a/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java
+++ b/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java
@@ -21,7 +21,6 @@
 import com.google.common.annotations.GwtCompatible;
 import com.google.common.base.Function;
 import com.google.errorprone.annotations.ForOverride;
-import java.lang.reflect.UndeclaredThrowableException;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
@@ -109,10 +108,6 @@ public final void run() {
     T transformResult;
     try {
       transformResult = doTransform(localFunction, sourceResult);
-    } catch (UndeclaredThrowableException e) {
-      // Set the cause of the exception as this future's exception.
-      setException(e.getCause());
-      return;
     } catch (Throwable t) {
       // This exception is irrelevant in this thread, but useful for the client.
       setException(t);
@@ -236,7 +231,6 @@ private static final class TransformFuture<I, O>
     @Nullable
     O doTransform(Function<? super I, ? extends O> function, @Nullable I input) {
       return function.apply(input);
-      // TODO(lukes): move the UndeclaredThrowable catch block here?
     }
 
     @Override
diff --git a/pom.xml b/pom.xml
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
   <properties>
     <!-- Override this with -Dtest.include="**/SomeTest.java" on the CLI -->
     <test.include>%regex[.*.class]</test.include>
-    <truth.version>0.35</truth.version>
+    <truth.version>0.41</truth.version>
     <animal.sniffer.version>1.14</animal.sniffer.version>
     <maven-javadoc-plugin.version>3.0.0</maven-javadoc-plugin.version>
   </properties>
@@ -222,7 +222,7 @@
       <dependency>
         <groupId>org.checkerframework</groupId>
         <artifactId>checker-qual</artifactId>
-        <version>2.0.0</version>
+        <version>2.5.2</version>
       </dependency>
       <dependency>
         <groupId>com.google.errorprone</groupId>


Test patch:
diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestsForMapsInJavaUtil.java b/android/guava-testlib/src/com/google/common/collect/testing/TestsForMapsInJavaUtil.java
--- a/android/guava-testlib/src/com/google/common/collect/testing/TestsForMapsInJavaUtil.java
+++ b/android/guava-testlib/src/com/google/common/collect/testing/TestsForMapsInJavaUtil.java
@@ -127,7 +127,10 @@ protected Collection<Method> suppressForConcurrentHashMap() {
   }
 
   protected Collection<Method> suppressForConcurrentSkipListMap() {
-    return asList(MapEntrySetTester.getSetValueMethod());
+    return asList(
+        MapEntrySetTester.getSetValueMethod(),
+        MapEntrySetTester.getSetValueWithNullValuesAbsentMethod(),
+        MapEntrySetTester.getSetValueWithNullValuesPresentMethod());
   }
 
   public Test testsForCheckedMap() {
diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/SetGenerators.java b/android/guava-testlib/src/com/google/common/collect/testing/google/SetGenerators.java
--- a/android/guava-testlib/src/com/google/common/collect/testing/google/SetGenerators.java
+++ b/android/guava-testlib/src/com/google/common/collect/testing/google/SetGenerators.java
@@ -355,7 +355,7 @@ public static class ContiguousSetHeadsetGenerator extends AbstractContiguousSetG
     @Override
     protected SortedSet<Integer> create(Integer[] elements) {
       SortedSet<Integer> set = nullCheckedTreeSet(elements);
-      int tooHigh = (set.isEmpty()) ? 0 : set.last() + 1;
+      int tooHigh = set.isEmpty() ? 0 : set.last() + 1;
       set.add(tooHigh);
       return checkedCreate(set).headSet(tooHigh);
     }
@@ -365,7 +365,7 @@ public static class ContiguousSetTailsetGenerator extends AbstractContiguousSetG
     @Override
     protected SortedSet<Integer> create(Integer[] elements) {
       SortedSet<Integer> set = nullCheckedTreeSet(elements);
-      int tooLow = (set.isEmpty()) ? 0 : set.first() - 1;
+      int tooLow = set.isEmpty() ? 0 : set.first() - 1;
       set.add(tooLow);
       return checkedCreate(set).tailSet(tooLow + 1);
     }
@@ -417,7 +417,7 @@ protected final ContiguousSet<Integer> checkedCreate(SortedSet<Integer> elements
         assertEquals(elements.get(i) + 1, (int) elements.get(i + 1));
       }
       Range<Integer> range =
-          (elements.isEmpty()) ? Range.closedOpen(0, 0) : Range.encloseAll(elements);
+          elements.isEmpty() ? Range.closedOpen(0, 0) : Range.encloseAll(elements);
       return ContiguousSet.create(range, DiscreteDomain.integers());
     }
   }
diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapEntrySetTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapEntrySetTester.java
--- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapEntrySetTester.java
+++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapEntrySetTester.java
@@ -115,6 +115,32 @@ public void testSetValue() {
     expectReplacement(entry(k0(), v3()));
   }
 
+  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
+  @CollectionSize.Require(absent = ZERO)
+  public void testSetValueWithNullValuesPresent() {
+    for (Entry<K, V> entry : getMap().entrySet()) {
+      if (entry.getKey().equals(k0())) {
+        assertEquals("entry.setValue() should return the old value", v0(), entry.setValue(null));
+        break;
+      }
+    }
+    expectReplacement(entry(k0(), (V) null));
+  }
+
+  @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES)
+  @CollectionSize.Require(absent = ZERO)
+  public void testSetValueWithNullValuesAbsent() {
+    for (Entry<K, V> entry : getMap().entrySet()) {
+      try {
+        entry.setValue(null);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException exception) {
+        break;
+      }
+    }
+    expectUnchanged();
+  }
+
   @GwtIncompatible // reflection
   public static Method getContainsEntryWithIncomparableKeyMethod() {
     return Helpers.getMethod(MapEntrySetTester.class, "testContainsEntryWithIncomparableKey");
@@ -129,4 +155,14 @@ public static Method getContainsEntryWithIncomparableValueMethod() {
   public static Method getSetValueMethod() {
     return Helpers.getMethod(MapEntrySetTester.class, "testSetValue");
   }
+
+  @GwtIncompatible // reflection
+  public static Method getSetValueWithNullValuesPresentMethod() {
+    return Helpers.getMethod(MapEntrySetTester.class, "testSetValueWithNullValuesPresent");
+  }
+
+  @GwtIncompatible // reflection
+  public static Method getSetValueWithNullValuesAbsentMethod() {
+    return Helpers.getMethod(MapEntrySetTester.class, "testSetValueWithNullValuesAbsent");
+  }
 }
diff --git a/android/guava-testlib/src/com/google/common/testing/TearDownStack.java b/android/guava-testlib/src/com/google/common/testing/TearDownStack.java
--- a/android/guava-testlib/src/com/google/common/testing/TearDownStack.java
+++ b/android/guava-testlib/src/com/google/common/testing/TearDownStack.java
@@ -80,7 +80,7 @@ public final void runTearDown() {
         }
       }
     }
-    if ((!suppressThrows) && (exceptions.size() > 0)) {
+    if (!suppressThrows && (exceptions.size() > 0)) {
       throw ClusterException.create(exceptions);
     }
   }
diff --git a/android/guava-testlib/test/com/google/common/collect/testing/MapTestSuiteBuilderTests.java b/android/guava-testlib/test/com/google/common/collect/testing/MapTestSuiteBuilderTests.java
--- a/android/guava-testlib/test/com/google/common/collect/testing/MapTestSuiteBuilderTests.java
+++ b/android/guava-testlib/test/com/google/common/collect/testing/MapTestSuiteBuilderTests.java
@@ -27,8 +27,11 @@
 import com.google.common.collect.testing.features.Feature;
 import com.google.common.collect.testing.features.MapFeature;
 import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -115,10 +118,127 @@ Map<String, String> wrap(final HashMap<String, String> map) {
             if (map.containsValue(null)) {
               throw new NullPointerException();
             }
+
             return new AbstractMap<String, String>() {
               @Override
               public Set<Entry<String, String>> entrySet() {
-                return map.entrySet();
+                return new EntrySet();
+              }
+
+              @Override
+              public int hashCode() {
+                return map.hashCode();
+              }
+
+              @Override
+              public boolean equals(Object o) {
+                return map.equals(o);
+              }
+
+              @Override
+              public String toString() {
+                return map.toString();
+              }
+
+              @Override
+              public String remove(Object key) {
+                return map.remove(key);
+              }
+
+              class EntrySet extends AbstractSet<Map.Entry<String, String>> {
+                @Override
+                public Iterator<Entry<String, String>> iterator() {
+                  return new Iterator<Entry<String, String>>() {
+
+                    final Iterator<Entry<String, String>> iterator = map.entrySet().iterator();
+
+                    @Override
+                    public void remove() {
+                      iterator.remove();
+                    }
+
+                    @Override
+                    public boolean hasNext() {
+                      return iterator.hasNext();
+                    }
+
+                    @Override
+                    public Entry<String, String> next() {
+                      return transform(iterator.next());
+                    }
+
+                    private Entry<String, String> transform(final Entry<String, String> next) {
+                      return new Entry<String, String>() {
+
+                        @Override
+                        public String setValue(String value) {
+                          checkNotNull(value);
+                          return next.setValue(value);
+                        }
+
+                        @Override
+                        public String getValue() {
+                          return next.getValue();
+                        }
+
+                        @Override
+                        public String getKey() {
+                          return next.getKey();
+                        }
+
+                        @Override
+                        public boolean equals(Object obj) {
+                          return next.equals(obj);
+                        }
+
+                        @Override
+                        public int hashCode() {
+                          return next.hashCode();
+                        }
+                      };
+                    }
+                  };
+                }
+
+                @Override
+                public int size() {
+                  return map.size();
+                }
+
+                @Override
+                public boolean remove(Object o) {
+                  return map.entrySet().remove(o);
+                }
+
+                @Override
+                public boolean containsAll(Collection<?> c) {
+                  return map.entrySet().containsAll(c);
+                }
+
+                @Override
+                public boolean removeAll(Collection<?> c) {
+                  return map.entrySet().removeAll(c);
+                }
+
+                @Override
+                public boolean retainAll(Collection<?> c) {
+                  return map.entrySet().retainAll(c);
+                }
+
+                @Override
+                public int hashCode() {
+                  return map.entrySet().hashCode();
+                }
+
+                @Override
+                public boolean equals(Object o) {
+                  return map.entrySet().equals(o);
+                }
+
+                @Override
+                public String toString() {
+                  return map.entrySet().toString();
+                }
               }
 
               @Override
diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingNavigableMapTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingNavigableMapTest.java
--- a/android/guava-tests/test/com/google/common/collect/ForwardingNavigableMapTest.java
+++ b/android/guava-tests/test/com/google/common/collect/ForwardingNavigableMapTest.java
@@ -279,7 +279,10 @@ protected SortedMap<String, String> create(Entry<String, String>[] entries) {
              * TODO(cpovirk): Consider making StandardDescendingMap return a ForwardingEntry that
              * supports setValue().
              */
-            .suppressing(MapEntrySetTester.getSetValueMethod())
+            .suppressing(
+                MapEntrySetTester.getSetValueMethod(),
+                MapEntrySetTester.getSetValueWithNullValuesAbsentMethod(),
+                MapEntrySetTester.getSetValueWithNullValuesPresentMethod())
             .createTestSuite());
     // TODO(lowasser): add forwarding-to-ImmutableSortedMap test
     return suite;
diff --git a/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformAsyncTest.java b/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformAsyncTest.java
--- a/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformAsyncTest.java
+++ b/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformAsyncTest.java
@@ -22,7 +22,6 @@
 import static com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly;
 
 import com.google.common.util.concurrent.ForwardingListenableFuture.SimpleForwardingListenableFuture;
-import java.lang.reflect.UndeclaredThrowableException;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -58,7 +57,7 @@ protected String getSuccessfulResult() {
 
   private class ChainingFunction implements AsyncFunction<Integer, String> {
     @Override
-    public ListenableFuture<String> apply(Integer input) {
+    public ListenableFuture<String> apply(Integer input) throws Exception {
       switch (input) {
         case VALID_INPUT_DATA:
           outputFuture.set(RESULT_DATA);
@@ -69,8 +68,8 @@ public ListenableFuture<String> apply(Integer input) {
           funcIsWaitingLatch.countDown();
           awaitUninterruptibly(funcCompletionLatch);
           break;
-        default:
-          throw new UndeclaredThrowableException(EXCEPTION);
+        case EXCEPTION_DATA:
+          throw EXCEPTION;
       }
       return outputFuture;
     }
diff --git a/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformTest.java b/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformTest.java
--- a/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformTest.java
+++ b/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformTest.java
@@ -29,6 +29,8 @@
  */
 public class FuturesTransformTest extends AbstractChainedListenableFutureTest<String> {
   private static final String RESULT_DATA = "SUCCESS";
+  private static final UndeclaredThrowableException WRAPPED_EXCEPTION =
+      new UndeclaredThrowableException(EXCEPTION);
 
   @Override
   protected ListenableFuture<String> buildChainingFuture(ListenableFuture<Integer> inputFuture) {
@@ -46,13 +48,13 @@ public String apply(Integer input) {
       if (input.intValue() == VALID_INPUT_DATA) {
         return RESULT_DATA;
       } else {
-        throw new UndeclaredThrowableException(EXCEPTION);
+        throw WRAPPED_EXCEPTION;
       }
     }
   }
 
   public void testFutureGetThrowsFunctionException() throws Exception {
     inputFuture.set(EXCEPTION_DATA);
-    listener.assertException(EXCEPTION);
+    listener.assertException(WRAPPED_EXCEPTION);
   }
 }
diff --git a/guava-testlib/src/com/google/common/collect/testing/TestsForMapsInJavaUtil.java b/guava-testlib/src/com/google/common/collect/testing/TestsForMapsInJavaUtil.java
--- a/guava-testlib/src/com/google/common/collect/testing/TestsForMapsInJavaUtil.java
+++ b/guava-testlib/src/com/google/common/collect/testing/TestsForMapsInJavaUtil.java
@@ -153,7 +153,10 @@ protected Collection<Method> suppressForConcurrentHashMap() {
   }
 
   protected Collection<Method> suppressForConcurrentSkipListMap() {
-    return asList(MapEntrySetTester.getSetValueMethod());
+    return asList(
+        MapEntrySetTester.getSetValueMethod(),
+        MapEntrySetTester.getSetValueWithNullValuesAbsentMethod(),
+        MapEntrySetTester.getSetValueWithNullValuesPresentMethod());
   }
 
   public Test testsForCheckedMap() {
diff --git a/guava-testlib/src/com/google/common/collect/testing/google/SetGenerators.java b/guava-testlib/src/com/google/common/collect/testing/google/SetGenerators.java
--- a/guava-testlib/src/com/google/common/collect/testing/google/SetGenerators.java
+++ b/guava-testlib/src/com/google/common/collect/testing/google/SetGenerators.java
@@ -355,7 +355,7 @@ public static class ContiguousSetHeadsetGenerator extends AbstractContiguousSetG
     @Override
     protected SortedSet<Integer> create(Integer[] elements) {
       SortedSet<Integer> set = nullCheckedTreeSet(elements);
-      int tooHigh = (set.isEmpty()) ? 0 : set.last() + 1;
+      int tooHigh = set.isEmpty() ? 0 : set.last() + 1;
       set.add(tooHigh);
       return checkedCreate(set).headSet(tooHigh);
     }
@@ -365,7 +365,7 @@ public static class ContiguousSetTailsetGenerator extends AbstractContiguousSetG
     @Override
     protected SortedSet<Integer> create(Integer[] elements) {
       SortedSet<Integer> set = nullCheckedTreeSet(elements);
-      int tooLow = (set.isEmpty()) ? 0 : set.first() - 1;
+      int tooLow = set.isEmpty() ? 0 : set.first() - 1;
       set.add(tooLow);
       return checkedCreate(set).tailSet(tooLow + 1);
     }
@@ -417,7 +417,7 @@ protected final ContiguousSet<Integer> checkedCreate(SortedSet<Integer> elements
         assertEquals(elements.get(i) + 1, (int) elements.get(i + 1));
       }
       Range<Integer> range =
-          (elements.isEmpty()) ? Range.closedOpen(0, 0) : Range.encloseAll(elements);
+          elements.isEmpty() ? Range.closedOpen(0, 0) : Range.encloseAll(elements);
       return ContiguousSet.create(range, DiscreteDomain.integers());
     }
   }
diff --git a/guava-testlib/src/com/google/common/collect/testing/testers/ListReplaceAllTester.java b/guava-testlib/src/com/google/common/collect/testing/testers/ListReplaceAllTester.java
--- a/guava-testlib/src/com/google/common/collect/testing/testers/ListReplaceAllTester.java
+++ b/guava-testlib/src/com/google/common/collect/testing/testers/ListReplaceAllTester.java
@@ -43,7 +43,7 @@ public void testReplaceAll() {
 
   @ListFeature.Require(SUPPORTS_SET)
   public void testReplaceAll_changesSome() {
-    getList().replaceAll(e -> (e.equals(samples.e0())) ? samples.e3() : e);
+    getList().replaceAll(e -> e.equals(samples.e0()) ? samples.e3() : e);
     E[] expected = createSamplesArray();
     for (int i = 0; i < expected.length; i++) {
       if (expected[i].equals(samples.e0())) {
diff --git a/guava-testlib/src/com/google/common/collect/testing/testers/MapEntrySetTester.java b/guava-testlib/src/com/google/common/collect/testing/testers/MapEntrySetTester.java
--- a/guava-testlib/src/com/google/common/collect/testing/testers/MapEntrySetTester.java
+++ b/guava-testlib/src/com/google/common/collect/testing/testers/MapEntrySetTester.java
@@ -115,6 +115,32 @@ public void testSetValue() {
     expectReplacement(entry(k0(), v3()));
   }
 
+  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
+  @CollectionSize.Require(absent = ZERO)
+  public void testSetValueWithNullValuesPresent() {
+    for (Entry<K, V> entry : getMap().entrySet()) {
+      if (entry.getKey().equals(k0())) {
+        assertEquals("entry.setValue() should return the old value", v0(), entry.setValue(null));
+        break;
+      }
+    }
+    expectReplacement(entry(k0(), (V) null));
+  }
+
+  @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES)
+  @CollectionSize.Require(absent = ZERO)
+  public void testSetValueWithNullValuesAbsent() {
+    for (Entry<K, V> entry : getMap().entrySet()) {
+      try {
+        entry.setValue(null);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException exception) {
+        break;
+      }
+    }
+    expectUnchanged();
+  }
+
   @GwtIncompatible // reflection
   public static Method getContainsEntryWithIncomparableKeyMethod() {
     return Helpers.getMethod(MapEntrySetTester.class, "testContainsEntryWithIncomparableKey");
@@ -129,4 +155,14 @@ public static Method getContainsEntryWithIncomparableValueMethod() {
   public static Method getSetValueMethod() {
     return Helpers.getMethod(MapEntrySetTester.class, "testSetValue");
   }
+
+  @GwtIncompatible // reflection
+  public static Method getSetValueWithNullValuesPresentMethod() {
+    return Helpers.getMethod(MapEntrySetTester.class, "testSetValueWithNullValuesPresent");
+  }
+
+  @GwtIncompatible // reflection
+  public static Method getSetValueWithNullValuesAbsentMethod() {
+    return Helpers.getMethod(MapEntrySetTester.class, "testSetValueWithNullValuesAbsent");
+  }
 }
diff --git a/guava-testlib/src/com/google/common/testing/TearDownStack.java b/guava-testlib/src/com/google/common/testing/TearDownStack.java
--- a/guava-testlib/src/com/google/common/testing/TearDownStack.java
+++ b/guava-testlib/src/com/google/common/testing/TearDownStack.java
@@ -80,7 +80,7 @@ public final void runTearDown() {
         }
       }
     }
-    if ((!suppressThrows) && (exceptions.size() > 0)) {
+    if (!suppressThrows && (exceptions.size() > 0)) {
       throw ClusterException.create(exceptions);
     }
   }
diff --git a/guava-testlib/test/com/google/common/collect/testing/MapTestSuiteBuilderTests.java b/guava-testlib/test/com/google/common/collect/testing/MapTestSuiteBuilderTests.java
--- a/guava-testlib/test/com/google/common/collect/testing/MapTestSuiteBuilderTests.java
+++ b/guava-testlib/test/com/google/common/collect/testing/MapTestSuiteBuilderTests.java
@@ -27,12 +27,16 @@
 import com.google.common.collect.testing.features.Feature;
 import com.google.common.collect.testing.features.MapFeature;
 import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.function.Predicate;
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
@@ -115,10 +119,137 @@ Map<String, String> wrap(final HashMap<String, String> map) {
             if (map.containsValue(null)) {
               throw new NullPointerException();
             }
+
             return new AbstractMap<String, String>() {
               @Override
               public Set<Entry<String, String>> entrySet() {
-                return map.entrySet();
+                return new EntrySet();
+              }
+
+              @Override
+              public int hashCode() {
+                return map.hashCode();
+              }
+
+              @Override
+              public boolean equals(Object o) {
+                return map.equals(o);
+              }
+
+              @Override
+              public String toString() {
+                return map.toString();
+              }
+
+              @Override
+              public String remove(Object key) {
+                return map.remove(key);
+              }
+
+              @Override
+              public boolean remove(Object key, Object value) {
+                return map.remove(key, value);
+              }
+
+              class EntrySet extends AbstractSet<Map.Entry<String, String>> {
+                @Override
+                public Iterator<Entry<String, String>> iterator() {
+                  return new Iterator<Entry<String, String>>() {
+
+                    final Iterator<Entry<String, String>> iterator = map.entrySet().iterator();
+
+                    @Override
+                    public void remove() {
+                      iterator.remove();
+                    }
+
+                    @Override
+                    public boolean hasNext() {
+                      return iterator.hasNext();
+                    }
+
+                    @Override
+                    public Entry<String, String> next() {
+                      return transform(iterator.next());
+                    }
+
+                    private Entry<String, String> transform(final Entry<String, String> next) {
+                      return new Entry<String, String>() {
+
+                        @Override
+                        public String setValue(String value) {
+                          checkNotNull(value);
+                          return next.setValue(value);
+                        }
+
+                        @Override
+                        public String getValue() {
+                          return next.getValue();
+                        }
+
+                        @Override
+                        public String getKey() {
+                          return next.getKey();
+                        }
+
+                        @Override
+                        public boolean equals(Object obj) {
+                          return next.equals(obj);
+                        }
+
+                        @Override
+                        public int hashCode() {
+                          return next.hashCode();
+                        }
+                      };
+                    }
+                  };
+                }
+
+                @Override
+                public int size() {
+                  return map.size();
+                }
+
+                @Override
+                public boolean remove(Object o) {
+                  return map.entrySet().remove(o);
+                }
+
+                @Override
+                public boolean removeIf(Predicate<? super Entry<String, String>> filter) {
+                  return map.entrySet().removeIf(filter);
+                }
+
+                @Override
+                public boolean containsAll(Collection<?> c) {
+                  return map.entrySet().containsAll(c);
+                }
+
+                @Override
+                public boolean removeAll(Collection<?> c) {
+                  return map.entrySet().removeAll(c);
+                }
+
+                @Override
+                public boolean retainAll(Collection<?> c) {
+                  return map.entrySet().retainAll(c);
+                }
+
+                @Override
+                public int hashCode() {
+                  return map.entrySet().hashCode();
+                }
+
+                @Override
+                public boolean equals(Object o) {
+                  return map.entrySet().equals(o);
+                }
+
+                @Override
+                public String toString() {
+                  return map.entrySet().toString();
+                }
               }
 
               @Override
diff --git a/guava-tests/test/com/google/common/collect/ForwardingNavigableMapTest.java b/guava-tests/test/com/google/common/collect/ForwardingNavigableMapTest.java
--- a/guava-tests/test/com/google/common/collect/ForwardingNavigableMapTest.java
+++ b/guava-tests/test/com/google/common/collect/ForwardingNavigableMapTest.java
@@ -279,7 +279,10 @@ protected SortedMap<String, String> create(Entry<String, String>[] entries) {
              * TODO(cpovirk): Consider making StandardDescendingMap return a ForwardingEntry that
              * supports setValue().
              */
-            .suppressing(MapEntrySetTester.getSetValueMethod())
+            .suppressing(
+                MapEntrySetTester.getSetValueMethod(),
+                MapEntrySetTester.getSetValueWithNullValuesAbsentMethod(),
+                MapEntrySetTester.getSetValueWithNullValuesPresentMethod())
             .createTestSuite());
     // TODO(lowasser): add forwarding-to-ImmutableSortedMap test
     return suite;
diff --git a/guava-tests/test/com/google/common/util/concurrent/FuturesTransformAsyncTest.java b/guava-tests/test/com/google/common/util/concurrent/FuturesTransformAsyncTest.java
--- a/guava-tests/test/com/google/common/util/concurrent/FuturesTransformAsyncTest.java
+++ b/guava-tests/test/com/google/common/util/concurrent/FuturesTransformAsyncTest.java
@@ -22,7 +22,6 @@
 import static com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly;
 
 import com.google.common.util.concurrent.ForwardingListenableFuture.SimpleForwardingListenableFuture;
-import java.lang.reflect.UndeclaredThrowableException;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -58,7 +57,7 @@ protected String getSuccessfulResult() {
 
   private class ChainingFunction implements AsyncFunction<Integer, String> {
     @Override
-    public ListenableFuture<String> apply(Integer input) {
+    public ListenableFuture<String> apply(Integer input) throws Exception {
       switch (input) {
         case VALID_INPUT_DATA:
           outputFuture.set(RESULT_DATA);
@@ -69,8 +68,8 @@ public ListenableFuture<String> apply(Integer input) {
           funcIsWaitingLatch.countDown();
           awaitUninterruptibly(funcCompletionLatch);
           break;
-        default:
-          throw new UndeclaredThrowableException(EXCEPTION);
+        case EXCEPTION_DATA:
+          throw EXCEPTION;
       }
       return outputFuture;
     }
diff --git a/guava-tests/test/com/google/common/util/concurrent/FuturesTransformTest.java b/guava-tests/test/com/google/common/util/concurrent/FuturesTransformTest.java
--- a/guava-tests/test/com/google/common/util/concurrent/FuturesTransformTest.java
+++ b/guava-tests/test/com/google/common/util/concurrent/FuturesTransformTest.java
@@ -29,6 +29,8 @@
  */
 public class FuturesTransformTest extends AbstractChainedListenableFutureTest<String> {
   private static final String RESULT_DATA = "SUCCESS";
+  private static final UndeclaredThrowableException WRAPPED_EXCEPTION =
+      new UndeclaredThrowableException(EXCEPTION);
 
   @Override
   protected ListenableFuture<String> buildChainingFuture(ListenableFuture<Integer> inputFuture) {
@@ -46,13 +48,13 @@ public String apply(Integer input) {
       if (input.intValue() == VALID_INPUT_DATA) {
         return RESULT_DATA;
       } else {
-        throw new UndeclaredThrowableException(EXCEPTION);
+        throw WRAPPED_EXCEPTION;
       }
     }
   }
 
   public void testFutureGetThrowsFunctionException() throws Exception {
     inputFuture.set(EXCEPTION_DATA);
-    listener.assertException(EXCEPTION);
+    listener.assertException(WRAPPED_EXCEPTION);
   }
 }