Skip to content

Commit

Permalink
Change most built-in Truth assertions to use the new (package-private…
Browse files Browse the repository at this point in the history
…) failure API and message format, and update depot tests to expect the new format.

The new message format replaces the existing "Not true that..." sentences with multi-line key-value pairs, which we internally call "facts," like:

expected: foo
but was : bar

Note that this reduces the amount of quoting (with "" and <>) that we need to do (though it does obscure changes in trailing whitespace, a problem we should address eventually).

It also causes us to throw ComparisonFailure in more cases and to use a different format (sometimes diff-style output) for those new ComparisonFailures.

(One note about a very specific change: This lets assertThat(optional).hasValue(value) use Truth's normal equality checking, which treats, e.g., 1 as equal to 1L and `new int[] {0}` as equal to `new int[] {0}`, even though Object.equals() return falses. I don't actually have a strong opinion on which method of equality checking is better overall (though it's certainly better for asserting that an OptionalDouble hasValue(NaN), if possibly inconvenient for asserting that it has value "zero or negative zero"), though. Rather, the purpose of this change is to provide consistent failure messages (including using ComparisonFailure).)

This commit exposes an API for testing fact-format messages but does not yet expose the API for producing them. (That API still needs further API Review.)

RELNOTES=Changed most of Truth's failure messages to a multi-line, key-value format. Provided an API for testing messages of this format. But did not yet expose the methods for custom subjects to generate messages in this format.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=191106221
  • Loading branch information
cpovirk authored and ronshapiro committed Apr 4, 2018
1 parent 08908c1 commit 4a51035
Show file tree
Hide file tree
Showing 66 changed files with 869 additions and 1,800 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.google.common.truth;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.truth.Fact.factWithoutValue;

import java.lang.reflect.Array;
import javax.annotation.Nullable;
Expand All @@ -34,14 +35,14 @@ abstract class AbstractArraySubject<S extends AbstractArraySubject<S, T>, T> ext
/** Fails if the array is not empty (i.e. {@code array.length != 0}). */
public final void isEmpty() {
if (length() > 0) {
fail("is empty");
fail(factWithoutValue("expected to be empty"));
}
}

/** Fails if the array is empty (i.e. {@code array.length == 0}). */
public final void isNotEmpty() {
if (length() == 0) {
fail("is not empty");
failWithoutActual(factWithoutValue("expected not to be empty"));
}
}

Expand All @@ -52,9 +53,7 @@ public final void isNotEmpty() {
*/
public final void hasLength(int length) {
checkArgument(length >= 0, "length (%s) must be >= 0");
if (length() != length) {
fail("has length", length);
}
check("length").that(length()).isEqualTo(length);
}

private int length() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Fact.factWithoutValue;

import com.google.common.collect.Maps;
import com.google.common.util.concurrent.AtomicLongMap;
Expand Down Expand Up @@ -57,32 +58,26 @@ public void isNotEqualTo(@Nullable Object other) {
/** Fails if the {@link AtomicLongMap} is not empty. */
public void isEmpty() {
if (!actual().isEmpty()) {
fail("is empty");
fail(factWithoutValue("expected to be empty"));
}
}

/** Fails if the {@link AtomicLongMap} is empty. */
public void isNotEmpty() {
if (actual().isEmpty()) {
fail("is not empty");
failWithoutActual(factWithoutValue("expected not to be empty"));
}
}

/** Fails if the {@link AtomicLongMap} does not have the given size. */
public void hasSize(int expectedSize) {
checkArgument(expectedSize >= 0, "expectedSize (%s) must be >= 0", expectedSize);
int actualSize = actual().size();
if (actualSize != expectedSize) {
failWithBadResults("has a size of", expectedSize, "is", actualSize);
}
check("size()").that(actual().size()).isEqualTo(expectedSize);
}

/** Fails if the {@link AtomicLongMap} does not have the given sum. */
public void hasSum(long expectedSum) {
long actualSum = actual().sum();
if (actualSum != expectedSum) {
failWithBadResults("has a sum of", expectedSum, "is", actualSum);
}
check("sum()").that(actual().sum()).isEqualTo(expectedSum);
}

/** Fails if the {@link AtomicLongMap} does not contain the given key. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package com.google.common.truth;

import static com.google.common.truth.Fact.fact;
import static com.google.common.truth.Fact.factWithoutValue;

import java.math.BigDecimal;
import javax.annotation.Nullable;

Expand Down Expand Up @@ -88,9 +91,8 @@ public void isEquivalentAccordingToCompareTo(BigDecimal expected) {

private void compareValues(BigDecimal expected) {
if (actual().compareTo(expected) != 0) {
failWithRawMessage(
"%s should have had the same value as <%s> (scale is ignored)",
actualAsString(), expected);
failWithoutActual(
fact("expected", expected), butWas(), factWithoutValue("(scale is ignored)"));
}
}
}
14 changes: 6 additions & 8 deletions core/src/main/java/com/google/common/truth/BooleanSubject.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package com.google.common.truth;

import static com.google.common.truth.Fact.factWithoutValue;

import javax.annotation.Nullable;

/**
Expand All @@ -30,22 +32,18 @@ public final class BooleanSubject extends Subject<BooleanSubject, Boolean> {
/** Fails if the subject is false or {@code null}. */
public void isTrue() {
if (actual() == null) {
failWithRawMessage("%s was expected to be true, but was null", booleanSubject());
isEqualTo(true); // fails
} else if (!actual()) {
failWithRawMessage("%s was expected to be true, but was false", booleanSubject());
failWithoutActual(factWithoutValue("expected to be true"));
}
}

/** Fails if the subject is true or {@code null}. */
public void isFalse() {
if (actual() == null) {
failWithRawMessage("%s was expected to be false, but was null", booleanSubject());
isEqualTo(false); // fails
} else if (actual()) {
failWithRawMessage("%s was expected to be false, but was true", booleanSubject());
failWithoutActual(factWithoutValue("expected to be false"));
}
}

private String booleanSubject() {
return internalCustomName() == null ? "The subject" : actualAsString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public final class ClassSubject extends Subject<ClassSubject, Class<?>> {
*/
public void isAssignableTo(Class<?> clazz) {
if (!clazz.isAssignableFrom(actual())) {
fail("is assignable to", clazz);
failWithFact("expected to be assignable to", clazz.getName());
}
}
}
20 changes: 9 additions & 11 deletions core/src/main/java/com/google/common/truth/ComparableSubject.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ protected ComparableSubject(FailureMetadata metadata, @Nullable T actual) {
/** Checks that the subject is in {@code range}. */
public final void isIn(Range<T> range) {
if (!range.contains(actual())) {
fail("is in", range);
failWithFact("expected to be in range", range);
}
}

/** Checks that the subject is <i>not</i> in {@code range}. */
public final void isNotIn(Range<T> range) {
if (range.contains(actual())) {
fail("is not in", range);
failWithFact("expected not to be in range", range);
}
}

Expand All @@ -54,11 +54,9 @@ public final void isNotIn(Range<T> range) {
* <p><b>Note:</b> Do not use this method for checking object equality. Instead, use {@link
* #isEqualTo(Object)}.
*/
public void isEquivalentAccordingToCompareTo(T other) {
if (actual().compareTo(other) != 0) {
failWithRawMessage(
"%s should have been equivalent to <%s> according to compareTo()",
actualAsString(), other);
public void isEquivalentAccordingToCompareTo(T expected) {
if (actual().compareTo(expected) != 0) {
failWithFact("expected value that sorts equal to", expected);
}
}

Expand All @@ -84,7 +82,7 @@ public void comparesEqualTo(T other) {
*/
public final void isGreaterThan(T other) {
if (actual().compareTo(other) <= 0) {
fail("is greater than", other);
failWithFact("expected to be greater than", other);
}
}

Expand All @@ -96,7 +94,7 @@ public final void isGreaterThan(T other) {
*/
public final void isLessThan(T other) {
if (actual().compareTo(other) >= 0) {
fail("is less than", other);
failWithFact("expected to be less than", other);
}
}

Expand All @@ -107,7 +105,7 @@ public final void isLessThan(T other) {
*/
public final void isAtMost(T other) {
if (actual().compareTo(other) > 0) {
fail("is at most", other);
failWithFact("expected to be at most", other);
}
}

Expand All @@ -118,7 +116,7 @@ public final void isAtMost(T other) {
*/
public final void isAtLeast(T other) {
if (actual().compareTo(other) < 0) {
fail("is at least", other);
failWithFact("expected to be at least", other);
}
}
}
38 changes: 23 additions & 15 deletions core/src/main/java/com/google/common/truth/DoubleSubject.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Fact.fact;
import static com.google.common.truth.Fact.factWithoutValue;
import static com.google.common.truth.MathUtil.equalWithinTolerance;
import static com.google.common.truth.MathUtil.notEqualWithinTolerance;
import static com.google.common.truth.Platform.doubleToString;
import static java.lang.Double.NaN;
import static java.lang.Double.doubleToLongBits;

import javax.annotation.Nullable;
Expand Down Expand Up @@ -107,9 +111,10 @@ public void of(double expected) {
checkTolerance(tolerance);

if (!equalWithinTolerance(actual, expected, tolerance)) {
failWithRawMessage(
"%s and <%s> should have been finite values within <%s> of each other",
actualAsString(), expected, tolerance);
failWithoutActual(
fact("expected", doubleToString(expected)),
butWas(),
fact("outside tolerance", doubleToString(tolerance)));
}
}
};
Expand Down Expand Up @@ -145,9 +150,10 @@ public void of(double expected) {
checkTolerance(tolerance);

if (!notEqualWithinTolerance(actual, expected, tolerance)) {
failWithRawMessage(
"%s and <%s> should have been finite values not within <%s> of each other",
actualAsString(), expected, tolerance);
failWithoutActual(
fact("expected not to be", doubleToString(expected)),
butWas(),
fact("within tolerance", doubleToString(tolerance)));
}
}
};
Expand Down Expand Up @@ -211,7 +217,7 @@ static void checkTolerance(double tolerance) {
/** Asserts that the subject is zero (i.e. it is either {@code 0.0} or {@code -0.0}). */
public final void isZero() {
if (actual() == null || actual().doubleValue() != 0.0) {
fail("is zero");
fail(factWithoutValue("expected zero"));
}
}

Expand All @@ -220,8 +226,10 @@ public final void isZero() {
* {@code -0.0} or {@code null}).
*/
public final void isNonZero() {
if (actual() == null || actual().doubleValue() == 0.0) {
fail("is non-zero");
if (actual() == null) {
fail(factWithoutValue("expected a double other than zero"));
} else if (actual().doubleValue() == 0.0) {
fail(factWithoutValue("expected not to be zero"));
}
}

Expand All @@ -237,9 +245,7 @@ public final void isNegativeInfinity() {

/** Asserts that the subject is {@link Double#NaN}. */
public final void isNaN() {
if (actual() == null || !actual().isNaN()) {
fail("is NaN");
}
isEqualTo(NaN);
}

/**
Expand All @@ -248,7 +254,7 @@ public final void isNaN() {
*/
public final void isFinite() {
if (actual() == null || actual().isNaN() || actual().isInfinite()) {
failWithRawMessage("%s should have been finite", actualAsString());
fail(factWithoutValue("expected to be finite"));
}
}

Expand All @@ -257,8 +263,10 @@ public final void isFinite() {
* {@link Double#POSITIVE_INFINITY} or {@link Double#NEGATIVE_INFINITY}).
*/
public final void isNotNaN() {
if (actual() == null || actual().isNaN()) {
failWithRawMessage("%s should not have been NaN", actualAsString());
if (actual() == null) {
fail(factWithoutValue("expected a double other than NaN"));
} else {
isNotEqualTo(NaN);
}
}
}
10 changes: 5 additions & 5 deletions core/src/main/java/com/google/common/truth/ExpectFailure.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@
import org.junit.runners.model.Statement;

/**
* A utility for testing that assertions against a custom {@link Subject} fail when they should.
* A utility for testing that assertions against a custom {@link Subject} fail when they should,
* plus a utility to assert about parts of the resulting failure messages.
*
* <p>Usage:
*
* <pre>{@code
* AssertionError failure =
* expectFailure(whenTesting -> whenTesting.that(cancelButton).isVisible());
* assertThat(failure).hasMessageThat().contains("visible");
* assertThat(failure).factKeys().containsExactly("expected to be visible");
*
* ...
*
Expand All @@ -53,7 +54,7 @@
* {@code ...
*
* expectFailure.whenTesting().about(uiElements()).that(cancelButton).isVisible();
* assertThat(expectFailure.getFailure()).hasMessageThat().contains("visible");
* assertThat(failure).factKeys().containsExactly("expected to be visible");
* }</pre>
*
* <p>{@code ExpectFailure} is similar to JUnit 5's <a
Expand Down Expand Up @@ -186,12 +187,11 @@ public void invokeAssertion(StandardSubjectBuilder whenTesting) {
});
}

// TODO(cpovirk): Expose this publicly once we finalize names.
/**
* Creates a subject for asserting about the given {@link AssertionError}, usually one produced by
* Truth.
*/
static TruthFailureSubject assertThat(AssertionError actual) {
public static TruthFailureSubject assertThat(AssertionError actual) {
return (TruthFailureSubject) assertAbout(truthFailures()).that(actual);
}

Expand Down
15 changes: 15 additions & 0 deletions core/src/main/java/com/google/common/truth/FailureMetadata.java
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,21 @@ FailureMetadata withMessage(String format, Object[] args) {
return derive(messages, steps);
}

void failEqualityCheck(
ImmutableList<Fact> headFacts,
ImmutableList<Fact> tailFacts,
String expected,
String actual) {
doFail(
ComparisonFailureWithFacts.create(
evaluateAll(messages),
concat(descriptionAsFacts(), headFacts),
concat(tailFacts, rootUnlessThrowableAsFacts()),
expected,
actual,
rootCause().orNull()));
}

void fail(ImmutableList<Fact> facts) {
doFail(
AssertionErrorWithFacts.create(
Expand Down
Loading

0 comments on commit 4a51035

Please sign in to comment.