• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2014 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.Preconditions.checkNotNull;
19 
20 import org.checkerframework.checker.nullness.qual.Nullable;
21 
22 /**
23  * Propositions for {@link Throwable} subjects.
24  *
25  * @author Kurt Alfred Kluever
26  */
27 public class ThrowableSubject extends Subject {
28   private final @Nullable Throwable actual;
29 
30   /**
31    * Constructor for use by subclasses. If you want to create an instance of this class itself, call
32    * {@link Subject#check(String, Object...) check(...)}{@code .that(actual)}.
33    */
ThrowableSubject(FailureMetadata metadata, @Nullable Throwable throwable)34   protected ThrowableSubject(FailureMetadata metadata, @Nullable Throwable throwable) {
35     this(metadata, throwable, null);
36   }
37 
ThrowableSubject( FailureMetadata metadata, @Nullable Throwable throwable, @Nullable String typeDescription)38   ThrowableSubject(
39       FailureMetadata metadata, @Nullable Throwable throwable, @Nullable String typeDescription) {
40     super(metadata, throwable, typeDescription);
41     this.actual = throwable;
42   }
43 
44   /*
45    * TODO(cpovirk): consider a special case for isEqualTo and isSameInstanceAs that adds |expected|
46    * as a suppressed exception
47    */
48 
49   /** Returns a {@code StringSubject} to make assertions about the throwable's message. */
hasMessageThat()50   public final StringSubject hasMessageThat() {
51     StandardSubjectBuilder check = check("getMessage()");
52     if (actual instanceof ErrorWithFacts && ((ErrorWithFacts) actual).facts().size() > 1) {
53       check =
54           check.withMessage(
55               "(Note from Truth: When possible, instead of asserting on the full message, assert"
56                   + " about individual facts by using ExpectFailure.assertThat.)");
57     }
58     return check.that(checkNotNull(actual).getMessage());
59   }
60 
61   /**
62    * Returns a new {@code ThrowableSubject} that supports assertions on this throwable's direct
63    * cause. This method can be invoked repeatedly (e.g. {@code
64    * assertThat(e).hasCauseThat().hasCauseThat()....} to assert on a particular indirect cause.
65    */
66   // Any Throwable is fine, and we use plain Throwable to emphasize that it's not used "for real."
67   @SuppressWarnings("ShouldNotSubclass")
hasCauseThat()68   public final ThrowableSubject hasCauseThat() {
69     // provides a more helpful error message if hasCauseThat() methods are chained too deep
70     // e.g. assertThat(new Exception()).hCT().hCT()....
71     // TODO(diamondm) in keeping with other subjects' behavior this should still NPE if the subject
72     // *itself* is null, since there's no context to lose. See also b/37645583
73     if (actual == null) {
74       check("getCause()")
75           .withMessage("Causal chain is not deep enough - add a .isNotNull() check?")
76           .fail();
77       return ignoreCheck()
78           .that(
79               new Throwable() {
80                 @Override
81                 @SuppressWarnings("UnsynchronizedOverridesSynchronized")
82                 public Throwable fillInStackTrace() {
83                   setStackTrace(new StackTraceElement[0]); // for old versions of Android
84                   return this;
85                 }
86               });
87     }
88     return check("getCause()").that(actual.getCause());
89   }
90 }
91