Skip to content
Commits on Source (103)
......@@ -4,3 +4,7 @@ target
.classpath
.settings
.clover
.*
*.iml
*.ipr
*.iws
sudo: false
language: java
jdk:
- &jdk_for_publishing oraclejdk8
install:
- mvn -B -U -P!standard-with-extra-repos dependency:go-offline test clean --quiet --fail-never -DskipTests=true
script:
- mvn -B -U -P!standard-with-extra-repos verify --fail-at-end -Dsource.skip=true -Dmaven.javadoc.skip=true
env:
global:
- secure: Q8O565LFFwytrjS+vRHlzw0sYGjFA4npvaMbyG5w3uoodnHKTh0tMOwiToGzGS0CHRf83LffTlbodVVIWkiyzxcbNwMslkOxLpFtI0Hif2apb/mSQ6vdO/SXTb1MVwQBqBGCF47KBw5XDjvRbXmIh8+4z9yIBO8oWqEbHvBYQl0=
- secure: EMRsJLQfGoSrArcemMmjmlI4Ut2eGoLgIW/lxHEa/zSzOqSGMUdxo7+Hd23VxQqWWYAa2fV4NPSUqBZrJkS6RFxQOE4CYLpmVenSRdVTNXa0Cw+48k6aw4/kOfOafn7s5EJ/pA0dYjFgj1V2F+vRIywrNs0tpziFanWvMVUBAOY=
- secure: XpixMe6WG/U2lUgbkZohDDiDJnrXdra+K+AUvg8ze35Wd6ffPdHh9oLMR6DbMq82VV21G9WcJyRQM7g/mCBHOXDt6/0fj/IPUaev15QsJITF1TAJRISxL4DClsJX6U7i2y+KaZ1pOSnsEFvDHE7QM9+DYqG4cPX++bt7Lr33v+4=
- JDK_FOR_PUBLISHING: *jdk_for_publishing
after_success:
- bash util/generate-latest-docs.sh
- bash util/publish-snapshot-on-commit.sh
branches:
only:
- master
- /^release.*$/
# https://github.com/travis-ci/travis-ci/issues/3259#issuecomment-130860338
addons:
apt:
packages:
- oracle-java8-installer
cache:
directories:
- $HOME/.m2
# How to contribute
We'd love to accept your patches and contributions to this project. There are a
just a few small guidelines you need to follow.
## Contributor License Agreement
Contributions to any Google project must be accompanied by a Contributor License
Agreement. This is not a copyright **assignment**, it simply gives Google
permission to use and redistribute your contributions as part of the project.
When submitting a pull request, if you have not already signed the
[Contributor License Agreement (CLA)][CLA], then a bot will remind you.
Code cannot even be evaluated without this step.
## Submitting a patch
1. It's generally best to start by opening a new issue describing the bug or
feature you're intending to fix. Even if you think it's relatively minor,
it's helpful to know what people are working on. Mention in the initial
issue that you are planning to work on that bug or feature so that it can be
assigned to you.
2. Follow the normal process of [forking] the project, and setup a new branch
to work in. It's important that each group of changes be done in separate
branches in order to ensure that a pull request only includes the commits
related to that bug or feature.
3. Any significant changes should almost always be accompanied by tests. The
project already has good test coverage, so look at some of the existing
tests if you're unsure how to go about it.
4. All contributions must be licensed Apache 2.0 and all files must have a copy
of the boilerplate licence comment (can be copied from an existing file).
Files should be formatted according to Google's [java style guide].
5. Do your best to have [well-formed commit messages] for each change. This
provides consistency throughout the project, and ensures that commit
messages are able to be formatted properly by various git tools.
6. Finally, push the commits to your fork and submit a [pull request].
[CLA]: https://cla.developers.google.com
[forking]: https://help.github.com/articles/fork-a-repo
[java style guide]: https://google.github.io/styleguide/javaguide.html
[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
[pull request]: https://help.github.com/articles/creating-a-pull-request
......@@ -200,4 +200,3 @@
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.
Truth: We've made failure a strategy.
# Truth
[![Main Site][gh-pages-shield]][gh-pages-link]
[![Build Status][travis-shield]][travis-link]
[![Maven Release][maven-shield]][maven-link]
[![Stackoverflow][stackoverflow-shield]][stackoverflow-link]
## What is Truth?
Truth is an assertion framework for Java tests, inspired by
FEST, and driven by some extensibility needs, written nearly entirely by Google
employees in their spare time or contributing in their capacity as Java core
librarians.
Truth can be used in place of JUnit's assertions, FEST, or Hamcrest's matchers,
or it can be used alongside where other approaches seem more suitable.
The full documentation for Truth is available at [its main website](http://google.github.io/truth)
## License
Truth is licensed under the open-source [Apache 2.0 license](LICENSE).
## Contributing
Please [see the guidelines for contributing](CONTRIBUTING.md) before creating
pull requests.
## Acknowledgements
Thanks to Github and Travis-CI for having a strong commitment to open-source,
and providing us with tools so we can provide others with code. And thanks to
Google for [Guava], for taking on the Truth project and making it part of their
core-libraries effort, and for encouraging us to try to solve problems in
better ways and share that with the world.
Also thanks to the authors of JUnit, TestNG, Hamcrest, FEST, and others for
creating testing tools that let us write high-quality code, for inspiring this
work and for moving the ball forward in the field of automated software testing.
This project works with, works alongside, and sometimes works in competition
with the above tools, but owes a debt that everyone owes to those gone before.
They paved the way, and we hope this contribution is helpful to the field.
<!-- references -->
[Guava]: http://github.com/google/guava
[gh-pages-shield]: https://img.shields.io/badge/main%20site-google.github.io/truth-ff55ff.png?style=flat
[gh-pages-link]: http://google.github.io/truth/
[travis-shield]: https://img.shields.io/travis/google/truth.png
[travis-link]: https://travis-ci.org/google/truth
[maven-shield]: https://img.shields.io/maven-central/v/com.google.truth/truth.png
[maven-link]: http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.google.truth%22%20AND%20a%3A%22truth%22
[stackoverflow-shield]: https://img.shields.io/badge/stackoverflow-truth-5555ff.png?style=flat
[stackoverflow-link]: http://stackoverflow.com/questions/tagged/google-truth
# To run all tests, just...
mvn test
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.google.truth</groupId>
<artifactId>truth-parent</artifactId>
<version>0.39</version>
</parent>
<artifactId>truth</artifactId>
<name>Truth Core</name>
<properties>
<guava.version>23.4-android</guava.version>
<guava-gwt.version>23.4-jre</guava-gwt.version>
<gwt.version>2.8.2</gwt.version>
<junit.version>4.12</junit.version>
<jsr305.version>3.0.2</jsr305.version>
<auto-value.version>1.5.3</auto-value.version>
<compile-testing.version>0.15</compile-testing.version>
<error-prone.annotations.version>2.1.3</error-prone.annotations.version>
</properties>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>${jsr305.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!-- Required only to build the -gwt sub-artifact. -->
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-user</artifactId>
<version>${gwt.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>${auto-value.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-gwt</artifactId>
<version>${guava-gwt.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-testlib</artifactId>
<version>${guava.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.testing.compile</groupId>
<artifactId>compile-testing</artifactId>
<version>${compile-testing.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
<version>${error-prone.annotations.version}</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<excludes>
<exclude>**/*.java</exclude>
<exclude>**/*.gwt.xml</exclude>
</excludes>
</resource>
</resources>
<testResources>
<testResource><directory>src/test/java</directory></testResource>
</testResources>
<plugins>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/super/**/*.java</exclude>
</excludes>
<testExcludes>
<!-- Requires Java 8. -->
<testExclude>**/EmployeeSubjectTest.java</testExclude>
</testExcludes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/super/**</exclude>
<exclude>**/*.gwt.xml</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<!-- TODO(cgruber): Replace this with Gwt annotation processing plugin. -->
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>attach-gwt-sources</id>
<phase>post-integration-test</phase>
<goals><goal>jar</goal></goals>
<configuration>
<classifier>gwt</classifier>
<classesDirectory>src/main/java</classesDirectory>
<includes>
<include>**/*.java</include>
<include>**/*.gwt.xml</include>
</includes>
<excludes>
<exclude>com/google/common/truth/ClassSubject.java</exclude>
<exclude>com/google/common/truth/Expect.java</exclude>
<exclude>com/google/common/truth/IteratingVerb.java</exclude>
<exclude>com/google/common/truth/ReflectionUtil.java</exclude>
<exclude>com/google/common/truth/codegen/**</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/*GwtTest.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>gwt-maven-plugin</artifactId>
<version>${gwt.version}</version>
<executions>
<execution>
<id>gwt-test</id>
<goals><goal>test</goal></goals>
<configuration>
<mode>htmlunit</mode>
<productionMode>true</productionMode>
<sourceLevel>auto</sourceLevel>
<userAgents>gecko1_8</userAgents>
<includes>**/*GwtTest.java</includes>
<!-- Keep these timeouts very large because, if we hit the timeout, the tests silently pass :( -->
<testTimeOut>86400 <!-- seconds --></testTimeOut>
<testMethodTimeout>1440 <!-- minutes --></testMethodTimeout>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>2.0.1</version>
</plugin>
</plugins>
</reporting>
<profiles>
<profile>
<id>java8</id>
<activation>
<jdk>[1.8,)</jdk>
</activation>
<properties>
<javadoc.param>-Xdoclint:none</javadoc.param>
</properties>
</profile>
</profiles>
</project>
/*
* Copyright (c) 2014 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.checkArgument;
import java.util.List;
import javax.annotation.Nullable;
/**
* A common supertype for Array subjects, abstracting some common display and error infrastructure.
*
* @author Christian Gruber (cgruber@israfil.net)
*/
abstract class AbstractArraySubject<S extends AbstractArraySubject<S, T>, T> extends Subject<S, T> {
AbstractArraySubject(FailureMetadata metadata, @Nullable T actual) {
super(metadata, actual);
}
/** Fails if the array is not empty (i.e. {@code array.length != 0}). */
public void isEmpty() {
if (!listRepresentation().isEmpty()) {
fail("is empty");
}
}
/** Fails if the array is empty (i.e. {@code array.length == 0}). */
public void isNotEmpty() {
if (listRepresentation().isEmpty()) {
fail("is not empty");
}
}
/**
* Fails if the array does not have the given length.
*
* @throws IllegalArgumentException if {@code length < 0}
*/
public void hasLength(int length) {
checkArgument(length >= 0, "length (%s) must be >= 0");
if (listRepresentation().size() != length) {
fail("has length", length);
}
}
abstract String underlyingType();
/** Returns a List representation suitable for displaying in a string. */
abstract List<?> listRepresentation();
// TODO(cgruber): Kill once displayedAs() exists, since this attempts to make .named() do that.
@Override
protected String actualCustomStringRepresentation() {
Object listRepresentation = actual() == null ? "null" : listRepresentation();
return (internalCustomName() == null)
? "(" + underlyingType() + brackets() + ") " + listRepresentation + ""
: "";
}
void failWithBadType(Object expected) {
String expectedBrackets = "";
Class<?> expectedType = expected.getClass();
while (expectedType.isArray()) {
expectedBrackets += "[]";
expectedType = expectedType.getComponentType();
}
String expectedTypeString = expectedType.getName() + expectedBrackets;
failWithRawMessage(
"Incompatible types compared. expected: %s, actual: %s%s",
StringUtil.compressType(expectedTypeString), underlyingType(), brackets());
}
/**
* Returns the brackets to put after the underlying type. Multi-dimensional array subjects should
* override this to return the correct number of brackets.
*/
String brackets() {
return "[]";
}
}
/*
* 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.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.AtomicLongMap;
import javax.annotation.Nullable;
/**
* Propositions for {@link AtomicLongMap} subjects.
*
* @author Kurt Alfred Kluever
*/
public final class AtomicLongMapSubject extends Subject<AtomicLongMapSubject, AtomicLongMap<?>> {
AtomicLongMapSubject(FailureMetadata metadata, @Nullable AtomicLongMap<?> map) {
super(metadata, map);
}
/**
* @deprecated {@link AtomicLongMap} does not define equality (i.e., it does not implement
* equals()), so you probably don't want to call this method. Instead, perform your assertion
* on the map view (e.g., assertThat(atomicLongMap.asMap()).isEqualTo(EXPECTED_MAP)).
*/
@Deprecated
@Override
public void isEqualTo(@Nullable Object other) {
super.isEqualTo(other);
}
/**
* @deprecated {@link AtomicLongMap} does not define equality (i.e., it does not implement
* equals()), so you probably don't want to call this method. Instead, perform your assertion
* on the map view (e.g., assertThat(atomicLongMap.asMap()).isNotEqualTo(UNEXPECTED_MAP)).
*/
@Deprecated
@Override
public void isNotEqualTo(@Nullable Object other) {
super.isNotEqualTo(other);
}
/** Fails if the {@link AtomicLongMap} is not empty. */
public void isEmpty() {
if (!actual().isEmpty()) {
fail("is empty");
}
}
/** Fails if the {@link AtomicLongMap} is empty. */
public void isNotEmpty() {
if (actual().isEmpty()) {
fail("is not empty");
}
}
/** Fails if the {@link AtomicLongMap} does not have the given size. */
public void hasSize(int expectedSize) {
checkArgument(expectedSize >= 0, "expectedSize (%s) must be >= 0", expectedSize);
int actualSize = actual().size();
if (actualSize != expectedSize) {
failWithBadResults("has a size of", expectedSize, "is", actualSize);
}
}
/** Fails if the {@link AtomicLongMap} does not have the given sum. */
public void hasSum(long expectedSum) {
long actualSum = actual().sum();
if (actualSum != expectedSum) {
failWithBadResults("has a sum of", expectedSum, "is", actualSum);
}
}
/** Fails if the {@link AtomicLongMap} does not contain the given key. */
public void containsKey(Object key) {
checkNotNull(key, "AtomicLongMap does not support null keys");
if (!actual().containsKey(key)) {
fail("contains key", key);
}
}
/** Fails if the {@link AtomicLongMap} contains the given key. */
public void doesNotContainKey(Object key) {
checkNotNull(key, "AtomicLongMap does not support null keys");
if (actual().containsKey(key)) {
fail("does not contain key", key);
}
}
/** Fails if the {@link AtomicLongMap} does not contain the given entry. */
public void containsEntry(Object key, long value) {
checkNotNull(key, "AtomicLongMap does not support null keys");
long actualValue = ((AtomicLongMap<Object>) actual()).get(key);
if (actualValue != value) {
fail("contains entry", Maps.immutableEntry(key, value));
}
}
/** Fails if the {@link AtomicLongMap} contains the given entry. */
public void doesNotContainEntry(@Nullable Object key, long value) {
if (key != null) {
long actualValue = ((AtomicLongMap<Object>) actual()).get(key);
if (actualValue == value) {
fail("does not contain entry", Maps.immutableEntry(key, value));
}
}
}
// TODO(kak): Consider adding containsExactly() / containsExactlyEntriesIn() like MapSubject?
}
/*
* Copyright (c) 2015 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 java.math.BigDecimal;
import javax.annotation.Nullable;
/**
* Propositions for {@link BigDecimal} typed subjects.
*
* @author Kurt Alfred Kluever
*/
public final class BigDecimalSubject extends ComparableSubject<BigDecimalSubject, BigDecimal> {
BigDecimalSubject(FailureMetadata metadata, @Nullable BigDecimal actual) {
super(metadata, actual);
}
/**
* Fails if the subject's value is not equal to the value of the given {@link BigDecimal}. (i.e.,
* fails if {@code actual.comparesTo(expected) != 0}).
*
* <p><b>Note:</b> The scale of the BigDecimal is ignored. If you want to compare the values and
* the scales, use {@link #isEqualTo(Object)}.
*/
public void isEqualToIgnoringScale(BigDecimal expected) {
compareValues(expected);
}
/**
* Fails if the subject's value is not equal to the value of the {@link BigDecimal} created from
* the expected string (i.e., fails if {@code actual.comparesTo(new BigDecimal(expected)) != 0}).
*
* <p><b>Note:</b> The scale of the BigDecimal is ignored. If you want to compare the values and
* the scales, use {@link #isEqualTo(Object)}.
*/
public void isEqualToIgnoringScale(String expected) {
compareValues(new BigDecimal(expected));
}
/**
* Fails if the subject's value is not equal to the value of the {@link BigDecimal} created from
* the expected {@code long} (i.e., fails if {@code actual.comparesTo(new BigDecimal(expected)) !=
* 0}).
*
* <p><b>Note:</b> The scale of the BigDecimal is ignored. If you want to compare the values and
* the scales, use {@link #isEqualTo(Object)}.
*/
public void isEqualToIgnoringScale(long expected) {
compareValues(new BigDecimal(expected));
}
/**
* Fails if the subject's value and scale is not equal to the given {@link BigDecimal}.
*
* <p><b>Note:</b> If you only want to compare the values of the BigDecimals and not their scales,
* use {@link #isEqualToIgnoringScale(BigDecimal)} instead.
*/
@Override // To express more specific javadoc
public void isEqualTo(@Nullable Object expected) {
super.isEqualTo(expected);
}
/**
* Fails if the subject is not equivalent to the given value according to {@link
* Comparable#compareTo}, (i.e., fails if {@code a.comparesTo(b) != 0}). This method behaves
* identically to (the more clearly named) {@link #isEqualToIgnoringScale(BigDecimal)}.
*
* <p><b>Note:</b> Do not use this method for checking object equality. Instead, use {@link
* #isEqualTo(Object)}.
*/
@Override
public void isEquivalentAccordingToCompareTo(BigDecimal expected) {
compareValues(expected);
}
private void compareValues(BigDecimal expected) {
if (actual().compareTo(expected) != 0) {
failWithRawMessage(
"%s should have had the same value as <%s> (scale is ignored)",
actualAsString(), expected);
}
}
}
/*
* Copyright (c) 2011 David Saff
* Copyright (c) 2011 Christian Gruber
* 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.
......@@ -14,33 +13,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.junit.contrib.truth.subjects;
import org.junit.contrib.truth.FailureStrategy;
import org.junit.contrib.truth.util.GwtCompatible;
package com.google.common.truth;
import javax.annotation.Nullable;
/**
* Propositions for boolean subjects
* Propositions for boolean subjects.
*
* @author Christian Gruber (cgruber@israfil.net)
*/
@GwtCompatible
public class BooleanSubject extends Subject<BooleanSubject, Boolean> {
public BooleanSubject(FailureStrategy failureStrategy, Boolean subject) {
super(failureStrategy, subject);
public final class BooleanSubject extends Subject<BooleanSubject, Boolean> {
BooleanSubject(FailureMetadata metadata, @Nullable Boolean actual) {
super(metadata, actual);
}
/** Fails if the subject is false or {@code null}. */
public void isTrue() {
if (getSubject() == null || !getSubject()) {
fail("is true");
if (actual() == null) {
failWithRawMessage("%s was expected to be true, but was null", booleanSubject());
} else if (!actual()) {
failWithRawMessage("%s was expected to be true, but was false", booleanSubject());
}
}
/** Fails if the subject is true or {@code null}. */
public void isFalse() {
if (getSubject() == null || getSubject()) {
fail("is false");
if (actual() == null) {
failWithRawMessage("%s was expected to be false, but was null", booleanSubject());
} else if (actual()) {
failWithRawMessage("%s was expected to be false, but was true", booleanSubject());
}
}
private String booleanSubject() {
return internalCustomName() == null ? "The subject" : actualAsString();
}
}
/*
* Copyright (c) 2011 David Saff
* Copyright (c) 2011 Christian Gruber
* 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.
......@@ -14,28 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.junit.contrib.truth.extensiontest;
package com.google.common.truth;
import org.junit.contrib.truth.FailureStrategy;
import org.junit.contrib.truth.subjects.Subject;
import com.google.common.annotations.GwtIncompatible;
import javax.annotation.Nullable;
/**
* A simple example Subject to demonstrate extension.
* Propositions for {@link Class} subjects.
*
* @author Christian Gruber (christianedwardgruber@gmail.com)
* @author Kurt Alfred Kluever
*/
public class MySubject extends Subject<MySubject, MyType> {
public MySubject(FailureStrategy failureStrategy, MyType subject) {
super(failureStrategy, subject);
@GwtIncompatible("reflection")
public final class ClassSubject extends Subject<ClassSubject, Class<?>> {
ClassSubject(FailureMetadata metadata, @Nullable Class<?> o) {
super(metadata, o);
}
public And<MySubject> matches(MyType object) {
if (getSubject().value != object.value) {
fail("matches", getSubject(), object);
/**
* Fails if this class or interface is not the same as or a subclass or subinterface of, the given
* class or interface.
*/
public void isAssignableTo(Class<?> clazz) {
if (!clazz.isAssignableFrom(actual())) {
fail("is assignable to", clazz);
}
return nextChain();
}
}
/*
* Copyright (c) 2014 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 com.google.common.collect.Range;
import javax.annotation.Nullable;
/**
* Propositions for {@link Comparable} typed subjects.
*
* @author Kurt Alfred Kluever
*/
public abstract class ComparableSubject<S extends ComparableSubject<S, T>, T extends Comparable>
extends Subject<S, T> {
/**
* Constructor for use by subclasses. If you want to create an instance of this class itself, call
* {@link Subject#check}{@code .that(actual)}.
*/
protected ComparableSubject(FailureMetadata metadata, @Nullable T actual) {
super(metadata, actual);
}
/** Checks that the subject is in {@code range}. */
public final void isIn(Range<T> range) {
if (!range.contains(actual())) {
fail("is in", range);
}
}
/** Checks that the subject is <i>not</i> in {@code range}. */
public final void isNotIn(Range<T> range) {
if (range.contains(actual())) {
fail("is not in", range);
}
}
/**
* Checks that the subject is equivalent to {@code other} according to {@link
* Comparable#compareTo}, (i.e., checks that {@code a.comparesTo(b) == 0}).
*
* <p><b>Note:</b> Do not use this method for checking object equality. Instead, use {@link
* #isEqualTo(Object)}.
*/
public void isEquivalentAccordingToCompareTo(T other) {
if (actual().compareTo(other) != 0) {
failWithRawMessage(
"%s should have been equivalent to <%s> according to compareTo()",
actualAsString(), other);
}
}
/**
* Checks that the subject is equivalent to {@code other} according to {@link
* Comparable#compareTo}, (i.e., checks that {@code a.comparesTo(b) == 0}).
*
* <p><b>Note:</b> Do not use this method for checking object equality. Instead, use {@link
* #isEqualTo(Object)}.
*
* @deprecated Use {@link #isEquivalentAccordingToCompareTo} instead.
*/
@Deprecated
public void comparesEqualTo(T other) {
isEquivalentAccordingToCompareTo(other);
}
/**
* Checks that the subject is greater than {@code other}.
*
* <p>Use {@link #isAtLeast} to check that the subject is greater than <i>or equal to</i> {@code
* other}.
*/
public final void isGreaterThan(T other) {
if (actual().compareTo(other) <= 0) {
fail("is greater than", other);
}
}
/**
* Checks that the subject is less than {@code other}.
*
* <p>Use {@link #isAtMost} to check that the subject is less than <i>or equal to</i> {@code
* other}.
*/
public final void isLessThan(T other) {
if (actual().compareTo(other) >= 0) {
fail("is less than", other);
}
}
/**
* Checks that the subject is less than or equal to {@code other}.
*
* <p>Use {@link #isLessThan} to check that the subject is less than {@code other}.
*/
public final void isAtMost(T other) {
if (actual().compareTo(other) > 0) {
fail("is at most", other);
}
}
/**
* Checks that the subject is greater than or equal to {@code other}.
*
* <p>Use {@link #isGreaterThan} to check that the subject is greater than {@code other}.
*/
public final void isAtLeast(T other) {
if (actual().compareTo(other) < 0) {
fail("is at least", other);
}
}
}
/*
* Copyright (c) 2016 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.DoubleSubject.checkTolerance;
import javax.annotation.Nullable;
/**
* Determines whether an instance of type {@code A} corresponds in some way to an instance of type
* {@code E}. For example, the implementation returned by the {@link #tolerance(double)} factory
* method implements approximate equality between numeric values, with values being said to
* correspond if the difference between them is does not exceed some fixed tolerance. The instances
* of type {@code A} are typically actual values from a collection returned by the code under test;
* the instances of type {@code E} are typically expected values with which the actual values are
* compared by the test.
*
* <p>The correspondence is required to be consistent: for any given values {@code actual} and
* {@code expected}, multiple invocations of {@code compare(actual, expected)} must consistently
* return {@code true} or consistently return {@code false} (provided that neither value is
* modified). Although {@code A} and {@code E} will often be the same types, they are <i>not</i>
* required to be the same, and even if they are it is <i>not</i> required that the correspondence
* should have any of the other properties of an equivalence relation (reflexivity, symmetry, or
* transitivity).
*
* <p>Instances of this are typically used via {@link IterableSubject#comparingElementsUsing},
* {@link MapSubject#comparingValuesUsing}, or {@link MultimapSubject#comparingValuesUsing}.
*
* @author Pete Gillin
*/
public abstract class Correspondence<A, E> {
/**
* Returns a {@link Correspondence} between {@link Number} instances that considers instances to
* correspond (i.e. {@link Correspondence#compare(Object, Object)} returns {@code true}) if the
* double values of each instance (i.e. the result of calling {@link Number#doubleValue()} on
* them) are finite values within {@code tolerance} of each other.
*
* <ul>
* <li>It does not consider instances to correspond if either value is infinite or NaN.
* <li>The conversion to double may result in a loss of precision for some numeric types.
* <li>The {@link Correspondence#compare(Object, Object)} method throws a {@link
* NullPointerException} if either {@link Number} instance is null.
* </ul>
*
* @param tolerance an inclusive upper bound on the difference between the double values of the
* two {@link Number} instances, which must be a non-negative finite value, i.e. not {@link
* Double#NaN}, {@link Double#POSITIVE_INFINITY}, or negative, including {@code -0.0}
*/
public static Correspondence<Number, Number> tolerance(double tolerance) {
return new TolerantNumericEquality(tolerance);
}
private static final class TolerantNumericEquality extends Correspondence<Number, Number> {
private final double tolerance;
private TolerantNumericEquality(double tolerance) {
this.tolerance = tolerance;
}
@Override
public boolean compare(Number actual, Number expected) {
checkTolerance(tolerance);
double actualDouble = checkNotNull(actual).doubleValue();
double expectedDouble = checkNotNull(expected).doubleValue();
return MathUtil.equalWithinTolerance(actualDouble, expectedDouble, tolerance);
}
@Override
public String toString() {
return "is a finite number within " + tolerance + " of";
}
}
/**
* Returns whether or not the {@code actual} value is said to correspond to the {@code expected}
* value for the purposes of this test.
*/
public abstract boolean compare(@Nullable A actual, @Nullable E expected);
/**
* Returns a description of the correspondence, suitable to fill the gap in a failure message of
* the form {@code "<some actual element> is an element that ... <some expected element>"}. Note
* that this is a fragment of a verb phrase which takes a singular subject.
*
* <p>Example 1: For a {@code Correspondence<String, Integer>} that tests whether the actual
* string parses to the expected integer, this would return {@code "parses to"} to result in a
* failure message of the form {@code "<some actual string> is an element that parses to <some
* expected integer>"}.
*
* <p>Example 2: For the {@code Correspondence<Number, Number>} returns by {@link #tolerance} this
* returns {@code "is a finite number within " + tolerance + " of"} to result in a failure message
* of the form {@code "<some actual number> is an element that is a finite number within 0.0001 of
* <some expected number>"}.
*/
@Override
public abstract String toString();
/**
* @throws UnsupportedOperationException always
* @deprecated {@link Object#equals(Object)} is not supported. If you meant to compare objects
* using this {@link Correspondence}, use {@link #compare}.
*/
@Deprecated
@Override
public final boolean equals(@Nullable Object o) {
throw new UnsupportedOperationException(
"Correspondence.equals(object) is not supported. If you meant to compare objects, use"
+ " .compare(actual, expected) instead.");
}
/**
* @throws UnsupportedOperationException always
* @deprecated {@link Object#hashCode()} is not supported.
*/
@Deprecated
@Override
public final int hashCode() {
throw new UnsupportedOperationException("Correspondence.hashCode() is not supported.");
}
}
/*
* Copyright (c) 2016 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;
/**
* In a fluent assertion chain, exposes one or more "custom" {@code that} methods, which accept a
* value under test and return a {@link Subject}.
*
* <p>(Note that the "custom" {@code that} methods are not defined on {@code CustomSubjectBuilder}
* itself, only on its subtypes, which are the types users actually interact with.)
*
* <p>For more information about the methods in this class, see <a
* href="https://google.github.io/truth/faq#full-chain">this FAQ entry</a>.
*
* <h3>For people extending Truth</h3>
*
* <p>When you write a custom subject, see <a href="https://google.github.io/truth/extension">our doc on
* extensions</a>. It explains the cases in which {@code CustomSubjectBuilder} is necessary.
*/
public abstract class CustomSubjectBuilder {
/**
* In a fluent assertion chain, the argument to the "custom" overload of {@link
* StandardSubjectBuilder#about(CustomSubjectBuilder.Factory) about}, the method that specifies
* what kind of {@link Subject} to create.
*
* <p>For more information about the fluent chain, see <a
* href="https://google.github.io/truth/faq#full-chain">this FAQ entry</a>.
*
* <h3>For people extending Truth</h3>
*
* <p>When you write a custom subject, see <a href="https://google.github.io/truth/extension">our doc on
* extensions</a>. It explains the cases in which {@code CustomSubjectBuilder.Factory} is
* necessary.
*/
public interface Factory<CustomSubjectBuilderT extends CustomSubjectBuilder> {
/** Creates a new {@link CustomSubjectBuilder} of the appropriate type. */
CustomSubjectBuilderT createSubjectBuilder(FailureMetadata metadata);
}
private final FailureMetadata metadata;
/** Constructor for use by subclasses. */
protected CustomSubjectBuilder(FailureMetadata metadata) {
this.metadata = checkNotNull(metadata);
}
/**
* Returns the {@link FailureMetadata} instance that {@code that} methods should pass to {@link
* Subject} constructors.
*/
protected final FailureMetadata metadata() {
return metadata;
}
// TODO(user,cgruber): Better enforce that subclasses implement a that() method.
}
/*
* Copyright (c) 2011 David Saff
* Copyright (c) 2011 Christian Gruber
* 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.
......@@ -14,21 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.junit.contrib.truth.delegatetest;
package com.google.common.truth;
import static org.junit.contrib.truth.Truth.ASSERT;
import static org.junit.contrib.truth.delegatetest.FooSubject.FOO;
import org.junit.Test;
import javax.annotation.Nullable;
// TODO(cpovirk): Make this package-private, and later remove it?
public final class DefaultSubject extends Subject<DefaultSubject, Object> {
/**
* A test that's more or less intended to show how one uses an extended verb.
*
* Constructor for use by subclasses. If you want to create an instance of this class itself, call
* {@link Subject#check}{@code .that(actual)}.
*/
public class DelegationTest {
@Test public void customTypeCompares() {
ASSERT.about(FOO).that(new Foo(5)).matches(new Foo(2 + 3));
DefaultSubject(FailureMetadata metadata, @Nullable Object o) {
super(metadata, o);
}
}
/*
* Copyright (c) 2014 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.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.MathUtil.equalWithinTolerance;
import static com.google.common.truth.MathUtil.notEqualWithinTolerance;
import static java.lang.Double.doubleToLongBits;
import javax.annotation.Nullable;
/**
* Propositions for {@link Double} subjects.
*
* @author Kurt Alfred Kluever
*/
public final class DoubleSubject extends ComparableSubject<DoubleSubject, Double> {
private static final long NEG_ZERO_BITS = doubleToLongBits(-0.0);
DoubleSubject(FailureMetadata metadata, @Nullable Double actual) {
super(metadata, actual);
}
/**
* A partially specified check about an approximate relationship to a {@code double} subject using
* a tolerance.
*/
public abstract static class TolerantDoubleComparison {
// Prevent subclassing outside of this class
private TolerantDoubleComparison() {}
/**
* Fails if the subject was expected to be within the tolerance of the given value but was not
* <i>or</i> if it was expected <i>not</i> to be within the tolerance but was. The subject and
* tolerance are specified earlier in the fluent call chain.
*/
public abstract void of(double expectedDouble);
/**
* @throws UnsupportedOperationException always
* @deprecated {@link Object#equals(Object)} is not supported on TolerantDoubleComparison. If
* you meant to compare doubles, use {@link #of(double)} instead.
*/
@Deprecated
@Override
public boolean equals(@Nullable Object o) {
throw new UnsupportedOperationException(
"If you meant to compare doubles, use .of(double) instead.");
}
/**
* @throws UnsupportedOperationException always
* @deprecated {@link Object#hashCode()} is not supported on TolerantDoubleComparison
*/
@Deprecated
@Override
public int hashCode() {
throw new UnsupportedOperationException("Subject.hashCode() is not supported.");
}
}
/**
* Prepares for a check that the subject is a finite number within the given tolerance of an
* expected value that will be provided in the next call in the fluent chain.
*
* <p>The check will fail if either the subject or the object is {@link Double#POSITIVE_INFINITY},
* {@link Double#NEGATIVE_INFINITY}, or {@link Double#NaN}. To check for those values, use {@link
* #isPositiveInfinity}, {@link #isNegativeInfinity}, {@link #isNaN}, or (with more generality)
* {@link #isEqualTo}.
*
* <p>The check will pass if both values are zero, even if one is {@code 0.0} and the other is
* {@code -0.0}. Use {@code #isEqualTo} to assert that a value is exactly {@code 0.0} or that it
* is exactly {@code -0.0}.
*
* <p>You can use a tolerance of {@code 0.0} to assert the exact equality of finite doubles, but
* often {@link #isEqualTo} is preferable (note the different behaviours around non-finite values
* and {@code -0.0}). See the documentation on {@link #isEqualTo} for advice on when exact
* equality assertions are appropriate.
*
* @param tolerance an inclusive upper bound on the difference between the subject and object
* allowed by the check, which must be a non-negative finite value, i.e. not {@link
* Double#NaN}, {@link Double#POSITIVE_INFINITY}, or negative, including {@code -0.0}
*/
public TolerantDoubleComparison isWithin(final double tolerance) {
return new TolerantDoubleComparison() {
@Override
public void of(double expected) {
Double actual = actual();
checkNotNull(
actual, "actual value cannot be null. tolerance=%s expected=%s", tolerance, expected);
checkTolerance(tolerance);
if (!equalWithinTolerance(actual, expected, tolerance)) {
failWithRawMessage(
"%s and <%s> should have been finite values within <%s> of each other",
actualAsString(), expected, tolerance);
}
}
};
}
/**
* Prepares for a check that the subject is a finite number not within the given tolerance of an
* expected value that will be provided in the next call in the fluent chain.
*
* <p>The check will fail if either the subject or the object is {@link Double#POSITIVE_INFINITY},
* {@link Double#NEGATIVE_INFINITY}, or {@link Double#NaN}. See {@link #isFinite}, {@link
* #isNotNaN}, or {@link #isNotEqualTo} for checks with other behaviours.
*
* <p>The check will fail if both values are zero, even if one is {@code 0.0} and the other is
* {@code -0.0}. Use {@code #isNotEqualTo} for a test which fails for a value of exactly zero with
* one sign but passes for zero with the opposite sign.
*
* <p>You can use a tolerance of {@code 0.0} to assert the exact non-equality of finite doubles,
* but sometimes {@link #isNotEqualTo} is preferable (note the different behaviours around
* non-finite values and {@code -0.0}).
*
* @param tolerance an exclusive lower bound on the difference between the subject and object
* allowed by the check, which must be a non-negative finite value, i.e. not {@code
* Double.NaN}, {@code Double.POSITIVE_INFINITY}, or negative, including {@code -0.0}
*/
public TolerantDoubleComparison isNotWithin(final double tolerance) {
return new TolerantDoubleComparison() {
@Override
public void of(double expected) {
Double actual = actual();
checkNotNull(
actual, "actual value cannot be null. tolerance=%s expected=%s", tolerance, expected);
checkTolerance(tolerance);
if (!notEqualWithinTolerance(actual, expected, tolerance)) {
failWithRawMessage(
"%s and <%s> should have been finite values not within <%s> of each other",
actualAsString(), expected, tolerance);
}
}
};
}
/**
* Asserts that the subject is exactly equal to the given value, with equality defined as by
* {@code Double#equals}. This method is <i>not</i> recommended when the code under test is doing
* any kind of arithmetic: use {@link #isWithin} with a suitable tolerance in that case. (Remember
* that the exact result of floating point arithmetic is sensitive to apparently trivial changes
* such as replacing {@code (a + b) + c} with {@code a + (b + c)}, and that unless {@code
* strictfp} is in force even the result of {@code (a + b) + c} is sensitive to the JVM's choice
* of precision for the intermediate result.) This method is recommended when the code under test
* is specified as either copying a value without modification from its input or returning a
* well-defined literal or constant value.
*
* <p><b>Note:</b> The assertion {@code isEqualTo(0.0)} fails for an input of {@code -0.0}, and
* vice versa. For an assertion that passes for either {@code 0.0} or {@code -0.0}, use {@link
* #isZero}.
*/
public final void isEqualTo(@Nullable Double other) {
super.isEqualTo(other);
}
/**
* Asserts that the subject is not exactly equal to the given value, with equality defined as by
* {@code Double#equals}. See {@link #isEqualTo} for advice on when exact equality is recommended.
* Use {@link #isNotWithin} for an assertion with a tolerance.
*
* <p><b>Note:</b> The assertion {@code isNotEqualTo(0.0)} passes for {@code -0.0}, and vice
* versa. For an assertion that fails for either {@code 0.0} or {@code -0.0}, use {@link
* #isNonZero}.
*/
public final void isNotEqualTo(@Nullable Double other) {
super.isNotEqualTo(other);
}
/**
* @deprecated Use {@link #isWithin} or {@link #isEqualTo} instead (see documentation for advice).
*/
@Override
@Deprecated
public final void isEquivalentAccordingToCompareTo(Double other) {
super.isEquivalentAccordingToCompareTo(other);
}
/**
* Ensures that the given tolerance is a non-negative finite value, i.e. not {@code Double.NaN},
* {@code Double.POSITIVE_INFINITY}, or negative, including {@code -0.0}.
*/
static void checkTolerance(double tolerance) {
checkArgument(!Double.isNaN(tolerance), "tolerance cannot be NaN");
checkArgument(tolerance >= 0.0, "tolerance (%s) cannot be negative", tolerance);
checkArgument(
doubleToLongBits(tolerance) != NEG_ZERO_BITS,
"tolerance (%s) cannot be negative",
tolerance);
checkArgument(tolerance != Double.POSITIVE_INFINITY, "tolerance cannot be POSITIVE_INFINITY");
}
/** Asserts that the subject is zero (i.e. it is either {@code 0.0} or {@code -0.0}). */
public final void isZero() {
if (actual() == null || actual().doubleValue() != 0.0) {
fail("is zero");
}
}
/**
* Asserts that the subject is a non-null value other than zero (i.e. it is not {@code 0.0},
* {@code -0.0} or {@code null}).
*/
public final void isNonZero() {
if (actual() == null || actual().doubleValue() == 0.0) {
fail("is non-zero");
}
}
/** Asserts that the subject is {@link Double#POSITIVE_INFINITY}. */
public final void isPositiveInfinity() {
isEqualTo(Double.POSITIVE_INFINITY);
}
/** Asserts that the subject is {@link Double#NEGATIVE_INFINITY}. */
public final void isNegativeInfinity() {
isEqualTo(Double.NEGATIVE_INFINITY);
}
/** Asserts that the subject is {@link Double#NaN}. */
public final void isNaN() {
if (actual() == null || !actual().isNaN()) {
fail("is NaN");
}
}
/**
* Asserts that the subject is finite, i.e. not {@link Double#POSITIVE_INFINITY}, {@link
* Double#NEGATIVE_INFINITY}, or {@link Double#NaN}.
*/
public final void isFinite() {
if (actual() == null || actual().isNaN() || actual().isInfinite()) {
failWithRawMessage("%s should have been finite", actualAsString());
}
}
/**
* Asserts that the subject is a non-null value other than {@link Double#NaN} (but it may be
* {@link Double#POSITIVE_INFINITY} or {@link Double#NEGATIVE_INFINITY}).
*/
public final void isNotNaN() {
if (actual() == null || actual().isNaN()) {
failWithRawMessage("%s should not have been NaN", actualAsString());
}
}
}
/*
* 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.base.Preconditions.checkState;
import static com.google.common.base.Throwables.getStackTraceAsString;
import static com.google.common.truth.Expect.TestPhase.AFTER;
import static com.google.common.truth.Expect.TestPhase.BEFORE;
import static com.google.common.truth.Expect.TestPhase.DURING;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.truth.Truth.AssertionErrorWithCause;
import com.google.errorprone.annotations.concurrent.GuardedBy;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import org.junit.internal.AssumptionViolatedException;
import org.junit.rules.ErrorCollector;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
/**
* A {@link TestRule} that batches up all failures encountered during a test, and reports them all
* together at the end (similar to {@link ErrorCollector}). It is also useful for making assertions
* from other threads or from within callbacks whose exceptions would be swallowed or logged, rather
* than propagated out to fail the test.
*
* <p>Usage:
*
* <pre>
* {@code @Rule public final Expect expect = Expect.create();}
*
* {@code ...}
*
* {@code expect.that(results).containsExactly(...);}
* {@code expect.that(errors).isEmpty();}
* </pre>
*
* If both of the assertions above fail, the test will fail with an exception that contains
* information about both.
*
* <p>{@code Expect} may be used concurrently from multiple threads. Note, however, that {@code
* Expect} has no way of knowing when all your other test threads are done. It simply checks for
* failures when the main thread finishes executing the test method. Thus, you must ensure that any
* background threads complete their assertions before then, or your test may ignore their results.
*
* <p>To record failures for the purpose of testing that an assertion fails when it should, see
* {@link ExpectFailure}.
*/
@GwtIncompatible("JUnit4")
public final class Expect extends StandardSubjectBuilder implements TestRule {
private static final class ExpectationGatherer implements FailureStrategy {
@GuardedBy("this")
private final List<AssertionError> failures = new ArrayList<AssertionError>();
@GuardedBy("this")
private TestPhase inRuleContext = BEFORE;
private final boolean showStackTrace;
ExpectationGatherer(boolean showStackTrace) {
this.showStackTrace = showStackTrace;
}
@Override
public synchronized void fail(AssertionError failure) {
record(failure);
}
synchronized void enterRuleContext() {
checkState(inRuleContext == BEFORE);
inRuleContext = DURING;
}
synchronized void leaveRuleContext(@Nullable Throwable caught) throws Throwable {
try {
if (caught == null) {
doLeaveRuleContext();
} else {
doLeaveRuleContext(caught);
}
/*
* We'd like to check this even if an exception was thrown, but we don't want to override
* the "real" failure. TODO(cpovirk): Maybe attach as a suppressed exception once we require
* a newer version of Android.
*/
checkState(inRuleContext == DURING);
} finally {
inRuleContext = AFTER;
}
}
synchronized void checkInRuleContext() {
doCheckInRuleContext(null);
}
synchronized boolean hasFailures() {
return !failures.isEmpty();
}
@Override
public synchronized String toString() {
int numFailures = failures.size();
StringBuilder message =
new StringBuilder(
numFailures + (numFailures > 1 ? " expectations" : " expectation") + " failed:\n");
int count = 0;
for (AssertionError failure : failures) {
count++;
message.append(" ");
message.append(count);
message.append(". ");
message.append(showStackTrace ? getStackTraceAsString(failure) : failure.getMessage());
message.append("\n");
}
return message.toString();
}
@GuardedBy("this")
private void doCheckInRuleContext(@Nullable AssertionError failure) {
switch (inRuleContext) {
case BEFORE:
throw new IllegalStateException(
"assertion made on Expect instance, but it's not enabled as a @Rule.", failure);
case DURING:
return;
case AFTER:
throw new IllegalStateException(
"assertion made on Expect instance, but its @Rule has already completed. Maybe "
+ "you're making assertions from a background thread and not waiting for them to "
+ "complete, or maybe you've shared an Expect instance across multiple tests? "
+ "We're throwing this exception to warn you that your assertion would have been "
+ "ignored. However, this exception might not cause any test to fail, or it "
+ "might cause some subsequent test to fail rather than the test that caused the "
+ "problem.",
failure);
}
throw new AssertionError();
}
@GuardedBy("this")
private void doLeaveRuleContext() {
if (hasFailures()) {
throw new AssertionError(this);
}
}
@GuardedBy("this")
private void doLeaveRuleContext(Throwable caught) throws Throwable {
if (hasFailures()) {
String message =
caught instanceof AssumptionViolatedException
? "Failures occurred before an assumption was violated"
: "Failures occurred before an exception was thrown while the test was running";
record(new AssertionErrorWithCause(message + ": " + caught, caught));
throw new AssertionError(this);
} else {
throw caught;
}
}
@GuardedBy("this")
private void record(AssertionError failure) {
doCheckInRuleContext(failure);
failures.add(failure);
}
}
private final ExpectationGatherer gatherer;
public static Expect create() {
return new Expect(new ExpectationGatherer(false /* showStackTrace */));
}
public static Expect createAndEnableStackTrace() {
return new Expect(new ExpectationGatherer(true /* showStackTrace */));
}
private Expect(ExpectationGatherer gatherer) {
super(FailureMetadata.forFailureStrategy(gatherer));
this.gatherer = checkNotNull(gatherer);
}
public boolean hasFailures() {
return gatherer.hasFailures();
}
@Override
void checkStatePreconditions() {
gatherer.checkInRuleContext();
}
@Override
public Statement apply(final Statement base, Description description) {
checkNotNull(base);
checkNotNull(description);
return new Statement() {
@Override
public void evaluate() throws Throwable {
gatherer.enterRuleContext();
Throwable caught = null;
try {
base.evaluate();
} catch (Throwable t) {
caught = t;
} finally {
gatherer.leaveRuleContext(caught);
}
}
};
}
enum TestPhase {
BEFORE,
DURING,
AFTER;
}
}
/*
* Copyright (c) 2017 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.base.Preconditions.checkState;
import static com.google.common.truth.StringUtil.format;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.truth.Truth.AssertionErrorWithCause;
import javax.annotation.Nullable;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
/**
* A utility for testing that assertions against a custom {@link Subject} fail when they should.
*
* <p>Usage:
*
* <pre>{@code
* AssertionError failure =
* expectFailure(whenTesting -> whenTesting.that(cancelButton).isVisible());
* assertThat(failure).hasMessageThat().contains("visible");
*
* ...
*
* private static AssertionError expectFailure(
* ExpectFailure.SimpleSubjectBuilderCallback<UiElementSubject, UiElement> assertionCallback) {
* return ExpectFailure.expectFailureAbout(uiElements(), assertionCallback);
* }
* }</pre>
*
* Or, if you can't use lambdas:
*
* <pre>
* {@code @Rule public final ExpectFailure expectFailure = new ExpectFailure();}
*
* {@code ...
*
* expectFailure.whenTesting().about(uiElements()).that(cancelButton).isVisible();
* assertThat(expectFailure.getFailure()).hasMessageThat().contains("visible");
* }</pre>
*
* <p>{@code ExpectFailure} is similar to JUnit 5's <a
* href="http://junit.org/junit5/docs/current/api/org/junit/jupiter/api/Assertions.html#assertThrows-java.lang.Class-org.junit.jupiter.api.function.Executable-">{@code
* assertThrows}</a>. We recommend it over {@code assertThrows} when you're testing a Truth subject
* because it also checks that the assertion you're testing correctly calls {@link
* FailureStrategy#fail} only once.
*/
public final class ExpectFailure implements Platform.JUnitTestRule {
private final FailureStrategy strategy =
new FailureStrategy() {
@Override
public void fail(AssertionError failure) {
captureFailure(failure);
}
};
private boolean inRuleContext = false;
private boolean failureExpected = false;
private @Nullable AssertionError failure = null;
/**
* Creates a new instance for use as a {@code @Rule}. See the class documentation for details, and
* consider using {@linkplain #expectFailure the lambda version} instead.
*/
public ExpectFailure() {}
/**
* Returns a test verb that expects the chained assertion to fail, and makes the failure available
* via {@link #getFailure}.
*
* <p>An instance of {@code ExpectFailure} supports only one {@code whenTesting} call per test
* method. The static {@link #expectFailure} method, by contrast, does not have this limitation.
*/
public StandardSubjectBuilder whenTesting() {
checkState(inRuleContext, "ExpectFailure must be used as a JUnit @Rule");
if (failure != null) {
throw new AssertionErrorWithCause("ExpectFailure already captured a failure", failure);
}
if (failureExpected) {
throw new AssertionError(
"ExpectFailure.whenTesting() called previously, but did not capture a failure.");
}
failureExpected = true;
return StandardSubjectBuilder.forCustomFailureStrategy(strategy);
}
/**
* Enters rule context to be ready to capture failures.
*
* <p>This should be rarely used directly, except if this class is as a long living object but not
* as a JUnit rule, like truth subject tests where for GWT compatible reasons.
*/
void enterRuleContext() {
this.inRuleContext = true;
}
/** Leaves rule context and verify if a failure has been caught if it's expected. */
void leaveRuleContext() {
this.inRuleContext = false;
}
/**
* Ensures a failure is caught if it's expected (i.e., {@link #whenTesting} is called) and throws
* error if not.
*/
void ensureFailureCaught() {
if (failureExpected && failure == null) {
throw new AssertionError("ExpectFailure.whenTesting() invoked, but no failure was caught.");
}
}
/** Returns the captured failure, if one occurred. */
public AssertionError getFailure() {
if (failure == null) {
throw new AssertionError("ExpectFailure did not capture a failure.");
}
return failure;
}
/**
* Captures the provided failure, or throws an {@link AssertionError} if a failure had previously
* been captured.
*/
private void captureFailure(AssertionError captured) {
if (failure != null) {
// TODO(diamondm) is it worthwhile to add the failures as suppressed exceptions?
throw new AssertionError(
format(
"ExpectFailure.whenTesting() caught multiple failures:\n\n%s\n\n%s\n",
Platform.getStackTraceAsString(failure), Platform.getStackTraceAsString(captured)));
}
failure = captured;
}
/**
* Static alternative that directly returns the triggered failure. This is intended to be used in
* Java 8 tests similar to {@code expectThrows()}:
*
* <p>{@code AssertionError failure = expectFailure(whenTesting ->
* whenTesting.that(4).isNotEqualTo(4));}
*/
public static AssertionError expectFailure(StandardSubjectBuilderCallback assertionCallback) {
ExpectFailure expectFailure = new ExpectFailure();
expectFailure.enterRuleContext(); // safe since this instance doesn't leave this method
assertionCallback.invokeAssertion(expectFailure.whenTesting());
return expectFailure.getFailure();
}
/**
* Static alternative that directly returns the triggered failure. This is intended to be used in
* Java 8 tests similar to {@code expectThrows()}:
*
* <p>{@code AssertionError failure = expectFailureAbout(myTypes(), whenTesting ->
* whenTesting.that(myType).hasProperty());}
*/
public static <S extends Subject<S, A>, A> AssertionError expectFailureAbout(
final Subject.Factory<S, A> factory,
final SimpleSubjectBuilderCallback<S, A> assertionCallback) {
// whenTesting -> assertionCallback.invokeAssertion(whenTesting.about(factory))
return expectFailure(
new StandardSubjectBuilderCallback() {
@Override
public void invokeAssertion(StandardSubjectBuilder whenTesting) {
assertionCallback.invokeAssertion(whenTesting.about(factory));
}
});
}
@Override
@GwtIncompatible("org.junit.rules.TestRule")
public Statement apply(final Statement base, Description description) {
checkNotNull(base);
checkNotNull(description);
return new Statement() {
@Override
public void evaluate() throws Throwable {
enterRuleContext();
try {
base.evaluate();
} finally {
leaveRuleContext();
}
ensureFailureCaught();
}
};
}
/**
* A "functional interface" for {@link #expectFailure expectFailure()} to invoke and capture
* failures.
*
* <p>Java 8 users should pass a lambda to {@code .expectFailure()} rather than directly implement
* this interface. Java 7 users can define an {@code @Rule ExpectFailure} instance instead,
* however if you prefer the {@code .expectFailure()} pattern you can use this interface to pass
* in an anonymous class.
*/
public interface StandardSubjectBuilderCallback {
void invokeAssertion(StandardSubjectBuilder whenTesting);
}
/**
* A "functional interface" for {@link #expectFailureAbout expectFailureAbout()} to invoke and
* capture failures.
*
* <p>Java 8 users should pass a lambda to {@code .expectFailureAbout()} rather than directly
* implement this interface. Java 7 users can define an {@code @Rule ExpectFailure} instance
* instead, however if you prefer the {@code .expectFailureAbout()} pattern you can use this
* interface to pass in an anonymous class.
*/
public interface SimpleSubjectBuilderCallback<S extends Subject<S, A>, A> {
void invokeAssertion(SimpleSubjectBuilder<S, A> whenTesting);
}
}