@CanIgnoreReturnValue
  public final Ordered containsExactlyEntriesIn(Multimap<?, ?> expectedMultimap) {
    checkNotNull(expectedMultimap, "expectedMultimap");
    checkNotNull(actual);
    ListMultimap<?, ?> missing = difference(expectedMultimap, actual);
    ListMultimap<?, ?> extra = difference(actual, expectedMultimap);

    // TODO(kak): Possible enhancement: Include "[1 copy]" if the element does appear in
    // the subject but not enough times. Similarly for unexpected extra items.
    if (!missing.isEmpty()) {
      if (!extra.isEmpty()) {
        boolean addTypeInfo = hasMatchingToStringPair(missing.entries(), extra.entries());
        // Note: The usage of countDuplicatesAndAddTypeInfo() below causes entries no longer to be
        // grouped by key in the 'missing' and 'unexpected items' parts of the message (we still
        // show the actual and expected multimaps in the standard format).
        String missingDisplay =
            addTypeInfo
                ? countDuplicatesAndAddTypeInfo(annotateEmptyStringsMultimap(missing).entries())
                : countDuplicatesMultimap(annotateEmptyStringsMultimap(missing));
        String extraDisplay =
            addTypeInfo
                ? countDuplicatesAndAddTypeInfo(annotateEmptyStringsMultimap(extra).entries())
                : countDuplicatesMultimap(annotateEmptyStringsMultimap(extra));
        failWithActual(
            fact("missing", missingDisplay),
            fact("unexpected", extraDisplay),
            simpleFact("---"),
            fact("expected", annotateEmptyStringsMultimap(expectedMultimap)));
        return ALREADY_FAILED;
      } else {
        failWithActual(
            fact("missing", countDuplicatesMultimap(annotateEmptyStringsMultimap(missing))),
            simpleFact("---"),
            fact("expected", annotateEmptyStringsMultimap(expectedMultimap)));
        return ALREADY_FAILED;
      }
    } else if (!extra.isEmpty()) {
      failWithActual(
          fact("unexpected", countDuplicatesMultimap(annotateEmptyStringsMultimap(extra))),
          simpleFact("---"),
          fact("expected", annotateEmptyStringsMultimap(expectedMultimap)));
      return ALREADY_FAILED;
    }

    return new MultimapInOrder(/* allowUnexpected = */ false, expectedMultimap);
  }
@Test
  public void containsExactlyEntriesIn_homogeneousMultimap_failsWithSameToString()
      throws Exception {
    expectFailureWhenTestingThat(ImmutableMultimap.of(1, "a", 1, "b", 2, "c"))
        .containsExactlyEntriesIn(ImmutableMultimap.of(1L, "a", 1L, "b", 2L, "c"));
    assertFailureKeys("missing", "unexpected", "---", "expected", "but was");
    assertFailureValue("missing", "[1=a, 1=b, 2=c] (Map.Entry<java.lang.Long, java.lang.String>)");
    assertFailureValue(
        "unexpected", "[1=a, 1=b, 2=c] (Map.Entry<java.lang.Integer, java.lang.String>)");
  }