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