Merge "Revert^4 "Upgrade guava to v30.0""
diff --git a/.travis.yml b/.travis.yml
index 435138f..fac7af9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,14 +3,13 @@
 language: java
 
 jdk:
-  - oraclejdk8
+  - openjdk8
   - openjdk11
 
-# https://github.com/travis-ci/travis-ci/issues/3259#issuecomment-130860338
 addons:
   apt:
     packages:
-      - oracle-java8-installer
+      - openjdk-8-source
 
 install: mvn -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn install -U -DskipTests=true -f $ROOT_POM
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8acd79c..8e54c6f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -42,8 +42,8 @@
 Guidelines for any code contributions:
 
   1. Any significant changes should be accompanied by tests. The project already
-     has good test coverage, so look at some of the existing tests if you're
-     unsure how to go about it.
+     has good test coverage, so look at some existing tests if you're unsure
+     how to go about it.
   2. All contributions must be licensed Apache 2.0 and all files must have a
      copy of the boilerplate license comment (can be copied from an existing
      file).
diff --git a/README.md b/README.md
index c063aec..f95062c 100644
--- a/README.md
+++ b/README.md
@@ -3,10 +3,11 @@
 [![Latest release](https://img.shields.io/github/release/google/guava.svg)](https://github.com/google/guava/releases/latest)
 [![Build Status](https://travis-ci.org/google/guava.svg?branch=master)](https://travis-ci.org/google/guava)
 
-Guava is a set of core libraries that includes new collection types (such as
-multimap and multiset), immutable collections, a graph library, functional
-types, an in-memory cache, and APIs/utilities for concurrency, I/O, hashing,
-primitives, reflection, string processing, and much more!
+Guava is a set of core Java libraries from Google that includes new collection types
+(such as multimap and multiset), immutable collections, a graph library, and
+utilities for concurrency, I/O, hashing, caching, primitives, strings, and more! It
+is widely used on most Java projects within Google, and widely used by many
+other companies as well.
 
 Guava comes in two flavors.
 
@@ -18,12 +19,12 @@
 
 ## Adding Guava to your build
 
-Guava's Maven group ID is `com.google.guava` and its artifact ID is `guava`.
+Guava's Maven group ID is `com.google.guava`, and its artifact ID is `guava`.
 Guava provides two different "flavors": one for use on a (Java 8+) JRE and one
 for use on Android or Java 7 or by any library that wants to be compatible with
 either of those. These flavors are specified in the Maven version field as
-either `27.1-jre` or `27.1-android`. For more about depending on
-Guava, see [using Guava in your build].
+either `30.0-jre` or `30.0-android`. For more about depending on Guava, see
+[using Guava in your build].
 
 To add a dependency on Guava using Maven, use the following:
 
@@ -31,9 +32,9 @@
 <dependency>
   <groupId>com.google.guava</groupId>
   <artifactId>guava</artifactId>
-  <version>27.1-jre</version>
+  <version>30.0-jre</version>
   <!-- or, for Android: -->
-  <version>27.1-android</version>
+  <version>30.0-android</version>
 </dependency>
 ```
 
@@ -41,68 +42,82 @@
 
 ```gradle
 dependencies {
-  compile 'com.google.guava:guava:27.1-jre'
-  // or, for Android:
-  api 'com.google.guava:guava:27.1-android'
+  // Pick one:
+
+  // 1. Use Guava in your implementation only:
+  implementation("com.google.guava:guava:30.0-jre")
+
+  // 2. Use Guava types in your public API:
+  api("com.google.guava:guava:30.0-jre")
+
+  // 3. Android - Use Guava in your implementation only:
+  implementation("com.google.guava:guava:30.0-android")
+
+  // 4. Android - Use Guava types in your public API:
+  api("com.google.guava:guava:30.0-android")
 }
 ```
 
-## Snapshots
+For more information on when to use `api` and when to use `implementation`,
+consult the
+[Gradle documentation on API and implementation separation](https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation).
+
+## Snapshots and Documentation
 
 Snapshots of Guava built from the `master` branch are available through Maven
 using version `HEAD-jre-SNAPSHOT`, or `HEAD-android-SNAPSHOT` for the Android
 flavor.
 
-- Snapshot API Docs: [guava][guava-snapshot-api-docs]
-- Snapshot API Diffs: [guava][guava-snapshot-api-diffs]
+-   Snapshot API Docs: [guava][guava-snapshot-api-docs]
+-   Snapshot API Diffs: [guava][guava-snapshot-api-diffs]
 
 ## Learn about Guava
 
-- Our users' guide, [Guava Explained]
-- [A nice collection](http://www.tfnico.com/presentations/google-guava) of other helpful links
+-   Our users' guide, [Guava Explained]
+-   [A nice collection](http://www.tfnico.com/presentations/google-guava) of
+    other helpful links
 
 ## Links
 
-- [GitHub project](https://github.com/google/guava)
-- [Issue tracker: Report a defect or feature request](https://github.com/google/guava/issues/new)
-- [StackOverflow: Ask "how-to" and "why-didn't-it-work" questions](https://stackoverflow.com/questions/ask?tags=guava+java)
-- [guava-discuss: For open-ended questions and discussion](http://groups.google.com/group/guava-discuss)
+-   [GitHub project](https://github.com/google/guava)
+-   [Issue tracker: Report a defect or feature request](https://github.com/google/guava/issues/new)
+-   [StackOverflow: Ask "how-to" and "why-didn't-it-work" questions](https://stackoverflow.com/questions/ask?tags=guava+java)
+-   [guava-announce: Announcements of releases and upcoming significant changes](http://groups.google.com/group/guava-announce)
+-   [guava-discuss: For open-ended questions and discussion](http://groups.google.com/group/guava-discuss)
 
 ## IMPORTANT WARNINGS
 
-1. APIs marked with the `@Beta` annotation at the class or method level
-are subject to change. They can be modified in any way, or even
-removed, at any time. If your code is a library itself (i.e. it is
-used on the CLASSPATH of users outside your own control), you should
-not use beta APIs, unless you [repackage] them. **If your
-code is a library, we strongly recommend using the [Guava Beta Checker] to
-ensure that you do not use any `@Beta` APIs!**
+1.  APIs marked with the `@Beta` annotation at the class or method level are
+    subject to change. They can be modified in any way, or even removed, at any
+    time. If your code is a library itself (i.e., it is used on the CLASSPATH of
+    users outside your own control), you should not use beta APIs unless you
+    [repackage] them. **If your code is a library, we strongly recommend using
+    the [Guava Beta Checker] to ensure that you do not use any `@Beta` APIs!**
 
-2. APIs without `@Beta` will remain binary-compatible for the indefinite
-future. (Previously, we sometimes removed such APIs after a deprecation period.
-The last release to remove non-`@Beta` APIs was Guava 21.0.) Even `@Deprecated`
-APIs will remain (again, unless they are `@Beta`). We have no plans to start
-removing things again, but officially, we're leaving our options open in case
-of surprises (like, say, a serious security problem).
+2.  APIs without `@Beta` will remain binary-compatible for the indefinite
+    future. (Previously, we sometimes removed such APIs after a deprecation
+    period. The last release to remove non-`@Beta` APIs was Guava 21.0.) Even
+    `@Deprecated` APIs will remain (again, unless they are `@Beta`). We have no
+    plans to start removing things again, but officially, we're leaving our
+    options open in case of surprises (like, say, a serious security problem).
 
-3. Guava has one dependency that is needed at runtime:
-`com.google.guava:failureaccess:1.0`
+3.  Guava has one dependency that is needed at runtime:
+    `com.google.guava:failureaccess:1.0.1`
 
-4. Serialized forms of ALL objects are subject to change unless noted
-otherwise. Do not persist these and assume they can be read by a
-future version of the library.
+4.  Serialized forms of ALL objects are subject to change unless noted
+    otherwise. Do not persist these and assume they can be read by a future
+    version of the library.
 
-5. Our classes are not designed to protect against a malicious caller.
-You should not use them for communication between trusted and
-untrusted code.
+5.  Our classes are not designed to protect against a malicious caller. You
+    should not use them for communication between trusted and untrusted code.
 
-6. For the mainline flavor, we unit-test the libraries using only OpenJDK 1.8 on
-Linux. Some features, especially in `com.google.common.io`, may not work
-correctly in other environments. For the Android flavor, our unit tests run on
-API level 15 (Ice Cream Sandwich).
+6.  For the mainline flavor, we unit-test the libraries using only OpenJDK 1.8
+    on Linux. Some features, especially in `com.google.common.io`, may not work
+    correctly in other environments. For the Android flavor, our unit tests run
+    on API level 15 (Ice Cream Sandwich).
 
-[guava-snapshot-api-docs]: https://google.github.io/guava/releases/snapshot-jre/api/docs/
-[guava-snapshot-api-diffs]: https://google.github.io/guava/releases/snapshot-jre/api/diffs/
+[guava-snapshot-api-docs]: https://guava.dev/releases/snapshot-jre/api/docs/
+[guava-snapshot-api-diffs]: https://guava.dev/releases/snapshot-jre/api/diffs/
 [Guava Explained]: https://github.com/google/guava/wiki/Home
 [Guava Beta Checker]: https://github.com/google/guava-beta-checker
 
diff --git a/android/guava-bom/pom.xml b/android/guava-bom/pom.xml
index 7c2bdad..cbb2fda 100644
--- a/android/guava-bom/pom.xml
+++ b/android/guava-bom/pom.xml
@@ -8,7 +8,7 @@
 
   <groupId>com.google.guava</groupId>
   <artifactId>guava-bom</artifactId>
-  <version>27.1-android</version>
+  <version>30.0-android</version>
   <packaging>pom</packaging>
   
   <parent>
@@ -29,7 +29,7 @@
 
   <licenses>
     <license>
-      <name>The Apache Software License, Version 2.0</name>
+      <name>Apache License, Version 2.0</name>
       <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
       <distribution>repo</distribution>
     </license>
diff --git a/android/guava-testlib/pom.xml b/android/guava-testlib/pom.xml
index 2b0ba53..ed18b46 100644
--- a/android/guava-testlib/pom.xml
+++ b/android/guava-testlib/pom.xml
@@ -5,7 +5,7 @@
   <parent>
     <groupId>com.google.guava</groupId>
     <artifactId>guava-parent</artifactId>
-    <version>27.1-android</version>
+    <version>30.0-android</version>
   </parent>
   <artifactId>guava-testlib</artifactId>
   <name>Guava Testing Library</name>
diff --git a/android/guava-testlib/src/com/google/common/collect/testing/CollectionTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/CollectionTestSuiteBuilder.java
index c2d9542..cf1ed23 100644
--- a/android/guava-testlib/src/com/google/common/collect/testing/CollectionTestSuiteBuilder.java
+++ b/android/guava-testlib/src/com/google/common/collect/testing/CollectionTestSuiteBuilder.java
@@ -91,8 +91,7 @@
   }
 
   private static Set<Feature<?>> computeReserializedCollectionFeatures(Set<Feature<?>> features) {
-    Set<Feature<?>> derivedFeatures = new HashSet<>();
-    derivedFeatures.addAll(features);
+    Set<Feature<?>> derivedFeatures = new HashSet<>(features);
     derivedFeatures.remove(SERIALIZABLE);
     derivedFeatures.remove(SERIALIZABLE_INCLUDING_VIEWS);
     return derivedFeatures;
diff --git a/android/guava-testlib/src/com/google/common/collect/testing/DerivedCollectionGenerators.java b/android/guava-testlib/src/com/google/common/collect/testing/DerivedCollectionGenerators.java
index af50129..3588e85 100644
--- a/android/guava-testlib/src/com/google/common/collect/testing/DerivedCollectionGenerators.java
+++ b/android/guava-testlib/src/com/google/common/collect/testing/DerivedCollectionGenerators.java
@@ -375,8 +375,7 @@
 
     @Override
     public SortedSet<E> create(Object... elements) {
-      @SuppressWarnings("unchecked") // set generators must pass SampleElements values
-      List<E> normalValues = (List) Arrays.asList(elements);
+      List<?> normalValues = (List<?>) Arrays.asList(elements);
       List<E> extremeValues = new ArrayList<>();
 
       // nulls are usually out of bounds for a subset, so ban them altogether
@@ -399,12 +398,12 @@
       }
 
       // the regular values should be visible after filtering
-      List<E> allEntries = new ArrayList<>();
+      List<Object> allEntries = new ArrayList<>();
       allEntries.addAll(extremeValues);
       allEntries.addAll(normalValues);
-      SortedSet<E> map = delegate.create(allEntries.toArray());
+      SortedSet<E> set = delegate.create(allEntries.toArray());
 
-      return createSubSet(map, firstExclusive, lastExclusive);
+      return createSubSet(set, firstExclusive, lastExclusive);
     }
 
     /** Calls the smallest subSet overload that filters out the extreme values. */
@@ -474,8 +473,6 @@
 
     @Override
     public SortedMap<K, V> create(Object... entries) {
-      @SuppressWarnings("unchecked") // map generators must past entry objects
-      List<Entry<K, V>> normalValues = (List) Arrays.asList(entries);
       List<Entry<K, V>> extremeValues = new ArrayList<>();
 
       // prepare extreme values to be filtered out of view
@@ -491,12 +488,12 @@
       }
 
       // the regular values should be visible after filtering
-      List<Entry<K, V>> allEntries = new ArrayList<>();
+      List<Entry<?, ?>> allEntries = new ArrayList<>();
       allEntries.addAll(extremeValues);
-      allEntries.addAll(normalValues);
-      SortedMap<K, V> map =
-          (SortedMap<K, V>)
-              delegate.create((Object[]) allEntries.toArray(new Entry<?, ?>[allEntries.size()]));
+      for (Object entry : entries) {
+        allEntries.add((Entry<?, ?>) entry);
+      }
+      SortedMap<K, V> map = (SortedMap<K, V>) delegate.create(allEntries.toArray());
 
       return createSubMap(map, firstExclusive, lastExclusive);
     }
diff --git a/android/guava-testlib/src/com/google/common/collect/testing/Helpers.java b/android/guava-testlib/src/com/google/common/collect/testing/Helpers.java
index 5023d39..8efafc3 100644
--- a/android/guava-testlib/src/com/google/common/collect/testing/Helpers.java
+++ b/android/guava-testlib/src/com/google/common/collect/testing/Helpers.java
@@ -169,8 +169,7 @@
   }
 
   public static void assertContainsAllOf(Iterable<?> actual, Object... expected) {
-    List<Object> expectedList = new ArrayList<>();
-    expectedList.addAll(Arrays.asList(expected));
+    List<Object> expectedList = new ArrayList<>(Arrays.asList(expected));
 
     for (Object o : actual) {
       expectedList.remove(o);
diff --git a/android/guava-testlib/src/com/google/common/collect/testing/IteratorTester.java b/android/guava-testlib/src/com/google/common/collect/testing/IteratorTester.java
index fd1643b..fdc418a 100644
--- a/android/guava-testlib/src/com/google/common/collect/testing/IteratorTester.java
+++ b/android/guava-testlib/src/com/google/common/collect/testing/IteratorTester.java
@@ -50,6 +50,36 @@
  * verify() method, which is called <em>after</em> each sequence and is guaranteed to be called
  * using the latest values obtained from {@link IteratorTester#newTargetIterator()}.
  *
+ * <p>The value you pass to the parameter {@code steps} should be greater than the length of your
+ * iterator, so that this class can check that your iterator behaves correctly when it is exhausted.
+ *
+ * <p>For example, to test {@link java.util.Collections#unmodifiableList(java.util.List)
+ * Collections.unmodifiableList}'s iterator:
+ *
+ * <pre>{@code
+ * List<String> expectedElements =
+ *     Arrays.asList("a", "b", "c", "d", "e");
+ * List<String> actualElements =
+ *     Collections.unmodifiableList(
+ *         Arrays.asList("a", "b", "c", "d", "e"));
+ * IteratorTester<String> iteratorTester =
+ *     new IteratorTester<String>(
+ *         6,
+ *         IteratorFeature.UNMODIFIABLE,
+ *         expectedElements,
+ *         IteratorTester.KnownOrder.KNOWN_ORDER) {
+ *       @Override
+ *       protected Iterator<String> newTargetIterator() {
+ *         return actualElements.iterator();
+ *       }
+ *     };
+ * iteratorTester.test();
+ * iteratorTester.testForEachRemaining();
+ * }</pre>
+ *
+ * <p><b>Note</b>: It is necessary to use {@code IteratorTester.KnownOrder} as shown above, rather
+ * than {@code KnownOrder} directly, because otherwise the code cannot be compiled.
+ *
  * @author Kevin Bourrillion
  * @author Chris Povirk
  */
diff --git a/android/guava-testlib/src/com/google/common/collect/testing/ListTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/ListTestSuiteBuilder.java
index 144428e..e010564 100644
--- a/android/guava-testlib/src/com/google/common/collect/testing/ListTestSuiteBuilder.java
+++ b/android/guava-testlib/src/com/google/common/collect/testing/ListTestSuiteBuilder.java
@@ -146,8 +146,7 @@
   }
 
   private static Set<Feature<?>> computeReserializedCollectionFeatures(Set<Feature<?>> features) {
-    Set<Feature<?>> derivedFeatures = new HashSet<>();
-    derivedFeatures.addAll(features);
+    Set<Feature<?>> derivedFeatures = new HashSet<>(features);
     derivedFeatures.remove(SERIALIZABLE);
     derivedFeatures.remove(SERIALIZABLE_INCLUDING_VIEWS);
     return derivedFeatures;
diff --git a/android/guava-testlib/src/com/google/common/collect/testing/MapInterfaceTest.java b/android/guava-testlib/src/com/google/common/collect/testing/MapInterfaceTest.java
index c8053b1..b8b5c28 100644
--- a/android/guava-testlib/src/com/google/common/collect/testing/MapInterfaceTest.java
+++ b/android/guava-testlib/src/com/google/common/collect/testing/MapInterfaceTest.java
@@ -301,10 +301,10 @@
     }
     assertTrue(map.containsKey(map.keySet().iterator().next()));
     if (allowsNullKeys) {
-      map.containsKey(null);
+      boolean unused = map.containsKey(null);
     } else {
       try {
-        map.containsKey(null);
+        boolean unused2 = map.containsKey(null);
       } catch (NullPointerException optional) {
       }
     }
@@ -323,10 +323,10 @@
     assertFalse(map.containsValue(unmappedValue));
     assertTrue(map.containsValue(map.values().iterator().next()));
     if (allowsNullValues) {
-      map.containsValue(null);
+      boolean unused = map.containsValue(null);
     } else {
       try {
-        map.containsKey(null);
+        boolean unused2 = map.containsKey(null);
       } catch (NullPointerException optional) {
       }
     }
diff --git a/android/guava-testlib/src/com/google/common/collect/testing/SetTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/SetTestSuiteBuilder.java
index 042ba9d..26a2870 100644
--- a/android/guava-testlib/src/com/google/common/collect/testing/SetTestSuiteBuilder.java
+++ b/android/guava-testlib/src/com/google/common/collect/testing/SetTestSuiteBuilder.java
@@ -112,8 +112,7 @@
   }
 
   private static Set<Feature<?>> computeReserializedCollectionFeatures(Set<Feature<?>> features) {
-    Set<Feature<?>> derivedFeatures = new HashSet<>();
-    derivedFeatures.addAll(features);
+    Set<Feature<?>> derivedFeatures = new HashSet<>(features);
     derivedFeatures.remove(SERIALIZABLE);
     derivedFeatures.remove(SERIALIZABLE_INCLUDING_VIEWS);
     return derivedFeatures;
diff --git a/android/guava-testlib/src/com/google/common/collect/testing/SortedSetTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/SortedSetTestSuiteBuilder.java
index ce01adb..e97b08f 100644
--- a/android/guava-testlib/src/com/google/common/collect/testing/SortedSetTestSuiteBuilder.java
+++ b/android/guava-testlib/src/com/google/common/collect/testing/SortedSetTestSuiteBuilder.java
@@ -86,8 +86,7 @@
     final TestSortedSetGenerator<E> delegate =
         (TestSortedSetGenerator<E>) parentBuilder.getSubjectGenerator().getInnerGenerator();
 
-    List<Feature<?>> features = new ArrayList<>();
-    features.addAll(parentBuilder.getFeatures());
+    List<Feature<?>> features = new ArrayList<>(parentBuilder.getFeatures());
     features.remove(CollectionFeature.ALLOWS_NULL_VALUES);
     features.add(CollectionFeature.SUBSET_VIEW);
 
diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetTestSuiteBuilder.java
index e60ccf8..266daa4 100644
--- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetTestSuiteBuilder.java
+++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetTestSuiteBuilder.java
@@ -88,8 +88,7 @@
   }
 
   private static Set<Feature<?>> computeEntrySetFeatures(Set<Feature<?>> features) {
-    Set<Feature<?>> derivedFeatures = new HashSet<>();
-    derivedFeatures.addAll(features);
+    Set<Feature<?>> derivedFeatures = new HashSet<>(features);
     derivedFeatures.remove(CollectionFeature.GENERAL_PURPOSE);
     derivedFeatures.remove(CollectionFeature.SUPPORTS_ADD);
     derivedFeatures.remove(CollectionFeature.ALLOWS_NULL_VALUES);
@@ -101,8 +100,7 @@
   }
 
   static Set<Feature<?>> computeElementSetFeatures(Set<Feature<?>> features) {
-    Set<Feature<?>> derivedFeatures = new HashSet<>();
-    derivedFeatures.addAll(features);
+    Set<Feature<?>> derivedFeatures = new HashSet<>(features);
     derivedFeatures.remove(CollectionFeature.GENERAL_PURPOSE);
     derivedFeatures.remove(CollectionFeature.SUPPORTS_ADD);
     if (!derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS)) {
@@ -112,8 +110,7 @@
   }
 
   private static Set<Feature<?>> computeReserializedMultisetFeatures(Set<Feature<?>> features) {
-    Set<Feature<?>> derivedFeatures = new HashSet<>();
-    derivedFeatures.addAll(features);
+    Set<Feature<?>> derivedFeatures = new HashSet<>(features);
     derivedFeatures.remove(CollectionFeature.SERIALIZABLE);
     derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS);
     return derivedFeatures;
diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/SortedMultisetTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/google/SortedMultisetTestSuiteBuilder.java
index f763dcc..dafd521 100644
--- a/android/guava-testlib/src/com/google/common/collect/testing/google/SortedMultisetTestSuiteBuilder.java
+++ b/android/guava-testlib/src/com/google/common/collect/testing/google/SortedMultisetTestSuiteBuilder.java
@@ -168,10 +168,10 @@
               public SortedMultiset<E> create(Object... entries) {
                 @SuppressWarnings("unchecked")
                 // we dangerously assume E is a string
-                List<E> extremeValues = (List) getExtremeValues();
+                List<E> extremeValues = (List<E>) getExtremeValues();
                 @SuppressWarnings("unchecked")
                 // map generators must past entry objects
-                List<E> normalValues = (List) Arrays.asList(entries);
+                List<E> normalValues = (List<E>) Arrays.asList(entries);
 
                 // prepare extreme values to be filtered out of view
                 Collections.sort(extremeValues, comparator);
@@ -266,8 +266,7 @@
     final TestMultisetGenerator<E> delegate =
         (TestMultisetGenerator<E>) parentBuilder.getSubjectGenerator();
 
-    Set<Feature<?>> features = new HashSet<>();
-    features.addAll(parentBuilder.getFeatures());
+    Set<Feature<?>> features = new HashSet<>(parentBuilder.getFeatures());
     features.remove(SERIALIZABLE);
     features.remove(SERIALIZABLE_INCLUDING_VIEWS);
 
diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionIteratorTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionIteratorTester.java
index 89e0662..b170497 100644
--- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionIteratorTester.java
+++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionIteratorTester.java
@@ -19,9 +19,11 @@
 import static com.google.common.collect.testing.Helpers.mapEntry;
 import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE;
 import static com.google.common.collect.testing.IteratorFeature.UNMODIFIABLE;
+import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES;
 import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER;
 import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ITERATOR_REMOVE;
 import static com.google.common.collect.testing.features.CollectionSize.ZERO;
+import static java.util.Arrays.asList;
 
 import com.google.common.annotations.GwtCompatible;
 import com.google.common.collect.testing.AbstractCollectionTester;
@@ -66,6 +68,17 @@
     assertEquals("Different ordered iteration", expected, iteratorElements);
   }
 
+  @CollectionFeature.Require(ALLOWS_NULL_VALUES)
+  @CollectionSize.Require(absent = ZERO)
+  public void testIterator_nullElement() {
+    initCollectionWithNullElement();
+    List<E> iteratorElements = new ArrayList<E>();
+    for (E element : collection) { // uses iterator()
+      iteratorElements.add(element);
+    }
+    Helpers.assertEqualIgnoringOrder(asList(createArrayWithNullElement()), iteratorElements);
+  }
+
   @CollectionFeature.Require(SUPPORTS_ITERATOR_REMOVE)
   @CollectionSize.Require(absent = ZERO)
   public void testIterator_removeAffectsBackingCollection() {
diff --git a/android/guava-testlib/src/com/google/common/testing/ArbitraryInstances.java b/android/guava-testlib/src/com/google/common/testing/ArbitraryInstances.java
index 906c1c5..01904d6 100644
--- a/android/guava-testlib/src/com/google/common/testing/ArbitraryInstances.java
+++ b/android/guava-testlib/src/com/google/common/testing/ArbitraryInstances.java
@@ -123,6 +123,7 @@
 import java.util.Set;
 import java.util.SortedMap;
 import java.util.SortedSet;
+import java.util.UUID;
 import java.util.concurrent.BlockingDeque;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ConcurrentHashMap;
@@ -205,6 +206,7 @@
           .put(Charset.class, Charsets.UTF_8)
           .put(Currency.class, Currency.getInstance(Locale.US))
           .put(Locale.class, Locale.US)
+          .put(UUID.class, UUID.randomUUID())
           // common.base
           .put(CharMatcher.class, CharMatcher.none())
           .put(Joiner.class, Joiner.on(','))
@@ -369,7 +371,14 @@
     constructor.setAccessible(true); // accessibility check is too slow
     try {
       return constructor.newInstance();
-    } catch (InstantiationException | IllegalAccessException impossible) {
+      /*
+       * Do not merge the 2 catch blocks below. javac would infer a type of
+       * ReflectiveOperationException, which Animal Sniffer would reject. (Old versions of
+       * Android don't *seem* to mind, but there might be edge cases of which we're unaware.)
+       */
+    } catch (InstantiationException impossible) {
+      throw new AssertionError(impossible);
+    } catch (IllegalAccessException impossible) {
       throw new AssertionError(impossible);
     } catch (InvocationTargetException e) {
       logger.log(Level.WARNING, "Exception while invoking default constructor.", e.getCause());
diff --git a/android/guava-testlib/src/com/google/common/testing/ClusterException.java b/android/guava-testlib/src/com/google/common/testing/ClusterException.java
index 5f50ff8..7665ab1 100644
--- a/android/guava-testlib/src/com/google/common/testing/ClusterException.java
+++ b/android/guava-testlib/src/com/google/common/testing/ClusterException.java
@@ -67,8 +67,7 @@
     super(
         exceptions.size() + " exceptions were thrown. The first exception is listed as a cause.",
         exceptions.iterator().next());
-    ArrayList<Throwable> temp = new ArrayList<>();
-    temp.addAll(exceptions);
+    ArrayList<Throwable> temp = new ArrayList<>(exceptions);
     this.exceptions = Collections.unmodifiableCollection(temp);
   }
 
diff --git a/android/guava-testlib/src/com/google/common/testing/EqualsTester.java b/android/guava-testlib/src/com/google/common/testing/EqualsTester.java
index dc2d197..2c8e08b 100644
--- a/android/guava-testlib/src/com/google/common/testing/EqualsTester.java
+++ b/android/guava-testlib/src/com/google/common/testing/EqualsTester.java
@@ -127,6 +127,11 @@
           "the Object#hashCode of " + item + " must be consistent",
           item.hashCode(),
           item.hashCode());
+      if (!(item instanceof String)) {
+        assertTrue(
+            item + " must not be Object#equals to its Object#toString representation",
+            !item.equals(item.toString()));
+      }
     }
   }
 
diff --git a/android/guava-testlib/src/com/google/common/testing/FreshValueGenerator.java b/android/guava-testlib/src/com/google/common/testing/FreshValueGenerator.java
index d6b329d..21e25cb 100644
--- a/android/guava-testlib/src/com/google/common/testing/FreshValueGenerator.java
+++ b/android/guava-testlib/src/com/google/common/testing/FreshValueGenerator.java
@@ -508,7 +508,14 @@
       @SuppressWarnings("unchecked") // getAvailableCurrencies() returns Set<Currency>.
       Set<Currency> currencies = (Set<Currency>) method.invoke(null);
       return pickInstance(currencies, Currency.getInstance(Locale.US));
-    } catch (NoSuchMethodException | InvocationTargetException notJava7) {
+      /*
+       * Do not merge the 2 catch blocks below. javac would infer a type of
+       * ReflectiveOperationException, which Animal Sniffer would reject. (Old versions of
+       * Android don't *seem* to mind, but there might be edge cases of which we're unaware.)
+       */
+    } catch (NoSuchMethodException notJava7) {
+      return preJava7FreshCurrency();
+    } catch (InvocationTargetException notJava7) {
       return preJava7FreshCurrency();
     } catch (IllegalAccessException impossible) {
       throw new AssertionError(impossible);
diff --git a/android/guava-testlib/src/com/google/common/testing/GcFinalization.java b/android/guava-testlib/src/com/google/common/testing/GcFinalization.java
index 92394f1..015afea 100644
--- a/android/guava-testlib/src/com/google/common/testing/GcFinalization.java
+++ b/android/guava-testlib/src/com/google/common/testing/GcFinalization.java
@@ -20,6 +20,7 @@
 
 import com.google.common.annotations.Beta;
 import com.google.common.annotations.GwtIncompatible;
+import com.google.errorprone.annotations.DoNotMock;
 import com.google.j2objc.annotations.J2ObjCIncompatible;
 import java.lang.ref.WeakReference;
 import java.util.Locale;
@@ -241,6 +242,7 @@
    *   <li>enqueuing weak references to unreachable referents in their reference queue
    * </ul>
    */
+  @DoNotMock("Implement with a lambda")
   public interface FinalizationPredicate {
     boolean isDone();
   }
diff --git a/android/guava-testlib/src/com/google/common/testing/TearDownAccepter.java b/android/guava-testlib/src/com/google/common/testing/TearDownAccepter.java
index a1d1f00..bad1f19 100644
--- a/android/guava-testlib/src/com/google/common/testing/TearDownAccepter.java
+++ b/android/guava-testlib/src/com/google/common/testing/TearDownAccepter.java
@@ -17,6 +17,7 @@
 package com.google.common.testing;
 
 import com.google.common.annotations.Beta;
+import com.google.errorprone.annotations.DoNotMock;
 import com.google.common.annotations.GwtCompatible;
 
 /**
@@ -26,6 +27,7 @@
  * @since 10.0
  */
 @Beta
+@DoNotMock("Implement with a lambda")
 @GwtCompatible
 public interface TearDownAccepter {
   /**
diff --git a/android/guava-testlib/src/com/google/common/util/concurrent/testing/AbstractCheckedFutureTest.java b/android/guava-testlib/src/com/google/common/util/concurrent/testing/AbstractCheckedFutureTest.java
deleted file mode 100755
index 5c2ce32..0000000
--- a/android/guava-testlib/src/com/google/common/util/concurrent/testing/AbstractCheckedFutureTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2007 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 com.google.common.util.concurrent.testing;
-
-import com.google.common.annotations.Beta;
-import com.google.common.annotations.GwtIncompatible;
-import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.ListenableFuture;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test case to make sure the {@link CheckedFuture#checkedGet()} and {@link
- * CheckedFuture#checkedGet(long, TimeUnit)} methods work correctly.
- *
- * @author Sven Mawson
- * @since 10.0
- */
-@Beta
-@GwtIncompatible
-public abstract class AbstractCheckedFutureTest extends AbstractListenableFutureTest {
-
-  /** More specific type for the create method. */
-  protected abstract <V> CheckedFuture<V, ?> createCheckedFuture(
-      V value, Exception except, CountDownLatch waitOn);
-
-  /** Checks that the exception is the correct type of cancellation exception. */
-  protected abstract void checkCancelledException(Exception e);
-
-  /** Checks that the exception is the correct type of execution exception. */
-  protected abstract void checkExecutionException(Exception e);
-
-  /** Checks that the exception is the correct type of interruption exception. */
-  protected abstract void checkInterruptedException(Exception e);
-
-  @Override
-  protected <V> ListenableFuture<V> createListenableFuture(
-      V value, Exception except, CountDownLatch waitOn) {
-    return createCheckedFuture(value, except, waitOn);
-  }
-
-  /**
-   * Tests that the {@link CheckedFuture#checkedGet()} method throws the correct type of
-   * cancellation exception when it is cancelled.
-   */
-  public void testCheckedGetThrowsApplicationExceptionOnCancellation() {
-
-    final CheckedFuture<Boolean, ?> future = createCheckedFuture(Boolean.TRUE, null, latch);
-
-    assertFalse(future.isDone());
-    assertFalse(future.isCancelled());
-
-    new Thread(
-            new Runnable() {
-              @Override
-              public void run() {
-                future.cancel(true);
-              }
-            })
-        .start();
-
-    try {
-      future.checkedGet();
-      fail("RPC Should have been cancelled.");
-    } catch (Exception e) {
-      checkCancelledException(e);
-    }
-
-    assertTrue(future.isDone());
-    assertTrue(future.isCancelled());
-  }
-
-  public void testCheckedGetThrowsApplicationExceptionOnInterruption() throws InterruptedException {
-
-    final CheckedFuture<Boolean, ?> future = createCheckedFuture(Boolean.TRUE, null, latch);
-
-    final CountDownLatch startingGate = new CountDownLatch(1);
-    final CountDownLatch successLatch = new CountDownLatch(1);
-
-    assertFalse(future.isDone());
-    assertFalse(future.isCancelled());
-
-    Thread getThread =
-        new Thread(
-            new Runnable() {
-              @Override
-              public void run() {
-                startingGate.countDown();
-
-                try {
-                  future.checkedGet();
-                } catch (Exception e) {
-                  checkInterruptedException(e);
-
-                  // This only gets hit if the original call throws an exception and
-                  // the check call above passes.
-                  successLatch.countDown();
-                }
-              }
-            });
-    getThread.start();
-
-    assertTrue(startingGate.await(500, TimeUnit.MILLISECONDS));
-    getThread.interrupt();
-
-    assertTrue(successLatch.await(500, TimeUnit.MILLISECONDS));
-
-    assertFalse(future.isDone());
-    assertFalse(future.isCancelled());
-  }
-
-  public void testCheckedGetThrowsApplicationExceptionOnError() {
-    final CheckedFuture<Boolean, ?> future =
-        createCheckedFuture(Boolean.TRUE, new Exception("Error"), latch);
-
-    assertFalse(future.isDone());
-    assertFalse(future.isCancelled());
-
-    new Thread(
-            new Runnable() {
-              @Override
-              public void run() {
-                latch.countDown();
-              }
-            })
-        .start();
-
-    try {
-      future.checkedGet();
-      fail();
-    } catch (Exception e) {
-      checkExecutionException(e);
-    }
-
-    assertTrue(future.isDone());
-    assertFalse(future.isCancelled());
-  }
-}
diff --git a/android/guava-testlib/test/com/google/common/testing/AbstractPackageSanityTestsTest.java b/android/guava-testlib/test/com/google/common/testing/AbstractPackageSanityTestsTest.java
index 666d726..cb6ba6a 100644
--- a/android/guava-testlib/test/com/google/common/testing/AbstractPackageSanityTestsTest.java
+++ b/android/guava-testlib/test/com/google/common/testing/AbstractPackageSanityTestsTest.java
@@ -72,7 +72,7 @@
     assertThat(findClassesToTest(ImmutableList.of(Foo.class))).contains(Foo.class);
   }
 
-  public void testFindClassesToTeset_ignoreUnderscores() {
+  public void testFindClassesToTest_ignoreUnderscores() {
     assertThat(findClassesToTest(ImmutableList.of(Foo.class, Foo_Bar.class)))
         .containsExactly(Foo.class, Foo_Bar.class);
     sanityTests.ignoreClasses(AbstractPackageSanityTests.UNDERSCORE_IN_NAME);
diff --git a/android/guava-testlib/test/com/google/common/testing/ArbitraryInstancesTest.java b/android/guava-testlib/test/com/google/common/testing/ArbitraryInstancesTest.java
index 3664b23..b47672f 100644
--- a/android/guava-testlib/test/com/google/common/testing/ArbitraryInstancesTest.java
+++ b/android/guava-testlib/test/com/google/common/testing/ArbitraryInstancesTest.java
@@ -109,6 +109,7 @@
 import java.util.SortedSet;
 import java.util.TreeMap;
 import java.util.TreeSet;
+import java.util.UUID;
 import java.util.concurrent.BlockingDeque;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ConcurrentMap;
@@ -163,6 +164,7 @@
     assertNotNull(ArbitraryInstances.get(Object.class));
     assertEquals(0, ArbitraryInstances.get(Number.class));
     assertEquals(Charsets.UTF_8, ArbitraryInstances.get(Charset.class));
+    assertNotNull(ArbitraryInstances.get(UUID.class));
   }
 
   public void testGet_collections() {
diff --git a/android/guava-testlib/test/com/google/common/testing/EqualsTesterTest.java b/android/guava-testlib/test/com/google/common/testing/EqualsTesterTest.java
index b992f1b..d615af6 100644
--- a/android/guava-testlib/test/com/google/common/testing/EqualsTesterTest.java
+++ b/android/guava-testlib/test/com/google/common/testing/EqualsTesterTest.java
@@ -272,6 +272,15 @@
         .testEquals();
   }
 
+  public void testEqualityBasedOnToString() {
+    try {
+      new EqualsTester().addEqualityGroup(new EqualsBasedOnToString("foo")).testEquals();
+      fail();
+    } catch (AssertionFailedError e) {
+      assertTrue(e.getMessage().contains("toString representation"));
+    }
+  }
+
   private static void assertErrorMessage(Throwable e, String message) {
     // TODO(kevinb): use a Truth assertion here
     if (!e.getMessage().contains(message)) {
@@ -422,4 +431,27 @@
       return name;
     }
   }
+
+  private static final class EqualsBasedOnToString {
+    private final String s;
+
+    private EqualsBasedOnToString(String s) {
+      this.s = s;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      return obj != null && obj.toString().equals(toString());
+    }
+
+    @Override
+    public int hashCode() {
+      return s.hashCode();
+    }
+
+    @Override
+    public String toString() {
+      return s;
+    }
+  }
 }
diff --git a/android/guava-testlib/test/com/google/common/testing/NullPointerTesterTest.java b/android/guava-testlib/test/com/google/common/testing/NullPointerTesterTest.java
index 28cdca2..99f01d6 100644
--- a/android/guava-testlib/test/com/google/common/testing/NullPointerTesterTest.java
+++ b/android/guava-testlib/test/com/google/common/testing/NullPointerTesterTest.java
@@ -339,21 +339,25 @@
     }
 
     /** Two-arg method with no Nullable params. */
+    @SuppressWarnings("GoodTime") // false positive; b/122617528
     public void normalNormal(String first, Integer second) {
       reactToNullParameters(first, second);
     }
 
     /** Two-arg method with the second param Nullable. */
+    @SuppressWarnings("GoodTime") // false positive; b/122617528
     public void normalNullable(String first, @NullableDecl Integer second) {
       reactToNullParameters(first, second);
     }
 
     /** Two-arg method with the first param Nullable. */
+    @SuppressWarnings("GoodTime") // false positive; b/122617528
     public void nullableNormal(@NullableDecl String first, Integer second) {
       reactToNullParameters(first, second);
     }
 
     /** Two-arg method with the both params Nullable. */
+    @SuppressWarnings("GoodTime") // false positive; b/122617528
     public void nullableNullable(@NullableDecl String first, @NullableDecl Integer second) {
       reactToNullParameters(first, second);
     }
diff --git a/android/guava-tests/benchmark/com/google/common/cache/ChainBenchmark.java b/android/guava-tests/benchmark/com/google/common/cache/ChainBenchmark.java
index 24d75b9..43fc75c 100644
--- a/android/guava-tests/benchmark/com/google/common/cache/ChainBenchmark.java
+++ b/android/guava-tests/benchmark/com/google/common/cache/ChainBenchmark.java
@@ -35,6 +35,7 @@
   private ReferenceEntry<Object, Object> head;
   private ReferenceEntry<Object, Object> chain;
 
+  @SuppressWarnings("GuardedBy")
   @BeforeExperiment
   void setUp() {
     LocalCache<Object, Object> cache =
@@ -43,6 +44,8 @@
     chain = null;
     for (int i = 0; i < length; i++) {
       Object key = new Object();
+      // TODO(b/145386688): This access should be guarded by 'this.segment', which is not currently
+      // held
       chain = segment.newEntry(key, cache.hash(key), chain);
       if (i == 0) {
         head = chain;
@@ -50,10 +53,13 @@
     }
   }
 
+  @SuppressWarnings("GuardedBy")
   @Benchmark
   int time(int reps) {
     int dummy = 0;
     for (int i = 0; i < reps; i++) {
+      // TODO(b/145386688): This access should be guarded by 'this.segment', which is not currently
+      // held
       segment.removeEntryFromChain(chain, head);
       dummy += segment.count;
     }
diff --git a/android/guava-tests/benchmark/com/google/common/cache/SegmentBenchmark.java b/android/guava-tests/benchmark/com/google/common/cache/SegmentBenchmark.java
index 5b82f83..a473d75 100644
--- a/android/guava-tests/benchmark/com/google/common/cache/SegmentBenchmark.java
+++ b/android/guava-tests/benchmark/com/google/common/cache/SegmentBenchmark.java
@@ -50,11 +50,14 @@
     checkState(segment.table.length() == capacity);
   }
 
+  @SuppressWarnings("GuardedBy")
   @Benchmark
   int time(int reps) {
     int dummy = 0;
     AtomicReferenceArray<ReferenceEntry<Object, Object>> oldTable = segment.table;
     for (int i = 0; i < reps; i++) {
+      // TODO(b/145386688): This access should be guarded by 'this.segment', which is not currently
+      // held
       segment.expand();
       segment.table = oldTable;
       dummy += segment.count;
diff --git a/android/guava-tests/benchmark/com/google/common/collect/MapBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/MapBenchmark.java
index fb8a26e..f10b94e 100644
--- a/android/guava-tests/benchmark/com/google/common/collect/MapBenchmark.java
+++ b/android/guava-tests/benchmark/com/google/common/collect/MapBenchmark.java
@@ -26,7 +26,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentSkipListMap;
 
@@ -224,12 +223,24 @@
   }
 
   @Benchmark
+  boolean createPopulateAndRemove(int reps) {
+    boolean dummy = false;
+    for (int i = 1; i < reps; i++) {
+      Map<Element, Element> map = impl.create(values);
+      for (Element value : values) {
+        dummy |= map.remove(value) == null;
+      }
+    }
+    return dummy;
+  }
+
+  @Benchmark
   boolean iterateWithEntrySet(int reps) {
     Map<Element, Element> map = mapToTest;
 
     boolean dummy = false;
     for (int i = 0; i < reps; i++) {
-      for (Entry<Element, Element> entry : map.entrySet()) {
+      for (Map.Entry<Element, Element> entry : map.entrySet()) {
         dummy ^= entry.getKey() != entry.getValue();
       }
     }
diff --git a/android/guava-tests/benchmark/com/google/common/collect/MapsMemoryBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/MapsMemoryBenchmark.java
index f13d9b7..b834846 100644
--- a/android/guava-tests/benchmark/com/google/common/collect/MapsMemoryBenchmark.java
+++ b/android/guava-tests/benchmark/com/google/common/collect/MapsMemoryBenchmark.java
@@ -45,6 +45,8 @@
     "HashMapImpl",
     "LinkedHashMapImpl",
     "ConcurrentHashMapImpl",
+    "CompactHashMapImpl",
+    "CompactLinkedHashMapImpl",
     "ImmutableMapImpl",
     "TreeMapImpl",
     "ImmutableSortedMapImpl",
diff --git a/android/guava-tests/benchmark/com/google/common/math/BigIntegerMathRoundingBenchmark.java b/android/guava-tests/benchmark/com/google/common/math/BigIntegerMathRoundingBenchmark.java
index e8616ee..be387f7 100644
--- a/android/guava-tests/benchmark/com/google/common/math/BigIntegerMathRoundingBenchmark.java
+++ b/android/guava-tests/benchmark/com/google/common/math/BigIntegerMathRoundingBenchmark.java
@@ -88,4 +88,14 @@
     }
     return tmp;
   }
+
+  @Benchmark
+  long roundToDouble(int reps) {
+    long tmp = 0;
+    for (int i = 0; i < reps; i++) {
+      int j = i & ARRAY_MASK;
+      tmp += Double.doubleToRawLongBits(BigIntegerMath.roundToDouble(nonzero1[j], mode));
+    }
+    return tmp;
+  }
 }
diff --git a/android/guava-tests/pom.xml b/android/guava-tests/pom.xml
index 223fc49..5191c8e 100644
--- a/android/guava-tests/pom.xml
+++ b/android/guava-tests/pom.xml
@@ -5,7 +5,7 @@
   <parent>
     <groupId>com.google.guava</groupId>
     <artifactId>guava-parent</artifactId>
-    <version>27.1-android</version>
+    <version>30.0-android</version>
   </parent>
   <artifactId>guava-tests</artifactId>
   <name>Guava Unit Tests</name>
diff --git a/android/guava-tests/test/com/google/common/base/CharMatcherTest.java b/android/guava-tests/test/com/google/common/base/CharMatcherTest.java
index db56260..5412882 100644
--- a/android/guava-tests/test/com/google/common/base/CharMatcherTest.java
+++ b/android/guava-tests/test/com/google/common/base/CharMatcherTest.java
@@ -727,12 +727,9 @@
     Set<Character> chars = new HashSet<>(size);
     for (int i = 0; i < size; i++) {
       char c;
-      while (true) {
+      do {
         c = (char) rand.nextInt(Character.MAX_VALUE - Character.MIN_VALUE + 1);
-        if (!chars.contains(c)) {
-          break;
-        }
-      }
+      } while (chars.contains(c));
       chars.add(c);
     }
     char[] retValue = new char[chars.size()];
diff --git a/android/guava-tests/test/com/google/common/base/OptionalTest.java b/android/guava-tests/test/com/google/common/base/OptionalTest.java
index 5bfe3be..35de2d4 100644
--- a/android/guava-tests/test/com/google/common/base/OptionalTest.java
+++ b/android/guava-tests/test/com/google/common/base/OptionalTest.java
@@ -246,7 +246,7 @@
     List<Optional<? extends Number>> optionals =
         ImmutableList.<Optional<? extends Number>>of(Optional.<Double>absent(), Optional.of(2));
     Iterable<Number> onlyPresent = Optional.presentInstances(optionals);
-    assertThat(onlyPresent).containsExactly(2).inOrder();
+    assertThat(onlyPresent).containsExactly(2);
   }
 
   private static Optional<Integer> getSomeOptionalInt() {
@@ -288,7 +288,7 @@
     // Sadly, the following is what users will have to do in some circumstances.
 
     @SuppressWarnings("unchecked") // safe covariant cast
-    Optional<Number> first = (Optional) numbers.first();
+    Optional<Number> first = (Optional<Number>) numbers.first();
     Number value = first.or(0.5); // fine
   }
 
diff --git a/android/guava-tests/test/com/google/common/base/SplitterTest.java b/android/guava-tests/test/com/google/common/base/SplitterTest.java
index 6b3d861..46928aa 100644
--- a/android/guava-tests/test/com/google/common/base/SplitterTest.java
+++ b/android/guava-tests/test/com/google/common/base/SplitterTest.java
@@ -758,6 +758,18 @@
     }
   }
 
+  /**
+   * Testing the behavior in https://github.com/google/guava/issues/1900 - this behavior may want to
+   * be changed?
+   */
+  public void testMapSplitter_extraValueDelimiter() {
+    try {
+      COMMA_SPLITTER.withKeyValueSeparator("=").split("a=1,c=2=");
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
   public void testMapSplitter_orderedResults() {
     Map<String, String> m =
         COMMA_SPLITTER.withKeyValueSeparator(":").split("boy:tom,girl:tina,cat:kitty,dog:tommy");
diff --git a/android/guava-tests/test/com/google/common/base/ThrowablesTest.java b/android/guava-tests/test/com/google/common/base/ThrowablesTest.java
index 076f899..e4c64aa 100644
--- a/android/guava-tests/test/com/google/common/base/ThrowablesTest.java
+++ b/android/guava-tests/test/com/google/common/base/ThrowablesTest.java
@@ -575,7 +575,7 @@
       Throwables.getRootCause(cause);
       fail("Should have throw IAE");
     } catch (IllegalArgumentException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(cause);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(cause);
     }
   }
 
@@ -675,7 +675,7 @@
       Throwables.getCausalChain(cause);
       fail("Should have throw IAE");
     } catch (IllegalArgumentException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(cause);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(cause);
     }
   }
 
@@ -684,15 +684,15 @@
     SomeCheckedException cause = new SomeCheckedException();
     SomeChainingException thrown = new SomeChainingException(cause);
 
-    assertThat(thrown).hasCauseThat().isSameAs(cause);
-    assertThat(Throwables.getCauseAs(thrown, SomeCheckedException.class)).isSameAs(cause);
-    assertThat(Throwables.getCauseAs(thrown, Exception.class)).isSameAs(cause);
+    assertThat(thrown).hasCauseThat().isSameInstanceAs(cause);
+    assertThat(Throwables.getCauseAs(thrown, SomeCheckedException.class)).isSameInstanceAs(cause);
+    assertThat(Throwables.getCauseAs(thrown, Exception.class)).isSameInstanceAs(cause);
 
     try {
       Throwables.getCauseAs(thrown, IllegalStateException.class);
       fail("Should have thrown CCE");
     } catch (ClassCastException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(thrown);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(thrown);
     }
   }
 
diff --git a/android/guava-tests/test/com/google/common/cache/AbstractCacheTest.java b/android/guava-tests/test/com/google/common/cache/AbstractCacheTest.java
index a9dcbfb..13ef33d 100644
--- a/android/guava-tests/test/com/google/common/cache/AbstractCacheTest.java
+++ b/android/guava-tests/test/com/google/common/cache/AbstractCacheTest.java
@@ -146,6 +146,14 @@
     assertEquals(27, stats.evictionCount());
   }
 
+  public void testSimpleStatsOverflow() {
+    StatsCounter counter = new SimpleStatsCounter();
+    counter.recordLoadSuccess(Long.MAX_VALUE);
+    counter.recordLoadSuccess(1);
+    CacheStats stats = counter.snapshot();
+    assertEquals(Long.MAX_VALUE, stats.totalLoadTime());
+  }
+
   public void testSimpleStatsIncrementBy() {
     long totalLoadTime = 0;
 
diff --git a/android/guava-tests/test/com/google/common/cache/CacheLoadingTest.java b/android/guava-tests/test/com/google/common/cache/CacheLoadingTest.java
index bc2cbd3..ba3e7e9 100644
--- a/android/guava-tests/test/com/google/common/cache/CacheLoadingTest.java
+++ b/android/guava-tests/test/com/google/common/cache/CacheLoadingTest.java
@@ -91,7 +91,7 @@
   }
 
   private void checkLoggedCause(Throwable t) {
-    assertThat(popLoggedThrowable()).hasCauseThat().isSameAs(t);
+    assertThat(popLoggedThrowable()).hasCauseThat().isSameInstanceAs(t);
   }
 
   private void checkLoggedInvalidLoad() {
@@ -889,7 +889,7 @@
       cache.get(new Object());
       fail();
     } catch (ExecutionError expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     stats = cache.stats();
     assertEquals(1, stats.missCount());
@@ -901,7 +901,7 @@
       cache.getUnchecked(new Object());
       fail();
     } catch (ExecutionError expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     stats = cache.stats();
     assertEquals(2, stats.missCount());
@@ -929,7 +929,7 @@
           });
       fail();
     } catch (ExecutionError expected) {
-      assertThat(expected).hasCauseThat().isSameAs(callableError);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(callableError);
     }
     stats = cache.stats();
     assertEquals(3, stats.missCount());
@@ -941,7 +941,7 @@
       cache.getAll(asList(new Object()));
       fail();
     } catch (ExecutionError expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     stats = cache.stats();
     assertEquals(4, stats.missCount());
@@ -1122,7 +1122,7 @@
       cache.getAll(asList(new Object()));
       fail();
     } catch (ExecutionError expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     stats = cache.stats();
     assertEquals(1, stats.missCount());
@@ -1145,7 +1145,7 @@
       cache.get(new Object());
       fail();
     } catch (ExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     stats = cache.stats();
     assertEquals(1, stats.missCount());
@@ -1157,7 +1157,7 @@
       cache.getUnchecked(new Object());
       fail();
     } catch (UncheckedExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     stats = cache.stats();
     assertEquals(2, stats.missCount());
@@ -1178,7 +1178,7 @@
       cache.get(new Object(), throwing(callableException));
       fail();
     } catch (ExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(callableException);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(callableException);
     }
     stats = cache.stats();
     assertEquals(3, stats.missCount());
@@ -1190,7 +1190,7 @@
       cache.getAll(asList(new Object()));
       fail();
     } catch (ExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     stats = cache.stats();
     assertEquals(4, stats.missCount());
@@ -1216,7 +1216,7 @@
       cache.get(new Object());
       fail();
     } catch (ExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     assertTrue(currentThread().interrupted());
     stats = cache.stats();
@@ -1229,7 +1229,7 @@
       cache.getUnchecked(new Object());
       fail();
     } catch (UncheckedExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     assertTrue(currentThread().interrupted());
     stats = cache.stats();
@@ -1252,7 +1252,7 @@
       cache.get(new Object(), throwing(callableException));
       fail();
     } catch (ExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(callableException);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(callableException);
     }
     assertTrue(currentThread().interrupted());
     stats = cache.stats();
@@ -1265,7 +1265,7 @@
       cache.getAll(asList(new Object()));
       fail();
     } catch (ExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     assertTrue(currentThread().interrupted());
     stats = cache.stats();
@@ -1447,7 +1447,7 @@
       cache.getAll(asList(new Object()));
       fail();
     } catch (ExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     stats = cache.stats();
     assertEquals(1, stats.missCount());
@@ -1471,7 +1471,7 @@
       cache.getAll(asList(new Object()));
       fail();
     } catch (ExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     assertTrue(currentThread().interrupted());
     stats = cache.stats();
@@ -1495,7 +1495,7 @@
       cache.get(new Object());
       fail();
     } catch (UncheckedExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     stats = cache.stats();
     assertEquals(1, stats.missCount());
@@ -1507,7 +1507,7 @@
       cache.getUnchecked(new Object());
       fail();
     } catch (UncheckedExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     stats = cache.stats();
     assertEquals(2, stats.missCount());
@@ -1528,7 +1528,7 @@
       cache.get(new Object(), throwing(callableException));
       fail();
     } catch (UncheckedExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(callableException);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(callableException);
     }
     stats = cache.stats();
     assertEquals(3, stats.missCount());
@@ -1540,7 +1540,7 @@
       cache.getAll(asList(new Object()));
       fail();
     } catch (UncheckedExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     stats = cache.stats();
     assertEquals(4, stats.missCount());
@@ -1721,7 +1721,7 @@
       cache.getAll(asList(new Object()));
       fail();
     } catch (UncheckedExecutionException expected) {
-      assertThat(expected).hasCauseThat().isSameAs(e);
+      assertThat(expected).hasCauseThat().isSameInstanceAs(e);
     }
     stats = cache.stats();
     assertEquals(1, stats.missCount());
@@ -1752,7 +1752,7 @@
       cache.getUnchecked(1);
       fail();
     } catch (UncheckedExecutionException ue) {
-      assertThat(ue).hasCauseThat().isSameAs(e);
+      assertThat(ue).hasCauseThat().isSameInstanceAs(e);
     }
 
     assertEquals("1", cache.getUnchecked(1));
@@ -1866,14 +1866,14 @@
     } catch (ExecutionException e) {
       fail();
     } catch (UncheckedExecutionException caughtEe) {
-      assertThat(caughtEe).hasCauseThat().isSameAs(uee);
+      assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee);
     }
 
     try {
       cacheUnchecked.getUnchecked(new Object());
       fail();
     } catch (UncheckedExecutionException caughtUee) {
-      assertThat(caughtUee).hasCauseThat().isSameAs(uee);
+      assertThat(caughtUee).hasCauseThat().isSameInstanceAs(uee);
     }
 
     cacheUnchecked.refresh(new Object());
@@ -1885,21 +1885,21 @@
     } catch (ExecutionException e) {
       fail();
     } catch (UncheckedExecutionException caughtEe) {
-      assertThat(caughtEe).hasCauseThat().isSameAs(uee);
+      assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee);
     }
 
     try {
       cacheChecked.get(new Object());
       fail();
     } catch (ExecutionException caughtEe) {
-      assertThat(caughtEe).hasCauseThat().isSameAs(ee);
+      assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee);
     }
 
     try {
       cacheChecked.getUnchecked(new Object());
       fail();
     } catch (UncheckedExecutionException caughtUee) {
-      assertThat(caughtUee).hasCauseThat().isSameAs(ee);
+      assertThat(caughtUee).hasCauseThat().isSameInstanceAs(ee);
     }
 
     cacheChecked.refresh(new Object());
@@ -1909,7 +1909,7 @@
       cacheChecked.getAll(asList(new Object()));
       fail();
     } catch (ExecutionException caughtEe) {
-      assertThat(caughtEe).hasCauseThat().isSameAs(ee);
+      assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee);
     }
   }
 
@@ -1929,14 +1929,14 @@
     } catch (ExecutionException e) {
       fail();
     } catch (UncheckedExecutionException caughtEe) {
-      assertThat(caughtEe).hasCauseThat().isSameAs(uee);
+      assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee);
     }
 
     try {
       cacheChecked.getAll(asList(new Object()));
       fail();
     } catch (ExecutionException caughtEe) {
-      assertThat(caughtEe).hasCauseThat().isSameAs(ee);
+      assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee);
     }
   }
 
@@ -2057,7 +2057,7 @@
       // doConcurrentGet alternates between calling getUnchecked and calling get, but an unchecked
       // exception thrown by the loader is always wrapped as an UncheckedExecutionException.
       assertThat(result.get(i)).isInstanceOf(UncheckedExecutionException.class);
-      assertThat(((UncheckedExecutionException) result.get(i))).hasCauseThat().isSameAs(e);
+      assertThat(((UncheckedExecutionException) result.get(i))).hasCauseThat().isSameInstanceAs(e);
     }
 
     // subsequent calls should call the loader again, not get the old exception
@@ -2103,10 +2103,10 @@
       int mod = i % 3;
       if (mod == 0 || mod == 2) {
         assertThat(result.get(i)).isInstanceOf(ExecutionException.class);
-        assertThat((ExecutionException) result.get(i)).hasCauseThat().isSameAs(e);
+        assertThat((ExecutionException) result.get(i)).hasCauseThat().isSameInstanceAs(e);
       } else {
         assertThat(result.get(i)).isInstanceOf(UncheckedExecutionException.class);
-        assertThat((UncheckedExecutionException) result.get(i)).hasCauseThat().isSameAs(e);
+        assertThat((UncheckedExecutionException) result.get(i)).hasCauseThat().isSameInstanceAs(e);
       }
     }
 
diff --git a/android/guava-tests/test/com/google/common/cache/CacheStatsTest.java b/android/guava-tests/test/com/google/common/cache/CacheStatsTest.java
index f029d37..3e715f1 100644
--- a/android/guava-tests/test/com/google/common/cache/CacheStatsTest.java
+++ b/android/guava-tests/test/com/google/common/cache/CacheStatsTest.java
@@ -98,4 +98,32 @@
 
     assertEquals(sum, one.plus(two));
   }
+
+  public void testPlusLarge() {
+    CacheStats maxCacheStats =
+        new CacheStats(
+            Long.MAX_VALUE,
+            Long.MAX_VALUE,
+            Long.MAX_VALUE,
+            Long.MAX_VALUE,
+            Long.MAX_VALUE,
+            Long.MAX_VALUE);
+    CacheStats smallCacheStats = new CacheStats(1, 1, 1, 1, 1, 1);
+
+    CacheStats sum = smallCacheStats.plus(maxCacheStats);
+    assertEquals(Long.MAX_VALUE, sum.requestCount());
+    assertEquals(Long.MAX_VALUE, sum.hitCount());
+    assertEquals(1.0, sum.hitRate());
+    assertEquals(Long.MAX_VALUE, sum.missCount());
+    assertEquals(1.0, sum.missRate());
+    assertEquals(Long.MAX_VALUE, sum.loadSuccessCount());
+    assertEquals(Long.MAX_VALUE, sum.loadExceptionCount());
+    assertEquals(1.0, sum.loadExceptionRate());
+    assertEquals(Long.MAX_VALUE, sum.loadCount());
+    assertEquals(Long.MAX_VALUE, sum.totalLoadTime());
+    assertEquals(1.0, sum.averageLoadPenalty());
+    assertEquals(Long.MAX_VALUE, sum.evictionCount());
+
+    assertEquals(sum, maxCacheStats.plus(smallCacheStats));
+  }
 }
diff --git a/android/guava-tests/test/com/google/common/cache/CacheTesting.java b/android/guava-tests/test/com/google/common/cache/CacheTesting.java
index d7a3b4a..255a894 100644
--- a/android/guava-tests/test/com/google/common/cache/CacheTesting.java
+++ b/android/guava-tests/test/com/google/common/cache/CacheTesting.java
@@ -393,7 +393,7 @@
 
       ReferenceEntry<?, ?> originalHead = segment.accessQueue.peek();
       @SuppressWarnings("unchecked")
-      ReferenceEntry<Integer, Integer> entry = (ReferenceEntry) originalHead;
+      ReferenceEntry<Integer, Integer> entry = (ReferenceEntry<Integer, Integer>) originalHead;
       operation.accept(entry);
       drainRecencyQueue(segment);
 
diff --git a/android/guava-tests/test/com/google/common/cache/ForwardingCacheTest.java b/android/guava-tests/test/com/google/common/cache/ForwardingCacheTest.java
index 127b3c5..412ff78 100644
--- a/android/guava-tests/test/com/google/common/cache/ForwardingCacheTest.java
+++ b/android/guava-tests/test/com/google/common/cache/ForwardingCacheTest.java
@@ -34,7 +34,7 @@
   private Cache<String, Boolean> forward;
   private Cache<String, Boolean> mock;
 
-  @SuppressWarnings("unchecked") // mock
+  @SuppressWarnings({"unchecked", "DoNotMock"}) // mock
   @Override
   public void setUp() throws Exception {
     super.setUp();
diff --git a/android/guava-tests/test/com/google/common/cache/ForwardingLoadingCacheTest.java b/android/guava-tests/test/com/google/common/cache/ForwardingLoadingCacheTest.java
index 01704ef..c5681d2 100644
--- a/android/guava-tests/test/com/google/common/cache/ForwardingLoadingCacheTest.java
+++ b/android/guava-tests/test/com/google/common/cache/ForwardingLoadingCacheTest.java
@@ -34,7 +34,7 @@
   private LoadingCache<String, Boolean> forward;
   private LoadingCache<String, Boolean> mock;
 
-  @SuppressWarnings("unchecked") // mock
+  @SuppressWarnings({"unchecked", "DoNotMock"}) // mock
   @Override
   public void setUp() throws Exception {
     super.setUp();
diff --git a/android/guava-tests/test/com/google/common/cache/LocalCacheTest.java b/android/guava-tests/test/com/google/common/cache/LocalCacheTest.java
index da2224e..667ea93 100644
--- a/android/guava-tests/test/com/google/common/cache/LocalCacheTest.java
+++ b/android/guava-tests/test/com/google/common/cache/LocalCacheTest.java
@@ -2413,7 +2413,7 @@
         ReferenceEntry<Object, Object> entry = segment.getEntry(keyOne, hashOne);
 
         @SuppressWarnings("unchecked")
-        Reference<Object> reference = (Reference) entry;
+        Reference<Object> reference = (Reference<Object>) entry;
         reference.enqueue();
 
         map.put(keyTwo, valueTwo);
@@ -2443,7 +2443,7 @@
         ValueReference<Object, Object> valueReference = entry.getValueReference();
 
         @SuppressWarnings("unchecked")
-        Reference<Object> reference = (Reference) valueReference;
+        Reference<Object> reference = (Reference<Object>) valueReference;
         reference.enqueue();
 
         map.put(keyTwo, valueTwo);
@@ -2471,7 +2471,7 @@
         ReferenceEntry<Object, Object> entry = segment.getEntry(keyOne, hashOne);
 
         @SuppressWarnings("unchecked")
-        Reference<Object> reference = (Reference) entry;
+        Reference<Object> reference = (Reference<Object>) entry;
         reference.enqueue();
 
         for (int i = 0; i < SMALL_MAX_SIZE; i++) {
@@ -2502,7 +2502,7 @@
         ValueReference<Object, Object> valueReference = entry.getValueReference();
 
         @SuppressWarnings("unchecked")
-        Reference<Object> reference = (Reference) valueReference;
+        Reference<Object> reference = (Reference<Object>) valueReference;
         reference.enqueue();
 
         for (int i = 0; i < SMALL_MAX_SIZE; i++) {
diff --git a/android/guava-tests/test/com/google/common/cache/LongAdderTest.java b/android/guava-tests/test/com/google/common/cache/LongAdderTest.java
index 78f6edc..876f907 100644
--- a/android/guava-tests/test/com/google/common/cache/LongAdderTest.java
+++ b/android/guava-tests/test/com/google/common/cache/LongAdderTest.java
@@ -14,11 +14,27 @@
 
 package com.google.common.cache;
 
-/**
- * No-op null-pointer test for {@link LongAdder} to override the {@link PackageSanityTests} version,
- * which checks package-private methods that we don't want to have to annotate as {@code Nullable}
- * because we don't want diffs from jsr166e.
- */
-public class LongAdderTest {
+import static com.google.common.truth.Truth.assertThat;
+
+import junit.framework.TestCase;
+
+/** Unit tests for {@link LongAdder}. */
+public class LongAdderTest extends TestCase {
+
+  /**
+   * No-op null-pointer test for {@link LongAdder} to override the {@link PackageSanityTests}
+   * version, which checks package-private methods that we don't want to have to annotate as {@code
+   * Nullable} because we don't want diffs from jsr166e.
+   */
   public void testNulls() {}
+
+  public void testOverflows() {
+    LongAdder longAdder = new LongAdder();
+    longAdder.add(Long.MAX_VALUE);
+    assertThat(longAdder.sum()).isEqualTo(Long.MAX_VALUE);
+    longAdder.add(1);
+    // silently overflows; is this a bug?
+    // See https://github.com/google/guava/issues/3503
+    assertThat(longAdder.sum()).isEqualTo(-9223372036854775808L);
+  }
 }
diff --git a/android/guava-tests/test/com/google/common/cache/NullCacheTest.java b/android/guava-tests/test/com/google/common/cache/NullCacheTest.java
index e418f6c..89dc3fb 100644
--- a/android/guava-tests/test/com/google/common/cache/NullCacheTest.java
+++ b/android/guava-tests/test/com/google/common/cache/NullCacheTest.java
@@ -123,7 +123,7 @@
       map.getUnchecked(new Object());
       fail();
     } catch (UncheckedExecutionException uee) {
-      assertThat(uee).hasCauseThat().isSameAs(e);
+      assertThat(uee).hasCauseThat().isSameInstanceAs(e);
     }
     assertTrue(listener.isEmpty());
     checkEmpty(map);
diff --git a/android/guava-tests/test/com/google/common/cache/PopulatedCachesTest.java b/android/guava-tests/test/com/google/common/cache/PopulatedCachesTest.java
index 06f0d27..1e71b63 100644
--- a/android/guava-tests/test/com/google/common/cache/PopulatedCachesTest.java
+++ b/android/guava-tests/test/com/google/common/cache/PopulatedCachesTest.java
@@ -237,21 +237,15 @@
     }
   }
 
-  @SuppressWarnings("unchecked") // generic array creation
-
   public void testEntrySet_populated() {
     for (LoadingCache<Object, Object> cache : caches()) {
       Set<Entry<Object, Object>> entries = cache.asMap().entrySet();
       List<Entry<Object, Object>> warmed = warmUp(cache, WARMUP_MIN, WARMUP_MAX);
 
       Set<?> expected = Maps.newHashMap(cache.asMap()).entrySet();
-      assertThat(entries).containsExactlyElementsIn((Collection<Entry<Object, Object>>) expected);
-      assertThat(entries.toArray())
-          .asList()
-          .containsExactlyElementsIn((Collection<Object>) expected);
-      assertThat(entries.toArray(new Entry[0]))
-          .asList()
-          .containsExactlyElementsIn((Collection<Entry>) expected);
+      assertThat(entries).containsExactlyElementsIn(expected);
+      assertThat(entries.toArray()).asList().containsExactlyElementsIn(expected);
+      assertThat(entries.toArray(new Object[0])).asList().containsExactlyElementsIn(expected);
 
       new EqualsTester()
           .addEqualityGroup(cache.asMap().entrySet(), entries)
diff --git a/android/guava-tests/test/com/google/common/collect/AbstractBiMapTest.java b/android/guava-tests/test/com/google/common/collect/AbstractBiMapTest.java
index 55a455b..e430f0e 100644
--- a/android/guava-tests/test/com/google/common/collect/AbstractBiMapTest.java
+++ b/android/guava-tests/test/com/google/common/collect/AbstractBiMapTest.java
@@ -28,6 +28,7 @@
 
   // The next two tests verify that map entries are not accessed after they're
   // removed, since IdentityHashMap throws an exception when that occurs.
+  @SuppressWarnings("IdentityHashMapBoxing") // explicitly testing IdentityHashMap
   public void testIdentityKeySetIteratorRemove() {
     BiMap<Integer, String> bimap =
         new AbstractBiMap<Integer, String>(
@@ -45,6 +46,7 @@
     assertEquals(1, bimap.inverse().size());
   }
 
+  @SuppressWarnings("IdentityHashMapBoxing") // explicitly testing IdentityHashMap
   public void testIdentityEntrySetIteratorRemove() {
     BiMap<Integer, String> bimap =
         new AbstractBiMap<Integer, String>(
diff --git a/android/guava-tests/test/com/google/common/collect/BenchmarkHelpers.java b/android/guava-tests/test/com/google/common/collect/BenchmarkHelpers.java
index 4d13736..65d2ef6 100644
--- a/android/guava-tests/test/com/google/common/collect/BenchmarkHelpers.java
+++ b/android/guava-tests/test/com/google/common/collect/BenchmarkHelpers.java
@@ -107,21 +107,7 @@
         return ContiguousSet.copyOf(contents);
       }
     },
-  //    @GoogleInternal
-  //    CompactHashSetImpl {
-  //      @Override
-  //      public <E extends Comparable<E>> Set<E> create(Collection<E> contents) {
-  //        return CompactHashSet.create(contents);
-  //      }
-  //    },
-  //    @GoogleInternal
-  //    CompactLinkedHashSetImpl {
-  //      @Override
-  //      public <E extends Comparable<E>> Set<E> create(Collection<E> contents) {
-  //        return CompactLinkedHashSet.create(contents);
-  //      }
-  //    },
-  ;
+    ;
   }
 
   public enum ListMultimapImpl {
@@ -217,24 +203,6 @@
         return new ConcurrentHashMap<>(map);
       }
     },
-    //    @GoogleInternal
-    //    CompactHashmapImpl {
-    //      @Override
-    //      public <K extends Comparable<K>, V> Map<K, V> create(Map<K, V> map) {
-    //        Map<K, V> result = CompactHashMap.createWithExpectedSize(map.size());
-    //        result.putAll(map);
-    //        return result;
-    //      }
-    //    },
-    //    @GoogleInternal
-    //    CompactLinkedHashmapImpl {
-    //      @Override
-    //      public <K extends Comparable<K>, V> Map<K, V> create(Map<K, V> map) {
-    //        Map<K, V> result = CompactLinkedHashMap.createWithExpectedSize(map.size());
-    //        result.putAll(map);
-    //        return result;
-    //      }
-    //    },
     ImmutableMapImpl {
       @Override
       public <K extends Comparable<K>, V> Map<K, V> create(Map<K, V> map) {
diff --git a/android/guava-tests/test/com/google/common/collect/CompactHashMapTest.java b/android/guava-tests/test/com/google/common/collect/CompactHashMapTest.java
index 33e6015..52c9bed 100644
--- a/android/guava-tests/test/com/google/common/collect/CompactHashMapTest.java
+++ b/android/guava-tests/test/com/google/common/collect/CompactHashMapTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.google.common.collect;
 
 import static com.google.common.collect.Iterables.getOnlyElement;
@@ -85,4 +86,35 @@
     entry.setValue("one");
     assertThat(map).containsEntry(1, "one");
   }
+
+  public void testAllocArraysDefault() {
+    CompactHashMap<Integer, String> map = CompactHashMap.create();
+    assertThat(map.needsAllocArrays()).isTrue();
+    assertThat(map.entries).isNull();
+    assertThat(map.keys).isNull();
+    assertThat(map.values).isNull();
+
+    map.put(1, "1");
+    assertThat(map.needsAllocArrays()).isFalse();
+    assertThat(map.entries).hasLength(CompactHashing.DEFAULT_SIZE);
+    assertThat(map.keys).hasLength(CompactHashing.DEFAULT_SIZE);
+    assertThat(map.values).hasLength(CompactHashing.DEFAULT_SIZE);
+  }
+
+  public void testAllocArraysExpectedSize() {
+    for (int i = 0; i <= CompactHashing.DEFAULT_SIZE; i++) {
+      CompactHashMap<Integer, String> map = CompactHashMap.createWithExpectedSize(i);
+      assertThat(map.needsAllocArrays()).isTrue();
+      assertThat(map.entries).isNull();
+      assertThat(map.keys).isNull();
+      assertThat(map.values).isNull();
+
+      map.put(1, "1");
+      assertThat(map.needsAllocArrays()).isFalse();
+      int expectedSize = Math.max(1, i);
+      assertThat(map.entries).hasLength(expectedSize);
+      assertThat(map.keys).hasLength(expectedSize);
+      assertThat(map.values).hasLength(expectedSize);
+    }
+  }
 }
diff --git a/android/guava-tests/test/com/google/common/collect/CompactHashSetTest.java b/android/guava-tests/test/com/google/common/collect/CompactHashSetTest.java
index e5c3a45..0f0216f 100644
--- a/android/guava-tests/test/com/google/common/collect/CompactHashSetTest.java
+++ b/android/guava-tests/test/com/google/common/collect/CompactHashSetTest.java
@@ -16,6 +16,9 @@
 
 package com.google.common.collect;
 
+import static com.google.common.truth.Truth.assertThat;
+import static java.util.stream.Collectors.*;
+
 import com.google.common.annotations.GwtIncompatible;
 import com.google.common.collect.testing.SetTestSuiteBuilder;
 import com.google.common.collect.testing.TestStringSetGenerator;
@@ -83,7 +86,26 @@
     return suite;
   }
 
-  public void testDummyMethod() {
-    // Just make sure the test runner doesn't complain about no test methods.
+  public void testAllocArraysDefault() {
+    CompactHashSet<Integer> set = CompactHashSet.create();
+    assertThat(set.needsAllocArrays()).isTrue();
+    assertThat(set.elements).isNull();
+
+    set.add(1);
+    assertThat(set.needsAllocArrays()).isFalse();
+    assertThat(set.elements).hasLength(CompactHashing.DEFAULT_SIZE);
+  }
+
+  public void testAllocArraysExpectedSize() {
+    for (int i = 0; i <= CompactHashing.DEFAULT_SIZE; i++) {
+      CompactHashSet<Integer> set = CompactHashSet.createWithExpectedSize(i);
+      assertThat(set.needsAllocArrays()).isTrue();
+      assertThat(set.elements).isNull();
+
+      set.add(1);
+      assertThat(set.needsAllocArrays()).isFalse();
+      int expectedSize = Math.max(1, i);
+      assertThat(set.elements).hasLength(expectedSize);
+    }
   }
 }
diff --git a/android/guava-tests/test/com/google/common/collect/CompactLinkedHashMapTest.java b/android/guava-tests/test/com/google/common/collect/CompactLinkedHashMapTest.java
index f7e2857..d1363bc 100644
--- a/android/guava-tests/test/com/google/common/collect/CompactLinkedHashMapTest.java
+++ b/android/guava-tests/test/com/google/common/collect/CompactLinkedHashMapTest.java
@@ -11,6 +11,7 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.google.common.collect;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -96,8 +97,8 @@
     map.put(4, "b");
     map.put(3, "d");
     map.put(2, "c");
-    map.remove(3);
-    testHasMapEntriesInOrder(map, 1, "a", 4, "b", 2, "c");
+    map.remove(4);
+    testHasMapEntriesInOrder(map, 1, "a", 3, "d", 2, "c");
   }
 
   public void testInsertionOrderAfterRemoveLastEntry() {
@@ -141,4 +142,39 @@
       assertEquals(expectedValue, values.get(i));
     }
   }
+
+  public void testAllocArraysDefault() {
+    CompactLinkedHashMap<Integer, String> map = CompactLinkedHashMap.create();
+    assertThat(map.needsAllocArrays()).isTrue();
+    assertThat(map.entries).isNull();
+    assertThat(map.keys).isNull();
+    assertThat(map.values).isNull();
+    assertThat(map.links).isNull();
+
+    map.put(1, Integer.toString(1));
+    assertThat(map.needsAllocArrays()).isFalse();
+    assertThat(map.entries).hasLength(CompactHashing.DEFAULT_SIZE);
+    assertThat(map.keys).hasLength(CompactHashing.DEFAULT_SIZE);
+    assertThat(map.values).hasLength(CompactHashing.DEFAULT_SIZE);
+    assertThat(map.links).hasLength(CompactHashing.DEFAULT_SIZE);
+  }
+
+  public void testAllocArraysExpectedSize() {
+    for (int i = 0; i <= CompactHashing.DEFAULT_SIZE; i++) {
+      CompactLinkedHashMap<Integer, String> map = CompactLinkedHashMap.createWithExpectedSize(i);
+      assertThat(map.needsAllocArrays()).isTrue();
+      assertThat(map.entries).isNull();
+      assertThat(map.keys).isNull();
+      assertThat(map.values).isNull();
+      assertThat(map.links).isNull();
+
+      map.put(1, Integer.toString(1));
+      assertThat(map.needsAllocArrays()).isFalse();
+      int expectedSize = Math.max(1, i);
+      assertThat(map.entries).hasLength(expectedSize);
+      assertThat(map.keys).hasLength(expectedSize);
+      assertThat(map.values).hasLength(expectedSize);
+      assertThat(map.links).hasLength(expectedSize);
+    }
+  }
 }
diff --git a/android/guava-tests/test/com/google/common/collect/CompactLinkedHashSetTest.java b/android/guava-tests/test/com/google/common/collect/CompactLinkedHashSetTest.java
index a95e486..299503e 100644
--- a/android/guava-tests/test/com/google/common/collect/CompactLinkedHashSetTest.java
+++ b/android/guava-tests/test/com/google/common/collect/CompactLinkedHashSetTest.java
@@ -16,6 +16,8 @@
 
 package com.google.common.collect;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import com.google.common.annotations.GwtIncompatible;
 import com.google.common.collect.testing.SetTestSuiteBuilder;
 import com.google.common.collect.testing.TestStringSetGenerator;
@@ -65,7 +67,26 @@
     return suite;
   }
 
-  public void testDummyMethod() {
-    // Just make sure the test runner doesn't complain about no test methods.
+  public void testAllocArraysDefault() {
+    CompactHashSet<Integer> set = CompactHashSet.create();
+    assertThat(set.needsAllocArrays()).isTrue();
+    assertThat(set.elements).isNull();
+
+    set.add(1);
+    assertThat(set.needsAllocArrays()).isFalse();
+    assertThat(set.elements).hasLength(CompactHashing.DEFAULT_SIZE);
+  }
+
+  public void testAllocArraysExpectedSize() {
+    for (int i = 0; i <= CompactHashing.DEFAULT_SIZE; i++) {
+      CompactHashSet<Integer> set = CompactHashSet.createWithExpectedSize(i);
+      assertThat(set.needsAllocArrays()).isTrue();
+      assertThat(set.elements).isNull();
+
+      set.add(1);
+      assertThat(set.needsAllocArrays()).isFalse();
+      int expectedSize = Math.max(1, i);
+      assertThat(set.elements).hasLength(expectedSize);
+    }
   }
 }
diff --git a/android/guava-tests/test/com/google/common/collect/ComparatorsTest.java b/android/guava-tests/test/com/google/common/collect/ComparatorsTest.java
index d5f8801..b30cb76 100644
--- a/android/guava-tests/test/com/google/common/collect/ComparatorsTest.java
+++ b/android/guava-tests/test/com/google/common/collect/ComparatorsTest.java
@@ -16,6 +16,7 @@
 
 package com.google.common.collect;
 
+import static com.google.common.truth.Truth.assertThat;
 import static java.util.Arrays.asList;
 
 import com.google.common.annotations.GwtCompatible;
@@ -71,4 +72,59 @@
     assertTrue(Comparators.isInStrictOrder(Collections.singleton(1), Ordering.natural()));
     assertTrue(Comparators.isInStrictOrder(Collections.<Integer>emptyList(), Ordering.natural()));
   }
+
+  public void testMinMaxNatural() {
+    assertThat(Comparators.min(1, 2)).isEqualTo(1);
+    assertThat(Comparators.min(2, 1)).isEqualTo(1);
+    assertThat(Comparators.max(1, 2)).isEqualTo(2);
+    assertThat(Comparators.max(2, 1)).isEqualTo(2);
+  }
+
+  public void testMinMaxNatural_equalInstances() {
+    Foo a = new Foo(1);
+    Foo b = new Foo(1);
+    assertThat(Comparators.min(a, b)).isSameInstanceAs(a);
+    assertThat(Comparators.max(a, b)).isSameInstanceAs(a);
+  }
+
+  public void testMinMaxComparator() {
+    Comparator<Integer> natural = Ordering.natural();
+    Comparator<Integer> reverse = Collections.reverseOrder(natural);
+    assertThat(Comparators.min(1, 2, reverse)).isEqualTo(2);
+    assertThat(Comparators.min(2, 1, reverse)).isEqualTo(2);
+    assertThat(Comparators.max(1, 2, reverse)).isEqualTo(1);
+    assertThat(Comparators.max(2, 1, reverse)).isEqualTo(1);
+  }
+
+  public void testMinMaxComparator_equalInstances() {
+    Comparator<Foo> natural = Ordering.natural();
+    Comparator<Foo> reverse = Collections.reverseOrder(natural);
+    Foo a = new Foo(1);
+    Foo b = new Foo(1);
+    assertThat(Comparators.min(a, b, reverse)).isSameInstanceAs(a);
+    assertThat(Comparators.max(a, b, reverse)).isSameInstanceAs(a);
+  }
+
+  private static class Foo implements Comparable<Foo> {
+    final Integer value;
+
+    Foo(int value) {
+      this.value = value;
+    }
+
+    @Override
+    public int hashCode() {
+      return value.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      return (o instanceof Foo) && ((Foo) o).value.equals(value);
+    }
+
+    @Override
+    public int compareTo(Foo other) {
+      return value.compareTo(other.value);
+    }
+  }
 }
diff --git a/android/guava-tests/test/com/google/common/collect/ConcurrentHashMultisetTest.java b/android/guava-tests/test/com/google/common/collect/ConcurrentHashMultisetTest.java
index 592a1c7..1d38c86 100644
--- a/android/guava-tests/test/com/google/common/collect/ConcurrentHashMultisetTest.java
+++ b/android/guava-tests/test/com/google/common/collect/ConcurrentHashMultisetTest.java
@@ -20,8 +20,8 @@
 import static com.google.common.collect.MapMakerInternalMap.Strength.WEAK;
 import static com.google.common.testing.SerializableTester.reserializeAndAssert;
 import static java.util.Arrays.asList;
+import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.isA;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingMapTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingMapTest.java
index 6ca5b8c..cc5d739 100644
--- a/android/guava-tests/test/com/google/common/collect/ForwardingMapTest.java
+++ b/android/guava-tests/test/com/google/common/collect/ForwardingMapTest.java
@@ -17,7 +17,7 @@
 package com.google.common.collect;
 
 import static java.lang.reflect.Modifier.STATIC;
-import static org.mockito.Mockito.anyObject;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -231,10 +231,10 @@
 
     // These are the methods specified by StandardEntrySet
     verify(map, atLeast(0)).clear();
-    verify(map, atLeast(0)).containsKey(anyObject());
-    verify(map, atLeast(0)).get(anyObject());
+    verify(map, atLeast(0)).containsKey(any());
+    verify(map, atLeast(0)).get(any());
     verify(map, atLeast(0)).isEmpty();
-    verify(map, atLeast(0)).remove(anyObject());
+    verify(map, atLeast(0)).remove(any());
     verify(map, atLeast(0)).size();
     verifyNoMoreInteractions(map);
   }
@@ -259,9 +259,9 @@
 
     // These are the methods specified by StandardKeySet
     verify(map, atLeast(0)).clear();
-    verify(map, atLeast(0)).containsKey(anyObject());
+    verify(map, atLeast(0)).containsKey(any());
     verify(map, atLeast(0)).isEmpty();
-    verify(map, atLeast(0)).remove(anyObject());
+    verify(map, atLeast(0)).remove(any());
     verify(map, atLeast(0)).size();
     verify(map, atLeast(0)).entrySet();
     verifyNoMoreInteractions(map);
@@ -287,7 +287,7 @@
 
     // These are the methods specified by StandardValues
     verify(map, atLeast(0)).clear();
-    verify(map, atLeast(0)).containsValue(anyObject());
+    verify(map, atLeast(0)).containsValue(any());
     verify(map, atLeast(0)).isEmpty();
     verify(map, atLeast(0)).size();
     verify(map, atLeast(0)).entrySet();
diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableBiMapTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableBiMapTest.java
index f1f59c5..cb23f3e 100644
--- a/android/guava-tests/test/com/google/common/collect/ImmutableBiMapTest.java
+++ b/android/guava-tests/test/com/google/common/collect/ImmutableBiMapTest.java
@@ -575,4 +575,7 @@
       assertEquals(alternatingKeysAndValues[i++], entry.getValue());
     }
   }
+
+  /** No-op test so that the class has at least one method, making Maven's test runner happy. */
+  public void testNoop() {}
 }
diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableMapTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableMapTest.java
index e6569b0..2cf43ef 100644
--- a/android/guava-tests/test/com/google/common/collect/ImmutableMapTest.java
+++ b/android/guava-tests/test/com/google/common/collect/ImmutableMapTest.java
@@ -17,6 +17,7 @@
 package com.google.common.collect;
 
 import static com.google.common.testing.SerializableTester.reserialize;
+import static com.google.common.truth.Truth.assertThat;
 
 import com.google.common.annotations.GwtCompatible;
 import com.google.common.annotations.GwtIncompatible;
@@ -45,12 +46,15 @@
 import com.google.common.testing.EqualsTester;
 import com.google.common.testing.NullPointerTester;
 import com.google.common.testing.SerializableTester;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
 import java.io.Serializable;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
@@ -647,6 +651,37 @@
       assertMapEquals(copy, "one", 1, "two", 2, "three", 3);
       assertSame(copy, ImmutableMap.copyOf(copy));
     }
+
+    public static void hashtableTestHelper(ImmutableList<Integer> sizes) {
+      for (int size : sizes) {
+        Builder<Integer, Integer> builder = ImmutableMap.builderWithExpectedSize(size);
+        for (int i = 0; i < size; i++) {
+          Integer integer = i;
+          builder.put(integer, integer);
+        }
+        ImmutableMap<Integer, Integer> map = builder.build();
+        assertEquals(size, map.size());
+        int entries = 0;
+        for (Integer key : map.keySet()) {
+          assertEquals(entries, key.intValue());
+          assertSame(key, map.get(key));
+          entries++;
+        }
+        assertEquals(size, entries);
+      }
+    }
+
+    public void testByteArrayHashtable() {
+      hashtableTestHelper(ImmutableList.of(2, 89));
+    }
+
+    public void testShortArrayHashtable() {
+      hashtableTestHelper(ImmutableList.of(90, 22937));
+    }
+
+    public void testIntArrayHashtable() {
+      hashtableTestHelper(ImmutableList.of(22938));
+    }
   }
 
   public void testNullGet() {
@@ -737,6 +772,58 @@
     assertTrue(reserializedValues instanceof ImmutableCollection);
   }
 
+  @GwtIncompatible // SerializableTester
+  public void testKeySetIsSerializable_regularImmutableMap() {
+    class NonSerializableClass {}
+
+    Map<String, NonSerializableClass> map =
+        RegularImmutableMap.create(1, new Object[] {"one", new NonSerializableClass()});
+    Set<String> set = map.keySet();
+
+    LenientSerializableTester.reserializeAndAssertLenient(set);
+  }
+
+  @GwtIncompatible // SerializableTester
+  public void testValuesCollectionIsSerializable_regularImmutableMap() {
+    class NonSerializableClass {}
+
+    Map<NonSerializableClass, String> map =
+        RegularImmutableMap.create(1, new Object[] {new NonSerializableClass(), "value"});
+    Collection<String> collection = map.values();
+
+    LenientSerializableTester.reserializeAndAssertElementsEqual(collection);
+  }
+
+  // TODO: Re-enable this test after moving to new serialization format in ImmutableMap.
+  @GwtIncompatible // SerializableTester
+  @SuppressWarnings("unchecked")
+  public void ignore_testSerializationNoDuplication_regularImmutableMap() throws Exception {
+    // Tests that searializing a map, its keySet, and values only writes the underlying data once.
+
+    Object[] entries = new Object[2000];
+    for (int i = 0; i < entries.length; i++) {
+      entries[i] = i;
+    }
+
+    ImmutableMap<Integer, Integer> map = RegularImmutableMap.create(entries.length / 2, entries);
+    Set<Integer> keySet = map.keySet();
+    Collection<Integer> values = map.values();
+
+    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+    ObjectOutputStream oos = new ObjectOutputStream(bytes);
+    oos.writeObject(map);
+    oos.flush();
+
+    int mapSize = bytes.size();
+    oos.writeObject(keySet);
+    oos.writeObject(values);
+    oos.close();
+
+    int finalSize = bytes.size();
+
+    assertThat(finalSize - mapSize).isLessThan(100);
+  }
+
   public void testEquals() {
     new EqualsTester()
         .addEqualityGroup(ImmutableMap.of(), ImmutableMap.builder().build())
diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableSetTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableSetTest.java
index 8a984c9..6dc6f22 100644
--- a/android/guava-tests/test/com/google/common/collect/ImmutableSetTest.java
+++ b/android/guava-tests/test/com/google/common/collect/ImmutableSetTest.java
@@ -366,4 +366,12 @@
     builder.add("baz");
     assertTrue(set.elements != builder.contents);
   }
+
+  public void testReuseBuilderReducingHashTableSizeWithPowerOfTwoTotalElements() {
+    ImmutableSet.Builder<Object> builder = ImmutableSet.builderWithExpectedSize(6);
+    builder.add(0);
+    ImmutableSet<Object> unused = builder.build();
+    ImmutableSet<Object> subject = builder.add(1).add(2).add(3).build();
+    assertFalse(subject.contains(4));
+  }
 }
diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableSortedSetTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableSortedSetTest.java
index 520ba65..cf2f5aa 100644
--- a/android/guava-tests/test/com/google/common/collect/ImmutableSortedSetTest.java
+++ b/android/guava-tests/test/com/google/common/collect/ImmutableSortedSetTest.java
@@ -1005,6 +1005,44 @@
     }
   }
 
+  public void testFloor_emptySet() {
+    ImmutableSortedSet<String> set = ImmutableSortedSet.copyOf(new String[] {});
+    assertThat(set.floor("f")).isNull();
+  }
+
+  public void testFloor_elementPresent() {
+    ImmutableSortedSet<String> set =
+        ImmutableSortedSet.copyOf(new String[] {"e", "a", "e", "f", "b", "i", "d", "a", "c", "k"});
+    assertThat(set.floor("f")).isEqualTo("f");
+    assertThat(set.floor("j")).isEqualTo("i");
+    assertThat(set.floor("q")).isEqualTo("k");
+  }
+
+  public void testFloor_elementAbsent() {
+    ImmutableSortedSet<String> set =
+        ImmutableSortedSet.copyOf(new String[] {"e", "e", "f", "b", "i", "d", "c", "k"});
+    assertThat(set.floor("a")).isNull();
+  }
+
+  public void testCeiling_emptySet() {
+    ImmutableSortedSet<String> set = ImmutableSortedSet.copyOf(new String[] {});
+    assertThat(set.ceiling("f")).isNull();
+  }
+
+  public void testCeiling_elementPresent() {
+    ImmutableSortedSet<String> set =
+        ImmutableSortedSet.copyOf(new String[] {"e", "e", "f", "f", "i", "d", "c", "k", "p", "c"});
+    assertThat(set.ceiling("f")).isEqualTo("f");
+    assertThat(set.ceiling("h")).isEqualTo("i");
+    assertThat(set.ceiling("a")).isEqualTo("c");
+  }
+
+  public void testCeiling_elementAbsent() {
+    ImmutableSortedSet<String> set =
+        ImmutableSortedSet.copyOf(new String[] {"e", "a", "e", "f", "b", "i", "d", "a", "c", "k"});
+    assertThat(set.ceiling("l")).isNull();
+  }
+
   public void testSubSetExclusiveExclusive() {
     String[] strings = NUMBER_NAMES.toArray(new String[0]);
     ImmutableSortedSet<String> set = ImmutableSortedSet.copyOf(strings);
diff --git a/android/guava-tests/test/com/google/common/collect/IteratorsTest.java b/android/guava-tests/test/com/google/common/collect/IteratorsTest.java
index afe22b8..15f7ccc 100644
--- a/android/guava-tests/test/com/google/common/collect/IteratorsTest.java
+++ b/android/guava-tests/test/com/google/common/collect/IteratorsTest.java
@@ -1279,8 +1279,7 @@
   }
 
   private static Enumeration<Integer> enumerate(Integer... ints) {
-    Vector<Integer> vector = new Vector<>();
-    vector.addAll(asList(ints));
+    Vector<Integer> vector = new Vector<>(asList(ints));
     return vector.elements();
   }
 
diff --git a/android/guava-tests/test/com/google/common/collect/LenientSerializableTester.java b/android/guava-tests/test/com/google/common/collect/LenientSerializableTester.java
index 03b35e0..ce3ec9d 100644
--- a/android/guava-tests/test/com/google/common/collect/LenientSerializableTester.java
+++ b/android/guava-tests/test/com/google/common/collect/LenientSerializableTester.java
@@ -24,6 +24,7 @@
 import com.google.common.annotations.GwtIncompatible;
 import com.google.common.testing.SerializableTester;
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import java.util.Collection;
 import java.util.Set;
 
 /**
@@ -60,5 +61,14 @@
     return copy;
   }
 
+  @CanIgnoreReturnValue
+  @GwtIncompatible // SerializableTester
+  static <E> Collection<E> reserializeAndAssertElementsEqual(Collection<E> original) {
+    Collection<E> copy = reserialize(original);
+    assertTrue(Iterables.elementsEqual(original, copy));
+    assertTrue(copy instanceof ImmutableCollection);
+    return copy;
+  }
+
   private LenientSerializableTester() {}
 }
diff --git a/android/guava-tests/test/com/google/common/collect/ListsImplTest.java b/android/guava-tests/test/com/google/common/collect/ListsImplTest.java
index 69d19ba..2d18f5a 100644
--- a/android/guava-tests/test/com/google/common/collect/ListsImplTest.java
+++ b/android/guava-tests/test/com/google/common/collect/ListsImplTest.java
@@ -17,6 +17,7 @@
 package com.google.common.collect;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import com.google.common.annotations.GwtCompatible;
 import com.google.common.annotations.GwtIncompatible;
@@ -138,7 +139,7 @@
     List<Object> unEqualItems =
         Arrays.asList(outOfOrder, diffValue, diffLength, empty, null, new Object());
     for (Object other : unEqualItems) {
-      assertThat(Lists.equalsImpl(base, other)).named("%s", other).isFalse();
+      assertWithMessage("%s", other).that(Lists.equalsImpl(base, other)).isFalse();
     }
   }
 
@@ -167,11 +168,11 @@
       int index = indexes.get(i);
       Iterable<String> iterableToAdd = toAdd.get(i);
       boolean expectedChanged = iterableToAdd.iterator().hasNext();
-      assertThat(Lists.addAllImpl(toTest, index, iterableToAdd))
-          .named(format, iterableToAdd, index)
+      assertWithMessage(format, iterableToAdd, index)
+          .that(Lists.addAllImpl(toTest, index, iterableToAdd))
           .isEqualTo(expectedChanged);
-      assertThat(toTest)
-          .named(format, iterableToAdd, index)
+      assertWithMessage(format, iterableToAdd, index)
+          .that(toTest)
           .containsExactlyElementsIn(expected.get(i));
     }
   }
@@ -216,7 +217,7 @@
     int index = 0;
     for (Object obj : toTest) {
       String name = "toTest[" + index + "] (" + obj + ")";
-      assertThat(Lists.indexOfImpl(toTest, obj)).named(name).isEqualTo(expected[index]);
+      assertWithMessage(name).that(Lists.indexOfImpl(toTest, obj)).isEqualTo(expected[index]);
       index++;
     }
   }
@@ -225,7 +226,7 @@
     int index = 0;
     for (Object obj : toTest) {
       String name = "toTest[" + index + "] (" + obj + ")";
-      assertThat(Lists.lastIndexOfImpl(toTest, obj)).named(name).isEqualTo(expected[index]);
+      assertWithMessage(name).that(Lists.lastIndexOfImpl(toTest, obj)).isEqualTo(expected[index]);
       index++;
     }
   }
diff --git a/android/guava-tests/test/com/google/common/collect/ListsTest.java b/android/guava-tests/test/com/google/common/collect/ListsTest.java
index 0d0e6f2..ef90e54 100644
--- a/android/guava-tests/test/com/google/common/collect/ListsTest.java
+++ b/android/guava-tests/test/com/google/common/collect/ListsTest.java
@@ -629,6 +629,16 @@
     assertEquals(actual.indexOf(list(1, 1, 1)), -1);
   }
 
+  public void testCartesianProduct_lastIndexOf() {
+    List<List<Integer>> actual = Lists.cartesianProduct(list(1, 1), list(2, 3));
+    assertThat(actual.lastIndexOf(list(1, 2))).isEqualTo(2);
+    assertThat(actual.lastIndexOf(list(1, 3))).isEqualTo(3);
+    assertThat(actual.lastIndexOf(list(1, 1))).isEqualTo(-1);
+
+    assertThat(actual.lastIndexOf(list(1))).isEqualTo(-1);
+    assertThat(actual.lastIndexOf(list(1, 1, 1))).isEqualTo(-1);
+  }
+
   @SuppressWarnings("unchecked") // varargs!
   public void testCartesianProduct_unrelatedTypes() {
     List<Integer> x = list(1, 2);
diff --git a/android/guava-tests/test/com/google/common/collect/MapMakerInternalMapTest.java b/android/guava-tests/test/com/google/common/collect/MapMakerInternalMapTest.java
index 535eed4..47fc74c 100644
--- a/android/guava-tests/test/com/google/common/collect/MapMakerInternalMapTest.java
+++ b/android/guava-tests/test/com/google/common/collect/MapMakerInternalMapTest.java
@@ -604,6 +604,7 @@
     assertNull(segment.get(key, hash));
   }
 
+  @SuppressWarnings("GuardedBy")
   public void testExpand() {
     MapMakerInternalMap<Object, Object, ?, ?> map =
         makeMap(createMapMaker().concurrencyLevel(1).initialCapacity(1));
@@ -629,6 +630,8 @@
 
     for (int i = 1; i <= originalCount * 2; i *= 2) {
       if (i > 1) {
+        // TODO(b/145386688): This access should be guarded by 'segment', which is not currently
+        // held
         segment.expand();
       }
       assertEquals(i, segment.table.length());
@@ -687,6 +690,7 @@
     assertNull(newFirst.getNext());
   }
 
+  @SuppressWarnings("GuardedBy")
   public void testExpand_cleanup() {
     MapMakerInternalMap<Object, Object, ?, ?> map =
         makeMap(createMapMaker().concurrencyLevel(1).initialCapacity(1));
@@ -719,6 +723,8 @@
 
     for (int i = 1; i <= originalCount * 2; i *= 2) {
       if (i > 1) {
+        // TODO(b/145386688): This access should be guarded by 'segment', which is not currently
+        // held
         segment.expand();
       }
       assertEquals(i, segment.table.length());
@@ -845,7 +851,7 @@
         InternalEntry<Object, Object, ?> entry = segment.getEntry(keyOne, hashOne);
 
         @SuppressWarnings("unchecked")
-        Reference<Object> reference = (Reference) entry;
+        Reference<Object> reference = (Reference<Object>) entry;
         reference.enqueue();
 
         map.put(keyTwo, valueTwo);
@@ -877,7 +883,7 @@
         WeakValueReference<Object, Object, ?> valueReference = entry.getValueReference();
 
         @SuppressWarnings("unchecked")
-        Reference<Object> reference = (Reference) valueReference;
+        Reference<Object> reference = (Reference<Object>) valueReference;
         reference.enqueue();
 
         map.put(keyTwo, valueTwo);
@@ -905,7 +911,7 @@
         InternalEntry<Object, Object, ?> entry = segment.getEntry(keyOne, hashOne);
 
         @SuppressWarnings("unchecked")
-        Reference<Object> reference = (Reference) entry;
+        Reference<Object> reference = (Reference<Object>) entry;
         reference.enqueue();
 
         for (int i = 0; i < SMALL_MAX_SIZE; i++) {
@@ -938,7 +944,7 @@
         WeakValueReference<Object, Object, ?> valueReference = entry.getValueReference();
 
         @SuppressWarnings("unchecked")
-        Reference<Object> reference = (Reference) valueReference;
+        Reference<Object> reference = (Reference<Object>) valueReference;
         reference.enqueue();
 
         for (int i = 0; i < SMALL_MAX_SIZE; i++) {
diff --git a/android/guava-tests/test/com/google/common/collect/MapsTest.java b/android/guava-tests/test/com/google/common/collect/MapsTest.java
index 8ea3475..76394c8 100644
--- a/android/guava-tests/test/com/google/common/collect/MapsTest.java
+++ b/android/guava-tests/test/com/google/common/collect/MapsTest.java
@@ -21,6 +21,7 @@
 import static com.google.common.collect.Maps.unmodifiableNavigableMap;
 import static com.google.common.collect.testing.Helpers.mapEntry;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static java.util.Arrays.asList;
 
 import com.google.common.annotations.GwtCompatible;
@@ -155,8 +156,8 @@
     for (int i = 1; i < size; i++) {
       map1.put(i, null);
     }
-    assertThat(bucketsOf(map1))
-        .named("table size after adding " + size + " elements")
+    assertWithMessage("table size after adding " + size + " elements")
+        .that(bucketsOf(map1))
         .isEqualTo(initialBuckets);
 
     /*
@@ -164,8 +165,8 @@
      * once; make sure that passes too.
      */
     map2.putAll(map1);
-    assertThat(bucketsOf(map1))
-        .named("table size after adding " + size + " elements")
+    assertWithMessage("table size after adding " + size + " elements")
+        .that(bucketsOf(map1))
         .isEqualTo(initialBuckets);
   }
 
@@ -244,6 +245,8 @@
     assertEquals(original, map);
   }
 
+  // Intentionally using IdentityHashMap to test creation.
+  @SuppressWarnings("IdentityHashMapBoxing")
   public void testIdentityHashMap() {
     IdentityHashMap<Integer, Integer> map = Maps.newIdentityHashMap();
     assertEquals(Collections.emptyMap(), map);
diff --git a/android/guava-tests/test/com/google/common/collect/QueuesTest.java b/android/guava-tests/test/com/google/common/collect/QueuesTest.java
index e85051b..66d99fe 100644
--- a/android/guava-tests/test/com/google/common/collect/QueuesTest.java
+++ b/android/guava-tests/test/com/google/common/collect/QueuesTest.java
@@ -77,7 +77,7 @@
   @Override
   public void tearDown() throws InterruptedException {
     threadPool.shutdown();
-    assertTrue("Some worker didn't finish in time", threadPool.awaitTermination(1, SECONDS));
+    assertTrue("Some worker didn't finish in time", threadPool.awaitTermination(10, SECONDS));
   }
 
   private static <T> int drain(
diff --git a/android/guava-tests/test/com/google/common/collect/RangeTest.java b/android/guava-tests/test/com/google/common/collect/RangeTest.java
index d76b96f..9a2cd74 100644
--- a/android/guava-tests/test/com/google/common/collect/RangeTest.java
+++ b/android/guava-tests/test/com/google/common/collect/RangeTest.java
@@ -23,6 +23,7 @@
 import static java.util.Arrays.asList;
 
 import com.google.common.annotations.GwtCompatible;
+import com.google.common.annotations.GwtIncompatible;
 import com.google.common.base.Predicate;
 import com.google.common.collect.testing.Helpers;
 import com.google.common.testing.EqualsTester;
@@ -37,7 +38,7 @@
  *
  * @author Kevin Bourrillion
  */
-@GwtCompatible
+@GwtCompatible(emulated = true)
 public class RangeTest extends TestCase {
   public void testOpen() {
     Range<Integer> range = Range.open(4, 8);
@@ -118,6 +119,8 @@
 
   public void testIsConnected() {
     assertTrue(Range.closed(3, 5).isConnected(Range.open(5, 6)));
+    assertTrue(Range.closed(3, 5).isConnected(Range.closed(5, 6)));
+    assertTrue(Range.closed(5, 6).isConnected(Range.closed(3, 5)));
     assertTrue(Range.closed(3, 5).isConnected(Range.openClosed(5, 5)));
     assertTrue(Range.open(3, 5).isConnected(Range.closed(5, 6)));
     assertTrue(Range.closed(3, 7).isConnected(Range.open(6, 8)));
@@ -471,6 +474,32 @@
     }
   }
 
+  public void testGap_invalidRangesWithInfinity() {
+    try {
+      Range.atLeast(1).gap(Range.atLeast(2));
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+
+    try {
+      Range.atLeast(2).gap(Range.atLeast(1));
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+
+    try {
+      Range.atMost(1).gap(Range.atMost(2));
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+
+    try {
+      Range.atMost(2).gap(Range.atMost(1));
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
   public void testGap_connectedAdjacentYieldsEmpty() {
     Range<Integer> range = Range.open(3, 4);
 
@@ -499,6 +528,8 @@
     assertEquals(Range.open(2, 4), closedRange.gap(Range.atMost(2)));
   }
 
+  // TODO(cpovirk): More extensive testing of gap().
+
   public void testSpan_general() {
     Range<Integer> range = Range.closed(4, 8);
 
@@ -568,6 +599,7 @@
         .testEquals();
   }
 
+  @GwtIncompatible // TODO(b/148207871): Restore once Eclipse compiler no longer flakes for this.
   public void testLegacyComparable() {
     Range<LegacyComparable> range = Range.closed(LegacyComparable.X, LegacyComparable.Y);
   }
diff --git a/android/guava-tests/test/com/google/common/collect/SetsTest.java b/android/guava-tests/test/com/google/common/collect/SetsTest.java
index 1873a14..8aa8d83 100644
--- a/android/guava-tests/test/com/google/common/collect/SetsTest.java
+++ b/android/guava-tests/test/com/google/common/collect/SetsTest.java
@@ -24,6 +24,7 @@
 import static com.google.common.collect.Sets.unmodifiableNavigableSet;
 import static com.google.common.collect.testing.IteratorFeature.UNMODIFIABLE;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static java.io.ObjectStreamConstants.TC_REFERENCE;
 import static java.io.ObjectStreamConstants.baseWireHandle;
 import static java.util.Collections.emptySet;
@@ -948,6 +949,13 @@
     }
   }
 
+  public void testPowerSetEquals_independentOfOrder() {
+    ImmutableSet<Integer> elements = ImmutableSet.of(1, 2, 3, 4);
+    Set<Set<Integer>> forward = powerSet(elements);
+    Set<Set<Integer>> reverse = powerSet(ImmutableSet.copyOf(elements.asList().reverse()));
+    new EqualsTester().addEqualityGroup(forward, reverse).testEquals();
+  }
+
   /**
    * Test that a hash code miscomputed by "input.hashCode() * tooFarValue / 2" is correct under our
    * {@code hashCode} implementation.
@@ -1032,8 +1040,8 @@
                     return input.size() == size;
                   }
                 });
-        assertThat(Sets.combinations(sampleSet, k))
-            .named("Sets.combinations(%s, %s)", sampleSet, k)
+        assertWithMessage("Sets.combinations(%s, %s)", sampleSet, k)
+            .that(Sets.combinations(sampleSet, k))
             .containsExactlyElementsIn(expected)
             .inOrder();
       }
diff --git a/android/guava-tests/test/com/google/common/collect/SynchronizedDequeTest.java b/android/guava-tests/test/com/google/common/collect/SynchronizedDequeTest.java
index 1c8de93..0a11b27 100644
--- a/android/guava-tests/test/com/google/common/collect/SynchronizedDequeTest.java
+++ b/android/guava-tests/test/com/google/common/collect/SynchronizedDequeTest.java
@@ -263,8 +263,8 @@
     create().add("foo");
     create().addAll(ImmutableList.of("foo"));
     create().clear();
-    create().contains("foo");
-    create().containsAll(ImmutableList.of("foo"));
+    boolean unused = create().contains("foo");
+    boolean unused2 = create().containsAll(ImmutableList.of("foo"));
     create().equals(new ArrayDeque<>(ImmutableList.of("foo")));
     create().hashCode();
     create().isEmpty();
diff --git a/android/guava-tests/test/com/google/common/collect/SynchronizedMapTest.java b/android/guava-tests/test/com/google/common/collect/SynchronizedMapTest.java
index 140720c..34d1c6f 100644
--- a/android/guava-tests/test/com/google/common/collect/SynchronizedMapTest.java
+++ b/android/guava-tests/test/com/google/common/collect/SynchronizedMapTest.java
@@ -175,11 +175,11 @@
   }
 
   public void testContainsKey() {
-    create().containsKey(null);
+    boolean unused = create().containsKey(null);
   }
 
   public void testContainsValue() {
-    create().containsValue(null);
+    boolean unused = create().containsValue(null);
   }
 
   public void testGet() {
diff --git a/android/guava-tests/test/com/google/common/collect/SynchronizedQueueTest.java b/android/guava-tests/test/com/google/common/collect/SynchronizedQueueTest.java
index a29c00f..70ff774 100644
--- a/android/guava-tests/test/com/google/common/collect/SynchronizedQueueTest.java
+++ b/android/guava-tests/test/com/google/common/collect/SynchronizedQueueTest.java
@@ -161,8 +161,8 @@
     create().add("foo");
     create().addAll(ImmutableList.of("foo"));
     create().clear();
-    create().contains("foo");
-    create().containsAll(ImmutableList.of("foo"));
+    boolean unused = create().contains("foo");
+    boolean unused2 = create().containsAll(ImmutableList.of("foo"));
     create().equals(new ArrayDeque<>(ImmutableList.of("foo")));
     create().hashCode();
     create().isEmpty();
diff --git a/android/guava-tests/test/com/google/common/collect/TreeRangeMapTest.java b/android/guava-tests/test/com/google/common/collect/TreeRangeMapTest.java
index 213b4fa..d82633a 100644
--- a/android/guava-tests/test/com/google/common/collect/TreeRangeMapTest.java
+++ b/android/guava-tests/test/com/google/common/collect/TreeRangeMapTest.java
@@ -489,6 +489,20 @@
     assertEquals(ImmutableMap.of(Range.closedOpen(0, 2), 1), rangeMap.asMapOfRanges());
   }
 
+  public void testPutCoalescingSubmapEmpty() {
+    RangeMap<Integer, Integer> rangeMap = TreeRangeMap.create();
+    rangeMap.put(Range.closedOpen(0, 1), 1);
+    rangeMap.put(Range.closedOpen(1, 2), 1);
+    assertEquals(
+        ImmutableMap.of(Range.closedOpen(0, 1), 1, Range.closedOpen(1, 2), 1),
+        rangeMap.asMapOfRanges());
+
+    RangeMap<Integer, Integer> subRangeMap = rangeMap.subRangeMap(Range.closedOpen(0, 2));
+    subRangeMap.putCoalescing(Range.closedOpen(1, 1), 1); // empty range coalesces connected ranges
+    assertEquals(ImmutableMap.of(Range.closedOpen(0, 2), 1), subRangeMap.asMapOfRanges());
+    assertEquals(ImmutableMap.of(Range.closedOpen(0, 2), 1), rangeMap.asMapOfRanges());
+  }
+
   public void testPutCoalescingComplex() {
     // {[0..1): 1, [1..3): 1, [3..5): 1, [7..10): 2, [12..15): 2, [18..19): 3}
     RangeMap<Integer, Integer> rangeMap = TreeRangeMap.create();
diff --git a/android/guava-tests/test/com/google/common/collect/TreeRangeSetTest.java b/android/guava-tests/test/com/google/common/collect/TreeRangeSetTest.java
index d033e24..498a1a1 100644
--- a/android/guava-tests/test/com/google/common/collect/TreeRangeSetTest.java
+++ b/android/guava-tests/test/com/google/common/collect/TreeRangeSetTest.java
@@ -285,6 +285,19 @@
     }
   }
 
+  public void testSubRangeSetAdd() {
+    TreeRangeSet<Integer> set = TreeRangeSet.create();
+    Range<Integer> range = Range.closedOpen(0, 5);
+    set.subRangeSet(range).add(range);
+  }
+
+  public void testSubRangeSetReplaceAdd() {
+    TreeRangeSet<Integer> set = TreeRangeSet.create();
+    Range<Integer> range = Range.closedOpen(0, 5);
+    set.add(range);
+    set.subRangeSet(range).add(range);
+  }
+
   public void testComplement() {
     for (Range<Integer> range1 : QUERY_RANGES) {
       for (Range<Integer> range2 : QUERY_RANGES) {
diff --git a/android/guava-tests/test/com/google/common/eventbus/EventBusTest.java b/android/guava-tests/test/com/google/common/eventbus/EventBusTest.java
index 647663c..249d3b0 100644
--- a/android/guava-tests/test/com/google/common/eventbus/EventBusTest.java
+++ b/android/guava-tests/test/com/google/common/eventbus/EventBusTest.java
@@ -289,6 +289,18 @@
     assertEquals(1, calls.get());
   }
 
+  public void testPrimitiveSubscribeFails() {
+    class SubscribesToPrimitive {
+      @Subscribe
+      public void toInt(int i) {}
+    }
+    try {
+      bus.register(new SubscribesToPrimitive());
+      fail("should have thrown");
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
   /** Records thrown exception information. */
   private static final class RecordingSubscriberExceptionHandler
       implements SubscriberExceptionHandler {
diff --git a/android/guava-tests/test/com/google/common/graph/AbstractDirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/AbstractDirectedGraphTest.java
deleted file mode 100644
index e8b8a43..0000000
--- a/android/guava-tests/test/com/google/common/graph/AbstractDirectedGraphTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.graph.GraphConstants.ENDPOINTS_MISMATCH;
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-import org.junit.Test;
-
-/**
- * Abstract base class for testing implementations of {@link Graph} interface.
- *
- * <p>This class is responsible for testing that a directed implementation of {@link Graph} is
- * correctly handling directed edges. Implementation-dependent test cases are left to subclasses.
- * Test cases that do not require the graph to be directed are found in superclasses.
- */
-public abstract class AbstractDirectedGraphTest extends AbstractGraphTest {
-  @Test
-  public void predecessors_oneEdge() {
-    putEdge(N1, N2);
-    assertThat(graph.predecessors(N2)).containsExactly(N1);
-    // Edge direction handled correctly
-    assertThat(graph.predecessors(N1)).isEmpty();
-  }
-
-  @Test
-  public void successors_oneEdge() {
-    putEdge(N1, N2);
-    assertThat(graph.successors(N1)).containsExactly(N2);
-    // Edge direction handled correctly
-    assertThat(graph.successors(N2)).isEmpty();
-  }
-
-  @Test
-  public void incidentEdges_oneEdge() {
-    putEdge(N1, N2);
-    EndpointPair<Integer> expectedEndpoints = EndpointPair.ordered(N1, N2);
-    assertThat(graph.incidentEdges(N1)).containsExactly(expectedEndpoints);
-    assertThat(graph.incidentEdges(N2)).containsExactly(expectedEndpoints);
-  }
-
-  @Test
-  public void inDegree_oneEdge() {
-    putEdge(N1, N2);
-    assertThat(graph.inDegree(N2)).isEqualTo(1);
-    // Edge direction handled correctly
-    assertThat(graph.inDegree(N1)).isEqualTo(0);
-  }
-
-  @Test
-  public void outDegree_oneEdge() {
-    putEdge(N1, N2);
-    assertThat(graph.outDegree(N1)).isEqualTo(1);
-    // Edge direction handled correctly
-    assertThat(graph.outDegree(N2)).isEqualTo(0);
-  }
-
-  @Test
-  public void hasEdgeConnecting_correct() {
-    putEdge(N1, N2);
-    assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(N1, N2))).isTrue();
-  }
-
-  @Test
-  public void hasEdgeConnecting_backwards() {
-    putEdge(N1, N2);
-    assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(N2, N1))).isFalse();
-  }
-
-  @Test
-  public void hasEdgeConnecting_mismatch() {
-    putEdge(N1, N2);
-    assertThat(graph.hasEdgeConnecting(EndpointPair.unordered(N1, N2))).isFalse();
-    assertThat(graph.hasEdgeConnecting(EndpointPair.unordered(N2, N1))).isFalse();
-  }
-
-  // Element Mutation
-
-  @Test
-  public void putEdge_existingNodes() {
-    // Adding nodes initially for safety (insulating from possible future
-    // modifications to proxy methods)
-    addNode(N1);
-    addNode(N2);
-    assertThat(putEdge(N1, N2)).isTrue();
-  }
-
-  @Test
-  public void putEdge_existingEdgeBetweenSameNodes() {
-    assertThat(putEdge(N1, N2)).isTrue();
-    assertThat(putEdge(N1, N2)).isFalse();
-  }
-
-  @Test
-  public void putEdge_orderMismatch() {
-    EndpointPair<Integer> endpoints = EndpointPair.unordered(N1, N2);
-    try {
-      putEdge(endpoints);
-      fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH);
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH);
-    }
-  }
-
-  public void removeEdge_antiparallelEdges() {
-    putEdge(N1, N2);
-    putEdge(N2, N1);
-
-    assertThat(graph.removeEdge(N1, N2)).isTrue();
-    assertThat(graph.successors(N1)).isEmpty();
-    assertThat(graph.predecessors(N1)).containsExactly(N2);
-    assertThat(graph.edges()).hasSize(1);
-
-    assertThat(graph.removeEdge(N2, N1)).isTrue();
-    assertThat(graph.successors(N1)).isEmpty();
-    assertThat(graph.predecessors(N1)).isEmpty();
-    assertThat(graph.edges()).isEmpty();
-  }
-
-  @Test
-  public void removeEdge_orderMismatch() {
-    putEdge(N1, N2);
-    EndpointPair<Integer> endpoints = EndpointPair.unordered(N1, N2);
-    try {
-      graph.removeEdge(endpoints);
-      fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH);
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH);
-    }
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/AbstractDirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/AbstractDirectedNetworkTest.java
deleted file mode 100644
index 6111f82..0000000
--- a/android/guava-tests/test/com/google/common/graph/AbstractDirectedNetworkTest.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.graph.GraphConstants.ENDPOINTS_MISMATCH;
-import static com.google.common.graph.TestUtil.assertEdgeNotInGraphErrorMessage;
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-import com.google.common.collect.ImmutableSet;
-import java.util.Collections;
-import java.util.Set;
-import org.junit.After;
-import org.junit.Test;
-
-/**
- * Abstract base class for testing implementations of {@link Network} interface.
- *
- * <p>This class is responsible for testing that a directed implementation of {@link Network} is
- * correctly handling directed edges. Implementation-dependent test cases are left to subclasses.
- * Test cases that do not require the graph to be directed are found in superclasses.
- */
-public abstract class AbstractDirectedNetworkTest extends AbstractNetworkTest {
-
-  @After
-  public void validateSourceAndTarget() {
-    for (Integer node : network.nodes()) {
-      for (String inEdge : network.inEdges(node)) {
-        EndpointPair<Integer> endpointPair = network.incidentNodes(inEdge);
-        assertThat(endpointPair.source()).isEqualTo(endpointPair.adjacentNode(node));
-        assertThat(endpointPair.target()).isEqualTo(node);
-      }
-
-      for (String outEdge : network.outEdges(node)) {
-        EndpointPair<Integer> endpointPair = network.incidentNodes(outEdge);
-        assertThat(endpointPair.source()).isEqualTo(node);
-        assertThat(endpointPair.target()).isEqualTo(endpointPair.adjacentNode(node));
-      }
-
-      for (Integer adjacentNode : network.adjacentNodes(node)) {
-        Set<String> edges = network.edgesConnecting(node, adjacentNode);
-        Set<String> antiParallelEdges = network.edgesConnecting(adjacentNode, node);
-        assertThat(node.equals(adjacentNode) || Collections.disjoint(edges, antiParallelEdges))
-            .isTrue();
-      }
-    }
-  }
-
-  @Test
-  public void edges_containsOrderMismatch() {
-    addEdge(N1, N2, E12);
-    EndpointPair<Integer> endpointsN1N2 = EndpointPair.unordered(N1, N2);
-    EndpointPair<Integer> endpointsN2N1 = EndpointPair.unordered(N2, N1);
-    assertThat(network.asGraph().edges()).doesNotContain(endpointsN1N2);
-    assertThat(network.asGraph().edges()).doesNotContain(endpointsN2N1);
-  }
-
-  @Test
-  public void edgesConnecting_orderMismatch() {
-    addEdge(N1, N2, E12);
-    try {
-      Set<String> unused = network.edgesConnecting(EndpointPair.unordered(N1, N2));
-      fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH);
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH);
-    }
-  }
-
-  @Test
-  public void edgeConnectingOrNull_orderMismatch() {
-    addEdge(N1, N2, E12);
-    try {
-      String unused = network.edgeConnectingOrNull(EndpointPair.unordered(N1, N2));
-      fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH);
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH);
-    }
-  }
-
-  @Override
-  @Test
-  public void incidentNodes_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.incidentNodes(E12).source()).isEqualTo(N1);
-    assertThat(network.incidentNodes(E12).target()).isEqualTo(N2);
-  }
-
-  @Test
-  public void edgesConnecting_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
-    // Passed nodes should be in the correct edge direction, first is the
-    // source node and the second is the target node
-    assertThat(network.edgesConnecting(N2, N1)).isEmpty();
-  }
-
-  @Test
-  public void inEdges_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.inEdges(N2)).containsExactly(E12);
-    // Edge direction handled correctly
-    assertThat(network.inEdges(N1)).isEmpty();
-  }
-
-  @Test
-  public void outEdges_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.outEdges(N1)).containsExactly(E12);
-    // Edge direction handled correctly
-    assertThat(network.outEdges(N2)).isEmpty();
-  }
-
-  @Test
-  public void predecessors_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.predecessors(N2)).containsExactly(N1);
-    // Edge direction handled correctly
-    assertThat(network.predecessors(N1)).isEmpty();
-  }
-
-  @Test
-  public void successors_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.successors(N1)).containsExactly(N2);
-    // Edge direction handled correctly
-    assertThat(network.successors(N2)).isEmpty();
-  }
-
-  @Test
-  public void source_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.incidentNodes(E12).source()).isEqualTo(N1);
-  }
-
-  @Test
-  public void source_edgeNotInGraph() {
-    try {
-      network.incidentNodes(EDGE_NOT_IN_GRAPH).source();
-      fail(ERROR_EDGE_NOT_IN_GRAPH);
-    } catch (IllegalArgumentException e) {
-      assertEdgeNotInGraphErrorMessage(e);
-    }
-  }
-
-  @Test
-  public void target_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.incidentNodes(E12).target()).isEqualTo(N2);
-  }
-
-  @Test
-  public void target_edgeNotInGraph() {
-    try {
-      network.incidentNodes(EDGE_NOT_IN_GRAPH).target();
-      fail(ERROR_EDGE_NOT_IN_GRAPH);
-    } catch (IllegalArgumentException e) {
-      assertEdgeNotInGraphErrorMessage(e);
-    }
-  }
-
-  @Test
-  public void inDegree_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.inDegree(N2)).isEqualTo(1);
-    // Edge direction handled correctly
-    assertThat(network.inDegree(N1)).isEqualTo(0);
-  }
-
-  @Test
-  public void outDegree_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.outDegree(N1)).isEqualTo(1);
-    // Edge direction handled correctly
-    assertThat(network.outDegree(N2)).isEqualTo(0);
-  }
-
-  // Element Mutation
-
-  @Test
-  public void addEdge_existingNodes() {
-    // Adding nodes initially for safety (insulating from possible future
-    // modifications to proxy methods)
-    addNode(N1);
-    addNode(N2);
-    assertThat(addEdge(N1, N2, E12)).isTrue();
-    assertThat(network.edges()).contains(E12);
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
-    // Direction of the added edge is correctly handled
-    assertThat(network.edgesConnecting(N2, N1)).isEmpty();
-  }
-
-  @Test
-  public void addEdge_existingEdgeBetweenSameNodes() {
-    addEdge(N1, N2, E12);
-    ImmutableSet<String> edges = ImmutableSet.copyOf(network.edges());
-    assertThat(addEdge(N1, N2, E12)).isFalse();
-    assertThat(network.edges()).containsExactlyElementsIn(edges);
-  }
-
-  @Test
-  public void addEdge_existingEdgeBetweenDifferentNodes() {
-    addEdge(N1, N2, E12);
-    try {
-      // Edge between totally different nodes
-      addEdge(N4, N5, E12);
-      fail(ERROR_ADDED_EXISTING_EDGE);
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE);
-    }
-    try {
-      // Edge between same nodes but in reverse direction
-      addEdge(N2, N1, E12);
-      fail(ERROR_ADDED_EXISTING_EDGE);
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE);
-    }
-  }
-
-  @Test
-  public void addEdge_parallelEdge() {
-    addEdge(N1, N2, E12);
-    try {
-      addEdge(N1, N2, EDGE_NOT_IN_GRAPH);
-      fail(ERROR_ADDED_PARALLEL_EDGE);
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessageThat().contains(ERROR_PARALLEL_EDGE);
-    }
-  }
-
-  @Test
-  public void addEdge_orderMismatch() {
-    EndpointPair<Integer> endpoints = EndpointPair.unordered(N1, N2);
-    try {
-      addEdge(endpoints, E12);
-      fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH);
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH);
-    }
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/AbstractGraphTest.java b/android/guava-tests/test/com/google/common/graph/AbstractGraphTest.java
index 2f81895..3a489a1 100644
--- a/android/guava-tests/test/com/google/common/graph/AbstractGraphTest.java
+++ b/android/guava-tests/test/com/google/common/graph/AbstractGraphTest.java
@@ -21,10 +21,10 @@
 import static com.google.common.graph.TestUtil.assertStronglyEquivalent;
 import static com.google.common.graph.TestUtil.sanityCheckSet;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
 import static org.junit.Assert.fail;
 
 import com.google.common.collect.ImmutableSet;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import java.util.HashSet;
 import java.util.Set;
 import org.junit.After;
@@ -40,7 +40,7 @@
  * graph. The following test cases are left for the subclasses to handle:
  *
  * <ul>
- *   <li>Test cases related to whether the graph is directed, undirected, mutable, or immutable.
+ *   <li>Test cases related to whether the graph is directed or undirected.
  *   <li>Test cases related to the specific implementation of the {@link Graph} interface.
  * </ul>
  *
@@ -48,7 +48,15 @@
  * TODO(user): Differentiate between directed and undirected edge strings.
  */
 public abstract class AbstractGraphTest {
-  MutableGraph<Integer> graph;
+
+  Graph<Integer> graph;
+
+  /**
+   * The same reference as {@link #graph}, except as a mutable graph. This field is null in case
+   * {@link #createGraph()} didn't return a mutable graph.
+   */
+  MutableGraph<Integer> graphAsMutableGraph;
+
   static final Integer N1 = 1;
   static final Integer N2 = 2;
   static final Integer N3 = 3;
@@ -66,57 +74,36 @@
   static final String ERROR_ADDED_SELF_LOOP = "Should not be allowed to add a self-loop edge.";
 
   /** Creates and returns an instance of the graph to be tested. */
-  public abstract MutableGraph<Integer> createGraph();
+  abstract Graph<Integer> createGraph();
 
   /**
    * A proxy method that adds the node {@code n} to the graph being tested. In case of Immutable
-   * graph implementations, this method should add {@code n} to the graph builder and build a new
-   * graph with the current builder state.
-   *
-   * @return {@code true} iff the graph was modified as a result of this call
+   * graph implementations, this method should replace {@link #graph} with a new graph that includes
+   * this node.
    */
-  @CanIgnoreReturnValue
-  protected boolean addNode(Integer n) {
-    return graph.addNode(n);
-  }
+  abstract void addNode(Integer n);
 
   /**
    * A proxy method that adds the edge {@code e} to the graph being tested. In case of Immutable
-   * graph implementations, this method should add {@code e} to the graph builder and build a new
-   * graph with the current builder state.
-   *
-   * <p>This method should be used in tests of specific implementations if you want to ensure
-   * uniform behavior (including side effects) with how edges are added elsewhere in the tests. For
-   * example, the existing implementations of this method explicitly add the supplied nodes to the
-   * graph, and then call {@code graph.addEdge()} to connect the edge to the nodes; this is not part
-   * of the contract of {@code graph.addEdge()} and is done for convenience. In cases where you want
-   * to avoid such side effects (e.g., if you're testing what happens in your implementation if you
-   * add an edge whose end-points don't already exist in the graph), you should <b>not</b> use this
-   * method.
-   *
-   * @return {@code true} iff the graph was modified as a result of this call
+   * graph implementations, this method should replace {@link #graph} with a new graph that includes
+   * this edge.
    */
-  @CanIgnoreReturnValue
-  protected boolean putEdge(Integer n1, Integer n2) {
-    graph.addNode(n1);
-    graph.addNode(n2);
-    return graph.putEdge(n1, n2);
-  }
+  abstract void putEdge(Integer n1, Integer n2);
 
-  @CanIgnoreReturnValue
-  protected boolean putEdge(EndpointPair<Integer> endpoints) {
-    graph.addNode(endpoints.nodeU());
-    graph.addNode(endpoints.nodeV());
-    return graph.putEdge(endpoints);
+  final boolean graphIsMutable() {
+    return graphAsMutableGraph != null;
   }
 
   @Before
-  public void init() {
+  public final void init() {
     graph = createGraph();
+    if (graph instanceof MutableGraph) {
+      graphAsMutableGraph = (MutableGraph<Integer>) graph;
+    }
   }
 
   @After
-  public void validateGraphState() {
+  public final void validateGraphState() {
     validateGraph(graph);
   }
 
@@ -361,24 +348,30 @@
 
   @Test
   public void addNode_newNode() {
-    assertThat(addNode(N1)).isTrue();
+    assume().that(graphIsMutable()).isTrue();
+
+    assertThat(graphAsMutableGraph.addNode(N1)).isTrue();
     assertThat(graph.nodes()).contains(N1);
   }
 
   @Test
   public void addNode_existingNode() {
+    assume().that(graphIsMutable()).isTrue();
+
     addNode(N1);
     ImmutableSet<Integer> nodes = ImmutableSet.copyOf(graph.nodes());
-    assertThat(addNode(N1)).isFalse();
+    assertThat(graphAsMutableGraph.addNode(N1)).isFalse();
     assertThat(graph.nodes()).containsExactlyElementsIn(nodes);
   }
 
   @Test
   public void removeNode_existingNode() {
+    assume().that(graphIsMutable()).isTrue();
+
     putEdge(N1, N2);
     putEdge(N4, N1);
-    assertThat(graph.removeNode(N1)).isTrue();
-    assertThat(graph.removeNode(N1)).isFalse();
+    assertThat(graphAsMutableGraph.removeNode(N1)).isTrue();
+    assertThat(graphAsMutableGraph.removeNode(N1)).isFalse();
     assertThat(graph.nodes()).containsExactly(N2, N4);
     assertThat(graph.adjacentNodes(N2)).isEmpty();
     assertThat(graph.adjacentNodes(N4)).isEmpty();
@@ -386,32 +379,38 @@
 
   @Test
   public void removeNode_antiparallelEdges() {
+    assume().that(graphIsMutable()).isTrue();
+
     putEdge(N1, N2);
     putEdge(N2, N1);
 
-    assertThat(graph.removeNode(N1)).isTrue();
+    assertThat(graphAsMutableGraph.removeNode(N1)).isTrue();
     assertThat(graph.nodes()).containsExactly(N2);
     assertThat(graph.edges()).isEmpty();
 
-    assertThat(graph.removeNode(N2)).isTrue();
+    assertThat(graphAsMutableGraph.removeNode(N2)).isTrue();
     assertThat(graph.nodes()).isEmpty();
     assertThat(graph.edges()).isEmpty();
   }
 
   @Test
   public void removeNode_nodeNotPresent() {
+    assume().that(graphIsMutable()).isTrue();
+
     addNode(N1);
     ImmutableSet<Integer> nodes = ImmutableSet.copyOf(graph.nodes());
-    assertThat(graph.removeNode(NODE_NOT_IN_GRAPH)).isFalse();
+    assertThat(graphAsMutableGraph.removeNode(NODE_NOT_IN_GRAPH)).isFalse();
     assertThat(graph.nodes()).containsExactlyElementsIn(nodes);
   }
 
   @Test
   public void removeNode_queryAfterRemoval() {
+    assume().that(graphIsMutable()).isTrue();
+
     addNode(N1);
     @SuppressWarnings("unused")
     Set<Integer> unused = graph.adjacentNodes(N1); // ensure cache (if any) is populated
-    assertThat(graph.removeNode(N1)).isTrue();
+    assertThat(graphAsMutableGraph.removeNode(N1)).isTrue();
     try {
       graph.adjacentNodes(N1);
       fail(ERROR_NODE_NOT_IN_GRAPH);
@@ -422,36 +421,45 @@
 
   @Test
   public void removeEdge_existingEdge() {
+    assume().that(graphIsMutable()).isTrue();
+
     putEdge(N1, N2);
     assertThat(graph.successors(N1)).containsExactly(N2);
     assertThat(graph.predecessors(N2)).containsExactly(N1);
-    assertThat(graph.removeEdge(N1, N2)).isTrue();
-    assertThat(graph.removeEdge(N1, N2)).isFalse();
+    assertThat(graphAsMutableGraph.removeEdge(N1, N2)).isTrue();
+    assertThat(graphAsMutableGraph.removeEdge(N1, N2)).isFalse();
     assertThat(graph.successors(N1)).isEmpty();
     assertThat(graph.predecessors(N2)).isEmpty();
   }
 
   @Test
   public void removeEdge_oneOfMany() {
+    assume().that(graphIsMutable()).isTrue();
+
     putEdge(N1, N2);
     putEdge(N1, N3);
     putEdge(N1, N4);
-    assertThat(graph.removeEdge(N1, N3)).isTrue();
+    assertThat(graphAsMutableGraph.removeEdge(N1, N3)).isTrue();
     assertThat(graph.adjacentNodes(N1)).containsExactly(N2, N4);
   }
 
   @Test
   public void removeEdge_nodeNotPresent() {
+    assume().that(graphIsMutable()).isTrue();
+
     putEdge(N1, N2);
-    assertThat(graph.removeEdge(N1, NODE_NOT_IN_GRAPH)).isFalse();
+    assertThat(graphAsMutableGraph.removeEdge(N1, NODE_NOT_IN_GRAPH)).isFalse();
     assertThat(graph.successors(N1)).contains(N2);
   }
 
   @Test
   public void removeEdge_edgeNotPresent() {
+    assume().that(graphIsMutable()).isTrue();
+
     putEdge(N1, N2);
     addNode(N3);
-    assertThat(graph.removeEdge(N1, N3)).isFalse();
+
+    assertThat(graphAsMutableGraph.removeEdge(N1, N3)).isFalse();
     assertThat(graph.successors(N1)).contains(N2);
   }
 }
diff --git a/android/guava-tests/test/com/google/common/graph/AbstractNetworkTest.java b/android/guava-tests/test/com/google/common/graph/AbstractNetworkTest.java
index 9154a73..e386252 100644
--- a/android/guava-tests/test/com/google/common/graph/AbstractNetworkTest.java
+++ b/android/guava-tests/test/com/google/common/graph/AbstractNetworkTest.java
@@ -22,14 +22,20 @@
 import static com.google.common.graph.TestUtil.assertStronglyEquivalent;
 import static com.google.common.graph.TestUtil.sanityCheckSet;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+import static java.util.concurrent.Executors.newFixedThreadPool;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -51,7 +57,15 @@
  * TODO(user): Differentiate between directed and undirected edge strings.
  */
 public abstract class AbstractNetworkTest {
-  MutableNetwork<Integer, String> network;
+
+  Network<Integer, String> network;
+
+  /**
+   * The same reference as {@link #network}, except as a mutable network. This field is null in case
+   * {@link #createGraph()} didn't return a mutable network.
+   */
+  MutableNetwork<Integer, String> networkAsMutableNetwork;
+
   static final Integer N1 = 1;
   static final Integer N2 = 2;
   static final Integer N3 = 3;
@@ -92,54 +106,32 @@
       "Reusing an existing edge to connect different nodes succeeded";
 
   /** Creates and returns an instance of the graph to be tested. */
-  public abstract MutableNetwork<Integer, String> createGraph();
+  abstract Network<Integer, String> createGraph();
 
   /**
    * A proxy method that adds the node {@code n} to the graph being tested. In case of Immutable
-   * graph implementations, this method should add {@code n} to the graph builder and build a new
-   * graph with the current builder state.
-   *
-   * @return {@code true} iff the graph was modified as a result of this call
+   * graph implementations, this method should replace {@link #network} with a new graph that
+   * includes this node.
    */
-  @CanIgnoreReturnValue
-  protected boolean addNode(Integer n) {
-    return network.addNode(n);
-  }
+  abstract void addNode(Integer n);
 
   /**
    * A proxy method that adds the edge {@code e} to the graph being tested. In case of Immutable
-   * graph implementations, this method should add {@code e} to the graph builder and build a new
-   * graph with the current builder state.
-   *
-   * <p>This method should be used in tests of specific implementations if you want to ensure
-   * uniform behavior (including side effects) with how edges are added elsewhere in the tests. For
-   * example, the existing implementations of this method explicitly add the supplied nodes to the
-   * graph, and then call {@code graph.addEdge()} to connect the edge to the nodes; this is not part
-   * of the contract of {@code graph.addEdge()} and is done for convenience. In cases where you want
-   * to avoid such side effects (e.g., if you're testing what happens in your implementation if you
-   * add an edge whose end-points don't already exist in the graph), you should <b>not</b> use this
-   * method.
-   *
-   * <p>TODO(user): remove the addNode() calls, that's now contractually guaranteed
-   *
-   * @return {@code true} iff the graph was modified as a result of this call
+   * graph implementations, this method should replace {@link #network} with a new graph that
+   * includes this edge.
    */
-  @CanIgnoreReturnValue
-  protected boolean addEdge(Integer n1, Integer n2, String e) {
-    network.addNode(n1);
-    network.addNode(n2);
-    return network.addEdge(n1, n2, e);
-  }
+  abstract void addEdge(Integer n1, Integer n2, String e);
 
-  protected boolean addEdge(EndpointPair<Integer> endpoints, String e) {
-    network.addNode(endpoints.nodeU());
-    network.addNode(endpoints.nodeV());
-    return network.addEdge(endpoints, e);
+  final boolean graphIsMutable() {
+    return networkAsMutableNetwork != null;
   }
 
   @Before
   public void init() {
     network = createGraph();
+    if (network instanceof MutableNetwork) {
+      networkAsMutableNetwork = (MutableNetwork<Integer, String>) network;
+    }
   }
 
   @After
@@ -497,6 +489,18 @@
   }
 
   @Test
+  public void adjacentEdges_parallelEdges() {
+    assume().that(network.allowsParallelEdges()).isTrue();
+
+    addEdge(N1, N2, E12);
+    addEdge(N1, N2, E12_A);
+    addEdge(N1, N2, E12_B);
+    addEdge(N3, N4, E34);
+
+    assertThat(network.adjacentEdges(E12)).containsExactly(E12_A, E12_B);
+  }
+
+  @Test
   public void edgesConnecting_disconnectedNodes() {
     addNode(N1);
     addNode(N2);
@@ -528,6 +532,60 @@
   }
 
   @Test
+  public void edgesConnecting_parallelEdges_directed() {
+    assume().that(network.allowsParallelEdges()).isTrue();
+    assume().that(network.isDirected()).isTrue();
+
+    addEdge(N1, N2, E12);
+    addEdge(N1, N2, E12_A);
+
+    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12, E12_A);
+    // Passed nodes should be in the correct edge direction, first is the
+    // source node and the second is the target node
+    assertThat(network.edgesConnecting(N2, N1)).isEmpty();
+  }
+
+  @Test
+  public void edgesConnecting_parallelEdges_undirected() {
+    assume().that(network.allowsParallelEdges()).isTrue();
+    assume().that(network.isDirected()).isFalse();
+
+    addEdge(N1, N2, E12);
+    addEdge(N1, N2, E12_A);
+    addEdge(N2, N1, E21);
+
+    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12, E12_A, E21);
+    assertThat(network.edgesConnecting(N2, N1)).containsExactly(E12, E12_A, E21);
+  }
+
+  @Test
+  public void edgesConnecting_parallelSelfLoopEdges() {
+    assume().that(network.allowsParallelEdges()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    addEdge(N1, N1, E11_A);
+
+    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11, E11_A);
+  }
+
+  @Test
+  public void hasEdgeConnecting_disconnectedNodes() {
+    addNode(N1);
+    addNode(N2);
+    assertThat(network.hasEdgeConnecting(N1, N2)).isFalse();
+  }
+
+  @Test
+  public void hasEdgesConnecting_nodesNotInGraph() {
+    addNode(N1);
+    addNode(N2);
+    assertThat(network.hasEdgeConnecting(N1, NODE_NOT_IN_GRAPH)).isFalse();
+    assertThat(network.hasEdgeConnecting(NODE_NOT_IN_GRAPH, N2)).isFalse();
+    assertThat(network.hasEdgeConnecting(NODE_NOT_IN_GRAPH, NODE_NOT_IN_GRAPH)).isFalse();
+  }
+
+  @Test
   public void inEdges_noInEdges() {
     addNode(N1);
     assertThat(network.inEdges(N1)).isEmpty();
@@ -593,45 +651,56 @@
 
   @Test
   public void addNode_newNode() {
-    assertTrue(addNode(N1));
-    assertThat(network.nodes()).contains(N1);
+    assume().that(graphIsMutable()).isTrue();
+
+    assertTrue(networkAsMutableNetwork.addNode(N1));
+    assertThat(networkAsMutableNetwork.nodes()).contains(N1);
   }
 
   @Test
   public void addNode_existingNode() {
+    assume().that(graphIsMutable()).isTrue();
+
     addNode(N1);
-    ImmutableSet<Integer> nodes = ImmutableSet.copyOf(network.nodes());
-    assertFalse(addNode(N1));
-    assertThat(network.nodes()).containsExactlyElementsIn(nodes);
+    ImmutableSet<Integer> nodes = ImmutableSet.copyOf(networkAsMutableNetwork.nodes());
+    assertFalse(networkAsMutableNetwork.addNode(N1));
+    assertThat(networkAsMutableNetwork.nodes()).containsExactlyElementsIn(nodes);
   }
 
   @Test
   public void removeNode_existingNode() {
+    assume().that(graphIsMutable()).isTrue();
+
     addEdge(N1, N2, E12);
     addEdge(N4, N1, E41);
-    assertTrue(network.removeNode(N1));
-    assertFalse(network.removeNode(N1));
-    assertThat(network.nodes()).containsExactly(N2, N4);
-    assertThat(network.edges()).doesNotContain(E12);
-    assertThat(network.edges()).doesNotContain(E41);
+    assertTrue(networkAsMutableNetwork.removeNode(N1));
+    assertFalse(networkAsMutableNetwork.removeNode(N1));
+    assertThat(networkAsMutableNetwork.nodes()).containsExactly(N2, N4);
+    assertThat(networkAsMutableNetwork.edges()).doesNotContain(E12);
+    assertThat(networkAsMutableNetwork.edges()).doesNotContain(E41);
   }
 
   @Test
   public void removeNode_nodeNotPresent() {
+    assume().that(graphIsMutable()).isTrue();
+
     addNode(N1);
-    ImmutableSet<Integer> nodes = ImmutableSet.copyOf(network.nodes());
-    assertFalse(network.removeNode(NODE_NOT_IN_GRAPH));
-    assertThat(network.nodes()).containsExactlyElementsIn(nodes);
+    ImmutableSet<Integer> nodes = ImmutableSet.copyOf(networkAsMutableNetwork.nodes());
+    assertFalse(networkAsMutableNetwork.removeNode(NODE_NOT_IN_GRAPH));
+    assertThat(networkAsMutableNetwork.nodes()).containsExactlyElementsIn(nodes);
   }
 
   @Test
   public void removeNode_queryAfterRemoval() {
+    assume().that(graphIsMutable()).isTrue();
+
     addNode(N1);
     @SuppressWarnings("unused")
-    Set<Integer> unused = network.adjacentNodes(N1); // ensure cache (if any) is populated
-    assertTrue(network.removeNode(N1));
+    Set<Integer> unused =
+        networkAsMutableNetwork.adjacentNodes(N1); // ensure cache (if any) is populated
+    assertTrue(networkAsMutableNetwork.removeNode(N1));
     try {
-      network.adjacentNodes(N1);
+      networkAsMutableNetwork.adjacentNodes(N1);
       fail(ERROR_NODE_NOT_IN_GRAPH);
     } catch (IllegalArgumentException e) {
       assertNodeNotInGraphErrorMessage(e);
@@ -640,42 +709,139 @@
 
   @Test
   public void removeEdge_existingEdge() {
+    assume().that(graphIsMutable()).isTrue();
+
     addEdge(N1, N2, E12);
-    assertTrue(network.removeEdge(E12));
-    assertFalse(network.removeEdge(E12));
-    assertThat(network.edges()).doesNotContain(E12);
-    assertThat(network.edgesConnecting(N1, N2)).isEmpty();
+    assertTrue(networkAsMutableNetwork.removeEdge(E12));
+    assertFalse(networkAsMutableNetwork.removeEdge(E12));
+    assertThat(networkAsMutableNetwork.edges()).doesNotContain(E12);
+    assertThat(networkAsMutableNetwork.edgesConnecting(N1, N2)).isEmpty();
   }
 
   @Test
   public void removeEdge_oneOfMany() {
+    assume().that(graphIsMutable()).isTrue();
+
     addEdge(N1, N2, E12);
     addEdge(N1, N3, E13);
     addEdge(N1, N4, E14);
-    assertThat(network.edges()).containsExactly(E12, E13, E14);
-    assertTrue(network.removeEdge(E13));
-    assertThat(network.edges()).containsExactly(E12, E14);
+    assertThat(networkAsMutableNetwork.edges()).containsExactly(E12, E13, E14);
+    assertTrue(networkAsMutableNetwork.removeEdge(E13));
+    assertThat(networkAsMutableNetwork.edges()).containsExactly(E12, E14);
   }
 
   @Test
   public void removeEdge_edgeNotPresent() {
+    assume().that(graphIsMutable()).isTrue();
+
     addEdge(N1, N2, E12);
-    ImmutableSet<String> edges = ImmutableSet.copyOf(network.edges());
-    assertFalse(network.removeEdge(EDGE_NOT_IN_GRAPH));
-    assertThat(network.edges()).containsExactlyElementsIn(edges);
+    ImmutableSet<String> edges = ImmutableSet.copyOf(networkAsMutableNetwork.edges());
+    assertFalse(networkAsMutableNetwork.removeEdge(EDGE_NOT_IN_GRAPH));
+    assertThat(networkAsMutableNetwork.edges()).containsExactlyElementsIn(edges);
   }
 
   @Test
   public void removeEdge_queryAfterRemoval() {
+    assume().that(graphIsMutable()).isTrue();
+
     addEdge(N1, N2, E12);
     @SuppressWarnings("unused")
-    EndpointPair<Integer> unused = network.incidentNodes(E12); // ensure cache (if any) is populated
-    assertTrue(network.removeEdge(E12));
+    EndpointPair<Integer> unused =
+        networkAsMutableNetwork.incidentNodes(E12); // ensure cache (if any) is populated
+    assertTrue(networkAsMutableNetwork.removeEdge(E12));
     try {
-      network.incidentNodes(E12);
+      networkAsMutableNetwork.incidentNodes(E12);
       fail(ERROR_EDGE_NOT_IN_GRAPH);
     } catch (IllegalArgumentException e) {
       assertEdgeNotInGraphErrorMessage(e);
     }
   }
+
+  @Test
+  public void removeEdge_parallelEdge() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsParallelEdges()).isTrue();
+
+    addEdge(N1, N2, E12);
+    addEdge(N1, N2, E12_A);
+    assertTrue(networkAsMutableNetwork.removeEdge(E12_A));
+    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
+  }
+
+  @Test
+  public void removeEdge_parallelSelfLoopEdge() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsParallelEdges()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    addEdge(N1, N1, E11_A);
+    addEdge(N1, N2, E12);
+    assertTrue(networkAsMutableNetwork.removeEdge(E11_A));
+    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
+    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
+    assertTrue(networkAsMutableNetwork.removeEdge(E11));
+    assertThat(network.edgesConnecting(N1, N1)).isEmpty();
+    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
+  }
+
+  @Test
+  public void concurrentIteration() throws Exception {
+    addEdge(1, 2, "foo");
+    addEdge(3, 4, "bar");
+    addEdge(5, 6, "baz");
+
+    int threadCount = 20;
+    ExecutorService executor = newFixedThreadPool(threadCount);
+    final CyclicBarrier barrier = new CyclicBarrier(threadCount);
+    ImmutableList.Builder<Future<?>> futures = ImmutableList.builder();
+    for (int i = 0; i < threadCount; i++) {
+      futures.add(
+          executor.submit(
+              new Callable<Object>() {
+                @Override
+                public Object call() throws Exception {
+                  barrier.await();
+                  Integer first = network.nodes().iterator().next();
+                  for (Integer node : network.nodes()) {
+                    Set<Integer> unused = network.successors(node);
+                  }
+                  /*
+                   * Also look up an earlier node so that, if the graph is using MapRetrievalCache,
+                   * we read one of the fields declared in that class.
+                   */
+                  Set<Integer> unused = network.successors(first);
+                  return null;
+                }
+              }));
+    }
+
+    /*
+     * It's unlikely that any operations would fail by throwing an exception, but let's check them
+     * just to be safe.
+     *
+     * The real purpose of this test is to produce a TSAN failure if MapIteratorCache is unsafe for
+     * reads from multiple threads -- unsafe, in fact, even in the absence of a concurrent write.
+     * The specific problem we had was unsafe reads of lastEntryReturnedBySomeIterator. (To fix the
+     * problem, we've since marked that field as volatile.)
+     *
+     * When MapIteratorCache is used from Immutable* classes, the TSAN failure doesn't indicate a
+     * real problem: The Entry objects are ImmutableMap entries, whose fields are all final and thus
+     * safe to read even when the Entry object is unsafely published. But with a mutable graph, the
+     * Entry object is likely to have a non-final value field, which is not safe to read when
+     * unsafely published. (The Entry object might even be newly created by each iterator.next()
+     * call, so we can't assume that writes to the Entry have been safely published by some other
+     * synchronization actions.)
+     *
+     * All that said: I haven't actually managed to make this particular test produce a TSAN error
+     * for the field accesses in MapIteratorCache. This teset *has* found other TSAN errors,
+     * including in MapRetrievalCache, so I'm not sure why this one is different. I did at least
+     * confirm that my change to MapIteratorCache fixes the TSAN error in the (larger) test it was
+     * originally reported in.
+     */
+    for (Future<?> future : futures.build()) {
+      future.get();
+    }
+    executor.shutdown();
+  }
 }
diff --git a/android/guava-tests/test/com/google/common/graph/AbstractStandardDirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/AbstractStandardDirectedGraphTest.java
new file mode 100644
index 0000000..c50a7da
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/graph/AbstractStandardDirectedGraphTest.java
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 2014 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 com.google.common.graph;
+
+import static com.google.common.graph.GraphConstants.ENDPOINTS_MISMATCH;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Set;
+import org.junit.Test;
+
+/**
+ * Abstract base class for testing directed {@link Graph} implementations defined in this package.
+ */
+public abstract class AbstractStandardDirectedGraphTest extends AbstractGraphTest {
+
+  @Override
+  @Test
+  public void nodes_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    Set<Integer> nodes = graph.nodes();
+    try {
+      nodes.add(N2);
+      fail(ERROR_MODIFIABLE_SET);
+    } catch (UnsupportedOperationException e) {
+      addNode(N1);
+      assertThat(graph.nodes()).containsExactlyElementsIn(nodes);
+    }
+  }
+
+  @Override
+  @Test
+  public void adjacentNodes_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N1);
+    Set<Integer> adjacentNodes = graph.adjacentNodes(N1);
+    try {
+      adjacentNodes.add(N2);
+      fail(ERROR_MODIFIABLE_SET);
+    } catch (UnsupportedOperationException e) {
+      putEdge(N1, N2);
+      assertThat(graph.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes);
+    }
+  }
+
+  @Override
+  @Test
+  public void predecessors_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N2);
+    Set<Integer> predecessors = graph.predecessors(N2);
+    try {
+      predecessors.add(N1);
+      fail(ERROR_MODIFIABLE_SET);
+    } catch (UnsupportedOperationException e) {
+      putEdge(N1, N2);
+      assertThat(graph.predecessors(N2)).containsExactlyElementsIn(predecessors);
+    }
+  }
+
+  @Override
+  @Test
+  public void successors_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N1);
+    Set<Integer> successors = graph.successors(N1);
+    try {
+      successors.add(N2);
+      fail(ERROR_MODIFIABLE_SET);
+    } catch (UnsupportedOperationException e) {
+      putEdge(N1, N2);
+      assertThat(successors).containsExactlyElementsIn(graph.successors(N1));
+    }
+  }
+
+  @Override
+  @Test
+  public void incidentEdges_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N1);
+    Set<EndpointPair<Integer>> incidentEdges = graph.incidentEdges(N1);
+    try {
+      incidentEdges.add(EndpointPair.ordered(N1, N2));
+      fail(ERROR_MODIFIABLE_SET);
+    } catch (UnsupportedOperationException e) {
+      putEdge(N1, N2);
+      assertThat(incidentEdges).containsExactlyElementsIn(graph.incidentEdges(N1));
+    }
+  }
+
+  @Test
+  public void predecessors_oneEdge() {
+    putEdge(N1, N2);
+    assertThat(graph.predecessors(N2)).containsExactly(N1);
+    // Edge direction handled correctly
+    assertThat(graph.predecessors(N1)).isEmpty();
+  }
+
+  @Test
+  public void successors_oneEdge() {
+    putEdge(N1, N2);
+    assertThat(graph.successors(N1)).containsExactly(N2);
+    // Edge direction handled correctly
+    assertThat(graph.successors(N2)).isEmpty();
+  }
+
+  @Test
+  public void incidentEdges_oneEdge() {
+    putEdge(N1, N2);
+    EndpointPair<Integer> expectedEndpoints = EndpointPair.ordered(N1, N2);
+    assertThat(graph.incidentEdges(N1)).containsExactly(expectedEndpoints);
+    assertThat(graph.incidentEdges(N2)).containsExactly(expectedEndpoints);
+  }
+
+  @Test
+  public void inDegree_oneEdge() {
+    putEdge(N1, N2);
+    assertThat(graph.inDegree(N2)).isEqualTo(1);
+    // Edge direction handled correctly
+    assertThat(graph.inDegree(N1)).isEqualTo(0);
+  }
+
+  @Test
+  public void outDegree_oneEdge() {
+    putEdge(N1, N2);
+    assertThat(graph.outDegree(N1)).isEqualTo(1);
+    // Edge direction handled correctly
+    assertThat(graph.outDegree(N2)).isEqualTo(0);
+  }
+
+  @Test
+  public void hasEdgeConnecting_correct() {
+    putEdge(N1, N2);
+    assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(N1, N2))).isTrue();
+  }
+
+  @Test
+  public void hasEdgeConnecting_backwards() {
+    putEdge(N1, N2);
+    assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(N2, N1))).isFalse();
+  }
+
+  @Test
+  public void hasEdgeConnecting_mismatch() {
+    putEdge(N1, N2);
+    assertThat(graph.hasEdgeConnecting(EndpointPair.unordered(N1, N2))).isFalse();
+    assertThat(graph.hasEdgeConnecting(EndpointPair.unordered(N2, N1))).isFalse();
+  }
+
+  @Test
+  public void adjacentNodes_selfLoop() {
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    putEdge(N1, N2);
+    assertThat(graph.adjacentNodes(N1)).containsExactly(N1, N2);
+  }
+
+  @Test
+  public void predecessors_selfLoop() {
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graph.predecessors(N1)).containsExactly(N1);
+    putEdge(N4, N1);
+    assertThat(graph.predecessors(N1)).containsExactly(N1, N4);
+  }
+
+  @Test
+  public void successors_selfLoop() {
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graph.successors(N1)).containsExactly(N1);
+    putEdge(N1, N2);
+    assertThat(graph.successors(N1)).containsExactly(N1, N2);
+  }
+
+  @Test
+  public void incidentEdges_selfLoop() {
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graph.incidentEdges(N1)).containsExactly(EndpointPair.ordered(N1, N1));
+    putEdge(N1, N2);
+    assertThat(graph.incidentEdges(N1))
+        .containsExactly(EndpointPair.ordered(N1, N1), EndpointPair.ordered(N1, N2));
+  }
+
+  @Test
+  public void degree_selfLoop() {
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graph.degree(N1)).isEqualTo(2);
+    putEdge(N1, N2);
+    assertThat(graph.degree(N1)).isEqualTo(3);
+  }
+
+  @Test
+  public void inDegree_selfLoop() {
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graph.inDegree(N1)).isEqualTo(1);
+    putEdge(N4, N1);
+    assertThat(graph.inDegree(N1)).isEqualTo(2);
+  }
+
+  @Test
+  public void outDegree_selfLoop() {
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graph.outDegree(N1)).isEqualTo(1);
+    putEdge(N1, N2);
+    assertThat(graph.outDegree(N1)).isEqualTo(2);
+  }
+
+  // Stable order tests
+
+  // Note: Stable order means that the ordering doesn't change between iterations and versions.
+  // Ideally, the ordering in test should never be updated.
+  @Test
+  public void stableIncidentEdgeOrder_edges_returnsInStableOrder() {
+    assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
+
+    populateStarShapedGraph();
+
+    assertThat(graph.edges())
+        .containsExactly(
+            EndpointPair.ordered(2, 1),
+            EndpointPair.ordered(1, 4),
+            EndpointPair.ordered(1, 3),
+            EndpointPair.ordered(1, 2),
+            EndpointPair.ordered(3, 1),
+            EndpointPair.ordered(5, 1))
+        .inOrder();
+  }
+
+  @Test
+  public void stableIncidentEdgeOrder_adjacentNodes_returnsInConnectingEdgeInsertionOrder() {
+    assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
+
+    populateStarShapedGraph();
+
+    assertThat(graph.adjacentNodes(1)).containsExactly(2, 4, 3, 5).inOrder();
+  }
+
+  @Test
+  public void stableIncidentEdgeOrder_predecessors_returnsInConnectingEdgeInsertionOrder() {
+    assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
+
+    populateStarShapedGraph();
+
+    assertThat(graph.predecessors(1)).containsExactly(2, 5, 3).inOrder();
+  }
+
+  @Test
+  public void stableIncidentEdgeOrder_successors_returnsInConnectingEdgeInsertionOrder() {
+    assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
+
+    populateStarShapedGraph();
+
+    assertThat(graph.successors(1)).containsExactly(4, 3, 2).inOrder();
+  }
+
+  @Test
+  public void stableIncidentEdgeOrder_incidentEdges_returnsInEdgeInsertionOrder() {
+    assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
+
+    populateStarShapedGraph();
+
+    assertThat(graph.incidentEdges(1))
+        .containsExactly(
+            EndpointPair.ordered(2, 1),
+            EndpointPair.ordered(1, 4),
+            EndpointPair.ordered(1, 3),
+            EndpointPair.ordered(5, 1),
+            EndpointPair.ordered(1, 2),
+            EndpointPair.ordered(3, 1))
+        .inOrder();
+  }
+
+  @Test
+  public void stableIncidentEdgeOrder_incidentEdges_withSelfLoop_returnsInEdgeInsertionOrder() {
+    assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(2, 1);
+    putEdge(1, 1);
+    putEdge(1, 3);
+    putEdge(1, 2);
+
+    assertThat(graph.incidentEdges(1))
+        .containsExactly(
+            EndpointPair.ordered(2, 1),
+            EndpointPair.ordered(1, 1),
+            EndpointPair.ordered(1, 3),
+            EndpointPair.ordered(1, 2))
+        .inOrder();
+  }
+
+  /**
+   * Populates the graph with nodes and edges in a star shape with node `1` in the middle.
+   *
+   * <p>Note that the edges are added in a shuffled order to properly test the effect of the
+   * insertion order.
+   */
+  private void populateStarShapedGraph() {
+    putEdge(2, 1);
+    putEdge(1, 4);
+    putEdge(1, 3);
+    putEdge(5, 1);
+    putEdge(1, 2);
+    putEdge(3, 1);
+  }
+
+  // Element Mutation
+
+  @Test
+  public void putEdge_existingNodes() {
+    assume().that(graphIsMutable()).isTrue();
+
+    // Adding nodes initially for safety (insulating from possible future
+    // modifications to proxy methods)
+    addNode(N1);
+    addNode(N2);
+
+    assertThat(graphAsMutableGraph.putEdge(N1, N2)).isTrue();
+  }
+
+  @Test
+  public void putEdge_existingEdgeBetweenSameNodes() {
+    assume().that(graphIsMutable()).isTrue();
+
+    assertThat(graphAsMutableGraph.putEdge(N1, N2)).isTrue();
+    assertThat(graphAsMutableGraph.putEdge(N1, N2)).isFalse();
+  }
+
+  @Test
+  public void putEdge_orderMismatch() {
+    assume().that(graphIsMutable()).isTrue();
+
+    EndpointPair<Integer> endpoints = EndpointPair.unordered(N1, N2);
+    try {
+      graphAsMutableGraph.putEdge(endpoints);
+      fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH);
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH);
+    }
+  }
+
+  /**
+   * Tests that the method {@code putEdge} will silently add the missing nodes to the graph, then
+   * add the edge connecting them. We are not using the proxy methods here as we want to test {@code
+   * putEdge} when the end-points are not elements of the graph.
+   */
+  @Test
+  public void putEdge_nodesNotInGraph() {
+    assume().that(graphIsMutable()).isTrue();
+
+    graphAsMutableGraph.addNode(N1);
+    assertTrue(graphAsMutableGraph.putEdge(N1, N5));
+    assertTrue(graphAsMutableGraph.putEdge(N4, N1));
+    assertTrue(graphAsMutableGraph.putEdge(N2, N3));
+    assertThat(graph.nodes()).containsExactly(N1, N5, N4, N2, N3).inOrder();
+    assertThat(graph.successors(N1)).containsExactly(N5);
+    assertThat(graph.successors(N2)).containsExactly(N3);
+    assertThat(graph.successors(N3)).isEmpty();
+    assertThat(graph.successors(N4)).containsExactly(N1);
+    assertThat(graph.successors(N5)).isEmpty();
+  }
+
+  @Test
+  public void putEdge_doesntAllowSelfLoops() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(graph.allowsSelfLoops()).isFalse();
+
+    try {
+      graphAsMutableGraph.putEdge(N1, N1);
+      fail(ERROR_ADDED_SELF_LOOP);
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP);
+    }
+  }
+
+  @Test
+  public void putEdge_allowsSelfLoops() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    assertThat(graphAsMutableGraph.putEdge(N1, N1)).isTrue();
+    assertThat(graph.successors(N1)).containsExactly(N1);
+    assertThat(graph.predecessors(N1)).containsExactly(N1);
+  }
+
+  @Test
+  public void putEdge_existingSelfLoopEdgeBetweenSameNodes() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    graphAsMutableGraph.putEdge(N1, N1);
+    assertThat(graphAsMutableGraph.putEdge(N1, N1)).isFalse();
+  }
+
+  @Test
+  public void removeEdge_antiparallelEdges() {
+    assume().that(graphIsMutable()).isTrue();
+
+    putEdge(N1, N2);
+    putEdge(N2, N1);
+
+    assertThat(graphAsMutableGraph.removeEdge(N1, N2)).isTrue();
+    assertThat(graph.successors(N1)).isEmpty();
+    assertThat(graph.predecessors(N1)).containsExactly(N2);
+    assertThat(graph.edges()).hasSize(1);
+
+    assertThat(graphAsMutableGraph.removeEdge(N2, N1)).isTrue();
+    assertThat(graph.successors(N1)).isEmpty();
+    assertThat(graph.predecessors(N1)).isEmpty();
+    assertThat(graph.edges()).isEmpty();
+  }
+
+  @Test
+  public void removeEdge_orderMismatch() {
+    assume().that(graphIsMutable()).isTrue();
+
+    putEdge(N1, N2);
+    EndpointPair<Integer> endpoints = EndpointPair.unordered(N1, N2);
+    try {
+      graphAsMutableGraph.removeEdge(endpoints);
+      fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH);
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH);
+    }
+  }
+
+  @Test
+  public void removeNode_existingNodeWithSelfLoopEdge() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    addNode(N1);
+    putEdge(N1, N1);
+    assertThat(graphAsMutableGraph.removeNode(N1)).isTrue();
+    assertThat(graph.nodes()).isEmpty();
+  }
+
+  @Test
+  public void removeEdge_existingSelfLoopEdge() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graphAsMutableGraph.removeEdge(N1, N1)).isTrue();
+    assertThat(graph.nodes()).containsExactly(N1);
+    assertThat(graph.successors(N1)).isEmpty();
+  }
+}
diff --git a/android/guava-tests/test/com/google/common/graph/AbstractStandardDirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/AbstractStandardDirectedNetworkTest.java
new file mode 100644
index 0000000..98bc2fd
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/graph/AbstractStandardDirectedNetworkTest.java
@@ -0,0 +1,704 @@
+/*
+ * Copyright (C) 2014 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 com.google.common.graph;
+
+import static com.google.common.graph.GraphConstants.ENDPOINTS_MISMATCH;
+import static com.google.common.graph.TestUtil.assertEdgeNotInGraphErrorMessage;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Collections;
+import java.util.Set;
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Abstract base class for testing directed {@link Network} implementations defined in this package.
+ */
+public abstract class AbstractStandardDirectedNetworkTest extends AbstractNetworkTest {
+
+  @After
+  public void validateSourceAndTarget() {
+    for (Integer node : network.nodes()) {
+      for (String inEdge : network.inEdges(node)) {
+        EndpointPair<Integer> endpointPair = network.incidentNodes(inEdge);
+        assertThat(endpointPair.source()).isEqualTo(endpointPair.adjacentNode(node));
+        assertThat(endpointPair.target()).isEqualTo(node);
+      }
+
+      for (String outEdge : network.outEdges(node)) {
+        EndpointPair<Integer> endpointPair = network.incidentNodes(outEdge);
+        assertThat(endpointPair.source()).isEqualTo(node);
+        assertThat(endpointPair.target()).isEqualTo(endpointPair.adjacentNode(node));
+      }
+
+      for (Integer adjacentNode : network.adjacentNodes(node)) {
+        Set<String> edges = network.edgesConnecting(node, adjacentNode);
+        Set<String> antiParallelEdges = network.edgesConnecting(adjacentNode, node);
+        assertThat(node.equals(adjacentNode) || Collections.disjoint(edges, antiParallelEdges))
+            .isTrue();
+      }
+    }
+  }
+
+  @Override
+  @Test
+  public void nodes_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    Set<Integer> nodes = network.nodes();
+    try {
+      nodes.add(N2);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addNode(N1);
+      assertThat(network.nodes()).containsExactlyElementsIn(nodes);
+    }
+  }
+
+  @Override
+  @Test
+  public void edges_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    Set<String> edges = network.edges();
+    try {
+      edges.add(E12);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.edges()).containsExactlyElementsIn(edges);
+    }
+  }
+
+  @Override
+  @Test
+  public void incidentEdges_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N1);
+    Set<String> incidentEdges = network.incidentEdges(N1);
+    try {
+      incidentEdges.add(E12);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.incidentEdges(N1)).containsExactlyElementsIn(incidentEdges);
+    }
+  }
+
+  @Override
+  @Test
+  public void adjacentNodes_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N1);
+    Set<Integer> adjacentNodes = network.adjacentNodes(N1);
+    try {
+      adjacentNodes.add(N2);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes);
+    }
+  }
+
+  @Override
+  public void adjacentEdges_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addEdge(N1, N2, E12);
+    Set<String> adjacentEdges = network.adjacentEdges(E12);
+    try {
+      adjacentEdges.add(E23);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N2, N3, E23);
+      assertThat(network.adjacentEdges(E12)).containsExactlyElementsIn(adjacentEdges);
+    }
+  }
+
+  @Override
+  @Test
+  public void edgesConnecting_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N1);
+    addNode(N2);
+    Set<String> edgesConnecting = network.edgesConnecting(N1, N2);
+    try {
+      edgesConnecting.add(E23);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.edgesConnecting(N1, N2)).containsExactlyElementsIn(edgesConnecting);
+    }
+  }
+
+  @Override
+  @Test
+  public void inEdges_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N2);
+    Set<String> inEdges = network.inEdges(N2);
+    try {
+      inEdges.add(E12);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.inEdges(N2)).containsExactlyElementsIn(inEdges);
+    }
+  }
+
+  @Override
+  @Test
+  public void outEdges_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N1);
+    Set<String> outEdges = network.outEdges(N1);
+    try {
+      outEdges.add(E12);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.outEdges(N1)).containsExactlyElementsIn(outEdges);
+    }
+  }
+
+  @Override
+  @Test
+  public void predecessors_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N2);
+    Set<Integer> predecessors = network.predecessors(N2);
+    try {
+      predecessors.add(N1);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.predecessors(N2)).containsExactlyElementsIn(predecessors);
+    }
+  }
+
+  @Override
+  @Test
+  public void successors_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N1);
+    Set<Integer> successors = network.successors(N1);
+    try {
+      successors.add(N2);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(successors).containsExactlyElementsIn(network.successors(N1));
+    }
+  }
+
+  @Test
+  public void edges_containsOrderMismatch() {
+    addEdge(N1, N2, E12);
+    EndpointPair<Integer> endpointsN1N2 = EndpointPair.unordered(N1, N2);
+    EndpointPair<Integer> endpointsN2N1 = EndpointPair.unordered(N2, N1);
+    assertThat(network.asGraph().edges()).doesNotContain(endpointsN1N2);
+    assertThat(network.asGraph().edges()).doesNotContain(endpointsN2N1);
+  }
+
+  @Test
+  public void edgesConnecting_orderMismatch() {
+    addEdge(N1, N2, E12);
+    try {
+      Set<String> unused = network.edgesConnecting(EndpointPair.unordered(N1, N2));
+      fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH);
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH);
+    }
+  }
+
+  @Test
+  public void edgeConnectingOrNull_orderMismatch() {
+    addEdge(N1, N2, E12);
+    try {
+      String unused = network.edgeConnectingOrNull(EndpointPair.unordered(N1, N2));
+      fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH);
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH);
+    }
+  }
+
+  @Override
+  @Test
+  public void incidentNodes_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.incidentNodes(E12).source()).isEqualTo(N1);
+    assertThat(network.incidentNodes(E12).target()).isEqualTo(N2);
+  }
+
+  @Test
+  public void edgesConnecting_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
+    // Passed nodes should be in the correct edge direction, first is the
+    // source node and the second is the target node
+    assertThat(network.edgesConnecting(N2, N1)).isEmpty();
+  }
+
+  @Test
+  public void inEdges_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.inEdges(N2)).containsExactly(E12);
+    // Edge direction handled correctly
+    assertThat(network.inEdges(N1)).isEmpty();
+  }
+
+  @Test
+  public void outEdges_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.outEdges(N1)).containsExactly(E12);
+    // Edge direction handled correctly
+    assertThat(network.outEdges(N2)).isEmpty();
+  }
+
+  @Test
+  public void predecessors_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.predecessors(N2)).containsExactly(N1);
+    // Edge direction handled correctly
+    assertThat(network.predecessors(N1)).isEmpty();
+  }
+
+  @Test
+  public void successors_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.successors(N1)).containsExactly(N2);
+    // Edge direction handled correctly
+    assertThat(network.successors(N2)).isEmpty();
+  }
+
+  @Test
+  public void source_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.incidentNodes(E12).source()).isEqualTo(N1);
+  }
+
+  @Test
+  public void source_edgeNotInGraph() {
+    try {
+      network.incidentNodes(EDGE_NOT_IN_GRAPH).source();
+      fail(ERROR_EDGE_NOT_IN_GRAPH);
+    } catch (IllegalArgumentException e) {
+      assertEdgeNotInGraphErrorMessage(e);
+    }
+  }
+
+  @Test
+  public void target_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.incidentNodes(E12).target()).isEqualTo(N2);
+  }
+
+  @Test
+  public void target_edgeNotInGraph() {
+    try {
+      network.incidentNodes(EDGE_NOT_IN_GRAPH).target();
+      fail(ERROR_EDGE_NOT_IN_GRAPH);
+    } catch (IllegalArgumentException e) {
+      assertEdgeNotInGraphErrorMessage(e);
+    }
+  }
+
+  @Test
+  public void inDegree_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.inDegree(N2)).isEqualTo(1);
+    // Edge direction handled correctly
+    assertThat(network.inDegree(N1)).isEqualTo(0);
+  }
+
+  @Test
+  public void outDegree_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.outDegree(N1)).isEqualTo(1);
+    // Edge direction handled correctly
+    assertThat(network.outDegree(N2)).isEqualTo(0);
+  }
+
+  @Test
+  public void edges_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.edges()).containsExactly(E11);
+  }
+
+  @Test
+  public void incidentEdges_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.incidentEdges(N1)).containsExactly(E11);
+  }
+
+  @Test
+  public void incidentNodes_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.incidentNodes(E11).source()).isEqualTo(N1);
+    assertThat(network.incidentNodes(E11).target()).isEqualTo(N1);
+  }
+
+  @Test
+  public void adjacentNodes_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    addEdge(N1, N2, E12);
+    assertThat(network.adjacentNodes(N1)).containsExactly(N1, N2);
+  }
+
+  @Test
+  public void adjacentEdges_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    addEdge(N1, N2, E12);
+    assertThat(network.adjacentEdges(E11)).containsExactly(E12);
+  }
+
+  @Test
+  public void edgesConnecting_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
+    addEdge(N1, N2, E12);
+    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
+    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
+  }
+
+  @Test
+  public void inEdges_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.inEdges(N1)).containsExactly(E11);
+    addEdge(N4, N1, E41);
+    assertThat(network.inEdges(N1)).containsExactly(E11, E41);
+  }
+
+  @Test
+  public void outEdges_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.outEdges(N1)).containsExactly(E11);
+    addEdge(N1, N2, E12);
+    assertThat(network.outEdges(N1)).containsExactly(E11, E12);
+  }
+
+  @Test
+  public void predecessors_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.predecessors(N1)).containsExactly(N1);
+    addEdge(N4, N1, E41);
+    assertThat(network.predecessors(N1)).containsExactly(N1, N4);
+  }
+
+  @Test
+  public void successors_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.successors(N1)).containsExactly(N1);
+    addEdge(N1, N2, E12);
+    assertThat(network.successors(N1)).containsExactly(N1, N2);
+  }
+
+  @Test
+  public void source_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.incidentNodes(E11).source()).isEqualTo(N1);
+  }
+
+  @Test
+  public void target_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.incidentNodes(E11).target()).isEqualTo(N1);
+  }
+
+  @Test
+  public void degree_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.degree(N1)).isEqualTo(2);
+    addEdge(N1, N2, E12);
+    assertThat(network.degree(N1)).isEqualTo(3);
+  }
+
+  @Test
+  public void inDegree_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.inDegree(N1)).isEqualTo(1);
+    addEdge(N4, N1, E41);
+    assertThat(network.inDegree(N1)).isEqualTo(2);
+  }
+
+  @Test
+  public void outDegree_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.outDegree(N1)).isEqualTo(1);
+    addEdge(N1, N2, E12);
+    assertThat(network.outDegree(N1)).isEqualTo(2);
+  }
+
+  // Element Mutation
+
+  @Test
+  public void addEdge_existingNodes() {
+    assume().that(graphIsMutable()).isTrue();
+
+    // Adding nodes initially for safety (insulating from possible future
+    // modifications to proxy methods)
+    addNode(N1);
+    addNode(N2);
+    assertThat(networkAsMutableNetwork.addEdge(N1, N2, E12)).isTrue();
+    assertThat(network.edges()).contains(E12);
+    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
+    // Direction of the added edge is correctly handled
+    assertThat(network.edgesConnecting(N2, N1)).isEmpty();
+  }
+
+  @Test
+  public void addEdge_existingEdgeBetweenSameNodes() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addEdge(N1, N2, E12);
+    ImmutableSet<String> edges = ImmutableSet.copyOf(network.edges());
+    assertThat(networkAsMutableNetwork.addEdge(N1, N2, E12)).isFalse();
+    assertThat(network.edges()).containsExactlyElementsIn(edges);
+  }
+
+  @Test
+  public void addEdge_existingEdgeBetweenDifferentNodes() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addEdge(N1, N2, E12);
+    try {
+      // Edge between totally different nodes
+      networkAsMutableNetwork.addEdge(N4, N5, E12);
+      fail(ERROR_ADDED_EXISTING_EDGE);
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE);
+    }
+    try {
+      // Edge between same nodes but in reverse direction
+      addEdge(N2, N1, E12);
+      fail(ERROR_ADDED_EXISTING_EDGE);
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE);
+    }
+  }
+
+  @Test
+  public void addEdge_parallelEdge_notAllowed() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsParallelEdges()).isFalse();
+
+    addEdge(N1, N2, E12);
+    try {
+      networkAsMutableNetwork.addEdge(N1, N2, EDGE_NOT_IN_GRAPH);
+      fail(ERROR_ADDED_PARALLEL_EDGE);
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessageThat().contains(ERROR_PARALLEL_EDGE);
+    }
+  }
+
+  @Test
+  public void addEdge_parallelEdge_allowsParallelEdges() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsParallelEdges()).isTrue();
+
+    assertTrue(networkAsMutableNetwork.addEdge(N1, N2, E12));
+    assertTrue(networkAsMutableNetwork.addEdge(N1, N2, E12_A));
+    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12, E12_A);
+  }
+
+  @Test
+  public void addEdge_orderMismatch() {
+    assume().that(graphIsMutable()).isTrue();
+
+    EndpointPair<Integer> endpoints = EndpointPair.unordered(N1, N2);
+    try {
+      networkAsMutableNetwork.addEdge(endpoints, E12);
+      fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH);
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH);
+    }
+  }
+
+  @Test
+  public void addEdge_selfLoop_notAllowed() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isFalse();
+
+    try {
+      networkAsMutableNetwork.addEdge(N1, N1, E11);
+      fail(ERROR_ADDED_SELF_LOOP);
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP);
+    }
+  }
+
+  /**
+   * This test checks an implementation dependent feature. It tests that the method {@code addEdge}
+   * will silently add the missing nodes to the graph, then add the edge connecting them. We are not
+   * using the proxy methods here as we want to test {@code addEdge} when the end-points are not
+   * elements of the graph.
+   */
+  @Test
+  public void addEdge_nodesNotInGraph() {
+    assume().that(graphIsMutable()).isTrue();
+
+    networkAsMutableNetwork.addNode(N1);
+    assertTrue(networkAsMutableNetwork.addEdge(N1, N5, E15));
+    assertTrue(networkAsMutableNetwork.addEdge(N4, N1, E41));
+    assertTrue(networkAsMutableNetwork.addEdge(N2, N3, E23));
+    assertThat(network.nodes()).containsExactly(N1, N5, N4, N2, N3);
+    assertThat(network.edges()).containsExactly(E15, E41, E23);
+    assertThat(network.edgesConnecting(N1, N5)).containsExactly(E15);
+    assertThat(network.edgesConnecting(N4, N1)).containsExactly(E41);
+    assertThat(network.edgesConnecting(N2, N3)).containsExactly(E23);
+    // Direction of the added edge is correctly handled
+    assertThat(network.edgesConnecting(N3, N2)).isEmpty();
+  }
+
+  @Test
+  public void addEdge_selfLoop_allowed() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    assertThat(networkAsMutableNetwork.addEdge(N1, N1, E11)).isTrue();
+    assertThat(network.edges()).contains(E11);
+    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
+  }
+
+  @Test
+  public void addEdge_existingSelfLoopEdgeBetweenSameNodes() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    ImmutableSet<String> edges = ImmutableSet.copyOf(network.edges());
+    assertThat(networkAsMutableNetwork.addEdge(N1, N1, E11)).isFalse();
+    assertThat(network.edges()).containsExactlyElementsIn(edges);
+  }
+
+  @Test
+  public void addEdge_existingEdgeBetweenDifferentNodes_selfLoops() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    try {
+      networkAsMutableNetwork.addEdge(N1, N2, E11);
+      fail("Reusing an existing self-loop edge to connect different nodes succeeded");
+    } catch (IllegalArgumentException e) {
+      assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE);
+    }
+    try {
+      networkAsMutableNetwork.addEdge(N2, N2, E11);
+      fail("Reusing an existing self-loop edge to make a different self-loop edge succeeded");
+    } catch (IllegalArgumentException e) {
+      assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE);
+    }
+    addEdge(N1, N2, E12);
+    try {
+      networkAsMutableNetwork.addEdge(N1, N1, E12);
+      fail("Reusing an existing edge to add a self-loop edge between different nodes succeeded");
+    } catch (IllegalArgumentException e) {
+      assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE);
+    }
+  }
+
+  @Test
+  public void addEdge_parallelSelfLoopEdge_notAllowed() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+    assume().that(network.allowsParallelEdges()).isFalse();
+
+    addEdge(N1, N1, E11);
+    try {
+      networkAsMutableNetwork.addEdge(N1, N1, EDGE_NOT_IN_GRAPH);
+      fail("Adding a parallel self-loop edge succeeded");
+    } catch (IllegalArgumentException e) {
+      assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE);
+    }
+  }
+
+  @Test
+  public void addEdge_parallelSelfLoopEdge_allowsParallelEdges() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+    assume().that(network.allowsParallelEdges()).isTrue();
+
+    assertTrue(networkAsMutableNetwork.addEdge(N1, N1, E11));
+    assertTrue(networkAsMutableNetwork.addEdge(N1, N1, E11_A));
+    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11, E11_A);
+  }
+
+  @Test
+  public void removeNode_existingNodeWithSelfLoopEdge() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addNode(N1);
+    addEdge(N1, N1, E11);
+    assertThat(networkAsMutableNetwork.removeNode(N1)).isTrue();
+    assertThat(network.nodes()).isEmpty();
+    assertThat(network.edges()).doesNotContain(E11);
+  }
+
+  @Test
+  public void removeEdge_existingSelfLoopEdge() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(networkAsMutableNetwork.removeEdge(E11)).isTrue();
+    assertThat(network.edges()).doesNotContain(E11);
+    assertThat(network.edgesConnecting(N1, N1)).isEmpty();
+  }
+}
diff --git a/android/guava-tests/test/com/google/common/graph/AbstractStandardUndirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/AbstractStandardUndirectedGraphTest.java
new file mode 100644
index 0000000..a483f42
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/graph/AbstractStandardUndirectedGraphTest.java
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2014 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 com.google.common.graph;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.testing.EqualsTester;
+import java.util.Set;
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Abstract base class for testing undirected {@link Graph} implementations defined in this package.
+ */
+public abstract class AbstractStandardUndirectedGraphTest extends AbstractGraphTest {
+
+  @After
+  public void validateUndirectedEdges() {
+    for (Integer node : graph.nodes()) {
+      new EqualsTester()
+          .addEqualityGroup(
+              graph.predecessors(node), graph.successors(node), graph.adjacentNodes(node))
+          .testEquals();
+    }
+  }
+
+  @Override
+  @Test
+  public void nodes_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    Set<Integer> nodes = graph.nodes();
+    try {
+      nodes.add(N2);
+      fail(ERROR_MODIFIABLE_SET);
+    } catch (UnsupportedOperationException e) {
+      addNode(N1);
+      assertThat(graph.nodes()).containsExactlyElementsIn(nodes);
+    }
+  }
+
+  @Override
+  @Test
+  public void adjacentNodes_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N1);
+    Set<Integer> adjacentNodes = graph.adjacentNodes(N1);
+    try {
+      adjacentNodes.add(N2);
+      fail(ERROR_MODIFIABLE_SET);
+    } catch (UnsupportedOperationException e) {
+      putEdge(N1, N2);
+      assertThat(graph.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes);
+    }
+  }
+
+  @Override
+  @Test
+  public void predecessors_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N2);
+    Set<Integer> predecessors = graph.predecessors(N2);
+    try {
+      predecessors.add(N1);
+      fail(ERROR_MODIFIABLE_SET);
+    } catch (UnsupportedOperationException e) {
+      putEdge(N1, N2);
+      assertThat(graph.predecessors(N2)).containsExactlyElementsIn(predecessors);
+    }
+  }
+
+  @Override
+  @Test
+  public void successors_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N1);
+    Set<Integer> successors = graph.successors(N1);
+    try {
+      successors.add(N2);
+      fail(ERROR_MODIFIABLE_SET);
+    } catch (UnsupportedOperationException e) {
+      putEdge(N1, N2);
+      assertThat(graph.successors(N1)).containsExactlyElementsIn(successors);
+    }
+  }
+
+  @Override
+  @Test
+  public void incidentEdges_checkReturnedSetMutability() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addNode(N1);
+    Set<EndpointPair<Integer>> incidentEdges = graph.incidentEdges(N1);
+    try {
+      incidentEdges.add(EndpointPair.unordered(N1, N2));
+      fail(ERROR_MODIFIABLE_SET);
+    } catch (UnsupportedOperationException e) {
+      putEdge(N1, N2);
+      assertThat(incidentEdges).containsExactlyElementsIn(graph.incidentEdges(N1));
+    }
+  }
+
+  @Test
+  public void predecessors_oneEdge() {
+    putEdge(N1, N2);
+    assertThat(graph.predecessors(N2)).containsExactly(N1);
+    assertThat(graph.predecessors(N1)).containsExactly(N2);
+  }
+
+  @Test
+  public void successors_oneEdge() {
+    putEdge(N1, N2);
+    assertThat(graph.successors(N1)).containsExactly(N2);
+    assertThat(graph.successors(N2)).containsExactly(N1);
+  }
+
+  @Test
+  public void incidentEdges_oneEdge() {
+    putEdge(N1, N2);
+    EndpointPair<Integer> expectedEndpoints = EndpointPair.unordered(N1, N2);
+    assertThat(graph.incidentEdges(N1)).containsExactly(expectedEndpoints);
+    assertThat(graph.incidentEdges(N2)).containsExactly(expectedEndpoints);
+  }
+
+  @Test
+  public void inDegree_oneEdge() {
+    putEdge(N1, N2);
+    assertThat(graph.inDegree(N2)).isEqualTo(1);
+    assertThat(graph.inDegree(N1)).isEqualTo(1);
+  }
+
+  @Test
+  public void outDegree_oneEdge() {
+    putEdge(N1, N2);
+    assertThat(graph.outDegree(N1)).isEqualTo(1);
+    assertThat(graph.outDegree(N2)).isEqualTo(1);
+  }
+
+  @Test
+  public void hasEdgeConnecting_correct() {
+    putEdge(N1, N2);
+    assertThat(graph.hasEdgeConnecting(EndpointPair.unordered(N1, N2))).isTrue();
+    assertThat(graph.hasEdgeConnecting(EndpointPair.unordered(N2, N1))).isTrue();
+  }
+
+  @Test
+  public void hasEdgeConnecting_mismatch() {
+    putEdge(N1, N2);
+    assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(N1, N2))).isTrue();
+    assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(N2, N1))).isTrue();
+  }
+
+  @Test
+  public void adjacentNodes_selfLoop() {
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    putEdge(N1, N2);
+    assertThat(graph.adjacentNodes(N1)).containsExactly(N1, N2);
+  }
+
+  @Test
+  public void predecessors_selfLoop() {
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graph.predecessors(N1)).containsExactly(N1);
+    putEdge(N1, N2);
+    assertThat(graph.predecessors(N1)).containsExactly(N1, N2);
+  }
+
+  @Test
+  public void successors_selfLoop() {
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graph.successors(N1)).containsExactly(N1);
+    putEdge(N2, N1);
+    assertThat(graph.successors(N1)).containsExactly(N1, N2);
+  }
+
+  @Test
+  public void incidentEdges_selfLoop() {
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graph.incidentEdges(N1)).containsExactly(EndpointPair.unordered(N1, N1));
+    putEdge(N1, N2);
+    assertThat(graph.incidentEdges(N1))
+        .containsExactly(EndpointPair.unordered(N1, N1), EndpointPair.unordered(N1, N2));
+  }
+
+  @Test
+  public void degree_selfLoop() {
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graph.degree(N1)).isEqualTo(2);
+    putEdge(N1, N2);
+    assertThat(graph.degree(N1)).isEqualTo(3);
+  }
+
+  @Test
+  public void inDegree_selfLoop() {
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graph.inDegree(N1)).isEqualTo(2);
+    putEdge(N1, N2);
+    assertThat(graph.inDegree(N1)).isEqualTo(3);
+  }
+
+  @Test
+  public void outDegree_selfLoop() {
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graph.outDegree(N1)).isEqualTo(2);
+    putEdge(N2, N1);
+    assertThat(graph.outDegree(N1)).isEqualTo(3);
+  }
+
+  // Stable order tests
+
+  // Note: Stable order means that the ordering doesn't change between iterations and versions.
+  // Ideally, the ordering in test should never be updated.
+  @Test
+  public void stableIncidentEdgeOrder_edges_returnsInStableOrder() {
+    assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
+
+    populateTShapedGraph();
+
+    assertThat(graph.edges())
+        .containsExactly(
+            EndpointPair.unordered(1, 2),
+            EndpointPair.unordered(1, 4),
+            EndpointPair.unordered(1, 3),
+            EndpointPair.unordered(4, 5))
+        .inOrder();
+  }
+
+  @Test
+  public void stableIncidentEdgeOrder_adjacentNodes_returnsInConnectingEdgeInsertionOrder() {
+    assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
+
+    populateTShapedGraph();
+
+    assertThat(graph.adjacentNodes(1)).containsExactly(2, 4, 3).inOrder();
+  }
+
+  @Test
+  public void stableIncidentEdgeOrder_predecessors_returnsInConnectingEdgeInsertionOrder() {
+    assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
+
+    populateTShapedGraph();
+
+    assertThat(graph.adjacentNodes(1)).containsExactly(2, 4, 3).inOrder();
+  }
+
+  @Test
+  public void stableIncidentEdgeOrder_successors_returnsInConnectingEdgeInsertionOrder() {
+    assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
+
+    populateTShapedGraph();
+
+    assertThat(graph.adjacentNodes(1)).containsExactly(2, 4, 3).inOrder();
+  }
+
+  @Test
+  public void stableIncidentEdgeOrder_incidentEdges_returnsInEdgeInsertionOrder() {
+    assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
+
+    populateTShapedGraph();
+
+    assertThat(graph.incidentEdges(1))
+        .containsExactly(
+            EndpointPair.unordered(1, 2),
+            EndpointPair.unordered(1, 4),
+            EndpointPair.unordered(1, 3))
+        .inOrder();
+  }
+
+  @Test
+  public void stableIncidentEdgeOrder_incidentEdges_withSelfLoop_returnsInEdgeInsertionOrder() {
+    assume().that(graph.incidentEdgeOrder().type()).isEqualTo(ElementOrder.Type.STABLE);
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(2, 1);
+    putEdge(1, 1);
+    putEdge(1, 3);
+
+    assertThat(graph.incidentEdges(1))
+        .containsExactly(
+            EndpointPair.unordered(2, 1),
+            EndpointPair.unordered(1, 1),
+            EndpointPair.unordered(1, 3))
+        .inOrder();
+  }
+
+  /**
+   * Populates the graph with nodes and edges in a star shape with node `1` in the middle.
+   *
+   * <p>Note that the edges are added in a shuffled order to properly test the effect of the
+   * insertion order.
+   */
+  private void populateTShapedGraph() {
+    putEdge(2, 1);
+    putEdge(1, 4);
+    putEdge(1, 3);
+    putEdge(1, 2); // Duplicate
+    putEdge(4, 5);
+  }
+
+  // Element Mutation
+
+  @Test
+  public void putEdge_existingNodes() {
+    assume().that(graphIsMutable()).isTrue();
+
+    // Adding nodes initially for safety (insulating from possible future
+    // modifications to proxy methods)
+    addNode(N1);
+    addNode(N2);
+
+    assertThat(graphAsMutableGraph.putEdge(N1, N2)).isTrue();
+  }
+
+  @Test
+  public void putEdge_existingEdgeBetweenSameNodes() {
+    assume().that(graphIsMutable()).isTrue();
+
+    putEdge(N1, N2);
+
+    assertThat(graphAsMutableGraph.putEdge(N2, N1)).isFalse();
+  }
+
+  /**
+   * Tests that the method {@code putEdge} will silently add the missing nodes to the graph, then
+   * add the edge connecting them. We are not using the proxy methods here as we want to test {@code
+   * putEdge} when the end-points are not elements of the graph.
+   */
+  @Test
+  public void putEdge_nodesNotInGraph() {
+    assume().that(graphIsMutable()).isTrue();
+
+    graphAsMutableGraph.addNode(N1);
+    assertTrue(graphAsMutableGraph.putEdge(N1, N5));
+    assertTrue(graphAsMutableGraph.putEdge(N4, N1));
+    assertTrue(graphAsMutableGraph.putEdge(N2, N3));
+    assertThat(graph.nodes()).containsExactly(N1, N5, N4, N2, N3).inOrder();
+    assertThat(graph.adjacentNodes(N1)).containsExactly(N4, N5);
+    assertThat(graph.adjacentNodes(N2)).containsExactly(N3);
+    assertThat(graph.adjacentNodes(N3)).containsExactly(N2);
+    assertThat(graph.adjacentNodes(N4)).containsExactly(N1);
+    assertThat(graph.adjacentNodes(N5)).containsExactly(N1);
+  }
+
+  @Test
+  public void putEdge_doesntAllowSelfLoops() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(graph.allowsSelfLoops()).isFalse();
+
+    try {
+      putEdge(N1, N1);
+      fail(ERROR_ADDED_SELF_LOOP);
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP);
+    }
+  }
+
+  @Test
+  public void putEdge_allowsSelfLoops() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    assertThat(graphAsMutableGraph.putEdge(N1, N1)).isTrue();
+    assertThat(graph.adjacentNodes(N1)).containsExactly(N1);
+  }
+
+  @Test
+  public void putEdge_existingSelfLoopEdgeBetweenSameNodes() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graphAsMutableGraph.putEdge(N1, N1)).isFalse();
+  }
+
+  @Test
+  public void removeEdge_antiparallelEdges() {
+    assume().that(graphIsMutable()).isTrue();
+
+    putEdge(N1, N2);
+    putEdge(N2, N1); // no-op
+
+    assertThat(graphAsMutableGraph.removeEdge(N1, N2)).isTrue();
+    assertThat(graph.adjacentNodes(N1)).isEmpty();
+    assertThat(graph.edges()).isEmpty();
+    assertThat(graphAsMutableGraph.removeEdge(N2, N1)).isFalse();
+  }
+
+  @Test
+  public void removeNode_existingNodeWithSelfLoopEdge() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    addNode(N1);
+    putEdge(N1, N1);
+    assertThat(graphAsMutableGraph.removeNode(N1)).isTrue();
+    assertThat(graph.nodes()).isEmpty();
+  }
+
+  @Test
+  public void removeEdge_existingSelfLoopEdge() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(graph.allowsSelfLoops()).isTrue();
+
+    putEdge(N1, N1);
+    assertThat(graphAsMutableGraph.removeEdge(N1, N1)).isTrue();
+    assertThat(graph.nodes()).containsExactly(N1);
+    assertThat(graph.adjacentNodes(N1)).isEmpty();
+  }
+}
diff --git a/android/guava-tests/test/com/google/common/graph/AbstractStandardUndirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/AbstractStandardUndirectedNetworkTest.java
new file mode 100644
index 0000000..5cda1c1
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/graph/AbstractStandardUndirectedNetworkTest.java
@@ -0,0 +1,595 @@
+/*
+ * Copyright (C) 2014 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 com.google.common.graph;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.testing.EqualsTester;
+import java.util.Set;
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Abstract base class for testing undirected {@link Network} implementations defined in this
+ * package.
+ */
+public abstract class AbstractStandardUndirectedNetworkTest extends AbstractNetworkTest {
+  private static final EndpointPair<Integer> ENDPOINTS_N1N2 = EndpointPair.ordered(N1, N2);
+  private static final EndpointPair<Integer> ENDPOINTS_N2N1 = EndpointPair.ordered(N2, N1);
+
+  @After
+  public void validateUndirectedEdges() {
+    for (Integer node : network.nodes()) {
+      new EqualsTester()
+          .addEqualityGroup(
+              network.inEdges(node), network.outEdges(node), network.incidentEdges(node))
+          .testEquals();
+      new EqualsTester()
+          .addEqualityGroup(
+              network.predecessors(node), network.successors(node), network.adjacentNodes(node))
+          .testEquals();
+
+      for (Integer adjacentNode : network.adjacentNodes(node)) {
+        assertThat(network.edgesConnecting(node, adjacentNode))
+            .containsExactlyElementsIn(network.edgesConnecting(adjacentNode, node));
+      }
+    }
+  }
+
+  @Override
+  @Test
+  public void nodes_checkReturnedSetMutability() {
+    Set<Integer> nodes = network.nodes();
+    try {
+      nodes.add(N2);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addNode(N1);
+      assertThat(network.nodes()).containsExactlyElementsIn(nodes);
+    }
+  }
+
+  @Override
+  @Test
+  public void edges_checkReturnedSetMutability() {
+    Set<String> edges = network.edges();
+    try {
+      edges.add(E12);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.edges()).containsExactlyElementsIn(edges);
+    }
+  }
+
+  @Override
+  @Test
+  public void incidentEdges_checkReturnedSetMutability() {
+    addNode(N1);
+    Set<String> incidentEdges = network.incidentEdges(N1);
+    try {
+      incidentEdges.add(E12);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.incidentEdges(N1)).containsExactlyElementsIn(incidentEdges);
+    }
+  }
+
+  @Override
+  @Test
+  public void adjacentNodes_checkReturnedSetMutability() {
+    addNode(N1);
+    Set<Integer> adjacentNodes = network.adjacentNodes(N1);
+    try {
+      adjacentNodes.add(N2);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes);
+    }
+  }
+
+  @Override
+  public void adjacentEdges_checkReturnedSetMutability() {
+    addEdge(N1, N2, E12);
+    Set<String> adjacentEdges = network.adjacentEdges(E12);
+    try {
+      adjacentEdges.add(E23);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N2, N3, E23);
+      assertThat(network.adjacentEdges(E12)).containsExactlyElementsIn(adjacentEdges);
+    }
+  }
+
+  @Override
+  @Test
+  public void edgesConnecting_checkReturnedSetMutability() {
+    addNode(N1);
+    addNode(N2);
+    Set<String> edgesConnecting = network.edgesConnecting(N1, N2);
+    try {
+      edgesConnecting.add(E23);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.edgesConnecting(N1, N2)).containsExactlyElementsIn(edgesConnecting);
+    }
+  }
+
+  @Override
+  @Test
+  public void inEdges_checkReturnedSetMutability() {
+    addNode(N2);
+    Set<String> inEdges = network.inEdges(N2);
+    try {
+      inEdges.add(E12);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.inEdges(N2)).containsExactlyElementsIn(inEdges);
+    }
+  }
+
+  @Override
+  @Test
+  public void outEdges_checkReturnedSetMutability() {
+    addNode(N1);
+    Set<String> outEdges = network.outEdges(N1);
+    try {
+      outEdges.add(E12);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.outEdges(N1)).containsExactlyElementsIn(outEdges);
+    }
+  }
+
+  @Override
+  @Test
+  public void predecessors_checkReturnedSetMutability() {
+    addNode(N2);
+    Set<Integer> predecessors = network.predecessors(N2);
+    try {
+      predecessors.add(N1);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.predecessors(N2)).containsExactlyElementsIn(predecessors);
+    }
+  }
+
+  @Override
+  @Test
+  public void successors_checkReturnedSetMutability() {
+    addNode(N1);
+    Set<Integer> successors = network.successors(N1);
+    try {
+      successors.add(N2);
+      fail(ERROR_MODIFIABLE_COLLECTION);
+    } catch (UnsupportedOperationException e) {
+      addEdge(N1, N2, E12);
+      assertThat(network.successors(N1)).containsExactlyElementsIn(successors);
+    }
+  }
+
+  @Test
+  public void edges_containsOrderMismatch() {
+    addEdge(N1, N2, E12);
+    assertThat(network.asGraph().edges()).contains(ENDPOINTS_N2N1);
+    assertThat(network.asGraph().edges()).contains(ENDPOINTS_N1N2);
+  }
+
+  @Test
+  public void edgeConnectingOrNull_orderMismatch() {
+    addEdge(N1, N2, E12);
+    assertThat(network.edgeConnectingOrNull(ENDPOINTS_N2N1)).isEqualTo(E12);
+    assertThat(network.edgeConnectingOrNull(ENDPOINTS_N1N2)).isEqualTo(E12);
+  }
+
+  @Test
+  public void edgesConnecting_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
+    assertThat(network.edgesConnecting(N2, N1)).containsExactly(E12);
+  }
+
+  @Test
+  public void inEdges_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.inEdges(N2)).containsExactly(E12);
+    assertThat(network.inEdges(N1)).containsExactly(E12);
+  }
+
+  @Test
+  public void outEdges_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.outEdges(N2)).containsExactly(E12);
+    assertThat(network.outEdges(N1)).containsExactly(E12);
+  }
+
+  @Test
+  public void predecessors_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.predecessors(N2)).containsExactly(N1);
+    assertThat(network.predecessors(N1)).containsExactly(N2);
+  }
+
+  @Test
+  public void successors_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.successors(N1)).containsExactly(N2);
+    assertThat(network.successors(N2)).containsExactly(N1);
+  }
+
+  @Test
+  public void inDegree_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.inDegree(N2)).isEqualTo(1);
+    assertThat(network.inDegree(N1)).isEqualTo(1);
+  }
+
+  @Test
+  public void outDegree_oneEdge() {
+    addEdge(N1, N2, E12);
+    assertThat(network.outDegree(N1)).isEqualTo(1);
+    assertThat(network.outDegree(N2)).isEqualTo(1);
+  }
+
+  @Test
+  public void edges_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.edges()).containsExactly(E11);
+  }
+
+  @Test
+  public void incidentEdges_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.incidentEdges(N1)).containsExactly(E11);
+  }
+
+  @Test
+  public void incidentNodes_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.incidentNodes(E11).nodeU()).isEqualTo(N1);
+    assertThat(network.incidentNodes(E11).nodeV()).isEqualTo(N1);
+  }
+
+  @Test
+  public void adjacentNodes_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    addEdge(N1, N2, E12);
+    assertThat(network.adjacentNodes(N1)).containsExactly(N1, N2);
+  }
+
+  @Test
+  public void adjacentEdges_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    addEdge(N1, N2, E12);
+    assertThat(network.adjacentEdges(E11)).containsExactly(E12);
+  }
+
+  @Test
+  public void edgesConnecting_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
+    addEdge(N1, N2, E12);
+    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
+    assertThat(network.edgesConnecting(N2, N1)).containsExactly(E12);
+    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
+  }
+
+  @Test
+  public void inEdges_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.inEdges(N1)).containsExactly(E11);
+    addEdge(N1, N2, E12);
+    assertThat(network.inEdges(N1)).containsExactly(E11, E12);
+  }
+
+  @Test
+  public void outEdges_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.outEdges(N1)).containsExactly(E11);
+    addEdge(N2, N1, E12);
+    assertThat(network.outEdges(N1)).containsExactly(E11, E12);
+  }
+
+  @Test
+  public void predecessors_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.predecessors(N1)).containsExactly(N1);
+    addEdge(N1, N2, E12);
+    assertThat(network.predecessors(N1)).containsExactly(N1, N2);
+  }
+
+  @Test
+  public void successors_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.successors(N1)).containsExactly(N1);
+    addEdge(N2, N1, E12);
+    assertThat(network.successors(N1)).containsExactly(N1, N2);
+  }
+
+  @Test
+  public void degree_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.degree(N1)).isEqualTo(2);
+    addEdge(N1, N2, E12);
+    assertThat(network.degree(N1)).isEqualTo(3);
+  }
+
+  @Test
+  public void inDegree_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.inDegree(N1)).isEqualTo(2);
+    addEdge(N1, N2, E12);
+    assertThat(network.inDegree(N1)).isEqualTo(3);
+  }
+
+  @Test
+  public void outDegree_selfLoop() {
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(network.outDegree(N1)).isEqualTo(2);
+    addEdge(N2, N1, E12);
+    assertThat(network.outDegree(N1)).isEqualTo(3);
+  }
+
+  // Element Mutation
+
+  @Test
+  public void addEdge_existingNodes() {
+    assume().that(graphIsMutable()).isTrue();
+
+    // Adding nodes initially for safety (insulating from possible future
+    // modifications to proxy methods)
+    addNode(N1);
+    addNode(N2);
+    assertThat(networkAsMutableNetwork.addEdge(N1, N2, E12)).isTrue();
+    assertThat(network.edges()).contains(E12);
+    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
+    assertThat(network.edgesConnecting(N2, N1)).containsExactly(E12);
+  }
+
+  @Test
+  public void addEdge_existingEdgeBetweenSameNodes() {
+    assume().that(graphIsMutable()).isTrue();
+
+    assertThat(networkAsMutableNetwork.addEdge(N1, N2, E12)).isTrue();
+    ImmutableSet<String> edges = ImmutableSet.copyOf(network.edges());
+    assertThat(networkAsMutableNetwork.addEdge(N1, N2, E12)).isFalse();
+    assertThat(network.edges()).containsExactlyElementsIn(edges);
+    assertThat(networkAsMutableNetwork.addEdge(N2, N1, E12)).isFalse();
+    assertThat(network.edges()).containsExactlyElementsIn(edges);
+  }
+
+  @Test
+  public void addEdge_existingEdgeBetweenDifferentNodes() {
+    assume().that(graphIsMutable()).isTrue();
+
+    addEdge(N1, N2, E12);
+    try {
+      // Edge between totally different nodes
+      networkAsMutableNetwork.addEdge(N4, N5, E12);
+      fail(ERROR_ADDED_EXISTING_EDGE);
+    } catch (IllegalArgumentException e) {
+      assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE);
+    }
+  }
+
+  @Test
+  public void addEdge_parallelEdge_notAllowed() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsParallelEdges()).isFalse();
+
+    addEdge(N1, N2, E12);
+    try {
+      networkAsMutableNetwork.addEdge(N1, N2, EDGE_NOT_IN_GRAPH);
+      fail(ERROR_ADDED_PARALLEL_EDGE);
+    } catch (IllegalArgumentException e) {
+      assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE);
+    }
+    try {
+      networkAsMutableNetwork.addEdge(N2, N1, EDGE_NOT_IN_GRAPH);
+      fail(ERROR_ADDED_PARALLEL_EDGE);
+    } catch (IllegalArgumentException e) {
+      assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE);
+    }
+  }
+
+  @Test
+  public void addEdge_parallelEdge_allowsParallelEdges() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsParallelEdges()).isTrue();
+
+    assertTrue(networkAsMutableNetwork.addEdge(N1, N2, E12));
+    assertTrue(networkAsMutableNetwork.addEdge(N2, N1, E21));
+    assertTrue(networkAsMutableNetwork.addEdge(N1, N2, E12_A));
+    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12, E12_A, E21);
+  }
+
+  @Test
+  public void addEdge_orderMismatch() {
+    assume().that(graphIsMutable()).isTrue();
+
+    EndpointPair<Integer> endpoints = EndpointPair.ordered(N1, N2);
+    assertThat(networkAsMutableNetwork.addEdge(endpoints, E12)).isTrue();
+  }
+
+  @Test
+  public void addEdge_selfLoop_notAllowed() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isFalse();
+
+    try {
+      networkAsMutableNetwork.addEdge(N1, N1, E11);
+      fail(ERROR_ADDED_SELF_LOOP);
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP);
+    }
+  }
+
+  /**
+   * This test checks an implementation dependent feature. It tests that the method {@code addEdge}
+   * will silently add the missing nodes to the graph, then add the edge connecting them. We are not
+   * using the proxy methods here as we want to test {@code addEdge} when the end-points are not
+   * elements of the graph.
+   */
+  @Test
+  public void addEdge_nodesNotInGraph() {
+    assume().that(graphIsMutable()).isTrue();
+
+    networkAsMutableNetwork.addNode(N1);
+    assertTrue(networkAsMutableNetwork.addEdge(N1, N5, E15));
+    assertTrue(networkAsMutableNetwork.addEdge(N4, N1, E41));
+    assertTrue(networkAsMutableNetwork.addEdge(N2, N3, E23));
+    assertThat(network.nodes()).containsExactly(N1, N5, N4, N2, N3);
+    assertThat(network.edges()).containsExactly(E15, E41, E23);
+    assertThat(network.edgesConnecting(N1, N5)).containsExactly(E15);
+    assertThat(network.edgesConnecting(N4, N1)).containsExactly(E41);
+    assertThat(network.edgesConnecting(N2, N3)).containsExactly(E23);
+    assertThat(network.edgesConnecting(N3, N2)).containsExactly(E23);
+  }
+
+  @Test
+  public void addEdge_selfLoop() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    assertThat(networkAsMutableNetwork.addEdge(N1, N1, E11)).isTrue();
+    assertThat(network.edges()).contains(E11);
+    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
+  }
+
+  @Test
+  public void addEdge_existingSelfLoopEdgeBetweenSameNodes() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    ImmutableSet<String> edges = ImmutableSet.copyOf(network.edges());
+    assertThat(networkAsMutableNetwork.addEdge(N1, N1, E11)).isFalse();
+    assertThat(network.edges()).containsExactlyElementsIn(edges);
+  }
+
+  @Test
+  public void addEdge_existingEdgeBetweenDifferentNodes_selfLoops() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    try {
+      networkAsMutableNetwork.addEdge(N1, N2, E11);
+      fail("Reusing an existing self-loop edge to connect different nodes succeeded");
+    } catch (IllegalArgumentException e) {
+      assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE);
+    }
+    try {
+      networkAsMutableNetwork.addEdge(N2, N2, E11);
+      fail("Reusing an existing self-loop edge to make a different self-loop edge succeeded");
+    } catch (IllegalArgumentException e) {
+      assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE);
+    }
+    addEdge(N1, N2, E12);
+    try {
+      networkAsMutableNetwork.addEdge(N1, N1, E12);
+      fail("Reusing an existing edge to add a self-loop edge between different nodes succeeded");
+    } catch (IllegalArgumentException e) {
+      assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE);
+    }
+  }
+
+  @Test
+  public void addEdge_parallelSelfLoopEdge_notAllowed() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+    assume().that(network.allowsParallelEdges()).isFalse();
+
+    addEdge(N1, N1, E11);
+    try {
+      networkAsMutableNetwork.addEdge(N1, N1, EDGE_NOT_IN_GRAPH);
+      fail("Adding a parallel self-loop edge succeeded");
+    } catch (IllegalArgumentException e) {
+      assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE);
+    }
+  }
+
+  @Test
+  public void addEdge_parallelSelfLoopEdge_allowsParallelEdges() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+    assume().that(network.allowsParallelEdges()).isTrue();
+
+    assertTrue(networkAsMutableNetwork.addEdge(N1, N1, E11));
+    assertTrue(networkAsMutableNetwork.addEdge(N1, N1, E11_A));
+    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11, E11_A);
+  }
+
+  @Test
+  public void removeNode_existingNodeWithSelfLoopEdge() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addNode(N1);
+    addEdge(N1, N1, E11);
+    assertThat(networkAsMutableNetwork.removeNode(N1)).isTrue();
+    assertThat(network.nodes()).isEmpty();
+    assertThat(network.edges()).doesNotContain(E11);
+  }
+
+  @Test
+  public void removeEdge_existingSelfLoopEdge() {
+    assume().that(graphIsMutable()).isTrue();
+    assume().that(network.allowsSelfLoops()).isTrue();
+
+    addEdge(N1, N1, E11);
+    assertThat(networkAsMutableNetwork.removeEdge(E11)).isTrue();
+    assertThat(network.edges()).doesNotContain(E11);
+    assertThat(network.edgesConnecting(N1, N1)).isEmpty();
+  }
+}
diff --git a/android/guava-tests/test/com/google/common/graph/AbstractUndirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/AbstractUndirectedGraphTest.java
deleted file mode 100644
index 4996a6a..0000000
--- a/android/guava-tests/test/com/google/common/graph/AbstractUndirectedGraphTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.common.testing.EqualsTester;
-import org.junit.After;
-import org.junit.Test;
-
-/**
- * Abstract base class for testing undirected implementations of the {@link Graph} interface.
- *
- * <p>This class is responsible for testing that an undirected implementation of {@link Graph} is
- * correctly handling undirected edges. Implementation-dependent test cases are left to subclasses.
- * Test cases that do not require the graph to be undirected are found in superclasses.
- */
-public abstract class AbstractUndirectedGraphTest extends AbstractGraphTest {
-
-  @After
-  public void validateUndirectedEdges() {
-    for (Integer node : graph.nodes()) {
-      new EqualsTester()
-          .addEqualityGroup(
-              graph.predecessors(node), graph.successors(node), graph.adjacentNodes(node))
-          .testEquals();
-    }
-  }
-
-  @Test
-  public void predecessors_oneEdge() {
-    putEdge(N1, N2);
-    assertThat(graph.predecessors(N2)).containsExactly(N1);
-    assertThat(graph.predecessors(N1)).containsExactly(N2);
-  }
-
-  @Test
-  public void successors_oneEdge() {
-    putEdge(N1, N2);
-    assertThat(graph.successors(N1)).containsExactly(N2);
-    assertThat(graph.successors(N2)).containsExactly(N1);
-  }
-
-  @Test
-  public void incidentEdges_oneEdge() {
-    putEdge(N1, N2);
-    EndpointPair<Integer> expectedEndpoints = EndpointPair.unordered(N1, N2);
-    assertThat(graph.incidentEdges(N1)).containsExactly(expectedEndpoints);
-    assertThat(graph.incidentEdges(N2)).containsExactly(expectedEndpoints);
-  }
-
-  @Test
-  public void inDegree_oneEdge() {
-    putEdge(N1, N2);
-    assertThat(graph.inDegree(N2)).isEqualTo(1);
-    assertThat(graph.inDegree(N1)).isEqualTo(1);
-  }
-
-  @Test
-  public void outDegree_oneEdge() {
-    putEdge(N1, N2);
-    assertThat(graph.outDegree(N1)).isEqualTo(1);
-    assertThat(graph.outDegree(N2)).isEqualTo(1);
-  }
-
-  @Test
-  public void hasEdgeConnecting_correct() {
-    putEdge(N1, N2);
-    assertThat(graph.hasEdgeConnecting(EndpointPair.unordered(N1, N2))).isTrue();
-    assertThat(graph.hasEdgeConnecting(EndpointPair.unordered(N2, N1))).isTrue();
-  }
-
-  @Test
-  public void hasEdgeConnecting_mismatch() {
-    putEdge(N1, N2);
-    assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(N1, N2))).isTrue();
-    assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(N2, N1))).isTrue();
-  }
-
-  // Element Mutation
-
-  @Test
-  public void addEdge_existingNodes() {
-    // Adding nodes initially for safety (insulating from possible future
-    // modifications to proxy methods)
-    addNode(N1);
-    addNode(N2);
-    assertThat(putEdge(N1, N2)).isTrue();
-  }
-
-  @Test
-  public void addEdge_existingEdgeBetweenSameNodes() {
-    putEdge(N1, N2);
-    assertThat(putEdge(N2, N1)).isFalse();
-  }
-
-  @Test
-  public void removeEdge_antiparallelEdges() {
-    putEdge(N1, N2);
-    putEdge(N2, N1); // no-op
-
-    assertThat(graph.removeEdge(N1, N2)).isTrue();
-    assertThat(graph.adjacentNodes(N1)).isEmpty();
-    assertThat(graph.edges()).isEmpty();
-    assertThat(graph.removeEdge(N2, N1)).isFalse();
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/AbstractUndirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/AbstractUndirectedNetworkTest.java
deleted file mode 100644
index 630ec02..0000000
--- a/android/guava-tests/test/com/google/common/graph/AbstractUndirectedNetworkTest.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.testing.EqualsTester;
-import org.junit.After;
-import org.junit.Test;
-
-/**
- * Abstract base class for testing undirected implementations of the {@link Network} interface.
- *
- * <p>This class is responsible for testing that an undirected implementation of {@link Network} is
- * correctly handling undirected edges. Implementation-dependent test cases are left to subclasses.
- * Test cases that do not require the graph to be undirected are found in superclasses.
- */
-public abstract class AbstractUndirectedNetworkTest extends AbstractNetworkTest {
-  private static final EndpointPair<Integer> ENDPOINTS_N1N2 = EndpointPair.ordered(N1, N2);
-  private static final EndpointPair<Integer> ENDPOINTS_N2N1 = EndpointPair.ordered(N2, N1);
-
-  @After
-  public void validateUndirectedEdges() {
-    for (Integer node : network.nodes()) {
-      new EqualsTester()
-          .addEqualityGroup(
-              network.inEdges(node), network.outEdges(node), network.incidentEdges(node))
-          .testEquals();
-      new EqualsTester()
-          .addEqualityGroup(
-              network.predecessors(node), network.successors(node), network.adjacentNodes(node))
-          .testEquals();
-
-      for (Integer adjacentNode : network.adjacentNodes(node)) {
-        assertThat(network.edgesConnecting(node, adjacentNode))
-            .containsExactlyElementsIn(network.edgesConnecting(adjacentNode, node));
-      }
-    }
-  }
-
-  @Test
-  public void edges_containsOrderMismatch() {
-    addEdge(N1, N2, E12);
-    assertThat(network.asGraph().edges()).contains(ENDPOINTS_N2N1);
-    assertThat(network.asGraph().edges()).contains(ENDPOINTS_N1N2);
-  }
-
-  @Test
-  public void edgesConnecting_orderMismatch() {
-    addEdge(N1, N2, E12);
-    assertThat(network.edgesConnecting(ENDPOINTS_N2N1)).containsExactly(E12);
-    assertThat(network.edgesConnecting(ENDPOINTS_N1N2)).containsExactly(E12);
-  }
-
-  @Test
-  public void edgeConnectingOrNull_orderMismatch() {
-    addEdge(N1, N2, E12);
-    assertThat(network.edgeConnectingOrNull(ENDPOINTS_N2N1)).isEqualTo(E12);
-    assertThat(network.edgeConnectingOrNull(ENDPOINTS_N1N2)).isEqualTo(E12);
-  }
-
-  @Test
-  public void edgesConnecting_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
-    assertThat(network.edgesConnecting(N2, N1)).containsExactly(E12);
-  }
-
-  @Test
-  public void inEdges_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.inEdges(N2)).containsExactly(E12);
-    assertThat(network.inEdges(N1)).containsExactly(E12);
-  }
-
-  @Test
-  public void outEdges_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.outEdges(N2)).containsExactly(E12);
-    assertThat(network.outEdges(N1)).containsExactly(E12);
-  }
-
-  @Test
-  public void predecessors_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.predecessors(N2)).containsExactly(N1);
-    assertThat(network.predecessors(N1)).containsExactly(N2);
-  }
-
-  @Test
-  public void successors_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.successors(N1)).containsExactly(N2);
-    assertThat(network.successors(N2)).containsExactly(N1);
-  }
-
-  @Test
-  public void inDegree_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.inDegree(N2)).isEqualTo(1);
-    assertThat(network.inDegree(N1)).isEqualTo(1);
-  }
-
-  @Test
-  public void outDegree_oneEdge() {
-    addEdge(N1, N2, E12);
-    assertThat(network.outDegree(N1)).isEqualTo(1);
-    assertThat(network.outDegree(N2)).isEqualTo(1);
-  }
-
-  // Element Mutation
-
-  @Test
-  public void addEdge_existingNodes() {
-    // Adding nodes initially for safety (insulating from possible future
-    // modifications to proxy methods)
-    addNode(N1);
-    addNode(N2);
-    assertThat(addEdge(N1, N2, E12)).isTrue();
-    assertThat(network.edges()).contains(E12);
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
-    assertThat(network.edgesConnecting(N2, N1)).containsExactly(E12);
-  }
-
-  @Test
-  public void addEdge_existingEdgeBetweenSameNodes() {
-    assertThat(addEdge(N1, N2, E12)).isTrue();
-    ImmutableSet<String> edges = ImmutableSet.copyOf(network.edges());
-    assertThat(addEdge(N1, N2, E12)).isFalse();
-    assertThat(network.edges()).containsExactlyElementsIn(edges);
-    assertThat(addEdge(N2, N1, E12)).isFalse();
-    assertThat(network.edges()).containsExactlyElementsIn(edges);
-  }
-
-  @Test
-  public void addEdge_existingEdgeBetweenDifferentNodes() {
-    addEdge(N1, N2, E12);
-    try {
-      // Edge between totally different nodes
-      addEdge(N4, N5, E12);
-      fail(ERROR_ADDED_EXISTING_EDGE);
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE);
-    }
-  }
-
-  @Test
-  public void addEdge_parallelEdge() {
-    addEdge(N1, N2, E12);
-    try {
-      addEdge(N1, N2, EDGE_NOT_IN_GRAPH);
-      fail(ERROR_ADDED_PARALLEL_EDGE);
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE);
-    }
-    try {
-      addEdge(N2, N1, EDGE_NOT_IN_GRAPH);
-      fail(ERROR_ADDED_PARALLEL_EDGE);
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE);
-    }
-  }
-
-  @Test
-  public void addEdge_orderMismatch() {
-    EndpointPair<Integer> endpoints = EndpointPair.ordered(N1, N2);
-    assertThat(addEdge(endpoints, E12)).isTrue();
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/ConfigurableDirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/ConfigurableDirectedGraphTest.java
deleted file mode 100644
index ed998eb..0000000
--- a/android/guava-tests/test/com/google/common/graph/ConfigurableDirectedGraphTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/** Tests for a directed {@link ConfigurableMutableGraph} allowing self-loops. */
-@RunWith(JUnit4.class)
-public class ConfigurableDirectedGraphTest extends ConfigurableSimpleDirectedGraphTest {
-
-  @Override
-  public MutableGraph<Integer> createGraph() {
-    return GraphBuilder.directed().allowsSelfLoops(true).build();
-  }
-
-  @Test
-  public void adjacentNodes_selfLoop() {
-    putEdge(N1, N1);
-    putEdge(N1, N2);
-    assertThat(graph.adjacentNodes(N1)).containsExactly(N1, N2);
-  }
-
-  @Test
-  public void predecessors_selfLoop() {
-    putEdge(N1, N1);
-    assertThat(graph.predecessors(N1)).containsExactly(N1);
-    putEdge(N4, N1);
-    assertThat(graph.predecessors(N1)).containsExactly(N1, N4);
-  }
-
-  @Test
-  public void successors_selfLoop() {
-    putEdge(N1, N1);
-    assertThat(graph.successors(N1)).containsExactly(N1);
-    putEdge(N1, N2);
-    assertThat(graph.successors(N1)).containsExactly(N1, N2);
-  }
-
-  @Test
-  public void incidentEdges_selfLoop() {
-    putEdge(N1, N1);
-    assertThat(graph.incidentEdges(N1)).containsExactly(EndpointPair.ordered(N1, N1));
-    putEdge(N1, N2);
-    assertThat(graph.incidentEdges(N1))
-        .containsExactly(EndpointPair.ordered(N1, N1), EndpointPair.ordered(N1, N2));
-  }
-
-  @Test
-  public void degree_selfLoop() {
-    putEdge(N1, N1);
-    assertThat(graph.degree(N1)).isEqualTo(2);
-    putEdge(N1, N2);
-    assertThat(graph.degree(N1)).isEqualTo(3);
-  }
-
-  @Test
-  public void inDegree_selfLoop() {
-    putEdge(N1, N1);
-    assertThat(graph.inDegree(N1)).isEqualTo(1);
-    putEdge(N4, N1);
-    assertThat(graph.inDegree(N1)).isEqualTo(2);
-  }
-
-  @Test
-  public void outDegree_selfLoop() {
-    putEdge(N1, N1);
-    assertThat(graph.outDegree(N1)).isEqualTo(1);
-    putEdge(N1, N2);
-    assertThat(graph.outDegree(N1)).isEqualTo(2);
-  }
-
-  @Override
-  @Test
-  public void addEdge_selfLoop() {
-    assertThat(putEdge(N1, N1)).isTrue();
-    assertThat(graph.successors(N1)).containsExactly(N1);
-    assertThat(graph.predecessors(N1)).containsExactly(N1);
-  }
-
-  @Test
-  public void addEdge_existingSelfLoopEdgeBetweenSameNodes() {
-    putEdge(N1, N1);
-    assertThat(putEdge(N1, N1)).isFalse();
-  }
-
-  @Test
-  public void removeNode_existingNodeWithSelfLoopEdge() {
-    addNode(N1);
-    putEdge(N1, N1);
-    assertThat(graph.removeNode(N1)).isTrue();
-    assertThat(graph.nodes()).isEmpty();
-  }
-
-  @Test
-  public void removeEdge_existingSelfLoopEdge() {
-    putEdge(N1, N1);
-    assertThat(graph.removeEdge(N1, N1)).isTrue();
-    assertThat(graph.nodes()).containsExactly(N1);
-    assertThat(graph.successors(N1)).isEmpty();
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/ConfigurableDirectedMultiNetworkTest.java b/android/guava-tests/test/com/google/common/graph/ConfigurableDirectedMultiNetworkTest.java
deleted file mode 100644
index 3a49e2d..0000000
--- a/android/guava-tests/test/com/google/common/graph/ConfigurableDirectedMultiNetworkTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Tests for a directed {@link ConfigurableMutableNetwork} allowing parallel edges and self-loops.
- */
-@RunWith(JUnit4.class)
-public class ConfigurableDirectedMultiNetworkTest extends ConfigurableDirectedNetworkTest {
-  @Override
-  public MutableNetwork<Integer, String> createGraph() {
-    return NetworkBuilder.directed().allowsParallelEdges(true).allowsSelfLoops(true).build();
-  }
-
-  @Test
-  public void adjacentEdges_parallelEdges() {
-    addEdge(N1, N2, E12);
-    addEdge(N1, N2, E12_A);
-    addEdge(N1, N2, E12_B);
-    addEdge(N3, N4, E34);
-    assertThat(network.adjacentEdges(E12)).containsExactly(E12_A, E12_B);
-  }
-
-  @Test
-  public void edgesConnecting_parallelEdges() {
-    assertTrue(addEdge(N1, N2, E12));
-    assertTrue(addEdge(N1, N2, E12_A));
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12, E12_A);
-    // Passed nodes should be in the correct edge direction, first is the
-    // source node and the second is the target node
-    assertThat(network.edgesConnecting(N2, N1)).isEmpty();
-  }
-
-  @Test
-  public void edgesConnecting_parallelSelfLoopEdges() {
-    assertTrue(addEdge(N1, N1, E11));
-    assertTrue(addEdge(N1, N1, E11_A));
-    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11, E11_A);
-  }
-
-  @Override
-  @Test
-  public void addEdge_parallelEdge() {
-    assertTrue(addEdge(N1, N2, E12));
-    assertTrue(addEdge(N1, N2, E12_A));
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12, E12_A);
-  }
-
-  @Override
-  @Test
-  public void addEdge_parallelSelfLoopEdge() {
-    assertTrue(addEdge(N1, N1, E11));
-    assertTrue(addEdge(N1, N1, E11_A));
-    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11, E11_A);
-  }
-
-  @Test
-  public void removeEdge_parallelEdge() {
-    addEdge(N1, N2, E12);
-    addEdge(N1, N2, E12_A);
-    assertTrue(network.removeEdge(E12_A));
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
-  }
-
-  @Test
-  public void removeEdge_parallelSelfLoopEdge() {
-    addEdge(N1, N1, E11);
-    addEdge(N1, N1, E11_A);
-    addEdge(N1, N2, E12);
-    assertTrue(network.removeEdge(E11_A));
-    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
-    assertTrue(network.removeEdge(E11));
-    assertThat(network.edgesConnecting(N1, N1)).isEmpty();
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/ConfigurableDirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/ConfigurableDirectedNetworkTest.java
deleted file mode 100644
index 671b1e8..0000000
--- a/android/guava-tests/test/com/google/common/graph/ConfigurableDirectedNetworkTest.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-import com.google.common.collect.ImmutableSet;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/** Tests for a directed {@link ConfigurableMutableNetwork} allowing self-loops. */
-@RunWith(JUnit4.class)
-public class ConfigurableDirectedNetworkTest extends ConfigurableSimpleDirectedNetworkTest {
-
-  @Override
-  public MutableNetwork<Integer, String> createGraph() {
-    return NetworkBuilder.directed().allowsSelfLoops(true).build();
-  }
-
-  @Test
-  public void edges_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.edges()).containsExactly(E11);
-  }
-
-  @Test
-  public void incidentEdges_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.incidentEdges(N1)).containsExactly(E11);
-  }
-
-  @Test
-  public void incidentNodes_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.incidentNodes(E11).source()).isEqualTo(N1);
-    assertThat(network.incidentNodes(E11).target()).isEqualTo(N1);
-  }
-
-  @Test
-  public void adjacentNodes_selfLoop() {
-    addEdge(N1, N1, E11);
-    addEdge(N1, N2, E12);
-    assertThat(network.adjacentNodes(N1)).containsExactly(N1, N2);
-  }
-
-  @Test
-  public void adjacentEdges_selfLoop() {
-    addEdge(N1, N1, E11);
-    addEdge(N1, N2, E12);
-    assertThat(network.adjacentEdges(E11)).containsExactly(E12);
-  }
-
-  @Test
-  public void edgesConnecting_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
-    addEdge(N1, N2, E12);
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
-    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
-  }
-
-  @Test
-  public void inEdges_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.inEdges(N1)).containsExactly(E11);
-    addEdge(N4, N1, E41);
-    assertThat(network.inEdges(N1)).containsExactly(E11, E41);
-  }
-
-  @Test
-  public void outEdges_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.outEdges(N1)).containsExactly(E11);
-    addEdge(N1, N2, E12);
-    assertThat(network.outEdges(N1)).containsExactly(E11, E12);
-  }
-
-  @Test
-  public void predecessors_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.predecessors(N1)).containsExactly(N1);
-    addEdge(N4, N1, E41);
-    assertThat(network.predecessors(N1)).containsExactly(N1, N4);
-  }
-
-  @Test
-  public void successors_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.successors(N1)).containsExactly(N1);
-    addEdge(N1, N2, E12);
-    assertThat(network.successors(N1)).containsExactly(N1, N2);
-  }
-
-  @Test
-  public void source_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.incidentNodes(E11).source()).isEqualTo(N1);
-  }
-
-  @Test
-  public void target_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.incidentNodes(E11).target()).isEqualTo(N1);
-  }
-
-  @Test
-  public void degree_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.degree(N1)).isEqualTo(2);
-    addEdge(N1, N2, E12);
-    assertThat(network.degree(N1)).isEqualTo(3);
-  }
-
-  @Test
-  public void inDegree_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.inDegree(N1)).isEqualTo(1);
-    addEdge(N4, N1, E41);
-    assertThat(network.inDegree(N1)).isEqualTo(2);
-  }
-
-  @Test
-  public void outDegree_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.outDegree(N1)).isEqualTo(1);
-    addEdge(N1, N2, E12);
-    assertThat(network.outDegree(N1)).isEqualTo(2);
-  }
-
-  @Override
-  @Test
-  public void addEdge_selfLoop() {
-    assertThat(addEdge(N1, N1, E11)).isTrue();
-    assertThat(network.edges()).contains(E11);
-    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
-  }
-
-  @Test
-  public void addEdge_existingSelfLoopEdgeBetweenSameNodes() {
-    addEdge(N1, N1, E11);
-    ImmutableSet<String> edges = ImmutableSet.copyOf(network.edges());
-    assertThat(addEdge(N1, N1, E11)).isFalse();
-    assertThat(network.edges()).containsExactlyElementsIn(edges);
-  }
-
-  @Test
-  public void addEdge_existingEdgeBetweenDifferentNodes_selfLoops() {
-    addEdge(N1, N1, E11);
-    try {
-      addEdge(N1, N2, E11);
-      fail("Reusing an existing self-loop edge to connect different nodes succeeded");
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE);
-    }
-    try {
-      addEdge(N2, N2, E11);
-      fail("Reusing an existing self-loop edge to make a different self-loop edge succeeded");
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE);
-    }
-    addEdge(N1, N2, E12);
-    try {
-      addEdge(N1, N1, E12);
-      fail("Reusing an existing edge to add a self-loop edge between different nodes succeeded");
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE);
-    }
-  }
-
-  @Test
-  public void addEdge_parallelSelfLoopEdge() {
-    addEdge(N1, N1, E11);
-    try {
-      addEdge(N1, N1, EDGE_NOT_IN_GRAPH);
-      fail("Adding a parallel self-loop edge succeeded");
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE);
-    }
-  }
-
-  @Test
-  public void removeNode_existingNodeWithSelfLoopEdge() {
-    addNode(N1);
-    addEdge(N1, N1, E11);
-    assertThat(network.removeNode(N1)).isTrue();
-    assertThat(network.nodes()).isEmpty();
-    assertThat(network.edges()).doesNotContain(E11);
-  }
-
-  @Test
-  public void removeEdge_existingSelfLoopEdge() {
-    addEdge(N1, N1, E11);
-    assertThat(network.removeEdge(E11)).isTrue();
-    assertThat(network.edges()).doesNotContain(E11);
-    assertThat(network.edgesConnecting(N1, N1)).isEmpty();
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/ConfigurableSimpleDirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/ConfigurableSimpleDirectedGraphTest.java
deleted file mode 100644
index 888cf9a..0000000
--- a/android/guava-tests/test/com/google/common/graph/ConfigurableSimpleDirectedGraphTest.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.util.Set;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Tests for a directed {@link ConfigurableMutableGraph}, creating a simple directed graph
- * (self-loop edges are not allowed).
- */
-@RunWith(JUnit4.class)
-public class ConfigurableSimpleDirectedGraphTest extends AbstractDirectedGraphTest {
-
-  @Override
-  public MutableGraph<Integer> createGraph() {
-    return GraphBuilder.directed().allowsSelfLoops(false).build();
-  }
-
-  @Override
-  @Test
-  public void nodes_checkReturnedSetMutability() {
-    Set<Integer> nodes = graph.nodes();
-    try {
-      nodes.add(N2);
-      fail(ERROR_MODIFIABLE_SET);
-    } catch (UnsupportedOperationException e) {
-      addNode(N1);
-      assertThat(graph.nodes()).containsExactlyElementsIn(nodes);
-    }
-  }
-
-  @Override
-  @Test
-  public void adjacentNodes_checkReturnedSetMutability() {
-    addNode(N1);
-    Set<Integer> adjacentNodes = graph.adjacentNodes(N1);
-    try {
-      adjacentNodes.add(N2);
-      fail(ERROR_MODIFIABLE_SET);
-    } catch (UnsupportedOperationException e) {
-      putEdge(N1, N2);
-      assertThat(graph.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes);
-    }
-  }
-
-  @Override
-  @Test
-  public void predecessors_checkReturnedSetMutability() {
-    addNode(N2);
-    Set<Integer> predecessors = graph.predecessors(N2);
-    try {
-      predecessors.add(N1);
-      fail(ERROR_MODIFIABLE_SET);
-    } catch (UnsupportedOperationException e) {
-      putEdge(N1, N2);
-      assertThat(graph.predecessors(N2)).containsExactlyElementsIn(predecessors);
-    }
-  }
-
-  @Override
-  @Test
-  public void successors_checkReturnedSetMutability() {
-    addNode(N1);
-    Set<Integer> successors = graph.successors(N1);
-    try {
-      successors.add(N2);
-      fail(ERROR_MODIFIABLE_SET);
-    } catch (UnsupportedOperationException e) {
-      putEdge(N1, N2);
-      assertThat(successors).containsExactlyElementsIn(graph.successors(N1));
-    }
-  }
-
-  @Override
-  @Test
-  public void incidentEdges_checkReturnedSetMutability() {
-    addNode(N1);
-    Set<EndpointPair<Integer>> incidentEdges = graph.incidentEdges(N1);
-    try {
-      incidentEdges.add(EndpointPair.ordered(N1, N2));
-      fail(ERROR_MODIFIABLE_SET);
-    } catch (UnsupportedOperationException e) {
-      putEdge(N1, N2);
-      assertThat(incidentEdges).containsExactlyElementsIn(graph.incidentEdges(N1));
-    }
-  }
-
-  // Element Mutation
-
-  @Test
-  public void addEdge_selfLoop() {
-    try {
-      putEdge(N1, N1);
-      fail(ERROR_ADDED_SELF_LOOP);
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_SELF_LOOP);
-    }
-  }
-
-  /**
-   * This test checks an implementation dependent feature. It tests that the method {@code addEdge}
-   * will silently add the missing nodes to the graph, then add the edge connecting them. We are not
-   * using the proxy methods here as we want to test {@code addEdge} when the end-points are not
-   * elements of the graph.
-   */
-  @Test
-  public void addEdge_nodesNotInGraph() {
-    graph.addNode(N1);
-    assertTrue(graph.putEdge(N1, N5));
-    assertTrue(graph.putEdge(N4, N1));
-    assertTrue(graph.putEdge(N2, N3));
-    assertThat(graph.nodes()).containsExactly(N1, N5, N4, N2, N3).inOrder();
-    assertThat(graph.successors(N1)).containsExactly(N5);
-    assertThat(graph.successors(N2)).containsExactly(N3);
-    assertThat(graph.successors(N3)).isEmpty();
-    assertThat(graph.successors(N4)).containsExactly(N1);
-    assertThat(graph.successors(N5)).isEmpty();
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/ConfigurableSimpleDirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/ConfigurableSimpleDirectedNetworkTest.java
deleted file mode 100644
index a8645b4..0000000
--- a/android/guava-tests/test/com/google/common/graph/ConfigurableSimpleDirectedNetworkTest.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.util.Set;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Tests for a directed {@link ConfigurableMutableNetwork}, creating a simple directed graph
- * (parallel and self-loop edges are not allowed).
- */
-@RunWith(JUnit4.class)
-public class ConfigurableSimpleDirectedNetworkTest extends AbstractDirectedNetworkTest {
-
-  @Override
-  public MutableNetwork<Integer, String> createGraph() {
-    return NetworkBuilder.directed().allowsParallelEdges(false).allowsSelfLoops(false).build();
-  }
-
-  @Override
-  @Test
-  public void nodes_checkReturnedSetMutability() {
-    Set<Integer> nodes = network.nodes();
-    try {
-      nodes.add(N2);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addNode(N1);
-      assertThat(network.nodes()).containsExactlyElementsIn(nodes);
-    }
-  }
-
-  @Override
-  @Test
-  public void edges_checkReturnedSetMutability() {
-    Set<String> edges = network.edges();
-    try {
-      edges.add(E12);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.edges()).containsExactlyElementsIn(edges);
-    }
-  }
-
-  @Override
-  @Test
-  public void incidentEdges_checkReturnedSetMutability() {
-    addNode(N1);
-    Set<String> incidentEdges = network.incidentEdges(N1);
-    try {
-      incidentEdges.add(E12);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.incidentEdges(N1)).containsExactlyElementsIn(incidentEdges);
-    }
-  }
-
-  @Override
-  @Test
-  public void adjacentNodes_checkReturnedSetMutability() {
-    addNode(N1);
-    Set<Integer> adjacentNodes = network.adjacentNodes(N1);
-    try {
-      adjacentNodes.add(N2);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes);
-    }
-  }
-
-  @Override
-  public void adjacentEdges_checkReturnedSetMutability() {
-    addEdge(N1, N2, E12);
-    Set<String> adjacentEdges = network.adjacentEdges(E12);
-    try {
-      adjacentEdges.add(E23);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N2, N3, E23);
-      assertThat(network.adjacentEdges(E12)).containsExactlyElementsIn(adjacentEdges);
-    }
-  }
-
-  @Override
-  @Test
-  public void edgesConnecting_checkReturnedSetMutability() {
-    addNode(N1);
-    addNode(N2);
-    Set<String> edgesConnecting = network.edgesConnecting(N1, N2);
-    try {
-      edgesConnecting.add(E23);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.edgesConnecting(N1, N2)).containsExactlyElementsIn(edgesConnecting);
-    }
-  }
-
-  @Override
-  @Test
-  public void inEdges_checkReturnedSetMutability() {
-    addNode(N2);
-    Set<String> inEdges = network.inEdges(N2);
-    try {
-      inEdges.add(E12);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.inEdges(N2)).containsExactlyElementsIn(inEdges);
-    }
-  }
-
-  @Override
-  @Test
-  public void outEdges_checkReturnedSetMutability() {
-    addNode(N1);
-    Set<String> outEdges = network.outEdges(N1);
-    try {
-      outEdges.add(E12);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.outEdges(N1)).containsExactlyElementsIn(outEdges);
-    }
-  }
-
-  @Override
-  @Test
-  public void predecessors_checkReturnedSetMutability() {
-    addNode(N2);
-    Set<Integer> predecessors = network.predecessors(N2);
-    try {
-      predecessors.add(N1);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.predecessors(N2)).containsExactlyElementsIn(predecessors);
-    }
-  }
-
-  @Override
-  @Test
-  public void successors_checkReturnedSetMutability() {
-    addNode(N1);
-    Set<Integer> successors = network.successors(N1);
-    try {
-      successors.add(N2);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(successors).containsExactlyElementsIn(network.successors(N1));
-    }
-  }
-
-  // Element Mutation
-
-  @Test
-  public void addEdge_selfLoop() {
-    try {
-      addEdge(N1, N1, E11);
-      fail(ERROR_ADDED_SELF_LOOP);
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_SELF_LOOP);
-    }
-  }
-
-  /**
-   * This test checks an implementation dependent feature. It tests that the method {@code addEdge}
-   * will silently add the missing nodes to the graph, then add the edge connecting them. We are not
-   * using the proxy methods here as we want to test {@code addEdge} when the end-points are not
-   * elements of the graph.
-   */
-  @Test
-  public void addEdge_nodesNotInGraph() {
-    network.addNode(N1);
-    assertTrue(network.addEdge(N1, N5, E15));
-    assertTrue(network.addEdge(N4, N1, E41));
-    assertTrue(network.addEdge(N2, N3, E23));
-    assertThat(network.nodes()).containsExactly(N1, N5, N4, N2, N3).inOrder();
-    assertThat(network.edges()).containsExactly(E15, E41, E23).inOrder();
-    assertThat(network.edgesConnecting(N1, N5)).containsExactly(E15);
-    assertThat(network.edgesConnecting(N4, N1)).containsExactly(E41);
-    assertThat(network.edgesConnecting(N2, N3)).containsExactly(E23);
-    // Direction of the added edge is correctly handled
-    assertThat(network.edgesConnecting(N3, N2)).isEmpty();
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/ConfigurableSimpleUndirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/ConfigurableSimpleUndirectedGraphTest.java
deleted file mode 100644
index 97693d1..0000000
--- a/android/guava-tests/test/com/google/common/graph/ConfigurableSimpleUndirectedGraphTest.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.util.Set;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Tests for an undirected {@link ConfigurableMutableGraph}, creating a simple undirected graph
- * (self-loop edges are not allowed).
- */
-@RunWith(JUnit4.class)
-public class ConfigurableSimpleUndirectedGraphTest extends AbstractUndirectedGraphTest {
-
-  @Override
-  public MutableGraph<Integer> createGraph() {
-    return GraphBuilder.undirected().allowsSelfLoops(false).build();
-  }
-
-  @Override
-  @Test
-  public void nodes_checkReturnedSetMutability() {
-    Set<Integer> nodes = graph.nodes();
-    try {
-      nodes.add(N2);
-      fail(ERROR_MODIFIABLE_SET);
-    } catch (UnsupportedOperationException e) {
-      addNode(N1);
-      assertThat(graph.nodes()).containsExactlyElementsIn(nodes);
-    }
-  }
-
-  @Override
-  @Test
-  public void adjacentNodes_checkReturnedSetMutability() {
-    addNode(N1);
-    Set<Integer> adjacentNodes = graph.adjacentNodes(N1);
-    try {
-      adjacentNodes.add(N2);
-      fail(ERROR_MODIFIABLE_SET);
-    } catch (UnsupportedOperationException e) {
-      putEdge(N1, N2);
-      assertThat(graph.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes);
-    }
-  }
-
-  @Override
-  @Test
-  public void predecessors_checkReturnedSetMutability() {
-    addNode(N2);
-    Set<Integer> predecessors = graph.predecessors(N2);
-    try {
-      predecessors.add(N1);
-      fail(ERROR_MODIFIABLE_SET);
-    } catch (UnsupportedOperationException e) {
-      putEdge(N1, N2);
-      assertThat(graph.predecessors(N2)).containsExactlyElementsIn(predecessors);
-    }
-  }
-
-  @Override
-  @Test
-  public void successors_checkReturnedSetMutability() {
-    addNode(N1);
-    Set<Integer> successors = graph.successors(N1);
-    try {
-      successors.add(N2);
-      fail(ERROR_MODIFIABLE_SET);
-    } catch (UnsupportedOperationException e) {
-      putEdge(N1, N2);
-      assertThat(graph.successors(N1)).containsExactlyElementsIn(successors);
-    }
-  }
-
-  @Override
-  @Test
-  public void incidentEdges_checkReturnedSetMutability() {
-    addNode(N1);
-    Set<EndpointPair<Integer>> incidentEdges = graph.incidentEdges(N1);
-    try {
-      incidentEdges.add(EndpointPair.unordered(N1, N2));
-      fail(ERROR_MODIFIABLE_SET);
-    } catch (UnsupportedOperationException e) {
-      putEdge(N1, N2);
-      assertThat(incidentEdges).containsExactlyElementsIn(graph.incidentEdges(N1));
-    }
-  }
-
-  // Element Mutation
-
-  @Test
-  public void addEdge_selfLoop() {
-    try {
-      putEdge(N1, N1);
-      fail(ERROR_ADDED_SELF_LOOP);
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_SELF_LOOP);
-    }
-  }
-
-  /**
-   * This test checks an implementation dependent feature. It tests that the method {@code addEdge}
-   * will silently add the missing nodes to the graph, then add the edge connecting them. We are not
-   * using the proxy methods here as we want to test {@code addEdge} when the end-points are not
-   * elements of the graph.
-   */
-  @Test
-  public void addEdge_nodesNotInGraph() {
-    graph.addNode(N1);
-    assertTrue(graph.putEdge(N1, N5));
-    assertTrue(graph.putEdge(N4, N1));
-    assertTrue(graph.putEdge(N2, N3));
-    assertThat(graph.nodes()).containsExactly(N1, N5, N4, N2, N3).inOrder();
-    assertThat(graph.adjacentNodes(N1)).containsExactly(N4, N5);
-    assertThat(graph.adjacentNodes(N2)).containsExactly(N3);
-    assertThat(graph.adjacentNodes(N3)).containsExactly(N2);
-    assertThat(graph.adjacentNodes(N4)).containsExactly(N1);
-    assertThat(graph.adjacentNodes(N5)).containsExactly(N1);
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/ConfigurableSimpleUndirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/ConfigurableSimpleUndirectedNetworkTest.java
deleted file mode 100644
index d1c4411..0000000
--- a/android/guava-tests/test/com/google/common/graph/ConfigurableSimpleUndirectedNetworkTest.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.util.Set;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Tests for an undirected {@link ConfigurableMutableNetwork}, creating a simple undirected graph
- * (parallel and self-loop edges are not allowed).
- */
-@RunWith(JUnit4.class)
-public class ConfigurableSimpleUndirectedNetworkTest extends AbstractUndirectedNetworkTest {
-
-  @Override
-  public MutableNetwork<Integer, String> createGraph() {
-    return NetworkBuilder.undirected().allowsParallelEdges(false).allowsSelfLoops(false).build();
-  }
-
-  @Override
-  @Test
-  public void nodes_checkReturnedSetMutability() {
-    Set<Integer> nodes = network.nodes();
-    try {
-      nodes.add(N2);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addNode(N1);
-      assertThat(network.nodes()).containsExactlyElementsIn(nodes);
-    }
-  }
-
-  @Override
-  @Test
-  public void edges_checkReturnedSetMutability() {
-    Set<String> edges = network.edges();
-    try {
-      edges.add(E12);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.edges()).containsExactlyElementsIn(edges);
-    }
-  }
-
-  @Override
-  @Test
-  public void incidentEdges_checkReturnedSetMutability() {
-    addNode(N1);
-    Set<String> incidentEdges = network.incidentEdges(N1);
-    try {
-      incidentEdges.add(E12);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.incidentEdges(N1)).containsExactlyElementsIn(incidentEdges);
-    }
-  }
-
-  @Override
-  @Test
-  public void adjacentNodes_checkReturnedSetMutability() {
-    addNode(N1);
-    Set<Integer> adjacentNodes = network.adjacentNodes(N1);
-    try {
-      adjacentNodes.add(N2);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes);
-    }
-  }
-
-  @Override
-  public void adjacentEdges_checkReturnedSetMutability() {
-    addEdge(N1, N2, E12);
-    Set<String> adjacentEdges = network.adjacentEdges(E12);
-    try {
-      adjacentEdges.add(E23);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N2, N3, E23);
-      assertThat(network.adjacentEdges(E12)).containsExactlyElementsIn(adjacentEdges);
-    }
-  }
-
-  @Override
-  @Test
-  public void edgesConnecting_checkReturnedSetMutability() {
-    addNode(N1);
-    addNode(N2);
-    Set<String> edgesConnecting = network.edgesConnecting(N1, N2);
-    try {
-      edgesConnecting.add(E23);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.edgesConnecting(N1, N2)).containsExactlyElementsIn(edgesConnecting);
-    }
-  }
-
-  @Override
-  @Test
-  public void inEdges_checkReturnedSetMutability() {
-    addNode(N2);
-    Set<String> inEdges = network.inEdges(N2);
-    try {
-      inEdges.add(E12);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.inEdges(N2)).containsExactlyElementsIn(inEdges);
-    }
-  }
-
-  @Override
-  @Test
-  public void outEdges_checkReturnedSetMutability() {
-    addNode(N1);
-    Set<String> outEdges = network.outEdges(N1);
-    try {
-      outEdges.add(E12);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.outEdges(N1)).containsExactlyElementsIn(outEdges);
-    }
-  }
-
-  @Override
-  @Test
-  public void predecessors_checkReturnedSetMutability() {
-    addNode(N2);
-    Set<Integer> predecessors = network.predecessors(N2);
-    try {
-      predecessors.add(N1);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.predecessors(N2)).containsExactlyElementsIn(predecessors);
-    }
-  }
-
-  @Override
-  @Test
-  public void successors_checkReturnedSetMutability() {
-    addNode(N1);
-    Set<Integer> successors = network.successors(N1);
-    try {
-      successors.add(N2);
-      fail(ERROR_MODIFIABLE_COLLECTION);
-    } catch (UnsupportedOperationException e) {
-      addEdge(N1, N2, E12);
-      assertThat(network.successors(N1)).containsExactlyElementsIn(successors);
-    }
-  }
-
-  // Element Mutation
-
-  @Test
-  public void addEdge_selfLoop() {
-    try {
-      addEdge(N1, N1, E11);
-      fail(ERROR_ADDED_SELF_LOOP);
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_SELF_LOOP);
-    }
-  }
-
-  /**
-   * This test checks an implementation dependent feature. It tests that the method {@code addEdge}
-   * will silently add the missing nodes to the graph, then add the edge connecting them. We are not
-   * using the proxy methods here as we want to test {@code addEdge} when the end-points are not
-   * elements of the graph.
-   */
-  @Test
-  public void addEdge_nodesNotInGraph() {
-    network.addNode(N1);
-    assertTrue(network.addEdge(N1, N5, E15));
-    assertTrue(network.addEdge(N4, N1, E41));
-    assertTrue(network.addEdge(N2, N3, E23));
-    assertThat(network.nodes()).containsExactly(N1, N5, N4, N2, N3).inOrder();
-    assertThat(network.edges()).containsExactly(E15, E41, E23).inOrder();
-    assertThat(network.edgesConnecting(N1, N5)).containsExactly(E15);
-    assertThat(network.edgesConnecting(N4, N1)).containsExactly(E41);
-    assertThat(network.edgesConnecting(N2, N3)).containsExactly(E23);
-    assertThat(network.edgesConnecting(N3, N2)).containsExactly(E23);
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/ConfigurableUndirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/ConfigurableUndirectedGraphTest.java
deleted file mode 100644
index 3bdfd74..0000000
--- a/android/guava-tests/test/com/google/common/graph/ConfigurableUndirectedGraphTest.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/** Tests for an undirected {@link ConfigurableMutableGraph} allowing self-loops. */
-@RunWith(JUnit4.class)
-public class ConfigurableUndirectedGraphTest extends ConfigurableSimpleUndirectedGraphTest {
-
-  @Override
-  public MutableGraph<Integer> createGraph() {
-    return GraphBuilder.undirected().allowsSelfLoops(true).build();
-  }
-
-  @Test
-  public void adjacentNodes_selfLoop() {
-    putEdge(N1, N1);
-    putEdge(N1, N2);
-    assertThat(graph.adjacentNodes(N1)).containsExactly(N1, N2);
-  }
-
-  @Test
-  public void predecessors_selfLoop() {
-    putEdge(N1, N1);
-    assertThat(graph.predecessors(N1)).containsExactly(N1);
-    putEdge(N1, N2);
-    assertThat(graph.predecessors(N1)).containsExactly(N1, N2);
-  }
-
-  @Test
-  public void successors_selfLoop() {
-    putEdge(N1, N1);
-    assertThat(graph.successors(N1)).containsExactly(N1);
-    putEdge(N2, N1);
-    assertThat(graph.successors(N1)).containsExactly(N1, N2);
-  }
-
-  @Test
-  public void incidentEdges_selfLoop() {
-    putEdge(N1, N1);
-    assertThat(graph.incidentEdges(N1)).containsExactly(EndpointPair.unordered(N1, N1));
-    putEdge(N1, N2);
-    assertThat(graph.incidentEdges(N1))
-        .containsExactly(EndpointPair.unordered(N1, N1), EndpointPair.unordered(N1, N2));
-  }
-
-  @Test
-  public void degree_selfLoop() {
-    putEdge(N1, N1);
-    assertThat(graph.degree(N1)).isEqualTo(2);
-    putEdge(N1, N2);
-    assertThat(graph.degree(N1)).isEqualTo(3);
-  }
-
-  @Test
-  public void inDegree_selfLoop() {
-    putEdge(N1, N1);
-    assertThat(graph.inDegree(N1)).isEqualTo(2);
-    putEdge(N1, N2);
-    assertThat(graph.inDegree(N1)).isEqualTo(3);
-  }
-
-  @Test
-  public void outDegree_selfLoop() {
-    putEdge(N1, N1);
-    assertThat(graph.outDegree(N1)).isEqualTo(2);
-    putEdge(N2, N1);
-    assertThat(graph.outDegree(N1)).isEqualTo(3);
-  }
-
-  @Override
-  @Test
-  public void addEdge_selfLoop() {
-    assertThat(putEdge(N1, N1)).isTrue();
-    assertThat(graph.adjacentNodes(N1)).containsExactly(N1);
-  }
-
-  @Test
-  public void addEdge_existingSelfLoopEdgeBetweenSameNodes() {
-    putEdge(N1, N1);
-    assertThat(putEdge(N1, N1)).isFalse();
-  }
-
-  @Test
-  public void removeNode_existingNodeWithSelfLoopEdge() {
-    addNode(N1);
-    putEdge(N1, N1);
-    assertThat(graph.removeNode(N1)).isTrue();
-    assertThat(graph.nodes()).isEmpty();
-  }
-
-  @Test
-  public void removeEdge_existingSelfLoopEdge() {
-    putEdge(N1, N1);
-    assertThat(graph.removeEdge(N1, N1)).isTrue();
-    assertThat(graph.nodes()).containsExactly(N1);
-    assertThat(graph.adjacentNodes(N1)).isEmpty();
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/ConfigurableUndirectedMultiNetworkTest.java b/android/guava-tests/test/com/google/common/graph/ConfigurableUndirectedMultiNetworkTest.java
deleted file mode 100644
index d239e22..0000000
--- a/android/guava-tests/test/com/google/common/graph/ConfigurableUndirectedMultiNetworkTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Tests for an undirected {@link ConfigurableMutableNetwork} allowing parallel edges and
- * self-loops.
- */
-@RunWith(JUnit4.class)
-public class ConfigurableUndirectedMultiNetworkTest extends ConfigurableUndirectedNetworkTest {
-  @Override
-  public MutableNetwork<Integer, String> createGraph() {
-    return NetworkBuilder.undirected().allowsParallelEdges(true).allowsSelfLoops(true).build();
-  }
-
-  @Test
-  public void adjacentEdges_parallelEdges() {
-    addEdge(N1, N2, E12);
-    addEdge(N1, N2, E12_A);
-    addEdge(N1, N2, E12_B);
-    addEdge(N3, N4, E34);
-    assertThat(network.adjacentEdges(E12)).containsExactly(E12_A, E12_B);
-  }
-
-  @Test
-  public void edgesConnecting_parallelEdges() {
-    assertTrue(addEdge(N1, N2, E12));
-    assertTrue(addEdge(N1, N2, E12_A));
-    assertTrue(addEdge(N2, N1, E21));
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12, E12_A, E21);
-    assertThat(network.edgesConnecting(N2, N1)).containsExactly(E12, E12_A, E21);
-  }
-
-  @Test
-  public void edgesConnecting_parallelSelfLoopEdges() {
-    assertTrue(addEdge(N1, N1, E11));
-    assertTrue(addEdge(N1, N1, E11_A));
-    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11, E11_A);
-  }
-
-  @Override
-  @Test
-  public void addEdge_parallelEdge() {
-    assertTrue(addEdge(N1, N2, E12));
-    assertTrue(addEdge(N1, N2, E12_A));
-    assertTrue(addEdge(N2, N1, E21));
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12, E12_A, E21);
-  }
-
-  @Override
-  @Test
-  public void addEdge_parallelSelfLoopEdge() {
-    assertTrue(addEdge(N1, N1, E11));
-    assertTrue(addEdge(N1, N1, E11_A));
-    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11, E11_A);
-  }
-
-  @Test
-  public void removeEdge_parallelEdge() {
-    addEdge(N1, N2, E12);
-    addEdge(N1, N2, E12_A);
-    addEdge(N2, N1, E21);
-    assertTrue(network.removeEdge(E12_A));
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12, E21);
-  }
-
-  @Test
-  public void removeEdge_parallelSelfLoopEdge() {
-    addEdge(N1, N1, E11);
-    addEdge(N1, N1, E11_A);
-    addEdge(N1, N2, E12);
-    assertTrue(network.removeEdge(E11_A));
-    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
-    assertTrue(network.removeEdge(E11));
-    assertThat(network.edgesConnecting(N1, N1)).isEmpty();
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/ConfigurableUndirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/ConfigurableUndirectedNetworkTest.java
deleted file mode 100644
index 28cd3e5..0000000
--- a/android/guava-tests/test/com/google/common/graph/ConfigurableUndirectedNetworkTest.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
-
-import com.google.common.collect.ImmutableSet;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/** Tests for an undirected {@link ConfigurableMutableNetwork} allowing self-loops. */
-@RunWith(JUnit4.class)
-public class ConfigurableUndirectedNetworkTest extends ConfigurableSimpleUndirectedNetworkTest {
-
-  @Override
-  public MutableNetwork<Integer, String> createGraph() {
-    return NetworkBuilder.undirected().allowsSelfLoops(true).build();
-  }
-
-  @Test
-  public void edges_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.edges()).containsExactly(E11);
-  }
-
-  @Test
-  public void incidentEdges_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.incidentEdges(N1)).containsExactly(E11);
-  }
-
-  @Test
-  public void incidentNodes_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.incidentNodes(E11).nodeU()).isEqualTo(N1);
-    assertThat(network.incidentNodes(E11).nodeV()).isEqualTo(N1);
-  }
-
-  @Test
-  public void adjacentNodes_selfLoop() {
-    addEdge(N1, N1, E11);
-    addEdge(N1, N2, E12);
-    assertThat(network.adjacentNodes(N1)).containsExactly(N1, N2);
-  }
-
-  @Test
-  public void adjacentEdges_selfLoop() {
-    addEdge(N1, N1, E11);
-    addEdge(N1, N2, E12);
-    assertThat(network.adjacentEdges(E11)).containsExactly(E12);
-  }
-
-  @Test
-  public void edgesConnecting_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
-    addEdge(N1, N2, E12);
-    assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12);
-    assertThat(network.edgesConnecting(N2, N1)).containsExactly(E12);
-    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
-  }
-
-  @Test
-  public void inEdges_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.inEdges(N1)).containsExactly(E11);
-    addEdge(N1, N2, E12);
-    assertThat(network.inEdges(N1)).containsExactly(E11, E12);
-  }
-
-  @Test
-  public void outEdges_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.outEdges(N1)).containsExactly(E11);
-    addEdge(N2, N1, E12);
-    assertThat(network.outEdges(N1)).containsExactly(E11, E12);
-  }
-
-  @Test
-  public void predecessors_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.predecessors(N1)).containsExactly(N1);
-    addEdge(N1, N2, E12);
-    assertThat(network.predecessors(N1)).containsExactly(N1, N2);
-  }
-
-  @Test
-  public void successors_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.successors(N1)).containsExactly(N1);
-    addEdge(N2, N1, E12);
-    assertThat(network.successors(N1)).containsExactly(N1, N2);
-  }
-
-  @Test
-  public void degree_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.degree(N1)).isEqualTo(2);
-    addEdge(N1, N2, E12);
-    assertThat(network.degree(N1)).isEqualTo(3);
-  }
-
-  @Test
-  public void inDegree_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.inDegree(N1)).isEqualTo(2);
-    addEdge(N1, N2, E12);
-    assertThat(network.inDegree(N1)).isEqualTo(3);
-  }
-
-  @Test
-  public void outDegree_selfLoop() {
-    addEdge(N1, N1, E11);
-    assertThat(network.outDegree(N1)).isEqualTo(2);
-    addEdge(N2, N1, E12);
-    assertThat(network.outDegree(N1)).isEqualTo(3);
-  }
-
-  @Override
-  @Test
-  public void addEdge_selfLoop() {
-    assertThat(addEdge(N1, N1, E11)).isTrue();
-    assertThat(network.edges()).contains(E11);
-    assertThat(network.edgesConnecting(N1, N1)).containsExactly(E11);
-  }
-
-  @Test
-  public void addEdge_existingSelfLoopEdgeBetweenSameNodes() {
-    addEdge(N1, N1, E11);
-    ImmutableSet<String> edges = ImmutableSet.copyOf(network.edges());
-    assertThat(addEdge(N1, N1, E11)).isFalse();
-    assertThat(network.edges()).containsExactlyElementsIn(edges);
-  }
-
-  @Test
-  public void addEdge_existingEdgeBetweenDifferentNodes_selfLoops() {
-    addEdge(N1, N1, E11);
-    try {
-      addEdge(N1, N2, E11);
-      fail("Reusing an existing self-loop edge to connect different nodes succeeded");
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE);
-    }
-    try {
-      addEdge(N2, N2, E11);
-      fail("Reusing an existing self-loop edge to make a different self-loop edge succeeded");
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE);
-    }
-    addEdge(N1, N2, E12);
-    try {
-      addEdge(N1, N1, E12);
-      fail("Reusing an existing edge to add a self-loop edge between different nodes succeeded");
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE);
-    }
-  }
-
-  @Test
-  public void addEdge_parallelSelfLoopEdge() {
-    addEdge(N1, N1, E11);
-    try {
-      addEdge(N1, N1, EDGE_NOT_IN_GRAPH);
-      fail("Adding a parallel self-loop edge succeeded");
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE);
-    }
-  }
-
-  @Test
-  public void removeNode_existingNodeWithSelfLoopEdge() {
-    addNode(N1);
-    addEdge(N1, N1, E11);
-    assertThat(network.removeNode(N1)).isTrue();
-    assertThat(network.nodes()).isEmpty();
-    assertThat(network.edges()).doesNotContain(E11);
-  }
-
-  @Test
-  public void removeEdge_existingSelfLoopEdge() {
-    addEdge(N1, N1, E11);
-    assertThat(network.removeEdge(E11)).isTrue();
-    assertThat(network.edges()).doesNotContain(E11);
-    assertThat(network.edgesConnecting(N1, N1)).isEmpty();
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/GraphsTest.java b/android/guava-tests/test/com/google/common/graph/GraphsTest.java
index ce892ad..a07def0 100644
--- a/android/guava-tests/test/com/google/common/graph/GraphsTest.java
+++ b/android/guava-tests/test/com/google/common/graph/GraphsTest.java
@@ -206,7 +206,7 @@
     MutableGraph<Integer> undirectedGraph = GraphBuilder.undirected().build();
     undirectedGraph.putEdge(N1, N2);
 
-    assertThat(transpose(undirectedGraph)).isSameAs(undirectedGraph);
+    assertThat(transpose(undirectedGraph)).isSameInstanceAs(undirectedGraph);
   }
 
   @Test
@@ -227,12 +227,12 @@
 
     Graph<Integer> transpose = transpose(directedGraph);
     assertThat(transpose).isEqualTo(expectedTranspose);
-    assertThat(transpose(transpose)).isSameAs(directedGraph);
+    assertThat(transpose(transpose)).isSameInstanceAs(directedGraph);
     AbstractGraphTest.validateGraph(transpose);
 
     for (Integer node : directedGraph.nodes()) {
-      assertThat(directedGraph.inDegree(node)).isSameAs(transpose.outDegree(node));
-      assertThat(directedGraph.outDegree(node)).isSameAs(transpose.inDegree(node));
+      assertThat(directedGraph.inDegree(node)).isSameInstanceAs(transpose.outDegree(node));
+      assertThat(directedGraph.outDegree(node)).isSameInstanceAs(transpose.inDegree(node));
     }
 
     assertThat(transpose.successors(N1)).doesNotContain(N2);
@@ -247,7 +247,7 @@
     MutableValueGraph<Integer, String> undirectedGraph = ValueGraphBuilder.undirected().build();
     undirectedGraph.putEdgeValue(N1, N2, E12);
 
-    assertThat(transpose(undirectedGraph)).isSameAs(undirectedGraph);
+    assertThat(transpose(undirectedGraph)).isSameInstanceAs(undirectedGraph);
   }
 
   @Test
@@ -270,13 +270,13 @@
 
     ValueGraph<Integer, String> transpose = transpose(directedGraph);
     assertThat(transpose).isEqualTo(expectedTranspose);
-    assertThat(transpose(transpose)).isSameAs(directedGraph);
+    assertThat(transpose(transpose)).isSameInstanceAs(directedGraph);
     AbstractGraphTest.validateGraph(transpose.asGraph());
 
     assertThat(transpose.edgeValueOrDefault(N1, N2, null)).isNull();
     for (Integer node : directedGraph.nodes()) {
-      assertThat(directedGraph.inDegree(node)).isSameAs(transpose.outDegree(node));
-      assertThat(directedGraph.outDegree(node)).isSameAs(transpose.inDegree(node));
+      assertThat(directedGraph.inDegree(node)).isSameInstanceAs(transpose.outDegree(node));
+      assertThat(directedGraph.outDegree(node)).isSameInstanceAs(transpose.inDegree(node));
     }
 
     directedGraph.putEdgeValue(N2, N1, E21);
@@ -290,7 +290,7 @@
     MutableNetwork<Integer, String> undirectedGraph = NetworkBuilder.undirected().build();
     undirectedGraph.addEdge(N1, N2, E12);
 
-    assertThat(transpose(undirectedGraph)).isSameAs(undirectedGraph);
+    assertThat(transpose(undirectedGraph)).isSameInstanceAs(undirectedGraph);
   }
 
   @Test
@@ -315,15 +315,15 @@
 
     Network<Integer, String> transpose = transpose(directedGraph);
     assertThat(transpose).isEqualTo(expectedTranspose);
-    assertThat(transpose(transpose)).isSameAs(directedGraph);
+    assertThat(transpose(transpose)).isSameInstanceAs(directedGraph);
     AbstractNetworkTest.validateNetwork(transpose);
 
     assertThat(transpose.edgesConnecting(N1, N2)).isEmpty();
     assertThat(transpose.edgeConnectingOrNull(N1, N2)).isNull();
 
     for (Integer node : directedGraph.nodes()) {
-      assertThat(directedGraph.inDegree(node)).isSameAs(transpose.outDegree(node));
-      assertThat(directedGraph.outDegree(node)).isSameAs(transpose.inDegree(node));
+      assertThat(directedGraph.inDegree(node)).isSameInstanceAs(transpose.outDegree(node));
+      assertThat(directedGraph.outDegree(node)).isSameInstanceAs(transpose.inDegree(node));
     }
 
     directedGraph.addEdge(N2, N1, E21);
@@ -487,7 +487,7 @@
       directedGraph.addEdge(N1, N1, E11);
       fail(ERROR_ADDED_SELF_LOOP);
     } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_SELF_LOOP);
+      assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP);
     }
   }
 
@@ -519,7 +519,7 @@
       undirectedGraph.addEdge(N1, N1, E11);
       fail(ERROR_ADDED_SELF_LOOP);
     } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains(ERROR_SELF_LOOP);
+      assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP);
     }
   }
 
diff --git a/android/guava-tests/test/com/google/common/graph/ImmutableGraphTest.java b/android/guava-tests/test/com/google/common/graph/ImmutableGraphTest.java
deleted file mode 100644
index 1a60836..0000000
--- a/android/guava-tests/test/com/google/common/graph/ImmutableGraphTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.google.common.graph;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/** Tests for {@link ImmutableGraph} and {@link ImmutableValueGraph} . */
-@RunWith(JUnit4.class)
-public class ImmutableGraphTest {
-
-  @Test
-  public void immutableGraph() {
-    MutableGraph<String> mutableGraph = GraphBuilder.directed().build();
-    mutableGraph.addNode("A");
-    ImmutableGraph<String> immutableGraph = ImmutableGraph.copyOf(mutableGraph);
-
-    assertThat(immutableGraph).isNotInstanceOf(MutableValueGraph.class);
-    assertThat(immutableGraph).isEqualTo(mutableGraph);
-
-    mutableGraph.addNode("B");
-    assertThat(immutableGraph).isNotEqualTo(mutableGraph);
-  }
-
-  @Test
-  public void immutableValueGraph() {
-    MutableValueGraph<String, Integer> mutableValueGraph = ValueGraphBuilder.directed().build();
-    mutableValueGraph.addNode("A");
-    ImmutableValueGraph<String, Integer> immutableValueGraph =
-        ImmutableValueGraph.copyOf(mutableValueGraph);
-
-    assertThat(immutableValueGraph.asGraph()).isInstanceOf(ImmutableGraph.class);
-    assertThat(immutableValueGraph).isNotInstanceOf(MutableValueGraph.class);
-    assertThat(immutableValueGraph).isEqualTo(mutableValueGraph);
-
-    mutableValueGraph.addNode("B");
-    assertThat(immutableValueGraph).isNotEqualTo(mutableValueGraph);
-  }
-
-  @Test
-  public void copyOfImmutableGraph_optimized() {
-    Graph<String> graph1 = ImmutableGraph.copyOf(GraphBuilder.directed().<String>build());
-    Graph<String> graph2 = ImmutableGraph.copyOf(graph1);
-
-    assertThat(graph2).isSameAs(graph1);
-  }
-
-  @Test
-  public void copyOfImmutableValueGraph_optimized() {
-    ValueGraph<String, Integer> graph1 =
-        ImmutableValueGraph.copyOf(ValueGraphBuilder.directed().<String, Integer>build());
-    ValueGraph<String, Integer> graph2 = ImmutableValueGraph.copyOf(graph1);
-
-    assertThat(graph2).isSameAs(graph1);
-  }
-}
diff --git a/android/guava-tests/test/com/google/common/graph/ImmutableNetworkTest.java b/android/guava-tests/test/com/google/common/graph/ImmutableNetworkTest.java
index e138656..5de3cf7 100644
--- a/android/guava-tests/test/com/google/common/graph/ImmutableNetworkTest.java
+++ b/android/guava-tests/test/com/google/common/graph/ImmutableNetworkTest.java
@@ -46,7 +46,7 @@
         ImmutableNetwork.copyOf(NetworkBuilder.directed().<String, String>build());
     Network<String, String> network2 = ImmutableNetwork.copyOf(network1);
 
-    assertThat(network2).isSameAs(network1);
+    assertThat(network2).isSameInstanceAs(network1);
   }
 
   @Test
@@ -74,4 +74,74 @@
     assertThat(network.edgesConnecting("A", "B")).containsExactly("AB");
     assertThat(network.edgesConnecting("B", "A")).containsExactly("AB");
   }
+
+  @Test
+  public void immutableNetworkBuilder_appliesNetworkBuilderConfig() {
+    ImmutableNetwork<String, Integer> emptyNetwork =
+        NetworkBuilder.directed()
+            .allowsSelfLoops(true)
+            .nodeOrder(ElementOrder.<String>natural())
+            .<String, Integer>immutable()
+            .build();
+
+    assertThat(emptyNetwork.isDirected()).isTrue();
+    assertThat(emptyNetwork.allowsSelfLoops()).isTrue();
+    assertThat(emptyNetwork.nodeOrder()).isEqualTo(ElementOrder.<String>natural());
+  }
+
+  /**
+   * Tests that the ImmutableNetwork.Builder doesn't change when the creating NetworkBuilder
+   * changes.
+   */
+  @Test
+  @SuppressWarnings("CheckReturnValue")
+  public void immutableNetworkBuilder_copiesNetworkBuilder() {
+    NetworkBuilder<String, Object> networkBuilder =
+        NetworkBuilder.directed()
+            .allowsSelfLoops(true)
+            .<String>nodeOrder(ElementOrder.<String>natural());
+    ImmutableNetwork.Builder<String, Integer> immutableNetworkBuilder =
+        networkBuilder.<String, Integer>immutable();
+
+    // Update NetworkBuilder, but this shouldn't impact immutableNetworkBuilder
+    networkBuilder.allowsSelfLoops(false).nodeOrder(ElementOrder.<String>unordered());
+
+    ImmutableNetwork<String, Integer> emptyNetwork = immutableNetworkBuilder.build();
+
+    assertThat(emptyNetwork.isDirected()).isTrue();
+    assertThat(emptyNetwork.allowsSelfLoops()).isTrue();
+    assertThat(emptyNetwork.nodeOrder()).isEqualTo(ElementOrder.<String>natural());
+  }
+
+  @Test
+  public void immutableNetworkBuilder_addNode() {
+    ImmutableNetwork<String, Integer> network =
+        NetworkBuilder.directed().<String, Integer>immutable().addNode("A").build();
+
+    assertThat(network.nodes()).containsExactly("A");
+    assertThat(network.edges()).isEmpty();
+  }
+
+  @Test
+  public void immutableNetworkBuilder_putEdgeFromNodes() {
+    ImmutableNetwork<String, Integer> network =
+        NetworkBuilder.directed().<String, Integer>immutable().addEdge("A", "B", 10).build();
+
+    assertThat(network.nodes()).containsExactly("A", "B");
+    assertThat(network.edges()).containsExactly(10);
+    assertThat(network.incidentNodes(10)).isEqualTo(EndpointPair.ordered("A", "B"));
+  }
+
+  @Test
+  public void immutableNetworkBuilder_putEdgeFromEndpointPair() {
+    ImmutableNetwork<String, Integer> network =
+        NetworkBuilder.directed()
+            .<String, Integer>immutable()
+            .addEdge(EndpointPair.ordered("A", "B"), 10)
+            .build();
+
+    assertThat(network.nodes()).containsExactly("A", "B");
+    assertThat(network.edges()).containsExactly(10);
+    assertThat(network.incidentNodes(10)).isEqualTo(EndpointPair.ordered("A", "B"));
+  }
 }
diff --git a/android/guava-tests/test/com/google/common/graph/ImmutableValueGraphTest.java b/android/guava-tests/test/com/google/common/graph/ImmutableValueGraphTest.java
new file mode 100644
index 0000000..8e5e67f
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/graph/ImmutableValueGraphTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2019 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 com.google.common.graph;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link ImmutableValueGraph} . */
+@RunWith(JUnit4.class)
+public class ImmutableValueGraphTest {
+
+  @Test
+  public void immutableValueGraph() {
+    MutableValueGraph<String, Integer> mutableValueGraph = ValueGraphBuilder.directed().build();
+    mutableValueGraph.addNode("A");
+    ImmutableValueGraph<String, Integer> immutableValueGraph =
+        ImmutableValueGraph.copyOf(mutableValueGraph);
+
+    assertThat(immutableValueGraph.asGraph()).isInstanceOf(ImmutableGraph.class);
+    assertThat(immutableValueGraph).isNotInstanceOf(MutableValueGraph.class);
+    assertThat(immutableValueGraph).isEqualTo(mutableValueGraph);
+
+    mutableValueGraph.addNode("B");
+    assertThat(immutableValueGraph).isNotEqualTo(mutableValueGraph);
+  }
+
+  @Test
+  public void copyOfImmutableValueGraph_optimized() {
+    ValueGraph<String, Integer> graph1 =
+        ImmutableValueGraph.copyOf(ValueGraphBuilder.directed().<String, Integer>build());
+    ValueGraph<String, Integer> graph2 = ImmutableValueGraph.copyOf(graph1);
+
+    assertThat(graph2).isSameInstanceAs(graph1);
+  }
+
+  @Test
+  public void incidentEdgeOrder_stable() {
+    ImmutableValueGraph<String, Integer> immutableValueGraph =
+        ImmutableValueGraph.copyOf(ValueGraphBuilder.directed().<String, Integer>build());
+
+    assertThat(immutableValueGraph.incidentEdgeOrder()).isEqualTo(ElementOrder.stable());
+  }
+
+  @Test
+  public void incidentEdgeOrder_fromUnorderedGraph_stable() {
+    ImmutableValueGraph<String, Integer> immutableValueGraph =
+        ImmutableValueGraph.copyOf(
+            ValueGraphBuilder.directed()
+                .incidentEdgeOrder(ElementOrder.unordered())
+                .<String, Integer>build());
+
+    assertThat(immutableValueGraph.incidentEdgeOrder()).isEqualTo(ElementOrder.stable());
+  }
+
+  @Test
+  public void immutableValueGraphBuilder_appliesGraphBuilderConfig() {
+    ImmutableValueGraph<String, Integer> emptyGraph =
+        ValueGraphBuilder.directed()
+            .allowsSelfLoops(true)
+            .nodeOrder(ElementOrder.<String>natural())
+            .<String, Integer>immutable()
+            .build();
+
+    assertThat(emptyGraph.isDirected()).isTrue();
+    assertThat(emptyGraph.allowsSelfLoops()).isTrue();
+    assertThat(emptyGraph.nodeOrder()).isEqualTo(ElementOrder.<String>natural());
+  }
+
+  /**
+   * Tests that the ImmutableValueGraph.Builder doesn't change when the creating ValueGraphBuilder
+   * changes.
+   */
+  @Test
+  @SuppressWarnings("CheckReturnValue")
+  public void immutableValueGraphBuilder_copiesGraphBuilder() {
+    ValueGraphBuilder<String, Object> graphBuilder =
+        ValueGraphBuilder.directed()
+            .allowsSelfLoops(true)
+            .<String>nodeOrder(ElementOrder.<String>natural());
+    ImmutableValueGraph.Builder<String, Integer> immutableValueGraphBuilder =
+        graphBuilder.<String, Integer>immutable();
+
+    // Update ValueGraphBuilder, but this shouldn't impact immutableValueGraphBuilder
+    graphBuilder.allowsSelfLoops(false).nodeOrder(ElementOrder.<String>unordered());
+
+    ImmutableValueGraph<String, Integer> emptyGraph = immutableValueGraphBuilder.build();
+
+    assertThat(emptyGraph.isDirected()).isTrue();
+    assertThat(emptyGraph.allowsSelfLoops()).isTrue();
+    assertThat(emptyGraph.nodeOrder()).isEqualTo(ElementOrder.<String>natural());
+  }
+
+  @Test
+  public void immutableValueGraphBuilder_addNode() {
+    ImmutableValueGraph<String, Integer> graph =
+        ValueGraphBuilder.directed().<String, Integer>immutable().addNode("A").build();
+
+    assertThat(graph.nodes()).containsExactly("A");
+    assertThat(graph.edges()).isEmpty();
+  }
+
+  @Test
+  public void immutableValueGraphBuilder_putEdgeFromNodes() {
+    ImmutableValueGraph<String, Integer> graph =
+        ValueGraphBuilder.directed()
+            .<String, Integer>immutable()
+            .putEdgeValue("A", "B", 10)
+            .build();
+
+    assertThat(graph.nodes()).containsExactly("A", "B");
+    assertThat(graph.edges()).containsExactly(EndpointPair.ordered("A", "B"));
+    assertThat(graph.edgeValueOrDefault("A", "B", null)).isEqualTo(10);
+  }
+
+  @Test
+  public void immutableValueGraphBuilder_putEdgeFromEndpointPair() {
+    ImmutableValueGraph<String, Integer> graph =
+        ValueGraphBuilder.directed()
+            .<String, Integer>immutable()
+            .putEdgeValue(EndpointPair.ordered("A", "B"), 10)
+            .build();
+
+    assertThat(graph.nodes()).containsExactly("A", "B");
+    assertThat(graph.edges()).containsExactly(EndpointPair.ordered("A", "B"));
+    assertThat(graph.edgeValueOrDefault("A", "B", null)).isEqualTo(10);
+  }
+
+  @Test
+  public void immutableValueGraphBuilder_incidentEdges_preservesIncidentEdgesOrder() {
+    ImmutableValueGraph<Integer, String> graph =
+        ValueGraphBuilder.directed()
+            .<Integer, String>immutable()
+            .putEdgeValue(2, 1, "2-1")
+            .putEdgeValue(2, 3, "2-3")
+            .putEdgeValue(1, 2, "1-2")
+            .build();
+
+    assertThat(graph.incidentEdges(2))
+        .containsExactly(
+            EndpointPair.ordered(2, 1), EndpointPair.ordered(2, 3), EndpointPair.ordered(1, 2))
+        .inOrder();
+  }
+
+  @Test
+  public void immutableValueGraphBuilder_incidentEdgeOrder_stable() {
+    ImmutableValueGraph<Integer, String> graph =
+        ValueGraphBuilder.directed().<Integer, String>immutable().build();
+
+    assertThat(graph.incidentEdgeOrder()).isEqualTo(ElementOrder.stable());
+  }
+
+  @Test
+  public void immutableValueGraphBuilder_fromUnorderedBuilder_incidentEdgeOrder_stable() {
+    ImmutableValueGraph<Integer, String> graph =
+        ValueGraphBuilder.directed()
+            .incidentEdgeOrder(ElementOrder.unordered())
+            .<Integer, String>immutable()
+            .build();
+
+    assertThat(graph.incidentEdgeOrder()).isEqualTo(ElementOrder.stable());
+  }
+}
diff --git a/android/guava-tests/test/com/google/common/graph/MapCacheTest.java b/android/guava-tests/test/com/google/common/graph/MapCacheTest.java
index 564032a..f66b19b 100644
--- a/android/guava-tests/test/com/google/common/graph/MapCacheTest.java
+++ b/android/guava-tests/test/com/google/common/graph/MapCacheTest.java
@@ -83,7 +83,7 @@
   public void testRemoveEqualKeyWithDifferentReference() {
     String fooReference1 = new String("foo");
     String fooReference2 = new String("foo");
-    assertThat(fooReference1).isNotSameAs(fooReference2);
+    assertThat(fooReference1).isNotSameInstanceAs(fooReference2);
 
     assertThat(mapCache.put(fooReference1, "bar")).isNull();
     assertThat(mapCache.get(fooReference1)).isEqualTo("bar"); // ensure first reference is cached
diff --git a/android/guava-tests/test/com/google/common/graph/PackageSanityTests.java b/android/guava-tests/test/com/google/common/graph/PackageSanityTests.java
index 2022952..f1526c2 100644
--- a/android/guava-tests/test/com/google/common/graph/PackageSanityTests.java
+++ b/android/guava-tests/test/com/google/common/graph/PackageSanityTests.java
@@ -35,16 +35,20 @@
   private static final AbstractGraphBuilder<?> GRAPH_BUILDER_B =
       ValueGraphBuilder.directed().allowsSelfLoops(true).expectedNodeCount(16);
 
-  private static final ImmutableGraph<String> IMMUTABLE_GRAPH_A = graphWithNode("A");
-  private static final ImmutableGraph<String> IMMUTABLE_GRAPH_B = graphWithNode("B");
+  private static final ImmutableGraph<String> IMMUTABLE_GRAPH_A =
+      GraphBuilder.directed().<String>immutable().addNode("A").build();
+  private static final ImmutableGraph<String> IMMUTABLE_GRAPH_B =
+      GraphBuilder.directed().<String>immutable().addNode("B").build();
 
   private static final NetworkBuilder<?, ?> NETWORK_BUILDER_A =
       NetworkBuilder.directed().allowsParallelEdges(true).expectedNodeCount(10);
   private static final NetworkBuilder<?, ?> NETWORK_BUILDER_B =
       NetworkBuilder.directed().allowsSelfLoops(true).expectedNodeCount(16);
 
-  private static final ImmutableNetwork<String, String> IMMUTABLE_NETWORK_A = networkWithNode("A");
-  private static final ImmutableNetwork<String, String> IMMUTABLE_NETWORK_B = networkWithNode("B");
+  private static final ImmutableNetwork<String, String> IMMUTABLE_NETWORK_A =
+      NetworkBuilder.directed().<String, String>immutable().addNode("A").build();
+  private static final ImmutableNetwork<String, String> IMMUTABLE_NETWORK_B =
+      NetworkBuilder.directed().<String, String>immutable().addNode("B").build();
 
   public PackageSanityTests() {
     setDistinctValues(AbstractGraphBuilder.class, GRAPH_BUILDER_A, GRAPH_BUILDER_B);
@@ -66,16 +70,4 @@
           .contains(ERROR_ELEMENT_NOT_IN_GRAPH);
     }
   }
-
-  private static <N> ImmutableGraph<N> graphWithNode(N node) {
-    MutableGraph<N> graph = GraphBuilder.directed().build();
-    graph.addNode(node);
-    return ImmutableGraph.copyOf(graph);
-  }
-
-  private static <N> ImmutableNetwork<N, N> networkWithNode(N node) {
-    MutableNetwork<N, N> network = NetworkBuilder.directed().build();
-    network.addNode(node);
-    return ImmutableNetwork.copyOf(network);
-  }
 }
diff --git a/android/guava-tests/test/com/google/common/graph/StandardImmutableDirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/StandardImmutableDirectedGraphTest.java
new file mode 100644
index 0000000..d89baa0
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/graph/StandardImmutableDirectedGraphTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 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 com.google.common.graph;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for a directed {@link StandardMutableGraph}. */
+@AndroidIncompatible
+@RunWith(Parameterized.class)
+public final class StandardImmutableDirectedGraphTest extends AbstractStandardDirectedGraphTest {
+
+  @Parameters(name = "allowsSelfLoops={0}")
+  public static Collection<Object[]> parameters() {
+    return Arrays.asList(new Object[][] {{false}, {true}});
+  }
+
+  private final boolean allowsSelfLoops;
+  private ImmutableGraph.Builder<Integer> graphBuilder;
+
+  public StandardImmutableDirectedGraphTest(boolean allowsSelfLoops) {
+    this.allowsSelfLoops = allowsSelfLoops;
+  }
+
+  @Override
+  public Graph<Integer> createGraph() {
+    graphBuilder = GraphBuilder.directed().allowsSelfLoops(allowsSelfLoops).immutable();
+    return graphBuilder.build();
+  }
+
+  @Override
+  final void addNode(Integer n) {
+    graphBuilder.addNode(n);
+    graph = graphBuilder.build();
+  }
+
+  @Override
+  final void putEdge(Integer n1, Integer n2) {
+    graphBuilder.putEdge(n1, n2);
+    graph = graphBuilder.build();
+  }
+}
diff --git a/android/guava-tests/test/com/google/common/graph/StandardImmutableDirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/StandardImmutableDirectedNetworkTest.java
new file mode 100644
index 0000000..b237ff9
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/graph/StandardImmutableDirectedNetworkTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 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 com.google.common.graph;
+
+import com.google.common.collect.Ordering;
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for a directed {@link ImmutableNetwork}. */
+@AndroidIncompatible
+@RunWith(Parameterized.class)
+public class StandardImmutableDirectedNetworkTest extends AbstractStandardDirectedNetworkTest {
+
+  @Parameters(name = "allowsSelfLoops={0}, allowsParallelEdges={1}, nodeOrder={2}, edgeOrder={3}")
+  public static Collection<Object[]> parameters() {
+    ElementOrder<?> naturalElementOrder = ElementOrder.sorted(Ordering.natural());
+
+    return Arrays.asList(
+        new Object[][] {
+          {false, false, ElementOrder.insertion(), ElementOrder.insertion()},
+          {true, false, ElementOrder.insertion(), ElementOrder.insertion()},
+          {false, false, naturalElementOrder, naturalElementOrder},
+          {true, true, ElementOrder.insertion(), ElementOrder.insertion()},
+        });
+  }
+
+  private final boolean allowsSelfLoops;
+  private final boolean allowsParallelEdges;
+  private final ElementOrder<Integer> nodeOrder;
+  private final ElementOrder<String> edgeOrder;
+
+  private ImmutableNetwork.Builder<Integer, String> networkBuilder;
+
+  public StandardImmutableDirectedNetworkTest(
+      boolean allowsSelfLoops,
+      boolean allowsParallelEdges,
+      ElementOrder<Integer> nodeOrder,
+      ElementOrder<String> edgeOrder) {
+    this.allowsSelfLoops = allowsSelfLoops;
+    this.allowsParallelEdges = allowsParallelEdges;
+    this.nodeOrder = nodeOrder;
+    this.edgeOrder = edgeOrder;
+  }
+
+  @Override
+  Network<Integer, String> createGraph() {
+    networkBuilder =
+        NetworkBuilder.directed()
+            .allowsSelfLoops(allowsSelfLoops)
+            .allowsParallelEdges(allowsParallelEdges)
+            .nodeOrder(nodeOrder)
+            .edgeOrder(edgeOrder)
+            .immutable();
+
+    return networkBuilder.build();
+  }
+
+  @Override
+  void addNode(Integer n) {
+    networkBuilder.addNode(n);
+    network = networkBuilder.build();
+  }
+
+  @Override
+  void addEdge(Integer n1, Integer n2, String e) {
+    networkBuilder.addEdge(n1, n2, e);
+    network = networkBuilder.build();
+  }
+}
diff --git a/android/guava-tests/test/com/google/common/graph/StandardImmutableGraphAdditionalTest.java b/android/guava-tests/test/com/google/common/graph/StandardImmutableGraphAdditionalTest.java
new file mode 100644
index 0000000..47cd6a0
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/graph/StandardImmutableGraphAdditionalTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 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 com.google.common.graph;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link ImmutableGraph} and {@link ImmutableGraph.Builder} that are not ready covered by
+ * {@link StandardImmutableDirectedGraphTest}.
+ */
+@RunWith(JUnit4.class)
+public class StandardImmutableGraphAdditionalTest {
+
+  @Test
+  public void immutableGraph() {
+    MutableGraph<String> mutableGraph = GraphBuilder.directed().build();
+    mutableGraph.addNode("A");
+    ImmutableGraph<String> immutableGraph = ImmutableGraph.copyOf(mutableGraph);
+
+    assertThat(immutableGraph).isNotInstanceOf(MutableValueGraph.class);
+    assertThat(immutableGraph).isEqualTo(mutableGraph);
+
+    mutableGraph.addNode("B");
+    assertThat(immutableGraph).isNotEqualTo(mutableGraph);
+  }
+
+  @Test
+  public void copyOfImmutableGraph_optimized() {
+    Graph<String> graph1 = ImmutableGraph.copyOf(GraphBuilder.directed().<String>build());
+    Graph<String> graph2 = ImmutableGraph.copyOf(graph1);
+
+    assertThat(graph2).isSameInstanceAs(graph1);
+  }
+
+  @Test
+  public void immutableGraphBuilder_appliesGraphBuilderConfig() {
+    ImmutableGraph<String> emptyGraph =
+        GraphBuilder.directed()
+            .allowsSelfLoops(true)
+            .nodeOrder(ElementOrder.<String>natural())
+            .immutable()
+            .build();
+
+    assertThat(emptyGraph.isDirected()).isTrue();
+    assertThat(emptyGraph.allowsSelfLoops()).isTrue();
+    assertThat(emptyGraph.nodeOrder()).isEqualTo(ElementOrder.<String>natural());
+  }
+
+  /**
+   * Tests that the ImmutableGraph.Builder doesn't change when the creating GraphBuilder changes.
+   */
+  @Test
+  @SuppressWarnings("CheckReturnValue")
+  public void immutableGraphBuilder_copiesGraphBuilder() {
+    GraphBuilder<String> graphBuilder =
+        GraphBuilder.directed()
+            .allowsSelfLoops(true)
+            .<String>nodeOrder(ElementOrder.<String>natural());
+    ImmutableGraph.Builder<String> immutableGraphBuilder = graphBuilder.immutable();
+
+    // Update GraphBuilder, but this shouldn't impact immutableGraphBuilder
+    graphBuilder.allowsSelfLoops(false).nodeOrder(ElementOrder.<String>unordered());
+
+    ImmutableGraph<String> emptyGraph = immutableGraphBuilder.build();
+
+    assertThat(emptyGraph.isDirected()).isTrue();
+    assertThat(emptyGraph.allowsSelfLoops()).isTrue();
+    assertThat(emptyGraph.nodeOrder()).isEqualTo(ElementOrder.<String>natural());
+  }
+
+  @Test
+  public void copyOf_incidentEdgeOrder() {
+    ImmutableGraph<Object> graph = ImmutableGraph.copyOf(GraphBuilder.undirected().build());
+
+    assertThat(graph.incidentEdgeOrder()).isEqualTo(ElementOrder.stable());
+  }
+
+  @Test
+  public void copyOf_fromUnorderedGraph_incidentEdgeOrder() {
+    ImmutableGraph<Object> graph =
+        ImmutableGraph.copyOf(
+            GraphBuilder.undirected().incidentEdgeOrder(ElementOrder.unordered()).build());
+
+    assertThat(graph.incidentEdgeOrder()).isEqualTo(ElementOrder.stable());
+  }
+
+  @Test
+  public void immutableGraphBuilder_addNode() {
+    ImmutableGraph<String> graph = GraphBuilder.directed().<String>immutable().addNode("A").build();
+
+    assertThat(graph.nodes()).containsExactly("A");
+    assertThat(graph.edges()).isEmpty();
+  }
+
+  @Test
+  public void immutableGraphBuilder_putEdgeFromNodes() {
+    ImmutableGraph<String> graph =
+        GraphBuilder.directed().<String>immutable().putEdge("A", "B").build();
+
+    assertThat(graph.nodes()).containsExactly("A", "B");
+    assertThat(graph.edges()).containsExactly(EndpointPair.ordered("A", "B"));
+  }
+
+  @Test
+  public void immutableGraphBuilder_putEdgeFromEndpointPair() {
+    ImmutableGraph<String> graph =
+        GraphBuilder.directed().<String>immutable().putEdge(EndpointPair.ordered("A", "B")).build();
+
+    assertThat(graph.nodes()).containsExactly("A", "B");
+    assertThat(graph.edges()).containsExactly(EndpointPair.ordered("A", "B"));
+  }
+}
diff --git a/android/guava-tests/test/com/google/common/graph/StandardImmutableUndirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/StandardImmutableUndirectedGraphTest.java
new file mode 100644
index 0000000..7f580a6
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/graph/StandardImmutableUndirectedGraphTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 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 com.google.common.graph;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for an undirected {@link StandardMutableGraph}. */
+@AndroidIncompatible
+@RunWith(Parameterized.class)
+public final class StandardImmutableUndirectedGraphTest
+    extends AbstractStandardUndirectedGraphTest {
+
+  @Parameters(name = "allowsSelfLoops={0}")
+  public static Collection<Object[]> parameters() {
+    return Arrays.asList(new Object[][] {{false}, {true}});
+  }
+
+  private final boolean allowsSelfLoops;
+  private ImmutableGraph.Builder<Integer> graphBuilder;
+
+  public StandardImmutableUndirectedGraphTest(boolean allowsSelfLoops) {
+    this.allowsSelfLoops = allowsSelfLoops;
+  }
+
+  @Override
+  public Graph<Integer> createGraph() {
+    graphBuilder = GraphBuilder.undirected().allowsSelfLoops(allowsSelfLoops).immutable();
+    return graphBuilder.build();
+  }
+
+  @Override
+  final void addNode(Integer n) {
+    graphBuilder.addNode(n);
+    graph = graphBuilder.build();
+  }
+
+  @Override
+  final void putEdge(Integer n1, Integer n2) {
+    graphBuilder.putEdge(n1, n2);
+    graph = graphBuilder.build();
+  }
+}
diff --git a/android/guava-tests/test/com/google/common/graph/StandardMutableDirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/StandardMutableDirectedGraphTest.java
new file mode 100644
index 0000000..191010e
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/graph/StandardMutableDirectedGraphTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 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 com.google.common.graph;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for a directed {@link StandardMutableGraph}. */
+@AndroidIncompatible
+@RunWith(Parameterized.class)
+public final class StandardMutableDirectedGraphTest extends AbstractStandardDirectedGraphTest {
+
+  @Parameters(name = "allowsSelfLoops={0}, incidentEdgeOrder={1}")
+  public static Collection<Object[]> parameters() {
+    return Arrays.asList(
+        new Object[][] {
+          {false, ElementOrder.unordered()},
+          {true, ElementOrder.unordered()},
+          {false, ElementOrder.stable()},
+          {true, ElementOrder.stable()},
+        });
+  }
+
+  private final boolean allowsSelfLoops;
+  private final ElementOrder<Integer> incidentEdgeOrder;
+
+  public StandardMutableDirectedGraphTest(
+      boolean allowsSelfLoops, ElementOrder<Integer> incidentEdgeOrder) {
+    this.allowsSelfLoops = allowsSelfLoops;
+    this.incidentEdgeOrder = incidentEdgeOrder;
+  }
+
+  @Override
+  public MutableGraph<Integer> createGraph() {
+    return GraphBuilder.directed()
+        .allowsSelfLoops(allowsSelfLoops)
+        .incidentEdgeOrder(incidentEdgeOrder)
+        .build();
+  }
+
+  @Override
+  final void addNode(Integer n) {
+    graphAsMutableGraph.addNode(n);
+  }
+
+  @Override
+  final void putEdge(Integer n1, Integer n2) {
+    graphAsMutableGraph.putEdge(n1, n2);
+  }
+}
diff --git a/android/guava-tests/test/com/google/common/graph/StandardMutableDirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/StandardMutableDirectedNetworkTest.java
new file mode 100644
index 0000000..fb8be1d
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/graph/StandardMutableDirectedNetworkTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 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 com.google.common.graph;
+
+import com.google.common.collect.Ordering;
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for a directed {@link StandardMutableNetwork} allowing self-loops. */
+@AndroidIncompatible
+@RunWith(Parameterized.class)
+public class StandardMutableDirectedNetworkTest extends AbstractStandardDirectedNetworkTest {
+
+  @Parameters(name = "allowsSelfLoops={0}, allowsParallelEdges={1}, nodeOrder={2}, edgeOrder={3}")
+  public static Collection<Object[]> parameters() {
+    ElementOrder<?> naturalElementOrder = ElementOrder.sorted(Ordering.natural());
+
+    return Arrays.asList(
+        new Object[][] {
+          {false, false, ElementOrder.insertion(), ElementOrder.insertion()},
+          {true, false, ElementOrder.insertion(), ElementOrder.insertion()},
+          {false, false, naturalElementOrder, naturalElementOrder},
+          {true, true, ElementOrder.insertion(), ElementOrder.insertion()},
+        });
+  }
+
+  private final boolean allowsSelfLoops;
+  private final boolean allowsParallelEdges;
+  private final ElementOrder<Integer> nodeOrder;
+  private final ElementOrder<String> edgeOrder;
+
+  public StandardMutableDirectedNetworkTest(
+      boolean allowsSelfLoops,
+      boolean allowsParallelEdges,
+      ElementOrder<Integer> nodeOrder,
+      ElementOrder<String> edgeOrder) {
+    this.allowsSelfLoops = allowsSelfLoops;
+    this.allowsParallelEdges = allowsParallelEdges;
+    this.nodeOrder = nodeOrder;
+    this.edgeOrder = edgeOrder;
+  }
+
+  @Override
+  MutableNetwork<Integer, String> createGraph() {
+    return NetworkBuilder.directed()
+        .allowsSelfLoops(allowsSelfLoops)
+        .allowsParallelEdges(allowsParallelEdges)
+        .nodeOrder(nodeOrder)
+        .edgeOrder(edgeOrder)
+        .build();
+  }
+
+  @Override
+  void addNode(Integer n) {
+    networkAsMutableNetwork.addNode(n);
+  }
+
+  @Override
+  void addEdge(Integer n1, Integer n2, String e) {
+    networkAsMutableNetwork.addEdge(n1, n2, e);
+  }
+}
diff --git a/android/guava-tests/test/com/google/common/graph/StandardMutableUndirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/StandardMutableUndirectedGraphTest.java
new file mode 100644
index 0000000..aa0372a
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/graph/StandardMutableUndirectedGraphTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 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 com.google.common.graph;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for an undirected {@link StandardMutableGraph}. */
+@AndroidIncompatible
+@RunWith(Parameterized.class)
+public class StandardMutableUndirectedGraphTest extends AbstractStandardUndirectedGraphTest {
+
+  @Parameters(name = "allowsSelfLoops={0}, incidentEdgeOrder={1}")
+  public static Collection<Object[]> parameters() {
+    return Arrays.asList(
+        new Object[][] {
+          {false, ElementOrder.unordered()},
+          {true, ElementOrder.unordered()},
+          {false, ElementOrder.stable()},
+          {true, ElementOrder.stable()},
+        });
+  }
+
+  private final boolean allowsSelfLoops;
+  private final ElementOrder<Integer> incidentEdgeOrder;
+
+  public StandardMutableUndirectedGraphTest(
+      boolean allowsSelfLoops, ElementOrder<Integer> incidentEdgeOrder) {
+    this.allowsSelfLoops = allowsSelfLoops;
+    this.incidentEdgeOrder = incidentEdgeOrder;
+  }
+
+  @Override
+  public MutableGraph<Integer> createGraph() {
+    return GraphBuilder.undirected()
+        .allowsSelfLoops(allowsSelfLoops)
+        .incidentEdgeOrder(incidentEdgeOrder)
+        .build();
+  }
+
+  @Override
+  final void addNode(Integer n) {
+    graphAsMutableGraph.addNode(n);
+  }
+
+  @Override
+  final void putEdge(Integer n1, Integer n2) {
+    graphAsMutableGraph.putEdge(n1, n2);
+  }
+}
diff --git a/android/guava-tests/test/com/google/common/graph/StandardMutableUndirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/StandardMutableUndirectedNetworkTest.java
new file mode 100644
index 0000000..c021b5d
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/graph/StandardMutableUndirectedNetworkTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 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 com.google.common.graph;
+
+import com.google.common.collect.Ordering;
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for an undirected {@link StandardMutableNetwork}. */
+@AndroidIncompatible
+@RunWith(Parameterized.class)
+public final class StandardMutableUndirectedNetworkTest
+    extends AbstractStandardUndirectedNetworkTest {
+
+  @Parameters(name = "allowsSelfLoops={0}, allowsParallelEdges={1}, nodeOrder={2}, edgeOrder={3}")
+  public static Collection<Object[]> parameters() {
+    ElementOrder<?> naturalElementOrder = ElementOrder.sorted(Ordering.natural());
+
+    return Arrays.asList(
+        new Object[][] {
+          {false, false, ElementOrder.insertion(), ElementOrder.insertion()},
+          {true, false, ElementOrder.insertion(), ElementOrder.insertion()},
+          {false, false, naturalElementOrder, naturalElementOrder},
+          {true, true, ElementOrder.insertion(), ElementOrder.insertion()},
+        });
+  }
+
+  private final boolean allowsSelfLoops;
+  private final boolean allowsParallelEdges;
+  private final ElementOrder<Integer> nodeOrder;
+  private final ElementOrder<String> edgeOrder;
+
+  public StandardMutableUndirectedNetworkTest(
+      boolean allowsSelfLoops,
+      boolean allowsParallelEdges,
+      ElementOrder<Integer> nodeOrder,
+      ElementOrder<String> edgeOrder) {
+    this.allowsSelfLoops = allowsSelfLoops;
+    this.allowsParallelEdges = allowsParallelEdges;
+    this.nodeOrder = nodeOrder;
+    this.edgeOrder = edgeOrder;
+  }
+
+  @Override
+  MutableNetwork<Integer, String> createGraph() {
+    return NetworkBuilder.undirected()
+        .allowsSelfLoops(allowsSelfLoops)
+        .allowsParallelEdges(allowsParallelEdges)
+        .nodeOrder(nodeOrder)
+        .edgeOrder(edgeOrder)
+        .build();
+  }
+
+  @Override
+  void addNode(Integer n) {
+    networkAsMutableNetwork.addNode(n);
+  }
+
+  @Override
+  void addEdge(Integer n1, Integer n2, String e) {
+    networkAsMutableNetwork.addEdge(n1, n2, e);
+  }
+}
diff --git a/android/guava-tests/test/com/google/common/graph/TraverserTest.java b/android/guava-tests/test/com/google/common/graph/TraverserTest.java
index f1db943..d4c8cf7 100644
--- a/android/guava-tests/test/com/google/common/graph/TraverserTest.java
+++ b/android/guava-tests/test/com/google/common/graph/TraverserTest.java
@@ -185,6 +185,13 @@
   }
 
   @Test
+  public void forGraph_breadthFirst_infinite() {
+    Iterable<Integer> result =
+        Traverser.forGraph(fixedSuccessors(Iterables.cycle(1, 2, 3))).breadthFirst(0);
+    assertThat(Iterables.limit(result, 4)).containsExactly(0, 1, 2, 3).inOrder();
+  }
+
+  @Test
   public void forGraph_breadthFirst_diamond() {
     Traverser<Character> traverser = Traverser.forGraph(DIAMOND_GRAPH);
     assertEqualCharNodes(traverser.breadthFirst('a'), "abcd");
@@ -374,6 +381,13 @@
   }
 
   @Test
+  public void forGraph_depthFirstPreOrder_infinite() {
+    Iterable<Integer> result =
+        Traverser.forGraph(fixedSuccessors(Iterables.cycle(1, 2, 3))).depthFirstPreOrder(0);
+    assertThat(Iterables.limit(result, 3)).containsExactly(0, 1, 2).inOrder();
+  }
+
+  @Test
   public void forGraph_depthFirstPreOrder_diamond() {
     Traverser<Character> traverser = Traverser.forGraph(DIAMOND_GRAPH);
     assertEqualCharNodes(traverser.depthFirstPreOrder('a'), "abdc");
@@ -519,11 +533,11 @@
     Iterable<Character> result = Traverser.forGraph(graph).depthFirstPreOrder('a');
 
     assertEqualCharNodes(Iterables.limit(result, 2), "ab");
-    assertThat(graph.requestedNodes).containsExactly('a', 'a', 'b', 'd');
+    assertThat(graph.requestedNodes).containsExactly('a', 'a', 'b');
 
     // Iterate again to see if calculation is done again
     assertEqualCharNodes(Iterables.limit(result, 2), "ab");
-    assertThat(graph.requestedNodes).containsExactly('a', 'a', 'a', 'b', 'b', 'd', 'd');
+    assertThat(graph.requestedNodes).containsExactly('a', 'a', 'a', 'b', 'b');
   }
 
   @Test
@@ -532,11 +546,11 @@
     Iterable<Character> result = Traverser.forGraph(graph).depthFirstPreOrder(charactersOf("ac"));
 
     assertEqualCharNodes(Iterables.limit(result, 2), "ab");
-    assertThat(graph.requestedNodes).containsExactly('a', 'a', 'b', 'c', 'd');
+    assertThat(graph.requestedNodes).containsExactly('a', 'a', 'b', 'c');
 
     // Iterate again to see if calculation is done again
     assertEqualCharNodes(Iterables.limit(result, 2), "ab");
-    assertThat(graph.requestedNodes).containsExactly('a', 'a', 'a', 'b', 'b', 'c', 'd', 'd');
+    assertThat(graph.requestedNodes).containsExactly('a', 'a', 'a', 'b', 'b', 'c');
   }
 
   @Test
@@ -785,6 +799,13 @@
   }
 
   @Test
+  public void forTree_breadthFirst_infinite() {
+    Iterable<Integer> result =
+        Traverser.forTree(fixedSuccessors(Iterables.cycle(1, 2, 3))).breadthFirst(0);
+    assertThat(Iterables.limit(result, 8)).containsExactly(0, 1, 2, 3, 1, 2, 3, 1).inOrder();
+  }
+
+  @Test
   public void forTree_breadthFirst_tree() throws Exception {
     Traverser<Character> traverser = Traverser.forTree(TREE);
 
@@ -913,6 +934,13 @@
   }
 
   @Test
+  public void forTree_depthFirstPreOrder_infinite() {
+    Iterable<Integer> result =
+        Traverser.forTree(fixedSuccessors(Iterables.cycle(1, 2, 3))).depthFirstPreOrder(0);
+    assertThat(Iterables.limit(result, 3)).containsExactly(0, 1, 1).inOrder();
+  }
+
+  @Test
   public void forTree_depthFirstPreOrderIterable_tree() throws Exception {
     Traverser<Character> traverser = Traverser.forTree(TREE);
 
@@ -1022,11 +1050,11 @@
     Iterable<Character> result = Traverser.forGraph(graph).depthFirstPreOrder('h');
 
     assertEqualCharNodes(Iterables.limit(result, 2), "hd");
-    assertThat(graph.requestedNodes).containsExactly('h', 'h', 'd', 'a');
+    assertThat(graph.requestedNodes).containsExactly('h', 'h', 'd');
 
     // Iterate again to see if calculation is done again
     assertEqualCharNodes(Iterables.limit(result, 2), "hd");
-    assertThat(graph.requestedNodes).containsExactly('h', 'h', 'h', 'd', 'd', 'a', 'a');
+    assertThat(graph.requestedNodes).containsExactly('h', 'h', 'h', 'd', 'd');
   }
 
   @Test
@@ -1238,4 +1266,13 @@
       return delegate.successors(node);
     }
   }
+
+  private static <N> SuccessorsFunction<N> fixedSuccessors(final Iterable<N> successors) {
+    return new SuccessorsFunction<N>() {
+      @Override
+      public Iterable<N> successors(N n) {
+        return successors;
+      }
+    };
+  }
 }
diff --git a/android/guava-tests/test/com/google/common/graph/ValueGraphTest.java b/android/guava-tests/test/com/google/common/graph/ValueGraphTest.java
index a295692..a3f4814 100644
--- a/android/guava-tests/test/com/google/common/graph/ValueGraphTest.java
+++ b/android/guava-tests/test/com/google/common/graph/ValueGraphTest.java
@@ -19,14 +19,21 @@
 import static com.google.common.graph.GraphConstants.ENDPOINTS_MISMATCH;
 import static com.google.common.graph.TestUtil.assertStronglyEquivalent;
 import static com.google.common.truth.Truth.assertThat;
+import static java.util.concurrent.Executors.newFixedThreadPool;
 import static org.junit.Assert.fail;
 
+import com.google.common.collect.ImmutableList;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
 import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-/** Tests for {@link ConfigurableMutableValueGraph} and related functionality. */
+/** Tests for {@link StandardMutableValueGraph} and related functionality. */
 // TODO(user): Expand coverage and move to proper test suite.
 @RunWith(JUnit4.class)
 public final class ValueGraphTest {
@@ -44,6 +51,7 @@
     assertThat(graph.nodes()).isEqualTo(asGraph.nodes());
     assertThat(graph.edges()).isEqualTo(asGraph.edges());
     assertThat(graph.nodeOrder()).isEqualTo(asGraph.nodeOrder());
+    assertThat(graph.incidentEdgeOrder()).isEqualTo(asGraph.incidentEdgeOrder());
     assertThat(graph.isDirected()).isEqualTo(asGraph.isDirected());
     assertThat(graph.allowsSelfLoops()).isEqualTo(asGraph.allowsSelfLoops());
 
@@ -114,6 +122,18 @@
   }
 
   @Test
+  public void incidentEdgeOrder_unordered() {
+    graph = ValueGraphBuilder.directed().incidentEdgeOrder(ElementOrder.unordered()).build();
+    assertThat(graph.incidentEdgeOrder()).isEqualTo(ElementOrder.unordered());
+  }
+
+  @Test
+  public void incidentEdgeOrder_stable() {
+    graph = ValueGraphBuilder.directed().incidentEdgeOrder(ElementOrder.stable()).build();
+    assertThat(graph.incidentEdgeOrder()).isEqualTo(ElementOrder.stable());
+  }
+
+  @Test
   public void hasEdgeConnecting_directed_correct() {
     graph = ValueGraphBuilder.directed().build();
     graph.putEdgeValue(1, 2, "A");
@@ -330,4 +350,72 @@
     otherGraph.putEdgeValue(1, 2, "valueB");
     assertThat(graph).isNotEqualTo(otherGraph); // values differ
   }
+
+  @Test
+  public void incidentEdges_stableIncidentEdgeOrder_preservesIncidentEdgesOrder_directed() {
+    graph = ValueGraphBuilder.directed().incidentEdgeOrder(ElementOrder.stable()).build();
+    graph.putEdgeValue(2, 1, "2-1");
+    graph.putEdgeValue(2, 3, "2-3");
+    graph.putEdgeValue(1, 2, "1-2");
+
+    assertThat(graph.incidentEdges(2))
+        .containsExactly(
+            EndpointPair.ordered(2, 1), EndpointPair.ordered(2, 3), EndpointPair.ordered(1, 2))
+        .inOrder();
+  }
+
+  @Test
+  public void incidentEdges_stableIncidentEdgeOrder_preservesIncidentEdgesOrder_undirected() {
+    graph = ValueGraphBuilder.undirected().incidentEdgeOrder(ElementOrder.stable()).build();
+    graph.putEdgeValue(2, 3, "2-3");
+    graph.putEdgeValue(2, 1, "2-1");
+    graph.putEdgeValue(2, 4, "2-4");
+    graph.putEdgeValue(1, 2, "1-2"); // Duplicate nodes, different value
+
+    assertThat(graph.incidentEdges(2))
+        .containsExactly(
+            EndpointPair.unordered(2, 3),
+            EndpointPair.unordered(1, 2),
+            EndpointPair.unordered(2, 4))
+        .inOrder();
+  }
+
+  @Test
+  public void concurrentIteration() throws Exception {
+    graph = ValueGraphBuilder.directed().build();
+    graph.putEdgeValue(1, 2, "A");
+    graph.putEdgeValue(3, 4, "B");
+    graph.putEdgeValue(5, 6, "C");
+
+    int threadCount = 20;
+    ExecutorService executor = newFixedThreadPool(threadCount);
+    final CyclicBarrier barrier = new CyclicBarrier(threadCount);
+    ImmutableList.Builder<Future<?>> futures = ImmutableList.builder();
+    for (int i = 0; i < threadCount; i++) {
+      futures.add(
+          executor.submit(
+              new Callable<Object>() {
+                @Override
+                public Object call() throws Exception {
+                  barrier.await();
+                  Integer first = graph.nodes().iterator().next();
+                  for (Integer node : graph.nodes()) {
+                    Set<Integer> unused = graph.successors(node);
+                  }
+                  /*
+                   * Also look up an earlier node so that, if the graph is using MapRetrievalCache,
+                   * we read one of the fields declared in that class.
+                   */
+                  Set<Integer> unused = graph.successors(first);
+                  return null;
+                }
+              }));
+    }
+
+    // For more about this test, see the equivalent in AbstractNetworkTest.
+    for (Future<?> future : futures.build()) {
+      future.get();
+    }
+    executor.shutdown();
+  }
 }
diff --git a/android/guava-tests/test/com/google/common/hash/BloomFilterTest.java b/android/guava-tests/test/com/google/common/hash/BloomFilterTest.java
index d1cf3fd..4d64f98 100644
--- a/android/guava-tests/test/com/google/common/hash/BloomFilterTest.java
+++ b/android/guava-tests/test/com/google/common/hash/BloomFilterTest.java
@@ -304,7 +304,7 @@
   public void testLargeNumberOfInsertions() {
     // We use horrible FPPs here to keep Java from OOM'ing
     BloomFilter<String> unused =
-        BloomFilter.create(Funnels.unencodedCharsFunnel(), Integer.MAX_VALUE / 2, 0.29);
+        BloomFilter.create(Funnels.unencodedCharsFunnel(), Integer.MAX_VALUE / 2, 0.30);
     unused = BloomFilter.create(Funnels.unencodedCharsFunnel(), 45L * Integer.MAX_VALUE, 0.99);
   }
 
diff --git a/android/guava-tests/test/com/google/common/hash/FunnelsTest.java b/android/guava-tests/test/com/google/common/hash/FunnelsTest.java
index 922bc5d..6b0c758 100644
--- a/android/guava-tests/test/com/google/common/hash/FunnelsTest.java
+++ b/android/guava-tests/test/com/google/common/hash/FunnelsTest.java
@@ -93,7 +93,7 @@
   }
 
   public void testSequential() {
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({"unchecked", "DoNotMock"})
     Funnel<Object> elementFunnel = mock(Funnel.class);
     PrimitiveSink primitiveSink = mock(PrimitiveSink.class);
     Funnel<Iterable<?>> sequential = Funnels.sequentialFunnel(elementFunnel);
diff --git a/android/guava-tests/test/com/google/common/hash/HashTestUtils.java b/android/guava-tests/test/com/google/common/hash/HashTestUtils.java
index f2b8971..8dfbdb0 100644
--- a/android/guava-tests/test/com/google/common/hash/HashTestUtils.java
+++ b/android/guava-tests/test/com/google/common/hash/HashTestUtils.java
@@ -509,9 +509,9 @@
     rng.nextBytes(bytes);
     ByteBuffer littleEndian = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
     ByteBuffer bigEndian = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
-    assertEquals(hashFunction.hashBytes(littleEndian), hashFunction.hashBytes(littleEndian));
+    assertEquals(hashFunction.hashBytes(littleEndian), hashFunction.hashBytes(bigEndian));
     assertEquals(ByteOrder.LITTLE_ENDIAN, littleEndian.order());
-    assertEquals(ByteOrder.BIG_ENDIAN, littleEndian.order());
+    assertEquals(ByteOrder.BIG_ENDIAN, bigEndian.order());
   }
 
   static void assertHasherByteBufferPreservesByteOrder(HashFunction hashFunction) {
@@ -522,9 +522,9 @@
     ByteBuffer bigEndian = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
     assertEquals(
         hashFunction.newHasher().putBytes(littleEndian).hash(),
-        hashFunction.newHasher().putBytes(littleEndian).hash());
+        hashFunction.newHasher().putBytes(bigEndian).hash());
     assertEquals(ByteOrder.LITTLE_ENDIAN, littleEndian.order());
-    assertEquals(ByteOrder.BIG_ENDIAN, littleEndian.order());
+    assertEquals(ByteOrder.BIG_ENDIAN, bigEndian.order());
   }
 
   static void assertHashBytesThrowsCorrectExceptions(HashFunction hashFunction) {
diff --git a/android/guava-tests/test/com/google/common/hash/HashingInputStreamTest.java b/android/guava-tests/test/com/google/common/hash/HashingInputStreamTest.java
index 58ed689..05351d9 100644
--- a/android/guava-tests/test/com/google/common/hash/HashingInputStreamTest.java
+++ b/android/guava-tests/test/com/google/common/hash/HashingInputStreamTest.java
@@ -35,6 +35,7 @@
   private static final byte[] testBytes = new byte[] {'y', 'a', 'm', 's'};
   private ByteArrayInputStream buffer;
 
+  @SuppressWarnings("DoNotMock")
   @Override
   protected void setUp() throws Exception {
     super.setUp();
diff --git a/android/guava-tests/test/com/google/common/hash/HashingOutputStreamTest.java b/android/guava-tests/test/com/google/common/hash/HashingOutputStreamTest.java
index fd8f340..55e8fbf 100644
--- a/android/guava-tests/test/com/google/common/hash/HashingOutputStreamTest.java
+++ b/android/guava-tests/test/com/google/common/hash/HashingOutputStreamTest.java
@@ -33,6 +33,7 @@
   private HashFunction hashFunction;
   private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
 
+  @SuppressWarnings("DoNotMock")
   @Override
   protected void setUp() throws Exception {
     super.setUp();
diff --git a/android/guava-tests/test/com/google/common/hash/SipHashFunctionTest.java b/android/guava-tests/test/com/google/common/hash/SipHashFunctionTest.java
index c76ec76..ded4447 100644
--- a/android/guava-tests/test/com/google/common/hash/SipHashFunctionTest.java
+++ b/android/guava-tests/test/com/google/common/hash/SipHashFunctionTest.java
@@ -34,7 +34,7 @@
   private static final HashFunction SIP_WITHOUT_KEY = Hashing.sipHash24();
 
   // These constants were originally ported from https://www.131002.net/siphash/siphash24.c. See:
-  // https://github.com/nahi/siphash-java-inline/blob/master/src/test/java/SipHashInlineTest.java
+  // https://github.com/nahi/siphash-java-inline/blob/master/src/test/java/org/jruby/util/SipHashInlineTest.java
   private static final long[] EXPECTED =
       new long[] {
         0x726fdb47dd0e0e31L,
diff --git a/android/guava-tests/test/com/google/common/io/BaseEncodingTest.java b/android/guava-tests/test/com/google/common/io/BaseEncodingTest.java
index 17bc325..832fb07 100644
--- a/android/guava-tests/test/com/google/common/io/BaseEncodingTest.java
+++ b/android/guava-tests/test/com/google/common/io/BaseEncodingTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.io.BaseEncoding.base32;
 import static com.google.common.io.BaseEncoding.base32Hex;
 import static com.google.common.io.BaseEncoding.base64;
+import static com.google.common.io.BaseEncoding.base64Url;
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.common.annotations.GwtCompatible;
@@ -31,6 +32,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.Reader;
 import java.io.StringReader;
 import java.io.StringWriter;
 import junit.framework.TestCase;
@@ -190,6 +192,16 @@
     testEncodesWithOffset(base64(), "foobar", 4, 0, "");
   }
 
+  public void testBase64Url() {
+    testDecodesByBytes(base64Url(), "_zzz", new byte[] {-1, 60, -13});
+    testDecodesByBytes(base64Url(), "-zzz", new byte[] {-5, 60, -13});
+  }
+
+  public void testBase64UrlInvalidDecodings() {
+    assertFailsToDecode(base64Url(), "+zzz", "Unrecognized character: +");
+    assertFailsToDecode(base64Url(), "/zzz", "Unrecognized character: /");
+  }
+
   public void testBase32() {
     // The following test vectors are specified in RFC 4648 itself
     testEncodingWithCasing(base32(), "", "");
@@ -383,29 +395,86 @@
     assertThat(encoding.decode(encoded)).isEqualTo(decoded.getBytes(UTF_8));
   }
 
+  private static void testDecodesByBytes(BaseEncoding encoding, String encoded, byte[] decoded) {
+    assertTrue(encoding.canDecode(encoded));
+    assertThat(encoding.decode(encoded)).isEqualTo(decoded);
+  }
+
   private static void assertFailsToDecode(BaseEncoding encoding, String cannotDecode) {
     assertFailsToDecode(encoding, cannotDecode, null);
   }
 
   private static void assertFailsToDecode(
       BaseEncoding encoding, String cannotDecode, @NullableDecl String expectedMessage) {
-    assertFalse(encoding.canDecode(cannotDecode));
-    try {
-      encoding.decode(cannotDecode);
-      fail("Expected IllegalArgumentException");
-    } catch (IllegalArgumentException expected) {
-      if (expectedMessage != null) {
-        assertThat(expected).hasCauseThat().hasMessageThat().isEqualTo(expectedMessage);
-      }
+    // We use this somewhat weird pattern with an enum for each assertion we want to make as a way
+    // of dealing with the fact that one of the assertions is @GwtIncompatible but we don't want to
+    // have to have duplicate @GwtIncompatible test methods just to make that assertion.
+    for (AssertFailsToDecodeStrategy strategy : AssertFailsToDecodeStrategy.values()) {
+      strategy.assertFailsToDecode(encoding, cannotDecode, expectedMessage);
     }
-    try {
-      encoding.decodeChecked(cannotDecode);
-      fail("Expected DecodingException");
-    } catch (DecodingException expected) {
-      if (expectedMessage != null) {
-        assertThat(expected).hasMessageThat().isEqualTo(expectedMessage);
+  }
+
+  enum AssertFailsToDecodeStrategy {
+    @GwtIncompatible // decodingStream(Reader)
+    DECODING_STREAM {
+      @Override
+      void assertFailsToDecode(
+          BaseEncoding encoding, String cannotDecode, @NullableDecl String expectedMessage) {
+        // Regression test for case where DecodingException was swallowed by default implementation
+        // of
+        // InputStream.read(byte[], int, int)
+        // See https://github.com/google/guava/issues/3542
+        Reader reader = new StringReader(cannotDecode);
+        InputStream decodingStream = encoding.decodingStream(reader);
+        try {
+          ByteStreams.exhaust(decodingStream);
+          fail("Expected DecodingException");
+        } catch (DecodingException expected) {
+          // Don't assert on the expectedMessage; the messages for exceptions thrown from the
+          // decoding stream may differ from the messages for the decode methods.
+        } catch (IOException e) {
+          fail("Expected DecodingException but got: " + e);
+        }
       }
-    }
+    },
+    CAN_DECODE {
+      @Override
+      void assertFailsToDecode(
+          BaseEncoding encoding, String cannotDecode, @NullableDecl String expectedMessage) {
+        assertFalse(encoding.canDecode(cannotDecode));
+      }
+    },
+    DECODE {
+      @Override
+      void assertFailsToDecode(
+          BaseEncoding encoding, String cannotDecode, @NullableDecl String expectedMessage) {
+        try {
+          encoding.decode(cannotDecode);
+          fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+          if (expectedMessage != null) {
+            assertThat(expected).hasCauseThat().hasMessageThat().isEqualTo(expectedMessage);
+          }
+        }
+      }
+    },
+    DECODE_CHECKED {
+      @Override
+      void assertFailsToDecode(
+          BaseEncoding encoding, String cannotDecode, @NullableDecl String expectedMessage) {
+        try {
+          encoding.decodeChecked(cannotDecode);
+          fail("Expected DecodingException");
+        } catch (DecodingException expected) {
+          if (expectedMessage != null) {
+            assertThat(expected).hasMessageThat().isEqualTo(expectedMessage);
+          }
+        }
+      }
+    };
+
+    abstract void assertFailsToDecode(
+        BaseEncoding encoding, String cannotDecode, @NullableDecl String expectedMessage);
   }
 
   @GwtIncompatible // Reader/Writer
diff --git a/android/guava-tests/test/com/google/common/io/ByteSourceTest.java b/android/guava-tests/test/com/google/common/io/ByteSourceTest.java
index 0f5b3d9..f0ba829 100644
--- a/android/guava-tests/test/com/google/common/io/ByteSourceTest.java
+++ b/android/guava-tests/test/com/google/common/io/ByteSourceTest.java
@@ -454,6 +454,10 @@
     }
   }
 
+  public void testSlice_returnEmptySource() {
+    assertEquals(ByteSource.empty(), source.slice(0, 3).slice(4, 3));
+  }
+
   private static int getAndResetRecords(TestLogHandler logHandler) {
     int records = logHandler.getStoredLogRecords().size();
     logHandler.clear();
diff --git a/android/guava-tests/test/com/google/common/io/ByteStreamsTest.java b/android/guava-tests/test/com/google/common/io/ByteStreamsTest.java
index 981238a..f715303 100644
--- a/android/guava-tests/test/com/google/common/io/ByteStreamsTest.java
+++ b/android/guava-tests/test/com/google/common/io/ByteStreamsTest.java
@@ -48,7 +48,7 @@
 
     ReadableByteChannel inChannel = Channels.newChannel(new ByteArrayInputStream(expected));
     ByteStreams.copy(inChannel, outChannel);
-    assertEquals(expected, out.toByteArray());
+    assertThat(out.toByteArray()).isEqualTo(expected);
   }
 
   public void testCopyFileChannel() throws IOException {
@@ -57,24 +57,18 @@
     WritableByteChannel outChannel = Channels.newChannel(out);
 
     File testFile = createTempFile();
-    FileOutputStream fos = new FileOutputStream(testFile);
     byte[] dummyData = newPreFilledByteArray(chunkSize);
-    try {
+    try (FileOutputStream fos = new FileOutputStream(testFile)) {
       for (int i = 0; i < 500; i++) {
         fos.write(dummyData);
       }
-    } finally {
-      fos.close();
     }
-    ReadableByteChannel inChannel = new RandomAccessFile(testFile, "r").getChannel();
-    try {
+    try (ReadableByteChannel inChannel = new RandomAccessFile(testFile, "r").getChannel()) {
       ByteStreams.copy(inChannel, outChannel);
-    } finally {
-      inChannel.close();
     }
     byte[] actual = out.toByteArray();
     for (int i = 0; i < 500 * chunkSize; i += chunkSize) {
-      assertEquals(dummyData, Arrays.copyOfRange(actual, i, i + chunkSize));
+      assertThat(Arrays.copyOfRange(actual, i, i + chunkSize)).isEqualTo(dummyData);
     }
   }
 
@@ -84,56 +78,56 @@
     try {
       ByteStreams.readFully(newTestStream(10), null, 0, 10);
       fail("expected exception");
-    } catch (NullPointerException e) {
+    } catch (NullPointerException expected) {
     }
 
     try {
       ByteStreams.readFully(null, b, 0, 10);
       fail("expected exception");
-    } catch (NullPointerException e) {
+    } catch (NullPointerException expected) {
     }
 
     try {
       ByteStreams.readFully(newTestStream(10), b, -1, 10);
       fail("expected exception");
-    } catch (IndexOutOfBoundsException e) {
+    } catch (IndexOutOfBoundsException expected) {
     }
 
     try {
       ByteStreams.readFully(newTestStream(10), b, 0, -1);
       fail("expected exception");
-    } catch (IndexOutOfBoundsException e) {
+    } catch (IndexOutOfBoundsException expected) {
     }
 
     try {
       ByteStreams.readFully(newTestStream(10), b, 0, -1);
       fail("expected exception");
-    } catch (IndexOutOfBoundsException e) {
+    } catch (IndexOutOfBoundsException expected) {
     }
 
     try {
       ByteStreams.readFully(newTestStream(10), b, 2, 10);
       fail("expected exception");
-    } catch (IndexOutOfBoundsException e) {
+    } catch (IndexOutOfBoundsException expected) {
     }
 
     try {
       ByteStreams.readFully(newTestStream(5), b, 0, 10);
       fail("expected exception");
-    } catch (EOFException e) {
+    } catch (EOFException expected) {
     }
 
     Arrays.fill(b, (byte) 0);
     ByteStreams.readFully(newTestStream(10), b, 0, 0);
-    assertEquals(new byte[10], b);
+    assertThat(b).isEqualTo(new byte[10]);
 
     Arrays.fill(b, (byte) 0);
     ByteStreams.readFully(newTestStream(10), b, 0, 10);
-    assertEquals(newPreFilledByteArray(10), b);
+    assertThat(b).isEqualTo(newPreFilledByteArray(10));
 
     Arrays.fill(b, (byte) 0);
     ByteStreams.readFully(newTestStream(10), b, 0, 5);
-    assertEquals(new byte[] {0, 1, 2, 3, 4, 0, 0, 0, 0, 0}, b);
+    assertThat(b).isEqualTo(new byte[] {0, 1, 2, 3, 4, 0, 0, 0, 0, 0});
   }
 
   public void testSkipFully() throws IOException {
@@ -146,7 +140,7 @@
     try {
       skipHelper(101, 0, new ByteArrayInputStream(bytes));
       fail("expected exception");
-    } catch (EOFException e) {
+    } catch (EOFException expected) {
     }
   }
 
@@ -183,7 +177,7 @@
     ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
     byte[] actual = new byte[bytes.length];
     in.readFully(actual);
-    assertEquals(bytes, actual);
+    assertThat(actual).isEqualTo(bytes);
   }
 
   public void testNewDataInput_readFullyAndThenSome() {
@@ -268,27 +262,27 @@
 
   public void testNewDataInput_readByte() {
     ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
-    for (int i = 0; i < bytes.length; i++) {
-      assertEquals(bytes[i], in.readByte());
+    for (byte aByte : bytes) {
+      assertEquals(aByte, in.readByte());
     }
     try {
       in.readByte();
       fail("expected exception");
-    } catch (IllegalStateException ex) {
-      assertThat(ex).hasCauseThat().isInstanceOf(EOFException.class);
+    } catch (IllegalStateException expected) {
+      assertThat(expected).hasCauseThat().isInstanceOf(EOFException.class);
     }
   }
 
   public void testNewDataInput_readUnsignedByte() {
     ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
-    for (int i = 0; i < bytes.length; i++) {
-      assertEquals(bytes[i], in.readUnsignedByte());
+    for (byte aByte : bytes) {
+      assertEquals(aByte, in.readUnsignedByte());
     }
     try {
       in.readUnsignedByte();
       fail("expected exception");
-    } catch (IllegalStateException ex) {
-      assertThat(ex).hasCauseThat().isInstanceOf(EOFException.class);
+    } catch (IllegalStateException expected) {
+      assertThat(expected).hasCauseThat().isInstanceOf(EOFException.class);
     }
   }
 
@@ -323,40 +317,40 @@
     ByteArrayDataOutput out = ByteStreams.newDataOutput();
     out.writeInt(0x12345678);
     out.writeInt(0x76543210);
-    assertEquals(bytes, out.toByteArray());
+    assertThat(out.toByteArray()).isEqualTo(bytes);
   }
 
   public void testNewDataOutput_sized() {
     ByteArrayDataOutput out = ByteStreams.newDataOutput(4);
     out.writeInt(0x12345678);
     out.writeInt(0x76543210);
-    assertEquals(bytes, out.toByteArray());
+    assertThat(out.toByteArray()).isEqualTo(bytes);
   }
 
   public void testNewDataOutput_writeLong() {
     ByteArrayDataOutput out = ByteStreams.newDataOutput();
     out.writeLong(0x1234567876543210L);
-    assertEquals(bytes, out.toByteArray());
+    assertThat(out.toByteArray()).isEqualTo(bytes);
   }
 
   public void testNewDataOutput_writeByteArray() {
     ByteArrayDataOutput out = ByteStreams.newDataOutput();
     out.write(bytes);
-    assertEquals(bytes, out.toByteArray());
+    assertThat(out.toByteArray()).isEqualTo(bytes);
   }
 
   public void testNewDataOutput_writeByte() {
     ByteArrayDataOutput out = ByteStreams.newDataOutput();
     out.write(0x12);
     out.writeByte(0x34);
-    assertEquals(new byte[] {0x12, 0x34}, out.toByteArray());
+    assertThat(out.toByteArray()).isEqualTo(new byte[] {0x12, 0x34});
   }
 
   public void testNewDataOutput_writeByteOffset() {
     ByteArrayDataOutput out = ByteStreams.newDataOutput();
     out.write(bytes, 4, 2);
     byte[] expected = {bytes[4], bytes[5]};
-    assertEquals(expected, out.toByteArray());
+    assertThat(out.toByteArray()).isEqualTo(expected);
   }
 
   public void testNewDataOutput_writeBoolean() {
@@ -364,13 +358,13 @@
     out.writeBoolean(true);
     out.writeBoolean(false);
     byte[] expected = {(byte) 1, (byte) 0};
-    assertEquals(expected, out.toByteArray());
+    assertThat(out.toByteArray()).isEqualTo(expected);
   }
 
   public void testNewDataOutput_writeChar() {
     ByteArrayDataOutput out = ByteStreams.newDataOutput();
     out.writeChar('a');
-    assertEquals(new byte[] {0, 97}, out.toByteArray());
+    assertThat(out.toByteArray()).isEqualTo(new byte[] {0, 97});
   }
 
   // Hardcoded because of Android problems. See testUtf16Expected.
@@ -382,14 +376,14 @@
     out.writeChars("r\u00C9sum\u00C9");
     // need to remove byte order mark before comparing
     byte[] expected = Arrays.copyOfRange(utf16ExpectedWithBom, 2, 14);
-    assertEquals(expected, out.toByteArray());
+    assertThat(out.toByteArray()).isEqualTo(expected);
   }
 
   @AndroidIncompatible // https://code.google.com/p/android/issues/detail?id=196848
   public void testUtf16Expected() {
     byte[] hardcodedExpected = utf16ExpectedWithBom;
     byte[] computedExpected = "r\u00C9sum\u00C9".getBytes(Charsets.UTF_16);
-    assertEquals(hardcodedExpected, computedExpected);
+    assertThat(computedExpected).isEqualTo(hardcodedExpected);
   }
 
   public void testNewDataOutput_writeUTF() {
@@ -400,26 +394,26 @@
     // writeUTF writes the length of the string in 2 bytes
     assertEquals(0, actual[0]);
     assertEquals(expected.length, actual[1]);
-    assertEquals(expected, Arrays.copyOfRange(actual, 2, actual.length));
+    assertThat(Arrays.copyOfRange(actual, 2, actual.length)).isEqualTo(expected);
   }
 
   public void testNewDataOutput_writeShort() {
     ByteArrayDataOutput out = ByteStreams.newDataOutput();
     out.writeShort(0x1234);
-    assertEquals(new byte[] {0x12, 0x34}, out.toByteArray());
+    assertThat(out.toByteArray()).isEqualTo(new byte[] {0x12, 0x34});
   }
 
   public void testNewDataOutput_writeDouble() {
     ByteArrayDataOutput out = ByteStreams.newDataOutput();
     out.writeDouble(Double.longBitsToDouble(0x1234567876543210L));
-    assertEquals(bytes, out.toByteArray());
+    assertThat(out.toByteArray()).isEqualTo(bytes);
   }
 
   public void testNewDataOutput_writeFloat() {
     ByteArrayDataOutput out = ByteStreams.newDataOutput();
     out.writeFloat(Float.intBitsToFloat(0x12345678));
     out.writeFloat(Float.intBitsToFloat(0x76543210));
-    assertEquals(bytes, out.toByteArray());
+    assertThat(out.toByteArray()).isEqualTo(bytes);
   }
 
   public void testNewDataOutput_BAOS() {
@@ -427,7 +421,7 @@
     ByteArrayDataOutput out = ByteStreams.newDataOutput(baos);
     out.writeInt(0x12345678);
     assertEquals(4, baos.size());
-    assertEquals(new byte[] {0x12, 0x34, 0x56, 0x78}, baos.toByteArray());
+    assertThat(baos.toByteArray()).isEqualTo(new byte[] {0x12, 0x34, 0x56, 0x78});
   }
 
   private static final byte[] PRE_FILLED_100 = newPreFilledByteArray(100);
@@ -435,13 +429,13 @@
   public void testToByteArray() throws IOException {
     InputStream in = new ByteArrayInputStream(PRE_FILLED_100);
     byte[] b = ByteStreams.toByteArray(in);
-    assertEquals(PRE_FILLED_100, b);
+    assertThat(b).isEqualTo(PRE_FILLED_100);
   }
 
   public void testToByteArray_emptyStream() throws IOException {
     InputStream in = newTestStream(0);
     byte[] b = ByteStreams.toByteArray(in);
-    assertEquals(new byte[0], b);
+    assertThat(b).isEqualTo(new byte[0]);
   }
 
   public void testToByteArray_largeStream() throws IOException {
@@ -449,44 +443,44 @@
     byte[] expected = newPreFilledByteArray(10000000);
     InputStream in = new ByteArrayInputStream(expected);
     byte[] b = ByteStreams.toByteArray(in);
-    assertEquals(expected, b);
+    assertThat(b).isEqualTo(expected);
   }
 
   public void testToByteArray_withSize_givenCorrectSize() throws IOException {
     InputStream in = new ByteArrayInputStream(PRE_FILLED_100);
     byte[] b = ByteStreams.toByteArray(in, 100);
-    assertEquals(PRE_FILLED_100, b);
+    assertThat(b).isEqualTo(PRE_FILLED_100);
   }
 
   public void testToByteArray_withSize_givenSmallerSize() throws IOException {
     InputStream in = new ByteArrayInputStream(PRE_FILLED_100);
     byte[] b = ByteStreams.toByteArray(in, 80);
-    assertEquals(PRE_FILLED_100, b);
+    assertThat(b).isEqualTo(PRE_FILLED_100);
   }
 
   public void testToByteArray_withSize_givenLargerSize() throws IOException {
     InputStream in = new ByteArrayInputStream(PRE_FILLED_100);
     byte[] b = ByteStreams.toByteArray(in, 120);
-    assertEquals(PRE_FILLED_100, b);
+    assertThat(b).isEqualTo(PRE_FILLED_100);
   }
 
   public void testToByteArray_withSize_givenSizeZero() throws IOException {
     InputStream in = new ByteArrayInputStream(PRE_FILLED_100);
     byte[] b = ByteStreams.toByteArray(in, 0);
-    assertEquals(PRE_FILLED_100, b);
+    assertThat(b).isEqualTo(PRE_FILLED_100);
   }
 
   public void testToByteArray_withSize_givenSizeOneSmallerThanActual() throws IOException {
     InputStream in = new ByteArrayInputStream(PRE_FILLED_100);
     // this results in toByteArrayInternal being called when the stream is actually exhausted
     byte[] b = ByteStreams.toByteArray(in, 99);
-    assertEquals(PRE_FILLED_100, b);
+    assertThat(b).isEqualTo(PRE_FILLED_100);
   }
 
   public void testToByteArray_withSize_givenSizeTwoSmallerThanActual() throws IOException {
     InputStream in = new ByteArrayInputStream(PRE_FILLED_100);
     byte[] b = ByteStreams.toByteArray(in, 98);
-    assertEquals(PRE_FILLED_100, b);
+    assertThat(b).isEqualTo(PRE_FILLED_100);
   }
 
   public void testExhaust() throws IOException {
@@ -508,7 +502,7 @@
   private static class SlowSkipper extends FilterInputStream {
     private final long max;
 
-    public SlowSkipper(InputStream in, long max) {
+    SlowSkipper(InputStream in, long max) {
       super(in);
       this.max = max;
     }
@@ -521,15 +515,15 @@
 
   public void testReadBytes() throws IOException {
     final byte[] array = newPreFilledByteArray(1000);
-    assertEquals(
-        array, ByteStreams.readBytes(new ByteArrayInputStream(array), new TestByteProcessor()));
+    assertThat(ByteStreams.readBytes(new ByteArrayInputStream(array), new TestByteProcessor()))
+        .isEqualTo(array);
   }
 
-  private class TestByteProcessor implements ByteProcessor<byte[]> {
+  private static class TestByteProcessor implements ByteProcessor<byte[]> {
     private final ByteArrayOutputStream out = new ByteArrayOutputStream();
 
     @Override
-    public boolean processBytes(byte[] buf, int off, int len) throws IOException {
+    public boolean processBytes(byte[] buf, int off, int len) {
       out.write(buf, off, len);
       return true;
     }
@@ -549,7 +543,8 @@
             new ByteProcessor<Integer>() {
               @Override
               public boolean processBytes(byte[] buf, int off, int len) {
-                assertEquals(copyOfRange(buf, off, off + len), newPreFilledByteArray(8192));
+                assertThat(newPreFilledByteArray(8192))
+                    .isEqualTo(Arrays.copyOfRange(buf, off, off + len));
                 return false;
               }
 
@@ -676,17 +671,4 @@
       return false;
     }
   }
-
-  private static byte[] copyOfRange(byte[] in, int from, int to) {
-    byte[] out = new byte[to - from];
-    for (int i = 0; i < to - from; i++) {
-      out[i] = in[from + i];
-    }
-    return out;
-  }
-
-  // TODO(cpovirk): Inline this.
-  private static void assertEquals(byte[] expected, byte[] actual) {
-    assertThat(actual).isEqualTo(expected);
-  }
 }
diff --git a/android/guava-tests/test/com/google/common/io/CharStreamsTest.java b/android/guava-tests/test/com/google/common/io/CharStreamsTest.java
index 3007f09..9b2c24e 100644
--- a/android/guava-tests/test/com/google/common/io/CharStreamsTest.java
+++ b/android/guava-tests/test/com/google/common/io/CharStreamsTest.java
@@ -267,6 +267,21 @@
     String test = "Test string for NullWriter";
     nullWriter.write(test);
     nullWriter.write(test, 2, 10);
+    nullWriter.append(null);
+    nullWriter.append(null, 0, 4);
+
+    try {
+      nullWriter.append(null, -1, 4);
+      fail();
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    try {
+      nullWriter.append(null, 0, 5);
+      fail();
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
     // nothing really to assert?
     assertSame(CharStreams.nullWriter(), CharStreams.nullWriter());
   }
diff --git a/android/guava-tests/test/com/google/common/math/BigDecimalMathTest.java b/android/guava-tests/test/com/google/common/math/BigDecimalMathTest.java
new file mode 100644
index 0000000..ff86fd5
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/math/BigDecimalMathTest.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2020 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 com.google.common.math;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static java.math.RoundingMode.CEILING;
+import static java.math.RoundingMode.DOWN;
+import static java.math.RoundingMode.FLOOR;
+import static java.math.RoundingMode.HALF_DOWN;
+import static java.math.RoundingMode.HALF_EVEN;
+import static java.math.RoundingMode.HALF_UP;
+import static java.math.RoundingMode.UNNECESSARY;
+import static java.math.RoundingMode.UP;
+import static java.math.RoundingMode.values;
+
+import com.google.common.annotations.GwtIncompatible;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.Map;
+import junit.framework.TestCase;
+
+@GwtIncompatible
+public class BigDecimalMathTest extends TestCase {
+  private static final class RoundToDoubleTester {
+    private final BigDecimal input;
+    private final Map<RoundingMode, Double> expectedValues = new EnumMap<>(RoundingMode.class);
+    private boolean unnecessaryShouldThrow = false;
+
+    RoundToDoubleTester(BigDecimal input) {
+      this.input = input;
+    }
+
+    RoundToDoubleTester setExpectation(double expectedValue, RoundingMode... modes) {
+      for (RoundingMode mode : modes) {
+        Double previous = expectedValues.put(mode, expectedValue);
+        if (previous != null) {
+          throw new AssertionError();
+        }
+      }
+      return this;
+    }
+
+    public RoundToDoubleTester roundUnnecessaryShouldThrow() {
+      unnecessaryShouldThrow = true;
+      return this;
+    }
+
+    public void test() {
+      assertThat(expectedValues.keySet())
+          .containsAtLeastElementsIn(EnumSet.complementOf(EnumSet.of(UNNECESSARY)));
+      for (Map.Entry<RoundingMode, Double> entry : expectedValues.entrySet()) {
+        RoundingMode mode = entry.getKey();
+        Double expectation = entry.getValue();
+        assertWithMessage("roundToDouble(" + input + ", " + mode + ")")
+            .that(BigDecimalMath.roundToDouble(input, mode))
+            .isEqualTo(expectation);
+      }
+
+      if (!expectedValues.containsKey(UNNECESSARY)) {
+        assertWithMessage("Expected roundUnnecessaryShouldThrow call")
+            .that(unnecessaryShouldThrow)
+            .isTrue();
+        try {
+          BigDecimalMath.roundToDouble(input, UNNECESSARY);
+          fail("Expected ArithmeticException for roundToDouble(" + input + ", UNNECESSARY)");
+        } catch (ArithmeticException expected) {
+          // expected
+        }
+      }
+    }
+  }
+
+  public void testRoundToDouble_zero() {
+    new RoundToDoubleTester(BigDecimal.ZERO).setExpectation(0.0, values()).test();
+  }
+
+  public void testRoundToDouble_oneThird() {
+    new RoundToDoubleTester(
+            BigDecimal.ONE.divide(BigDecimal.valueOf(3), new MathContext(50, HALF_EVEN)))
+        .roundUnnecessaryShouldThrow()
+        .setExpectation(0.33333333333333337, UP, CEILING)
+        .setExpectation(0.3333333333333333, HALF_EVEN, FLOOR, DOWN, HALF_UP, HALF_DOWN)
+        .test();
+  }
+
+  public void testRoundToDouble_halfMinDouble() {
+    BigDecimal minDouble = new BigDecimal(Double.MIN_VALUE);
+    BigDecimal halfMinDouble = minDouble.divide(BigDecimal.valueOf(2));
+    new RoundToDoubleTester(halfMinDouble)
+        .roundUnnecessaryShouldThrow()
+        .setExpectation(Double.MIN_VALUE, UP, CEILING, HALF_UP)
+        .setExpectation(0.0, HALF_EVEN, FLOOR, DOWN, HALF_DOWN)
+        .test();
+  }
+
+  public void testRoundToDouble_halfNegativeMinDouble() {
+    BigDecimal minDouble = new BigDecimal(-Double.MIN_VALUE);
+    BigDecimal halfMinDouble = minDouble.divide(BigDecimal.valueOf(2));
+    new RoundToDoubleTester(halfMinDouble)
+        .roundUnnecessaryShouldThrow()
+        .setExpectation(-Double.MIN_VALUE, UP, FLOOR, HALF_UP)
+        .setExpectation(-0.0, HALF_EVEN, CEILING, DOWN, HALF_DOWN)
+        .test();
+  }
+
+  public void testRoundToDouble_smallPositive() {
+    new RoundToDoubleTester(BigDecimal.valueOf(16)).setExpectation(16.0, values()).test();
+  }
+
+  public void testRoundToDouble_maxPreciselyRepresentable() {
+    new RoundToDoubleTester(BigDecimal.valueOf(1L << 53))
+        .setExpectation(Math.pow(2, 53), values())
+        .test();
+  }
+
+  public void testRoundToDouble_maxPreciselyRepresentablePlusOne() {
+    double twoToThe53 = Math.pow(2, 53);
+    // the representable doubles are 2^53 and 2^53 + 2.
+    // 2^53+1 is halfway between, so HALF_UP will go up and HALF_DOWN will go down.
+    new RoundToDoubleTester(BigDecimal.valueOf((1L << 53) + 1))
+        .setExpectation(twoToThe53, DOWN, FLOOR, HALF_DOWN, HALF_EVEN)
+        .setExpectation(Math.nextUp(twoToThe53), CEILING, UP, HALF_UP)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  public void testRoundToDouble_twoToThe54PlusOne() {
+    double twoToThe54 = Math.pow(2, 54);
+    // the representable doubles are 2^54 and 2^54 + 4
+    // 2^54+1 is less than halfway between, so HALF_DOWN and HALF_UP will both go down.
+    new RoundToDoubleTester(BigDecimal.valueOf((1L << 54) + 1))
+        .setExpectation(twoToThe54, DOWN, FLOOR, HALF_DOWN, HALF_UP, HALF_EVEN)
+        .setExpectation(Math.nextUp(twoToThe54), CEILING, UP)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  public void testRoundToDouble_twoToThe54PlusOneHalf() {
+    double twoToThe54 = Math.pow(2, 54);
+    // the representable doubles are 2^54 and 2^54 + 4
+    // 2^54+1 is less than halfway between, so HALF_DOWN and HALF_UP will both go down.
+    new RoundToDoubleTester(BigDecimal.valueOf(1L << 54).add(new BigDecimal(0.5)))
+        .setExpectation(twoToThe54, DOWN, FLOOR, HALF_DOWN, HALF_UP, HALF_EVEN)
+        .setExpectation(Math.nextUp(twoToThe54), CEILING, UP)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  public void testRoundToDouble_twoToThe54PlusThree() {
+    double twoToThe54 = Math.pow(2, 54);
+    // the representable doubles are 2^54 and 2^54 + 4
+    // 2^54+3 is more than halfway between, so HALF_DOWN and HALF_UP will both go up.
+    new RoundToDoubleTester(BigDecimal.valueOf((1L << 54) + 3))
+        .setExpectation(twoToThe54, DOWN, FLOOR)
+        .setExpectation(Math.nextUp(twoToThe54), CEILING, UP, HALF_DOWN, HALF_UP, HALF_EVEN)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  public void testRoundToDouble_twoToThe54PlusFour() {
+    new RoundToDoubleTester(BigDecimal.valueOf((1L << 54) + 4))
+        .setExpectation(Math.pow(2, 54) + 4, values())
+        .test();
+  }
+
+  public void testRoundToDouble_maxDouble() {
+    BigDecimal maxDoubleAsBD = new BigDecimal(Double.MAX_VALUE);
+    new RoundToDoubleTester(maxDoubleAsBD).setExpectation(Double.MAX_VALUE, values()).test();
+  }
+
+  public void testRoundToDouble_maxDoublePlusOne() {
+    BigDecimal maxDoubleAsBD = new BigDecimal(Double.MAX_VALUE).add(BigDecimal.ONE);
+    new RoundToDoubleTester(maxDoubleAsBD)
+        .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN)
+        .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  public void testRoundToDouble_wayTooBig() {
+    BigDecimal bi = BigDecimal.valueOf(2).pow(2 * Double.MAX_EXPONENT);
+    new RoundToDoubleTester(bi)
+        .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN)
+        .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  public void testRoundToDouble_smallNegative() {
+    new RoundToDoubleTester(BigDecimal.valueOf(-16)).setExpectation(-16.0, values()).test();
+  }
+
+  public void testRoundToDouble_minPreciselyRepresentable() {
+    new RoundToDoubleTester(BigDecimal.valueOf(-1L << 53))
+        .setExpectation(-Math.pow(2, 53), values())
+        .test();
+  }
+
+  public void testRoundToDouble_minPreciselyRepresentableMinusOne() {
+    // the representable doubles are -2^53 and -2^53 - 2.
+    // -2^53-1 is halfway between, so HALF_UP will go up and HALF_DOWN will go down.
+    new RoundToDoubleTester(BigDecimal.valueOf((-1L << 53) - 1))
+        .setExpectation(-Math.pow(2, 53), DOWN, CEILING, HALF_DOWN, HALF_EVEN)
+        .setExpectation(DoubleUtils.nextDown(-Math.pow(2, 53)), FLOOR, UP, HALF_UP)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  public void testRoundToDouble_negativeTwoToThe54MinusOne() {
+    new RoundToDoubleTester(BigDecimal.valueOf((-1L << 54) - 1))
+        .setExpectation(-Math.pow(2, 54), DOWN, CEILING, HALF_DOWN, HALF_UP, HALF_EVEN)
+        .setExpectation(DoubleUtils.nextDown(-Math.pow(2, 54)), FLOOR, UP)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  public void testRoundToDouble_negativeTwoToThe54MinusThree() {
+    new RoundToDoubleTester(BigDecimal.valueOf((-1L << 54) - 3))
+        .setExpectation(-Math.pow(2, 54), DOWN, CEILING)
+        .setExpectation(
+            DoubleUtils.nextDown(-Math.pow(2, 54)), FLOOR, UP, HALF_DOWN, HALF_UP, HALF_EVEN)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  public void testRoundToDouble_negativeTwoToThe54MinusFour() {
+    new RoundToDoubleTester(BigDecimal.valueOf((-1L << 54) - 4))
+        .setExpectation(-Math.pow(2, 54) - 4, values())
+        .test();
+  }
+
+  public void testRoundToDouble_minDouble() {
+    BigDecimal minDoubleAsBD = new BigDecimal(-Double.MAX_VALUE);
+    new RoundToDoubleTester(minDoubleAsBD).setExpectation(-Double.MAX_VALUE, values()).test();
+  }
+
+  public void testRoundToDouble_minDoubleMinusOne() {
+    BigDecimal minDoubleAsBD = new BigDecimal(-Double.MAX_VALUE).subtract(BigDecimal.ONE);
+    new RoundToDoubleTester(minDoubleAsBD)
+        .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN)
+        .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  public void testRoundToDouble_negativeWayTooBig() {
+    BigDecimal bi = BigDecimal.valueOf(2).pow(2 * Double.MAX_EXPONENT).negate();
+    new RoundToDoubleTester(bi)
+        .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN)
+        .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+}
diff --git a/android/guava-tests/test/com/google/common/math/BigIntegerMathTest.java b/android/guava-tests/test/com/google/common/math/BigIntegerMathTest.java
index 33f3bcf..8a9b4fc 100644
--- a/android/guava-tests/test/com/google/common/math/BigIntegerMathTest.java
+++ b/android/guava-tests/test/com/google/common/math/BigIntegerMathTest.java
@@ -22,6 +22,8 @@
 import static com.google.common.math.MathTesting.NEGATIVE_BIGINTEGER_CANDIDATES;
 import static com.google.common.math.MathTesting.NONZERO_BIGINTEGER_CANDIDATES;
 import static com.google.common.math.MathTesting.POSITIVE_BIGINTEGER_CANDIDATES;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static java.math.BigInteger.ONE;
 import static java.math.BigInteger.TEN;
 import static java.math.BigInteger.ZERO;
@@ -33,6 +35,7 @@
 import static java.math.RoundingMode.HALF_UP;
 import static java.math.RoundingMode.UNNECESSARY;
 import static java.math.RoundingMode.UP;
+import static java.math.RoundingMode.values;
 import static java.util.Arrays.asList;
 
 import com.google.common.annotations.GwtCompatible;
@@ -41,6 +44,9 @@
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.math.RoundingMode;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.Map;
 import junit.framework.TestCase;
 
 /**
@@ -542,6 +548,219 @@
     }
   }
 
+  @GwtIncompatible
+  private static final class RoundToDoubleTester {
+    private final BigInteger input;
+    private final Map<RoundingMode, Double> expectedValues = new EnumMap<>(RoundingMode.class);
+    private boolean unnecessaryShouldThrow = false;
+
+    RoundToDoubleTester(BigInteger input) {
+      this.input = input;
+    }
+
+    RoundToDoubleTester setExpectation(double expectedValue, RoundingMode... modes) {
+      for (RoundingMode mode : modes) {
+        Double previous = expectedValues.put(mode, expectedValue);
+        if (previous != null) {
+          throw new AssertionError();
+        }
+      }
+      return this;
+    }
+
+    public RoundToDoubleTester roundUnnecessaryShouldThrow() {
+      unnecessaryShouldThrow = true;
+      return this;
+    }
+
+    public void test() {
+      assertThat(expectedValues.keySet())
+          .containsAtLeastElementsIn(EnumSet.complementOf(EnumSet.of(UNNECESSARY)));
+      for (Map.Entry<RoundingMode, Double> entry : expectedValues.entrySet()) {
+        RoundingMode mode = entry.getKey();
+        Double expectation = entry.getValue();
+        assertWithMessage("roundToDouble(" + input + ", " + mode + ")")
+            .that(BigIntegerMath.roundToDouble(input, mode))
+            .isEqualTo(expectation);
+      }
+
+      if (!expectedValues.containsKey(UNNECESSARY)) {
+        assertWithMessage("Expected roundUnnecessaryShouldThrow call")
+            .that(unnecessaryShouldThrow)
+            .isTrue();
+        try {
+          BigIntegerMath.roundToDouble(input, UNNECESSARY);
+          fail("Expected ArithmeticException for roundToDouble(" + input + ", UNNECESSARY)");
+        } catch (ArithmeticException expected) {
+          // expected
+        }
+      }
+    }
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_Zero() {
+    new RoundToDoubleTester(BigInteger.ZERO).setExpectation(0.0, values()).test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_smallPositive() {
+    new RoundToDoubleTester(BigInteger.valueOf(16)).setExpectation(16.0, values()).test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_maxPreciselyRepresentable() {
+    new RoundToDoubleTester(BigInteger.valueOf(1L << 53))
+        .setExpectation(Math.pow(2, 53), values())
+        .test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_maxPreciselyRepresentablePlusOne() {
+    double twoToThe53 = Math.pow(2, 53);
+    // the representable doubles are 2^53 and 2^53 + 2.
+    // 2^53+1 is halfway between, so HALF_UP will go up and HALF_DOWN will go down.
+    new RoundToDoubleTester(BigInteger.valueOf((1L << 53) + 1))
+        .setExpectation(twoToThe53, DOWN, FLOOR, HALF_DOWN, HALF_EVEN)
+        .setExpectation(Math.nextUp(twoToThe53), CEILING, UP, HALF_UP)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_twoToThe54PlusOne() {
+    double twoToThe54 = Math.pow(2, 54);
+    // the representable doubles are 2^54 and 2^54 + 4
+    // 2^54+1 is less than halfway between, so HALF_DOWN and HALF_UP will both go down.
+    new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 1))
+        .setExpectation(twoToThe54, DOWN, FLOOR, HALF_DOWN, HALF_UP, HALF_EVEN)
+        .setExpectation(Math.nextUp(twoToThe54), CEILING, UP)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_twoToThe54PlusThree() {
+    double twoToThe54 = Math.pow(2, 54);
+    // the representable doubles are 2^54 and 2^54 + 4
+    // 2^54+3 is more than halfway between, so HALF_DOWN and HALF_UP will both go up.
+    new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 3))
+        .setExpectation(twoToThe54, DOWN, FLOOR)
+        .setExpectation(Math.nextUp(twoToThe54), CEILING, UP, HALF_DOWN, HALF_UP, HALF_EVEN)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_twoToThe54PlusFour() {
+    new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 4))
+        .setExpectation(Math.pow(2, 54) + 4, values())
+        .test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_maxDouble() {
+    BigInteger maxDoubleAsBI = DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY);
+    new RoundToDoubleTester(maxDoubleAsBI).setExpectation(Double.MAX_VALUE, values()).test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_maxDoublePlusOne() {
+    BigInteger maxDoubleAsBI =
+        DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY).add(BigInteger.ONE);
+    new RoundToDoubleTester(maxDoubleAsBI)
+        .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN)
+        .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_wayTooBig() {
+    BigInteger bi = BigInteger.ONE.shiftLeft(2 * Double.MAX_EXPONENT);
+    new RoundToDoubleTester(bi)
+        .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN)
+        .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_smallNegative() {
+    new RoundToDoubleTester(BigInteger.valueOf(-16)).setExpectation(-16.0, values()).test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_minPreciselyRepresentable() {
+    new RoundToDoubleTester(BigInteger.valueOf(-1L << 53))
+        .setExpectation(-Math.pow(2, 53), values())
+        .test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_minPreciselyRepresentableMinusOne() {
+    // the representable doubles are -2^53 and -2^53 - 2.
+    // -2^53-1 is halfway between, so HALF_UP will go up and HALF_DOWN will go down.
+    new RoundToDoubleTester(BigInteger.valueOf((-1L << 53) - 1))
+        .setExpectation(-Math.pow(2, 53), DOWN, CEILING, HALF_DOWN, HALF_EVEN)
+        .setExpectation(DoubleUtils.nextDown(-Math.pow(2, 53)), FLOOR, UP, HALF_UP)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_negativeTwoToThe54MinusOne() {
+    new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 1))
+        .setExpectation(-Math.pow(2, 54), DOWN, CEILING, HALF_DOWN, HALF_UP, HALF_EVEN)
+        .setExpectation(DoubleUtils.nextDown(-Math.pow(2, 54)), FLOOR, UP)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_negativeTwoToThe54MinusThree() {
+    new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 3))
+        .setExpectation(-Math.pow(2, 54), DOWN, CEILING)
+        .setExpectation(
+            DoubleUtils.nextDown(-Math.pow(2, 54)), FLOOR, UP, HALF_DOWN, HALF_UP, HALF_EVEN)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_negativeTwoToThe54MinusFour() {
+    new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 4))
+        .setExpectation(-Math.pow(2, 54) - 4, values())
+        .test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_minDouble() {
+    BigInteger minDoubleAsBI = DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY);
+    new RoundToDoubleTester(minDoubleAsBI).setExpectation(-Double.MAX_VALUE, values()).test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_minDoubleMinusOne() {
+    BigInteger minDoubleAsBI =
+        DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY).subtract(BigInteger.ONE);
+    new RoundToDoubleTester(minDoubleAsBI)
+        .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN)
+        .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
+  @GwtIncompatible
+  public void testRoundToDouble_negativeWayTooBig() {
+    BigInteger bi = BigInteger.ONE.shiftLeft(2 * Double.MAX_EXPONENT).negate();
+    new RoundToDoubleTester(bi)
+        .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN)
+        .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR)
+        .roundUnnecessaryShouldThrow()
+        .test();
+  }
+
   @GwtIncompatible // NullPointerTester
   public void testNullPointers() {
     NullPointerTester tester = new NullPointerTester();
diff --git a/android/guava-tests/test/com/google/common/math/LongMathTest.java b/android/guava-tests/test/com/google/common/math/LongMathTest.java
index 1387e00..2284668 100644
--- a/android/guava-tests/test/com/google/common/math/LongMathTest.java
+++ b/android/guava-tests/test/com/google/common/math/LongMathTest.java
@@ -25,7 +25,8 @@
 import static com.google.common.math.MathTesting.NONZERO_LONG_CANDIDATES;
 import static com.google.common.math.MathTesting.POSITIVE_INTEGER_CANDIDATES;
 import static com.google.common.math.MathTesting.POSITIVE_LONG_CANDIDATES;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static java.math.BigInteger.valueOf;
 import static java.math.RoundingMode.FLOOR;
 import static java.math.RoundingMode.UNNECESSARY;
@@ -36,6 +37,7 @@
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.math.RoundingMode;
+import java.util.EnumSet;
 import java.util.Random;
 import junit.framework.TestCase;
 
@@ -949,7 +951,76 @@
     }
   }
 
+  private static final long[] roundToDoubleTestCandidates = {
+    0,
+    16,
+    1L << 53,
+    (1L << 53) + 1,
+    (1L << 53) + 2,
+    (1L << 53) + 3,
+    (1L << 53) + 4,
+    1L << 54,
+    (1L << 54) + 1,
+    (1L << 54) + 2,
+    (1L << 54) + 3,
+    (1L << 54) + 4,
+    0x7ffffffffffffe00L, // halfway between 2^63 and next-lower double
+    0x7ffffffffffffe01L, // above + 1
+    0x7ffffffffffffdffL, // above - 1
+    Long.MAX_VALUE - (1L << 11) + 1,
+    Long.MAX_VALUE - 2,
+    Long.MAX_VALUE - 1,
+    Long.MAX_VALUE,
+    -16,
+    -1L << 53,
+    -(1L << 53) - 1,
+    -(1L << 53) - 2,
+    -(1L << 53) - 3,
+    -(1L << 53) - 4,
+    -1L << 54,
+    -(1L << 54) - 1,
+    -(1L << 54) - 2,
+    -(1L << 54) - 3,
+    -(1L << 54) - 4,
+    Long.MIN_VALUE + 2,
+    Long.MIN_VALUE + 1,
+    Long.MIN_VALUE
+  };
+
+  @GwtIncompatible
+  public void testRoundToDoubleAgainstBigInteger() {
+    for (RoundingMode roundingMode : EnumSet.complementOf(EnumSet.of(UNNECESSARY))) {
+      for (long candidate : roundToDoubleTestCandidates) {
+        assertThat(LongMath.roundToDouble(candidate, roundingMode))
+            .isEqualTo(BigIntegerMath.roundToDouble(BigInteger.valueOf(candidate), roundingMode));
+      }
+    }
+  }
+
+  @GwtIncompatible
+  public void testRoundToDoubleAgainstBigIntegerUnnecessary() {
+    for (long candidate : roundToDoubleTestCandidates) {
+      Double expectedDouble = null;
+      try {
+        expectedDouble = BigIntegerMath.roundToDouble(BigInteger.valueOf(candidate), UNNECESSARY);
+      } catch (ArithmeticException expected) {
+        // do nothing
+      }
+
+      if (expectedDouble != null) {
+        assertThat(LongMath.roundToDouble(candidate, UNNECESSARY)).isEqualTo(expectedDouble);
+      } else {
+        try {
+          LongMath.roundToDouble(candidate, UNNECESSARY);
+          fail("Expected ArithmeticException on roundToDouble(" + candidate + ", UNNECESSARY)");
+        } catch (ArithmeticException expected) {
+          // success
+        }
+      }
+    }
+  }
+
   private static void failFormat(String template, Object... args) {
-    assert_().fail(template, args);
+    assertWithMessage(template, args).fail();
   }
 }
diff --git a/android/guava-tests/test/com/google/common/math/PairedStatsAccumulatorTest.java b/android/guava-tests/test/com/google/common/math/PairedStatsAccumulatorTest.java
index 0a21790..34f82e9 100644
--- a/android/guava-tests/test/com/google/common/math/PairedStatsAccumulatorTest.java
+++ b/android/guava-tests/test/com/google/common/math/PairedStatsAccumulatorTest.java
@@ -43,6 +43,7 @@
 import static com.google.common.math.StatsTesting.createFilledPairedStatsAccumulator;
 import static com.google.common.math.StatsTesting.createPartitionedFilledPairedStatsAccumulator;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import com.google.common.math.StatsTesting.ManyValues;
 import java.util.Collections;
@@ -210,17 +211,17 @@
       double populationCovarianceByAddAllPartitionedPairedStats =
           accumulatorByAddAllPartitionedPairedStats.populationCovariance();
       if (values.hasAnyNonFinite()) {
-        assertThat(populationCovariance).named("population covariance of " + values).isNaN();
-        assertThat(populationCovarianceByAddAllPartitionedPairedStats)
-            .named("population covariance by addAll(PairedStats) of " + values)
+        assertWithMessage("population covariance of " + values).that(populationCovariance).isNaN();
+        assertWithMessage("population covariance by addAll(PairedStats) of " + values)
+            .that(populationCovarianceByAddAllPartitionedPairedStats)
             .isNaN();
       } else {
-        assertThat(populationCovariance)
-            .named("population covariance of " + values)
+        assertWithMessage("population covariance of " + values)
+            .that(populationCovariance)
             .isWithin(ALLOWED_ERROR)
             .of(MANY_VALUES_SUM_OF_PRODUCTS_OF_DELTAS / MANY_VALUES_COUNT);
-        assertThat(populationCovarianceByAddAllPartitionedPairedStats)
-            .named("population covariance by addAll(PairedStats) of " + values)
+        assertWithMessage("population covariance by addAll(PairedStats) of " + values)
+            .that(populationCovarianceByAddAllPartitionedPairedStats)
             .isWithin(ALLOWED_ERROR)
             .of(MANY_VALUES_SUM_OF_PRODUCTS_OF_DELTAS / MANY_VALUES_COUNT);
       }
@@ -340,22 +341,22 @@
       double pearsonsCorrelationCoefficientByAddAllPartitionedPairedStats =
           accumulatorByAddAllPartitionedPairedStats.pearsonsCorrelationCoefficient();
       if (values.hasAnyNonFinite()) {
-        assertThat(pearsonsCorrelationCoefficient)
-            .named("Pearson's correlation coefficient of " + values)
+        assertWithMessage("Pearson's correlation coefficient of " + values)
+            .that(pearsonsCorrelationCoefficient)
             .isNaN();
-        assertThat(pearsonsCorrelationCoefficient)
-            .named("Pearson's correlation coefficient by addAll(PairedStats) of " + values)
+        assertWithMessage("Pearson's correlation coefficient by addAll(PairedStats) of " + values)
+            .that(pearsonsCorrelationCoefficient)
             .isNaN();
       } else {
-        assertThat(pearsonsCorrelationCoefficient)
-            .named("Pearson's correlation coefficient of " + values)
+        assertWithMessage("Pearson's correlation coefficient of " + values)
+            .that(pearsonsCorrelationCoefficient)
             .isWithin(ALLOWED_ERROR)
             .of(
                 accumulator.populationCovariance()
                     / (accumulator.xStats().populationStandardDeviation()
                         * accumulator.yStats().populationStandardDeviation()));
-        assertThat(pearsonsCorrelationCoefficientByAddAllPartitionedPairedStats)
-            .named("Pearson's correlation coefficient by addAll(PairedStats) of " + values)
+        assertWithMessage("Pearson's correlation coefficient by addAll(PairedStats) of " + values)
+            .that(pearsonsCorrelationCoefficientByAddAllPartitionedPairedStats)
             .isWithin(ALLOWED_ERROR)
             .of(
                 accumulatorByAddAllPartitionedPairedStats.populationCovariance()
diff --git a/android/guava-tests/test/com/google/common/math/PairedStatsTest.java b/android/guava-tests/test/com/google/common/math/PairedStatsTest.java
index f427ae6..7dd9e94 100644
--- a/android/guava-tests/test/com/google/common/math/PairedStatsTest.java
+++ b/android/guava-tests/test/com/google/common/math/PairedStatsTest.java
@@ -47,6 +47,7 @@
 import static com.google.common.math.StatsTesting.assertVerticalLinearTransformation;
 import static com.google.common.math.StatsTesting.createPairedStatsOf;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.math.StatsTesting.ManyValues;
@@ -104,10 +105,10 @@
       PairedStats stats = createPairedStatsOf(values.asIterable(), OTHER_MANY_VALUES);
       double populationCovariance = stats.populationCovariance();
       if (values.hasAnyNonFinite()) {
-        assertThat(populationCovariance).named("population covariance of " + values).isNaN();
+        assertWithMessage("population covariance of " + values).that(populationCovariance).isNaN();
       } else {
-        assertThat(populationCovariance)
-            .named("population covariance of " + values)
+        assertWithMessage("population covariance of " + values)
+            .that(populationCovariance)
             .isWithin(ALLOWED_ERROR)
             .of(MANY_VALUES_SUM_OF_PRODUCTS_OF_DELTAS / MANY_VALUES_COUNT);
       }
@@ -169,12 +170,12 @@
       PairedStats stats = createPairedStatsOf(MANY_VALUES, values.asIterable());
       double pearsonsCorrelationCoefficient = stats.pearsonsCorrelationCoefficient();
       if (values.hasAnyNonFinite()) {
-        assertThat(pearsonsCorrelationCoefficient)
-            .named("Pearson's correlation coefficient of " + values)
+        assertWithMessage("Pearson's correlation coefficient of " + values)
+            .that(pearsonsCorrelationCoefficient)
             .isNaN();
       } else {
-        assertThat(pearsonsCorrelationCoefficient)
-            .named("Pearson's correlation coefficient of " + values)
+        assertWithMessage("Pearson's correlation coefficient of " + values)
+            .that(pearsonsCorrelationCoefficient)
             .isWithin(ALLOWED_ERROR)
             .of(
                 stats.populationCovariance()
diff --git a/android/guava-tests/test/com/google/common/math/QuantilesTest.java b/android/guava-tests/test/com/google/common/math/QuantilesTest.java
index bdd3521..5ac5f6e 100644
--- a/android/guava-tests/test/com/google/common/math/QuantilesTest.java
+++ b/android/guava-tests/test/com/google/common/math/QuantilesTest.java
@@ -20,6 +20,7 @@
 import static com.google.common.math.Quantiles.percentiles;
 import static com.google.common.math.Quantiles.quartiles;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static java.lang.Double.NEGATIVE_INFINITY;
 import static java.lang.Double.NaN;
 import static java.lang.Double.POSITIVE_INFINITY;
@@ -35,6 +36,7 @@
 import com.google.common.primitives.Ints;
 import com.google.common.primitives.Longs;
 import com.google.common.truth.Correspondence;
+import com.google.common.truth.Correspondence.BinaryPredicate;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -87,20 +89,17 @@
    * each other or identical non-finite values.
    */
   private static final Correspondence<Double, Double> QUANTILE_CORRESPONDENCE =
-      new Correspondence<Double, Double>() {
-
-        @Override
-        public boolean compare(@NullableDecl Double actual, @NullableDecl Double expected) {
-          // Test for equality to allow non-finite values to match; otherwise, use the finite test.
-          return actual.equals(expected)
-              || FINITE_QUANTILE_CORRESPONDENCE.compare(actual, expected);
-        }
-
-        @Override
-        public String toString() {
-          return "is identical to or " + FINITE_QUANTILE_CORRESPONDENCE;
-        }
-      };
+      Correspondence.from(
+          new BinaryPredicate<Double, Double>() {
+            @Override
+            public boolean apply(@NullableDecl Double actual, @NullableDecl Double expected) {
+              // Test for equality to allow non-finite values to match; otherwise, use the finite
+              // test.
+              return actual.equals(expected)
+                  || FINITE_QUANTILE_CORRESPONDENCE.compare(actual, expected);
+            }
+          },
+          "is identical to or " + FINITE_QUANTILE_CORRESPONDENCE);
 
   // 1. Tests on a hardcoded dataset for chains starting with median(), quartiles(), and scale(10):
 
@@ -291,6 +290,18 @@
             8, SIXTEEN_SQUARES_DECILE_8);
   }
 
+  public void testScale_indexes_varargs_compute_indexOrderIsMaintained() {
+    assertThat(Quantiles.scale(10).indexes(0, 10, 5, 1, 8, 1).compute(SIXTEEN_SQUARES_INTEGERS))
+        .comparingValuesUsing(QUANTILE_CORRESPONDENCE)
+        .containsExactly(
+            0, SIXTEEN_SQUARES_MIN,
+            10, SIXTEEN_SQUARES_MAX,
+            5, SIXTEEN_SQUARES_MEDIAN,
+            1, SIXTEEN_SQUARES_DECILE_1,
+            8, SIXTEEN_SQUARES_DECILE_8)
+        .inOrder();
+  }
+
   public void testScale_indexes_varargs_compute_doubleVarargs() {
     double[] dataset = Doubles.toArray(SIXTEEN_SQUARES_DOUBLES);
     assertThat(Quantiles.scale(10).indexes(0, 10, 5, 1, 8, 1).compute(dataset))
@@ -509,8 +520,8 @@
 
   public void testPercentiles_index_compute_doubleCollection() {
     for (int index = 0; index <= 100; index++) {
-      assertThat(percentiles().index(index).compute(PSEUDORANDOM_DATASET))
-          .named("quantile at index " + index)
+      assertWithMessage("quantile at index " + index)
+          .that(percentiles().index(index).compute(PSEUDORANDOM_DATASET))
           .isWithin(ALLOWED_ERROR)
           .of(expectedLargeDatasetPercentile(index));
     }
@@ -521,8 +532,8 @@
     // Assert that the computation gives the correct result for all possible percentiles.
     for (int index = 0; index <= 100; index++) {
       double[] dataset = Doubles.toArray(PSEUDORANDOM_DATASET);
-      assertThat(percentiles().index(index).computeInPlace(dataset))
-          .named("quantile at index " + index)
+      assertWithMessage("quantile at index " + index)
+          .that(percentiles().index(index).computeInPlace(dataset))
           .isWithin(ALLOWED_ERROR)
           .of(expectedLargeDatasetPercentile(index));
     }
@@ -745,4 +756,13 @@
     } catch (IllegalArgumentException expected) {
     }
   }
+
+  public void testScale_indexes_indexes_computeInPlace_empty() {
+    int[] emptyIndexes = {};
+    try {
+      Quantiles.ScaleAndIndexes unused = Quantiles.scale(10).indexes(emptyIndexes);
+      fail("Expected IllegalArgumentException");
+    } catch (IllegalArgumentException expected) {
+    }
+  }
 }
diff --git a/android/guava-tests/test/com/google/common/math/StatsAccumulatorTest.java b/android/guava-tests/test/com/google/common/math/StatsAccumulatorTest.java
index a38c803..6926a69 100644
--- a/android/guava-tests/test/com/google/common/math/StatsAccumulatorTest.java
+++ b/android/guava-tests/test/com/google/common/math/StatsAccumulatorTest.java
@@ -44,6 +44,7 @@
 import static com.google.common.math.StatsTesting.TWO_VALUES_MIN;
 import static com.google.common.math.StatsTesting.TWO_VALUES_SUM_OF_SQUARES_OF_DELTAS;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static java.lang.Math.sqrt;
 
 import com.google.common.collect.ImmutableList;
@@ -75,6 +76,7 @@
   private StatsAccumulator manyValuesAccumulatorByRepeatedAdd;
   private StatsAccumulator manyValuesAccumulatorByAddAndAddAll;
   private StatsAccumulator manyValuesAccumulatorByAddAllStats;
+  private StatsAccumulator manyValuesAccumulatorByAddAllStatsAccumulator;
   private StatsAccumulator integerManyValuesAccumulatorByAddAllIterable;
   private StatsAccumulator longManyValuesAccumulatorByAddAllIterator;
   private StatsAccumulator longManyValuesAccumulatorByAddAllVarargs;
@@ -129,6 +131,12 @@
     manyValuesAccumulatorByAddAllStats.addAll(
         Stats.of(MANY_VALUES.subList(MANY_VALUES.size() / 2, MANY_VALUES.size())));
 
+    manyValuesAccumulatorByAddAllStatsAccumulator = new StatsAccumulator();
+    manyValuesAccumulatorByAddAllStatsAccumulator.addAll(
+        statsAccumulatorOf(MANY_VALUES.subList(0, MANY_VALUES.size() / 2)));
+    manyValuesAccumulatorByAddAllStatsAccumulator.addAll(
+        statsAccumulatorOf(MANY_VALUES.subList(MANY_VALUES.size() / 2, MANY_VALUES.size())));
+
     integerManyValuesAccumulatorByAddAllIterable = new StatsAccumulator();
     integerManyValuesAccumulatorByAddAllIterable.addAll(INTEGER_MANY_VALUES);
 
@@ -139,6 +147,12 @@
     longManyValuesAccumulatorByAddAllVarargs.addAll(Longs.toArray(LONG_MANY_VALUES));
   }
 
+  private static StatsAccumulator statsAccumulatorOf(Iterable<? extends Number> values) {
+    StatsAccumulator accumulator = new StatsAccumulator();
+    accumulator.addAll(values);
+    return accumulator;
+  }
+
   public void testCount() {
     assertThat(emptyAccumulator.count()).isEqualTo(0);
     assertThat(emptyAccumulatorByAddAllEmptyIterable.count()).isEqualTo(0);
@@ -153,6 +167,7 @@
     assertThat(manyValuesAccumulatorByRepeatedAdd.count()).isEqualTo(MANY_VALUES_COUNT);
     assertThat(manyValuesAccumulatorByAddAndAddAll.count()).isEqualTo(MANY_VALUES_COUNT);
     assertThat(manyValuesAccumulatorByAddAllStats.count()).isEqualTo(MANY_VALUES_COUNT);
+    assertThat(manyValuesAccumulatorByAddAllStatsAccumulator.count()).isEqualTo(MANY_VALUES_COUNT);
     assertThat(integerManyValuesAccumulatorByAddAllIterable.count())
         .isEqualTo(StatsTesting.INTEGER_MANY_VALUES_COUNT);
     assertThat(longManyValuesAccumulatorByAddAllIterator.count())
@@ -212,6 +227,9 @@
     assertThat(manyValuesAccumulatorByAddAllStats.mean())
         .isWithin(ALLOWED_ERROR)
         .of(MANY_VALUES_MEAN);
+    assertThat(manyValuesAccumulatorByAddAllStatsAccumulator.mean())
+        .isWithin(ALLOWED_ERROR)
+        .of(MANY_VALUES_MEAN);
     // For datasets of many double values created from an iterable, we test many combinations of
     // finite and non-finite values:
     for (ManyValues values : ALL_MANY_VALUES) {
@@ -224,37 +242,40 @@
       double mean = accumulator.mean();
       double meanByAddAllStats = accumulatorByAddAllStats.mean();
       if (values.hasAnyNaN()) {
-        assertThat(mean).named("mean of " + values).isNaN();
-        assertThat(meanByAddAllStats).named("mean by addAll(Stats) of " + values).isNaN();
+        assertWithMessage("mean of " + values).that(mean).isNaN();
+        assertWithMessage("mean by addAll(Stats) of " + values).that(meanByAddAllStats).isNaN();
       } else if (values.hasAnyPositiveInfinity() && values.hasAnyNegativeInfinity()) {
-        assertThat(mean).named("mean of " + values).isNaN();
-        assertThat(meanByAddAllStats).named("mean by addAll(Stats) of " + values).isNaN();
+        assertWithMessage("mean of " + values).that(mean).isNaN();
+        assertWithMessage("mean by addAll(Stats) of " + values).that(meanByAddAllStats).isNaN();
       } else if (values.hasAnyPositiveInfinity()) {
-        assertThat(mean).named("mean of " + values).isPositiveInfinity();
-        assertThat(meanByAddAllStats)
-            .named("mean by addAll(Stats) of " + values)
+        assertWithMessage("mean of " + values).that(mean).isPositiveInfinity();
+        assertWithMessage("mean by addAll(Stats) of " + values)
+            .that(meanByAddAllStats)
             .isPositiveInfinity();
       } else if (values.hasAnyNegativeInfinity()) {
-        assertThat(mean).named("mean of " + values).isNegativeInfinity();
-        assertThat(meanByAddAllStats)
-            .named("mean by addAll(Stats) of " + values)
+        assertWithMessage("mean of " + values).that(mean).isNegativeInfinity();
+        assertWithMessage("mean by addAll(Stats) of " + values)
+            .that(meanByAddAllStats)
             .isNegativeInfinity();
       } else {
-        assertThat(mean).named("mean of " + values).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MEAN);
-        assertThat(meanByAddAllStats)
-            .named("mean by addAll(Stats) of " + values)
+        assertWithMessage("mean of " + values)
+            .that(mean)
+            .isWithin(ALLOWED_ERROR)
+            .of(MANY_VALUES_MEAN);
+        assertWithMessage("mean by addAll(Stats) of " + values)
+            .that(meanByAddAllStats)
             .isWithin(ALLOWED_ERROR)
             .of(MANY_VALUES_MEAN);
       }
     }
     assertThat(integerManyValuesAccumulatorByAddAllIterable.mean())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * INTEGER_MANY_VALUES_MEAN)
         .of(INTEGER_MANY_VALUES_MEAN);
     assertThat(longManyValuesAccumulatorByAddAllIterator.mean())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_MEAN)
         .of(LONG_MANY_VALUES_MEAN);
     assertThat(longManyValuesAccumulatorByAddAllVarargs.mean())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_MEAN)
         .of(LONG_MANY_VALUES_MEAN);
   }
 
@@ -286,14 +307,17 @@
     assertThat(manyValuesAccumulatorByAddAllStats.sum())
         .isWithin(ALLOWED_ERROR)
         .of(MANY_VALUES_MEAN * MANY_VALUES_COUNT);
-    assertThat(integerManyValuesAccumulatorByAddAllIterable.sum())
+    assertThat(manyValuesAccumulatorByAddAllStatsAccumulator.sum())
         .isWithin(ALLOWED_ERROR)
+        .of(MANY_VALUES_MEAN * MANY_VALUES_COUNT);
+    assertThat(integerManyValuesAccumulatorByAddAllIterable.sum())
+        .isWithin(ALLOWED_ERROR * INTEGER_MANY_VALUES_MEAN)
         .of(INTEGER_MANY_VALUES_MEAN * INTEGER_MANY_VALUES_COUNT);
     assertThat(longManyValuesAccumulatorByAddAllIterator.sum())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_MEAN)
         .of(LONG_MANY_VALUES_MEAN * LONG_MANY_VALUES_COUNT);
     assertThat(longManyValuesAccumulatorByAddAllVarargs.sum())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_MEAN)
         .of(LONG_MANY_VALUES_MEAN * LONG_MANY_VALUES_COUNT);
   }
 
@@ -339,6 +363,9 @@
     assertThat(manyValuesAccumulatorByAddAllStats.populationVariance())
         .isWithin(ALLOWED_ERROR)
         .of(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / MANY_VALUES_COUNT);
+    assertThat(manyValuesAccumulatorByAddAllStatsAccumulator.populationVariance())
+        .isWithin(ALLOWED_ERROR)
+        .of(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / MANY_VALUES_COUNT);
     // For datasets of many double values created from an iterator, we test many combinations of
     // finite and non-finite values:
     for (ManyValues values : ALL_MANY_VALUES) {
@@ -351,29 +378,29 @@
       double populationVariance = accumulator.populationVariance();
       double populationVarianceByAddAllStats = accumulatorByAddAllStats.populationVariance();
       if (values.hasAnyNonFinite()) {
-        assertThat(populationVariance).named("population variance of " + values).isNaN();
-        assertThat(populationVarianceByAddAllStats)
-            .named("population variance by addAll(Stats) of " + values)
+        assertWithMessage("population variance of " + values).that(populationVariance).isNaN();
+        assertWithMessage("population variance by addAll(Stats) of " + values)
+            .that(populationVarianceByAddAllStats)
             .isNaN();
       } else {
-        assertThat(populationVariance)
-            .named("population variance of " + values)
+        assertWithMessage("population variance of " + values)
+            .that(populationVariance)
             .isWithin(ALLOWED_ERROR)
             .of(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / MANY_VALUES_COUNT);
-        assertThat(populationVarianceByAddAllStats)
-            .named("population variance by addAll(Stats) of " + values)
+        assertWithMessage("population variance by addAll(Stats) of " + values)
+            .that(populationVarianceByAddAllStats)
             .isWithin(ALLOWED_ERROR)
             .of(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / MANY_VALUES_COUNT);
       }
     }
     assertThat(integerManyValuesAccumulatorByAddAllIterable.populationVariance())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / INTEGER_MANY_VALUES_COUNT);
     assertThat(longManyValuesAccumulatorByAddAllIterator.populationVariance())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / LONG_MANY_VALUES_COUNT);
     assertThat(longManyValuesAccumulatorByAddAllVarargs.populationVariance())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / LONG_MANY_VALUES_COUNT);
   }
 
@@ -421,14 +448,17 @@
     assertThat(manyValuesAccumulatorByAddAllStats.populationStandardDeviation())
         .isWithin(ALLOWED_ERROR)
         .of(sqrt(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / MANY_VALUES_COUNT));
-    assertThat(integerManyValuesAccumulatorByAddAllIterable.populationStandardDeviation())
+    assertThat(manyValuesAccumulatorByAddAllStatsAccumulator.populationStandardDeviation())
         .isWithin(ALLOWED_ERROR)
+        .of(sqrt(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / MANY_VALUES_COUNT));
+    assertThat(integerManyValuesAccumulatorByAddAllIterable.populationStandardDeviation())
+        .isWithin(ALLOWED_ERROR * sqrt(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS))
         .of(sqrt(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / INTEGER_MANY_VALUES_COUNT));
     assertThat(longManyValuesAccumulatorByAddAllIterator.populationStandardDeviation())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * sqrt(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS))
         .of(sqrt(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / LONG_MANY_VALUES_COUNT));
     assertThat(longManyValuesAccumulatorByAddAllVarargs.populationStandardDeviation())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * sqrt(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS))
         .of(sqrt(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / LONG_MANY_VALUES_COUNT));
   }
 
@@ -482,14 +512,17 @@
     assertThat(manyValuesAccumulatorByAddAllStats.sampleVariance())
         .isWithin(ALLOWED_ERROR)
         .of(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (MANY_VALUES_COUNT - 1));
-    assertThat(integerManyValuesAccumulatorByAddAllIterable.sampleVariance())
+    assertThat(manyValuesAccumulatorByAddAllStatsAccumulator.sampleVariance())
         .isWithin(ALLOWED_ERROR)
+        .of(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (MANY_VALUES_COUNT - 1));
+    assertThat(integerManyValuesAccumulatorByAddAllIterable.sampleVariance())
+        .isWithin(ALLOWED_ERROR * INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (INTEGER_MANY_VALUES_COUNT - 1));
     assertThat(longManyValuesAccumulatorByAddAllIterator.sampleVariance())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (LONG_MANY_VALUES_COUNT - 1));
     assertThat(longManyValuesAccumulatorByAddAllVarargs.sampleVariance())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (LONG_MANY_VALUES_COUNT - 1));
   }
 
@@ -543,14 +576,17 @@
     assertThat(manyValuesAccumulatorByAddAllStats.sampleStandardDeviation())
         .isWithin(ALLOWED_ERROR)
         .of(sqrt(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (MANY_VALUES_COUNT - 1)));
-    assertThat(integerManyValuesAccumulatorByAddAllIterable.sampleStandardDeviation())
+    assertThat(manyValuesAccumulatorByAddAllStatsAccumulator.sampleStandardDeviation())
         .isWithin(ALLOWED_ERROR)
+        .of(sqrt(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (MANY_VALUES_COUNT - 1)));
+    assertThat(integerManyValuesAccumulatorByAddAllIterable.sampleStandardDeviation())
+        .isWithin(ALLOWED_ERROR * sqrt(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS))
         .of(sqrt(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (INTEGER_MANY_VALUES_COUNT - 1)));
     assertThat(longManyValuesAccumulatorByAddAllIterator.sampleStandardDeviation())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(sqrt(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (LONG_MANY_VALUES_COUNT - 1)));
     assertThat(longManyValuesAccumulatorByAddAllVarargs.sampleStandardDeviation())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(sqrt(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (LONG_MANY_VALUES_COUNT - 1)));
   }
 
@@ -570,28 +606,17 @@
       fail("Expected IllegalStateException");
     } catch (IllegalStateException expected) {
     }
-    assertThat(oneValueAccumulator.max()).isWithin(ALLOWED_ERROR).of(ONE_VALUE);
-    assertThat(oneValueAccumulatorByAddAllEmptyStats.max()).isWithin(ALLOWED_ERROR).of(ONE_VALUE);
-    assertThat(twoValuesAccumulator.max()).isWithin(ALLOWED_ERROR).of(TWO_VALUES_MAX);
-    assertThat(twoValuesAccumulatorByAddAllStats.max()).isWithin(ALLOWED_ERROR).of(TWO_VALUES_MAX);
-    assertThat(manyValuesAccumulatorByAddAllIterable.max())
-        .isWithin(ALLOWED_ERROR)
-        .of(MANY_VALUES_MAX);
-    assertThat(manyValuesAccumulatorByAddAllIterator.max())
-        .isWithin(ALLOWED_ERROR)
-        .of(MANY_VALUES_MAX);
-    assertThat(manyValuesAccumulatorByAddAllVarargs.max())
-        .isWithin(ALLOWED_ERROR)
-        .of(MANY_VALUES_MAX);
-    assertThat(manyValuesAccumulatorByRepeatedAdd.max())
-        .isWithin(ALLOWED_ERROR)
-        .of(MANY_VALUES_MAX);
-    assertThat(manyValuesAccumulatorByAddAndAddAll.max())
-        .isWithin(ALLOWED_ERROR)
-        .of(MANY_VALUES_MAX);
-    assertThat(manyValuesAccumulatorByAddAllStats.max())
-        .isWithin(ALLOWED_ERROR)
-        .of(MANY_VALUES_MAX);
+    assertThat(oneValueAccumulator.max()).isEqualTo(ONE_VALUE);
+    assertThat(oneValueAccumulatorByAddAllEmptyStats.max()).isEqualTo(ONE_VALUE);
+    assertThat(twoValuesAccumulator.max()).isEqualTo(TWO_VALUES_MAX);
+    assertThat(twoValuesAccumulatorByAddAllStats.max()).isEqualTo(TWO_VALUES_MAX);
+    assertThat(manyValuesAccumulatorByAddAllIterable.max()).isEqualTo(MANY_VALUES_MAX);
+    assertThat(manyValuesAccumulatorByAddAllIterator.max()).isEqualTo(MANY_VALUES_MAX);
+    assertThat(manyValuesAccumulatorByAddAllVarargs.max()).isEqualTo(MANY_VALUES_MAX);
+    assertThat(manyValuesAccumulatorByRepeatedAdd.max()).isEqualTo(MANY_VALUES_MAX);
+    assertThat(manyValuesAccumulatorByAddAndAddAll.max()).isEqualTo(MANY_VALUES_MAX);
+    assertThat(manyValuesAccumulatorByAddAllStats.max()).isEqualTo(MANY_VALUES_MAX);
+    assertThat(manyValuesAccumulatorByAddAllStatsAccumulator.max()).isEqualTo(MANY_VALUES_MAX);
     // For datasets of many double values created from an array, we test many combinations of
     // finite and non-finite values:
     for (ManyValues values : ALL_MANY_VALUES) {
@@ -604,30 +629,24 @@
       double max = accumulator.max();
       double maxByAddAllStats = accumulatorByAddAllStats.max();
       if (values.hasAnyNaN()) {
-        assertThat(max).named("max of " + values).isNaN();
-        assertThat(maxByAddAllStats).named("max by addAll(Stats) of " + values).isNaN();
+        assertWithMessage("max of " + values).that(max).isNaN();
+        assertWithMessage("max by addAll(Stats) of " + values).that(maxByAddAllStats).isNaN();
       } else if (values.hasAnyPositiveInfinity()) {
-        assertThat(max).named("max of " + values).isPositiveInfinity();
-        assertThat(maxByAddAllStats)
-            .named("max by addAll(Stats) of " + values)
+        assertWithMessage("max of " + values).that(max).isPositiveInfinity();
+        assertWithMessage("max by addAll(Stats) of " + values)
+            .that(maxByAddAllStats)
             .isPositiveInfinity();
       } else {
-        assertThat(max).named("max of " + values).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MAX);
-        assertThat(maxByAddAllStats)
-            .named("max by addAll(Stats) of " + values)
-            .isWithin(ALLOWED_ERROR)
-            .of(MANY_VALUES_MAX);
+        assertWithMessage("max of " + values).that(max).isEqualTo(MANY_VALUES_MAX);
+        assertWithMessage("max by addAll(Stats) of " + values)
+            .that(maxByAddAllStats)
+            .isEqualTo(MANY_VALUES_MAX);
       }
     }
     assertThat(integerManyValuesAccumulatorByAddAllIterable.max())
-        .isWithin(ALLOWED_ERROR)
-        .of(INTEGER_MANY_VALUES_MAX);
-    assertThat(longManyValuesAccumulatorByAddAllIterator.max())
-        .isWithin(ALLOWED_ERROR)
-        .of(LONG_MANY_VALUES_MAX);
-    assertThat(longManyValuesAccumulatorByAddAllVarargs.max())
-        .isWithin(ALLOWED_ERROR)
-        .of(LONG_MANY_VALUES_MAX);
+        .isEqualTo(INTEGER_MANY_VALUES_MAX);
+    assertThat(longManyValuesAccumulatorByAddAllIterator.max()).isEqualTo(LONG_MANY_VALUES_MAX);
+    assertThat(longManyValuesAccumulatorByAddAllVarargs.max()).isEqualTo(LONG_MANY_VALUES_MAX);
   }
 
   public void testMin() {
@@ -646,28 +665,17 @@
       fail("Expected IllegalStateException");
     } catch (IllegalStateException expected) {
     }
-    assertThat(oneValueAccumulator.min()).isWithin(ALLOWED_ERROR).of(ONE_VALUE);
-    assertThat(oneValueAccumulatorByAddAllEmptyStats.min()).isWithin(ALLOWED_ERROR).of(ONE_VALUE);
-    assertThat(twoValuesAccumulator.min()).isWithin(ALLOWED_ERROR).of(TWO_VALUES_MIN);
-    assertThat(twoValuesAccumulatorByAddAllStats.min()).isWithin(ALLOWED_ERROR).of(TWO_VALUES_MIN);
-    assertThat(manyValuesAccumulatorByAddAllIterable.min())
-        .isWithin(ALLOWED_ERROR)
-        .of(MANY_VALUES_MIN);
-    assertThat(manyValuesAccumulatorByAddAllIterator.min())
-        .isWithin(ALLOWED_ERROR)
-        .of(MANY_VALUES_MIN);
-    assertThat(manyValuesAccumulatorByAddAllVarargs.min())
-        .isWithin(ALLOWED_ERROR)
-        .of(MANY_VALUES_MIN);
-    assertThat(manyValuesAccumulatorByRepeatedAdd.min())
-        .isWithin(ALLOWED_ERROR)
-        .of(MANY_VALUES_MIN);
-    assertThat(manyValuesAccumulatorByAddAndAddAll.min())
-        .isWithin(ALLOWED_ERROR)
-        .of(MANY_VALUES_MIN);
-    assertThat(manyValuesAccumulatorByAddAllStats.min())
-        .isWithin(ALLOWED_ERROR)
-        .of(MANY_VALUES_MIN);
+    assertThat(oneValueAccumulator.min()).isEqualTo(ONE_VALUE);
+    assertThat(oneValueAccumulatorByAddAllEmptyStats.min()).isEqualTo(ONE_VALUE);
+    assertThat(twoValuesAccumulator.min()).isEqualTo(TWO_VALUES_MIN);
+    assertThat(twoValuesAccumulatorByAddAllStats.min()).isEqualTo(TWO_VALUES_MIN);
+    assertThat(manyValuesAccumulatorByAddAllIterable.min()).isEqualTo(MANY_VALUES_MIN);
+    assertThat(manyValuesAccumulatorByAddAllIterator.min()).isEqualTo(MANY_VALUES_MIN);
+    assertThat(manyValuesAccumulatorByAddAllVarargs.min()).isEqualTo(MANY_VALUES_MIN);
+    assertThat(manyValuesAccumulatorByRepeatedAdd.min()).isEqualTo(MANY_VALUES_MIN);
+    assertThat(manyValuesAccumulatorByAddAndAddAll.min()).isEqualTo(MANY_VALUES_MIN);
+    assertThat(manyValuesAccumulatorByAddAllStats.min()).isEqualTo(MANY_VALUES_MIN);
+    assertThat(manyValuesAccumulatorByAddAllStatsAccumulator.min()).isEqualTo(MANY_VALUES_MIN);
     // For datasets of many double values created by adding elements individually, we test many
     // combinations of finite and non-finite values:
     for (ManyValues values : ALL_MANY_VALUES) {
@@ -680,29 +688,23 @@
       double min = accumulator.min();
       double minByAddAllStats = accumulatorByAddAllStats.min();
       if (values.hasAnyNaN()) {
-        assertThat(min).named("min of " + values).isNaN();
-        assertThat(minByAddAllStats).named("min by addAll(Stats) of " + values).isNaN();
+        assertWithMessage("min of " + values).that(min).isNaN();
+        assertWithMessage("min by addAll(Stats) of " + values).that(minByAddAllStats).isNaN();
       } else if (values.hasAnyNegativeInfinity()) {
-        assertThat(min).named("min of " + values).isNegativeInfinity();
-        assertThat(minByAddAllStats)
-            .named("min by addAll(Stats) of " + values)
+        assertWithMessage("min of " + values).that(min).isNegativeInfinity();
+        assertWithMessage("min by addAll(Stats) of " + values)
+            .that(minByAddAllStats)
             .isNegativeInfinity();
       } else {
-        assertThat(min).named("min of " + values).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MIN);
-        assertThat(minByAddAllStats)
-            .named("min by addAll(Stats) of " + values)
-            .isWithin(ALLOWED_ERROR)
-            .of(MANY_VALUES_MIN);
+        assertWithMessage("min of " + values).that(min).isEqualTo(MANY_VALUES_MIN);
+        assertWithMessage("min by addAll(Stats) of " + values)
+            .that(minByAddAllStats)
+            .isEqualTo(MANY_VALUES_MIN);
       }
     }
     assertThat(integerManyValuesAccumulatorByAddAllIterable.min())
-        .isWithin(ALLOWED_ERROR)
-        .of(INTEGER_MANY_VALUES_MIN);
-    assertThat(longManyValuesAccumulatorByAddAllIterator.min())
-        .isWithin(ALLOWED_ERROR)
-        .of(LONG_MANY_VALUES_MIN);
-    assertThat(longManyValuesAccumulatorByAddAllVarargs.min())
-        .isWithin(ALLOWED_ERROR)
-        .of(LONG_MANY_VALUES_MIN);
+        .isEqualTo(INTEGER_MANY_VALUES_MIN);
+    assertThat(longManyValuesAccumulatorByAddAllIterator.min()).isEqualTo(LONG_MANY_VALUES_MIN);
+    assertThat(longManyValuesAccumulatorByAddAllVarargs.min()).isEqualTo(LONG_MANY_VALUES_MIN);
   }
 }
diff --git a/android/guava-tests/test/com/google/common/math/StatsTest.java b/android/guava-tests/test/com/google/common/math/StatsTest.java
index 2e9e540..76de5b5 100644
--- a/android/guava-tests/test/com/google/common/math/StatsTest.java
+++ b/android/guava-tests/test/com/google/common/math/StatsTest.java
@@ -64,6 +64,7 @@
 import static com.google.common.math.StatsTesting.TWO_VALUES_STATS;
 import static com.google.common.math.StatsTesting.TWO_VALUES_SUM_OF_SQUARES_OF_DELTAS;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static java.lang.Double.NEGATIVE_INFINITY;
 import static java.lang.Double.NaN;
 import static java.lang.Double.POSITIVE_INFINITY;
@@ -123,15 +124,18 @@
     for (ManyValues values : ALL_MANY_VALUES) {
       double mean = Stats.of(values.asArray()).mean();
       if (values.hasAnyNaN()) {
-        assertThat(mean).named("mean of " + values).isNaN();
+        assertWithMessage("mean of " + values).that(mean).isNaN();
       } else if (values.hasAnyPositiveInfinity() && values.hasAnyNegativeInfinity()) {
-        assertThat(mean).named("mean of " + values).isNaN();
+        assertWithMessage("mean of " + values).that(mean).isNaN();
       } else if (values.hasAnyPositiveInfinity()) {
-        assertThat(mean).named("mean of " + values).isPositiveInfinity();
+        assertWithMessage("mean of " + values).that(mean).isPositiveInfinity();
       } else if (values.hasAnyNegativeInfinity()) {
-        assertThat(mean).named("mean of " + values).isNegativeInfinity();
+        assertWithMessage("mean of " + values).that(mean).isNegativeInfinity();
       } else {
-        assertThat(mean).named("mean of " + values).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MEAN);
+        assertWithMessage("mean of " + values)
+            .that(mean)
+            .isWithin(ALLOWED_ERROR)
+            .of(MANY_VALUES_MEAN);
       }
     }
     assertThat(MANY_VALUES_STATS_ITERABLE.mean()).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MEAN);
@@ -141,19 +145,19 @@
         .isWithin(ALLOWED_ERROR * Double.MAX_VALUE)
         .of(LARGE_VALUES_MEAN);
     assertThat(INTEGER_MANY_VALUES_STATS_VARARGS.mean())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * INTEGER_MANY_VALUES_MEAN)
         .of(INTEGER_MANY_VALUES_MEAN);
     assertThat(INTEGER_MANY_VALUES_STATS_ITERABLE.mean())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * INTEGER_MANY_VALUES_MEAN)
         .of(INTEGER_MANY_VALUES_MEAN);
     assertThat(LARGE_INTEGER_VALUES_STATS.mean())
         .isWithin(ALLOWED_ERROR * Integer.MAX_VALUE)
         .of(LARGE_INTEGER_VALUES_MEAN);
     assertThat(LONG_MANY_VALUES_STATS_ITERATOR.mean())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_MEAN)
         .of(LONG_MANY_VALUES_MEAN);
     assertThat(LONG_MANY_VALUES_STATS_SNAPSHOT.mean())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_MEAN)
         .of(LONG_MANY_VALUES_MEAN);
     assertThat(LARGE_LONG_VALUES_STATS.mean())
         .isWithin(ALLOWED_ERROR * Long.MAX_VALUE)
@@ -178,16 +182,16 @@
         .isWithin(ALLOWED_ERROR)
         .of(MANY_VALUES_MEAN * MANY_VALUES_COUNT);
     assertThat(INTEGER_MANY_VALUES_STATS_VARARGS.sum())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * INTEGER_MANY_VALUES_MEAN)
         .of(INTEGER_MANY_VALUES_MEAN * INTEGER_MANY_VALUES_COUNT);
     assertThat(INTEGER_MANY_VALUES_STATS_ITERABLE.sum())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * INTEGER_MANY_VALUES_MEAN)
         .of(INTEGER_MANY_VALUES_MEAN * INTEGER_MANY_VALUES_COUNT);
     assertThat(LONG_MANY_VALUES_STATS_ITERATOR.sum())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_MEAN)
         .of(LONG_MANY_VALUES_MEAN * LONG_MANY_VALUES_COUNT);
     assertThat(LONG_MANY_VALUES_STATS_SNAPSHOT.sum())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_MEAN)
         .of(LONG_MANY_VALUES_MEAN * LONG_MANY_VALUES_COUNT);
   }
 
@@ -217,10 +221,10 @@
     for (ManyValues values : ALL_MANY_VALUES) {
       double populationVariance = Stats.of(values.asIterable()).populationVariance();
       if (values.hasAnyNonFinite()) {
-        assertThat(populationVariance).named("population variance of " + values).isNaN();
+        assertWithMessage("population variance of " + values).that(populationVariance).isNaN();
       } else {
-        assertThat(populationVariance)
-            .named("population variance of " + values)
+        assertWithMessage("population variance of " + values)
+            .that(populationVariance)
             .isWithin(ALLOWED_ERROR)
             .of(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / MANY_VALUES_COUNT);
       }
@@ -232,19 +236,19 @@
         .isWithin(ALLOWED_ERROR)
         .of(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / MANY_VALUES_COUNT);
     assertThat(INTEGER_MANY_VALUES_STATS_VARARGS.populationVariance())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / INTEGER_MANY_VALUES_COUNT);
     assertThat(INTEGER_MANY_VALUES_STATS_ITERABLE.populationVariance())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / INTEGER_MANY_VALUES_COUNT);
     assertThat(LARGE_INTEGER_VALUES_STATS.populationVariance())
         .isWithin(ALLOWED_ERROR * Integer.MAX_VALUE * Integer.MAX_VALUE)
         .of(LARGE_INTEGER_VALUES_POPULATION_VARIANCE);
     assertThat(LONG_MANY_VALUES_STATS_ITERATOR.populationVariance())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / LONG_MANY_VALUES_COUNT);
     assertThat(LONG_MANY_VALUES_STATS_SNAPSHOT.populationVariance())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / LONG_MANY_VALUES_COUNT);
     assertThat(LARGE_LONG_VALUES_STATS.populationVariance())
         .isWithin(ALLOWED_ERROR * Long.MAX_VALUE * Long.MAX_VALUE)
@@ -279,16 +283,16 @@
         .isWithin(ALLOWED_ERROR)
         .of(sqrt(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / MANY_VALUES_COUNT));
     assertThat(INTEGER_MANY_VALUES_STATS_VARARGS.populationStandardDeviation())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * sqrt(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS))
         .of(sqrt(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / INTEGER_MANY_VALUES_COUNT));
     assertThat(INTEGER_MANY_VALUES_STATS_ITERABLE.populationStandardDeviation())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * sqrt(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS))
         .of(sqrt(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / INTEGER_MANY_VALUES_COUNT));
     assertThat(LONG_MANY_VALUES_STATS_ITERATOR.populationStandardDeviation())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * sqrt(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS))
         .of(sqrt(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / LONG_MANY_VALUES_COUNT));
     assertThat(LONG_MANY_VALUES_STATS_SNAPSHOT.populationStandardDeviation())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * sqrt(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS))
         .of(sqrt(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / LONG_MANY_VALUES_COUNT));
   }
 
@@ -324,16 +328,16 @@
         .isWithin(ALLOWED_ERROR)
         .of(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (MANY_VALUES_COUNT - 1));
     assertThat(INTEGER_MANY_VALUES_STATS_VARARGS.sampleVariance())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (INTEGER_MANY_VALUES_COUNT - 1));
     assertThat(INTEGER_MANY_VALUES_STATS_ITERABLE.sampleVariance())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (INTEGER_MANY_VALUES_COUNT - 1));
     assertThat(LONG_MANY_VALUES_STATS_ITERATOR.sampleVariance())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (LONG_MANY_VALUES_COUNT - 1));
     assertThat(LONG_MANY_VALUES_STATS_SNAPSHOT.sampleVariance())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS)
         .of(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (LONG_MANY_VALUES_COUNT - 1));
   }
 
@@ -369,16 +373,16 @@
         .isWithin(ALLOWED_ERROR)
         .of(sqrt(MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (MANY_VALUES_COUNT - 1)));
     assertThat(INTEGER_MANY_VALUES_STATS_VARARGS.sampleStandardDeviation())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * sqrt(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS))
         .of(sqrt(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (INTEGER_MANY_VALUES_COUNT - 1)));
     assertThat(INTEGER_MANY_VALUES_STATS_ITERABLE.sampleStandardDeviation())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * sqrt(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS))
         .of(sqrt(INTEGER_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (INTEGER_MANY_VALUES_COUNT - 1)));
     assertThat(LONG_MANY_VALUES_STATS_ITERATOR.sampleStandardDeviation())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * sqrt(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS))
         .of(sqrt(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (LONG_MANY_VALUES_COUNT - 1)));
     assertThat(LONG_MANY_VALUES_STATS_SNAPSHOT.sampleStandardDeviation())
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * sqrt(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS))
         .of(sqrt(LONG_MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS / (LONG_MANY_VALUES_COUNT - 1)));
   }
 
@@ -393,38 +397,30 @@
       fail("Expected IllegalStateException");
     } catch (IllegalStateException expected) {
     }
-    assertThat(ONE_VALUE_STATS.max()).isWithin(ALLOWED_ERROR).of(ONE_VALUE);
+    assertThat(ONE_VALUE_STATS.max()).isEqualTo(ONE_VALUE);
     assertThat(Stats.of(POSITIVE_INFINITY).max()).isPositiveInfinity();
     assertThat(Stats.of(NEGATIVE_INFINITY).max()).isNegativeInfinity();
     assertThat(Stats.of(NaN).max()).isNaN();
-    assertThat(TWO_VALUES_STATS.max()).isWithin(ALLOWED_ERROR).of(TWO_VALUES_MAX);
-    assertThat(MANY_VALUES_STATS_VARARGS.max()).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MAX);
-    assertThat(MANY_VALUES_STATS_ITERABLE.max()).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MAX);
+    assertThat(TWO_VALUES_STATS.max()).isEqualTo(TWO_VALUES_MAX);
+    assertThat(MANY_VALUES_STATS_VARARGS.max()).isEqualTo(MANY_VALUES_MAX);
+    assertThat(MANY_VALUES_STATS_ITERABLE.max()).isEqualTo(MANY_VALUES_MAX);
     // For datasets of many double values created from an iterator, we test many combinations of
     // finite and non-finite values:
     for (ManyValues values : ALL_MANY_VALUES) {
       double max = Stats.of(values.asIterable().iterator()).max();
       if (values.hasAnyNaN()) {
-        assertThat(max).named("max of " + values).isNaN();
+        assertWithMessage("max of " + values).that(max).isNaN();
       } else if (values.hasAnyPositiveInfinity()) {
-        assertThat(max).named("max of " + values).isPositiveInfinity();
+        assertWithMessage("max of " + values).that(max).isPositiveInfinity();
       } else {
-        assertThat(max).named("max of " + values).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MAX);
+        assertWithMessage("max of " + values).that(max).isEqualTo(MANY_VALUES_MAX);
       }
     }
-    assertThat(MANY_VALUES_STATS_SNAPSHOT.max()).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MAX);
-    assertThat(INTEGER_MANY_VALUES_STATS_VARARGS.max())
-        .isWithin(ALLOWED_ERROR)
-        .of(INTEGER_MANY_VALUES_MAX);
-    assertThat(INTEGER_MANY_VALUES_STATS_ITERABLE.max())
-        .isWithin(ALLOWED_ERROR)
-        .of(INTEGER_MANY_VALUES_MAX);
-    assertThat(LONG_MANY_VALUES_STATS_ITERATOR.max())
-        .isWithin(ALLOWED_ERROR)
-        .of(LONG_MANY_VALUES_MAX);
-    assertThat(LONG_MANY_VALUES_STATS_SNAPSHOT.max())
-        .isWithin(ALLOWED_ERROR)
-        .of(LONG_MANY_VALUES_MAX);
+    assertThat(MANY_VALUES_STATS_SNAPSHOT.max()).isEqualTo(MANY_VALUES_MAX);
+    assertThat(INTEGER_MANY_VALUES_STATS_VARARGS.max()).isEqualTo(INTEGER_MANY_VALUES_MAX);
+    assertThat(INTEGER_MANY_VALUES_STATS_ITERABLE.max()).isEqualTo(INTEGER_MANY_VALUES_MAX);
+    assertThat(LONG_MANY_VALUES_STATS_ITERATOR.max()).isEqualTo(LONG_MANY_VALUES_MAX);
+    assertThat(LONG_MANY_VALUES_STATS_SNAPSHOT.max()).isEqualTo(LONG_MANY_VALUES_MAX);
   }
 
   public void testMin() {
@@ -438,14 +434,14 @@
       fail("Expected IllegalStateException");
     } catch (IllegalStateException expected) {
     }
-    assertThat(ONE_VALUE_STATS.min()).isWithin(ALLOWED_ERROR).of(ONE_VALUE);
+    assertThat(ONE_VALUE_STATS.min()).isEqualTo(ONE_VALUE);
     assertThat(Stats.of(POSITIVE_INFINITY).min()).isPositiveInfinity();
     assertThat(Stats.of(NEGATIVE_INFINITY).min()).isNegativeInfinity();
     assertThat(Stats.of(NaN).min()).isNaN();
-    assertThat(TWO_VALUES_STATS.min()).isWithin(ALLOWED_ERROR).of(TWO_VALUES_MIN);
-    assertThat(MANY_VALUES_STATS_VARARGS.min()).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MIN);
-    assertThat(MANY_VALUES_STATS_ITERABLE.min()).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MIN);
-    assertThat(MANY_VALUES_STATS_ITERATOR.min()).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MIN);
+    assertThat(TWO_VALUES_STATS.min()).isEqualTo(TWO_VALUES_MIN);
+    assertThat(MANY_VALUES_STATS_VARARGS.min()).isEqualTo(MANY_VALUES_MIN);
+    assertThat(MANY_VALUES_STATS_ITERABLE.min()).isEqualTo(MANY_VALUES_MIN);
+    assertThat(MANY_VALUES_STATS_ITERATOR.min()).isEqualTo(MANY_VALUES_MIN);
     // For datasets of many double values created from an accumulator snapshot, we test many
     // combinations of finite and non-finite values:
     for (ManyValues values : ALL_MANY_VALUES) {
@@ -453,25 +449,17 @@
       accumulator.addAll(values.asIterable());
       double min = accumulator.snapshot().min();
       if (values.hasAnyNaN()) {
-        assertThat(min).named("min of " + values).isNaN();
+        assertWithMessage("min of " + values).that(min).isNaN();
       } else if (values.hasAnyNegativeInfinity()) {
-        assertThat(min).named("min of " + values).isNegativeInfinity();
+        assertWithMessage("min of " + values).that(min).isNegativeInfinity();
       } else {
-        assertThat(min).named("min of " + values).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MIN);
+        assertWithMessage("min of " + values).that(min).isEqualTo(MANY_VALUES_MIN);
       }
     }
-    assertThat(INTEGER_MANY_VALUES_STATS_VARARGS.min())
-        .isWithin(ALLOWED_ERROR)
-        .of(INTEGER_MANY_VALUES_MIN);
-    assertThat(INTEGER_MANY_VALUES_STATS_ITERABLE.min())
-        .isWithin(ALLOWED_ERROR)
-        .of(INTEGER_MANY_VALUES_MIN);
-    assertThat(LONG_MANY_VALUES_STATS_ITERATOR.min())
-        .isWithin(ALLOWED_ERROR)
-        .of(LONG_MANY_VALUES_MIN);
-    assertThat(LONG_MANY_VALUES_STATS_SNAPSHOT.min())
-        .isWithin(ALLOWED_ERROR)
-        .of(LONG_MANY_VALUES_MIN);
+    assertThat(INTEGER_MANY_VALUES_STATS_VARARGS.min()).isEqualTo(INTEGER_MANY_VALUES_MIN);
+    assertThat(INTEGER_MANY_VALUES_STATS_ITERABLE.min()).isEqualTo(INTEGER_MANY_VALUES_MIN);
+    assertThat(LONG_MANY_VALUES_STATS_ITERATOR.min()).isEqualTo(LONG_MANY_VALUES_MIN);
+    assertThat(LONG_MANY_VALUES_STATS_SNAPSHOT.min()).isEqualTo(LONG_MANY_VALUES_MIN);
   }
 
   public void testEqualsAndHashCode() {
@@ -537,28 +525,33 @@
     for (ManyValues values : ALL_MANY_VALUES) {
       double mean = Stats.meanOf(values.asArray());
       if (values.hasAnyNaN()) {
-        assertThat(mean).named("mean of " + values).isNaN();
+        assertWithMessage("mean of " + values).that(mean).isNaN();
       } else if (values.hasAnyPositiveInfinity() && values.hasAnyNegativeInfinity()) {
-        assertThat(mean).named("mean of " + values).isNaN();
+        assertWithMessage("mean of " + values).that(mean).isNaN();
       } else if (values.hasAnyPositiveInfinity()) {
-        assertThat(mean).named("mean of " + values).isPositiveInfinity();
+        assertWithMessage("mean of " + values).that(mean).isPositiveInfinity();
       } else if (values.hasAnyNegativeInfinity()) {
-        assertThat(mean).named("mean of " + values).isNegativeInfinity();
+        assertWithMessage("mean of " + values).that(mean).isNegativeInfinity();
       } else {
-        assertThat(mean).named("mean of " + values).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MEAN);
+        assertWithMessage("mean of " + values)
+            .that(mean)
+            .isWithin(ALLOWED_ERROR)
+            .of(MANY_VALUES_MEAN);
       }
     }
     assertThat(Stats.meanOf(MANY_VALUES)).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MEAN);
     assertThat(Stats.meanOf(MANY_VALUES.iterator())).isWithin(ALLOWED_ERROR).of(MANY_VALUES_MEAN);
     assertThat(Stats.meanOf(INTEGER_MANY_VALUES))
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * INTEGER_MANY_VALUES_MEAN)
         .of(INTEGER_MANY_VALUES_MEAN);
     assertThat(Stats.meanOf(Ints.toArray(INTEGER_MANY_VALUES)))
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * INTEGER_MANY_VALUES_MEAN)
         .of(INTEGER_MANY_VALUES_MEAN);
-    assertThat(Stats.meanOf(LONG_MANY_VALUES)).isWithin(ALLOWED_ERROR).of(LONG_MANY_VALUES_MEAN);
+    assertThat(Stats.meanOf(LONG_MANY_VALUES))
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_MEAN)
+        .of(LONG_MANY_VALUES_MEAN);
     assertThat(Stats.meanOf(Longs.toArray(LONG_MANY_VALUES)))
-        .isWithin(ALLOWED_ERROR)
+        .isWithin(ALLOWED_ERROR * LONG_MANY_VALUES_MEAN)
         .of(LONG_MANY_VALUES_MEAN);
   }
 
diff --git a/android/guava-tests/test/com/google/common/math/StatsTesting.java b/android/guava-tests/test/com/google/common/math/StatsTesting.java
index b0aa362..12689d3 100644
--- a/android/guava-tests/test/com/google/common/math/StatsTesting.java
+++ b/android/guava-tests/test/com/google/common/math/StatsTesting.java
@@ -222,7 +222,7 @@
   static final Stats MANY_VALUES_STATS_VARARGS = Stats.of(1.1, -44.44, 33.33, 555.555, -2.2);
   static final Stats MANY_VALUES_STATS_ITERABLE = Stats.of(MANY_VALUES);
   static final Stats MANY_VALUES_STATS_ITERATOR = Stats.of(MANY_VALUES.iterator());
-  static final Stats MANY_VALUES_STATS_SNAPSHOT;
+  static final Stats MANY_VALUES_STATS_SNAPSHOT = buildManyValuesStatsSnapshot();
   static final Stats LARGE_VALUES_STATS = Stats.of(LARGE_VALUES);
   static final Stats OTHER_MANY_VALUES_STATS = Stats.of(OTHER_MANY_VALUES);
   static final Stats INTEGER_MANY_VALUES_STATS_VARARGS =
@@ -230,20 +230,21 @@
   static final Stats INTEGER_MANY_VALUES_STATS_ITERABLE = Stats.of(INTEGER_MANY_VALUES);
   static final Stats LARGE_INTEGER_VALUES_STATS = Stats.of(LARGE_INTEGER_VALUES);
   static final Stats LONG_MANY_VALUES_STATS_ITERATOR = Stats.of(LONG_MANY_VALUES.iterator());
-  static final Stats LONG_MANY_VALUES_STATS_SNAPSHOT;
+  static final Stats LONG_MANY_VALUES_STATS_SNAPSHOT = buildLongManyValuesStatsSnapshot();
   static final Stats LARGE_LONG_VALUES_STATS = Stats.of(LARGE_LONG_VALUES);
 
-  static {
+  private static Stats buildManyValuesStatsSnapshot() {
     StatsAccumulator accumulator = new StatsAccumulator();
     accumulator.addAll(MANY_VALUES);
-    MANY_VALUES_STATS_SNAPSHOT = accumulator.snapshot();
+    Stats stats = accumulator.snapshot();
     accumulator.add(999.999); // should do nothing to the snapshot
+    return stats;
   }
 
-  static {
+  private static Stats buildLongManyValuesStatsSnapshot() {
     StatsAccumulator accumulator = new StatsAccumulator();
     accumulator.addAll(LONG_MANY_VALUES);
-    LONG_MANY_VALUES_STATS_SNAPSHOT = accumulator.snapshot();
+    return accumulator.snapshot();
   }
 
   static final ImmutableList<Stats> ALL_STATS =
@@ -275,42 +276,43 @@
       createPairedStatsOf(ImmutableList.of(ONE_VALUE), ImmutableList.of(OTHER_ONE_VALUE));
   static final PairedStats TWO_VALUES_PAIRED_STATS =
       createPairedStatsOf(TWO_VALUES, OTHER_TWO_VALUES);
-  static final PairedStats MANY_VALUES_PAIRED_STATS;
+  static final PairedStats MANY_VALUES_PAIRED_STATS = buildManyValuesPairedStats();
   static final PairedStats DUPLICATE_MANY_VALUES_PAIRED_STATS =
       createPairedStatsOf(MANY_VALUES, OTHER_MANY_VALUES);
-  static final PairedStats HORIZONTAL_VALUES_PAIRED_STATS;
-  static final PairedStats VERTICAL_VALUES_PAIRED_STATS;
-  static final PairedStats CONSTANT_VALUES_PAIRED_STATS;
+  static final PairedStats HORIZONTAL_VALUES_PAIRED_STATS = buildHorizontalValuesPairedStats();
+  static final PairedStats VERTICAL_VALUES_PAIRED_STATS = buildVerticalValuesPairedStats();
+  static final PairedStats CONSTANT_VALUES_PAIRED_STATS = buildConstantValuesPairedStats();
 
-  static {
+  private static PairedStats buildManyValuesPairedStats() {
     PairedStatsAccumulator accumulator =
         createFilledPairedStatsAccumulator(MANY_VALUES, OTHER_MANY_VALUES);
-    MANY_VALUES_PAIRED_STATS = accumulator.snapshot();
+    PairedStats stats = accumulator.snapshot();
     accumulator.add(99.99, 9999.9999); // should do nothing to the snapshot
+    return stats;
   }
 
-  static {
+  private static PairedStats buildHorizontalValuesPairedStats() {
     PairedStatsAccumulator accumulator = new PairedStatsAccumulator();
     for (double x : MANY_VALUES) {
       accumulator.add(x, OTHER_ONE_VALUE);
     }
-    HORIZONTAL_VALUES_PAIRED_STATS = accumulator.snapshot();
+    return accumulator.snapshot();
   }
 
-  static {
+  private static PairedStats buildVerticalValuesPairedStats() {
     PairedStatsAccumulator accumulator = new PairedStatsAccumulator();
     for (double y : OTHER_MANY_VALUES) {
       accumulator.add(ONE_VALUE, y);
     }
-    VERTICAL_VALUES_PAIRED_STATS = accumulator.snapshot();
+    return accumulator.snapshot();
   }
 
-  static {
+  private static PairedStats buildConstantValuesPairedStats() {
     PairedStatsAccumulator accumulator = new PairedStatsAccumulator();
     for (int i = 0; i < MANY_VALUES_COUNT; ++i) {
       accumulator.add(ONE_VALUE, OTHER_ONE_VALUE);
     }
-    CONSTANT_VALUES_PAIRED_STATS = accumulator.snapshot();
+    return accumulator.snapshot();
   }
 
   static final ImmutableList<PairedStats> ALL_PAIRED_STATS =
@@ -387,8 +389,8 @@
         .of(x1 + xDelta);
     assertThat(transformation.slope()).isWithin(ALLOWED_ERROR).of(yDelta / xDelta);
     assertThat(transformation.inverse().slope()).isWithin(ALLOWED_ERROR).of(xDelta / yDelta);
-    assertThat(transformation.inverse()).isSameAs(transformation.inverse());
-    assertThat(transformation.inverse().inverse()).isSameAs(transformation);
+    assertThat(transformation.inverse()).isSameInstanceAs(transformation.inverse());
+    assertThat(transformation.inverse().inverse()).isSameInstanceAs(transformation);
   }
 
   /**
@@ -415,8 +417,8 @@
       fail("Expected IllegalStateException");
     } catch (IllegalStateException expected) {
     }
-    assertThat(transformation.inverse()).isSameAs(transformation.inverse());
-    assertThat(transformation.inverse().inverse()).isSameAs(transformation);
+    assertThat(transformation.inverse()).isSameInstanceAs(transformation.inverse());
+    assertThat(transformation.inverse().inverse()).isSameInstanceAs(transformation);
   }
 
   /**
@@ -443,8 +445,8 @@
     } catch (IllegalStateException expected) {
     }
     assertThat(transformation.inverse().slope()).isWithin(ALLOWED_ERROR).of(0.0);
-    assertThat(transformation.inverse()).isSameAs(transformation.inverse());
-    assertThat(transformation.inverse().inverse()).isSameAs(transformation);
+    assertThat(transformation.inverse()).isSameInstanceAs(transformation.inverse());
+    assertThat(transformation.inverse().inverse()).isSameInstanceAs(transformation);
   }
 
   /**
@@ -456,7 +458,7 @@
     assertThat(transformation.isVertical()).isFalse();
     assertThat(transformation.slope()).isNaN();
     assertThat(transformation.transform(0.0)).isNaN();
-    assertThat(transformation.inverse()).isSameAs(transformation);
+    assertThat(transformation.inverse()).isSameInstanceAs(transformation);
   }
 
   /**
diff --git a/android/guava-tests/test/com/google/common/net/HttpHeadersTest.java b/android/guava-tests/test/com/google/common/net/HttpHeadersTest.java
index 65e6fdb..9927e6b 100644
--- a/android/guava-tests/test/com/google/common/net/HttpHeadersTest.java
+++ b/android/guava-tests/test/com/google/common/net/HttpHeadersTest.java
@@ -36,18 +36,22 @@
   public void testConstantNameMatchesString() throws Exception {
     // Special case some of the weird HTTP Header names...
     ImmutableBiMap<String, String> specialCases =
-        ImmutableBiMap.of(
-            "ETAG",
-            "ETag",
-            "SOURCE_MAP",
-            "SourceMap",
-            "X_WEBKIT_CSP",
-            "X-WebKit-CSP",
-            "X_WEBKIT_CSP_REPORT_ONLY",
-            "X-WebKit-CSP-Report-Only");
+        ImmutableBiMap.<String, String>builder()
+            .put("CDN_LOOP", "CDN-Loop")
+            .put("ETAG", "ETag")
+            .put("SOURCE_MAP", "SourceMap")
+            .put("SEC_WEBSOCKET_ACCEPT", "Sec-WebSocket-Accept")
+            .put("SEC_WEBSOCKET_EXTENSIONS", "Sec-WebSocket-Extensions")
+            .put("SEC_WEBSOCKET_KEY", "Sec-WebSocket-Key")
+            .put("SEC_WEBSOCKET_PROTOCOL", "Sec-WebSocket-Protocol")
+            .put("SEC_WEBSOCKET_VERSION", "Sec-WebSocket-Version")
+            .put("X_WEBKIT_CSP", "X-WebKit-CSP")
+            .put("X_WEBKIT_CSP_REPORT_ONLY", "X-WebKit-CSP-Report-Only")
+            .build();
     ImmutableSet<String> uppercaseAcronyms =
         ImmutableSet.of(
-            "ID", "DNT", "DNS", "HTTP2", "IP", "MD5", "P3P", "TE", "UID", "URL", "WWW", "XSS");
+            "CH", "ID", "DNT", "DNS", "HTTP2", "IP", "MD5", "P3P", "TE", "UA", "UID", "URL", "WWW",
+            "XSS");
     assertConstantNameMatchesString(HttpHeaders.class, specialCases, uppercaseAcronyms);
   }
 
diff --git a/android/guava-tests/test/com/google/common/net/InetAddressesTest.java b/android/guava-tests/test/com/google/common/net/InetAddressesTest.java
index 62c7bd0..ed3aa27 100644
--- a/android/guava-tests/test/com/google/common/net/InetAddressesTest.java
+++ b/android/guava-tests/test/com/google/common/net/InetAddressesTest.java
@@ -18,7 +18,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.common.testing.NullPointerTester;
+import java.math.BigInteger;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
@@ -39,77 +41,80 @@
   }
 
   public void testForStringBogusInput() {
-    String[] bogusInputs = {
-      "",
-      "016.016.016.016",
-      "016.016.016",
-      "016.016",
-      "016",
-      "000.000.000.000",
-      "000",
-      "0x0a.0x0a.0x0a.0x0a",
-      "0x0a.0x0a.0x0a",
-      "0x0a.0x0a",
-      "0x0a",
-      "42.42.42.42.42",
-      "42.42.42",
-      "42.42",
-      "42",
-      "42..42.42",
-      "42..42.42.42",
-      "42.42.42.42.",
-      "42.42.42.42...",
-      ".42.42.42.42",
-      "...42.42.42.42",
-      "42.42.42.-0",
-      "42.42.42.+0",
-      ".",
-      "...",
-      "bogus",
-      "bogus.com",
-      "192.168.0.1.com",
-      "12345.67899.-54321.-98765",
-      "257.0.0.0",
-      "42.42.42.-42",
-      "3ffe::1.net",
-      "3ffe::1::1",
-      "1::2::3::4:5",
-      "::7:6:5:4:3:2:", // should end with ":0"
-      ":6:5:4:3:2:1::", // should begin with "0:"
-      "2001::db:::1",
-      "FEDC:9878",
-      "+1.+2.+3.4",
-      "1.2.3.4e0",
-      "::7:6:5:4:3:2:1:0", // too many parts
-      "7:6:5:4:3:2:1:0::", // too many parts
-      "9:8:7:6:5:4:3::2:1", // too many parts
-      "0:1:2:3::4:5:6:7", // :: must remove at least one 0.
-      "3ffe:0:0:0:0:0:0:0:1", // too many parts (9 instead of 8)
-      "3ffe::10000", // hextet exceeds 16 bits
-      "3ffe::goog",
-      "3ffe::-0",
-      "3ffe::+0",
-      "3ffe::-1",
-      ":",
-      ":::",
-      "::1.2.3",
-      "::1.2.3.4.5",
-      "::1.2.3.4:",
-      "1.2.3.4::",
-      "2001:db8::1:",
-      ":2001:db8::1",
-      ":1:2:3:4:5:6:7",
-      "1:2:3:4:5:6:7:",
-      ":1:2:3:4:5:6:"
-    };
+    ImmutableSet<String> bogusInputs =
+        ImmutableSet.of(
+            "",
+            "016.016.016.016",
+            "016.016.016",
+            "016.016",
+            "016",
+            "000.000.000.000",
+            "000",
+            "0x0a.0x0a.0x0a.0x0a",
+            "0x0a.0x0a.0x0a",
+            "0x0a.0x0a",
+            "0x0a",
+            "42.42.42.42.42",
+            "42.42.42",
+            "42.42",
+            "42",
+            "42..42.42",
+            "42..42.42.42",
+            "42.42.42.42.",
+            "42.42.42.42...",
+            ".42.42.42.42",
+            ".42.42.42",
+            "...42.42.42.42",
+            "42.42.42.-0",
+            "42.42.42.+0",
+            ".",
+            "...",
+            "bogus",
+            "bogus.com",
+            "192.168.0.1.com",
+            "12345.67899.-54321.-98765",
+            "257.0.0.0",
+            "42.42.42.-42",
+            "42.42.42.ab",
+            "3ffe::1.net",
+            "3ffe::1::1",
+            "1::2::3::4:5",
+            "::7:6:5:4:3:2:", // should end with ":0"
+            ":6:5:4:3:2:1::", // should begin with "0:"
+            "2001::db:::1",
+            "FEDC:9878",
+            "+1.+2.+3.4",
+            "1.2.3.4e0",
+            "6:5:4:3:2:1:0", // too few parts
+            "::7:6:5:4:3:2:1:0", // too many parts
+            "7:6:5:4:3:2:1:0::", // too many parts
+            "9:8:7:6:5:4:3::2:1", // too many parts
+            "0:1:2:3::4:5:6:7", // :: must remove at least one 0.
+            "3ffe:0:0:0:0:0:0:0:1", // too many parts (9 instead of 8)
+            "3ffe::10000", // hextet exceeds 16 bits
+            "3ffe::goog",
+            "3ffe::-0",
+            "3ffe::+0",
+            "3ffe::-1",
+            ":",
+            ":::",
+            "::1.2.3",
+            "::1.2.3.4.5",
+            "::1.2.3.4:",
+            "1.2.3.4::",
+            "2001:db8::1:",
+            ":2001:db8::1",
+            ":1:2:3:4:5:6:7",
+            "1:2:3:4:5:6:7:",
+            ":1:2:3:4:5:6:");
 
-    for (int i = 0; i < bogusInputs.length; i++) {
+    for (String bogusInput : bogusInputs) {
       try {
-        InetAddresses.forString(bogusInputs[i]);
-        fail("IllegalArgumentException expected for '" + bogusInputs[i] + "'");
+        InetAddresses.forString(bogusInput);
+        fail("IllegalArgumentException expected for '" + bogusInput + "'");
       } catch (IllegalArgumentException expected) {
       }
-      assertFalse(InetAddresses.isInetAddress(bogusInputs[i]));
+      assertFalse(InetAddresses.isInetAddress(bogusInput));
     }
   }
 
@@ -124,40 +129,35 @@
 
   public void testForStringIPv4Input() throws UnknownHostException {
     String ipStr = "192.168.0.1";
-    InetAddress ipv4Addr = null;
     // Shouldn't hit DNS, because it's an IP string literal.
-    ipv4Addr = InetAddress.getByName(ipStr);
+    InetAddress ipv4Addr = InetAddress.getByName(ipStr);
     assertEquals(ipv4Addr, InetAddresses.forString(ipStr));
     assertTrue(InetAddresses.isInetAddress(ipStr));
   }
 
   public void testForStringIPv6Input() throws UnknownHostException {
     String ipStr = "3ffe::1";
-    InetAddress ipv6Addr = null;
     // Shouldn't hit DNS, because it's an IP string literal.
-    ipv6Addr = InetAddress.getByName(ipStr);
+    InetAddress ipv6Addr = InetAddress.getByName(ipStr);
     assertEquals(ipv6Addr, InetAddresses.forString(ipStr));
     assertTrue(InetAddresses.isInetAddress(ipStr));
   }
 
   public void testForStringIPv6EightColons() throws UnknownHostException {
-    String[] eightColons = {
-      "::7:6:5:4:3:2:1", "::7:6:5:4:3:2:0", "7:6:5:4:3:2:1::", "0:6:5:4:3:2:1::",
-    };
+    ImmutableSet<String> eightColons =
+        ImmutableSet.of("::7:6:5:4:3:2:1", "::7:6:5:4:3:2:0", "7:6:5:4:3:2:1::", "0:6:5:4:3:2:1::");
 
-    for (int i = 0; i < eightColons.length; i++) {
-      InetAddress ipv6Addr = null;
+    for (String ipString : eightColons) {
       // Shouldn't hit DNS, because it's an IP string literal.
-      ipv6Addr = InetAddress.getByName(eightColons[i]);
-      assertEquals(ipv6Addr, InetAddresses.forString(eightColons[i]));
-      assertTrue(InetAddresses.isInetAddress(eightColons[i]));
+      InetAddress ipv6Addr = InetAddress.getByName(ipString);
+      assertEquals(ipv6Addr, InetAddresses.forString(ipString));
+      assertTrue(InetAddresses.isInetAddress(ipString));
     }
   }
 
   public void testConvertDottedQuadToHex() throws UnknownHostException {
-    String[] ipStrings = {
-      "7::0.128.0.127", "7::0.128.0.128", "7::128.128.0.127", "7::0.128.128.127"
-    };
+    ImmutableSet<String> ipStrings =
+        ImmutableSet.of("7::0.128.0.127", "7::0.128.0.128", "7::128.128.0.127", "7::0.128.128.127");
 
     for (String ipString : ipStrings) {
       // Shouldn't hit DNS, because it's an IP string literal.
@@ -167,6 +167,58 @@
     }
   }
 
+  // see https://github.com/google/guava/issues/2587
+  private static final ImmutableSet<String> SCOPE_IDS =
+      ImmutableSet.of("eno1", "en1", "eth0", "X", "1", "2", "14", "20");
+
+  public void testIPv4AddressWithScopeId() {
+    ImmutableSet<String> ipStrings = ImmutableSet.of("1.2.3.4", "192.168.0.1");
+    for (String ipString : ipStrings) {
+      for (String scopeId : SCOPE_IDS) {
+        String withScopeId = ipString + "%" + scopeId;
+        assertFalse(
+            "InetAddresses.isInetAddress(" + withScopeId + ") should be false but was true",
+            InetAddresses.isInetAddress(withScopeId));
+      }
+    }
+  }
+
+  public void testDottedQuadAddressWithScopeId() {
+    ImmutableSet<String> ipStrings =
+        ImmutableSet.of("7::0.128.0.127", "7::0.128.0.128", "7::128.128.0.127", "7::0.128.128.127");
+    for (String ipString : ipStrings) {
+      for (String scopeId : SCOPE_IDS) {
+        String withScopeId = ipString + "%" + scopeId;
+        assertFalse(
+            "InetAddresses.isInetAddress(" + withScopeId + ") should be false but was true",
+            InetAddresses.isInetAddress(withScopeId));
+      }
+    }
+  }
+
+  public void testIPv6AddressWithScopeId() {
+    ImmutableSet<String> ipStrings =
+        ImmutableSet.of(
+            "0:0:0:0:0:0:0:1",
+            "fe80::a",
+            "fe80::1",
+            "fe80::2",
+            "fe80::42",
+            "fe80::3dd0:7f8e:57b7:34d5",
+            "fe80::71a3:2b00:ddd3:753f",
+            "fe80::8b2:d61e:e5c:b333",
+            "fe80::b059:65f4:e877:c40");
+    for (String ipString : ipStrings) {
+      for (String scopeId : SCOPE_IDS) {
+        String withScopeId = ipString + "%" + scopeId;
+        assertTrue(
+            "InetAddresses.isInetAddress(" + withScopeId + ") should be true but was false",
+            InetAddresses.isInetAddress(withScopeId));
+        assertEquals(InetAddresses.forString(withScopeId), InetAddresses.forString(ipString));
+      }
+    }
+  }
+
   public void testToAddrStringIPv4() {
     // Don't need to test IPv4 much; it just calls getHostAddress().
     assertEquals("1.2.3.4", InetAddresses.toAddrString(InetAddresses.forString("1.2.3.4")));
@@ -315,34 +367,30 @@
   }
 
   public void testCompatIPv4Addresses() {
-    String[] nonCompatAddresses = {
-      "3ffe::1", "::", "::1",
-    };
+    ImmutableSet<String> nonCompatAddresses = ImmutableSet.of("3ffe::1", "::", "::1");
 
-    for (int i = 0; i < nonCompatAddresses.length; i++) {
-      InetAddress ip = InetAddresses.forString(nonCompatAddresses[i]);
+    for (String nonCompatAddress : nonCompatAddresses) {
+      InetAddress ip = InetAddresses.forString(nonCompatAddress);
       assertFalse(InetAddresses.isCompatIPv4Address((Inet6Address) ip));
       try {
         InetAddresses.getCompatIPv4Address((Inet6Address) ip);
-        fail("IllegalArgumentException expected for '" + nonCompatAddresses[i] + "'");
+        fail("IllegalArgumentException expected for '" + nonCompatAddress + "'");
       } catch (IllegalArgumentException expected) {
       }
     }
 
-    String[] validCompatAddresses = {
-      "::1.2.3.4", "::102:304",
-    };
+    ImmutableSet<String> validCompatAddresses = ImmutableSet.of("::1.2.3.4", "::102:304");
     String compatStr = "1.2.3.4";
     InetAddress compat = InetAddresses.forString(compatStr);
 
-    for (int i = 0; i < validCompatAddresses.length; i++) {
-      InetAddress ip = InetAddresses.forString(validCompatAddresses[i]);
-      assertTrue("checking '" + validCompatAddresses[i] + "'", ip instanceof Inet6Address);
+    for (String validCompatAddress : validCompatAddresses) {
+      InetAddress ip = InetAddresses.forString(validCompatAddress);
+      assertTrue("checking '" + validCompatAddress + "'", ip instanceof Inet6Address);
       assertTrue(
-          "checking '" + validCompatAddresses[i] + "'",
+          "checking '" + validCompatAddress + "'",
           InetAddresses.isCompatIPv4Address((Inet6Address) ip));
       assertEquals(
-          "checking '" + validCompatAddresses[i] + "'",
+          "checking '" + validCompatAddress + "'",
           compat,
           InetAddresses.getCompatIPv4Address((Inet6Address) ip));
     }
@@ -389,16 +437,14 @@
   }
 
   public void test6to4Addresses() {
-    String[] non6to4Addresses = {
-      "::1.2.3.4", "3ffe::1", "::", "::1",
-    };
+    ImmutableSet<String> non6to4Addresses = ImmutableSet.of("::1.2.3.4", "3ffe::1", "::", "::1");
 
-    for (int i = 0; i < non6to4Addresses.length; i++) {
-      InetAddress ip = InetAddresses.forString(non6to4Addresses[i]);
+    for (String non6to4Address : non6to4Addresses) {
+      InetAddress ip = InetAddresses.forString(non6to4Address);
       assertFalse(InetAddresses.is6to4Address((Inet6Address) ip));
       try {
         InetAddresses.get6to4IPv4Address((Inet6Address) ip);
-        fail("IllegalArgumentException expected for '" + non6to4Addresses[i] + "'");
+        fail("IllegalArgumentException expected for '" + non6to4Address + "'");
       } catch (IllegalArgumentException expected) {
       }
     }
@@ -413,16 +459,14 @@
   }
 
   public void testTeredoAddresses() {
-    String[] nonTeredoAddresses = {
-      "::1.2.3.4", "3ffe::1", "::", "::1",
-    };
+    ImmutableSet<String> nonTeredoAddresses = ImmutableSet.of("::1.2.3.4", "3ffe::1", "::", "::1");
 
-    for (int i = 0; i < nonTeredoAddresses.length; i++) {
-      InetAddress ip = InetAddresses.forString(nonTeredoAddresses[i]);
+    for (String nonTeredoAddress : nonTeredoAddresses) {
+      InetAddress ip = InetAddresses.forString(nonTeredoAddress);
       assertFalse(InetAddresses.isTeredoAddress((Inet6Address) ip));
       try {
         InetAddresses.getTeredoInfo((Inet6Address) ip);
-        fail("IllegalArgumentException expected for '" + nonTeredoAddresses[i] + "'");
+        fail("IllegalArgumentException expected for '" + nonTeredoAddress + "'");
       } catch (IllegalArgumentException expected) {
       }
     }
@@ -457,37 +501,39 @@
 
   public void testIsatapAddresses() {
     InetAddress ipv4 = InetAddresses.forString("1.2.3.4");
-    String[] validIsatapAddresses = {
-      "2001:db8::5efe:102:304",
-      "2001:db8::100:5efe:102:304", // Private Multicast? Not likely.
-      "2001:db8::200:5efe:102:304",
-      "2001:db8::300:5efe:102:304" // Public Multicast? Also unlikely.
-    };
-    String[] nonIsatapAddresses = {
-      "::1.2.3.4",
-      "3ffe::1",
-      "::",
-      "::1",
-      "2001:db8::0040:5efe:102:304",
-      "2001:db8::5ffe:102:304",
-      "2001:db8::5eff:102:304",
-      "2001:0:102:203:200:5efe:506:708", // Teredo address; not ISATAP
-    };
+    ImmutableSet<String> validIsatapAddresses =
+        ImmutableSet.of(
+            "2001:db8::5efe:102:304",
+            "2001:db8::100:5efe:102:304", // Private Multicast? Not likely.
+            "2001:db8::200:5efe:102:304",
+            "2001:db8::300:5efe:102:304" // Public Multicast? Also unlikely.
+            );
+    ImmutableSet<String> nonIsatapAddresses =
+        ImmutableSet.of(
+            "::1.2.3.4",
+            "3ffe::1",
+            "::",
+            "::1",
+            "2001:db8::0040:5efe:102:304",
+            "2001:db8::5ffe:102:304",
+            "2001:db8::5eff:102:304",
+            "2001:0:102:203:200:5efe:506:708" // Teredo address; not ISATAP
+            );
 
-    for (int i = 0; i < validIsatapAddresses.length; i++) {
-      InetAddress ip = InetAddresses.forString(validIsatapAddresses[i]);
+    for (String validIsatapAddress : validIsatapAddresses) {
+      InetAddress ip = InetAddresses.forString(validIsatapAddress);
       assertTrue(InetAddresses.isIsatapAddress((Inet6Address) ip));
       assertEquals(
-          "checking '" + validIsatapAddresses[i] + "'",
+          "checking '" + validIsatapAddress + "'",
           ipv4,
           InetAddresses.getIsatapIPv4Address((Inet6Address) ip));
     }
-    for (int i = 0; i < nonIsatapAddresses.length; i++) {
-      InetAddress ip = InetAddresses.forString(nonIsatapAddresses[i]);
+    for (String nonIsatapAddress : nonIsatapAddresses) {
+      InetAddress ip = InetAddresses.forString(nonIsatapAddress);
       assertFalse(InetAddresses.isIsatapAddress((Inet6Address) ip));
       try {
         InetAddresses.getIsatapIPv4Address((Inet6Address) ip);
-        fail("IllegalArgumentException expected for '" + nonIsatapAddresses[i] + "'");
+        fail("IllegalArgumentException expected for '" + nonIsatapAddress + "'");
       } catch (IllegalArgumentException expected) {
       }
     }
@@ -525,75 +571,77 @@
 
   public void testGetCoercedIPv4Address() {
     // Check that a coerced IPv4 address is unaltered.
-    InetAddress localHost4 = InetAddresses.forString("127.0.0.1");
-    assertEquals(localHost4, InetAddresses.getCoercedIPv4Address(localHost4));
+    assertThat(InetAddresses.getCoercedIPv4Address(InetAddresses.forString("127.0.0.1")))
+        .isEqualTo(InetAddresses.forString("127.0.0.1"));
 
     // ::1 special case
-    assertEquals(localHost4, InetAddresses.getCoercedIPv4Address(InetAddresses.forString("::1")));
+    assertThat(InetAddresses.getCoercedIPv4Address(InetAddresses.forString("::1")))
+        .isEqualTo(InetAddresses.forString("127.0.0.1"));
 
     // :: special case
-    assertEquals(
-        InetAddresses.forString("0.0.0.0"),
-        InetAddresses.getCoercedIPv4Address(InetAddresses.forString("::")));
+    assertThat(InetAddresses.getCoercedIPv4Address(InetAddresses.forString("::")))
+        .isEqualTo(InetAddresses.forString("0.0.0.0"));
 
     // test compat address (should be hashed)
-    assertTrue(
-        InetAddresses.forString("1.2.3.4")
-            != InetAddresses.getCoercedIPv4Address(InetAddresses.forString("::1.2.3.4")));
+    assertThat(InetAddresses.getCoercedIPv4Address(InetAddresses.forString("::1.2.3.4")))
+        .isNotEqualTo(InetAddresses.forString("1.2.3.4"));
 
     // test 6to4 address (should be hashed)
-    assertTrue(
-        InetAddresses.forString("1.2.3.4")
-            != InetAddresses.getCoercedIPv4Address(InetAddresses.forString("2002:0102:0304::1")));
+    assertThat(InetAddresses.getCoercedIPv4Address(InetAddresses.forString("2002:0102:0304::1")))
+        .isNotEqualTo(InetAddresses.forString("1.2.3.4"));
 
     // 2 6to4 addresses differing in the embedded IPv4 address should
     // hash to the different values.
-    assertTrue(
-        InetAddresses.getCoercedIPv4Address(InetAddresses.forString("2002:0102:0304::1"))
-            != InetAddresses.getCoercedIPv4Address(InetAddresses.forString("2002:0506:0708::1")));
+    assertThat(InetAddresses.getCoercedIPv4Address(InetAddresses.forString("2002:0102:0304::1")))
+        .isNotEqualTo(
+            InetAddresses.getCoercedIPv4Address(InetAddresses.forString("2002:0506:0708::1")));
 
     // 2 6to4 addresses NOT differing in the embedded IPv4 address should
     // hash to the same value.
-    assertTrue(
-        InetAddresses.getCoercedIPv4Address(InetAddresses.forString("2002:0102:0304::1"))
-            != InetAddresses.getCoercedIPv4Address(InetAddresses.forString("2002:0102:0304::2")));
+    assertThat(InetAddresses.getCoercedIPv4Address(InetAddresses.forString("2002:0102:0304::1")))
+        .isEqualTo(
+            InetAddresses.getCoercedIPv4Address(InetAddresses.forString("2002:0102:0304::2")));
 
     // test Teredo address (should be hashed)
-    assertTrue(
-        InetAddresses.forString("192.0.2.45")
-            != InetAddresses.getCoercedIPv4Address(
-                InetAddresses.forString("2001:0000:4136:e378:8000:63bf:3fff:fdd2")));
+    assertThat(
+            InetAddresses.getCoercedIPv4Address(
+                InetAddresses.forString("2001:0000:4136:e378:8000:63bf:3fff:fdd2")))
+        .isNotEqualTo(InetAddresses.forString("192.0.2.45"));
 
-    // 2 Teredo addresses differing in the embedded IPv4 address should
-    // hash to the different values.
-    assertTrue(
-        InetAddresses.getCoercedIPv4Address(
-                InetAddresses.forString("2001:0000:4136:e378:8000:63bf:3fff:fdd2"))
-            != InetAddresses.getCoercedIPv4Address(
-                InetAddresses.forString("2001:0000:4136:e379:8000:63bf:3fff:fdd2")));
+    // 2 Teredo addresses differing in their embedded IPv4 addresses should hash to different
+    // values.
+    assertThat(
+            InetAddresses.getCoercedIPv4Address(
+                InetAddresses.forString("2001:0000:4136:e378:8000:63bf:3fff:fdd2")))
+        .isNotEqualTo(
+            InetAddresses.getCoercedIPv4Address(
+                InetAddresses.forString("2001:0000:4136:e378:8000:63bf:3fff:fdd3")));
 
-    // 2 Teredo addresses NOT differing in the embedded IPv4 address should
-    // hash to the same value.
-    assertEquals(
-        InetAddresses.getCoercedIPv4Address(
-            InetAddresses.forString("2001:0000:4136:e378:8000:63bf:3fff:fdd2")),
-        InetAddresses.getCoercedIPv4Address(
-            InetAddresses.forString("2001:0000:4136:e378:9000:63bf:3fff:fdd2")));
+    // 2 Teredo addresses NOT differing in the their embedded IPv4 addresses should hash to the same
+    // value.
+    assertThat(
+            InetAddresses.getCoercedIPv4Address(
+                InetAddresses.forString("2001:0000:4136:e378:8000:63bf:3fff:fdd2")))
+        .isEqualTo(
+            InetAddresses.getCoercedIPv4Address(
+                InetAddresses.forString("2001:0000:5136:f378:9000:73bf:3fff:fdd2")));
 
     // Test that an address hashes in to the 224.0.0.0/3 number-space.
-    InetAddress coerced =
-        InetAddresses.getCoercedIPv4Address(InetAddresses.forString("2001:4860::1"));
-    assertTrue(0xe0000000 <= InetAddresses.coerceToInteger(coerced));
-    assertTrue(InetAddresses.coerceToInteger(coerced) <= 0xfffffffe);
+    int coercedInt =
+        InetAddresses.coerceToInteger(
+            InetAddresses.getCoercedIPv4Address(InetAddresses.forString("2001:4860::1")));
+    assertThat(coercedInt).isAtLeast(0xe0000000);
+    assertThat(coercedInt).isAtMost(0xfffffffe);
   }
 
-  public void testToInteger() {
-    InetAddress ipv4Addr = InetAddresses.forString("127.0.0.1");
-    assertEquals(0x7f000001, InetAddresses.coerceToInteger(ipv4Addr));
+  public void testCoerceToInteger() {
+    assertThat(InetAddresses.coerceToInteger(InetAddresses.forString("127.0.0.1")))
+        .isEqualTo(0x7f000001);
   }
 
   public void testFromInteger() {
-    assertEquals(InetAddresses.fromInteger(0x7f000001), InetAddresses.forString("127.0.0.1"));
+    assertThat(InetAddresses.fromInteger(0x7f000001))
+        .isEqualTo(InetAddresses.forString("127.0.0.1"));
   }
 
   public void testFromLittleEndianByteArray() throws UnknownHostException {
@@ -722,4 +770,80 @@
     } catch (IllegalArgumentException expected) {
     }
   }
+
+  public void testFromIpv4BigIntegerThrowsLessThanZero() {
+    try {
+      InetAddresses.fromIPv4BigInteger(BigInteger.valueOf(-1L));
+      fail();
+    } catch (IllegalArgumentException expected) {
+      assertEquals("BigInteger must be greater than or equal to 0", expected.getMessage());
+    }
+  }
+
+  public void testFromIpv6BigIntegerThrowsLessThanZero() {
+    try {
+      InetAddresses.fromIPv6BigInteger(BigInteger.valueOf(-1L));
+      fail();
+    } catch (IllegalArgumentException expected) {
+      assertEquals("BigInteger must be greater than or equal to 0", expected.getMessage());
+    }
+  }
+
+  public void testFromIpv4BigIntegerValid() {
+    checkBigIntegerConversion("0.0.0.0", BigInteger.ZERO);
+    checkBigIntegerConversion("0.0.0.1", BigInteger.ONE);
+    checkBigIntegerConversion("127.255.255.255", BigInteger.valueOf(Integer.MAX_VALUE));
+    checkBigIntegerConversion(
+        "255.255.255.254", BigInteger.valueOf(Integer.MAX_VALUE).multiply(BigInteger.valueOf(2)));
+    checkBigIntegerConversion(
+        "255.255.255.255", BigInteger.ONE.shiftLeft(32).subtract(BigInteger.ONE));
+  }
+
+  public void testFromIpv6BigIntegerValid() {
+    checkBigIntegerConversion("::", BigInteger.ZERO);
+    checkBigIntegerConversion("::1", BigInteger.ONE);
+    checkBigIntegerConversion("::7fff:ffff", BigInteger.valueOf(Integer.MAX_VALUE));
+    checkBigIntegerConversion("::7fff:ffff:ffff:ffff", BigInteger.valueOf(Long.MAX_VALUE));
+    checkBigIntegerConversion(
+        "::ffff:ffff:ffff:ffff", BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE));
+    checkBigIntegerConversion(
+        "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+        BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE));
+  }
+
+  public void testFromIpv4BigIntegerInputTooLarge() {
+    try {
+      InetAddresses.fromIPv4BigInteger(BigInteger.ONE.shiftLeft(32).add(BigInteger.ONE));
+      fail();
+    } catch (IllegalArgumentException expected) {
+      assertEquals(
+          "BigInteger cannot be converted to InetAddress because it has more than 4 bytes:"
+              + " 4294967297",
+          expected.getMessage());
+    }
+  }
+
+  public void testFromIpv6BigIntegerInputTooLarge() {
+    try {
+      InetAddresses.fromIPv6BigInteger(BigInteger.ONE.shiftLeft(128).add(BigInteger.ONE));
+      fail();
+    } catch (IllegalArgumentException expected) {
+      assertEquals(
+          "BigInteger cannot be converted to InetAddress because it has more than 16 bytes:"
+              + " 340282366920938463463374607431768211457",
+          expected.getMessage());
+    }
+  }
+
+  /** Checks that the IP converts to the big integer and the big integer converts to the IP. */
+  private static void checkBigIntegerConversion(String ip, BigInteger bigIntegerIp) {
+    InetAddress address = InetAddresses.forString(ip);
+    boolean isIpv6 = address instanceof Inet6Address;
+    assertEquals(bigIntegerIp, InetAddresses.toBigInteger(address));
+    assertEquals(
+        address,
+        isIpv6
+            ? InetAddresses.fromIPv6BigInteger(bigIntegerIp)
+            : InetAddresses.fromIPv4BigInteger(bigIntegerIp));
+  }
 }
diff --git a/android/guava-tests/test/com/google/common/net/InternetDomainNameTest.java b/android/guava-tests/test/com/google/common/net/InternetDomainNameTest.java
index 126076e..09602b7 100644
--- a/android/guava-tests/test/com/google/common/net/InternetDomainNameTest.java
+++ b/android/guava-tests/test/com/google/common/net/InternetDomainNameTest.java
@@ -63,6 +63,8 @@
           "f_a",
           "foo.net.us\uFF61ocm",
           "woo.com.",
+          "8server.shop",
+          "123.cn",
           "a" + DELTA + "b.com",
           ALMOST_TOO_MANY_LEVELS,
           ALMOST_TOO_LONG);
diff --git a/android/guava-tests/test/com/google/common/net/MediaTypeTest.java b/android/guava-tests/test/com/google/common/net/MediaTypeTest.java
index 7dfa9b8..cec3cdd 100644
--- a/android/guava-tests/test/com/google/common/net/MediaTypeTest.java
+++ b/android/guava-tests/test/com/google/common/net/MediaTypeTest.java
@@ -148,6 +148,38 @@
     }
   }
 
+  public void testCreate_nonAsciiType() {
+    try {
+      MediaType.create("…", "a");
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testCreate_nonAsciiSubtype() {
+    try {
+      MediaType.create("a", "…");
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testCreate_emptyType() {
+    try {
+      MediaType.create("", "a");
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testCreate_emptySubtype() {
+    try {
+      MediaType.create("a", "");
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
   public void testCreateApplicationType() {
     MediaType newType = MediaType.createApplicationType("yams");
     assertEquals("application", newType.type());
@@ -160,6 +192,12 @@
     assertEquals("yams", newType.subtype());
   }
 
+  public void testCreateFontType() {
+    MediaType newType = MediaType.createFontType("yams");
+    assertEquals("font", newType.type());
+    assertEquals("yams", newType.subtype());
+  }
+
   public void testCreateImageType() {
     MediaType newType = MediaType.createImageType("yams");
     assertEquals("image", newType.type());
@@ -225,6 +263,26 @@
     }
   }
 
+  public void testWithParameters_nonAsciiParameter() {
+    MediaType mediaType = MediaType.parse("text/plain");
+    ImmutableListMultimap<String, String> parameters = ImmutableListMultimap.of("…", "a");
+    try {
+      mediaType.withParameters(parameters);
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testWithParameters_nonAsciiParameterValue() {
+    MediaType mediaType = MediaType.parse("text/plain");
+    ImmutableListMultimap<String, String> parameters = ImmutableListMultimap.of("a", "…");
+    try {
+      mediaType.withParameters(parameters);
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
   public void testWithParameter() {
     assertEquals(
         MediaType.parse("text/plain; a=1"), MediaType.parse("text/plain").withParameter("a", "1"));
@@ -248,6 +306,33 @@
     }
   }
 
+  public void testWithParameter_nonAsciiParameter() {
+    MediaType mediaType = MediaType.parse("text/plain");
+    try {
+      mediaType.withParameter("…", "a");
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testWithParameter_nonAsciiParameterValue() {
+    MediaType mediaType = MediaType.parse("text/plain");
+    try {
+      mediaType.withParameter("a", "…");
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testWithParameter_emptyParameter() {
+    MediaType mediaType = MediaType.parse("text/plain");
+    try {
+      mediaType.withParameter("", "a");
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
   public void testWithParametersIterable() {
     assertEquals(
         MediaType.parse("text/plain"),
@@ -275,6 +360,24 @@
     }
   }
 
+  public void testWithParametersIterable_nonAsciiParameter() {
+    MediaType mediaType = MediaType.parse("text/plain");
+    try {
+      mediaType.withParameters("…", ImmutableSet.of("a"));
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testWithParametersIterable_nonAsciiParameterValue() {
+    MediaType mediaType = MediaType.parse("text/plain");
+    try {
+      mediaType.withParameters("a", ImmutableSet.of("…"));
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
   public void testWithParametersIterable_nullValue() {
     MediaType mediaType = MediaType.parse("text/plain");
     try {
@@ -507,10 +610,13 @@
   public void testToString() {
     assertEquals("text/plain", MediaType.create("text", "plain").toString());
     assertEquals(
-        "text/plain; something=\"cr@zy\"; something-else=\"crazy with spaces\"",
+        "text/plain; something=\"cr@zy\"; something-else=\"crazy with spaces\";"
+            + " and-another-thing=\"\"; normal-thing=foo",
         MediaType.create("text", "plain")
             .withParameter("something", "cr@zy")
             .withParameter("something-else", "crazy with spaces")
+            .withParameter("and-another-thing", "")
+            .withParameter("normal-thing", "foo")
             .toString());
   }
 }
diff --git a/android/guava-tests/test/com/google/common/primitives/DoublesTest.java b/android/guava-tests/test/com/google/common/primitives/DoublesTest.java
index 293fdb1..871b84c 100644
--- a/android/guava-tests/test/com/google/common/primitives/DoublesTest.java
+++ b/android/guava-tests/test/com/google/common/primitives/DoublesTest.java
@@ -194,6 +194,7 @@
     assertEquals(-1, Doubles.lastIndexOf(new double[] {NaN, 5.0}, NaN));
   }
 
+  @GwtIncompatible
   public void testMax_noArgs() {
     try {
       Doubles.max();
@@ -216,6 +217,7 @@
     assertTrue(Double.isNaN(Doubles.max(VALUES)));
   }
 
+  @GwtIncompatible
   public void testMin_noArgs() {
     try {
       Doubles.min();
diff --git a/android/guava-tests/test/com/google/common/primitives/FloatsTest.java b/android/guava-tests/test/com/google/common/primitives/FloatsTest.java
index 7eb0c5b..fd59ad4 100644
--- a/android/guava-tests/test/com/google/common/primitives/FloatsTest.java
+++ b/android/guava-tests/test/com/google/common/primitives/FloatsTest.java
@@ -186,6 +186,7 @@
     assertEquals(-1, Floats.lastIndexOf(new float[] {NaN, 5f}, NaN));
   }
 
+  @GwtIncompatible
   public void testMax_noArgs() {
     try {
       Floats.max();
@@ -207,6 +208,7 @@
     assertTrue(Float.isNaN(Floats.max(VALUES)));
   }
 
+  @GwtIncompatible
   public void testMin_noArgs() {
     try {
       Floats.min();
diff --git a/android/guava-tests/test/com/google/common/primitives/ImmutableDoubleArrayTest.java b/android/guava-tests/test/com/google/common/primitives/ImmutableDoubleArrayTest.java
index b55d4ba..4d98836 100644
--- a/android/guava-tests/test/com/google/common/primitives/ImmutableDoubleArrayTest.java
+++ b/android/guava-tests/test/com/google/common/primitives/ImmutableDoubleArrayTest.java
@@ -90,7 +90,8 @@
      * We don't guarantee the same-as property, so we aren't obligated to test it. However, it's
      * useful in testing - when two things are the same then one can't have bugs the other doesn't.
      */
-    assertThat(ImmutableDoubleArray.copyOf(new double[0])).isSameAs(ImmutableDoubleArray.of());
+    assertThat(ImmutableDoubleArray.copyOf(new double[0]))
+        .isSameInstanceAs(ImmutableDoubleArray.of());
   }
 
   public void testCopyOf_array_nonempty() {
@@ -102,7 +103,7 @@
 
   public void testCopyOf_iterable_notCollection_empty() {
     Iterable<Double> iterable = iterable(Collections.<Double>emptySet());
-    assertThat(ImmutableDoubleArray.copyOf(iterable)).isSameAs(ImmutableDoubleArray.of());
+    assertThat(ImmutableDoubleArray.copyOf(iterable)).isSameInstanceAs(ImmutableDoubleArray.of());
   }
 
   public void testCopyOf_iterable_notCollection_nonempty() {
@@ -114,7 +115,7 @@
 
   public void testCopyOf_iterable_collection_empty() {
     Iterable<Double> iterable = Collections.emptySet();
-    assertThat(ImmutableDoubleArray.copyOf(iterable)).isSameAs(ImmutableDoubleArray.of());
+    assertThat(ImmutableDoubleArray.copyOf(iterable)).isSameInstanceAs(ImmutableDoubleArray.of());
   }
 
   public void testCopyOf_iterable_collection_nonempty() {
@@ -126,7 +127,7 @@
 
   public void testCopyOf_collection_empty() {
     Collection<Double> iterable = Collections.emptySet();
-    assertThat(ImmutableDoubleArray.copyOf(iterable)).isSameAs(ImmutableDoubleArray.of());
+    assertThat(ImmutableDoubleArray.copyOf(iterable)).isSameInstanceAs(ImmutableDoubleArray.of());
   }
 
   public void testCopyOf_collection_nonempty() {
@@ -331,9 +332,9 @@
     ImmutableDoubleArray iia1 = ImmutableDoubleArray.of(5);
     ImmutableDoubleArray iia3 = ImmutableDoubleArray.of(5, 25, 125);
 
-    assertThat(iia0.subArray(0, 0)).isSameAs(ImmutableDoubleArray.of());
-    assertThat(iia1.subArray(0, 0)).isSameAs(ImmutableDoubleArray.of());
-    assertThat(iia1.subArray(1, 1)).isSameAs(ImmutableDoubleArray.of());
+    assertThat(iia0.subArray(0, 0)).isSameInstanceAs(ImmutableDoubleArray.of());
+    assertThat(iia1.subArray(0, 0)).isSameInstanceAs(ImmutableDoubleArray.of());
+    assertThat(iia1.subArray(1, 1)).isSameInstanceAs(ImmutableDoubleArray.of());
     assertThat(iia1.subArray(0, 1).asList()).containsExactly(5.0);
     assertThat(iia3.subArray(0, 2).asList()).containsExactly(5.0, 25.0).inOrder();
     assertThat(iia3.subArray(1, 3).asList()).containsExactly(25.0, 125.0).inOrder();
@@ -400,9 +401,9 @@
 
   @GwtIncompatible // SerializableTester
   public void testSerialization() {
-    assertThat(reserialize(ImmutableDoubleArray.of())).isSameAs(ImmutableDoubleArray.of());
+    assertThat(reserialize(ImmutableDoubleArray.of())).isSameInstanceAs(ImmutableDoubleArray.of());
     assertThat(reserialize(ImmutableDoubleArray.of(0, 1).subArray(1, 1)))
-        .isSameAs(ImmutableDoubleArray.of());
+        .isSameInstanceAs(ImmutableDoubleArray.of());
 
     ImmutableDoubleArray iia = ImmutableDoubleArray.of(0, 1, 3, 6).subArray(1, 3);
     ImmutableDoubleArray iia2 = reserialize(iia);
@@ -412,14 +413,14 @@
 
   private static void assertActuallyTrims(ImmutableDoubleArray iia) {
     ImmutableDoubleArray trimmed = iia.trimmed();
-    assertThat(trimmed).isNotSameAs(iia);
+    assertThat(trimmed).isNotSameInstanceAs(iia);
 
     // Yes, this is apparently how you check array equality in Truth
     assertThat(trimmed.toArray()).isEqualTo(iia.toArray());
   }
 
   private static void assertDoesntActuallyTrim(ImmutableDoubleArray iia) {
-    assertThat(iia.trimmed()).isSameAs(iia);
+    assertThat(iia.trimmed()).isSameInstanceAs(iia);
   }
 
   @GwtIncompatible // suite
diff --git a/android/guava-tests/test/com/google/common/primitives/ImmutableIntArrayTest.java b/android/guava-tests/test/com/google/common/primitives/ImmutableIntArrayTest.java
index 7fd7dea..86274d4 100644
--- a/android/guava-tests/test/com/google/common/primitives/ImmutableIntArrayTest.java
+++ b/android/guava-tests/test/com/google/common/primitives/ImmutableIntArrayTest.java
@@ -88,7 +88,7 @@
      * We don't guarantee the same-as property, so we aren't obligated to test it. However, it's
      * useful in testing - when two things are the same then one can't have bugs the other doesn't.
      */
-    assertThat(ImmutableIntArray.copyOf(new int[0])).isSameAs(ImmutableIntArray.of());
+    assertThat(ImmutableIntArray.copyOf(new int[0])).isSameInstanceAs(ImmutableIntArray.of());
   }
 
   public void testCopyOf_array_nonempty() {
@@ -100,7 +100,7 @@
 
   public void testCopyOf_iterable_notCollection_empty() {
     Iterable<Integer> iterable = iterable(Collections.<Integer>emptySet());
-    assertThat(ImmutableIntArray.copyOf(iterable)).isSameAs(ImmutableIntArray.of());
+    assertThat(ImmutableIntArray.copyOf(iterable)).isSameInstanceAs(ImmutableIntArray.of());
   }
 
   public void testCopyOf_iterable_notCollection_nonempty() {
@@ -112,7 +112,7 @@
 
   public void testCopyOf_iterable_collection_empty() {
     Iterable<Integer> iterable = Collections.emptySet();
-    assertThat(ImmutableIntArray.copyOf(iterable)).isSameAs(ImmutableIntArray.of());
+    assertThat(ImmutableIntArray.copyOf(iterable)).isSameInstanceAs(ImmutableIntArray.of());
   }
 
   public void testCopyOf_iterable_collection_nonempty() {
@@ -124,7 +124,7 @@
 
   public void testCopyOf_collection_empty() {
     Collection<Integer> iterable = Collections.emptySet();
-    assertThat(ImmutableIntArray.copyOf(iterable)).isSameAs(ImmutableIntArray.of());
+    assertThat(ImmutableIntArray.copyOf(iterable)).isSameInstanceAs(ImmutableIntArray.of());
   }
 
   public void testCopyOf_collection_nonempty() {
@@ -319,9 +319,9 @@
     ImmutableIntArray iia1 = ImmutableIntArray.of(5);
     ImmutableIntArray iia3 = ImmutableIntArray.of(5, 25, 125);
 
-    assertThat(iia0.subArray(0, 0)).isSameAs(ImmutableIntArray.of());
-    assertThat(iia1.subArray(0, 0)).isSameAs(ImmutableIntArray.of());
-    assertThat(iia1.subArray(1, 1)).isSameAs(ImmutableIntArray.of());
+    assertThat(iia0.subArray(0, 0)).isSameInstanceAs(ImmutableIntArray.of());
+    assertThat(iia1.subArray(0, 0)).isSameInstanceAs(ImmutableIntArray.of());
+    assertThat(iia1.subArray(1, 1)).isSameInstanceAs(ImmutableIntArray.of());
     assertThat(iia1.subArray(0, 1).asList()).containsExactly(5);
     assertThat(iia3.subArray(0, 2).asList()).containsExactly(5, 25).inOrder();
     assertThat(iia3.subArray(1, 3).asList()).containsExactly(25, 125).inOrder();
@@ -388,9 +388,9 @@
 
   @GwtIncompatible // SerializableTester
   public void testSerialization() {
-    assertThat(reserialize(ImmutableIntArray.of())).isSameAs(ImmutableIntArray.of());
+    assertThat(reserialize(ImmutableIntArray.of())).isSameInstanceAs(ImmutableIntArray.of());
     assertThat(reserialize(ImmutableIntArray.of(0, 1).subArray(1, 1)))
-        .isSameAs(ImmutableIntArray.of());
+        .isSameInstanceAs(ImmutableIntArray.of());
 
     ImmutableIntArray iia = ImmutableIntArray.of(0, 1, 3, 6).subArray(1, 3);
     ImmutableIntArray iia2 = reserialize(iia);
@@ -400,14 +400,14 @@
 
   private static void assertActuallyTrims(ImmutableIntArray iia) {
     ImmutableIntArray trimmed = iia.trimmed();
-    assertThat(trimmed).isNotSameAs(iia);
+    assertThat(trimmed).isNotSameInstanceAs(iia);
 
     // Yes, this is apparently how you check array equality in Truth
     assertThat(trimmed.toArray()).isEqualTo(iia.toArray());
   }
 
   private static void assertDoesntActuallyTrim(ImmutableIntArray iia) {
-    assertThat(iia.trimmed()).isSameAs(iia);
+    assertThat(iia.trimmed()).isSameInstanceAs(iia);
   }
 
   @GwtIncompatible // suite
diff --git a/android/guava-tests/test/com/google/common/primitives/ImmutableLongArrayTest.java b/android/guava-tests/test/com/google/common/primitives/ImmutableLongArrayTest.java
index e8813c1..ff879ec 100644
--- a/android/guava-tests/test/com/google/common/primitives/ImmutableLongArrayTest.java
+++ b/android/guava-tests/test/com/google/common/primitives/ImmutableLongArrayTest.java
@@ -90,7 +90,7 @@
      * We don't guarantee the same-as property, so we aren't obligated to test it. However, it's
      * useful in testing - when two things are the same then one can't have bugs the other doesn't.
      */
-    assertThat(ImmutableLongArray.copyOf(new long[0])).isSameAs(ImmutableLongArray.of());
+    assertThat(ImmutableLongArray.copyOf(new long[0])).isSameInstanceAs(ImmutableLongArray.of());
   }
 
   public void testCopyOf_array_nonempty() {
@@ -102,7 +102,7 @@
 
   public void testCopyOf_iterable_notCollection_empty() {
     Iterable<Long> iterable = iterable(Collections.<Long>emptySet());
-    assertThat(ImmutableLongArray.copyOf(iterable)).isSameAs(ImmutableLongArray.of());
+    assertThat(ImmutableLongArray.copyOf(iterable)).isSameInstanceAs(ImmutableLongArray.of());
   }
 
   public void testCopyOf_iterable_notCollection_nonempty() {
@@ -114,7 +114,7 @@
 
   public void testCopyOf_iterable_collection_empty() {
     Iterable<Long> iterable = Collections.emptySet();
-    assertThat(ImmutableLongArray.copyOf(iterable)).isSameAs(ImmutableLongArray.of());
+    assertThat(ImmutableLongArray.copyOf(iterable)).isSameInstanceAs(ImmutableLongArray.of());
   }
 
   public void testCopyOf_iterable_collection_nonempty() {
@@ -126,7 +126,7 @@
 
   public void testCopyOf_collection_empty() {
     Collection<Long> iterable = Collections.emptySet();
-    assertThat(ImmutableLongArray.copyOf(iterable)).isSameAs(ImmutableLongArray.of());
+    assertThat(ImmutableLongArray.copyOf(iterable)).isSameInstanceAs(ImmutableLongArray.of());
   }
 
   public void testCopyOf_collection_nonempty() {
@@ -321,9 +321,9 @@
     ImmutableLongArray iia1 = ImmutableLongArray.of(5);
     ImmutableLongArray iia3 = ImmutableLongArray.of(5, 25, 125);
 
-    assertThat(iia0.subArray(0, 0)).isSameAs(ImmutableLongArray.of());
-    assertThat(iia1.subArray(0, 0)).isSameAs(ImmutableLongArray.of());
-    assertThat(iia1.subArray(1, 1)).isSameAs(ImmutableLongArray.of());
+    assertThat(iia0.subArray(0, 0)).isSameInstanceAs(ImmutableLongArray.of());
+    assertThat(iia1.subArray(0, 0)).isSameInstanceAs(ImmutableLongArray.of());
+    assertThat(iia1.subArray(1, 1)).isSameInstanceAs(ImmutableLongArray.of());
     assertThat(iia1.subArray(0, 1).asList()).containsExactly(5L);
     assertThat(iia3.subArray(0, 2).asList()).containsExactly(5L, 25L).inOrder();
     assertThat(iia3.subArray(1, 3).asList()).containsExactly(25L, 125L).inOrder();
@@ -390,9 +390,9 @@
 
   @GwtIncompatible // SerializableTester
   public void testSerialization() {
-    assertThat(reserialize(ImmutableLongArray.of())).isSameAs(ImmutableLongArray.of());
+    assertThat(reserialize(ImmutableLongArray.of())).isSameInstanceAs(ImmutableLongArray.of());
     assertThat(reserialize(ImmutableLongArray.of(0, 1).subArray(1, 1)))
-        .isSameAs(ImmutableLongArray.of());
+        .isSameInstanceAs(ImmutableLongArray.of());
 
     ImmutableLongArray iia = ImmutableLongArray.of(0, 1, 3, 6).subArray(1, 3);
     ImmutableLongArray iia2 = reserialize(iia);
@@ -402,14 +402,14 @@
 
   private static void assertActuallyTrims(ImmutableLongArray iia) {
     ImmutableLongArray trimmed = iia.trimmed();
-    assertThat(trimmed).isNotSameAs(iia);
+    assertThat(trimmed).isNotSameInstanceAs(iia);
 
     // Yes, this is apparently how you check array equality in Truth
     assertThat(trimmed.toArray()).isEqualTo(iia.toArray());
   }
 
   private static void assertDoesntActuallyTrim(ImmutableLongArray iia) {
-    assertThat(iia.trimmed()).isSameAs(iia);
+    assertThat(iia.trimmed()).isSameInstanceAs(iia);
   }
 
   @GwtIncompatible // suite
diff --git a/android/guava-tests/test/com/google/common/primitives/IntsTest.java b/android/guava-tests/test/com/google/common/primitives/IntsTest.java
index 4487897..7215422 100644
--- a/android/guava-tests/test/com/google/common/primitives/IntsTest.java
+++ b/android/guava-tests/test/com/google/common/primitives/IntsTest.java
@@ -155,6 +155,7 @@
     assertEquals(3, Ints.lastIndexOf(new int[] {(int) 2, (int) 3, (int) 2, (int) 3}, (int) 3));
   }
 
+  @GwtIncompatible
   public void testMax_noArgs() {
     try {
       Ints.max();
@@ -169,6 +170,7 @@
     assertEquals((int) 9, Ints.max((int) 8, (int) 6, (int) 7, (int) 5, (int) 3, (int) 0, (int) 9));
   }
 
+  @GwtIncompatible
   public void testMin_noArgs() {
     try {
       Ints.min();
diff --git a/android/guava-tests/test/com/google/common/primitives/ShortsTest.java b/android/guava-tests/test/com/google/common/primitives/ShortsTest.java
index bc4d951..0816c69 100644
--- a/android/guava-tests/test/com/google/common/primitives/ShortsTest.java
+++ b/android/guava-tests/test/com/google/common/primitives/ShortsTest.java
@@ -175,6 +175,7 @@
         3, Shorts.lastIndexOf(new short[] {(short) 2, (short) 3, (short) 2, (short) 3}, (short) 3));
   }
 
+  @GwtIncompatible
   public void testMax_noArgs() {
     try {
       Shorts.max();
@@ -191,6 +192,7 @@
         Shorts.max((short) 8, (short) 6, (short) 7, (short) 5, (short) 3, (short) 0, (short) 9));
   }
 
+  @GwtIncompatible
   public void testMin_noArgs() {
     try {
       Shorts.min();
diff --git a/android/guava-tests/test/com/google/common/reflect/ClassPathTest.java b/android/guava-tests/test/com/google/common/reflect/ClassPathTest.java
index 58ebbbe..8bccae7 100644
--- a/android/guava-tests/test/com/google/common/reflect/ClassPathTest.java
+++ b/android/guava-tests/test/com/google/common/reflect/ClassPathTest.java
@@ -37,17 +37,14 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
-import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.security.Permission;
 import java.security.PermissionCollection;
-import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.jar.Attributes;
-import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import java.util.jar.JarOutputStream;
 import java.util.jar.Manifest;
@@ -59,15 +56,16 @@
 /** Functional tests of {@link ClassPath}. */
 public class ClassPathTest extends TestCase {
   private static final Logger log = Logger.getLogger(ClassPathTest.class.getName());
+  private static final File FILE = new File(".");
 
   public void testEquals() {
     new EqualsTester()
         .addEqualityGroup(classInfo(ClassPathTest.class), classInfo(ClassPathTest.class))
         .addEqualityGroup(classInfo(Test.class), classInfo(Test.class, getClass().getClassLoader()))
         .addEqualityGroup(
-            new ResourceInfo("a/b/c.txt", getClass().getClassLoader()),
-            new ResourceInfo("a/b/c.txt", getClass().getClassLoader()))
-        .addEqualityGroup(new ResourceInfo("x.txt", getClass().getClassLoader()))
+            new ResourceInfo(FILE, "a/b/c.txt", getClass().getClassLoader()),
+            new ResourceInfo(FILE, "a/b/c.txt", getClass().getClassLoader()))
+        .addEqualityGroup(new ResourceInfo(FILE, "x.txt", getClass().getClassLoader()))
         .testEquals();
   }
 
@@ -339,19 +337,20 @@
 
   public void testGetSimpleName() {
     ClassLoader classLoader = getClass().getClassLoader();
-    assertEquals("Foo", new ClassInfo("Foo.class", classLoader).getSimpleName());
-    assertEquals("Foo", new ClassInfo("a/b/Foo.class", classLoader).getSimpleName());
-    assertEquals("Foo", new ClassInfo("a/b/Bar$Foo.class", classLoader).getSimpleName());
-    assertEquals("", new ClassInfo("a/b/Bar$1.class", classLoader).getSimpleName());
-    assertEquals("Foo", new ClassInfo("a/b/Bar$Foo.class", classLoader).getSimpleName());
-    assertEquals("", new ClassInfo("a/b/Bar$1.class", classLoader).getSimpleName());
-    assertEquals("Local", new ClassInfo("a/b/Bar$1Local.class", classLoader).getSimpleName());
+    assertEquals("Foo", new ClassInfo(FILE, "Foo.class", classLoader).getSimpleName());
+    assertEquals("Foo", new ClassInfo(FILE, "a/b/Foo.class", classLoader).getSimpleName());
+    assertEquals("Foo", new ClassInfo(FILE, "a/b/Bar$Foo.class", classLoader).getSimpleName());
+    assertEquals("", new ClassInfo(FILE, "a/b/Bar$1.class", classLoader).getSimpleName());
+    assertEquals("Foo", new ClassInfo(FILE, "a/b/Bar$Foo.class", classLoader).getSimpleName());
+    assertEquals("", new ClassInfo(FILE, "a/b/Bar$1.class", classLoader).getSimpleName());
+    assertEquals("Local", new ClassInfo(FILE, "a/b/Bar$1Local.class", classLoader).getSimpleName());
   }
 
   public void testGetPackageName() {
-    assertEquals("", new ClassInfo("Foo.class", getClass().getClassLoader()).getPackageName());
     assertEquals(
-        "a.b", new ClassInfo("a/b/Foo.class", getClass().getClassLoader()).getPackageName());
+        "", new ClassInfo(FILE, "Foo.class", getClass().getClassLoader()).getPackageName());
+    assertEquals(
+        "a.b", new ClassInfo(FILE, "a/b/Foo.class", getClass().getClassLoader()).getPackageName());
   }
 
   // Test that ResourceInfo.urls() returns identical content to ClassLoader.getResources()
@@ -464,7 +463,7 @@
   private static ResourceInfo resourceInfo(Class<?> cls) {
     String resource = cls.getName().replace('.', '/') + ".class";
     ClassLoader loader = cls.getClassLoader();
-    return ResourceInfo.of(resource, loader);
+    return ResourceInfo.of(FILE, resource, loader);
   }
 
   private static ClassInfo classInfo(Class<?> cls) {
@@ -473,7 +472,7 @@
 
   private static ClassInfo classInfo(Class<?> cls, ClassLoader classLoader) {
     String resource = cls.getName().replace('.', '/') + ".class";
-    return new ClassInfo(resource, classLoader);
+    return new ClassInfo(FILE, resource, classLoader);
   }
 
   private static Manifest manifestClasspath(String classpath) throws IOException {
@@ -518,20 +517,8 @@
     final Set<String> resources = new HashSet<>();
 
     @Override
-    protected void scanDirectory(ClassLoader loader, File root) throws IOException {
-      URI base = root.toURI();
-      for (File entry : Files.fileTraverser().depthFirstPreOrder(root)) {
-        String resourceName = new File(base.relativize(entry.toURI()).getPath()).getPath();
-        resources.add(resourceName);
-      }
-    }
-
-    @Override
-    protected void scanJarFile(ClassLoader loader, JarFile file) throws IOException {
-      Enumeration<JarEntry> entries = file.entries();
-      while (entries.hasMoreElements()) {
-        resources.add(entries.nextElement().getName());
-      }
+    protected void scanResource(ResourceInfo resource) throws IOException {
+      resources.add(resource.getResourceName());
     }
   }
 
@@ -557,13 +544,13 @@
     }
 
     @Override
-    protected void scanJarFile(ClassLoader loader, JarFile file) throws IOException {
+    void scanJarFile(ClassLoader classloader, JarFile file) throws IOException {
       this.found = new File(file.getName());
       throw new StopScanningException();
     }
 
     @Override
-    protected void scanDirectory(ClassLoader loader, File root) {}
+    protected void scanResource(ResourceInfo resource) {}
 
     // Special exception just to terminate the scanning when we get any jar file to use.
     private static final class StopScanningException extends RuntimeException {}
diff --git a/android/guava-tests/test/com/google/common/reflect/SubtypeTester.java b/android/guava-tests/test/com/google/common/reflect/SubtypeTester.java
index dcf9626..3eec668 100644
--- a/android/guava-tests/test/com/google/common/reflect/SubtypeTester.java
+++ b/android/guava-tests/test/com/google/common/reflect/SubtypeTester.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import com.google.errorprone.annotations.RequiredModifiers;
 import java.lang.annotation.ElementType;
@@ -84,11 +85,11 @@
     Type returnType = method.getGenericReturnType();
     Type paramType = getOnlyParameterType();
     TestSubtype spec = method.getAnnotation(TestSubtype.class);
-    assertThat(TypeToken.of(paramType).isSubtypeOf(returnType))
-        .named("%s is subtype of %s", paramType, returnType)
+    assertWithMessage("%s is subtype of %s", paramType, returnType)
+        .that(TypeToken.of(paramType).isSubtypeOf(returnType))
         .isTrue();
-    assertThat(TypeToken.of(returnType).isSupertypeOf(paramType))
-        .named("%s is supertype of %s", returnType, paramType)
+    assertWithMessage("%s is supertype of %s", returnType, paramType)
+        .that(TypeToken.of(returnType).isSupertypeOf(paramType))
         .isTrue();
     if (!spec.suppressGetSubtype()) {
       assertThat(getSubtype(returnType, TypeToken.of(paramType).getRawType())).isEqualTo(paramType);
@@ -108,11 +109,11 @@
     Type returnType = method.getGenericReturnType();
     Type paramType = getOnlyParameterType();
     TestSubtype spec = method.getAnnotation(TestSubtype.class);
-    assertThat(TypeToken.of(paramType).isSubtypeOf(returnType))
-        .named("%s is subtype of %s", paramType, returnType)
+    assertWithMessage("%s is subtype of %s", paramType, returnType)
+        .that(TypeToken.of(paramType).isSubtypeOf(returnType))
         .isFalse();
-    assertThat(TypeToken.of(returnType).isSupertypeOf(paramType))
-        .named("%s is supertype of %s", returnType, paramType)
+    assertWithMessage("%s is supertype of %s", returnType, paramType)
+        .that(TypeToken.of(returnType).isSupertypeOf(paramType))
         .isFalse();
     if (!spec.suppressGetSubtype()) {
       try {
diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractAbstractFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractAbstractFutureTest.java
index c7474f2..f49b962 100644
--- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractAbstractFutureTest.java
+++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractAbstractFutureTest.java
@@ -462,14 +462,14 @@
       getDone(future);
       fail();
     } catch (ExecutionException e) {
-      assertThat(e.getCause()).isSameAs(expectedException);
+      assertThat(e.getCause()).isSameInstanceAs(expectedException);
     }
 
     try {
       getDoneFromTimeoutOverload(future);
       fail();
     } catch (ExecutionException e) {
-      assertThat(e).hasCauseThat().isSameAs(expectedException);
+      assertThat(e).hasCauseThat().isSameInstanceAs(expectedException);
     }
   }
 
diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureFallbackAtomicHelperTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureFallbackAtomicHelperTest.java
index d075f80..77d04ff 100644
--- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureFallbackAtomicHelperTest.java
+++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureFallbackAtomicHelperTest.java
@@ -39,7 +39,7 @@
  * </ul>
  *
  * To force selection of our fallback strategies we load {@link AbstractFuture} (and all of {@code
- * com.google.common.util.concurrent} in degenerate class loaders which make certain platform
+ * com.google.common.util.concurrent}) in degenerate class loaders which make certain platform
  * classes unavailable. Then we construct a test suite so we can run the normal AbstractFutureTest
  * test methods in these degenerate classloaders.
  */
diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java
index 368e2d4..3c210ce 100644
--- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java
+++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java
@@ -19,11 +19,13 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import com.google.common.annotations.GwtIncompatible;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Range;
 import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.internal.InternalFutureFailureAccess;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
@@ -78,8 +80,8 @@
     // Ensure we get a unique execution exception on each get
     assertNotSame(ee1, ee2);
 
-    assertThat(ee1).hasCauseThat().isSameAs(failure);
-    assertThat(ee2).hasCauseThat().isSameAs(failure);
+    assertThat(ee1).hasCauseThat().isSameInstanceAs(failure);
+    assertThat(ee2).hasCauseThat().isSameInstanceAs(failure);
 
     checkStackTrace(ee1);
     checkStackTrace(ee2);
@@ -155,7 +157,7 @@
       normalFuture.get();
       fail();
     } catch (ExecutionException e) {
-      assertThat(e).hasCauseThat().isSameAs(exception);
+      assertThat(e).hasCauseThat().isSameInstanceAs(exception);
     }
   }
 
@@ -212,6 +214,44 @@
     assertThat(SettableFuture.create().toString()).isNotEqualTo(SettableFuture.create().toString());
   }
 
+  public void testToString_oom() throws Exception {
+    SettableFuture<Object> future = SettableFuture.create();
+    future.set(
+        new Object() {
+          @Override
+          public String toString() {
+            throw new OutOfMemoryError();
+          }
+
+          @Override
+          public int hashCode() {
+            throw new OutOfMemoryError();
+          }
+        });
+
+    String unused = future.toString();
+
+    SettableFuture<Object> future2 = SettableFuture.create();
+
+    // A more organic OOM from a toString implementation
+    Object object =
+        new Object() {
+          @Override
+          public String toString() {
+            return new String(new char[50_000]);
+          }
+        };
+    List<Object> list = Collections.singletonList(object);
+    for (int i = 0; i < 10; i++) {
+      Object[] array = new Object[500];
+      Arrays.fill(array, list);
+      list = Arrays.asList(array);
+    }
+    future2.set(list);
+
+    unused = future.toString();
+  }
+
   public void testToString_notDone() throws Exception {
     AbstractFuture<Object> testFuture =
         new AbstractFuture<Object>() {
@@ -232,6 +272,20 @@
     }
   }
 
+  public void testToString_completesDuringToString() throws Exception {
+    AbstractFuture<Object> testFuture =
+        new AbstractFuture<Object>() {
+          @Override
+          public String pendingToString() {
+            // Complete ourselves during the toString calculation
+            this.set(true);
+            return "cause=[Because this test isn't done]";
+          }
+        };
+    assertThat(testFuture.toString())
+        .matches("[^\\[]+\\[status=SUCCESS, result=\\[java.lang.Boolean@\\w+\\]\\]");
+  }
+
   /**
    * This test attempts to cause a future to wait for longer than it was requested to from a timed
    * get() call. As measurements of time are prone to flakiness, it tries to assert based on ranges
@@ -290,11 +344,11 @@
     testFuture3.setFuture(testFuture2);
     assertThat(testFuture3.toString())
         .matches(
-            "[^\\[]+\\[status=PENDING, info=\\[setFuture="
-                + "\\[[^\\[]+\\[status=PENDING, info=\\[cause=\\[Someday...\\]\\]\\]\\]\\]\\]");
+            "[^\\[]+\\[status=PENDING, setFuture=\\[[^\\[]+\\[status=PENDING,"
+                + " info=\\[cause=\\[Someday...]]]]]");
     testFuture2.set("result string");
     assertThat(testFuture3.toString())
-        .matches("[^\\[]+\\[status=SUCCESS, result=\\[result string\\]\\]");
+        .matches("[^\\[]+\\[status=SUCCESS, result=\\[java.lang.String@\\w+\\]\\]");
   }
 
   public void testToString_cancelled() throws Exception {
@@ -381,7 +435,7 @@
     final ExecutorService executor = Executors.newFixedThreadPool(barrier.getParties());
     final AtomicReference<AbstractFuture<String>> currentFuture = Atomics.newReference();
     final AtomicInteger numSuccessfulSetCalls = new AtomicInteger();
-    Callable<Void> completeSucessFullyRunnable =
+    Callable<Void> completeSuccessfullyRunnable =
         new Callable<Void>() {
           @Override
           public Void call() {
@@ -416,7 +470,7 @@
             return null;
           }
         };
-    Callable<Void> setFutureCompleteSucessFullyRunnable =
+    Callable<Void> setFutureCompleteSuccessfullyRunnable =
         new Callable<Void>() {
           ListenableFuture<String> future = Futures.immediateFuture("setFuture");
 
@@ -497,10 +551,10 @@
           }
         };
     List<Callable<?>> allTasks = new ArrayList<>();
-    allTasks.add(completeSucessFullyRunnable);
+    allTasks.add(completeSuccessfullyRunnable);
     allTasks.add(completeExceptionallyRunnable);
     allTasks.add(cancelRunnable);
-    allTasks.add(setFutureCompleteSucessFullyRunnable);
+    allTasks.add(setFutureCompleteSuccessfullyRunnable);
     allTasks.add(setFutureCompleteExceptionallyRunnable);
     allTasks.add(setFutureCancelRunnable);
     for (int k = 0; k < 50; k++) {
@@ -563,24 +617,24 @@
     final ExecutorService executor = Executors.newFixedThreadPool(barrier.getParties());
     final AtomicReference<AbstractFuture<String>> currentFuture = Atomics.newReference();
     final AtomicReference<AbstractFuture<String>> setFutureFuture = Atomics.newReference();
-    final AtomicBoolean setFutureSetSucess = new AtomicBoolean();
-    final AtomicBoolean setFutureCompletionSucess = new AtomicBoolean();
-    final AtomicBoolean cancellationSucess = new AtomicBoolean();
+    final AtomicBoolean setFutureSetSuccess = new AtomicBoolean();
+    final AtomicBoolean setFutureCompletionSuccess = new AtomicBoolean();
+    final AtomicBoolean cancellationSuccess = new AtomicBoolean();
     Runnable cancelRunnable =
         new Runnable() {
           @Override
           public void run() {
-            cancellationSucess.set(currentFuture.get().cancel(true));
+            cancellationSuccess.set(currentFuture.get().cancel(true));
             awaitUnchecked(barrier);
           }
         };
-    Runnable setFutureCompleteSucessFullyRunnable =
+    Runnable setFutureCompleteSuccessfullyRunnable =
         new Runnable() {
           @Override
           public void run() {
             AbstractFuture<String> future = setFutureFuture.get();
-            setFutureSetSucess.set(currentFuture.get().setFuture(future));
-            setFutureCompletionSucess.set(future.set("hello-async-world"));
+            setFutureSetSuccess.set(currentFuture.get().setFuture(future));
+            setFutureCompletionSuccess.set(future.set("hello-async-world"));
             awaitUnchecked(barrier);
           }
         };
@@ -626,7 +680,7 @@
         };
     List<Runnable> allTasks = new ArrayList<>();
     allTasks.add(cancelRunnable);
-    allTasks.add(setFutureCompleteSucessFullyRunnable);
+    allTasks.add(setFutureCompleteSuccessfullyRunnable);
     for (int k = 0; k < size; k++) {
       // For each listener we add a task that submits it to the executor directly for the blocking
       // get usecase and another task that adds it as a listener to the future to exercise both
@@ -659,12 +713,12 @@
       Object result = Iterables.getOnlyElement(finalResults);
       if (result == CancellationException.class) {
         assertTrue(future.isCancelled());
-        assertTrue(cancellationSucess.get());
+        assertTrue(cancellationSuccess.get());
         // cancellation can interleave in 3 ways
         // 1. prior to setFuture
         // 2. after setFuture before set() on the future assigned
         // 3. after setFuture and set() are called but before the listener completes.
-        if (!setFutureSetSucess.get() || !setFutureCompletionSucess.get()) {
+        if (!setFutureSetSuccess.get() || !setFutureCompletionSuccess.get()) {
           // If setFuture fails or set on the future fails then it must be because that future was
           // cancelled
           assertTrue(setFuture.isCancelled());
@@ -672,14 +726,14 @@
         }
       } else {
         // set on the future completed
-        assertFalse(cancellationSucess.get());
-        assertTrue(setFutureSetSucess.get());
-        assertTrue(setFutureCompletionSucess.get());
+        assertFalse(cancellationSuccess.get());
+        assertTrue(setFutureSetSuccess.get());
+        assertTrue(setFutureCompletionSuccess.get());
       }
       // reset for next iteration
-      setFutureSetSucess.set(false);
-      setFutureCompletionSucess.set(false);
-      cancellationSucess.set(false);
+      setFutureSetSuccess.set(false);
+      setFutureCompletionSuccess.set(false);
+      cancellationSuccess.set(false);
       finalResults.clear();
     }
     executor.shutdown();
@@ -696,17 +750,17 @@
     final ExecutorService executor = Executors.newFixedThreadPool(barrier.getParties());
     final AtomicReference<AbstractFuture<String>> currentFuture = Atomics.newReference();
     final AtomicBoolean setFutureSuccess = new AtomicBoolean();
-    final AtomicBoolean cancellationSucess = new AtomicBoolean();
+    final AtomicBoolean cancellationSuccess = new AtomicBoolean();
     Callable<Void> cancelRunnable =
         new Callable<Void>() {
           @Override
           public Void call() {
-            cancellationSucess.set(currentFuture.get().cancel(true));
+            cancellationSuccess.set(currentFuture.get().cancel(true));
             awaitUnchecked(barrier);
             return null;
           }
         };
-    Callable<Void> setFutureCompleteSucessFullyRunnable =
+    Callable<Void> setFutureCompleteSuccessfullyRunnable =
         new Callable<Void>() {
           final ListenableFuture<String> future = Futures.immediateFuture("hello");
 
@@ -736,7 +790,7 @@
         };
     List<Callable<?>> allTasks = new ArrayList<>();
     allTasks.add(cancelRunnable);
-    allTasks.add(setFutureCompleteSucessFullyRunnable);
+    allTasks.add(setFutureCompleteSuccessfullyRunnable);
     allTasks.add(Executors.callable(collectResultsRunnable));
     assertEquals(allTasks.size() + 1, barrier.getParties()); // sanity check
     for (int i = 0; i < 1000; i++) {
@@ -754,15 +808,15 @@
       Object result = Iterables.getOnlyElement(finalResults);
       if (result == CancellationException.class) {
         assertTrue(future.isCancelled());
-        assertTrue(cancellationSucess.get());
+        assertTrue(cancellationSuccess.get());
         assertFalse(setFutureSuccess.get());
       } else {
         assertTrue(setFutureSuccess.get());
-        assertFalse(cancellationSucess.get());
+        assertFalse(cancellationSuccess.get());
       }
       // reset for next iteration
       setFutureSuccess.set(false);
-      cancellationSucess.set(false);
+      cancellationSuccess.set(false);
       finalResults.clear();
     }
     executor.shutdown();
@@ -783,6 +837,23 @@
     assertTrue(orig.isDone());
   }
 
+  // Verify that StackOverflowError in a long chain of SetFuture doesn't cause the entire toString
+  // call to fail
+  @GwtIncompatible
+  @AndroidIncompatible
+  public void testSetFutureToString_stackOverflow() {
+    SettableFuture<String> orig = SettableFuture.create();
+    SettableFuture<String> prev = orig;
+    for (int i = 0; i < 100000; i++) {
+      SettableFuture<String> curr = SettableFuture.create();
+      prev.setFuture(curr);
+      prev = curr;
+    }
+    // orig represents the 'outermost' future
+    assertThat(orig.toString())
+        .contains("Exception thrown from implementation: class java.lang.StackOverflowError");
+  }
+
   public void testSetFuture_misbehavingFutureThrows() throws Exception {
     SettableFuture<String> future = SettableFuture.create();
     ListenableFuture<String> badFuture =
@@ -886,7 +957,7 @@
   public void testSetFutureSelf_toString() {
     SettableFuture<String> orig = SettableFuture.create();
     orig.setFuture(orig);
-    assertThat(orig.toString()).contains("[status=PENDING, info=[setFuture=[this future]]]");
+    assertThat(orig.toString()).contains("[status=PENDING, setFuture=[this future]]");
   }
 
   public void testSetSelf_toString() {
@@ -895,21 +966,33 @@
     assertThat(orig.toString()).contains("[status=SUCCESS, result=[this future]]");
   }
 
+  public void testSetFutureSelf_toStringException() {
+    SettableFuture<String> orig = SettableFuture.create();
+    orig.setFuture(
+        new AbstractFuture<String>() {
+          @Override
+          public String toString() {
+            throw new NullPointerException();
+          }
+        });
+    assertThat(orig.toString())
+        .contains(
+            "[status=PENDING, setFuture=[Exception thrown from implementation: class"
+                + " java.lang.NullPointerException]]");
+  }
+
   public void testSetIndirectSelf_toString() {
     final SettableFuture<Object> orig = SettableFuture.create();
     // unlike the above this indirection defeats the trivial cycle detection and causes a SOE
-    orig.set(
-        new Object() {
+    orig.setFuture(
+        new ForwardingListenableFuture<Object>() {
           @Override
-          public String toString() {
-            return orig.toString();
+          protected ListenableFuture<Object> delegate() {
+            return orig;
           }
         });
-    try {
-      orig.toString();
-      fail();
-    } catch (StackOverflowError expected) {
-    }
+    assertThat(orig.toString())
+        .contains("Exception thrown from implementation: class java.lang.StackOverflowError");
   }
 
   // Regression test for a case where we would fail to execute listeners immediately on done futures
diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractIdleServiceTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractIdleServiceTest.java
index 74f5d7c..7cad8b0 100644
--- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractIdleServiceTest.java
+++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractIdleServiceTest.java
@@ -68,7 +68,7 @@
         service.startAsync().awaitRunning();
         fail();
       } catch (RuntimeException e) {
-        assertThat(e).hasCauseThat().isSameAs(exception);
+        assertThat(e).hasCauseThat().isSameInstanceAs(exception);
       }
       assertEquals(Service.State.FAILED, service.state());
     }
@@ -87,7 +87,7 @@
         service.stopAsync().awaitTerminated();
         fail();
       } catch (RuntimeException e) {
-        assertThat(e).hasCauseThat().isSameAs(exception);
+        assertThat(e).hasCauseThat().isSameInstanceAs(exception);
       }
       assertEquals(Service.State.FAILED, service.state());
     }
@@ -117,7 +117,7 @@
       service.startAsync().awaitRunning();
       fail();
     } catch (RuntimeException e) {
-      assertThat(e).hasCauseThat().isSameAs(exception);
+      assertThat(e).hasCauseThat().isSameInstanceAs(exception);
     }
     assertEquals(1, service.startUpCalled);
     assertEquals(Service.State.FAILED, service.state());
@@ -164,7 +164,7 @@
       service.stopAsync().awaitTerminated();
       fail();
     } catch (RuntimeException e) {
-      assertThat(e).hasCauseThat().isSameAs(exception);
+      assertThat(e).hasCauseThat().isSameInstanceAs(exception);
     }
     assertEquals(1, service.startUpCalled);
     assertEquals(1, service.shutDownCalled);
diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractScheduledServiceTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractScheduledServiceTest.java
index 8e7cde9..16a4cd0 100644
--- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractScheduledServiceTest.java
+++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractScheduledServiceTest.java
@@ -98,7 +98,7 @@
       fail();
     } catch (CancellationException expected) {
     }
-    // An execution exception holds a runtime exception (from throwables.propogate) that holds our
+    // An execution exception holds a runtime exception (from throwables.propagate) that holds our
     // original exception.
     assertEquals(service.runException, service.failureCause());
     assertEquals(Service.State.FAILED, service.state());
diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractServiceTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractServiceTest.java
index faac76e..5f42106 100644
--- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractServiceTest.java
+++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractServiceTest.java
@@ -42,7 +42,7 @@
  */
 public class AbstractServiceTest extends TestCase {
 
-  private static final long LONG_TIMEOUT_MILLIS = 2500;
+  private static final long LONG_TIMEOUT_MILLIS = 10000;
   private Thread executionThread;
   private Throwable thrownByExecutionThread;
 
@@ -234,21 +234,21 @@
    */
   public void testManualServiceStopMultipleTimesWhileStarting() throws Exception {
     ManualSwitchedService service = new ManualSwitchedService();
-    final AtomicInteger stopppingCount = new AtomicInteger();
+    final AtomicInteger stoppingCount = new AtomicInteger();
     service.addListener(
         new Listener() {
           @Override
           public void stopping(State from) {
-            stopppingCount.incrementAndGet();
+            stoppingCount.incrementAndGet();
           }
         },
         directExecutor());
 
     service.startAsync();
     service.stopAsync();
-    assertEquals(1, stopppingCount.get());
+    assertEquals(1, stoppingCount.get());
     service.stopAsync();
-    assertEquals(1, stopppingCount.get());
+    assertEquals(1, stoppingCount.get());
   }
 
   public void testManualServiceStopWhileNew() throws Exception {
diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AtomicLongMapBasherTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AtomicLongMapBasherTest.java
new file mode 100644
index 0000000..d905dcd
--- /dev/null
+++ b/android/guava-tests/test/com/google/common/util/concurrent/AtomicLongMapBasherTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2011 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 com.google.common.util.concurrent;
+
+import com.google.common.annotations.GwtIncompatible;
+import java.util.Random;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import junit.framework.TestCase;
+
+/**
+ * Basher test for {@link AtomicLongMap}.
+ *
+ * @author mike nonemacher
+ */
+@GwtIncompatible // threads
+
+public class AtomicLongMapBasherTest extends TestCase {
+  private final Random random = new Random(301);
+
+  public void testModify_basher() throws InterruptedException {
+    int nTasks = 3000;
+    int nThreads = 100;
+    final int getsPerTask = 1000;
+    final int deltaRange = 10000;
+    final String key = "key";
+
+    final AtomicLong sum = new AtomicLong();
+    final AtomicLongMap<String> map = AtomicLongMap.create();
+
+    ExecutorService threadPool = Executors.newFixedThreadPool(nThreads);
+    for (int i = 0; i < nTasks; i++) {
+      @SuppressWarnings("unused") // go/futurereturn-lsc
+      Future<?> possiblyIgnoredError =
+          threadPool.submit(
+              new Runnable() {
+                @Override
+                public void run() {
+                  int threadSum = 0;
+                  for (int j = 0; j < getsPerTask; j++) {
+                    long delta = random.nextInt(deltaRange);
+                    int behavior = random.nextInt(10);
+