2929import static java .util .Collections .singletonList ;
3030
3131import com .google .common .collect .ImmutableList ;
32- import com .google .common .collect .ImmutableMap ;
3332import com .google .common .collect .LinkedHashMultiset ;
3433import com .google .common .collect .Maps ;
3534import com .google .common .collect .Multiset ;
@@ -78,46 +77,70 @@ public final void isEqualTo(@Nullable Object expected) {
7877 return ;
7978 }
8079
81- containsEntriesInAnyOrder ((Map <?, ?>) expected , /* allowUnexpected= */ false );
80+ containsEntriesInAnyOrder (actual , (Map <?, ?>) expected , /* allowUnexpected= */ false );
8281 }
8382
8483 /** Checks that the actual map is empty. */
8584 public final void isEmpty () {
86- if (!checkNotNull (actual ).isEmpty ()) {
85+ if (actual == null ) {
86+ failWithActual (simpleFact ("expected an empty map" ));
87+ } else if (!actual .isEmpty ()) {
8788 failWithActual (simpleFact ("expected to be empty" ));
8889 }
8990 }
9091
9192 /** Checks that the actual map is not empty. */
9293 public final void isNotEmpty () {
93- if (checkNotNull (actual ).isEmpty ()) {
94+ if (actual == null ) {
95+ failWithActual (simpleFact ("expected a nonempty map" ));
96+ } else if (actual .isEmpty ()) {
9497 failWithoutActual (simpleFact ("expected not to be empty" ));
9598 }
9699 }
97100
98101 /** Checks that the actual map has the given size. */
99102 public final void hasSize (int size ) {
100- checkArgument (size >= 0 , "expected size (%s) must be >= 0" , size );
101- check ("size()" ).that (checkNotNull (actual ).size ()).isEqualTo (size );
103+ if (actual == null ) {
104+ failWithActual ("expected a map with size" , size );
105+ } else if (size < 0 ) {
106+ failWithoutActual (
107+ simpleFact ("expected a map with a negative size, but that is impossible" ),
108+ fact ("expected size" , size ),
109+ fact ("actual size" , actual .size ()),
110+ actualContents ());
111+ } else {
112+ check ("size()" ).that (actual .size ()).isEqualTo (size );
113+ }
102114 }
103115
104116 /** Checks that the actual map contains the given key. */
105117 public final void containsKey (@ Nullable Object key ) {
106- check ("keySet()" ).that (checkNotNull (actual ).keySet ()).contains (key );
118+ if (actual == null ) {
119+ failWithActual ("expected a map that contains key" , key );
120+ return ;
121+ }
122+ check ("keySet()" ).that (actual .keySet ()).contains (key );
107123 }
108124
109125 /** Checks that the actual map does not contain the given key. */
110126 public final void doesNotContainKey (@ Nullable Object key ) {
111- check ("keySet()" ).that (checkNotNull (actual ).keySet ()).doesNotContain (key );
127+ if (actual == null ) {
128+ failWithActual ("expected a map that does not contain key" , key );
129+ return ;
130+ }
131+ check ("keySet()" ).that (actual .keySet ()).doesNotContain (key );
112132 }
113133
114134 /** Checks that the actual map contains the given entry. */
115135 public final void containsEntry (@ Nullable Object key , @ Nullable Object value ) {
116136 Entry <?, ?> entry = immutableEntry (key , value );
117- checkNotNull (actual );
137+ if (actual == null ) {
138+ failWithActual ("expected a map that contains entry" , entry );
139+ return ;
140+ }
118141 if (!actual .entrySet ().contains (entry )) {
119- List <@ Nullable Object > keyList = singletonList (key );
120- List <@ Nullable Object > valueList = singletonList (value );
142+ List <? > keyList = singletonList (key );
143+ List <? > valueList = singletonList (value );
121144 if (actual .containsKey (key )) {
122145 Object actualValue = actual .get (key );
123146 if (Objects .equals (actualValue , value )) {
@@ -185,15 +208,22 @@ public final void containsEntry(@Nullable Object key, @Nullable Object value) {
185208
186209 /** Checks that the actual map does not contain the given entry. */
187210 public final void doesNotContainEntry (@ Nullable Object key , @ Nullable Object value ) {
188- checkNoNeedToDisplayBothValues ("entrySet()" )
189- .that (checkNotNull (actual ).entrySet ())
190- .doesNotContain (immutableEntry (key , value ));
211+ Entry <?, ?> entry = immutableEntry (key , value );
212+ if (actual == null ) {
213+ failWithActual ("expected a map that does not contain entry" , entry );
214+ return ;
215+ }
216+ checkNoNeedToDisplayBothValues ("entrySet()" ).that (actual .entrySet ()).doesNotContain (entry );
191217 }
192218
193219 /** Checks that the actual map is empty. */
194220 @ CanIgnoreReturnValue
195221 public final Ordered containsExactly () {
196- return containsExactlyEntriesIn (ImmutableMap .of ());
222+ isEmpty ();
223+ // If the actual value was empty, then it is vacuously in order, so we'd return IN_ORDER.
224+ // If the actual value not empty (or was null), then we'd return ALREADY_FAILED.
225+ // Luckily, the two are equivalent, so it doesn't matter which we pick.
226+ return IN_ORDER ;
197227 }
198228
199229 /**
@@ -243,36 +273,49 @@ public final Ordered containsAtLeast(
243273
244274 /** Checks that the actual map contains exactly the given set of entries in the given map. */
245275 @ CanIgnoreReturnValue
246- public final Ordered containsExactlyEntriesIn (Map <?, ?> expected ) {
247- if (expected .isEmpty ()) {
248- if (checkNotNull (actual ).isEmpty ()) {
249- return IN_ORDER ;
250- } else {
251- isEmpty (); // fails
252- return ALREADY_FAILED ;
253- }
276+ public final Ordered containsExactlyEntriesIn (@ Nullable Map <?, ?> expected ) {
277+ if (expected == null ) {
278+ failWithoutActual (
279+ simpleFact ("could not perform containment check because expected map was null" ),
280+ actualContents ());
281+ return ALREADY_FAILED ;
282+ } else if (actual == null ) {
283+ failWithActual ("expected a map that contains exactly" , expected );
284+ return ALREADY_FAILED ;
285+ } else if (expected .isEmpty ()) {
286+ return containsExactly ();
254287 }
255- return containsEntriesInAnyOrder (expected , /* allowUnexpected= */ false )
288+ return containsEntriesInAnyOrder (actual , expected , /* allowUnexpected= */ false )
256289 ? MapInOrder .create (
257- this , expected , /* allowUnexpected= */ false , /* correspondence= */ null )
290+ this , actual , expected , /* allowUnexpected= */ false , /* correspondence= */ null )
258291 : ALREADY_FAILED ;
259292 }
260293
261294 /** Checks that the actual map contains at least the given set of entries in the given map. */
262295 @ CanIgnoreReturnValue
263- public final Ordered containsAtLeastEntriesIn (Map <?, ?> expected ) {
264- if (expected .isEmpty ()) {
296+ public final Ordered containsAtLeastEntriesIn (@ Nullable Map <?, ?> expected ) {
297+ if (expected == null ) {
298+ failWithoutActual (
299+ simpleFact ("could not perform containment check because expected map was null" ),
300+ actualContents ());
301+ return ALREADY_FAILED ;
302+ } else if (actual == null ) {
303+ failWithActual ("expected a map that contains at least" , expected );
304+ return ALREADY_FAILED ;
305+ } else if (expected .isEmpty ()) {
265306 return IN_ORDER ;
266307 }
267- return containsEntriesInAnyOrder (expected , /* allowUnexpected= */ true )
268- ? MapInOrder .create (this , expected , /* allowUnexpected= */ true , /* correspondence= */ null )
308+ return containsEntriesInAnyOrder (actual , expected , /* allowUnexpected= */ true )
309+ ? MapInOrder .create (
310+ this , actual , expected , /* allowUnexpected= */ true , /* correspondence= */ null )
269311 : ALREADY_FAILED ;
270312 }
271313
272314 @ CanIgnoreReturnValue
273- private boolean containsEntriesInAnyOrder (Map <?, ?> expected , boolean allowUnexpected ) {
315+ private boolean containsEntriesInAnyOrder (
316+ Map <?, ?> actual , Map <?, ?> expected , boolean allowUnexpected ) {
274317 MapDifference <@ Nullable Object , @ Nullable Object , @ Nullable Object > diff =
275- MapDifference .create (checkNotNull ( actual ) , expected , allowUnexpected , Objects ::equals );
318+ MapDifference .create (actual , expected , allowUnexpected , Objects ::equals );
276319 if (diff .isEmpty ()) {
277320 return true ;
278321 }
@@ -438,16 +481,19 @@ private static String maybeAddType(@Nullable Object o, boolean includeTypes) {
438481
439482 private static final class MapInOrder implements Ordered {
440483 private final MapSubject subject ;
484+ private final Map <?, ?> actual ;
441485 private final Map <?, ?> expectedMap ;
442486 private final boolean allowUnexpected ;
443487 private final @ Nullable Correspondence <?, ?> correspondence ;
444488
445489 private MapInOrder (
446490 MapSubject subject ,
491+ Map <?, ?> actual ,
447492 Map <?, ?> expectedMap ,
448493 boolean allowUnexpected ,
449494 @ Nullable Correspondence <?, ?> correspondence ) {
450495 this .subject = subject ;
496+ this .actual = actual ;
451497 this .expectedMap = expectedMap ;
452498 this .allowUnexpected = allowUnexpected ;
453499 this .correspondence = correspondence ;
@@ -462,11 +508,10 @@ private MapInOrder(
462508 @ Override
463509 public void inOrder () {
464510 // We're using the fact that Sets.intersection keeps the order of the first set.
465- checkNotNull (subject .actual );
466511 List <?> expectedKeyOrder =
467- new ArrayList <>(Sets .intersection (expectedMap .keySet (), subject . actual .keySet ()));
512+ new ArrayList <>(Sets .intersection (expectedMap .keySet (), actual .keySet ()));
468513 List <?> actualKeyOrder =
469- new ArrayList <>(Sets .intersection (subject . actual .keySet (), expectedMap .keySet ()));
514+ new ArrayList <>(Sets .intersection (actual .keySet (), expectedMap .keySet ()));
470515 if (!actualKeyOrder .equals (expectedKeyOrder )) {
471516 ImmutableList .Builder <Fact > facts =
472517 factsBuilder ()
@@ -492,10 +537,11 @@ private void failWithActual(Iterable<Fact> facts) {
492537
493538 static MapInOrder create (
494539 MapSubject subject ,
540+ Map <?, ?> actual ,
495541 Map <?, ?> expectedMap ,
496542 boolean allowUnexpected ,
497543 @ Nullable Correspondence <?, ?> correspondence ) {
498- return new MapInOrder (subject , expectedMap , allowUnexpected , correspondence );
544+ return new MapInOrder (subject , actual , expectedMap , allowUnexpected , correspondence );
499545 }
500546 }
501547
@@ -590,9 +636,13 @@ private UsingCorrespondence(
590636 */
591637 @ SuppressWarnings ("UnnecessaryCast" ) // needed by nullness checker
592638 public void containsEntry (@ Nullable Object key , E value ) {
593- if (checkNotNull (actual ).containsKey (key )) {
639+ if (actual == null ) {
640+ failWithActual ("expected a map that contains entry" , immutableEntry (key , value ));
641+ return ;
642+ }
643+ if (actual .containsKey (key )) {
594644 // Found matching key.
595- A actualValue = getCastSubject ( ).get (key );
645+ A actualValue = castActual ( actual ).get (key );
596646 Correspondence .ExceptionStore exceptions = Correspondence .ExceptionStore .forMapValues ();
597647 if (correspondence .safeCompare ((A ) actualValue , value , exceptions )) {
598648 // The expected key had the expected value. There's no need to check exceptions here,
@@ -627,7 +677,7 @@ public void containsEntry(@Nullable Object key, E value) {
627677 // Did not find matching key. Look for the matching value with a different key.
628678 Set <@ Nullable Object > keys = new LinkedHashSet <>();
629679 Correspondence .ExceptionStore exceptions = Correspondence .ExceptionStore .forMapValues ();
630- for (Map .Entry <?, A > actualEntry : getCastSubject ( ).entrySet ()) {
680+ for (Map .Entry <?, A > actualEntry : castActual ( actual ).entrySet ()) {
631681 if (correspondence .safeCompare (actualEntry .getValue (), value , exceptions )) {
632682 keys .add (actualEntry .getKey ());
633683 }
@@ -665,9 +715,13 @@ public void containsEntry(@Nullable Object key, E value) {
665715 */
666716 @ SuppressWarnings ("UnnecessaryCast" ) // needed by nullness checker
667717 public void doesNotContainEntry (@ Nullable Object key , E value ) {
668- if (checkNotNull (actual ).containsKey (key )) {
718+ if (actual == null ) {
719+ failWithActual ("expected a map that does not contain entry" , immutableEntry (key , value ));
720+ return ;
721+ }
722+ if (actual .containsKey (key )) {
669723 // Found matching key. Fail if the value matches, too.
670- A actualValue = getCastSubject ( ).get (key );
724+ A actualValue = castActual ( actual ).get (key );
671725 Correspondence .ExceptionStore exceptions = Correspondence .ExceptionStore .forMapValues ();
672726 if (correspondence .safeCompare ((A ) actualValue , value , exceptions )) {
673727 // The matching key had a matching value. There's no need to check exceptions here,
@@ -741,36 +795,47 @@ public Ordered containsAtLeast(@Nullable Object k0, @Nullable E v0, @Nullable Ob
741795 * correspond to the values of the given map.
742796 */
743797 @ CanIgnoreReturnValue
744- public Ordered containsExactlyEntriesIn (Map <?, ? extends E > expected ) {
745- if (expected .isEmpty ()) {
746- if (checkNotNull (actual ).isEmpty ()) {
747- return IN_ORDER ;
748- } else {
749- subject .isEmpty (); // fails
750- return ALREADY_FAILED ;
751- }
798+ public Ordered containsExactlyEntriesIn (@ Nullable Map <?, ? extends E > expected ) {
799+ if (expected == null ) {
800+ failWithoutActual (
801+ simpleFact ("could not perform containment check because expected map was null" ),
802+ actualContents ());
803+ return ALREADY_FAILED ;
804+ } else if (expected .isEmpty ()) {
805+ return subject .containsExactly ();
806+ } else if (actual == null ) {
807+ failWithActual ("expected a map that contains exactly" , expected );
808+ return ALREADY_FAILED ;
752809 }
753- return internalContainsEntriesIn (expected , /* allowUnexpected= */ false );
810+ return internalContainsEntriesIn (actual , expected , /* allowUnexpected= */ false );
754811 }
755812
756813 /**
757814 * Checks that the actual map contains at least the keys in the given map, mapping to values
758815 * that correspond to the values of the given map.
759816 */
760817 @ CanIgnoreReturnValue
761- public Ordered containsAtLeastEntriesIn (Map <?, ? extends E > expected ) {
762- if (expected .isEmpty ()) {
818+ public Ordered containsAtLeastEntriesIn (@ Nullable Map <?, ? extends E > expected ) {
819+ if (expected == null ) {
820+ failWithoutActual (
821+ simpleFact ("could not perform containment check because expected map was null" ),
822+ actualContents ());
823+ return ALREADY_FAILED ;
824+ } else if (expected .isEmpty ()) {
763825 return IN_ORDER ;
826+ } else if (actual == null ) {
827+ failWithActual ("expected a map that contains at least" , expected );
828+ return ALREADY_FAILED ;
764829 }
765- return internalContainsEntriesIn (expected , /* allowUnexpected= */ true );
830+ return internalContainsEntriesIn (actual , expected , /* allowUnexpected= */ true );
766831 }
767832
768833 private <K extends @ Nullable Object , V extends E > Ordered internalContainsEntriesIn (
769- Map <K , V > expected , boolean allowUnexpected ) {
834+ Map <?, ?> actual , Map < K , V > expected , boolean allowUnexpected ) {
770835 Correspondence .ExceptionStore exceptions = Correspondence .ExceptionStore .forMapValues ();
771836 MapDifference <@ Nullable Object , A , V > diff =
772837 MapDifference .create (
773- getCastSubject ( ),
838+ castActual ( actual ),
774839 expected ,
775840 allowUnexpected ,
776841 (actualValue , expectedValue ) ->
@@ -779,7 +844,7 @@ public Ordered containsAtLeastEntriesIn(Map<?, ? extends E> expected) {
779844 // The maps correspond exactly. There's no need to check exceptions here, because if
780845 // Correspondence.compare() threw then safeCompare() would return false and the diff would
781846 // record that we had the wrong value for that key.
782- return MapInOrder .create (subject , expected , allowUnexpected , correspondence );
847+ return MapInOrder .create (subject , actual , expected , allowUnexpected , correspondence );
783848 }
784849 failWithoutActual (
785850 factsBuilder ()
@@ -798,8 +863,8 @@ private <V extends E> Differ<A, V> differ(Correspondence.ExceptionStore exceptio
798863 }
799864
800865 @ SuppressWarnings ("unchecked" ) // throwing ClassCastException is the correct behaviour
801- private Map <?, A > getCastSubject ( ) {
802- return (Map <?, A >) checkNotNull ( actual ) ;
866+ private Map <?, A > castActual ( Map <?, ?> actual ) {
867+ return (Map <?, A >) actual ;
803868 }
804869
805870 private String actualCustomStringRepresentationForPackageMembersToCall () {
0 commit comments