• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2011 Google, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.common.truth;
17 
18 import static com.google.common.base.CaseFormat.LOWER_CAMEL;
19 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
20 import static com.google.common.base.CharMatcher.whitespace;
21 import static com.google.common.base.MoreObjects.firstNonNull;
22 import static com.google.common.base.Strings.lenientFormat;
23 import static com.google.common.truth.Fact.fact;
24 import static com.google.common.truth.Fact.simpleFact;
25 import static com.google.common.truth.Platform.doubleToString;
26 import static com.google.common.truth.Platform.floatToString;
27 import static com.google.common.truth.Subject.EqualityCheck.SAME_INSTANCE;
28 import static com.google.common.truth.SubjectUtils.accumulate;
29 import static com.google.common.truth.SubjectUtils.append;
30 import static com.google.common.truth.SubjectUtils.concat;
31 import static com.google.common.truth.SubjectUtils.sandwich;
32 import static java.util.Arrays.asList;
33 
34 import com.google.common.base.Function;
35 import com.google.common.base.Objects;
36 import com.google.common.collect.ImmutableList;
37 import com.google.common.collect.Iterables;
38 import com.google.common.collect.Lists;
39 import com.google.common.primitives.Booleans;
40 import com.google.common.primitives.Bytes;
41 import com.google.common.primitives.Chars;
42 import com.google.common.primitives.Ints;
43 import com.google.common.primitives.Longs;
44 import com.google.common.primitives.Shorts;
45 import com.google.common.truth.FailureMetadata.OldAndNewValuesAreSimilar;
46 import com.google.errorprone.annotations.DoNotCall;
47 import com.google.errorprone.annotations.ForOverride;
48 import java.lang.reflect.Array;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.List;
52 import org.checkerframework.checker.nullness.qual.Nullable;
53 
54 /**
55  * An object that lets you perform checks on the value under test. For example, {@code Subject}
56  * contains {@link #isEqualTo(Object)} and {@link #isInstanceOf(Class)}, and {@link StringSubject}
57  * contains {@link StringSubject#startsWith startsWith(String)}.
58  *
59  * <p>To create a {@code Subject} instance, most users will call an {@link Truth#assertThat
60  * assertThat} method. For information about other ways to create an instance, see <a
61  * href="https://truth.dev/faq#full-chain">this FAQ entry</a>.
62  *
63  * <h3>For people extending Truth</h3>
64  *
65  * <p>For information about writing a custom {@link Subject}, see <a
66  * href="https://truth.dev/extension">our doc on extensions</a>.
67  *
68  * @author David Saff
69  * @author Christian Gruber
70  */
71 public class Subject {
72   /**
73    * In a fluent assertion chain, the argument to the common overload of {@link
74    * StandardSubjectBuilder#about(Subject.Factory) about}, the method that specifies what kind of
75    * {@link Subject} to create.
76    *
77    * <p>For more information about the fluent chain, see <a
78    * href="https://truth.dev/faq#full-chain">this FAQ entry</a>.
79    *
80    * <h3>For people extending Truth</h3>
81    *
82    * <p>When you write a custom subject, see <a href="https://truth.dev/extension">our doc on
83    * extensions</a>. It explains where {@code Subject.Factory} fits into the process.
84    */
85   public interface Factory<SubjectT extends Subject, ActualT> {
86     /** Creates a new {@link Subject}. */
createSubject(FailureMetadata metadata, ActualT actual)87     SubjectT createSubject(FailureMetadata metadata, ActualT actual);
88   }
89 
90   private static final FailureStrategy IGNORE_STRATEGY =
91       new FailureStrategy() {
92         @Override
93         public void fail(AssertionError failure) {}
94       };
95 
96   private final FailureMetadata metadata;
97   private final Object actual;
98   private String customName = null;
99   private final @Nullable String typeDescriptionOverride;
100 
101   /**
102    * Constructor for use by subclasses. If you want to create an instance of this class itself, call
103    * {@link Subject#check(String, Object...) check(...)}{@code .that(actual)}.
104    */
Subject(FailureMetadata metadata, @Nullable Object actual)105   protected Subject(FailureMetadata metadata, @Nullable Object actual) {
106     this(metadata, actual, /*typeDescriptionOverride=*/ null);
107   }
108 
109   /**
110    * Special constructor that lets subclasses provide a description of the type they're testing. For
111    * example, {@link ThrowableSubject} passes the description "throwable." Normally, Truth is able
112    * to infer this name from the class name. However, if we lack runtime type information (notably,
113    * under j2cl with class metadata off), we might not have access to the original class name.
114    *
115    * <p>We don't expect to make this a public API: Class names are nearly always available. It's
116    * just that we want to be able to run Truth's own tests run with class metadata off, and it's
117    * easier to tweak the subjects to know their own names rather than generalize the tests to accept
118    * obfuscated names.
119    */
Subject( FailureMetadata metadata, @Nullable Object actual, @Nullable String typeDescriptionOverride)120   Subject(
121       FailureMetadata metadata, @Nullable Object actual, @Nullable String typeDescriptionOverride) {
122     this.metadata = metadata.updateForSubject(this);
123     this.actual = actual;
124     this.typeDescriptionOverride = typeDescriptionOverride;
125   }
126 
127   /** Fails if the subject is not null. */
isNull()128   public void isNull() {
129     standardIsEqualTo(null);
130   }
131 
132   /** Fails if the subject is null. */
isNotNull()133   public void isNotNull() {
134     standardIsNotEqualTo(null);
135   }
136 
137   /**
138    * Fails if the subject is not equal to the given object. For the purposes of this comparison, two
139    * objects are equal if any of the following is true:
140    *
141    * <ul>
142    *   <li>they are equal according to {@link Objects#equal}
143    *   <li>they are arrays and are considered equal by the appropriate {@link Arrays#equals}
144    *       overload
145    *   <li>they are boxed integer types ({@code Byte}, {@code Short}, {@code Character}, {@code
146    *       Integer}, or {@code Long}) and they are numerically equal when converted to {@code Long}.
147    *   <li>the actual value is a boxed floating-point type ({@code Double} or {@code Float}), the
148    *       expected value is an {@code Integer}, and the two are numerically equal when converted to
149    *       {@code Double}. (This allows {@code assertThat(someDouble).isEqualTo(0)} to pass.)
150    * </ul>
151    *
152    * <p><b>Note:</b> This method does not test the {@link Object#equals} implementation itself; it
153    * <i>assumes</i> that method is functioning correctly according to its contract. Testing an
154    * {@code equals} implementation requires a utility such as <a
155    * href="https://mvnrepository.com/artifact/com.google.guava/guava-testlib">guava-testlib</a>'s <a
156    * href="https://static.javadoc.io/com.google.guava/guava-testlib/23.0/com/google/common/testing/EqualsTester.html">EqualsTester</a>.
157    *
158    * <p>In some cases, this method might not even call {@code equals}. It may instead perform other
159    * tests that will return the same result as long as {@code equals} is implemented according to
160    * the contract for its type.
161    */
162   /*
163    * TODO(cpovirk): Possibly ban overriding isEqualTo+isNotEqualTo in favor of a
164    * compareForEquality(Object, Object) method. That way, people would need to override only one
165    * method, they would get a ComparisonFailure and other message niceties, and they'd have less to
166    * test.
167    */
isEqualTo(@ullable Object expected)168   public void isEqualTo(@Nullable Object expected) {
169     standardIsEqualTo(expected);
170   }
171 
standardIsEqualTo(@ullable Object expected)172   private void standardIsEqualTo(@Nullable Object expected) {
173     ComparisonResult difference = compareForEquality(expected);
174     if (!difference.valuesAreEqual()) {
175       failEqualityCheck(EqualityCheck.EQUAL, expected, difference);
176     }
177   }
178 
179   /**
180    * Fails if the subject is equal to the given object. The meaning of equality is the same as for
181    * the {@link #isEqualTo} method.
182    */
isNotEqualTo(@ullable Object unexpected)183   public void isNotEqualTo(@Nullable Object unexpected) {
184     standardIsNotEqualTo(unexpected);
185   }
186 
standardIsNotEqualTo(@ullable Object unexpected)187   private void standardIsNotEqualTo(@Nullable Object unexpected) {
188     ComparisonResult difference = compareForEquality(unexpected);
189     if (difference.valuesAreEqual()) {
190       String unexpectedAsString = formatActualOrExpected(unexpected);
191       if (actualCustomStringRepresentation().equals(unexpectedAsString)) {
192         failWithoutActual(fact("expected not to be", unexpectedAsString));
193       } else {
194         failWithoutActual(
195             fact("expected not to be", unexpectedAsString),
196             fact(
197                 "but was; string representation of actual value",
198                 actualCustomStringRepresentation()));
199       }
200     }
201   }
202 
203   /**
204    * Returns whether {@code actual} equals {@code expected} differ and, in some cases, a description
205    * of how they differ.
206    *
207    * <p>The equality check follows the rules described on {@link #isEqualTo}.
208    */
compareForEquality(@ullable Object expected)209   private ComparisonResult compareForEquality(@Nullable Object expected) {
210     if (actual == null && expected == null) {
211       return ComparisonResult.equal();
212     } else if (actual == null || expected == null) {
213       return ComparisonResult.differentNoDescription();
214     } else if (actual instanceof byte[] && expected instanceof byte[]) {
215       /*
216        * For a special error message and to use faster Arrays.equals to avoid at least one timeout.
217        *
218        * TODO(cpovirk): For performance, use Arrays.equals for other array types (here and/or in
219        * checkArrayEqualsRecursive)? Exception: double[] and float[], whose GWT implementations I
220        * think may have both false positives and false negatives (so we can't even use Arrays.equals
221        * as a fast path for them, nor deepEquals for an Object[] that might contain them). We would
222        * still fall back to the slower checkArrayEqualsRecursive to produce a nicer failure message
223        * -- but naturally only for tests that are about to fail, when performance matters less.
224        */
225       return checkByteArrayEquals((byte[]) expected, (byte[]) actual);
226     } else if (actual.getClass().isArray() && expected.getClass().isArray()) {
227       return checkArrayEqualsRecursive(expected, actual, "");
228     } else if (isIntegralBoxedPrimitive(actual) && isIntegralBoxedPrimitive(expected)) {
229       return ComparisonResult.fromEqualsResult(integralValue(actual) == integralValue(expected));
230     } else if (actual instanceof Double && expected instanceof Double) {
231       return ComparisonResult.fromEqualsResult(
232           Double.compare((Double) actual, (Double) expected) == 0);
233     } else if (actual instanceof Float && expected instanceof Float) {
234       return ComparisonResult.fromEqualsResult(
235           Float.compare((Float) actual, (Float) expected) == 0);
236     } else if (actual instanceof Double && expected instanceof Integer) {
237       return ComparisonResult.fromEqualsResult(
238           Double.compare((Double) actual, (Integer) expected) == 0);
239     } else if (actual instanceof Float && expected instanceof Integer) {
240       return ComparisonResult.fromEqualsResult(
241           Double.compare((Float) actual, (Integer) expected) == 0);
242     } else {
243       return ComparisonResult.fromEqualsResult(actual == expected || actual.equals(expected));
244     }
245   }
246 
isIntegralBoxedPrimitive(@ullable Object o)247   private static boolean isIntegralBoxedPrimitive(@Nullable Object o) {
248     return o instanceof Byte
249         || o instanceof Short
250         || o instanceof Character
251         || o instanceof Integer
252         || o instanceof Long;
253   }
254 
integralValue(Object o)255   private static long integralValue(Object o) {
256     if (o instanceof Character) {
257       return (long) ((Character) o).charValue();
258     } else if (o instanceof Number) {
259       return ((Number) o).longValue();
260     } else {
261       throw new AssertionError(o + " must be either a Character or a Number.");
262     }
263   }
264 
265   /** Fails if the subject is not the same instance as the given object. */
isSameInstanceAs(@ullable Object expected)266   public final void isSameInstanceAs(@Nullable Object expected) {
267     if (actual != expected) {
268       failEqualityCheck(
269           SAME_INSTANCE,
270           expected,
271           /*
272            * Pass through *whether* the values are equal so that failEqualityCheck() can print that
273            * information. But remove the description of the difference, which is always about
274            * content, since people calling isSameInstanceAs() are explicitly not interested in
275            * content, only object identity.
276            */
277           compareForEquality(expected).withoutDescription());
278     }
279   }
280 
281   /** Fails if the subject is the same instance as the given object. */
isNotSameInstanceAs(@ullable Object unexpected)282   public final void isNotSameInstanceAs(@Nullable Object unexpected) {
283     if (actual == unexpected) {
284       /*
285        * We use actualCustomStringRepresentation() because it might be overridden to be better than
286        * actual.toString()/unexpected.toString().
287        */
288       failWithoutActual(
289           fact("expected not to be specific instance", actualCustomStringRepresentation()));
290     }
291   }
292 
293   /** Fails if the subject is not an instance of the given class. */
isInstanceOf(Class<?> clazz)294   public void isInstanceOf(Class<?> clazz) {
295     if (clazz == null) {
296       throw new NullPointerException("clazz");
297     }
298     if (actual == null) {
299       failWithActual("expected instance of", clazz.getName());
300       return;
301     }
302     if (!Platform.isInstanceOfType(actual, clazz)) {
303       if (classMetadataUnsupported()) {
304         throw new UnsupportedOperationException(
305             actualCustomStringRepresentation()
306                 + ", an instance of "
307                 + actual.getClass().getName()
308                 + ", may or may not be an instance of "
309                 + clazz.getName()
310                 + ". Under -XdisableClassMetadata, we do not have enough information to tell.");
311       }
312       failWithoutActual(
313           fact("expected instance of", clazz.getName()),
314           fact("but was instance of", actual.getClass().getName()),
315           fact("with value", actualCustomStringRepresentation()));
316     }
317   }
318 
319   /** Fails if the subject is an instance of the given class. */
isNotInstanceOf(Class<?> clazz)320   public void isNotInstanceOf(Class<?> clazz) {
321     if (clazz == null) {
322       throw new NullPointerException("clazz");
323     }
324     if (classMetadataUnsupported()) {
325       throw new UnsupportedOperationException(
326           "isNotInstanceOf is not supported under -XdisableClassMetadata");
327     }
328     if (actual == null) {
329       return; // null is not an instance of clazz.
330     }
331     if (Platform.isInstanceOfType(actual, clazz)) {
332       failWithActual("expected not to be an instance of", clazz.getName());
333       /*
334        * TODO(cpovirk): Consider including actual.getClass() if it's not clazz itself but only a
335        * subtype.
336        */
337     }
338   }
339 
340   /** Fails unless the subject is equal to any element in the given iterable. */
isIn(Iterable<?> iterable)341   public void isIn(Iterable<?> iterable) {
342     if (!Iterables.contains(iterable, actual)) {
343       failWithActual("expected any of", iterable);
344     }
345   }
346 
347   /** Fails unless the subject is equal to any of the given elements. */
isAnyOf( @ullable Object first, @Nullable Object second, @Nullable Object ... rest)348   public void isAnyOf(
349       @Nullable Object first, @Nullable Object second, @Nullable Object /*@Nullable*/... rest) {
350     isIn(accumulate(first, second, rest));
351   }
352 
353   /** Fails if the subject is equal to any element in the given iterable. */
isNotIn(Iterable<?> iterable)354   public void isNotIn(Iterable<?> iterable) {
355     if (Iterables.contains(iterable, actual)) {
356       failWithActual("expected not to be any of", iterable);
357     }
358   }
359 
360   /** Fails if the subject is equal to any of the given elements. */
isNoneOf( @ullable Object first, @Nullable Object second, @Nullable Object ... rest)361   public void isNoneOf(
362       @Nullable Object first, @Nullable Object second, @Nullable Object /*@Nullable*/... rest) {
363     isNotIn(accumulate(first, second, rest));
364   }
365 
366   /** Returns the actual value under test. */
actual()367   final Object actual() {
368     return actual;
369   }
370 
371   /**
372    * Supplies the direct string representation of the actual value to other methods which may prefix
373    * or otherwise position it in an error message. This should only be overridden to provide an
374    * improved string representation of the value under test, as it would appear in any given error
375    * message, and should not be used for additional prefixing.
376    *
377    * <p>Subjects should override this with care.
378    *
379    * <p>By default, this returns {@code String.ValueOf(getActualValue())}.
380    */
381   /*
382    * TODO(cpovirk): Consider whether this API pulls its weight. If users want to format the actual
383    * value, maybe they should do so themselves? Of course, they won't have a chance to use a custom
384    * format for inherited implementations like isEqualTo(). But if they want to format the actual
385    * value specially, then it seems likely that they'll want to format the expected value specially,
386    * too. And that applies just as well to APIs like isIn(). Maybe we'll want an API that supports
387    * formatting those values, too (like formatActualOrExpected below)? See also the related
388    * b/70930431. But note that we are likely to use this from FailureMetadata, at least in the short
389    * term, for better or for worse.
390    */
391   @ForOverride
actualCustomStringRepresentation()392   protected String actualCustomStringRepresentation() {
393     return formatActualOrExpected(actual);
394   }
395 
actualCustomStringRepresentationForPackageMembersToCall()396   final String actualCustomStringRepresentationForPackageMembersToCall() {
397     return actualCustomStringRepresentation();
398   }
399 
formatActualOrExpected(@ullable Object o)400   private String formatActualOrExpected(@Nullable Object o) {
401     if (o instanceof byte[]) {
402       return base16((byte[]) o);
403     } else if (o != null && o.getClass().isArray()) {
404       String wrapped = Iterables.toString(stringableIterable(new Object[] {o}));
405       return wrapped.substring(1, wrapped.length() - 1);
406     } else if (o instanceof Double) {
407       return doubleToString((Double) o);
408     } else if (o instanceof Float) {
409       return floatToString((Float) o);
410     } else {
411       return String.valueOf(o);
412     }
413   }
414 
415   // We could add a dep on com.google.common.io, but that seems overkill for base16 encoding
base16(byte[] bytes)416   private static String base16(byte[] bytes) {
417     StringBuilder sb = new StringBuilder(2 * bytes.length);
418     for (byte b : bytes) {
419       sb.append(hexDigits[(b >> 4) & 0xf]).append(hexDigits[b & 0xf]);
420     }
421     return sb.toString();
422   }
423 
424   private static final char[] hexDigits = "0123456789ABCDEF".toCharArray();
425 
stringableIterable(Object[] array)426   private static Iterable<?> stringableIterable(Object[] array) {
427     return Iterables.transform(asList(array), STRINGIFY);
428   }
429 
430   private static final Function<Object, Object> STRINGIFY =
431       new Function<Object, Object>() {
432         @Override
433         public Object apply(@Nullable Object input) {
434           if (input != null && input.getClass().isArray()) {
435             Iterable<?> iterable;
436             if (input.getClass() == boolean[].class) {
437               iterable = Booleans.asList((boolean[]) input);
438             } else if (input.getClass() == int[].class) {
439               iterable = Ints.asList((int[]) input);
440             } else if (input.getClass() == long[].class) {
441               iterable = Longs.asList((long[]) input);
442             } else if (input.getClass() == short[].class) {
443               iterable = Shorts.asList((short[]) input);
444             } else if (input.getClass() == byte[].class) {
445               iterable = Bytes.asList((byte[]) input);
446             } else if (input.getClass() == double[].class) {
447               iterable = doubleArrayAsString((double[]) input);
448             } else if (input.getClass() == float[].class) {
449               iterable = floatArrayAsString((float[]) input);
450             } else if (input.getClass() == char[].class) {
451               iterable = Chars.asList((char[]) input);
452             } else {
453               iterable = Arrays.asList((Object[]) input);
454             }
455             return Iterables.transform(iterable, STRINGIFY);
456           }
457           return input;
458         }
459       };
460 
461   /**
462    * The result of comparing two objects for equality. This includes both the "equal"/"not-equal"
463    * bit and, in the case of "not equal," optional facts describing the difference.
464    */
465   private static final class ComparisonResult {
466     /**
467      * If {@code equal} is true, returns an equal result; if false, a non-equal result with no
468      * description.
469      */
fromEqualsResult(boolean equal)470     static ComparisonResult fromEqualsResult(boolean equal) {
471       return equal ? EQUAL : DIFFERENT_NO_DESCRIPTION;
472     }
473 
474     /** Returns a non-equal result with the given description. */
differentWithDescription(Fact... facts)475     static ComparisonResult differentWithDescription(Fact... facts) {
476       return new ComparisonResult(ImmutableList.copyOf(facts));
477     }
478 
479     /** Returns an equal result. */
equal()480     static ComparisonResult equal() {
481       return EQUAL;
482     }
483 
484     /** Returns a non-equal result with no description. */
differentNoDescription()485     static ComparisonResult differentNoDescription() {
486       return DIFFERENT_NO_DESCRIPTION;
487     }
488 
489     private static final ComparisonResult EQUAL = new ComparisonResult(null);
490     private static final ComparisonResult DIFFERENT_NO_DESCRIPTION =
491         new ComparisonResult(ImmutableList.<Fact>of());
492 
493     private final @Nullable ImmutableList<Fact> facts;
494 
ComparisonResult(ImmutableList<Fact> facts)495     private ComparisonResult(ImmutableList<Fact> facts) {
496       this.facts = facts;
497     }
498 
valuesAreEqual()499     boolean valuesAreEqual() {
500       return facts == null;
501     }
502 
factsOrEmpty()503     ImmutableList<Fact> factsOrEmpty() {
504       return firstNonNull(facts, ImmutableList.<Fact>of());
505     }
506 
507     /** Returns an instance with the same "equal"/"not-equal" bit but with no description. */
withoutDescription()508     ComparisonResult withoutDescription() {
509       return fromEqualsResult(valuesAreEqual());
510     }
511   }
512 
513   /**
514    * Returns null if the arrays are equal. If not equal, returns a string comparing the two arrays,
515    * displaying them in the style "[1, 2, 3]" to supplement the main failure message, which uses the
516    * style "010203."
517    */
checkByteArrayEquals(byte[] expected, byte[] actual)518   private static ComparisonResult checkByteArrayEquals(byte[] expected, byte[] actual) {
519     if (Arrays.equals(expected, actual)) {
520       return ComparisonResult.equal();
521     }
522     return ComparisonResult.differentWithDescription(
523         fact("expected", Arrays.toString(expected)), fact("but was", Arrays.toString(actual)));
524   }
525 
526   /**
527    * Returns null if the arrays are equal, recursively. If not equal, returns the string of the
528    * index at which they're different.
529    */
530   /*
531    * TODO(cpovirk): Decide whether it's worthwhile to go to this trouble to display the index at
532    * which the arrays differ. If we were to stop doing that, we could mostly delegate to
533    * Arrays.equals() and our float/double arrayEquals methods. (We'd use deepEquals, but it doesn't
534    * have our special double/float handling for GWT.)
535    */
checkArrayEqualsRecursive( Object expectedArray, Object actualArray, String lastIndex)536   private static ComparisonResult checkArrayEqualsRecursive(
537       Object expectedArray, Object actualArray, String lastIndex) {
538     if (expectedArray == actualArray) {
539       return ComparisonResult.equal();
540     }
541     String expectedType = arrayType(expectedArray);
542     String actualType = arrayType(actualArray);
543     if (!expectedType.equals(actualType)) {
544       Fact indexFact =
545           lastIndex.isEmpty() ? simpleFact("wrong type") : fact("wrong type for index", lastIndex);
546       return ComparisonResult.differentWithDescription(
547           indexFact, fact("expected", expectedType), fact("but was", actualType));
548     }
549     int actualLength = Array.getLength(actualArray);
550     int expectedLength = Array.getLength(expectedArray);
551     if (expectedLength != actualLength) {
552       Fact indexFact =
553           lastIndex.isEmpty()
554               ? simpleFact("wrong length")
555               : fact("wrong length for index", lastIndex);
556       return ComparisonResult.differentWithDescription(
557           indexFact, fact("expected", expectedLength), fact("but was", actualLength));
558     }
559     for (int i = 0; i < actualLength; i++) {
560       String index = lastIndex + "[" + i + "]";
561       Object expected = Array.get(expectedArray, i);
562       Object actual = Array.get(actualArray, i);
563       if (actual != null
564           && actual.getClass().isArray()
565           && expected != null
566           && expected.getClass().isArray()) {
567         ComparisonResult result = checkArrayEqualsRecursive(expected, actual, index);
568         if (!result.valuesAreEqual()) {
569           return result;
570         }
571       } else if (!gwtSafeObjectEquals(actual, expected)) {
572         return ComparisonResult.differentWithDescription(fact("differs at index", index));
573       }
574     }
575     return ComparisonResult.equal();
576   }
577 
arrayType(Object array)578   private static String arrayType(Object array) {
579     if (array.getClass() == boolean[].class) {
580       return "boolean[]";
581     } else if (array.getClass() == int[].class) {
582       return "int[]";
583     } else if (array.getClass() == long[].class) {
584       return "long[]";
585     } else if (array.getClass() == short[].class) {
586       return "short[]";
587     } else if (array.getClass() == byte[].class) {
588       return "byte[]";
589     } else if (array.getClass() == double[].class) {
590       return "double[]";
591     } else if (array.getClass() == float[].class) {
592       return "float[]";
593     } else if (array.getClass() == char[].class) {
594       return "char[]";
595     } else {
596       return "Object[]";
597     }
598   }
599 
gwtSafeObjectEquals(Object actual, Object expected)600   private static boolean gwtSafeObjectEquals(Object actual, Object expected) {
601     if (actual instanceof Double && expected instanceof Double) {
602       return Double.doubleToLongBits((Double) actual) == Double.doubleToLongBits((Double) expected);
603     } else if (actual instanceof Float && expected instanceof Float) {
604       return Float.floatToIntBits((Float) actual) == Float.floatToIntBits((Float) expected);
605     } else {
606       return Objects.equal(actual, expected);
607     }
608   }
609 
doubleArrayAsString(double[] items)610   private static List<String> doubleArrayAsString(double[] items) {
611     List<String> itemAsStrings = new ArrayList<>(items.length);
612     for (double item : items) {
613       itemAsStrings.add(doubleToString(item));
614     }
615     return itemAsStrings;
616   }
617 
floatArrayAsString(float[] items)618   private static List<String> floatArrayAsString(float[] items) {
619     List<String> itemAsStrings = new ArrayList<>(items.length);
620     for (float item : items) {
621       itemAsStrings.add(floatToString(item));
622     }
623     return itemAsStrings;
624   }
625 
626   /**
627    * Returns a builder for creating a derived subject but without providing information about how
628    * the derived subject will relate to the current subject. In most cases, you should provide such
629    * information by using {@linkplain #check(String, Object...) the other overload}.
630    *
631    * @deprecated Use {@linkplain #check(String, Object...) the other overload}, which requires you
632    *     to supply more information to include in any failure messages.
633    */
634   @Deprecated
check()635   final StandardSubjectBuilder check() {
636     return new StandardSubjectBuilder(metadata.updateForCheckCall());
637   }
638 
639   /**
640    * Returns a builder for creating a derived subject.
641    *
642    * <p>Derived subjects retain the {@link FailureStrategy} and {@linkplain
643    * StandardSubjectBuilder#withMessage messages} of the current subject, and in some cases, they
644    * automatically supplement their failure message with information about the original subject.
645    *
646    * <p>For example, {@link ThrowableSubject#hasMessageThat}, which returns a {@link StringSubject},
647    * is implemented with {@code check("getMessage()").that(actual.getMessage())}.
648    *
649    * <p>The arguments to {@code check} describe how the new subject was derived from the old,
650    * formatted like a chained method call. This allows Truth to include that information in its
651    * failure messages. For example, {@code assertThat(caught).hasCauseThat().hasMessageThat()} will
652    * produce a failure message that includes the string "throwable.getCause().getMessage()," thanks
653    * to internal {@code check} calls that supplied "getCause()" and "getMessage()" as arguments.
654    *
655    * <p>If the method you're delegating to accepts parameters, you can pass {@code check} a format
656    * string. For example, {@link MultimapSubject#valuesForKey} calls {@code
657    * check("valuesForKey(%s)", key)}.
658    *
659    * <p>If you aren't really delegating to an instance method on the actual value -- maybe you're
660    * calling a static method, or you're calling a chain of several methods -- you can supply
661    * whatever string will be most useful to users. For example, if you're delegating to {@code
662    * getOnlyElement(actual.colors())}, you might call {@code check("onlyColor()")}.
663    *
664    * @param format a template with {@code %s} placeholders
665    * @param args the arguments to be inserted into those placeholders
666    */
check(String format, Object... args)667   protected final StandardSubjectBuilder check(String format, Object... args) {
668     return doCheck(OldAndNewValuesAreSimilar.DIFFERENT, format, args);
669   }
670 
671   // TODO(b/134064106): Figure out a public API for this.
672 
checkNoNeedToDisplayBothValues(String format, Object... args)673   final StandardSubjectBuilder checkNoNeedToDisplayBothValues(String format, Object... args) {
674     return doCheck(OldAndNewValuesAreSimilar.SIMILAR, format, args);
675   }
676 
doCheck( OldAndNewValuesAreSimilar valuesAreSimilar, String format, Object[] args)677   private StandardSubjectBuilder doCheck(
678       OldAndNewValuesAreSimilar valuesAreSimilar, String format, Object[] args) {
679     final LazyMessage message = new LazyMessage(format, args);
680     Function<String, String> descriptionUpdate =
681         new Function<String, String>() {
682           @Override
683           public String apply(String input) {
684             return input + "." + message;
685           }
686         };
687     return new StandardSubjectBuilder(
688         metadata.updateForCheckCall(valuesAreSimilar, descriptionUpdate));
689   }
690 
691   /**
692    * Begins a new call chain that ignores any failures. This is useful for subjects that normally
693    * delegate with to other subjects by using {@link #check} but have already reported a failure. In
694    * such cases it may still be necessary to return a {@code Subject} instance even though any
695    * subsequent assertions are meaningless. For example, if a user chains together more {@link
696    * ThrowableSubject#hasCauseThat} calls than the actual exception has causes, {@code hasCauseThat}
697    * returns {@code ignoreCheck().that(... a dummy exception ...)}.
698    */
ignoreCheck()699   protected final StandardSubjectBuilder ignoreCheck() {
700     return StandardSubjectBuilder.forCustomFailureStrategy(IGNORE_STRATEGY);
701   }
702 
703   /**
704    * Fails, reporting a message with two "{@linkplain Fact facts}":
705    *
706    * <ul>
707    *   <li><i>key</i>: <i>value</i>
708    *   <li>but was: <i>actual value</i>.
709    * </ul>
710    *
711    * <p>This is the simplest failure API. For more advanced needs, see {@linkplain
712    * #failWithActual(Fact, Fact...) the other overload} and {@link #failWithoutActual(Fact, Fact...)
713    * failWithoutActual}.
714    *
715    * <p>Example usage: The check {@code contains(String)} calls {@code failWithActual("expected to
716    * contain", string)}.
717    */
failWithActual(String key, @Nullable Object value)718   protected final void failWithActual(String key, @Nullable Object value) {
719     failWithActual(fact(key, value));
720   }
721 
722   /**
723    * Fails, reporting a message with the given facts, followed by an automatically added fact of the
724    * form:
725    *
726    * <ul>
727    *   <li>but was: <i>actual value</i>.
728    * </ul>
729    *
730    * <p>If you have only one fact to report (and it's a {@linkplain Fact#fact key-value fact}),
731    * prefer {@linkplain #failWithActual(String, Object) the simpler overload}.
732    *
733    * <p>Example usage: The check {@code isEmpty()} calls {@code failWithActual(simpleFact("expected
734    * to be empty"))}.
735    */
failWithActual(Fact first, Fact... rest)736   protected final void failWithActual(Fact first, Fact... rest) {
737     doFail(sandwich(first, rest, butWas()));
738   }
739 
740   // TODO(cpovirk): Consider making this protected if there's a need for it.
failWithActual(Iterable<Fact> facts)741   final void failWithActual(Iterable<Fact> facts) {
742     doFail(append(ImmutableList.copyOf(facts), butWas()));
743   }
744 
745   /**
746    * Reports a failure constructing a message from a simple verb.
747    *
748    * @param check the check being asserted
749    * @deprecated Prefer to construct {@link Fact}-style methods, typically by using {@link
750    *     #failWithActual(Fact, Fact...) failWithActual}{@code (}{@link Fact#simpleFact
751    *     simpleFact(...)}{@code )}. However, if you want to preserve your exact failure message as a
752    *     migration aid, you can inline this method (and then inline the resulting method call, as
753    *     well).
754    */
755   @Deprecated
fail(String check)756   final void fail(String check) {
757     fail(check, new Object[0]);
758   }
759 
760   /**
761    * Assembles a failure message and passes such to the FailureStrategy
762    *
763    * @param verb the check being asserted
764    * @param other the value against which the subject is compared
765    * @deprecated Prefer to construct {@link Fact}-style methods, typically by using {@link
766    *     #failWithActual(String, Object)}. However, if you want to preserve your exact failure
767    *     message as a migration aid, you can inline this method (and then inline the resulting
768    *     method call, as well).
769    */
770   @Deprecated
fail(String verb, Object other)771   final void fail(String verb, Object other) {
772     fail(verb, new Object[] {other});
773   }
774 
775   /**
776    * Assembles a failure message and passes such to the FailureStrategy
777    *
778    * @param verb the check being asserted
779    * @param messageParts the expectations against which the subject is compared
780    * @deprecated Prefer to construct {@link Fact}-style methods, typically by using {@link
781    *     #failWithActual(Fact, Fact...)}. However, if you want to preserve your exact failure
782    *     message as a migration aid, you can inline this method.
783    */
784   @Deprecated
fail(String verb, Object... messageParts)785   final void fail(String verb, Object... messageParts) {
786     StringBuilder message = new StringBuilder("Not true that <");
787     message.append(actualCustomStringRepresentation()).append("> ").append(verb);
788     for (Object part : messageParts) {
789       message.append(" <").append(part).append(">");
790     }
791     failWithoutActual(simpleFact(message.toString()));
792   }
793 
794   enum EqualityCheck {
795     EQUAL("expected"),
796     SAME_INSTANCE("expected specific instance");
797 
798     final String keyForExpected;
799 
EqualityCheck(String keyForExpected)800     EqualityCheck(String keyForExpected) {
801       this.keyForExpected = keyForExpected;
802     }
803   }
804 
805   /**
806    * Special version of {@link #failEqualityCheck} for use from {@link IterableSubject}, documented
807    * further there.
808    */
failEqualityCheckForEqualsWithoutDescription(Object expected)809   final void failEqualityCheckForEqualsWithoutDescription(Object expected) {
810     failEqualityCheck(EqualityCheck.EQUAL, expected, ComparisonResult.differentNoDescription());
811   }
812 
failEqualityCheck( EqualityCheck equalityCheck, Object expected, ComparisonResult difference)813   private void failEqualityCheck(
814       EqualityCheck equalityCheck, Object expected, ComparisonResult difference) {
815     String actualString = actualCustomStringRepresentation();
816     String expectedString = formatActualOrExpected(expected);
817     String actualClass = actual == null ? "(null reference)" : actual.getClass().getName();
818     String expectedClass = expected == null ? "(null reference)" : expected.getClass().getName();
819 
820     /*
821      * It's a little odd for expectedString to be formatActualOrExpected(expected) but actualString
822      * *not* to be formatActualOrExpected(actual), since we're going to compare the two. Instead,
823      * actualString is actualCustomStringRepresentation() -- as it is for other assertions, since
824      * users may have overridden that method. While actualCustomStringRepresentation() defaults to
825      * formatActualOrExpected(actual), it's only a default.
826      *
827      * What we really want here is probably to delete actualCustomStringRepresentation() and migrate
828      * users to formatActualOrExpected(actual).
829      */
830     boolean sameToStrings = actualString.equals(expectedString);
831     boolean sameClassNames = actualClass.equals(expectedClass);
832     // TODO(cpovirk): Handle "same class name, different class loader."
833     // `equal` is always false for isEqualTo, but it varies for isSameInstanceAs:
834     boolean equal = difference.valuesAreEqual();
835 
836     if (equalityCheck == EqualityCheck.EQUAL
837         && (tryFailForTrailingWhitespaceOnly(expected) || tryFailForEmptyString(expected))) {
838       // tryFailForTrailingWhitespaceOnly or tryFailForEmptyString reported a failure, so we're done
839       return;
840     }
841 
842     if (sameToStrings) {
843       if (sameClassNames) {
844         String doppelgangerDescription =
845             equal
846                 ? "(different but equal instance of same class with same string representation)"
847                 : "(non-equal instance of same class with same string representation)";
848         failEqualityCheckNoComparisonFailure(
849             difference,
850             fact(equalityCheck.keyForExpected, expectedString),
851             fact("but was", doppelgangerDescription));
852       } else {
853         failEqualityCheckNoComparisonFailure(
854             difference,
855             fact(equalityCheck.keyForExpected, expectedString),
856             fact("an instance of", expectedClass),
857             fact("but was", "(non-equal value with same string representation)"),
858             fact("an instance of", actualClass));
859       }
860     } else {
861       if (equalityCheck == EqualityCheck.EQUAL && actual != null && expected != null) {
862         metadata.failEqualityCheck(
863             nameAsFacts(), difference.factsOrEmpty(), expectedString, actualString);
864       } else {
865         failEqualityCheckNoComparisonFailure(
866             difference,
867             fact(equalityCheck.keyForExpected, expectedString),
868             fact("but was", actualString));
869       }
870     }
871   }
872 
873   /**
874    * Checks whether the actual and expected values are strings that match except for trailing
875    * whitespace. If so, reports a failure and returns true.
876    */
tryFailForTrailingWhitespaceOnly(Object expected)877   private boolean tryFailForTrailingWhitespaceOnly(Object expected) {
878     if (!(actual instanceof String) || !(expected instanceof String)) {
879       return false;
880     }
881 
882     /*
883      * TODO(cpovirk): Consider applying this for non-String types. The danger there is that we don't
884      * know whether toString() (or actualCustomStringRepresentation/formatActualOrExpected) and
885      * equals() are consistent for those types.
886      */
887     String actualString = (String) actual;
888     String expectedString = (String) expected;
889     String actualNoTrailing = whitespace().trimTrailingFrom(actualString);
890     String expectedNoTrailing = whitespace().trimTrailingFrom(expectedString);
891     String expectedTrailing =
892         escapeWhitespace(expectedString.substring(expectedNoTrailing.length()));
893     String actualTrailing = escapeWhitespace(actualString.substring(actualNoTrailing.length()));
894 
895     if (!actualNoTrailing.equals(expectedNoTrailing)) {
896       return false;
897     }
898 
899     if (actualString.startsWith(expectedString)) {
900       failWithoutActual(
901           fact("expected", expectedString),
902           fact("but contained extra trailing whitespace", actualTrailing));
903     } else if (expectedString.startsWith(actualString)) {
904       failWithoutActual(
905           fact("expected", expectedString),
906           fact("but was missing trailing whitespace", expectedTrailing));
907     } else {
908       failWithoutActual(
909           fact("expected", expectedString),
910           fact("with trailing whitespace", expectedTrailing),
911           fact("but trailing whitespace was", actualTrailing));
912     }
913 
914     return true;
915   }
916 
escapeWhitespace(String in)917   private static String escapeWhitespace(String in) {
918     StringBuilder out = new StringBuilder();
919     for (char c : in.toCharArray()) {
920       out.append(escapeWhitespace(c));
921     }
922     return out.toString();
923   }
924 
escapeWhitespace(char c)925   private static String escapeWhitespace(char c) {
926     switch (c) {
927       case '\t':
928         return "\\t";
929       case '\n':
930         return "\\n";
931       case '\f':
932         return "\\f";
933       case '\r':
934         return "\\r";
935       case ' ':
936         return "␣";
937       default:
938         return new String(asUnicodeHexEscape(c));
939     }
940   }
941 
942   /**
943    * Checks whether the actual and expected values are empty strings. If so, reports a failure and
944    * returns true.
945    */
tryFailForEmptyString(Object expected)946   private boolean tryFailForEmptyString(Object expected) {
947     if (!(actual instanceof String) || !(expected instanceof String)) {
948       return false;
949     }
950 
951     String actualString = (String) actual;
952     String expectedString = (String) expected;
953     if (actualString.isEmpty()) {
954       failWithoutActual(fact("expected", expectedString), simpleFact("but was an empty string"));
955       return true;
956     } else if (expectedString.isEmpty()) {
957       failWithoutActual(simpleFact("expected an empty string"), fact("but was", actualString));
958       return true;
959     }
960 
961     // Neither string was empty
962     return false;
963   }
964 
965   // From SourceCodeEscapers:
966 
967   private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
968 
asUnicodeHexEscape(char c)969   private static char[] asUnicodeHexEscape(char c) {
970     // Equivalent to String.format("\\u%04x", (int) c);
971     char[] r = new char[6];
972     r[0] = '\\';
973     r[1] = 'u';
974     r[5] = HEX_DIGITS[c & 0xF];
975     c = (char) (c >>> 4);
976     r[4] = HEX_DIGITS[c & 0xF];
977     c = (char) (c >>> 4);
978     r[3] = HEX_DIGITS[c & 0xF];
979     c = (char) (c >>> 4);
980     r[2] = HEX_DIGITS[c & 0xF];
981     return r;
982   }
983 
failEqualityCheckNoComparisonFailure(ComparisonResult difference, Fact... facts)984   private void failEqualityCheckNoComparisonFailure(ComparisonResult difference, Fact... facts) {
985     // TODO(cpovirk): Is it possible for difference.factsOrEmpty() to be nonempty? If not, remove.
986     doFail(concat(asList(facts), difference.factsOrEmpty()));
987   }
988 
989   /**
990    * Assembles a failure message and passes it to the FailureStrategy
991    *
992    * @param verb the check being asserted
993    * @param expected the expectations against which the subject is compared
994    * @param failVerb the failure of the check being asserted
995    * @param actual the actual value the subject was compared against
996    * @deprecated Prefer to construct {@link Fact}-style methods, typically by using {@link
997    *     #failWithActual(Fact, Fact...)}. However, if you want to preserve your exact failure
998    *     message as a migration aid, you can inline this method.
999    */
1000   @Deprecated
failWithBadResults(String verb, Object expected, String failVerb, Object actual)1001   final void failWithBadResults(String verb, Object expected, String failVerb, Object actual) {
1002     String message =
1003         lenientFormat(
1004             "Not true that <%s> %s <%s>. It %s <%s>",
1005             actualCustomStringRepresentation(),
1006             verb,
1007             expected,
1008             failVerb,
1009             (actual == null) ? "null reference" : actual);
1010     failWithoutActual(simpleFact(message));
1011   }
1012 
1013   /**
1014    * Assembles a failure message with an alternative representation of the wrapped subject and
1015    * passes it to the FailureStrategy
1016    *
1017    * @param verb the check being asserted
1018    * @param expected the expected value of the check
1019    * @param actual the custom representation of the subject to be reported in the failure.
1020    * @deprecated Prefer to construct {@link Fact}-style methods, typically by using {@link
1021    *     #failWithoutActual(Fact, Fact...)}. However, if you want to preserve your exact failure
1022    *     message as a migration aid, you can inline this method.
1023    */
1024   @Deprecated
failWithCustomSubject(String verb, Object expected, Object actual)1025   final void failWithCustomSubject(String verb, Object expected, Object actual) {
1026     String message =
1027         lenientFormat(
1028             "Not true that <%s> %s <%s>",
1029             (actual == null) ? "null reference" : actual, verb, expected);
1030     failWithoutActual(simpleFact(message));
1031   }
1032 
1033   /**
1034    * @deprecated Prefer to construct {@link Fact}-style methods, typically by using {@link
1035    *     #failWithoutActual(Fact, Fact...) failWithoutActual}{@code (}{@link Fact#simpleFact
1036    *     simpleFact(...)}{@code )}. However, if you want to preserve your exact failure message as a
1037    *     migration aid, you can inline this method.
1038    */
1039   @Deprecated
failWithoutSubject(String check)1040   final void failWithoutSubject(String check) {
1041     String strSubject = this.customName == null ? "the subject" : "\"" + customName + "\"";
1042     failWithoutActual(simpleFact(lenientFormat("Not true that %s %s", strSubject, check)));
1043   }
1044 
1045   /**
1046    * Fails, reporting a message with the given facts, <i>without automatically adding the actual
1047    * value.</i>
1048    *
1049    * <p>Most failure messages should report the actual value, so most checks should call {@link
1050    * #failWithActual(Fact, Fact...) failWithActual} instead. However, {@code failWithoutActual} is
1051    * useful in some cases:
1052    *
1053    * <ul>
1054    *   <li>when the actual value is obvious from the rest of the message. For example, {@code
1055    *       isNotEmpty()} calls {@code failWithoutActual(simpleFact("expected not to be empty")}.
1056    *   <li>when the actual value shouldn't come last or should have a different key than the default
1057    *       of "but was." For example, {@code isNotWithin(...).of(...)} calls {@code
1058    *       failWithoutActual} so that it can put the expected and actual values together, followed
1059    *       by the tolerance.
1060    * </ul>
1061    *
1062    * <p>Example usage: The check {@code isEmpty()} calls {@code failWithActual(simpleFact("expected
1063    * to be empty"))}.
1064    */
failWithoutActual(Fact first, Fact... rest)1065   protected final void failWithoutActual(Fact first, Fact... rest) {
1066     doFail(ImmutableList.copyOf(Lists.asList(first, rest)));
1067   }
1068 
1069   // TODO(cpovirk): Consider making this protected if there's a need for it.
failWithoutActual(Iterable<Fact> facts)1070   final void failWithoutActual(Iterable<Fact> facts) {
1071     doFail(ImmutableList.copyOf(facts));
1072   }
1073 
1074   /**
1075    * Assembles a failure message without a given subject and passes it to the FailureStrategy
1076    *
1077    * @param check the check being asserted
1078    * @deprecated Prefer to construct {@link Fact}-style methods, typically by using {@link
1079    *     #failWithoutActual(Fact, Fact...) failWithoutActual}{@code (}{@link Fact#simpleFact
1080    *     simpleFact(...)}{@code )}. However, if you want to preserve your exact failure message as a
1081    *     migration aid, you can inline this method (and then inline the resulting method call, as
1082    *     well).
1083    */
1084   @Deprecated
failWithoutActual(String check)1085   final void failWithoutActual(String check) {
1086     failWithoutSubject(check);
1087   }
1088 
1089   /**
1090    * @throws UnsupportedOperationException always
1091    * @deprecated {@link Object#equals(Object)} is not supported on Truth subjects. If you are
1092    *     writing a test assertion (actual vs. expected), use {@link #isEqualTo(Object)} instead.
1093    */
1094   @DoNotCall(
1095       "Subject.equals() is not supported. Did you mean to call"
1096           + " assertThat(actual).isEqualTo(expected) instead of"
1097           + " assertThat(actual).equals(expected)?")
1098   @Deprecated
1099   @Override
equals(@ullable Object o)1100   public final boolean equals(@Nullable Object o) {
1101     throw new UnsupportedOperationException(
1102         "Subject.equals() is not supported. Did you mean to call"
1103             + " assertThat(actual).isEqualTo(expected) instead of"
1104             + " assertThat(actual).equals(expected)?");
1105   }
1106 
1107   /**
1108    * @throws UnsupportedOperationException always
1109    * @deprecated {@link Object#hashCode()} is not supported on Truth subjects.
1110    */
1111   @DoNotCall("Subject.hashCode() is not supported.")
1112   @Deprecated
1113   @Override
hashCode()1114   public final int hashCode() {
1115     throw new UnsupportedOperationException("Subject.hashCode() is not supported.");
1116   }
1117 
1118   /**
1119    * @throws UnsupportedOperationException always
1120    * @deprecated {@link Object#toString()} is not supported on Truth subjects.
1121    */
1122   @Deprecated
1123   @Override
1124   public
toString()1125   String toString() {
1126     throw new UnsupportedOperationException(
1127         "Subject.toString() is not supported. Did you mean to call assertThat(foo.toString())"
1128             + " instead of assertThat(foo).toString()?");
1129   }
1130 
1131   /**
1132    * Returns a "but was: <actual value>" string. This method should be rarely needed, since Truth
1133    * inserts a "but was" fact by default for assertions. However, it's occasionally useful for calls
1134    * to {@code failWithoutActual} that want a "but was" fact but don't want it to come last, where
1135    * Truth inserts it by default.
1136    */
1137   /*
1138    * TODO(cpovirk): Consider giving this protected access.
1139    *
1140    * It is likely better than what users would otherwise do -- `fact("but was", actual)`, which
1141    * ignores actualCustomStringRepresentation() (which is inaccessible outside the package).
1142    *
1143    * But I want to think more about this. In particular, if people use this to reimplement
1144    * isEqualTo(), I would be sad that they're missing out on its normal special handling. That's
1145    * probably not enough reason to avoid adding this, but we can hold it back for now.
1146    */
butWas()1147   final Fact butWas() {
1148     return fact("but was", actualCustomStringRepresentation());
1149   }
1150 
1151   /*
1152    * Computed lazily so that we're not doing expensive string operations during every assertion,
1153    * only during every failure.
1154    */
typeDescription()1155   final String typeDescription() {
1156     return typeDescriptionOrGuess(getClass(), typeDescriptionOverride);
1157   }
1158 
typeDescriptionOrGuess( Class<? extends Subject> clazz, @Nullable String typeDescriptionOverride)1159   private static String typeDescriptionOrGuess(
1160       Class<? extends Subject> clazz, @Nullable String typeDescriptionOverride) {
1161     if (typeDescriptionOverride != null) {
1162       return typeDescriptionOverride;
1163     }
1164     /*
1165      * j2cl doesn't store enough metadata to know whether "Foo$BarSubject" is a nested class, so it
1166      * can't tell whether the simple name is "Foo$BarSubject" or just "BarSubject": b/71808768. It
1167      * returns "Foo$BarSubject" to err on the side of preserving information. We want just
1168      * "BarSubject," so we strip any likely enclosing type ourselves.
1169      */
1170     String subjectClass = clazz.getSimpleName().replaceFirst(".*[$]", "");
1171     String actualClass =
1172         (subjectClass.endsWith("Subject") && !subjectClass.equals("Subject"))
1173             ? subjectClass.substring(0, subjectClass.length() - "Subject".length())
1174             : "Object";
1175     return UPPER_CAMEL.to(LOWER_CAMEL, actualClass);
1176   }
1177 
classMetadataUnsupported()1178   private static boolean classMetadataUnsupported() {
1179     // https://github.com/google/truth/issues/198
1180     // TODO(cpovirk): Consider whether to remove instanceof tests under GWT entirely.
1181     // TODO(cpovirk): Run more Truth tests under GWT, and add tests for this.
1182     return String.class.getSuperclass() == null;
1183   }
1184 
doFail(ImmutableList<Fact> facts)1185   private void doFail(ImmutableList<Fact> facts) {
1186     metadata.fail(prependNameIfAny(facts));
1187   }
1188 
prependNameIfAny(ImmutableList<Fact> facts)1189   private ImmutableList<Fact> prependNameIfAny(ImmutableList<Fact> facts) {
1190     return concat(nameAsFacts(), facts);
1191   }
1192 
nameAsFacts()1193   private ImmutableList<Fact> nameAsFacts() {
1194     return customName == null
1195         ? ImmutableList.<Fact>of()
1196         : ImmutableList.of(fact("name", customName));
1197   }
1198 }
1199