• 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.Preconditions.checkNotNull;
19 import static com.google.common.base.Preconditions.checkState;
20 import static com.google.common.base.Verify.verifyNotNull;
21 import static com.google.common.truth.ComparisonFailures.makeComparisonFailureFacts;
22 import static com.google.common.truth.Fact.fact;
23 import static com.google.common.truth.LazyMessage.evaluateAll;
24 import static com.google.common.truth.Platform.cleanStackTrace;
25 import static com.google.common.truth.Platform.inferDescription;
26 import static com.google.common.truth.Platform.makeComparisonFailure;
27 import static com.google.common.truth.SubjectUtils.append;
28 import static com.google.common.truth.SubjectUtils.concat;
29 
30 import com.google.common.base.Function;
31 import com.google.common.collect.ImmutableList;
32 import org.jspecify.annotations.Nullable;
33 
34 /**
35  * An opaque, immutable object containing state from the previous calls in the fluent assertion
36  * chain. It appears primarily as a parameter to {@link Subject} constructors (and {@link
37  * Subject.Factory} methods), which should pass it to the superclass constructor and not otherwise
38  * use or store it. In particular, users should not attempt to call {@code Subject} constructors or
39  * {@code Subject.Factory} methods directly. Instead, they should use the appropriate factory
40  * method:
41  *
42  * <ul>
43  *   <li>If you're writing a test: {@link Truth#assertAbout(Subject.Factory)}{@code .that(...)}
44  *   <li>If you're creating a derived subject from within another subject: {@code
45  *       check(...).about(...).that(...)}
46  *   <li>If you're testing your subject to verify that assertions fail when they should: {@link
47  *       ExpectFailure}
48  * </ul>
49  *
50  * <p>(One exception: Implementations of {@link CustomSubjectBuilder} do directly call constructors,
51  * using their {@link CustomSubjectBuilder#metadata()} method to get an instance to pass to the
52  * constructor.)
53  */
54 public final class FailureMetadata {
forFailureStrategy(FailureStrategy failureStrategy)55   static FailureMetadata forFailureStrategy(FailureStrategy failureStrategy) {
56     return new FailureMetadata(
57         failureStrategy, ImmutableList.<LazyMessage>of(), ImmutableList.<Step>of());
58   }
59 
60   private final FailureStrategy strategy;
61 
62   /**
63    * The data from a call to either (a) a {@link Subject} constructor or (b) {@link Subject#check}.
64    */
65   private static final class Step {
subjectCreation(Subject subject)66     static Step subjectCreation(Subject subject) {
67       return new Step(checkNotNull(subject), null, null);
68     }
69 
checkCall( @ullable OldAndNewValuesAreSimilar valuesAreSimilar, @Nullable Function<String, String> descriptionUpdate)70     static Step checkCall(
71         @Nullable OldAndNewValuesAreSimilar valuesAreSimilar,
72         @Nullable Function<String, String> descriptionUpdate) {
73       return new Step(null, descriptionUpdate, valuesAreSimilar);
74     }
75 
76     /*
77      * We store Subject, rather than the actual value itself, so that we can call
78      * actualCustomStringRepresentation(). Why not call actualCustomStringRepresentation()
79      * immediately? First, it might be expensive, and second, the Subject isn't initialized at the
80      * time we receive it. We *might* be able to make it safe to call if it looks only at actual(),
81      * but it might try to look at facts initialized by a subclass, which aren't ready yet.
82      */
83     final @Nullable Subject subject;
84 
85     final @Nullable Function<String, String> descriptionUpdate;
86 
87     // Present only when descriptionUpdate is.
88     final @Nullable OldAndNewValuesAreSimilar valuesAreSimilar;
89 
Step( @ullable Subject subject, @Nullable Function<String, String> descriptionUpdate, @Nullable OldAndNewValuesAreSimilar valuesAreSimilar)90     private Step(
91         @Nullable Subject subject,
92         @Nullable Function<String, String> descriptionUpdate,
93         @Nullable OldAndNewValuesAreSimilar valuesAreSimilar) {
94       this.subject = subject;
95       this.descriptionUpdate = descriptionUpdate;
96       this.valuesAreSimilar = valuesAreSimilar;
97     }
98 
isCheckCall()99     boolean isCheckCall() {
100       return subject == null;
101     }
102   }
103 
104   /*
105    * TODO(cpovirk): This implementation is wasteful, especially because `steps` is used even by
106    * non-chaining assertions. If it ever does matter, we could use an immutable cactus stack -- or
107    * probably even avoid storing most of the chain entirely (unless we end up wanting more of the
108    * chain to show "telescoping context," as in "the int value of this optional in this list in this
109    * multimap").
110    */
111 
112   private final ImmutableList<LazyMessage> messages;
113 
114   private final ImmutableList<Step> steps;
115 
FailureMetadata( FailureStrategy strategy, ImmutableList<LazyMessage> messages, ImmutableList<Step> steps)116   FailureMetadata(
117       FailureStrategy strategy, ImmutableList<LazyMessage> messages, ImmutableList<Step> steps) {
118     this.strategy = checkNotNull(strategy);
119     this.messages = checkNotNull(messages);
120     this.steps = checkNotNull(steps);
121   }
122 
123   /**
124    * Returns a new instance that includes the given subject in its chain of values. Truth users do
125    * not need to call this method directly; Truth automatically accumulates context, starting from
126    * the initial that(...) call and continuing into any chained calls, like {@link
127    * ThrowableSubject#hasMessageThat}.
128    */
updateForSubject(Subject subject)129   FailureMetadata updateForSubject(Subject subject) {
130     ImmutableList<Step> steps = append(this.steps, Step.subjectCreation(subject));
131     return derive(messages, steps);
132   }
133 
updateForCheckCall()134   FailureMetadata updateForCheckCall() {
135     ImmutableList<Step> steps = append(this.steps, Step.checkCall(null, null));
136     return derive(messages, steps);
137   }
138 
updateForCheckCall( OldAndNewValuesAreSimilar valuesAreSimilar, Function<String, String> descriptionUpdate)139   FailureMetadata updateForCheckCall(
140       OldAndNewValuesAreSimilar valuesAreSimilar, Function<String, String> descriptionUpdate) {
141     checkNotNull(descriptionUpdate);
142     ImmutableList<Step> steps =
143         append(this.steps, Step.checkCall(valuesAreSimilar, descriptionUpdate));
144     return derive(messages, steps);
145   }
146 
147   /**
148    * Whether the value of the original subject and the value of the derived subject are "similar
149    * enough" that we don't need to display both. For example, if we're printing a message about the
150    * value of optional.get(), there's no need to print the optional itself because it adds no
151    * information. Similarly, if we're printing a message about the asList() view of an array,
152    * there's no need to also print the array.
153    */
154   enum OldAndNewValuesAreSimilar {
155     SIMILAR,
156     DIFFERENT;
157   }
158 
159   /**
160    * Returns a new instance whose failures will contain the given message. The way for Truth users
161    * to set a message is {@code check(...).withMessage(...).that(...)} (for calls from within a
162    * {@code Subject}) or {@link Truth#assertWithMessage} (for most other calls).
163    */
withMessage(String format, @Nullable Object[] args)164   FailureMetadata withMessage(String format, @Nullable Object[] args) {
165     ImmutableList<LazyMessage> messages = append(this.messages, new LazyMessage(format, args));
166     return derive(messages, steps);
167   }
168 
failEqualityCheck( ImmutableList<Fact> tailFacts, String expected, String actual)169   void failEqualityCheck(
170       ImmutableList<Fact> tailFacts,
171       String expected,
172       String actual) {
173     doFail(
174         makeComparisonFailure(
175             evaluateAll(messages),
176             makeComparisonFailureFacts(
177                 description(), concat(tailFacts, rootUnlessThrowable()), expected, actual),
178             expected,
179             actual,
180             rootCause()));
181   }
182 
fail(ImmutableList<Fact> facts)183   void fail(ImmutableList<Fact> facts) {
184     doFail(
185         new AssertionErrorWithFacts(
186             evaluateAll(messages),
187             concat(description(), facts, rootUnlessThrowable()),
188             rootCause()));
189   }
190 
doFail(AssertionError failure)191   private void doFail(AssertionError failure) {
192     cleanStackTrace(failure);
193     strategy.fail(failure);
194   }
195 
derive(ImmutableList<LazyMessage> messages, ImmutableList<Step> steps)196   private FailureMetadata derive(ImmutableList<LazyMessage> messages, ImmutableList<Step> steps) {
197     return new FailureMetadata(strategy, messages, steps);
198   }
199 
200   /**
201    * Returns a description of the final actual value, if it appears "interesting" enough to show.
202    * The description is considered interesting if the chain of derived subjects ends with at least
203    * one derivation that we have a name for. It's also considered interesting in the absence of
204    * derived subjects if we inferred a name for the root actual value from the bytecode.
205    *
206    * <p>We don't want to say: "value of string: expected [foo] but was [bar]" (OK, we might still
207    * decide to say this, but for now, we don't.)
208    *
209    * <p>We do want to say: "value of throwable.getMessage(): expected [foo] but was [bar]"
210    *
211    * <p>We also want to say: "value of getLogMessages(): expected not to be empty"
212    *
213    * <p>To support that, {@code descriptionIsInteresting} tracks whether we've been given context
214    * through {@code check} calls <i>that include names</i> or, initially, whether we inferred a name
215    * for the root actual value from the bytecode.
216    *
217    * <p>If we're missing a naming function halfway through, we have to reset: We don't want to claim
218    * that the value is "foo.bar.baz" when it's "foo.bar.somethingelse.baz." We have to go back to
219    * "object.baz." (But note that {@link #rootUnlessThrowable} will still provide the value of the
220    * root foo to the user as long as we had at least one naming function: We might not know the
221    * root's exact relationship to the final object, but we know it's some object "different enough"
222    * to be worth displaying.)
223    */
description()224   private ImmutableList<Fact> description() {
225     String description = inferDescription();
226     boolean descriptionIsInteresting = description != null;
227     for (Step step : steps) {
228       if (step.isCheckCall()) {
229         checkState(description != null);
230         if (step.descriptionUpdate == null) {
231           description = null;
232           descriptionIsInteresting = false;
233         } else {
234           description = verifyNotNull(step.descriptionUpdate.apply(description));
235           descriptionIsInteresting = true;
236         }
237         continue;
238       }
239 
240       if (description == null) {
241         description = checkNotNull(step.subject).typeDescription();
242       }
243     }
244     return descriptionIsInteresting
245         ? ImmutableList.of(fact("value of", description))
246         : ImmutableList.<Fact>of();
247   }
248 
249   /**
250    * Returns the root actual value, if we know it's "different enough" from the final actual value.
251    *
252    * <p>We don't want to say: "expected [foo] but was [bar]. string: [bar]"
253    *
254    * <p>We do want to say: "expected [foo] but was [bar]. myObject: MyObject[string=bar, i=0]"
255    *
256    * <p>To support that, {@code seenDerivation} tracks whether we've seen multiple actual values,
257    * which is equivalent to whether we've seen multiple Subject instances or, more informally,
258    * whether the user is making a chained assertion.
259    *
260    * <p>There's one wrinkle: Sometimes chaining doesn't add information. This is often true with
261    * "internal" chaining, like when StreamSubject internally creates an IterableSubject to delegate
262    * to. The two subjects' string representations will be identical (or, in some cases, _almost_
263    * identical), so there is no value in showing both. In such cases, implementations can call the
264    * no-arg {@code checkNoNeedToDisplayBothValues()}, which sets {@code valuesAreSimilar},
265    * instructing this method that that particular chain link "doesn't count." (Note also that there
266    * are some edge cases that we're not sure how to handle yet, for which we might introduce
267    * additional {@code check}-like methods someday.)
268    */
269   // TODO(b/134505914): Consider returning multiple facts in some cases.
rootUnlessThrowable()270   private ImmutableList<Fact> rootUnlessThrowable() {
271     Step rootSubject = null;
272     boolean seenDerivation = false;
273     for (Step step : steps) {
274       if (step.isCheckCall()) {
275         /*
276          * If we don't have a description update, don't trigger display of a root object. (If we
277          * did, we'd change the messages of a bunch of existing subjects, and we don't want to bite
278          * that off yet.)
279          *
280          * If we do have a description update, then trigger display of a root object but only if the
281          * old and new values are "different enough" to be worth both displaying.
282          */
283         seenDerivation |=
284             step.descriptionUpdate != null
285                 && step.valuesAreSimilar == OldAndNewValuesAreSimilar.DIFFERENT;
286         continue;
287       }
288 
289       if (rootSubject == null) {
290         if (checkNotNull(step.subject).actual() instanceof Throwable) {
291           /*
292            * We'll already include the Throwable as a cause of the AssertionError (see rootCause()),
293            * so we don't need to include it again in the message.
294            */
295           return ImmutableList.of();
296         }
297         rootSubject = step;
298       }
299     }
300     /*
301      * TODO(cpovirk): Maybe say "root foo was: ..." instead of just "foo was: ..." if there's more
302      * than one foo in the chain, if the description string doesn't start with "foo," and/or if the
303      * name we have is just "object?"
304      */
305     return seenDerivation
306         ? ImmutableList.of(
307             fact(
308                 // TODO(cpovirk): Use inferDescription() here when appropriate? But it can be long.
309                 checkNotNull(checkNotNull(rootSubject).subject).typeDescription() + " was",
310                 checkNotNull(checkNotNull(rootSubject).subject)
311                     .actualCustomStringRepresentationForPackageMembersToCall()))
312         : ImmutableList.<Fact>of();
313   }
314 
315   /**
316    * Returns the first {@link Throwable} in the chain of actual values. Typically, we'll have a root
317    * cause only if the assertion chain contains a {@link ThrowableSubject}.
318    */
rootCause()319   private @Nullable Throwable rootCause() {
320     for (Step step : steps) {
321       if (!step.isCheckCall() && checkNotNull(step.subject).actual() instanceof Throwable) {
322         return (Throwable) step.subject.actual();
323       }
324     }
325     return null;
326   }
327 }
328