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