From 2151add71d6af7ccdd66d36d7f0b06ab6c5b831b Mon Sep 17 00:00:00 2001 From: Stefan Haustein Date: Tue, 14 Mar 2023 07:11:21 -0700 Subject: [PATCH] Null-mark Truth. RELNOTES=Null-mark Truth PiperOrigin-RevId: 516515683 --- .../common/truth/AbstractArraySubject.java | 5 +- .../common/truth/ActualValueInference.java | 23 +-- .../common/truth/AssertionErrorWithFacts.java | 4 +- .../common/truth/BigDecimalSubject.java | 9 +- .../google/common/truth/BooleanSubject.java | 2 +- .../com/google/common/truth/ClassSubject.java | 6 +- .../common/truth/ComparableSubject.java | 28 ++-- .../google/common/truth/Correspondence.java | 54 ++++--- .../common/truth/CustomSubjectBuilder.java | 1 + .../google/common/truth/DoubleSubject.java | 4 +- .../java/com/google/common/truth/Expect.java | 2 +- .../google/common/truth/ExpectFailure.java | 2 +- .../google/common/truth/FailureMetadata.java | 13 +- .../google/common/truth/FailureStrategy.java | 1 + .../com/google/common/truth/FloatSubject.java | 4 +- .../google/common/truth/GraphMatching.java | 10 +- .../common/truth/GuavaOptionalSubject.java | 4 +- .../google/common/truth/IntegerSubject.java | 6 +- .../google/common/truth/IterableSubject.java | 132 +++++++++--------- .../com/google/common/truth/LazyMessage.java | 2 +- .../com/google/common/truth/LongSubject.java | 6 +- .../com/google/common/truth/MapSubject.java | 103 +++++++------- .../google/common/truth/MultimapSubject.java | 98 +++++++------ .../google/common/truth/MultisetSubject.java | 5 +- .../common/truth/ObjectArraySubject.java | 8 +- .../java/com/google/common/truth/Ordered.java | 1 + .../com/google/common/truth/Platform.java | 54 ++++--- .../truth/PrimitiveBooleanArraySubject.java | 6 +- .../truth/PrimitiveByteArraySubject.java | 6 +- .../truth/PrimitiveCharArraySubject.java | 6 +- .../truth/PrimitiveDoubleArraySubject.java | 12 +- .../truth/PrimitiveFloatArraySubject.java | 12 +- .../truth/PrimitiveIntArraySubject.java | 6 +- .../truth/PrimitiveLongArraySubject.java | 6 +- .../truth/PrimitiveShortArraySubject.java | 6 +- .../common/truth/SimpleSubjectBuilder.java | 1 + .../common/truth/StackTraceCleaner.java | 15 +- .../google/common/truth/StringSubject.java | 47 ++++--- .../java/com/google/common/truth/Subject.java | 59 ++++---- .../com/google/common/truth/SubjectUtils.java | 29 ++-- .../com/google/common/truth/TableSubject.java | 32 +++-- .../google/common/truth/ThrowableSubject.java | 6 +- .../java/com/google/common/truth/Truth.java | 27 ++-- .../common/truth/TruthFailureSubject.java | 5 +- .../com/google/common/truth/TruthJUnit.java | 5 +- .../com/google/common/truth/Platform.java | 8 +- .../google/common/truth/OptionalSubject.java | 4 +- .../truth/extensions/re2j/Re2jSubjects.java | 28 ++-- 48 files changed, 514 insertions(+), 399 deletions(-) diff --git a/core/src/main/java/com/google/common/truth/AbstractArraySubject.java b/core/src/main/java/com/google/common/truth/AbstractArraySubject.java index d3b01cff6..8eff47a14 100644 --- a/core/src/main/java/com/google/common/truth/AbstractArraySubject.java +++ b/core/src/main/java/com/google/common/truth/AbstractArraySubject.java @@ -16,6 +16,7 @@ package com.google.common.truth; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.truth.Fact.simpleFact; import java.lang.reflect.Array; @@ -27,7 +28,7 @@ * @author Christian Gruber (cgruber@israfil.net) */ abstract class AbstractArraySubject extends Subject { - private final Object actual; + private final @Nullable Object actual; AbstractArraySubject( FailureMetadata metadata, @Nullable Object actual, @Nullable String typeDescription) { @@ -60,6 +61,6 @@ public final void hasLength(int length) { } private int length() { - return Array.getLength(actual); + return Array.getLength(checkNotNull(actual)); } } diff --git a/core/src/main/java/com/google/common/truth/ActualValueInference.java b/core/src/main/java/com/google/common/truth/ActualValueInference.java index 31dfd24a2..4aed574b7 100644 --- a/core/src/main/java/com/google/common/truth/ActualValueInference.java +++ b/core/src/main/java/com/google/common/truth/ActualValueInference.java @@ -15,6 +15,7 @@ import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Iterables.getOnlyElement; import static java.lang.Thread.currentThread; @@ -974,9 +975,11 @@ private void pushDescriptor(String desc) { * @param invocation the method invocation being visited, or {@code null} if a non-method * descriptor is being visited */ - private void pushDescriptorAndMaybeProcessMethodCall(String desc, Invocation invocation) { + private void pushDescriptorAndMaybeProcessMethodCall( + String desc, @Nullable Invocation invocation) { if (invocation != null && invocation.isOnSubjectInstance()) { - actualValueAtLocation.put(labelsSeen.build(), invocation.receiver().actualValue()); + actualValueAtLocation.put( + labelsSeen.build(), checkNotNull(invocation.receiver()).actualValue()); } boolean hasParams = invocation != null && (Type.getArgumentsAndReturnSizes(desc) >> 2) > 1; @@ -1011,7 +1014,8 @@ private void pushDescriptorAndMaybeProcessMethodCall(String desc, Invocation inv } } - private void pushMaybeDescribed(InferredType type, Invocation invocation, boolean hasParams) { + private void pushMaybeDescribed( + InferredType type, @Nullable Invocation invocation, boolean hasParams) { push(invocation == null ? opaque(type) : invocation.deriveEntry(type, hasParams)); } @@ -1032,10 +1036,11 @@ private StackEntry pop(int count) { operandStack, methodSignature); int expectedLastIndex = operandStack.size() - count - 1; - StackEntry lastPopped = null; - for (int i = operandStack.size() - 1; i > expectedLastIndex; --i) { - lastPopped = operandStack.remove(i); - } + + StackEntry lastPopped; + do { + lastPopped = operandStack.remove(operandStack.size() - 1); + } while (operandStack.size() - 1 > expectedLastIndex); return lastPopped; } @@ -1262,7 +1267,7 @@ final StackEntry deriveEntry(InferredType type, boolean hasParams) { } else if (actualValue() != null) { return subjectFor(type, actualValue()); } else if (isOnSubjectInstance()) { - return subjectFor(type, receiver().actualValue()); + return subjectFor(type, checkNotNull(receiver()).actualValue()); } else if (BORING_NAMES.contains(name())) { /* * TODO(cpovirk): For no-arg instance methods like get(), return "foo.get()," where "foo" is @@ -1496,7 +1501,7 @@ private static boolean isSet(int flags, int bitmask) { return (flags & bitmask) == bitmask; } - private static void closeQuietly(InputStream stream) { + private static void closeQuietly(@Nullable InputStream stream) { if (stream == null) { return; } diff --git a/core/src/main/java/com/google/common/truth/AssertionErrorWithFacts.java b/core/src/main/java/com/google/common/truth/AssertionErrorWithFacts.java index db465057d..cc5d36d9d 100644 --- a/core/src/main/java/com/google/common/truth/AssertionErrorWithFacts.java +++ b/core/src/main/java/com/google/common/truth/AssertionErrorWithFacts.java @@ -47,13 +47,13 @@ final class AssertionErrorWithFacts extends AssertionError implements ErrorWithF @Override @SuppressWarnings("UnsynchronizedOverridesSynchronized") - public Throwable getCause() { + public @Nullable Throwable getCause() { return cause; } @Override public String toString() { - return getLocalizedMessage(); + return checkNotNull(getLocalizedMessage()); } @Override diff --git a/core/src/main/java/com/google/common/truth/BigDecimalSubject.java b/core/src/main/java/com/google/common/truth/BigDecimalSubject.java index 64cf82369..3b5326d87 100644 --- a/core/src/main/java/com/google/common/truth/BigDecimalSubject.java +++ b/core/src/main/java/com/google/common/truth/BigDecimalSubject.java @@ -15,6 +15,7 @@ */ package com.google.common.truth; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.truth.Fact.fact; import static com.google.common.truth.Fact.simpleFact; @@ -27,7 +28,7 @@ * @author Kurt Alfred Kluever */ public final class BigDecimalSubject extends ComparableSubject { - private final BigDecimal actual; + private final @Nullable BigDecimal actual; BigDecimalSubject(FailureMetadata metadata, @Nullable BigDecimal actual) { super(metadata, actual); @@ -88,12 +89,12 @@ public void isEqualTo(@Nullable Object expected) { * #isEqualTo(Object)}. */ @Override - public void isEquivalentAccordingToCompareTo(BigDecimal expected) { + public void isEquivalentAccordingToCompareTo(@Nullable BigDecimal expected) { compareValues(expected); } - private void compareValues(BigDecimal expected) { - if (actual.compareTo(expected) != 0) { + private void compareValues(@Nullable BigDecimal expected) { + if (checkNotNull(actual).compareTo(expected) != 0) { failWithoutActual(fact("expected", expected), butWas(), simpleFact("(scale is ignored)")); } } diff --git a/core/src/main/java/com/google/common/truth/BooleanSubject.java b/core/src/main/java/com/google/common/truth/BooleanSubject.java index 528cbc626..4e7f3da0a 100644 --- a/core/src/main/java/com/google/common/truth/BooleanSubject.java +++ b/core/src/main/java/com/google/common/truth/BooleanSubject.java @@ -25,7 +25,7 @@ * @author Christian Gruber (cgruber@israfil.net) */ public final class BooleanSubject extends Subject { - private final Boolean actual; + private final @Nullable Boolean actual; BooleanSubject(FailureMetadata metadata, @Nullable Boolean actual) { super(metadata, actual); diff --git a/core/src/main/java/com/google/common/truth/ClassSubject.java b/core/src/main/java/com/google/common/truth/ClassSubject.java index a2ca5c487..efb9bbe16 100644 --- a/core/src/main/java/com/google/common/truth/ClassSubject.java +++ b/core/src/main/java/com/google/common/truth/ClassSubject.java @@ -15,6 +15,8 @@ */ package com.google.common.truth; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.annotations.GwtIncompatible; import org.checkerframework.checker.nullness.qual.Nullable; @@ -25,7 +27,7 @@ */ @GwtIncompatible("reflection") public final class ClassSubject extends Subject { - private final Class actual; + private final @Nullable Class actual; ClassSubject(FailureMetadata metadata, @Nullable Class o) { super(metadata, o); @@ -37,7 +39,7 @@ public final class ClassSubject extends Subject { * class or interface. */ public void isAssignableTo(Class clazz) { - if (!clazz.isAssignableFrom(actual)) { + if (!clazz.isAssignableFrom(checkNotNull(actual))) { failWithActual("expected to be assignable to", clazz.getName()); } } diff --git a/core/src/main/java/com/google/common/truth/ComparableSubject.java b/core/src/main/java/com/google/common/truth/ComparableSubject.java index ed769734f..7e8125286 100644 --- a/core/src/main/java/com/google/common/truth/ComparableSubject.java +++ b/core/src/main/java/com/google/common/truth/ComparableSubject.java @@ -15,6 +15,8 @@ */ package com.google.common.truth; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.collect.Range; import org.checkerframework.checker.nullness.qual.Nullable; @@ -29,7 +31,7 @@ public abstract class ComparableSubject extends Subject { * Constructor for use by subclasses. If you want to create an instance of this class itself, call * {@link Subject#check(String, Object...) check(...)}{@code .that(actual)}. */ - private final T actual; + private final @Nullable T actual; protected ComparableSubject(FailureMetadata metadata, @Nullable T actual) { super(metadata, actual); @@ -38,14 +40,14 @@ protected ComparableSubject(FailureMetadata metadata, @Nullable T actual) { /** Checks that the subject is in {@code range}. */ public final void isIn(Range range) { - if (!range.contains(actual)) { + if (!range.contains(checkNotNull(actual))) { failWithActual("expected to be in range", range); } } /** Checks that the subject is not in {@code range}. */ public final void isNotIn(Range range) { - if (range.contains(actual)) { + if (range.contains(checkNotNull(actual))) { failWithActual("expected not to be in range", range); } } @@ -57,8 +59,8 @@ public final void isNotIn(Range range) { *

Note: Do not use this method for checking object equality. Instead, use {@link * #isEqualTo(Object)}. */ - public void isEquivalentAccordingToCompareTo(T expected) { - if (actual.compareTo(expected) != 0) { + public void isEquivalentAccordingToCompareTo(@Nullable T expected) { + if (checkNotNull(actual).compareTo(expected) != 0) { failWithActual("expected value that sorts equal to", expected); } } @@ -69,8 +71,8 @@ public void isEquivalentAccordingToCompareTo(T expected) { *

To check that the subject is greater than or equal to {@code other}, use {@link * #isAtLeast}. */ - public final void isGreaterThan(T other) { - if (actual.compareTo(other) <= 0) { + public final void isGreaterThan(@Nullable T other) { + if (checkNotNull(actual).compareTo(other) <= 0) { failWithActual("expected to be greater than", other); } } @@ -81,8 +83,8 @@ public final void isGreaterThan(T other) { *

To check that the subject is less than or equal to {@code other}, use {@link * #isAtMost}. */ - public final void isLessThan(T other) { - if (actual.compareTo(other) >= 0) { + public final void isLessThan(@Nullable T other) { + if (checkNotNull(actual).compareTo(other) >= 0) { failWithActual("expected to be less than", other); } } @@ -93,8 +95,8 @@ public final void isLessThan(T other) { *

To check that the subject is strictly less than {@code other}, use {@link * #isLessThan}. */ - public final void isAtMost(T other) { - if (actual.compareTo(other) > 0) { + public final void isAtMost(@Nullable T other) { + if (checkNotNull(actual).compareTo(other) > 0) { failWithActual("expected to be at most", other); } } @@ -105,8 +107,8 @@ public final void isAtMost(T other) { *

To check that the subject is strictly greater than {@code other}, use {@link * #isGreaterThan}. */ - public final void isAtLeast(T other) { - if (actual.compareTo(other) < 0) { + public final void isAtLeast(@Nullable T other) { + if (checkNotNull(actual).compareTo(other) < 0) { failWithActual("expected to be at least", other); } } diff --git a/core/src/main/java/com/google/common/truth/Correspondence.java b/core/src/main/java/com/google/common/truth/Correspondence.java index ae2b4bc97..704a9e6c3 100644 --- a/core/src/main/java/com/google/common/truth/Correspondence.java +++ b/core/src/main/java/com/google/common/truth/Correspondence.java @@ -65,7 +65,7 @@ * * @author Pete Gillin */ -public abstract class Correspondence { +public abstract class Correspondence { /** * Constructs a {@link Correspondence} that compares actual and expected elements using the given @@ -112,7 +112,7 @@ public abstract class Correspondence { * is an element that "}, e.g. * {@code "contains"}, {@code "is an instance of"}, or {@code "is equivalent to"} */ - public static Correspondence from( + public static Correspondence from( BinaryPredicate predicate, String description) { return new FromBinaryPredicate<>(predicate, description); } @@ -126,7 +126,7 @@ public static Correspondence from( * you should almost never see {@code BinaryPredicate} used as the type of a field or variable, or * a return type. */ - public interface BinaryPredicate { + public interface BinaryPredicate { /** * Returns whether or not the actual and expected values satisfy the condition defined by this @@ -135,7 +135,9 @@ public interface BinaryPredicate { boolean apply(A actual, E expected); } - private static final class FromBinaryPredicate extends Correspondence { + private static final class FromBinaryPredicate< + A extends @Nullable Object, E extends @Nullable Object> + extends Correspondence { private final BinaryPredicate predicate; private final String description; @@ -191,8 +193,9 @@ public String toString() { * is an element that "}, e.g. * {@code "has an ID of"} */ - public static Correspondence transforming( - Function actualTransform, String description) { + public static + Correspondence transforming( + Function actualTransform, String description) { return new Transforming<>(actualTransform, identity(), description); } @@ -238,12 +241,14 @@ public static Correspondence transforming( * is an element that "}, e.g. * {@code "has the same ID as"} */ - public static Correspondence transforming( - Function actualTransform, Function expectedTransform, String description) { + public static + Correspondence transforming( + Function actualTransform, Function expectedTransform, String description) { return new Transforming<>(actualTransform, expectedTransform, description); } - private static final class Transforming extends Correspondence { + private static final class Transforming + extends Correspondence { private final Function actualTransform; private final Function expectedTransform; @@ -395,7 +400,7 @@ public Correspondence formattingDiffsUsing(DiffFormatter { + public interface DiffFormatter { /** * Returns a {@link String} describing the difference between the {@code actual} and {@code @@ -405,7 +410,8 @@ public interface DiffFormatter { String formatDiff(A actual, E expected); } - private static class FormattingDiffs extends Correspondence { + private static class FormattingDiffs + extends Correspondence { private final Correspondence delegate; private final DiffFormatter formatter; @@ -525,9 +531,10 @@ private static class StoredException { private final Exception exception; private final String methodName; - private final List methodArguments; + private final List<@Nullable Object> methodArguments; - StoredException(Exception exception, String methodName, List methodArguments) { + StoredException( + Exception exception, String methodName, List<@Nullable Object> methodArguments) { this.exception = checkNotNull(exception); this.methodName = checkNotNull(methodName); this.methodArguments = checkNotNull(methodArguments); @@ -552,9 +559,9 @@ private String describe() { static final class ExceptionStore { private final String argumentLabel; - private StoredException firstCompareException = null; - private StoredException firstPairingException = null; - private StoredException firstFormatDiffException = null; + private @Nullable StoredException firstCompareException = null; + private @Nullable StoredException firstPairingException = null; + private @Nullable StoredException firstFormatDiffException = null; static ExceptionStore forIterable() { return new ExceptionStore("elements"); @@ -580,7 +587,10 @@ private ExceptionStore(String argumentLabel) { * exception was encountered */ void addCompareException( - Class callingClass, Exception exception, Object actual, Object expected) { + Class callingClass, + Exception exception, + @Nullable Object actual, + @Nullable Object expected) { if (firstCompareException == null) { truncateStackTrace(exception, callingClass); firstCompareException = new StoredException(exception, "compare", asList(actual, expected)); @@ -597,7 +607,8 @@ void addCompareException( * @param actual The {@code actual} argument to the {@code apply} call during which the * exception was encountered */ - void addActualKeyFunctionException(Class callingClass, Exception exception, Object actual) { + void addActualKeyFunctionException( + Class callingClass, Exception exception, @Nullable Object actual) { if (firstPairingException == null) { truncateStackTrace(exception, callingClass); firstPairingException = @@ -616,7 +627,7 @@ void addActualKeyFunctionException(Class callingClass, Exception exception, O * exception was encountered */ void addExpectedKeyFunctionException( - Class callingClass, Exception exception, Object expected) { + Class callingClass, Exception exception, @Nullable Object expected) { if (firstPairingException == null) { truncateStackTrace(exception, callingClass); firstPairingException = @@ -636,7 +647,10 @@ void addExpectedKeyFunctionException( * exception was encountered */ void addFormatDiffException( - Class callingClass, Exception exception, Object actual, Object expected) { + Class callingClass, + Exception exception, + @Nullable Object actual, + @Nullable Object expected) { if (firstFormatDiffException == null) { truncateStackTrace(exception, callingClass); firstFormatDiffException = diff --git a/core/src/main/java/com/google/common/truth/CustomSubjectBuilder.java b/core/src/main/java/com/google/common/truth/CustomSubjectBuilder.java index 9fee6f553..0b84ef88c 100644 --- a/core/src/main/java/com/google/common/truth/CustomSubjectBuilder.java +++ b/core/src/main/java/com/google/common/truth/CustomSubjectBuilder.java @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull; + /** * In a fluent assertion chain, exposes one or more "custom" {@code that} methods, which accept a * value under test and return a {@link Subject}. diff --git a/core/src/main/java/com/google/common/truth/DoubleSubject.java b/core/src/main/java/com/google/common/truth/DoubleSubject.java index a198bff81..a3fbb9c74 100644 --- a/core/src/main/java/com/google/common/truth/DoubleSubject.java +++ b/core/src/main/java/com/google/common/truth/DoubleSubject.java @@ -36,7 +36,7 @@ public final class DoubleSubject extends ComparableSubject { private static final long NEG_ZERO_BITS = doubleToLongBits(-0.0); - private final Double actual; + private final @Nullable Double actual; DoubleSubject(FailureMetadata metadata, @Nullable Double actual) { super(metadata, actual); @@ -201,7 +201,7 @@ public final void isNotEqualTo(@Nullable Object other) { */ @Override @Deprecated - public final void isEquivalentAccordingToCompareTo(Double other) { + public final void isEquivalentAccordingToCompareTo(@Nullable Double other) { super.isEquivalentAccordingToCompareTo(other); } diff --git a/core/src/main/java/com/google/common/truth/Expect.java b/core/src/main/java/com/google/common/truth/Expect.java index 5acc31d97..3da547102 100644 --- a/core/src/main/java/com/google/common/truth/Expect.java +++ b/core/src/main/java/com/google/common/truth/Expect.java @@ -86,7 +86,7 @@ public final class Expect extends StandardSubjectBuilder implements TestRule { private static final class ExpectationGatherer implements FailureStrategy { @GuardedBy("this") - private final List failures = new ArrayList(); + private final List failures = new ArrayList<>(); @GuardedBy("this") private TestPhase inRuleContext = BEFORE; diff --git a/core/src/main/java/com/google/common/truth/ExpectFailure.java b/core/src/main/java/com/google/common/truth/ExpectFailure.java index 2c6ec5893..174fa518b 100644 --- a/core/src/main/java/com/google/common/truth/ExpectFailure.java +++ b/core/src/main/java/com/google/common/truth/ExpectFailure.java @@ -195,7 +195,7 @@ public void invokeAssertion(StandardSubjectBuilder whenTesting) { * Creates a subject for asserting about the given {@link AssertionError}, usually one produced by * Truth. */ - public static TruthFailureSubject assertThat(AssertionError actual) { + public static TruthFailureSubject assertThat(@Nullable AssertionError actual) { return assertAbout(truthFailures()).that(actual); } diff --git a/core/src/main/java/com/google/common/truth/FailureMetadata.java b/core/src/main/java/com/google/common/truth/FailureMetadata.java index 7bc12bca1..e6d86a740 100644 --- a/core/src/main/java/com/google/common/truth/FailureMetadata.java +++ b/core/src/main/java/com/google/common/truth/FailureMetadata.java @@ -68,7 +68,7 @@ static Step subjectCreation(Subject subject) { } static Step checkCall( - OldAndNewValuesAreSimilar valuesAreSimilar, + @Nullable OldAndNewValuesAreSimilar valuesAreSimilar, @Nullable Function descriptionUpdate) { return new Step(null, descriptionUpdate, valuesAreSimilar); } @@ -238,7 +238,7 @@ private ImmutableList description() { } if (description == null) { - description = step.subject.typeDescription(); + description = checkNotNull(step.subject).typeDescription(); } } return descriptionIsInteresting @@ -287,7 +287,7 @@ private ImmutableList rootUnlessThrowable() { } if (rootSubject == null) { - if (step.subject.actual() instanceof Throwable) { + if (checkNotNull(step.subject).actual() instanceof Throwable) { /* * We'll already include the Throwable as a cause of the AssertionError (see rootCause()), * so we don't need to include it again in the message. @@ -306,8 +306,9 @@ private ImmutableList rootUnlessThrowable() { ? ImmutableList.of( fact( // TODO(cpovirk): Use inferDescription() here when appropriate? But it can be long. - rootSubject.subject.typeDescription() + " was", - rootSubject.subject.actualCustomStringRepresentationForPackageMembersToCall())) + checkNotNull(checkNotNull(rootSubject).subject).typeDescription() + " was", + checkNotNull(checkNotNull(rootSubject).subject) + .actualCustomStringRepresentationForPackageMembersToCall())) : ImmutableList.of(); } @@ -317,7 +318,7 @@ private ImmutableList rootUnlessThrowable() { */ private @Nullable Throwable rootCause() { for (Step step : steps) { - if (!step.isCheckCall() && step.subject.actual() instanceof Throwable) { + if (!step.isCheckCall() && checkNotNull(step.subject).actual() instanceof Throwable) { return (Throwable) step.subject.actual(); } } diff --git a/core/src/main/java/com/google/common/truth/FailureStrategy.java b/core/src/main/java/com/google/common/truth/FailureStrategy.java index 81f1092a7..b10d5509a 100644 --- a/core/src/main/java/com/google/common/truth/FailureStrategy.java +++ b/core/src/main/java/com/google/common/truth/FailureStrategy.java @@ -15,6 +15,7 @@ */ package com.google.common.truth; + /** * Defines what to do when a check fails. * diff --git a/core/src/main/java/com/google/common/truth/FloatSubject.java b/core/src/main/java/com/google/common/truth/FloatSubject.java index 765b3844b..85546557b 100644 --- a/core/src/main/java/com/google/common/truth/FloatSubject.java +++ b/core/src/main/java/com/google/common/truth/FloatSubject.java @@ -36,7 +36,7 @@ public final class FloatSubject extends ComparableSubject { private static final int NEG_ZERO_BITS = floatToIntBits(-0.0f); - private final Float actual; + private final @Nullable Float actual; private final DoubleSubject asDouble; FloatSubject(FailureMetadata metadata, @Nullable Float actual) { @@ -209,7 +209,7 @@ public final void isNotEqualTo(@Nullable Object other) { */ @Override @Deprecated - public final void isEquivalentAccordingToCompareTo(Float other) { + public final void isEquivalentAccordingToCompareTo(@Nullable Float other) { super.isEquivalentAccordingToCompareTo(other); } diff --git a/core/src/main/java/com/google/common/truth/GraphMatching.java b/core/src/main/java/com/google/common/truth/GraphMatching.java index 6a266f5df..ef21fef5b 100644 --- a/core/src/main/java/com/google/common/truth/GraphMatching.java +++ b/core/src/main/java/com/google/common/truth/GraphMatching.java @@ -15,6 +15,8 @@ */ package com.google.common.truth; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.base.Optional; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; @@ -142,7 +144,7 @@ private Optional breadthFirstSearch(BiMap matching, Map freeRhsVertexLayer.get()) { break; @@ -164,7 +166,7 @@ private Optional breadthFirstSearch(BiMap matching, Map freeRhsVertexLayer) { // We've gone past the target layer, so we're not going to find what we're looking for. return false; @@ -240,7 +242,7 @@ private boolean depthFirstSearch( } else { // We found a non-free RHS vertex. Follow the matched edge from that RHS vertex to find // the next LHS vertex. - U nextLhs = matching.inverse().get(rhs); + U nextLhs = checkNotNull(matching.inverse().get(rhs)); if (layers.containsKey(nextLhs) && layers.get(nextLhs) == layer + 1) { // The next LHS vertex is in the next layer of the BFS, so we can use this path for our // DFS. Recurse into the DFS. diff --git a/core/src/main/java/com/google/common/truth/GuavaOptionalSubject.java b/core/src/main/java/com/google/common/truth/GuavaOptionalSubject.java index bbaf8053e..d65448d6b 100644 --- a/core/src/main/java/com/google/common/truth/GuavaOptionalSubject.java +++ b/core/src/main/java/com/google/common/truth/GuavaOptionalSubject.java @@ -30,7 +30,7 @@ * @author Christian Gruber */ public final class GuavaOptionalSubject extends Subject { - private final Optional actual; + private final @Nullable Optional actual; GuavaOptionalSubject( FailureMetadata metadata, @Nullable Optional actual, @Nullable String typeDescription) { @@ -67,7 +67,7 @@ public void isAbsent() { * assertThat(myOptional.get()).contains("foo"); * } */ - public void hasValue(Object expected) { + public void hasValue(@Nullable Object expected) { if (expected == null) { throw new NullPointerException("Optional cannot have a null value."); } diff --git a/core/src/main/java/com/google/common/truth/IntegerSubject.java b/core/src/main/java/com/google/common/truth/IntegerSubject.java index 2f892379b..bf1446408 100644 --- a/core/src/main/java/com/google/common/truth/IntegerSubject.java +++ b/core/src/main/java/com/google/common/truth/IntegerSubject.java @@ -33,10 +33,12 @@ protected IntegerSubject(FailureMetadata metadata, @Nullable Integer integer) { super(metadata, integer); } - /** @deprecated Use {@link #isEqualTo} instead. Integer comparison is consistent with equality. */ + /** + * @deprecated Use {@link #isEqualTo} instead. Integer comparison is consistent with equality. + */ @Override @Deprecated - public final void isEquivalentAccordingToCompareTo(Integer other) { + public final void isEquivalentAccordingToCompareTo(@Nullable Integer other) { super.isEquivalentAccordingToCompareTo(other); } } diff --git a/core/src/main/java/com/google/common/truth/IterableSubject.java b/core/src/main/java/com/google/common/truth/IterableSubject.java index 2fa67429d..d0367b139 100644 --- a/core/src/main/java/com/google/common/truth/IterableSubject.java +++ b/core/src/main/java/com/google/common/truth/IterableSubject.java @@ -90,7 +90,7 @@ // Can't be final since MultisetSubject and SortedSetSubject extend it public class IterableSubject extends Subject { - private final Iterable actual; + private final @Nullable Iterable actual; /** * Constructor for use by subclasses. If you want to create an instance of this class itself, call @@ -141,14 +141,14 @@ public void isEqualTo(@Nullable Object expected) { /** Fails if the subject is not empty. */ public final void isEmpty() { - if (!Iterables.isEmpty(actual)) { + if (!Iterables.isEmpty(checkNotNull(actual))) { failWithActual(simpleFact("expected to be empty")); } } /** Fails if the subject is empty. */ public final void isNotEmpty() { - if (Iterables.isEmpty(actual)) { + if (Iterables.isEmpty(checkNotNull(actual))) { failWithoutActual(simpleFact("expected not to be empty")); } } @@ -156,14 +156,14 @@ public final void isNotEmpty() { /** Fails if the subject does not have the given size. */ public final void hasSize(int expectedSize) { checkArgument(expectedSize >= 0, "expectedSize(%s) must be >= 0", expectedSize); - int actualSize = size(actual); + int actualSize = size(checkNotNull(actual)); check("size()").that(actualSize).isEqualTo(expectedSize); } /** Checks (with a side-effect failure) that the subject contains the supplied item. */ public final void contains(@Nullable Object element) { - if (!Iterables.contains(actual, element)) { - List elementList = newArrayList(element); + if (!Iterables.contains(checkNotNull(actual), element)) { + List<@Nullable Object> elementList = newArrayList(element); if (hasMatchingToStringPair(actual, elementList)) { failWithoutActual( fact("expected to contain", element), @@ -182,7 +182,7 @@ public final void contains(@Nullable Object element) { /** Checks (with a side-effect failure) that the subject does not contain the supplied item. */ public final void doesNotContain(@Nullable Object element) { - if (Iterables.contains(actual, element)) { + if (Iterables.contains(checkNotNull(actual), element)) { failWithActual("expected not to contain", element); } } @@ -190,7 +190,7 @@ public final void doesNotContain(@Nullable Object element) { /** Checks that the subject does not contain duplicate elements. */ public final void containsNoDuplicates() { List> duplicates = newArrayList(); - for (Multiset.Entry entry : LinkedHashMultiset.create(actual).entrySet()) { + for (Multiset.Entry entry : LinkedHashMultiset.create(checkNotNull(actual)).entrySet()) { if (entry.getCount() > 1) { duplicates.add(entry); } @@ -214,8 +214,9 @@ public final void containsAnyOf( * collection or fails. */ // TODO(cpovirk): Consider using makeElementFacts-style messages here, in contains(), etc. - public final void containsAnyIn(Iterable expected) { - Collection actual = iterableToCollection(this.actual); + public final void containsAnyIn(@Nullable Iterable expected) { + checkNotNull(expected); + Collection actual = iterableToCollection(checkNotNull(this.actual)); for (Object item : expected) { if (actual.contains(item)) { return; @@ -228,7 +229,7 @@ public final void containsAnyIn(Iterable expected) { fact( "though it did contain", countDuplicatesAndAddTypeInfo( - retainMatchingToString(this.actual, expected /* itemsToCheck */))), + retainMatchingToString(checkNotNull(this.actual), expected /* itemsToCheck */))), fullContents()); } else { failWithActual("expected to contain any of", expected); @@ -271,11 +272,11 @@ public final Ordered containsAtLeast( */ @CanIgnoreReturnValue public final Ordered containsAtLeastElementsIn(Iterable expectedIterable) { - List actual = Lists.newLinkedList(this.actual); + List actual = Lists.newLinkedList(checkNotNull(this.actual)); final Collection expected = iterableToCollection(expectedIterable); - List missing = newArrayList(); - List actualNotInOrder = newArrayList(); + List<@Nullable Object> missing = newArrayList(); + List<@Nullable Object> actualNotInOrder = newArrayList(); boolean ordered = true; // step through the expected elements... @@ -307,7 +308,8 @@ public void inOrder() { ImmutableList.Builder facts = ImmutableList.builder(); facts.add(simpleFact("required elements were all found, but order was wrong")); facts.add(fact("expected order for required elements", expected)); - List actualOrder = Lists.newArrayList(IterableSubject.this.actual); + List actualOrder = + Lists.newArrayList(checkNotNull(IterableSubject.this.actual)); if (actualOrder.retainAll(expected)) { facts.add(fact("but order was", actualOrder)); facts.add(fullContents()); @@ -335,7 +337,7 @@ public final Ordered containsAtLeastElementsIn(Object[] expected) { private Ordered failAtLeast(Collection expected, Collection missingRawObjects) { Collection nearMissRawObjects = - retainMatchingToString(actual, missingRawObjects /* itemsToCheck */); + retainMatchingToString(checkNotNull(actual), missingRawObjects /* itemsToCheck */); ImmutableList.Builder facts = ImmutableList.builder(); facts.addAll( @@ -360,7 +362,8 @@ private Ordered failAtLeast(Collection expected, Collection missingRawObje * Removes at most the given number of available elements from the input list and adds them to the * given output collection. */ - private static void moveElements(List input, Collection output, int maxElements) { + private static void moveElements( + List input, Collection<@Nullable Object> output, int maxElements) { for (int i = 0; i < maxElements; i++) { output.add(input.remove(0)); } @@ -381,7 +384,8 @@ private static void moveElements(List input, Collection output, int m */ @CanIgnoreReturnValue public final Ordered containsExactly(@Nullable Object @Nullable ... varargs) { - List expected = (varargs == null) ? newArrayList((Object) null) : asList(varargs); + List<@Nullable Object> expected = + (varargs == null) ? newArrayList((@Nullable Object) null) : asList(varargs); return containsExactlyElementsIn( expected, varargs != null && varargs.length == 1 && varargs[0] instanceof Iterable); } @@ -397,7 +401,7 @@ public final Ordered containsExactly(@Nullable Object @Nullable ... varargs) { * on the object returned by this method. */ @CanIgnoreReturnValue - public final Ordered containsExactlyElementsIn(Iterable expected) { + public final Ordered containsExactlyElementsIn(@Nullable Iterable expected) { return containsExactlyElementsIn(expected, false); } @@ -411,13 +415,14 @@ public final Ordered containsExactlyElementsIn(Iterable expected) { * on the object returned by this method. */ @CanIgnoreReturnValue - public final Ordered containsExactlyElementsIn(Object[] expected) { - return containsExactlyElementsIn(asList(expected)); + public final Ordered containsExactlyElementsIn(@Nullable Object @Nullable [] expected) { + return containsExactlyElementsIn(asList(checkNotNull(expected))); } private Ordered containsExactlyElementsIn( - final Iterable required, boolean addElementsInWarning) { - Iterator actualIter = actual.iterator(); + final @Nullable Iterable required, boolean addElementsInWarning) { + checkNotNull(required); + Iterator actualIter = checkNotNull(actual).iterator(); Iterator requiredIter = required.iterator(); if (!requiredIter.hasNext()) { @@ -463,12 +468,12 @@ private Ordered containsExactlyElementsIn( return ALREADY_FAILED; } // Missing elements; elements that are not missing will be removed as we iterate. - Collection missing = newArrayList(); + Collection<@Nullable Object> missing = newArrayList(); missing.add(requiredElement); Iterators.addAll(missing, requiredIter); // Extra elements that the subject had but shouldn't have. - Collection extra = newArrayList(); + Collection<@Nullable Object> extra = newArrayList(); // Remove all actual elements from missing, and add any that weren't in missing // to extra. @@ -705,8 +710,8 @@ public final void containsNoneOf( * elements equal any of the excluded.) */ public final void containsNoneIn(Iterable excluded) { - Collection actual = iterableToCollection(this.actual); - Collection present = new ArrayList<>(); + Collection actual = iterableToCollection(checkNotNull(this.actual)); + Collection<@Nullable Object> present = new ArrayList<>(); for (Object item : Sets.newLinkedHashSet(excluded)) { if (actual.contains(item)) { present.add(item); @@ -725,7 +730,7 @@ public final void containsNoneIn(Iterable excluded) { * or fails. (Duplicates are irrelevant to this test, which fails if any of the actual elements * equal any of the excluded.) */ - public final void containsNoneIn(Object[] excluded) { + public final void containsNoneIn(@Nullable Object[] excluded) { containsNoneIn(asList(excluded)); } @@ -781,8 +786,8 @@ public final void isInStrictOrder(final Comparator comparator) { "expected to be in strict order", new PairwiseChecker() { @Override - public boolean check(Object prev, Object next) { - return ((Comparator) comparator).compare(prev, next) < 0; + public boolean check(@Nullable Object prev, @Nullable Object next) { + return ((Comparator<@Nullable Object>) comparator).compare(prev, next) < 0; } }); } @@ -813,18 +818,18 @@ public final void isInOrder(final Comparator comparator) { "expected to be in order", new PairwiseChecker() { @Override - public boolean check(Object prev, Object next) { - return ((Comparator) comparator).compare(prev, next) <= 0; + public boolean check(@Nullable Object prev, @Nullable Object next) { + return ((Comparator<@Nullable Object>) comparator).compare(prev, next) <= 0; } }); } private interface PairwiseChecker { - boolean check(Object prev, Object next); + boolean check(@Nullable Object prev, @Nullable Object next); } private void pairwiseCheck(String expectedFact, PairwiseChecker checker) { - Iterator iterator = actual.iterator(); + Iterator iterator = checkNotNull(actual).iterator(); if (iterator.hasNext()) { Object prev = iterator.next(); while (iterator.hasNext()) { @@ -857,11 +862,12 @@ public void isNoneOf( */ @Override @Deprecated - public void isNotIn(Iterable iterable) { + public void isNotIn(@Nullable Iterable iterable) { + checkNotNull(iterable); if (Iterables.contains(iterable, actual)) { failWithActual("expected not to be any of", iterable); } - List nonIterables = new ArrayList<>(); + List<@Nullable Object> nonIterables = new ArrayList<>(); for (Object element : iterable) { if (!(element instanceof Iterable)) { nonIterables.add(element); @@ -901,8 +907,9 @@ private Fact fullContents() { *

Any of the methods on the returned object may throw {@link ClassCastException} if they * encounter an actual element that is not of type {@code A}. */ - public UsingCorrespondence comparingElementsUsing( - Correspondence correspondence) { + public + UsingCorrespondence comparingElementsUsing( + Correspondence correspondence) { return new UsingCorrespondence<>(this, correspondence); } @@ -946,7 +953,7 @@ public UsingCorrespondence formattingDiffsUsing( * expected elements are of type {@code E}. Call methods on this object to actually execute the * check. */ - public static class UsingCorrespondence { + public static class UsingCorrespondence { private final IterableSubject subject; private final Correspondence correspondence; @@ -1107,7 +1114,7 @@ public UsingCorrespondence displayingDiffsPairedBy( * during comparisons? Or maybe we should take the risk for user convenience? If we make * changes, also make them in MapSubject, MultimapSubject, and possibly others. */ - public void contains(@Nullable E expected) { + public void contains(E expected) { Correspondence.ExceptionStore exceptions = Correspondence.ExceptionStore.forIterable(); for (A actual : getCastActual()) { if (correspondence.safeCompare(actual, expected, exceptions)) { @@ -1157,7 +1164,7 @@ public void contains(@Nullable E expected) { } /** Checks that none of the actual elements correspond to the given element. */ - public void doesNotContain(@Nullable E excluded) { + public void doesNotContain(E excluded) { Correspondence.ExceptionStore exceptions = Correspondence.ExceptionStore.forIterable(); List matchingElements = new ArrayList<>(); for (A actual : getCastActual()) { @@ -1218,9 +1225,9 @@ public final Ordered containsExactly(@Nullable E @Nullable ... expected) { * on the object returned by this method. */ @CanIgnoreReturnValue - public Ordered containsExactlyElementsIn(final Iterable expected) { + public Ordered containsExactlyElementsIn(final @Nullable Iterable expected) { List actualList = iterableToList(getCastActual()); - List expectedList = iterableToList(expected); + List expectedList = iterableToList(checkNotNull(expected)); if (expectedList.isEmpty()) { if (actualList.isEmpty()) { @@ -1295,8 +1302,8 @@ public void inOrder() { * on the object returned by this method. */ @CanIgnoreReturnValue - public Ordered containsExactlyElementsIn(E[] expected) { - return containsExactlyElementsIn(asList(expected)); + public Ordered containsExactlyElementsIn(E @Nullable [] expected) { + return containsExactlyElementsIn(asList(checkNotNull(expected))); } /** @@ -1381,7 +1388,7 @@ private ImmutableList describeMissingOrExtra( List extra, Correspondence.ExceptionStore exceptions) { if (pairer.isPresent()) { - @Nullable Pairing pairing = pairer.get().pair(missing, extra, exceptions); + Pairing pairing = pairer.get().pair(missing, extra, exceptions); if (pairing != null) { return describeMissingOrExtraWithPairing(pairing, exceptions); } else { @@ -1434,11 +1441,11 @@ private ImmutableList formatExtras( E missing, List extras, Correspondence.ExceptionStore exceptions) { - List diffs = new ArrayList<>(extras.size()); + List<@Nullable String> diffs = new ArrayList<>(extras.size()); boolean hasDiffs = false; for (int i = 0; i < extras.size(); i++) { A extra = extras.get(i); - @Nullable String diff = correspondence.safeFormatDiff(extra, missing, exceptions); + String diff = correspondence.safeFormatDiff(extra, missing, exceptions); diffs.add(diff); if (diff != null) { hasDiffs = true; @@ -1465,7 +1472,8 @@ private ImmutableList formatExtras( * Returns all the elements of the given list other than those with the given indexes. Assumes * that all the given indexes really are valid indexes into the list. */ - private List findNotIndexed(List list, Set indexes) { + private List findNotIndexed( + List list, Set indexes) { if (indexes.size() == list.size()) { // If there are as many distinct valid indexes are there are elements in the list then every // index must be in there once. @@ -1554,8 +1562,7 @@ private boolean failIfOneToOneMappingHasMissingOrExtra( */ @SafeVarargs @CanIgnoreReturnValue - public final Ordered containsAtLeast( - @Nullable E first, @Nullable E second, @Nullable E @Nullable ... rest) { + public final Ordered containsAtLeast(E first, E second, E @Nullable ... rest) { return containsAtLeastElementsIn(accumulate(first, second, rest)); } @@ -1718,7 +1725,7 @@ private ImmutableList describeMissing( List extra, Correspondence.ExceptionStore exceptions) { if (pairer.isPresent()) { - @Nullable Pairing pairing = pairer.get().pair(missing, extra, exceptions); + Pairing pairing = pairer.get().pair(missing, extra, exceptions); if (pairing != null) { return describeMissingWithPairing(pairing, exceptions); } else { @@ -1803,8 +1810,7 @@ private boolean failIfOneToOneMappingHasMissing( * expected elements. */ @SafeVarargs - public final void containsAnyOf( - @Nullable E first, @Nullable E second, @Nullable E @Nullable ... rest) { + public final void containsAnyOf(E first, E second, E @Nullable ... rest) { containsAnyIn(accumulate(first, second, rest)); } @@ -1910,9 +1916,7 @@ private ImmutableList describeAnyMatchesByKey( */ @SafeVarargs public final void containsNoneOf( - @Nullable E firstExcluded, - @Nullable E secondExcluded, - @Nullable E @Nullable ... restOfExcluded) { + E firstExcluded, E secondExcluded, E @Nullable ... restOfExcluded) { containsNoneIn(accumulate(firstExcluded, secondExcluded, restOfExcluded)); } @@ -1973,7 +1977,7 @@ public void containsNoneIn(E[] excluded) { @SuppressWarnings("unchecked") // throwing ClassCastException is the correct behaviour private Iterable getCastActual() { - return (Iterable) subject.actual; + return (Iterable) checkNotNull(subject.actual); } // TODO(b/69154276): Consider commoning up some of the logic between IterableSubject.Pairer, @@ -2009,7 +2013,7 @@ private final class Pairer { // Populate expectedKeys with the keys of the corresponding elements of expectedValues. // We do this ahead of time to avoid invoking the key function twice for each element. - List expectedKeys = new ArrayList<>(expectedValues.size()); + List<@Nullable Object> expectedKeys = new ArrayList<>(expectedValues.size()); for (E expected : expectedValues) { expectedKeys.add(expectedKey(expected, exceptions)); } @@ -2018,7 +2022,7 @@ private final class Pairer { // We will remove the unpaired keys later. Return null if we find a duplicate key. for (int i = 0; i < expectedValues.size(); i++) { E expected = expectedValues.get(i); - @Nullable Object key = expectedKeys.get(i); + Object key = expectedKeys.get(i); if (key != null) { if (pairing.pairedKeysToExpectedValues.containsKey(key)) { return null; @@ -2030,9 +2034,9 @@ private final class Pairer { // Populate pairedKeysToActualValues and unpairedActualValues. for (A actual : actualValues) { - @Nullable Object key = actualKey(actual, exceptions); + Object key = actualKey(actual, exceptions); if (pairing.pairedKeysToExpectedValues.containsKey(key)) { - pairing.pairedKeysToActualValues.put(key, actual); + pairing.pairedKeysToActualValues.put(checkNotNull(key), actual); } else { pairing.unpairedActualValues.add(actual); } @@ -2041,7 +2045,7 @@ private final class Pairer { // Populate unpairedExpectedValues and remove unpaired keys from pairedKeysToExpectedValues. for (int i = 0; i < expectedValues.size(); i++) { E expected = expectedValues.get(i); - @Nullable Object key = expectedKeys.get(i); + Object key = expectedKeys.get(i); if (!pairing.pairedKeysToActualValues.containsKey(key)) { pairing.unpairedExpectedValues.add(expected); pairing.pairedKeysToExpectedValues.remove(key); @@ -2055,7 +2059,7 @@ List pairOne( E expectedValue, Iterable actualValues, Correspondence.ExceptionStore exceptions) { - @Nullable Object key = expectedKey(expectedValue, exceptions); + Object key = expectedKey(expectedValue, exceptions); List matches = new ArrayList<>(); if (key != null) { for (A actual : actualValues) { diff --git a/core/src/main/java/com/google/common/truth/LazyMessage.java b/core/src/main/java/com/google/common/truth/LazyMessage.java index 3fd26c904..f43eed85e 100644 --- a/core/src/main/java/com/google/common/truth/LazyMessage.java +++ b/core/src/main/java/com/google/common/truth/LazyMessage.java @@ -27,7 +27,7 @@ final class LazyMessage { "Incorrect number of args (%s) for the given placeholders (%s) in string template:\"%s\""; private final String format; - private final Object[] args; + private final @Nullable Object[] args; LazyMessage(String format, @Nullable Object... args) { this.format = format; diff --git a/core/src/main/java/com/google/common/truth/LongSubject.java b/core/src/main/java/com/google/common/truth/LongSubject.java index 27f99f546..d56de24d3 100644 --- a/core/src/main/java/com/google/common/truth/LongSubject.java +++ b/core/src/main/java/com/google/common/truth/LongSubject.java @@ -33,10 +33,12 @@ protected LongSubject(FailureMetadata metadata, @Nullable Long actual) { super(metadata, actual); } - /** @deprecated Use {@link #isEqualTo} instead. Long comparison is consistent with equality. */ + /** + * @deprecated Use {@link #isEqualTo} instead. Long comparison is consistent with equality. + */ @Override @Deprecated - public final void isEquivalentAccordingToCompareTo(Long other) { + public final void isEquivalentAccordingToCompareTo(@Nullable Long other) { super.isEquivalentAccordingToCompareTo(other); } diff --git a/core/src/main/java/com/google/common/truth/MapSubject.java b/core/src/main/java/com/google/common/truth/MapSubject.java index 2744d505f..308049195 100644 --- a/core/src/main/java/com/google/common/truth/MapSubject.java +++ b/core/src/main/java/com/google/common/truth/MapSubject.java @@ -51,7 +51,7 @@ * @author Kurt Alfred Kluever */ public class MapSubject extends Subject { - private final Map actual; + private final @Nullable Map actual; /** * Constructor for use by subclasses. If you want to create an instance of this class itself, call @@ -80,14 +80,14 @@ public final void isEqualTo(@Nullable Object other) { /** Fails if the map is not empty. */ public final void isEmpty() { - if (!actual.isEmpty()) { + if (!checkNotNull(actual).isEmpty()) { failWithActual(simpleFact("expected to be empty")); } } /** Fails if the map is empty. */ public final void isNotEmpty() { - if (actual.isEmpty()) { + if (checkNotNull(actual).isEmpty()) { failWithoutActual(simpleFact("expected not to be empty")); } } @@ -95,25 +95,26 @@ public final void isNotEmpty() { /** Fails if the map does not have the given size. */ public final void hasSize(int expectedSize) { checkArgument(expectedSize >= 0, "expectedSize (%s) must be >= 0", expectedSize); - check("size()").that(actual.size()).isEqualTo(expectedSize); + check("size()").that(checkNotNull(actual).size()).isEqualTo(expectedSize); } /** Fails if the map does not contain the given key. */ public final void containsKey(@Nullable Object key) { - check("keySet()").that(actual.keySet()).contains(key); + check("keySet()").that(checkNotNull(actual).keySet()).contains(key); } /** Fails if the map contains the given key. */ public final void doesNotContainKey(@Nullable Object key) { - check("keySet()").that(actual.keySet()).doesNotContain(key); + check("keySet()").that(checkNotNull(actual).keySet()).doesNotContain(key); } /** Fails if the map does not contain the given entry. */ public final void containsEntry(@Nullable Object key, @Nullable Object value) { - Map.Entry entry = immutableEntry(key, value); + Map.Entry<@Nullable Object, @Nullable Object> entry = immutableEntry(key, value); + checkNotNull(actual); if (!actual.entrySet().contains(entry)) { - List keyList = singletonList(key); - List valueList = singletonList(value); + List<@Nullable Object> keyList = singletonList(key); + List<@Nullable Object> valueList = singletonList(value); if (actual.containsKey(key)) { Object actualValue = actual.get(key); /* @@ -138,7 +139,7 @@ public final void containsEntry(@Nullable Object key, @Nullable Object value) { retainMatchingToString(actual.keySet(), /* itemsToCheck= */ keyList))), fact("full contents", actualCustomStringRepresentationForPackageMembersToCall())); } else if (actual.containsValue(value)) { - Set keys = new LinkedHashSet<>(); + Set<@Nullable Object> keys = new LinkedHashSet<>(); for (Map.Entry actualEntry : actual.entrySet()) { if (Objects.equal(actualEntry.getValue(), value)) { keys.add(actualEntry.getKey()); @@ -168,7 +169,7 @@ public final void containsEntry(@Nullable Object key, @Nullable Object value) { /** Fails if the map contains the given entry. */ public final void doesNotContainEntry(@Nullable Object key, @Nullable Object value) { checkNoNeedToDisplayBothValues("entrySet()") - .that(actual.entrySet()) + .that(checkNotNull(actual).entrySet()) .doesNotContain(immutableEntry(key, value)); } @@ -198,7 +199,7 @@ public final Ordered containsAtLeast( return containsAtLeastEntriesIn(accumulateMap("containsAtLeast", k0, v0, rest)); } - private static Map accumulateMap( + private static Map<@Nullable Object, @Nullable Object> accumulateMap( String functionName, @Nullable Object k0, @Nullable Object v0, @Nullable Object... rest) { checkArgument( rest.length % 2 == 0, @@ -206,9 +207,9 @@ private static Map accumulateMap( + "(i.e., the number of key/value parameters (%s) must be even).", rest.length + 2); - Map expectedMap = Maps.newLinkedHashMap(); + Map<@Nullable Object, @Nullable Object> expectedMap = Maps.newLinkedHashMap(); expectedMap.put(k0, v0); - Multiset keys = LinkedHashMultiset.create(); + Multiset<@Nullable Object> keys = LinkedHashMultiset.create(); keys.add(k0); for (int i = 0; i < rest.length; i += 2) { Object key = rest[i]; @@ -227,7 +228,7 @@ private static Map accumulateMap( @CanIgnoreReturnValue public final Ordered containsExactlyEntriesIn(Map expectedMap) { if (expectedMap.isEmpty()) { - if (actual.isEmpty()) { + if (checkNotNull(actual).isEmpty()) { return IN_ORDER; } else { isEmpty(); // fails @@ -259,8 +260,8 @@ public final Ordered containsAtLeastEntriesIn(Map expectedMap) { @CanIgnoreReturnValue private boolean containsEntriesInAnyOrder(Map expectedMap, boolean allowUnexpected) { - MapDifference diff = - MapDifference.create(actual, expectedMap, allowUnexpected, EQUALITY); + MapDifference<@Nullable Object, @Nullable Object, @Nullable Object> diff = + MapDifference.create(checkNotNull(actual), expectedMap, allowUnexpected, EQUALITY); if (diff.isEmpty()) { return true; } @@ -284,37 +285,39 @@ private boolean containsEntriesInAnyOrder(Map expectedMap, boolean allowUn return false; } - private interface ValueTester { - boolean test(@Nullable A actualValue, @Nullable E expectedValue); + private interface ValueTester { + boolean test(A actualValue, E expectedValue); } @SuppressWarnings("UnnecessaryAnonymousClass") // for Java 7 compatibility - private static final ValueTester EQUALITY = - new ValueTester() { + private static final ValueTester<@Nullable Object, @Nullable Object> EQUALITY = + new ValueTester<@Nullable Object, @Nullable Object>() { @Override public boolean test(@Nullable Object actualValue, @Nullable Object expectedValue) { return Objects.equal(actualValue, expectedValue); } }; - private interface Differ { - String diff(A actual, E expected); + private interface Differ { + @Nullable String diff(A actual, E expected); } // This is mostly like the MapDifference code in com.google.common.collect, generalized to remove // the requirement that the values of the two maps are of the same type and are compared with a // symmetric Equivalence. - private static class MapDifference { + private static class MapDifference< + K extends @Nullable Object, A extends @Nullable Object, E extends @Nullable Object> { private final Map missing; private final Map unexpected; private final Map> wrongValues; private final Set allKeys; - static MapDifference create( - Map actual, - Map expected, - boolean allowUnexpected, - ValueTester valueTester) { + static + MapDifference create( + Map actual, + Map expected, + boolean allowUnexpected, + ValueTester valueTester) { Map unexpected = new LinkedHashMap<>(actual); Map missing = new LinkedHashMap<>(); Map> wrongValues = new LinkedHashMap<>(); @@ -322,7 +325,7 @@ static MapDifference create( K expectedKey = expectedEntry.getKey(); E expectedValue = expectedEntry.getValue(); if (actual.containsKey(expectedKey)) { - A actualValue = unexpected.remove(expectedKey); + A actualValue = (A) unexpected.remove(expectedKey); if (!valueTester.test(actualValue, expectedValue)) { wrongValues.put(expectedKey, new ValueDifference<>(actualValue, expectedValue)); } @@ -390,11 +393,11 @@ private boolean includeKeyTypes() { } } - private static class ValueDifference { + private static class ValueDifference { private final A actual; private final E expected; - ValueDifference(@Nullable A actual, @Nullable E expected) { + ValueDifference(A actual, E expected) { this.actual = actual; this.expected = expected; } @@ -417,7 +420,7 @@ ImmutableList describe(@Nullable Differ differ) { } } - private static String maybeAddType(Object object, boolean includeTypes) { + private static String maybeAddType(@Nullable Object object, boolean includeTypes) { return includeTypes ? lenientFormat("%s (%s)", object, objectToTypeName(object)) : String.valueOf(object); @@ -447,6 +450,7 @@ private class MapInOrder implements Ordered { @Override public void inOrder() { // We're using the fact that Sets.intersection keeps the order of the first set. + checkNotNull(actual); List expectedKeyOrder = Lists.newArrayList(Sets.intersection(expectedMap.keySet(), actual.keySet())); List actualKeyOrder = @@ -509,8 +513,9 @@ public void inOrder() {} * encounter an actual value that is not of type {@code A} or an expected value that is not of * type {@code E}. */ - public final UsingCorrespondence comparingValuesUsing( - Correspondence correspondence) { + public final + UsingCorrespondence comparingValuesUsing( + Correspondence correspondence) { return new UsingCorrespondence<>(correspondence); } @@ -552,7 +557,7 @@ public final UsingCorrespondence formattingDiffsUsing( * *

Note that keys will always be compared with regular object equality ({@link Object#equals}). */ - public final class UsingCorrespondence { + public final class UsingCorrespondence { private final Correspondence correspondence; @@ -564,18 +569,18 @@ private UsingCorrespondence(Correspondence correspondence) * Fails if the map does not contain an entry with the given key and a value that corresponds to * the given value. */ - public void containsEntry(@Nullable Object expectedKey, @Nullable E expectedValue) { - if (actual.containsKey(expectedKey)) { + public void containsEntry(@Nullable Object expectedKey, E expectedValue) { + if (checkNotNull(actual).containsKey(expectedKey)) { // Found matching key. A actualValue = getCastSubject().get(expectedKey); Correspondence.ExceptionStore exceptions = Correspondence.ExceptionStore.forMapValues(); - if (correspondence.safeCompare(actualValue, expectedValue, exceptions)) { + if (correspondence.safeCompare((A) actualValue, expectedValue, exceptions)) { // The expected key had the expected value. There's no need to check exceptions here, // because if Correspondence.compare() threw then safeCompare() would return false. return; } // Found matching key with non-matching value. - String diff = correspondence.safeFormatDiff(actualValue, expectedValue, exceptions); + String diff = correspondence.safeFormatDiff((A) actualValue, expectedValue, exceptions); if (diff != null) { failWithoutActual( ImmutableList.builder() @@ -600,7 +605,7 @@ public void containsEntry(@Nullable Object expectedKey, @Nullable E expectedValu } } else { // Did not find matching key. Look for the matching value with a different key. - Set keys = new LinkedHashSet<>(); + Set<@Nullable Object> keys = new LinkedHashSet<>(); Correspondence.ExceptionStore exceptions = Correspondence.ExceptionStore.forMapValues(); for (Map.Entry actualEntry : getCastSubject().entrySet()) { if (correspondence.safeCompare(actualEntry.getValue(), expectedValue, exceptions)) { @@ -638,12 +643,12 @@ public void containsEntry(@Nullable Object expectedKey, @Nullable E expectedValu * Fails if the map contains an entry with the given key and a value that corresponds to the * given value. */ - public void doesNotContainEntry(@Nullable Object excludedKey, @Nullable E excludedValue) { - if (actual.containsKey(excludedKey)) { + public void doesNotContainEntry(@Nullable Object excludedKey, E excludedValue) { + if (checkNotNull(actual).containsKey(excludedKey)) { // Found matching key. Fail if the value matches, too. A actualValue = getCastSubject().get(excludedKey); Correspondence.ExceptionStore exceptions = Correspondence.ExceptionStore.forMapValues(); - if (correspondence.safeCompare(actualValue, excludedValue, exceptions)) { + if (correspondence.safeCompare((A) actualValue, excludedValue, exceptions)) { // The matching key had a matching value. There's no need to check exceptions here, // because if Correspondence.compare() threw then safeCompare() would return false. failWithoutActual( @@ -714,7 +719,7 @@ public Ordered containsAtLeast(@Nullable Object k0, @Nullable E v0, @Nullable Ob @CanIgnoreReturnValue public Ordered containsExactlyEntriesIn(Map expectedMap) { if (expectedMap.isEmpty()) { - if (actual.isEmpty()) { + if (checkNotNull(actual).isEmpty()) { return IN_ORDER; } else { isEmpty(); // fails @@ -736,10 +741,10 @@ public Ordered containsAtLeastEntriesIn(Map expectedMap) { return internalContainsEntriesIn(expectedMap, /* allowUnexpected = */ true); } - private Ordered internalContainsEntriesIn( + private Ordered internalContainsEntriesIn( Map expectedMap, boolean allowUnexpected) { final Correspondence.ExceptionStore exceptions = Correspondence.ExceptionStore.forMapValues(); - MapDifference diff = + MapDifference<@Nullable Object, A, V> diff = MapDifference.create( getCastSubject(), expectedMap, @@ -772,7 +777,7 @@ public boolean test(A actualValue, E expectedValue) { private Differ differ(final Correspondence.ExceptionStore exceptions) { return new Differ() { @Override - public String diff(A actual, V expected) { + public @Nullable String diff(A actual, V expected) { return correspondence.safeFormatDiff(actual, expected, exceptions); } }; @@ -780,7 +785,7 @@ public String diff(A actual, V expected) { @SuppressWarnings("unchecked") // throwing ClassCastException is the correct behaviour private Map getCastSubject() { - return (Map) actual; + return (Map) checkNotNull(actual); } } } diff --git a/core/src/main/java/com/google/common/truth/MultimapSubject.java b/core/src/main/java/com/google/common/truth/MultimapSubject.java index a80545ad2..fafc13633 100644 --- a/core/src/main/java/com/google/common/truth/MultimapSubject.java +++ b/core/src/main/java/com/google/common/truth/MultimapSubject.java @@ -40,6 +40,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; @@ -63,7 +64,7 @@ public class MultimapSubject extends Subject { public void inOrder() {} }; - private final Multimap actual; + private final @Nullable Multimap actual; /** * Constructor for use by subclasses. If you want to create an instance of this class itself, call @@ -83,14 +84,14 @@ protected MultimapSubject(FailureMetadata metadata, @Nullable Multimap mul /** Fails if the multimap is not empty. */ public final void isEmpty() { - if (!actual.isEmpty()) { + if (!checkNotNull(actual).isEmpty()) { failWithActual(simpleFact("expected to be empty")); } } /** Fails if the multimap is empty. */ public final void isNotEmpty() { - if (actual.isEmpty()) { + if (checkNotNull(actual).isEmpty()) { failWithoutActual(simpleFact("expected not to be empty")); } } @@ -98,25 +99,26 @@ public final void isNotEmpty() { /** Fails if the multimap does not have the given size. */ public final void hasSize(int expectedSize) { checkArgument(expectedSize >= 0, "expectedSize(%s) must be >= 0", expectedSize); - check("size()").that(actual.size()).isEqualTo(expectedSize); + check("size()").that(checkNotNull(actual).size()).isEqualTo(expectedSize); } /** Fails if the multimap does not contain the given key. */ public final void containsKey(@Nullable Object key) { - check("keySet()").that(actual.keySet()).contains(key); + check("keySet()").that(checkNotNull(actual).keySet()).contains(key); } /** Fails if the multimap contains the given key. */ public final void doesNotContainKey(@Nullable Object key) { - check("keySet()").that(actual.keySet()).doesNotContain(key); + check("keySet()").that(checkNotNull(actual).keySet()).doesNotContain(key); } /** Fails if the multimap does not contain the given entry. */ public final void containsEntry(@Nullable Object key, @Nullable Object value) { // TODO(kak): Can we share any of this logic w/ MapSubject.containsEntry()? + checkNotNull(actual); if (!actual.containsEntry(key, value)) { - Map.Entry entry = immutableEntry(key, value); - List> entryList = ImmutableList.of(entry); + Map.Entry<@Nullable Object, @Nullable Object> entry = immutableEntry(key, value); + List> entryList = ImmutableList.of(entry); // TODO(cpovirk): If the key is present but not with the right value, we could fail using // something like valuesForKey(key).contains(value). Consider whether this is worthwhile. if (hasMatchingToStringPair(actual.entries(), entryList)) { @@ -136,7 +138,7 @@ public final void containsEntry(@Nullable Object key, @Nullable Object value) { fact("though it did contain values with that key", actual.asMap().get(key)), fact("full contents", actualCustomStringRepresentationForPackageMembersToCall())); } else if (actual.containsValue(value)) { - Set keys = new LinkedHashSet<>(); + Set<@Nullable Object> keys = new LinkedHashSet<>(); for (Map.Entry actualEntry : actual.entries()) { if (Objects.equal(actualEntry.getValue(), value)) { keys.add(actualEntry.getKey()); @@ -156,7 +158,7 @@ public final void containsEntry(@Nullable Object key, @Nullable Object value) { /** Fails if the multimap contains the given entry. */ public final void doesNotContainEntry(@Nullable Object key, @Nullable Object value) { checkNoNeedToDisplayBothValues("entries()") - .that(actual.entries()) + .that(checkNotNull(actual).entries()) .doesNotContain(immutableEntry(key, value)); } @@ -177,7 +179,8 @@ public final void doesNotContainEntry(@Nullable Object key, @Nullable Object val * currently they must perform it _after_. */ public IterableSubject valuesForKey(@Nullable Object key) { - return check("valuesForKey(%s)", key).that(((Multimap) actual).get(key)); + return check("valuesForKey(%s)", key) + .that(((Multimap<@Nullable Object, @Nullable Object>) checkNotNull(actual)).get(key)); } @Override @@ -202,9 +205,9 @@ public final void isEqualTo(@Nullable Object other) { lenientFormat( "a %s cannot equal a %s if either is non-empty", actualType, otherType))); } else if (actual instanceof ListMultimap) { - containsExactlyEntriesIn((Multimap) other).inOrder(); + containsExactlyEntriesIn((Multimap) checkNotNull(other)).inOrder(); } else if (actual instanceof SetMultimap) { - containsExactlyEntriesIn((Multimap) other); + containsExactlyEntriesIn((Multimap) checkNotNull(other)); } else { super.isEqualTo(other); } @@ -221,6 +224,7 @@ public final void isEqualTo(@Nullable Object other) { @CanIgnoreReturnValue public final Ordered containsExactlyEntriesIn(Multimap expectedMultimap) { checkNotNull(expectedMultimap, "expectedMultimap"); + checkNotNull(actual); ListMultimap missing = difference(expectedMultimap, actual); ListMultimap extra = difference(actual, expectedMultimap); @@ -276,6 +280,7 @@ public final Ordered containsExactlyEntriesIn(Multimap expectedMultimap) { @CanIgnoreReturnValue public final Ordered containsAtLeastEntriesIn(Multimap expectedMultimap) { checkNotNull(expectedMultimap, "expectedMultimap"); + checkNotNull(actual); ListMultimap missing = difference(expectedMultimap, actual); // TODO(kak): Possible enhancement: Include "[1 copy]" if the element does appear in @@ -294,7 +299,7 @@ public final Ordered containsAtLeastEntriesIn(Multimap expectedMultimap) { /** Fails if the multimap is not empty. */ @CanIgnoreReturnValue public final Ordered containsExactly() { - return check().about(iterableEntries()).that(actual.entries()).containsExactly(); + return check().about(iterableEntries()).that(checkNotNull(actual).entries()).containsExactly(); } /** @@ -321,7 +326,7 @@ public final Ordered containsAtLeast( return containsAtLeastEntriesIn(accumulateMultimap(k0, v0, rest)); } - private static Multimap accumulateMultimap( + private static Multimap<@Nullable Object, @Nullable Object> accumulateMultimap( @Nullable Object k0, @Nullable Object v0, @Nullable Object... rest) { checkArgument( rest.length % 2 == 0, @@ -329,7 +334,8 @@ private static Multimap accumulateMultimap( + "(i.e., the number of key/value parameters (%s) must be even).", rest.length + 2); - LinkedListMultimap expectedMultimap = LinkedListMultimap.create(); + LinkedListMultimap<@Nullable Object, @Nullable Object> expectedMultimap = + LinkedListMultimap.create(); expectedMultimap.put(k0, v0); for (int i = 0; i < rest.length; i += 2) { expectedMultimap.put(rest[i], rest[i + 1]); @@ -340,8 +346,8 @@ private static Multimap accumulateMultimap( private Factory> iterableEntries() { return new Factory>() { @Override - public IterableSubject createSubject(FailureMetadata metadata, Iterable actual) { - return new IterableEntries(metadata, MultimapSubject.this, actual); + public IterableSubject createSubject(FailureMetadata metadata, @Nullable Iterable actual) { + return new IterableEntries(metadata, MultimapSubject.this, checkNotNull(actual)); } }; } @@ -352,7 +358,7 @@ private static class IterableEntries extends IterableSubject { IterableEntries(FailureMetadata metadata, MultimapSubject multimapSubject, Iterable actual) { super(metadata, actual); // We want to use the multimap's toString() instead of the iterable of entries' toString(): - this.stringRepresentation = multimapSubject.actual.toString(); + this.stringRepresentation = String.valueOf(multimapSubject.actual); } @Override @@ -379,11 +385,12 @@ private class MultimapInOrder implements Ordered { @Override public void inOrder() { // We use the fact that Sets.intersection's result has the same order as the first parameter + checkNotNull(actual); boolean keysInOrder = Lists.newArrayList(Sets.intersection(actual.keySet(), expectedMultimap.keySet())) .equals(Lists.newArrayList(expectedMultimap.keySet())); - LinkedHashSet keysWithValuesOutOfOrder = Sets.newLinkedHashSet(); + LinkedHashSet<@Nullable Object> keysWithValuesOutOfOrder = Sets.newLinkedHashSet(); for (Object key : expectedMultimap.keySet()) { List actualVals = Lists.newArrayList(get(actual, key)); List expectedVals = Lists.newArrayList(get(expectedMultimap, key)); @@ -432,7 +439,7 @@ public void inOrder() { * where the contract explicitly states that the iterator isn't advanced beyond the value if the * value is found. */ - private static boolean advanceToFind(Iterator iterator, Object value) { + private static boolean advanceToFind(Iterator iterator, @Nullable Object value) { while (iterator.hasNext()) { if (Objects.equal(iterator.next(), value)) { return true; @@ -441,16 +448,17 @@ private static boolean advanceToFind(Iterator iterator, Object value) { return false; } - private static Collection get(Multimap multimap, @Nullable Object key) { + private static Collection get( + Multimap multimap, @Nullable Object key) { if (multimap.containsKey(key)) { - return multimap.asMap().get(key); + return checkNotNull(multimap.asMap().get(key)); } else { - return ImmutableList.of(); + return Collections.emptyList(); } } private static ListMultimap difference(Multimap minuend, Multimap subtrahend) { - ListMultimap difference = LinkedListMultimap.create(); + ListMultimap<@Nullable Object, @Nullable Object> difference = LinkedListMultimap.create(); for (Object key : minuend.keySet()) { List valDifference = difference( @@ -461,8 +469,9 @@ private static Collection get(Multimap multimap, @Nullable Object k } private static List difference(List minuend, List subtrahend) { - LinkedHashMultiset remaining = LinkedHashMultiset.create(subtrahend); - List difference = Lists.newArrayList(); + LinkedHashMultiset<@Nullable Object> remaining = + LinkedHashMultiset.<@Nullable Object>create(subtrahend); + List<@Nullable Object> difference = Lists.newArrayList(); for (Object elem : minuend) { if (!remaining.remove(elem)) { difference.add(elem); @@ -492,7 +501,8 @@ private static String countDuplicatesMultimap(Multimap multimap) { */ private static Multimap annotateEmptyStringsMultimap(Multimap multimap) { if (multimap.containsKey("") || multimap.containsValue("")) { - ListMultimap annotatedMultimap = LinkedListMultimap.create(); + ListMultimap<@Nullable Object, @Nullable Object> annotatedMultimap = + LinkedListMultimap.create(); for (Map.Entry entry : multimap.entries()) { Object key = "".equals(entry.getKey()) ? HUMAN_UNDERSTANDABLE_EMPTY_STRING : entry.getKey(); Object value = @@ -526,8 +536,9 @@ private static String countDuplicatesMultimap(Multimap multimap) { *

Any of the methods on the returned object may throw {@link ClassCastException} if they * encounter an actual value that is not of type {@code A}. */ - public UsingCorrespondence comparingValuesUsing( - Correspondence correspondence) { + public + UsingCorrespondence comparingValuesUsing( + Correspondence correspondence) { return new UsingCorrespondence<>(correspondence); } @@ -542,7 +553,7 @@ public UsingCorrespondence comparingValuesUsing( * *

Note that keys will always be compared with regular object equality ({@link Object#equals}). */ - public final class UsingCorrespondence { + public final class UsingCorrespondence { private final Correspondence correspondence; @@ -554,10 +565,10 @@ private UsingCorrespondence(Correspondence correspondence) * Fails if the multimap does not contain an entry with the given key and a value that * corresponds to the given value. */ - public void containsEntry(@Nullable Object expectedKey, @Nullable E expectedValue) { - if (actual.containsKey(expectedKey)) { + public void containsEntry(@Nullable Object expectedKey, E expectedValue) { + if (checkNotNull(actual).containsKey(expectedKey)) { // Found matching key. - Collection actualValues = getCastActual().asMap().get(expectedKey); + Collection actualValues = checkNotNull(getCastActual().asMap().get(expectedKey)); Correspondence.ExceptionStore exceptions = Correspondence.ExceptionStore.forMapValues(); for (A actualValue : actualValues) { if (correspondence.safeCompare(actualValue, expectedValue, exceptions)) { @@ -647,9 +658,9 @@ public void containsEntry(@Nullable Object expectedKey, @Nullable E expectedValu * Fails if the multimap contains an entry with the given key and a value that corresponds to * the given value. */ - public void doesNotContainEntry(@Nullable Object excludedKey, @Nullable E excludedValue) { - if (actual.containsKey(excludedKey)) { - Collection actualValues = getCastActual().asMap().get(excludedKey); + public void doesNotContainEntry(@Nullable Object excludedKey, E excludedValue) { + if (checkNotNull(actual).containsKey(excludedKey)) { + Collection actualValues = checkNotNull(getCastActual().asMap().get(excludedKey)); List matchingValues = new ArrayList<>(); Correspondence.ExceptionStore exceptions = Correspondence.ExceptionStore.forMapValues(); for (A actualValue : actualValues) { @@ -714,7 +725,7 @@ public Ordered containsExactlyEntriesIn(Multimap expectedMultima * public containsExactlyEntriesIn method. This is recommended by Effective Java item 31 (3rd * edition). */ - private Ordered internalContainsExactlyEntriesIn( + private Ordered internalContainsExactlyEntriesIn( Multimap expectedMultimap) { // Note: The non-fuzzy MultimapSubject.containsExactlyEntriesIn has a custom implementation // and produces somewhat better failure messages simply asserting about the iterables of @@ -724,7 +735,7 @@ private Ordered internalContainsExactlyEntriesIn( // complexity for little gain. return check() .about(iterableEntries()) - .that(actual.entries()) + .that(checkNotNull(actual).entries()) .comparingElementsUsing(new EntryCorrespondence(correspondence)) .containsExactlyElementsIn(expectedMultimap.entries()); } @@ -748,7 +759,7 @@ public Ordered containsAtLeastEntriesIn(Multimap expectedMultima * public containsAtLeastEntriesIn method. This is recommended by Effective Java item 31 (3rd * edition). */ - private Ordered internalContainsAtLeastEntriesIn( + private Ordered internalContainsAtLeastEntriesIn( Multimap expectedMultimap) { // Note: The non-fuzzy MultimapSubject.containsAtLeastEntriesIn has a custom implementation // and produces somewhat better failure messages simply asserting about the iterables of @@ -758,7 +769,7 @@ private Ordered internalContainsAtLeastEntriesIn( // complexity for little gain. return check() .about(iterableEntries()) - .that(actual.entries()) + .that(checkNotNull(actual).entries()) .comparingElementsUsing(new EntryCorrespondence(correspondence)) .containsAtLeastElementsIn(expectedMultimap.entries()); } @@ -797,11 +808,12 @@ public Ordered containsAtLeast(@Nullable Object k0, @Nullable E v0, @Nullable Ob @SuppressWarnings("unchecked") // throwing ClassCastException is the correct behaviour private Multimap getCastActual() { - return (Multimap) actual; + return (Multimap) checkNotNull(actual); } } - private static final class EntryCorrespondence + private static final class EntryCorrespondence< + K extends @Nullable Object, A extends @Nullable Object, E extends @Nullable Object> extends Correspondence, Map.Entry> { private final Correspondence valueCorrespondence; diff --git a/core/src/main/java/com/google/common/truth/MultisetSubject.java b/core/src/main/java/com/google/common/truth/MultisetSubject.java index 40037fe38..9e8a63151 100644 --- a/core/src/main/java/com/google/common/truth/MultisetSubject.java +++ b/core/src/main/java/com/google/common/truth/MultisetSubject.java @@ -16,6 +16,7 @@ package com.google.common.truth; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.Multiset; import org.checkerframework.checker.nullness.qual.Nullable; @@ -27,7 +28,7 @@ */ public final class MultisetSubject extends IterableSubject { - private final Multiset actual; + private final @Nullable Multiset actual; MultisetSubject(FailureMetadata metadata, @Nullable Multiset multiset) { super(metadata, multiset); @@ -37,7 +38,7 @@ public final class MultisetSubject extends IterableSubject { /** Fails if the element does not have the given count. */ public final void hasCount(@Nullable Object element, int expectedCount) { checkArgument(expectedCount >= 0, "expectedCount(%s) must be >= 0", expectedCount); - int actualCount = ((Multiset) actual).count(element); + int actualCount = checkNotNull(actual).count(element); check("count(%s)", element).that(actualCount).isEqualTo(expectedCount); } } diff --git a/core/src/main/java/com/google/common/truth/ObjectArraySubject.java b/core/src/main/java/com/google/common/truth/ObjectArraySubject.java index 2b6184e42..daa287d26 100644 --- a/core/src/main/java/com/google/common/truth/ObjectArraySubject.java +++ b/core/src/main/java/com/google/common/truth/ObjectArraySubject.java @@ -15,6 +15,8 @@ */ package com.google.common.truth; +import static com.google.common.base.Preconditions.checkNotNull; + import java.util.Arrays; import org.checkerframework.checker.nullness.qual.Nullable; @@ -23,8 +25,8 @@ * * @author Christian Gruber */ -public final class ObjectArraySubject extends AbstractArraySubject { - private final T[] actual; +public final class ObjectArraySubject extends AbstractArraySubject { + private final @Nullable T @Nullable [] actual; ObjectArraySubject( FailureMetadata metadata, @Nullable T @Nullable [] o, @Nullable String typeDescription) { @@ -33,6 +35,6 @@ public final class ObjectArraySubject extends AbstractArraySubject { } public IterableSubject asList() { - return checkNoNeedToDisplayBothValues("asList()").that(Arrays.asList(actual)); + return checkNoNeedToDisplayBothValues("asList()").that(Arrays.asList(checkNotNull(actual))); } } diff --git a/core/src/main/java/com/google/common/truth/Ordered.java b/core/src/main/java/com/google/common/truth/Ordered.java index 00ed0cc3c..2f7efed38 100644 --- a/core/src/main/java/com/google/common/truth/Ordered.java +++ b/core/src/main/java/com/google/common/truth/Ordered.java @@ -15,6 +15,7 @@ */ package com.google.common.truth; + /** * Returned by calls like {@link IterableSubject#containsExactly}, {@code Ordered} lets the caller * additionally check that the expected elements were present in the order they were passed to the diff --git a/core/src/main/java/com/google/common/truth/Platform.java b/core/src/main/java/com/google/common/truth/Platform.java index c0719697a..ccc31b9b7 100644 --- a/core/src/main/java/com/google/common/truth/Platform.java +++ b/core/src/main/java/com/google/common/truth/Platform.java @@ -15,6 +15,7 @@ */ package com.google.common.truth; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Suppliers.memoize; import static com.google.common.base.Throwables.throwIfUnchecked; import static com.google.common.truth.DiffUtils.generateUnifiedDiff; @@ -61,7 +62,7 @@ static boolean containsMatch(String actual, String regex) { static Throwable[] getSuppressed(Throwable throwable) { try { Method getSuppressed = throwable.getClass().getMethod("getSuppressed"); - return (Throwable[]) getSuppressed.invoke(throwable); + return (Throwable[]) checkNotNull(getSuppressed.invoke(throwable)); } catch (NoSuchMethodException e) { return new Throwable[0]; } catch (IllegalAccessException e) { @@ -80,6 +81,8 @@ static void cleanStackTrace(Throwable throwable) { * the value passed to {@code assertThat} or {@code that}, as distinct from any later actual * values produced by chaining calls like {@code hasMessageThat}. */ + // Checker complains that first invoke argument is null. + @SuppressWarnings("argument.type.incompatible") static @Nullable String inferDescription() { if (isInferDescriptionDisabled()) { return null; @@ -183,7 +186,7 @@ public final String getMessage() { @Override @SuppressWarnings("UnsynchronizedOverridesSynchronized") - public final Throwable getCause() { + public final @Nullable Throwable getCause() { return cause; } @@ -191,7 +194,7 @@ public final Throwable getCause() { // TODO(cpovirk): Write a test that fails without this. Ditto for SimpleAssertionError. @Override public final String toString() { - return getLocalizedMessage(); + return checkNotNull(getLocalizedMessage()); } } @@ -204,7 +207,7 @@ static String floatToString(float value) { } /** Turns a non-double, non-float object into a string. */ - static String stringValueOfNonFloatingPoint(Object o) { + static String stringValueOfNonFloatingPoint(@Nullable Object o) { return String.valueOf(o); } @@ -215,7 +218,7 @@ static String getStackTraceAsString(Throwable throwable) { /** Tests if current platform is Android. */ static boolean isAndroid() { - return System.getProperty("java.runtime.name").contains("Android"); + return checkNotNull(System.getProperty("java.runtime.name", "")).contains("Android"); } /** @@ -308,27 +311,31 @@ static boolean isKotlinRange(Iterable iterable) { // (If the class isn't available, then nothing could be an instance of ClosedRange.) } - private static final Supplier> closedRangeClassIfAvailable = + // Not using lambda here because of wrong nullability type inference in this case. + private static final Supplier<@Nullable Class> closedRangeClassIfAvailable = memoize( - () -> { - try { - return Class.forName("kotlin.ranges.ClosedRange"); - /* - * TODO(cpovirk): Consider looking up the Method we'll need here, too: If it's not - * present (maybe because Proguard stripped it, similar to cl/462826082), then we - * don't want our caller to continue on to call kotlinRangeContains, since it won't be - * able to give an answer about what ClosedRange.contains will return. (Alternatively, - * we could make kotlinRangeContains contain its own fallback to Iterables.contains. - * Conceivably its first fallback could even be to try reading `start` and - * `endInclusive` from the ClosedRange instance, but even then, we'd want to check in - * advance whether we're able to access those.) - */ - } catch (ClassNotFoundException notAvailable) { - return null; + new Supplier<@Nullable Class>() { + @Override + public @Nullable Class get() { + try { + return Class.forName("kotlin.ranges.ClosedRange"); + /* + * TODO(cpovirk): Consider looking up the Method we'll need here, too: If it's not + * present (maybe because Proguard stripped it, similar to cl/462826082), then we + * don't want our caller to continue on to call kotlinRangeContains, since it won't + * be able to give an answer about what ClosedRange.contains will return. + * (Alternatively, we could make kotlinRangeContains contain its own fallback to + * Iterables.contains. Conceivably its first fallback could even be to try reading + * `start` and `endInclusive` from the ClosedRange instance, but even then, we'd + * want to check in advance whether we're able to access those.) + */ + } catch (ClassNotFoundException notAvailable) { + return null; + } } }); - static boolean kotlinRangeContains(Iterable haystack, Object needle) { + static boolean kotlinRangeContains(Iterable haystack, @Nullable Object needle) { try { return (boolean) closedRangeContainsMethod.get().invoke(haystack, needle); } catch (InvocationTargetException e) { @@ -349,7 +356,8 @@ static boolean kotlinRangeContains(Iterable haystack, Object needle) { memoize( () -> { try { - return closedRangeClassIfAvailable.get().getMethod("contains", Comparable.class); + return checkNotNull(closedRangeClassIfAvailable.get()) + .getMethod("contains", Comparable.class); } catch (NoSuchMethodException e) { // That method exists. (But see the discussion at closedRangeClassIfAvailable above.) throw newLinkageError(e); diff --git a/core/src/main/java/com/google/common/truth/PrimitiveBooleanArraySubject.java b/core/src/main/java/com/google/common/truth/PrimitiveBooleanArraySubject.java index a4cf61cf5..33ff6d895 100644 --- a/core/src/main/java/com/google/common/truth/PrimitiveBooleanArraySubject.java +++ b/core/src/main/java/com/google/common/truth/PrimitiveBooleanArraySubject.java @@ -15,6 +15,8 @@ */ package com.google.common.truth; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.primitives.Booleans; import org.checkerframework.checker.nullness.qual.Nullable; @@ -24,7 +26,7 @@ * @author Christian Gruber (cgruber@israfil.net) */ public final class PrimitiveBooleanArraySubject extends AbstractArraySubject { - private final boolean[] actual; + private final boolean @Nullable [] actual; PrimitiveBooleanArraySubject( FailureMetadata metadata, boolean @Nullable [] o, @Nullable String typeDescription) { @@ -33,6 +35,6 @@ public final class PrimitiveBooleanArraySubject extends AbstractArraySubject { } public IterableSubject asList() { - return checkNoNeedToDisplayBothValues("asList()").that(Booleans.asList(actual)); + return checkNoNeedToDisplayBothValues("asList()").that(Booleans.asList(checkNotNull(actual))); } } diff --git a/core/src/main/java/com/google/common/truth/PrimitiveByteArraySubject.java b/core/src/main/java/com/google/common/truth/PrimitiveByteArraySubject.java index 204ad3840..f8169ca4d 100644 --- a/core/src/main/java/com/google/common/truth/PrimitiveByteArraySubject.java +++ b/core/src/main/java/com/google/common/truth/PrimitiveByteArraySubject.java @@ -15,6 +15,8 @@ */ package com.google.common.truth; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.primitives.Bytes; import org.checkerframework.checker.nullness.qual.Nullable; @@ -24,7 +26,7 @@ * @author Kurt Alfred Kluever */ public final class PrimitiveByteArraySubject extends AbstractArraySubject { - private final byte[] actual; + private final byte @Nullable [] actual; PrimitiveByteArraySubject( FailureMetadata metadata, byte @Nullable [] o, @Nullable String typeDescription) { @@ -33,6 +35,6 @@ public final class PrimitiveByteArraySubject extends AbstractArraySubject { } public IterableSubject asList() { - return checkNoNeedToDisplayBothValues("asList()").that(Bytes.asList(actual)); + return checkNoNeedToDisplayBothValues("asList()").that(Bytes.asList(checkNotNull(actual))); } } diff --git a/core/src/main/java/com/google/common/truth/PrimitiveCharArraySubject.java b/core/src/main/java/com/google/common/truth/PrimitiveCharArraySubject.java index 5a89e71c4..79e6f12eb 100644 --- a/core/src/main/java/com/google/common/truth/PrimitiveCharArraySubject.java +++ b/core/src/main/java/com/google/common/truth/PrimitiveCharArraySubject.java @@ -15,6 +15,8 @@ */ package com.google.common.truth; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.primitives.Chars; import org.checkerframework.checker.nullness.qual.Nullable; @@ -24,7 +26,7 @@ * @author Christian Gruber (cgruber@israfil.net) */ public final class PrimitiveCharArraySubject extends AbstractArraySubject { - private final char[] actual; + private final char @Nullable [] actual; PrimitiveCharArraySubject( FailureMetadata metadata, char @Nullable [] o, @Nullable String typeDescription) { @@ -33,6 +35,6 @@ public final class PrimitiveCharArraySubject extends AbstractArraySubject { } public IterableSubject asList() { - return checkNoNeedToDisplayBothValues("asList()").that(Chars.asList(actual)); + return checkNoNeedToDisplayBothValues("asList()").that(Chars.asList(checkNotNull(actual))); } } diff --git a/core/src/main/java/com/google/common/truth/PrimitiveDoubleArraySubject.java b/core/src/main/java/com/google/common/truth/PrimitiveDoubleArraySubject.java index 4fb7ad455..6fdb27507 100644 --- a/core/src/main/java/com/google/common/truth/PrimitiveDoubleArraySubject.java +++ b/core/src/main/java/com/google/common/truth/PrimitiveDoubleArraySubject.java @@ -31,7 +31,7 @@ * @author Christian Gruber (cgruber@israfil.net) */ public final class PrimitiveDoubleArraySubject extends AbstractArraySubject { - private final double[] actual; + private final double @Nullable [] actual; PrimitiveDoubleArraySubject( FailureMetadata metadata, double @Nullable [] o, @Nullable String typeDescription) { @@ -65,7 +65,7 @@ public final class PrimitiveDoubleArraySubject extends AbstractArraySubject { */ // TODO(cpovirk): Move some or all of this Javadoc to the supertype, maybe deleting this override? @Override - public void isEqualTo(Object expected) { + public void isEqualTo(@Nullable Object expected) { super.isEqualTo(expected); } @@ -84,7 +84,7 @@ public void isEqualTo(Object expected) { * */ @Override - public void isNotEqualTo(Object expected) { + public void isNotEqualTo(@Nullable Object expected) { super.isNotEqualTo(expected); } @@ -234,7 +234,7 @@ public void containsNoneOf(double[] excluded) { private IterableSubject iterableSubject() { return checkNoNeedToDisplayBothValues("asList()") .about(iterablesWithCustomDoubleToString()) - .that(Doubles.asList(actual)); + .that(Doubles.asList(checkNotNull(actual))); } /* @@ -248,7 +248,7 @@ private IterableSubject iterableSubject() { private Factory> iterablesWithCustomDoubleToString() { return new Factory>() { @Override - public IterableSubject createSubject(FailureMetadata metadata, Iterable actual) { + public IterableSubject createSubject(FailureMetadata metadata, @Nullable Iterable actual) { return new IterableSubjectWithInheritedToString(metadata, actual); } }; @@ -256,7 +256,7 @@ public IterableSubject createSubject(FailureMetadata metadata, Iterable actua private final class IterableSubjectWithInheritedToString extends IterableSubject { - IterableSubjectWithInheritedToString(FailureMetadata metadata, Iterable actual) { + IterableSubjectWithInheritedToString(FailureMetadata metadata, @Nullable Iterable actual) { super(metadata, actual); } diff --git a/core/src/main/java/com/google/common/truth/PrimitiveFloatArraySubject.java b/core/src/main/java/com/google/common/truth/PrimitiveFloatArraySubject.java index 580ff4f41..339a4d5be 100644 --- a/core/src/main/java/com/google/common/truth/PrimitiveFloatArraySubject.java +++ b/core/src/main/java/com/google/common/truth/PrimitiveFloatArraySubject.java @@ -31,7 +31,7 @@ * @author Christian Gruber (cgruber@israfil.net) */ public final class PrimitiveFloatArraySubject extends AbstractArraySubject { - private final float[] actual; + private final float @Nullable [] actual; PrimitiveFloatArraySubject( FailureMetadata metadata, float @Nullable [] o, @Nullable String typeDescription) { @@ -64,7 +64,7 @@ public final class PrimitiveFloatArraySubject extends AbstractArraySubject { * */ @Override - public void isEqualTo(Object expected) { + public void isEqualTo(@Nullable Object expected) { super.isEqualTo(expected); } @@ -83,7 +83,7 @@ public void isEqualTo(Object expected) { * */ @Override - public void isNotEqualTo(Object expected) { + public void isNotEqualTo(@Nullable Object expected) { super.isNotEqualTo(expected); } @@ -239,7 +239,7 @@ public void containsNoneOf(float[] excluded) { private IterableSubject iterableSubject() { return checkNoNeedToDisplayBothValues("asList()") .about(iterablesWithCustomFloatToString()) - .that(Floats.asList(actual)); + .that(Floats.asList(checkNotNull(actual))); } /* @@ -253,7 +253,7 @@ private IterableSubject iterableSubject() { private Factory> iterablesWithCustomFloatToString() { return new Factory>() { @Override - public IterableSubject createSubject(FailureMetadata metadata, Iterable actual) { + public IterableSubject createSubject(FailureMetadata metadata, @Nullable Iterable actual) { return new IterableSubjectWithInheritedToString(metadata, actual); } }; @@ -261,7 +261,7 @@ public IterableSubject createSubject(FailureMetadata metadata, Iterable actua private final class IterableSubjectWithInheritedToString extends IterableSubject { - IterableSubjectWithInheritedToString(FailureMetadata metadata, Iterable actual) { + IterableSubjectWithInheritedToString(FailureMetadata metadata, @Nullable Iterable actual) { super(metadata, actual); } diff --git a/core/src/main/java/com/google/common/truth/PrimitiveIntArraySubject.java b/core/src/main/java/com/google/common/truth/PrimitiveIntArraySubject.java index 0dc669817..b6c64634d 100644 --- a/core/src/main/java/com/google/common/truth/PrimitiveIntArraySubject.java +++ b/core/src/main/java/com/google/common/truth/PrimitiveIntArraySubject.java @@ -15,6 +15,8 @@ */ package com.google.common.truth; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.primitives.Ints; import org.checkerframework.checker.nullness.qual.Nullable; @@ -24,7 +26,7 @@ * @author Christian Gruber (cgruber@israfil.net) */ public final class PrimitiveIntArraySubject extends AbstractArraySubject { - private final int[] actual; + private final int @Nullable [] actual; PrimitiveIntArraySubject( FailureMetadata metadata, int @Nullable [] o, @Nullable String typeDescription) { @@ -33,6 +35,6 @@ public final class PrimitiveIntArraySubject extends AbstractArraySubject { } public IterableSubject asList() { - return checkNoNeedToDisplayBothValues("asList()").that(Ints.asList(actual)); + return checkNoNeedToDisplayBothValues("asList()").that(Ints.asList(checkNotNull(actual))); } } diff --git a/core/src/main/java/com/google/common/truth/PrimitiveLongArraySubject.java b/core/src/main/java/com/google/common/truth/PrimitiveLongArraySubject.java index 53c0d5963..074410863 100644 --- a/core/src/main/java/com/google/common/truth/PrimitiveLongArraySubject.java +++ b/core/src/main/java/com/google/common/truth/PrimitiveLongArraySubject.java @@ -15,6 +15,8 @@ */ package com.google.common.truth; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.primitives.Longs; import org.checkerframework.checker.nullness.qual.Nullable; @@ -24,7 +26,7 @@ * @author Christian Gruber (cgruber@israfil.net) */ public final class PrimitiveLongArraySubject extends AbstractArraySubject { - private final long[] actual; + private final long @Nullable [] actual; PrimitiveLongArraySubject( FailureMetadata metadata, long @Nullable [] o, @Nullable String typeDescription) { @@ -33,6 +35,6 @@ public final class PrimitiveLongArraySubject extends AbstractArraySubject { } public IterableSubject asList() { - return checkNoNeedToDisplayBothValues("asList()").that(Longs.asList(actual)); + return checkNoNeedToDisplayBothValues("asList()").that(Longs.asList(checkNotNull(actual))); } } diff --git a/core/src/main/java/com/google/common/truth/PrimitiveShortArraySubject.java b/core/src/main/java/com/google/common/truth/PrimitiveShortArraySubject.java index c96bf3f39..dcefab478 100644 --- a/core/src/main/java/com/google/common/truth/PrimitiveShortArraySubject.java +++ b/core/src/main/java/com/google/common/truth/PrimitiveShortArraySubject.java @@ -15,6 +15,8 @@ */ package com.google.common.truth; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.primitives.Shorts; import org.checkerframework.checker.nullness.qual.Nullable; @@ -24,7 +26,7 @@ * @author Christian Gruber (cgruber@israfil.net) */ public final class PrimitiveShortArraySubject extends AbstractArraySubject { - private final short[] actual; + private final short @Nullable [] actual; PrimitiveShortArraySubject( FailureMetadata metadata, short @Nullable [] o, @Nullable String typeDescription) { @@ -33,6 +35,6 @@ public final class PrimitiveShortArraySubject extends AbstractArraySubject { } public IterableSubject asList() { - return checkNoNeedToDisplayBothValues("asList()").that(Shorts.asList(actual)); + return checkNoNeedToDisplayBothValues("asList()").that(Shorts.asList(checkNotNull(actual))); } } diff --git a/core/src/main/java/com/google/common/truth/SimpleSubjectBuilder.java b/core/src/main/java/com/google/common/truth/SimpleSubjectBuilder.java index 4451ee464..80caa364c 100644 --- a/core/src/main/java/com/google/common/truth/SimpleSubjectBuilder.java +++ b/core/src/main/java/com/google/common/truth/SimpleSubjectBuilder.java @@ -44,4 +44,5 @@ public final class SimpleSubjectBuilder { public SubjectT that(@Nullable ActualT actual) { return subjectFactory.createSubject(metadata, actual); } + } diff --git a/core/src/main/java/com/google/common/truth/StackTraceCleaner.java b/core/src/main/java/com/google/common/truth/StackTraceCleaner.java index 32841c853..414582711 100644 --- a/core/src/main/java/com/google/common/truth/StackTraceCleaner.java +++ b/core/src/main/java/com/google/common/truth/StackTraceCleaner.java @@ -16,6 +16,7 @@ package com.google.common.truth; import static com.google.common.base.MoreObjects.firstNonNull; +import static com.google.common.base.Preconditions.checkNotNull; import static java.lang.Thread.currentThread; import com.google.common.annotations.GwtIncompatible; @@ -26,6 +27,7 @@ import java.util.List; import java.util.ListIterator; import java.util.Set; +import org.checkerframework.checker.nullness.qual.Nullable; /** Utility that cleans stack traces to remove noise from common frameworks. */ @GwtIncompatible @@ -48,8 +50,8 @@ static void cleanStackTrace(Throwable throwable) { private final Throwable throwable; private final List cleanedStackTrace = new ArrayList<>(); - private StackTraceElementWrapper lastStackFrameElementWrapper = null; - private StackFrameType currentStreakType = null; + private @Nullable StackTraceElementWrapper lastStackFrameElementWrapper = null; + private @Nullable StackFrameType currentStreakType = null; private int currentStreakLength = 0; /** @@ -174,10 +176,11 @@ private void endStreak() { if (currentStreakLength == 1) { // A single frame isn't a streak. Just include the frame as-is in the result. - cleanedStackTrace.add(lastStackFrameElementWrapper); + cleanedStackTrace.add(checkNotNull(lastStackFrameElementWrapper)); } else { // Add a single frame to the result summarizing the streak of framework frames - cleanedStackTrace.add(createStreakReplacementFrame(currentStreakType, currentStreakLength)); + cleanedStackTrace.add( + createStreakReplacementFrame(checkNotNull(currentStreakType), currentStreakLength)); } clearStreak(); @@ -253,8 +256,8 @@ private static boolean isFromClassOrClassNestedInside( return false; } - private static boolean isSubtypeOf(Class subclass, String superclass) { - for (; subclass != null; subclass = subclass.getSuperclass()) { + private static boolean isSubtypeOf(@Nullable Class subclass, String superclass) { + for (; subclass != null; subclass = checkNotNull(subclass).getSuperclass()) { if (subclass.getCanonicalName() != null && subclass.getCanonicalName().equals(superclass)) { return true; } diff --git a/core/src/main/java/com/google/common/truth/StringSubject.java b/core/src/main/java/com/google/common/truth/StringSubject.java index 705bb96b8..1a66fcea6 100644 --- a/core/src/main/java/com/google/common/truth/StringSubject.java +++ b/core/src/main/java/com/google/common/truth/StringSubject.java @@ -32,7 +32,7 @@ * @author Christian Gruber (cgruber@israfil.net) */ public class StringSubject extends ComparableSubject { - private final String actual; + private final @Nullable String actual; /** * Constructor for use by subclasses. If you want to create an instance of this class itself, call @@ -43,17 +43,19 @@ protected StringSubject(FailureMetadata metadata, @Nullable String string) { this.actual = string; } - /** @deprecated Use {@link #isEqualTo} instead. String comparison is consistent with equality. */ + /** + * @deprecated Use {@link #isEqualTo} instead. String comparison is consistent with equality. + */ @Override @Deprecated - public final void isEquivalentAccordingToCompareTo(String other) { + public final void isEquivalentAccordingToCompareTo(@Nullable String other) { super.isEquivalentAccordingToCompareTo(other); } /** Fails if the string does not have the given length. */ public void hasLength(int expectedLength) { checkArgument(expectedLength >= 0, "expectedLength(%s) must be >= 0", expectedLength); - check("length()").that(actual.length()).isEqualTo(expectedLength); + check("length()").that(checkNotNull(actual).length()).isEqualTo(expectedLength); } /** Fails if the string is not equal to the zero-length "empty string." */ @@ -75,7 +77,7 @@ public void isNotEmpty() { } /** Fails if the string does not contain the given sequence. */ - public void contains(CharSequence string) { + public void contains(@Nullable CharSequence string) { checkNotNull(string); if (actual == null) { failWithActual("expected a string that contains", string); @@ -85,7 +87,7 @@ public void contains(CharSequence string) { } /** Fails if the string contains the given sequence. */ - public void doesNotContain(CharSequence string) { + public void doesNotContain(@Nullable CharSequence string) { checkNotNull(string); if (actual == null) { failWithActual("expected a string that does not contain", string); @@ -95,7 +97,7 @@ public void doesNotContain(CharSequence string) { } /** Fails if the string does not start with the given string. */ - public void startsWith(String string) { + public void startsWith(@Nullable String string) { checkNotNull(string); if (actual == null) { failWithActual("expected a string that starts with", string); @@ -105,7 +107,7 @@ public void startsWith(String string) { } /** Fails if the string does not end with the given string. */ - public void endsWith(String string) { + public void endsWith(@Nullable String string) { checkNotNull(string); if (actual == null) { failWithActual("expected a string that ends with", string); @@ -115,7 +117,7 @@ public void endsWith(String string) { } /** Fails if the string does not match the given regex. */ - public void matches(String regex) { + public void matches(@Nullable String regex) { checkNotNull(regex); if (actual == null) { failWithActual("expected a string that matches", regex); @@ -133,7 +135,7 @@ public void matches(String regex) { /** Fails if the string does not match the given regex. */ @GwtIncompatible("java.util.regex.Pattern") - public void matches(Pattern regex) { + public void matches(@Nullable Pattern regex) { checkNotNull(regex); if (actual == null) { failWithActual("expected a string that matches", regex); @@ -152,7 +154,7 @@ public void matches(Pattern regex) { } /** Fails if the string matches the given regex. */ - public void doesNotMatch(String regex) { + public void doesNotMatch(@Nullable String regex) { checkNotNull(regex); if (actual == null) { failWithActual("expected a string that does not match", regex); @@ -163,7 +165,7 @@ public void doesNotMatch(String regex) { /** Fails if the string matches the given regex. */ @GwtIncompatible("java.util.regex.Pattern") - public void doesNotMatch(Pattern regex) { + public void doesNotMatch(@Nullable Pattern regex) { checkNotNull(regex); if (actual == null) { failWithActual("expected a string that does not match", regex); @@ -174,7 +176,7 @@ public void doesNotMatch(Pattern regex) { /** Fails if the string does not contain a match on the given regex. */ @GwtIncompatible("java.util.regex.Pattern") - public void containsMatch(Pattern regex) { + public void containsMatch(@Nullable Pattern regex) { checkNotNull(regex); if (actual == null) { failWithActual("expected a string that contains a match for", regex); @@ -184,7 +186,7 @@ public void containsMatch(Pattern regex) { } /** Fails if the string does not contain a match on the given regex. */ - public void containsMatch(String regex) { + public void containsMatch(@Nullable String regex) { checkNotNull(regex); if (actual == null) { failWithActual("expected a string that contains a match for", regex); @@ -195,7 +197,7 @@ public void containsMatch(String regex) { /** Fails if the string contains a match on the given regex. */ @GwtIncompatible("java.util.regex.Pattern") - public void doesNotContainMatch(Pattern regex) { + public void doesNotContainMatch(@Nullable Pattern regex) { checkNotNull(regex); if (actual == null) { failWithActual("expected a string that does not contain a match for", regex); @@ -211,7 +213,7 @@ public void doesNotContainMatch(Pattern regex) { } /** Fails if the string contains a match on the given regex. */ - public void doesNotContainMatch(String regex) { + public void doesNotContainMatch(@Nullable String regex) { checkNotNull(regex); if (actual == null) { failWithActual("expected a string that does not contain a match for", regex); @@ -246,7 +248,7 @@ private CaseInsensitiveStringComparison() {} * *

Example: "abc" is equal to "ABC", but not to "abcd". */ - public void isEqualTo(String expected) { + public void isEqualTo(@Nullable String expected) { if (actual == null) { if (expected != null) { failWithoutActual( @@ -268,7 +270,7 @@ public void isEqualTo(String expected) { * Fails if the subject is equal to the given string (while ignoring case). The meaning of * equality is the same as for the {@link #isEqualTo} method. */ - public void isNotEqualTo(String unexpected) { + public void isNotEqualTo(@Nullable String unexpected) { if (actual == null) { if (unexpected == null) { failWithoutActual( @@ -284,7 +286,7 @@ public void isNotEqualTo(String unexpected) { } /** Fails if the string does not contain the given sequence (while ignoring case). */ - public void contains(CharSequence expectedSequence) { + public void contains(@Nullable CharSequence expectedSequence) { checkNotNull(expectedSequence); String expected = expectedSequence.toString(); if (actual == null) { @@ -299,7 +301,7 @@ public void contains(CharSequence expectedSequence) { } /** Fails if the string contains the given sequence (while ignoring case). */ - public void doesNotContain(CharSequence expectedSequence) { + public void doesNotContain(@Nullable CharSequence expectedSequence) { checkNotNull(expectedSequence); String expected = expectedSequence.toString(); if (actual == null) { @@ -313,12 +315,13 @@ public void doesNotContain(CharSequence expectedSequence) { } } - private boolean containsIgnoreCase(String string) { + private boolean containsIgnoreCase(@Nullable String string) { + checkNotNull(string); if (string.isEmpty()) { // TODO(b/79459427): Fix for J2CL discrepancy when string is empty return true; } - String subject = actual; + String subject = checkNotNull(actual); for (int subjectOffset = 0; subjectOffset <= subject.length() - string.length(); subjectOffset++) { diff --git a/core/src/main/java/com/google/common/truth/Subject.java b/core/src/main/java/com/google/common/truth/Subject.java index c571eecac..d6a137827 100644 --- a/core/src/main/java/com/google/common/truth/Subject.java +++ b/core/src/main/java/com/google/common/truth/Subject.java @@ -20,6 +20,7 @@ import static com.google.common.base.CharMatcher.whitespace; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Strings.lenientFormat; import static com.google.common.truth.Fact.fact; import static com.google.common.truth.Fact.simpleFact; @@ -88,7 +89,7 @@ public class Subject { */ public interface Factory { /** Creates a new {@link Subject}. */ - SubjectT createSubject(FailureMetadata metadata, ActualT actual); + SubjectT createSubject(FailureMetadata metadata, @Nullable ActualT actual); } private static final FailureStrategy IGNORE_STRATEGY = @@ -97,8 +98,8 @@ public interface Factory { public void fail(AssertionError failure) {} }; - private final FailureMetadata metadata; - private final Object actual; + private final @Nullable FailureMetadata metadata; + private final @Nullable Object actual; private final @Nullable String typeDescriptionOverride; /** @@ -121,8 +122,10 @@ protected Subject(FailureMetadata metadata, @Nullable Object actual) { * obfuscated names. */ Subject( - FailureMetadata metadata, @Nullable Object actual, @Nullable String typeDescriptionOverride) { - this.metadata = metadata.updateForSubject(this); + @Nullable FailureMetadata metadata, + @Nullable Object actual, + @Nullable String typeDescriptionOverride) { + this.metadata = metadata == null ? null : metadata.updateForSubject(this); this.actual = actual; this.typeDescriptionOverride = typeDescriptionOverride; } @@ -353,13 +356,14 @@ private static boolean isInstanceOfType(Object instance, Class clazz) { } /** Fails unless the subject is equal to any element in the given iterable. */ - public void isIn(Iterable iterable) { + public void isIn(@Nullable Iterable iterable) { + checkNotNull(iterable); if (!contains(iterable, actual)) { failWithActual("expected any of", iterable); } } - private static boolean contains(Iterable haystack, Object needle) { + private static boolean contains(Iterable haystack, @Nullable Object needle) { if (isKotlinRange(haystack)) { return kotlinRangeContains(haystack, needle); } @@ -373,7 +377,8 @@ public void isAnyOf( } /** Fails if the subject is equal to any element in the given iterable. */ - public void isNotIn(Iterable iterable) { + public void isNotIn(@Nullable Iterable iterable) { + checkNotNull(iterable); if (Iterables.contains(iterable, actual)) { failWithActual("expected not to be any of", iterable); } @@ -386,7 +391,7 @@ public void isNoneOf( } /** Returns the actual value under test. */ - final Object actual() { + final @Nullable Object actual() { return actual; } @@ -449,10 +454,10 @@ private static Iterable stringableIterable(Object[] array) { return Iterables.transform(asList(array), STRINGIFY); } - private static final Function STRINGIFY = - new Function() { + private static final Function<@Nullable Object, @Nullable Object> STRINGIFY = + new Function<@Nullable Object, @Nullable Object>() { @Override - public Object apply(@Nullable Object input) { + public @Nullable Object apply(@Nullable Object input) { if (input != null && input.getClass().isArray()) { Iterable iterable; if (input.getClass() == boolean[].class) { @@ -514,7 +519,7 @@ static ComparisonResult differentNoDescription() { private final @Nullable ImmutableList facts; - private ComparisonResult(ImmutableList facts) { + private ComparisonResult(@Nullable ImmutableList facts) { this.facts = facts; } @@ -619,7 +624,7 @@ private static String arrayType(Object array) { } } - private static boolean gwtSafeObjectEquals(Object actual, Object expected) { + private static boolean gwtSafeObjectEquals(@Nullable Object actual, @Nullable Object expected) { if (actual instanceof Double && expected instanceof Double) { return Double.doubleToLongBits((Double) actual) == Double.doubleToLongBits((Double) expected); } else if (actual instanceof Float && expected instanceof Float) { @@ -655,7 +660,7 @@ private static List floatArrayAsString(float[] items) { */ @Deprecated final StandardSubjectBuilder check() { - return new StandardSubjectBuilder(metadata.updateForCheckCall()); + return new StandardSubjectBuilder(checkNotNull(metadata).updateForCheckCall()); } /** @@ -686,18 +691,19 @@ final StandardSubjectBuilder check() { * @param format a template with {@code %s} placeholders * @param args the arguments to be inserted into those placeholders */ - protected final StandardSubjectBuilder check(String format, Object... args) { + protected final StandardSubjectBuilder check(String format, @Nullable Object... args) { return doCheck(OldAndNewValuesAreSimilar.DIFFERENT, format, args); } // TODO(b/134064106): Figure out a public API for this. - final StandardSubjectBuilder checkNoNeedToDisplayBothValues(String format, Object... args) { + final StandardSubjectBuilder checkNoNeedToDisplayBothValues( + String format, @Nullable Object... args) { return doCheck(OldAndNewValuesAreSimilar.SIMILAR, format, args); } private StandardSubjectBuilder doCheck( - OldAndNewValuesAreSimilar valuesAreSimilar, String format, Object[] args) { + OldAndNewValuesAreSimilar valuesAreSimilar, String format, @Nullable Object[] args) { final LazyMessage message = new LazyMessage(format, args); Function descriptionUpdate = new Function() { @@ -707,7 +713,7 @@ public String apply(String input) { } }; return new StandardSubjectBuilder( - metadata.updateForCheckCall(valuesAreSimilar, descriptionUpdate)); + checkNotNull(metadata).updateForCheckCall(valuesAreSimilar, descriptionUpdate)); } /** @@ -804,7 +810,7 @@ final void fail(String verb, Object other) { * message as a migration aid, you can inline this method. */ @Deprecated - final void fail(String verb, Object... messageParts) { + final void fail(String verb, @Nullable Object... messageParts) { StringBuilder message = new StringBuilder("Not true that <"); message.append(actualCustomStringRepresentation()).append("> ").append(verb); for (Object part : messageParts) { @@ -828,12 +834,12 @@ enum EqualityCheck { * Special version of {@link #failEqualityCheck} for use from {@link IterableSubject}, documented * further there. */ - final void failEqualityCheckForEqualsWithoutDescription(Object expected) { + final void failEqualityCheckForEqualsWithoutDescription(@Nullable Object expected) { failEqualityCheck(EqualityCheck.EQUAL, expected, ComparisonResult.differentNoDescription()); } private void failEqualityCheck( - EqualityCheck equalityCheck, Object expected, ComparisonResult difference) { + EqualityCheck equalityCheck, @Nullable Object expected, ComparisonResult difference) { String actualString = actualCustomStringRepresentation(); String expectedString = formatActualOrExpected(expected); String actualClass = actual == null ? "(null reference)" : actual.getClass().getName(); @@ -881,7 +887,8 @@ private void failEqualityCheck( } } else { if (equalityCheck == EqualityCheck.EQUAL && actual != null && expected != null) { - metadata.failEqualityCheck(difference.factsOrEmpty(), expectedString, actualString); + checkNotNull(metadata) + .failEqualityCheck(difference.factsOrEmpty(), expectedString, actualString); } else { failEqualityCheckNoComparisonFailure( difference, @@ -895,7 +902,7 @@ private void failEqualityCheck( * Checks whether the actual and expected values are strings that match except for trailing * whitespace. If so, reports a failure and returns true. */ - private boolean tryFailForTrailingWhitespaceOnly(Object expected) { + private boolean tryFailForTrailingWhitespaceOnly(@Nullable Object expected) { if (!(actual instanceof String) || !(expected instanceof String)) { return false; } @@ -964,7 +971,7 @@ private static String escapeWhitespace(char c) { * Checks whether the actual and expected values are empty strings. If so, reports a failure and * returns true. */ - private boolean tryFailForEmptyString(Object expected) { + private boolean tryFailForEmptyString(@Nullable Object expected) { if (!(actual instanceof String) || !(expected instanceof String)) { return false; } @@ -1203,6 +1210,6 @@ private static boolean classMetadataUnsupported() { } private void doFail(ImmutableList facts) { - metadata.fail(facts); + checkNotNull(metadata).fail(facts); } } diff --git a/core/src/main/java/com/google/common/truth/SubjectUtils.java b/core/src/main/java/com/google/common/truth/SubjectUtils.java index bbeec5974..c9a280765 100644 --- a/core/src/main/java/com/google/common/truth/SubjectUtils.java +++ b/core/src/main/java/com/google/common/truth/SubjectUtils.java @@ -29,14 +29,15 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultiset; +import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multiset; -import com.google.common.collect.ListMultimap; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Utility methods used in {@code Subject} implementors. @@ -49,15 +50,15 @@ private SubjectUtils() {} static final String HUMAN_UNDERSTANDABLE_EMPTY_STRING = "\"\" (empty String)"; - static List accumulate(T first, T second, T... rest) { + static List accumulate(T first, T second, T @Nullable ... rest) { // rest should never be deliberately null, so assume that the caller passed null // in the third position but intended it to be the third element in the array of values. // Javac makes the opposite inference, so handle that here. - List items = new ArrayList(2 + ((rest == null) ? 1 : rest.length)); + List items = new ArrayList<>(2 + ((rest == null) ? 1 : rest.length)); items.add(first); items.add(second); if (rest == null) { - items.add(null); + items.add((T) null); } else { items.addAll(Arrays.asList(rest)); } @@ -89,7 +90,8 @@ static String entryString(Multiset.Entry entry) { return (count > 1) ? item + " [" + count + " copies]" : item; } - private static NonHashingMultiset countDuplicatesToMultiset(Iterable items) { + private static NonHashingMultiset countDuplicatesToMultiset( + Iterable items) { // We use avoid hashing in case the elements don't have a proper // .hashCode() method (e.g., MessageSet from old versions of protobuf). NonHashingMultiset multiset = new NonHashingMultiset<>(); @@ -138,7 +140,7 @@ static DuplicateGroupedAndTyped countDuplicatesAndMaybeAddTypeInfoReturnObject( } } - private static final class NonHashingMultiset { + private static final class NonHashingMultiset { // This ought to be static, but the generics are easier when I can refer to . private final Function>, Multiset.Entry> unwrapKey = new Function>, Multiset.Entry>() { @@ -261,13 +263,14 @@ static String iterableToStringWithTypeInfo(Iterable itemsIterable) { * *

Example: {@code retainMatchingToString([1L, 2L, 2L], [2, 3]) == [2L, 2L]} */ - static List retainMatchingToString(Iterable items, Iterable itemsToCheck) { - ListMultimap stringValueToItemsToCheck = ArrayListMultimap.create(); + static List<@Nullable Object> retainMatchingToString( + Iterable items, Iterable itemsToCheck) { + ListMultimap stringValueToItemsToCheck = ArrayListMultimap.create(); for (Object itemToCheck : itemsToCheck) { stringValueToItemsToCheck.put(String.valueOf(itemToCheck), itemToCheck); } - List result = Lists.newArrayList(); + List<@Nullable Object> result = Lists.newArrayList(); for (Object item : items) { for (Object itemToCheck : stringValueToItemsToCheck.get(String.valueOf(item))) { if (!Objects.equal(itemToCheck, item)) { @@ -292,7 +295,7 @@ static boolean hasMatchingToStringPair(Iterable items1, Iterable items2) { return !retainMatchingToString(items1, items2).isEmpty(); } - static String objectToTypeName(Object item) { + static String objectToTypeName(@Nullable Object item) { // TODO(cpovirk): Merge this with the code in Subject.failEqualityCheck(). if (item == null) { // The name "null type" comes from the interface javax.lang.model.type.NullType. @@ -342,7 +345,7 @@ private static List addTypeInfoToEveryItem(Iterable items) { return itemsWithTypeInfo; } - static Collection iterableToCollection(Iterable iterable) { + static Collection iterableToCollection(Iterable iterable) { if (iterable instanceof Collection) { // Should be safe to assume that any Iterable implementing Collection isn't a one-shot // iterable, right? I sure hope so. @@ -352,7 +355,7 @@ static Collection iterableToCollection(Iterable iterable) { } } - static List iterableToList(Iterable iterable) { + static List iterableToList(Iterable iterable) { if (iterable instanceof List) { return (List) iterable; } else { @@ -366,7 +369,7 @@ static List iterableToList(Iterable iterable) { * *

Returns the given iterable if it contains no empty strings. */ - static Iterable annotateEmptyStrings(Iterable items) { + static Iterable annotateEmptyStrings(Iterable items) { if (Iterables.contains(items, "")) { List annotatedItems = Lists.newArrayList(); for (T item : items) { diff --git a/core/src/main/java/com/google/common/truth/TableSubject.java b/core/src/main/java/com/google/common/truth/TableSubject.java index 87fcd69f0..ba991285f 100644 --- a/core/src/main/java/com/google/common/truth/TableSubject.java +++ b/core/src/main/java/com/google/common/truth/TableSubject.java @@ -30,7 +30,7 @@ * @author Kurt Alfred Kluever */ public final class TableSubject extends Subject { - private final Table actual; + private final @Nullable Table actual; TableSubject(FailureMetadata metadata, @Nullable Table table) { super(metadata, table); @@ -39,14 +39,14 @@ public final class TableSubject extends Subject { /** Fails if the table is not empty. */ public void isEmpty() { - if (!actual.isEmpty()) { + if (!checkNotNull(actual).isEmpty()) { failWithActual(simpleFact("expected to be empty")); } } /** Fails if the table is empty. */ public void isNotEmpty() { - if (actual.isEmpty()) { + if (checkNotNull(actual).isEmpty()) { failWithoutActual(simpleFact("expected not to be empty")); } } @@ -54,19 +54,19 @@ public void isNotEmpty() { /** Fails if the table does not have the given size. */ public final void hasSize(int expectedSize) { checkArgument(expectedSize >= 0, "expectedSize(%s) must be >= 0", expectedSize); - check("size()").that(actual.size()).isEqualTo(expectedSize); + check("size()").that(checkNotNull(actual).size()).isEqualTo(expectedSize); } /** Fails if the table does not contain a mapping for the given row key and column key. */ public void contains(@Nullable Object rowKey, @Nullable Object columnKey) { - if (!actual.contains(rowKey, columnKey)) { + if (!checkNotNull(actual).contains(rowKey, columnKey)) { fail("contains mapping for row/column", rowKey, columnKey); } } /** Fails if the table contains a mapping for the given row key and column key. */ public void doesNotContain(@Nullable Object rowKey, @Nullable Object columnKey) { - if (actual.contains(rowKey, columnKey)) { + if (checkNotNull(actual).contains(rowKey, columnKey)) { fail("does not contain mapping for row/column", rowKey, columnKey); } } @@ -74,39 +74,45 @@ public void doesNotContain(@Nullable Object rowKey, @Nullable Object columnKey) /** Fails if the table does not contain the given cell. */ public void containsCell( @Nullable Object rowKey, @Nullable Object colKey, @Nullable Object value) { - containsCell(Tables.immutableCell(rowKey, colKey, value)); + containsCell( + Tables.<@Nullable Object, @Nullable Object, @Nullable Object>immutableCell( + rowKey, colKey, value)); } /** Fails if the table does not contain the given cell. */ public void containsCell(Cell cell) { checkNotNull(cell); - checkNoNeedToDisplayBothValues("cellSet()").that(actual.cellSet()).contains(cell); + checkNoNeedToDisplayBothValues("cellSet()").that(checkNotNull(actual).cellSet()).contains(cell); } /** Fails if the table contains the given cell. */ public void doesNotContainCell( @Nullable Object rowKey, @Nullable Object colKey, @Nullable Object value) { - doesNotContainCell(Tables.immutableCell(rowKey, colKey, value)); + doesNotContainCell( + Tables.<@Nullable Object, @Nullable Object, @Nullable Object>immutableCell( + rowKey, colKey, value)); } /** Fails if the table contains the given cell. */ public void doesNotContainCell(Cell cell) { checkNotNull(cell); - checkNoNeedToDisplayBothValues("cellSet()").that(actual.cellSet()).doesNotContain(cell); + checkNoNeedToDisplayBothValues("cellSet()") + .that(checkNotNull(actual).cellSet()) + .doesNotContain(cell); } /** Fails if the table does not contain the given row key. */ public void containsRow(@Nullable Object rowKey) { - check("rowKeySet()").that(actual.rowKeySet()).contains(rowKey); + check("rowKeySet()").that(checkNotNull(actual).rowKeySet()).contains(rowKey); } /** Fails if the table does not contain the given column key. */ public void containsColumn(@Nullable Object columnKey) { - check("columnKeySet()").that(actual.columnKeySet()).contains(columnKey); + check("columnKeySet()").that(checkNotNull(actual).columnKeySet()).contains(columnKey); } /** Fails if the table does not contain the given value. */ public void containsValue(@Nullable Object value) { - check("values()").that(actual.values()).contains(value); + check("values()").that(checkNotNull(actual).values()).contains(value); } } diff --git a/core/src/main/java/com/google/common/truth/ThrowableSubject.java b/core/src/main/java/com/google/common/truth/ThrowableSubject.java index 8e0761da4..e6dd6a492 100644 --- a/core/src/main/java/com/google/common/truth/ThrowableSubject.java +++ b/core/src/main/java/com/google/common/truth/ThrowableSubject.java @@ -15,6 +15,8 @@ */ package com.google.common.truth; +import static com.google.common.base.Preconditions.checkNotNull; + import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -23,7 +25,7 @@ * @author Kurt Alfred Kluever */ public class ThrowableSubject extends Subject { - private final Throwable actual; + private final @Nullable Throwable actual; /** * Constructor for use by subclasses. If you want to create an instance of this class itself, call @@ -53,7 +55,7 @@ public final StringSubject hasMessageThat() { "(Note from Truth: When possible, instead of asserting on the full message, assert" + " about individual facts by using ExpectFailure.assertThat.)"); } - return check.that(actual.getMessage()); + return check.that(checkNotNull(actual).getMessage()); } /** diff --git a/core/src/main/java/com/google/common/truth/Truth.java b/core/src/main/java/com/google/common/truth/Truth.java index 57620501e..6628b08d7 100644 --- a/core/src/main/java/com/google/common/truth/Truth.java +++ b/core/src/main/java/com/google/common/truth/Truth.java @@ -24,6 +24,7 @@ import com.google.common.collect.Table; import java.math.BigDecimal; import java.util.Map; +import javax.annotation.CheckForNull; import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -101,7 +102,7 @@ public static StandardSubjectBuilder assert_() { * StandardSubjectBuilder#about about(...)}, as discussed in this FAQ entry. */ - public static StandardSubjectBuilder assertWithMessage(String messageToPrepend) { + public static StandardSubjectBuilder assertWithMessage(@Nullable String messageToPrepend) { return assert_().withMessage(messageToPrepend); } @@ -121,7 +122,7 @@ public static StandardSubjectBuilder assertWithMessage(String messageToPrepend) * @throws IllegalArgumentException if the number of placeholders in the format string does not * equal the number of given arguments */ - public static StandardSubjectBuilder assertWithMessage(String format, Object... args) { + public static StandardSubjectBuilder assertWithMessage(String format, @Nullable Object... args) { return assert_().withMessage(format, args); } @@ -197,35 +198,37 @@ public static ObjectArraySubject assertThat(@Nullable T @Nullable [] actu return assert_().that(actual); } - public static PrimitiveBooleanArraySubject assertThat(boolean @Nullable [] actual) { + // TODO(b/269115309): Switch back to @Nullable for Kotlin 1.8.20 + + public static PrimitiveBooleanArraySubject assertThat(@CheckForNull boolean[] actual) { return assert_().that(actual); } - public static PrimitiveShortArraySubject assertThat(short @Nullable [] actual) { + public static PrimitiveShortArraySubject assertThat(@CheckForNull short[] actual) { return assert_().that(actual); } - public static PrimitiveIntArraySubject assertThat(int @Nullable [] actual) { + public static PrimitiveIntArraySubject assertThat(@CheckForNull int[] actual) { return assert_().that(actual); } - public static PrimitiveLongArraySubject assertThat(long @Nullable [] actual) { + public static PrimitiveLongArraySubject assertThat(@CheckForNull long[] actual) { return assert_().that(actual); } - public static PrimitiveByteArraySubject assertThat(byte @Nullable [] actual) { + public static PrimitiveByteArraySubject assertThat(@CheckForNull byte[] actual) { return assert_().that(actual); } - public static PrimitiveCharArraySubject assertThat(char @Nullable [] actual) { + public static PrimitiveCharArraySubject assertThat(@CheckForNull char[] actual) { return assert_().that(actual); } - public static PrimitiveFloatArraySubject assertThat(float @Nullable [] actual) { + public static PrimitiveFloatArraySubject assertThat(@CheckForNull float[] actual) { return assert_().that(actual); } - public static PrimitiveDoubleArraySubject assertThat(double @Nullable [] actual) { + public static PrimitiveDoubleArraySubject assertThat(@CheckForNull double[] actual) { return assert_().that(actual); } @@ -301,13 +304,13 @@ static SimpleAssertionError createWithNoStack(String message) { @Override @SuppressWarnings("UnsynchronizedOverridesSynchronized") - public Throwable getCause() { + public @Nullable Throwable getCause() { return cause; } @Override public String toString() { - return getLocalizedMessage(); + return checkNotNull(getLocalizedMessage()); } } } diff --git a/core/src/main/java/com/google/common/truth/TruthFailureSubject.java b/core/src/main/java/com/google/common/truth/TruthFailureSubject.java index 80a0ead5b..eb9f93878 100644 --- a/core/src/main/java/com/google/common/truth/TruthFailureSubject.java +++ b/core/src/main/java/com/google/common/truth/TruthFailureSubject.java @@ -55,12 +55,13 @@ public static Factory truthFailures() { private static final Factory FACTORY = new Factory() { @Override - public TruthFailureSubject createSubject(FailureMetadata metadata, AssertionError actual) { + public TruthFailureSubject createSubject( + FailureMetadata metadata, @Nullable AssertionError actual) { return new TruthFailureSubject(metadata, actual, "failure"); } }; - private final AssertionError actual; + private final @Nullable AssertionError actual; TruthFailureSubject( FailureMetadata metadata, @Nullable AssertionError actual, @Nullable String typeDescription) { diff --git a/core/src/main/java/com/google/common/truth/TruthJUnit.java b/core/src/main/java/com/google/common/truth/TruthJUnit.java index 3c70d8871..f6db3a3d6 100644 --- a/core/src/main/java/com/google/common/truth/TruthJUnit.java +++ b/core/src/main/java/com/google/common/truth/TruthJUnit.java @@ -15,7 +15,9 @@ */ package com.google.common.truth; + import com.google.common.annotations.GwtIncompatible; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.internal.AssumptionViolatedException; /** @@ -66,7 +68,8 @@ public static final StandardSubjectBuilder assume() { // TODO(diamondm): remove this and use org.junit.AssumptionViolatedException once we're on v4.12 private static class ThrowableAssumptionViolatedException extends AssumptionViolatedException { - public ThrowableAssumptionViolatedException(String message, Throwable throwable) { + public ThrowableAssumptionViolatedException( + @Nullable String message, @Nullable Throwable throwable) { super(message); if (throwable != null) initCause(throwable); } diff --git a/core/src/main/java/com/google/common/truth/super/com/google/common/truth/Platform.java b/core/src/main/java/com/google/common/truth/super/com/google/common/truth/Platform.java index 615a26f11..8846fde0e 100644 --- a/core/src/main/java/com/google/common/truth/super/com/google/common/truth/Platform.java +++ b/core/src/main/java/com/google/common/truth/super/com/google/common/truth/Platform.java @@ -95,7 +95,7 @@ abstract static class PlatformComparisonFailure extends AssertionError { @Override public final String toString() { - return getLocalizedMessage(); + return "" + getLocalizedMessage(); } } @@ -150,9 +150,9 @@ private static native Object dump(Message msg) /*-{ }-*/; /** Turns a non-double, non-float object into a string. */ - static String stringValueOfNonFloatingPoint(Object o) { + static String stringValueOfNonFloatingPoint(@Nullable Object o) { // Check if we are in J2CL mode by probing a system property that only exists in GWT. - boolean inJ2clMode = System.getProperty("superdevmode", "doesntexist").equals("doesntexist"); + boolean inJ2clMode = "doesntexist".equals(System.getProperty("superdevmode", "doesntexist")); if (inJ2clMode && o instanceof Message) { Message msg = (Message) o; boolean dumpAvailable = @@ -265,7 +265,7 @@ static boolean isKotlinRange(Iterable iterable) { return false; } - static boolean kotlinRangeContains(Iterable haystack, Object needle) { + static boolean kotlinRangeContains(Iterable haystack, @Nullable Object needle) { throw new AssertionError(); // never called under GWT because isKotlinRange returns false } } diff --git a/extensions/java8/src/main/java/com/google/common/truth/OptionalSubject.java b/extensions/java8/src/main/java/com/google/common/truth/OptionalSubject.java index 1b9fb5c96..65ca4dae9 100644 --- a/extensions/java8/src/main/java/com/google/common/truth/OptionalSubject.java +++ b/extensions/java8/src/main/java/com/google/common/truth/OptionalSubject.java @@ -27,7 +27,7 @@ * @author Christian Gruber */ public final class OptionalSubject extends Subject { - private final Optional actual; + private final @Nullable Optional actual; OptionalSubject( FailureMetadata failureMetadata, @@ -68,7 +68,7 @@ public void isEmpty() { * assertThat(myOptional.get()).contains("foo"); * } */ - public void hasValue(Object expected) { + public void hasValue(@Nullable Object expected) { if (expected == null) { throw new NullPointerException("Optional cannot have a null value."); } diff --git a/extensions/re2j/src/main/java/com/google/common/truth/extensions/re2j/Re2jSubjects.java b/extensions/re2j/src/main/java/com/google/common/truth/extensions/re2j/Re2jSubjects.java index 67b1ed81c..1dc387f5d 100644 --- a/extensions/re2j/src/main/java/com/google/common/truth/extensions/re2j/Re2jSubjects.java +++ b/extensions/re2j/src/main/java/com/google/common/truth/extensions/re2j/Re2jSubjects.java @@ -15,10 +15,13 @@ */ package com.google.common.truth.extensions.re2j; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.annotations.GwtIncompatible; import com.google.common.truth.FailureMetadata; import com.google.common.truth.Subject; import com.google.re2j.Pattern; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Truth subjects for re2j regular expressions. @@ -51,26 +54,27 @@ public static final class Re2jStringSubject extends Subject { private static final Subject.Factory FACTORY = new Subject.Factory() { @Override - public Re2jStringSubject createSubject(FailureMetadata failureMetadata, String target) { + public Re2jStringSubject createSubject( + FailureMetadata failureMetadata, @Nullable String target) { return new Re2jStringSubject(failureMetadata, target); } }; - private final String actual; + private final @Nullable String actual; - private Re2jStringSubject(FailureMetadata failureMetadata, String subject) { + private Re2jStringSubject(FailureMetadata failureMetadata, @Nullable String subject) { super(failureMetadata, subject); this.actual = subject; } @Override protected String actualCustomStringRepresentation() { - return quote(actual); + return quote(checkNotNull(actual)); } /** Fails if the string does not match the given regex. */ public void matches(String regex) { - if (!Pattern.matches(regex, actual)) { + if (!Pattern.matches(regex, checkNotNull(actual))) { failWithActual("expected to match ", regex); } } @@ -78,14 +82,14 @@ public void matches(String regex) { /** Fails if the string does not match the given regex. */ @GwtIncompatible("com.google.re2j.Pattern") public void matches(Pattern regex) { - if (!regex.matcher(actual).matches()) { + if (!regex.matcher(checkNotNull(actual)).matches()) { failWithActual("expected to match ", regex); } } /** Fails if the string matches the given regex. */ public void doesNotMatch(String regex) { - if (Pattern.matches(regex, actual)) { + if (Pattern.matches(regex, checkNotNull(actual))) { failWithActual("expected to fail to match", regex); } } @@ -93,7 +97,7 @@ public void doesNotMatch(String regex) { /** Fails if the string matches the given regex. */ @GwtIncompatible("com.google.re2j.Pattern") public void doesNotMatch(Pattern regex) { - if (regex.matcher(actual).matches()) { + if (regex.matcher(checkNotNull(actual)).matches()) { failWithActual("expected to fail to match", regex); } } @@ -101,14 +105,14 @@ public void doesNotMatch(Pattern regex) { /** Fails if the string does not contain a match on the given regex. */ @GwtIncompatible("com.google.re2j.Pattern") public void containsMatch(Pattern pattern) { - if (!pattern.matcher(actual).find()) { + if (!pattern.matcher(checkNotNull(actual)).find()) { failWithActual("expected to contain a match for", pattern); } } /** Fails if the string does not contain a match on the given regex. */ public void containsMatch(String regex) { - if (!doContainsMatch(actual, regex)) { + if (!doContainsMatch(checkNotNull(actual), regex)) { failWithActual("expected to contain a match for", regex); } } @@ -116,14 +120,14 @@ public void containsMatch(String regex) { /** Fails if the string contains a match on the given regex. */ @GwtIncompatible("com.google.re2j.Pattern") public void doesNotContainMatch(Pattern pattern) { - if (pattern.matcher(actual).find()) { + if (pattern.matcher(checkNotNull(actual)).find()) { failWithActual("expected not to contain a match for", pattern); } } /** Fails if the string contains a match on the given regex. */ public void doesNotContainMatch(String regex) { - if (doContainsMatch(actual, regex)) { + if (doContainsMatch(checkNotNull(actual), regex)) { failWithActual("expected not to contain a match for", regex); } }