/*
* Copyright (c) 2011 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.truth;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.StringUtil.messageFor;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Objects;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
@GwtIncompatible("JUnit4")
public class Expect extends TestVerb implements TestRule {
public static class ExpectationGatherer extends FailureStrategy {
private final List<ExpectationFailure> messages = new ArrayList<ExpectationFailure>();
private final boolean showStackTrace;
public ExpectationGatherer() {
this.showStackTrace = false;
}
public ExpectationGatherer(boolean showStackTrace) {
this.showStackTrace = showStackTrace;
}
@Override
public void fail(String message) {
fail(checkNotNull(message), new Throwable(message));
}
@Override
public void failComparing(String message, CharSequence expected, CharSequence actual) {
String errorMessage = messageFor(message, expected, actual);
fail(errorMessage, new Throwable(errorMessage));
}
@Override
public void fail(String message, Throwable cause) {
messages.add(ExpectationFailure.create(message, cause));
}
public List<ExpectationFailure> getMessages() {
return messages;
}
@Override
public String toString() {
Throwable earliestCause = null;
StringBuilder message = new StringBuilder("All failed expectations:\n");
int count = 0;
for (ExpectationFailure failure : getMessages()) {
if (earliestCause == null && failure.cause() != null) {
earliestCause = failure.cause();
}
message
.append(" ")
.append((count++) + 1)
.append(". ")
.append(failure.message())
.append("\n");
if (showStackTrace && failure.cause() != null) {
// Append stack trace to the failure message
StringWriter stackTraceWriter = new StringWriter();
failure.cause().printStackTrace(new PrintWriter(stackTraceWriter));
message.append(stackTraceWriter + "\n");
}
}
return message.toString();
}
}
static final class ExpectationFailure {
private final String message;
@Nullable private final Throwable cause;
static ExpectationFailure create(String message, @Nullable Throwable cause) {
return new ExpectationFailure(message, cause);
}
private ExpectationFailure(String message, @Nullable Throwable cause) {
this.message = checkNotNull(message);
this.cause = cause;
}
String message() {
return message;
}
@Nullable
Throwable cause() {
return cause;
}
@Override
public boolean equals(@Nullable Object other) {
if (other instanceof ExpectationFailure) {
ExpectationFailure that = (ExpectationFailure) other;
return this.message.equals(that.message) && Objects.equal(this.cause, that.cause);
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hashCode(message, cause);
}
}
private final ExpectationGatherer gatherer;
private boolean inRuleContext = false;
public static Expect create() {
return create(new ExpectationGatherer());
}
public static Expect create(ExpectationGatherer gatherer) {
return new Expect(gatherer);
}
public static Expect createAndEnableStackTrace() {
return new Expect(new ExpectationGatherer(true /* showStackTrace */));
}
Expect(ExpectationGatherer gatherer) {
super(gatherer);
this.gatherer = checkNotNull(gatherer);
}
public boolean hasFailures() {
return !gatherer.getMessages().isEmpty();
}
@Override
protected FailureStrategy getFailureStrategy() {
if (!inRuleContext) {
String message = "assertion made on Expect instance, but it's not enabled as a @Rule.";
throw new IllegalStateException(message);
}
return super.getFailureStrategy();
}
// TODO(cgruber): Make this override TestRule when 4.9 is released.
@Override
public Statement apply(final Statement base, Description description) {
checkNotNull(base);
checkNotNull(description);
return new Statement() {
@Override
public void evaluate() throws Throwable {
inRuleContext = true;
base.evaluate();
inRuleContext = false;
Throwable earliestCause = null;
if (!gatherer.getMessages().isEmpty()) {
AssertionError error = new AssertionError(gatherer.toString());
error.initCause(earliestCause);
throw error;
}
}
};
}
}