package org.mutabilitydetector.unittesting;
/*
* #%L
* MutabilityDetector
* %%
* Copyright (C) 2008 - 2014 Graham Allan
* %%
* 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.
* #L%
*/
import static org.mutabilitydetector.Configurations.OUT_OF_THE_BOX_CONFIGURATION;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.hamcrest.Matcher;
import org.mutabilitydetector.AnalysisResult;
import org.mutabilitydetector.Configuration;
import org.mutabilitydetector.ConfigurationBuilder;
import org.mutabilitydetector.Configurations;
import org.mutabilitydetector.IsImmutable;
import org.mutabilitydetector.MutabilityReason;
import org.mutabilitydetector.MutableReasonDetail;
/**
*
* <h1>Mutability Detector</h1>
* <p>
* <i>Mutability Detector allows you to write a unit test that checks your
* classes are immutable.</i>
* </p>
* <h1>Help Guide</h2>
* <h2>Contents</h2>
* <ol>
* <li>Preamble
* <ul>
* <li><a href="#AboutHelpGuide">About this help guide</a></li>
* <li><a href="#AboutExamples">About these examples</a></li>
* </ul>
* </li>
* <li><a href="#FirstTestCase">Your first test case.</a></li>
* <li><a href="#ConfiguringTheAssertion">A more specific assertion</a></li>
* <li><a href="#AllowingAReason">Allowing a reason for mutability</a></li>
* <ul>
* <li><a href="#OutOfTheBox">Out-of-the-box Allowed Reasons</a>
* <ul>
* <li><a href="#AllowingAbstractImmutable">Abstract immutable
* implementations</a></li>
* <li><a href="#AllowingAbstractImmutableFields">Assigning abstract immutable
* fields</a></li>
* <li><a href="#AllowingGenericTypeFields">Assigning generic fields</a></li>
* <li><a href="#AllowingNonFinalFields">Non-final fields</a></li>
* <li><a href="#FieldAssumptions_UnmodifiableCopy">Safely copying into
* collection field</a></li>
* <li><a href="#FieldAssumptions_NotModfied">Mutable field never modified</a></li>
* <li><a href="#FieldAssumptions_Caching">Caching values internally</a></li>
* </ul>
* </li>
* <li><a href="#WritingAnAllowedReason">Writing your own allowed reason</a></li>
* </ul>
* </li>
* <li><a href="#HardcodingResults">Hardcoding analysis results</a>
* <ul>
* <li><a href="#WhyHardcodeResults">Why hardcode results?</a></li>
* <li><a href="#DifferentAsserter">Creating your own asserter</a></li>
* <li><a href="#AddingHarcodedResults">Adding hardcoded results</a></li>
* <li><a href="#TestHardcodedDirectly">Testing class with hardcoded result</a></li>
* </ul>
* </li>
* </ol>
*
*
*
*
* <h4 id="AboutHelpGuide">About this help guide</h4>
* <p>
* The help contents here are also available on the <a href=
* "http://mutabilitydetector.github.com/MutabilityDetector/mvn-site/apidocs/org/mutabilitydetector/unittesting/MutabilityAssert.html"
* >project's JavaDoc</a>.
* </p>
* This style of documentation is used as it provides content suitable for a web
* page and for offline use in the JavaDoc viewer of your favourite IDE. It has
* been <strike>shamelessly stolen from</strike> inspired by the Mockito
* project, thanks guys.
*
* <h4 id="AboutExamples">About these examples</h4> I am assuming JUnit as the
* unit testing library. However, Mutability Detector should work with any unit
* testing library that uses the exception mechanism for their assertions, such
* as TestNG. If Mutability Detector is incompatible with your favourite testing
* library, please get in touch, and we'll see what we can do about that.
*
* <h3 id="FirstTestCase">Your first test case.</h3>
*
* The most simple assertion you can make will look something like this:
*
* <pre>
* <code>
* import static org.mutabilitydetector.unittesting.MutabilityAssert.assertImmutable;
*
* @Test public void checkMyClassIsImmutable() {
* assertImmutable(MyClass.class);
* }
* </code>
* </pre>
*
* <p>
* This assertion will trigger an analysis of <code>MyClass</code>, passing if
* found to be immutable, failing if found to be mutable.
* </p>
*
*
* <h3 id="ConfiguringTheAssertion">Configuring the assertion</h3>
* <p>
* The method used above is a shortcut for more expressive forms of the
* assertion, and does not allow any further configuration. An equivalent
* assertion is:
* </p>
*
* <pre>
* <code>
* import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
* import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;
*
* @Test public void checkMyClassIsImmutable() {
* assertInstancesOf(MyClass.class, areImmutable());
* }</code>
* </pre>
*
*
* This is the form that can be used for extra configuration of the assertion.
* Let's take a look at an assertion that is configured differently. Consider a
* class which is immutable, except for fields not being declared
* <code>final</code>. According to <a href="http://jcip.net/">Java Concurrency
* In Practice</a>, instances of classes like this, as long as they are
* <i>safely published</i> are still considered <i>effectively immutable</i>.
* Please note however, Mutability Detector does not check that objects are
* safely published. <br />
* To represent this in a unit test, the assertion would like this:
*
* <pre>
* <code>
* import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
* import static org.mutabilitydetector.unittesting.MutabilityMatchers.areEffectivelyImmutable;
* import static org.mutabilitydetector.unittesting.AllowedReason.allowingNonFinalFields;
*
* @Test public void checkMyClassIsImmutable() {
* assertInstancesOf(MyClassWhereTheFieldsAreNotFinal.class,
* areEffectivelyImmutable(),
* allowingNonFinalFields());
* }</code>
* </pre>
*
* See also:
* <ul>
* <li>{@link IsImmutable#EFFECTIVELY_IMMUTABLE}</li>
* </ul>
*
* <p>
* The second parameter to the method
* {@link MutabilityAssert#assertInstancesOf(Class, Matcher)} is a
* <code>Matcher<AnalysisResult></code>, where <code>Matcher</code> is a
* <a href="http://code.google.com/p/hamcrest/">hamcrest matcher</a>, and
* {@link AnalysisResult} is provided by Mutability Detector to represent the
* result of the static analysis performed on the given class. This means, if
* none of the out-of-the-box matchers are quite right for your scenario, you
* can supply your own. Your implementation of {@link Matcher#matches(Object)}
* should return true for a test pass, false for a test failure.
* </p>
*
*
* <h3 id="AllowingAReason">Allowing a reason</h3>
* There can also be cases where your class is found to be mutable, but you know
* for your scenario that it's an acceptable reason. Consider the following
* class:
*
* <pre>
* <code>public abstract class AbstractIntHolder {
* private final int intField;
*
* public AbstractIntHolder(int intToStore) {
* this.intField = intToStore;
* }
* }</code>
* </pre>
*
* <p>
* In this case, if you assert <code>AbstractIntHolder</code> is immutable, the
* test will fail. This is because AbstractIntHolder can be subclassed, which
* means clients of this class, who for example, accept parameters of this type
* and store them to fields, cannot depend on receiving a concrete, immutable
* object. If, in your code, you know that all subclasses will also be immutable
* (hopefully you have tests for them too) then you can say that it is okay that
* this class can be subclassed, because you know all subclasses are immutable
* as well.
* </p>
* <p>
* Given such a scenario, the way to get your test to pass, and still provide a
* check that the class doesn't become mutable by some other cause, is to
* <i>allow a reason</i> for mutability. An example of allowing said reason for
* <code>AbstractIntHolder</code> could look like this:
* </p>
*
* <pre>
* <code>
* import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
* import static org.mutabilitydetector.unittesting.MutabilityMatchers.areEffectivelyImmutable;
* import static org.mutabilitydetector.unittesting.AllowedReason.allowingForSubclassing;
*
* @Test public void checkMyClassIsImmutable() {
* assertInstancesOf(AbstractIntHolder.class, areImmutable(), allowingForSubclassing());
* }
* </code>
* </pre>
*
* This will allow your test to pass, but fail for any other reasons that are
* introduced, e.g. if someone adds a setter method.
*
* <p>
* Similar to the <code>Matcher<AnalysisResult></code> parameter, the
* allowed reason parameter of
* {@link #assertInstancesOf(Class, Matcher, Matcher)} is a
* <code>Matcher<{@link MutableReasonDetail}></code>. Mutability Detector
* will provide only a few out-of-the-box implementations for this, which are
* unlikely to cover each scenario where you want to permit a certain aspect of
* mutability.
* </p>
* <h3 id="OutOfTheBox">Out-of-the-box allowed reasons</h3>
* <p>
* <h4 id="AllowingAbstractImmutable">Abstract class with immutable
* implementation</h4>
* It can be useful to write an abstract class, designed for extension, which is
* immutable. To ensure that a concrete class B, extending abstract class A is
* immutable, it is necessary to test that both <code>A</code> and
* <code>B</code> are immutable. However, if you write the assertion
* <code>assertImmutable(A.class);</code>, it will fail, as it can be subclassed
* (see {@link MutabilityReason#CAN_BE_SUBCLASSED}). To specifically allow this,
* use the allowed reason: {@link AllowedReason#allowingForSubclassing()} <br>
* <br>
* For example:<br>
* <code>assertInstancesOf(A.class, areImmutable(), allowingForSubclassing());</code>
*
* <h4 id="AllowingAbstractImmutableFields">Depending on other classes being
* immutable</h4>
* Consider the following code: <code>
* <pre>
* public final class MyImmutable {
* public final ShouldAlsoBeImmutable field;
*
* public MyImmutable(ShouldAlsoBeImmutable dependsOnThisBeingImmutable) {
* this.field = dependsOnThisBeingImmutable;
* }
* }
* </pre>
* <code>
*
* If <code>ShouldAlsoBeImmutable</code> is not a concrete class (an
* <code>interface</code> or <code>abstract</code> class),
* <code>assertImmutable(MyImmutable.class);</code> will fail, as there's no
* guarantee that the runtime implementation of <code>ShouldBeImmutable</code>
* is actually immutable. A common example is taking a parameter of
* <code>java.util.List</code>, where you require that it is an immutable
* implementation, e.g: a copy created with
* {@link Collections#unmodifiableList(List)}. For this scenario, use
* {@link AllowedReason#provided(Class)}.
*
* To make the above example pass, use an allowed reason like so:<br>
* <br>
*
* <pre>
* <code>
* assertInstancesOf(MyImmutable.class,
* areImmutable(),
* AllowedReason.provided(ShouldAlsoBeImmutable.class).isAlsoImmutable());
* </code>
* </pre>
*
*
* <h4 id="AllowingGenericTypeFields"></h4>
* In some cases, classes use a generic type, and treat that type as immutable.
* Since the runtime type of a generic class cannot be known by the class
* at compile time, MutabilityDetector cannot say for sure that the type will
* be immutable and raises it as an error.
* <br>
* Consider the following class:
*
* <pre>
* <code>
* public final HasGenericField<T> {
* public final T genericThing;
*
* public HasGenericField(T genericThing) {
* this.genericThing = genericThing;
* }
* }
* </code>
* </pre>
* In this case <<code>T<code>> may or may not be an immutable type at runtime,
* it depends on what generic type is used to construct the instance. To prevent
* classes like this being declared as immutable, use an assertion like this:
*
* <pre>
* <code>
* assertInstancesOf(HasGenericField.class,
* areImmutable(),
* AllowedReason.provided("T").isAlsoImmutable());
* </code>
* </pre>
*
* Mutability Detector does not currently behave any differently for bounded type
* parameters such as <code>T extends Foo</code> or <code>T super Foo & Bar</code>.
* Only the name of the generic type is considered, and must match exactly the
* name of the type parameter used by the class.
*
* </p>
*
* <h4 id="AllowingNonFinalFields">Non-final fields</h3> If you have fields
* which are neither mutated nor reassigned, you can suppress warnings about
* them not being declared as final. Since the non-final field warning relates
* to visibility in the Java Memory Model, and there are other ways to guarantee
* visibility (e.g. assigning before a volatile write) it may be desirable.
* Consider the following class:
*
* <pre>
* <code>
* public final class NonFinalField {
* private String myField;
*
* public NonFinalField(String myField) {
* this.myField = myField;
* }
*
* public String getMyField() {
* return myField;
* }
* }
* </code>
* </pre>
*
* This can be made to pass by allowing non-final fields, like so:
*
* <pre>
* <code>
* assertInstancesOf(NonFinalField.class,
* areImmutable(),
* AllowedReason.allowingNonFinalFields());
* </code>
* </pre>
*
* <h4 id="FieldAssumptions_UnmodifiableCopy">Safely copying into collection
* field</h4>
* Fields of collection types are normally interfaces (e.g. List, Set,
* Iterable), and assigning these types to a field will result in a warning.
* Mutability Detector has support for recognising the pattern of copying and
* wrapping in an unmodifiable collection, however, it is limited to types and
* methods from the standard JDK. Consider the following class:
*
* <pre>
* <code>
* import java.util.List;
*
* public final class HasCollectionField {
* private final List<String> myStrings;
*
* public HasCollectionField(List<String> strings) {
* List<String> copy = copyIntoNewList(strings);
* List<String> unmodifiable = wrapWithUnmodifiable(strings);
* this.myStrings = unmodifiable;
* }
* }
* </code>
* </pre>
*
* In this case we safely copy the list (<code>copyIntoNewList</code>) and the
* copy is then wrapped in an unmodifiable list that will prevent mutation (
* <code>wrapWithUnmodifiable</code>). However, since Mutability Detector is
* unaware of these two methods, it will conclude that a mutable
* <code>List</code> type has been assigned to the private field.
* <p>
* This can be made to pass with the following:
*
* <pre>
* <code>
* assertInstancesOf(HasCollectionField.class,
* areImmutable(),
* AllowedReason.assumingFields("myStrings").areSafelyCopiedUnmodifiableCollectionsWithImmutableElements());
* </code>
* </pre>
*
* This also assumes that the collection contains only immutable elements, and
* will suppress warnings generated when, for example, the field is a
* {@link List} of mutable {@link Date}s.
*
* <h4 id="FieldAssumptions_NotModfied">Mutable field never modified</h4>
* While it is absolutely possible to build an immutable object with mutable
* fields, Mutability Detector errs on the side of caution. Thus, your class
* could have a field of a mutable type, which neither escapes, nor is mutated
* by the owning class, but still fails a test for immutability.
*
* Consider the following class:
*
* <pre>
* <code>
* import java.util.Date;
*
* public final class HasDateField {
* private final Date myDate;
*
* public HasDateField(Date date) {
* this.myDate = new Date(date.getTime());
* }
*
* public Date getDate() {
* return new Date(myDate.getTime());
* }
* }
* </pre>
*
* </code>
*
* A test for this class fails because the field <code>myDate</code> is a
* mutable type. This can be made to pass with the following:
*
* <pre>
* <code>
* assertInstancesOf(HasDateField.class,
* areImmutable(),
* AllowedReason.assumingFields("myDate").areNotModifiedAndDoNotEscape());
* </code>
* </pre>
*
* <h4 id="FieldAssumptions_Caching">Caching values internally</h4>
* As with {@link String}, it is possible to reassign fields or mutate internal
* state and still be immutable. As long as callers cannot observe the change
* the class can be deemed immutable.
*
* Consider the following class:
*
* <pre>
* <code>
* public final class MutatesAsInternalCaching {
* private final String myString;
* private final String otherString;
* private int lengthWhenConcatenated;
* public MutatesAsInternalCaching(String myString, String otherString) {
* this.myString = myString;
* this.otherString = otherString;
* }
*
* public int getConcatenatedLength() {
* if (lengthWhenConcatenated == 0) {
* lengthWhenConcatenated = myString.concat(otherString).length();
* }
* return lengthWhenConcatenated;
* }
* }
* </code>
* </pre>
*
* Here, the field <code>lengthWhenConcatenated</code> is computed lazily. While
* there is a field reassignment, which is a mutation, callers will never
* perceive the mutation, as the calculation is done on the first request. Even
* in a multithreaded environment, this is safe, and will result in no
* observable mutation. Since the result is computed from other immutable
* values, if multiple threads hit the race condition of seeing an empty value
* while another thread is computing the result, the field will always be set to
* the same value. Multiple assignments will appear as exactly one assignment,
* just as with a final field.
* <p>
* This is called a 'benign data race', and exists in {@link String}, with its
* {@link #hashCode()} method.
* <p>
* <b>WARNING: This technique should be used with care, as it is very easy to get
* wrong.</b>
* <p>
* To allow this in tests, use an assertion like the following:
*
* <pre>
* <code>
* assertInstancesOf(MutatesAsInternalCaching.class,
* areImmutable(),
* AllowedReason.assumingFields("lengthWhenConcatenated").areModifiedAsPartOfAnUnobservableCachingStrategy());
* </code>
* </pre>
*
* This will also allow the use of mutable types and collections, not just
* reassignments of primitive fields. Thus populating an array or collection for
* future caching should also be allowed with this matcher.
*
* <h3 id="WritingAnAllowedReason">Writing your own allowed reasons</h3>
* <p>
* If none of the out-of-the-box allowed reasons suit your needs, it is possible
* to supply your own implementation. The allowed reason in the signature of
* {@link MutabilityAssert#assertInstancesOf(Class, Matcher, Matcher)} is a
* Hamcrest <code>Matcher<{@link MutableReasonDetail}></code>. For a
* mutable class to pass the test, each {@link MutableReasonDetail} of the
* {@link AnalysisResult} (provided by Mutability Detector) must be matched by
* at least one allowed reason.
* </p>
*
*
* <h3 id="HardcodingResults">Configuring MutabilityAssert to use Hardcoded
* Results</h3>
*
* As of version 0.9, Mutability Detector uses a predefined list of hardcoded
* results, in order to improve the accuracy of the analysis. For example, prior
* to 0.9, java.lang.String was <a
* href="https://github.com/MutabilityDetector/MutabilityDetector/issues/4"
* >considered to be mutable</a>. The out of the box hardcoded results includes
* a non-exhaustive list of immutable classes from the standard JDK.
*
* See also:
* <ul>
* <li>{@link Configurations#JDK_CONFIGURATION}</li>
* </ul>
*
* <p>
*
* If you have found that Mutability Detector is unable to correctly analyse one
* of your classes, or a class in a library you use, you may wish to add your
* class to the list of predefined results. Follow these steps to choose your
* own predefined list.
*
* <h4 id="WhyHardcodeResults">Why Would You Want To Hardcode Results?</h4>
*
* Imagine a couple of classes like this:
* <p>
*
* <pre>
* <code>
* @Immutable
* public final class ActuallyImmutable {
* // is immutable, but like java.lang.String, is incorrectly
* // called mutable.
* }
*
* @Immutable
* public final class UsesActuallyImmutable {
* public final ActuallyImmutable myImmutableField = ...;
* }
*
* // in a test case
* MutabilityAssert.assertImmutable(UsesActuallyImmutable.class); // this test fails
* </code>
* </pre>
*
* Because there's an error in the analysis of <code>ActuallyImmutable</code>,
* this "taints" <code>UsesActuallyImmutable</code>, which will also be
* considered mutable. Because of the transitive nature of a false positive,
* this can cause Mutability Detector to think that entire object graphs are
* mutable when they're not. Hardcoding your own results is a way to overcome
* incorrect analysis.
*
* <h4 id="DifferentAsserter">Using A Different Asserter</h4>
*
* To be able to hardcode results, you need your own instance of
* {@link MutabilityAsserter}. Normally assertions are made using the class
* {@link MutabilityAssert}. To choose different options from this class, you
* must create and make available your own asserter with its own configuration.
* Do this by constructing an instance of MutabilityAssert, like so:
*
* <pre>
* <code>
* public class SomeClassAccessibleByMyTests {
* public static final MutabilityAsserter MUTABILITY = MutabilityAsserter.configured(...);
* } </code>
* </pre>
*
* This allows your test case to have an assertion like:
*
* <pre>
* <code>// in a test case
* MUTABILITY.assertImmutable(MyClass.class);
* </pre>
*
* </code>
*
* <h4 id="AddingHarcodedResults">Hardcoding Analysis Results</h4>
*
* Notice in the above example, the parameters given to the
* <code>MutabilityAssert.configured()</code> method are not shown. The
* parameter, of type {@link Configuration}, is what will contain your hardcoded
* results. In the following example, To overcome this, instantiate
* MutabilityAsserter like this:
*
* <pre>
* <code>
* // as a field
* MutabilityAsserter MUTABILITY = MutabilityAsserter.configured(new ConfigurationBuilder() {
* @Override public void configure() {
* hardcodeAsDefinitelyImmutable(ActuallyImmutable.class);
* }
* });
*
* // in a test case
* MUTABILITY.assertImmutable(UsesActuallyImmutable.class); // this now passes
* </code>
* </pre>
*
* Now classes which transitively depend on <code>ActuallyImmutable</code> being
* correctly analysed will not result in false positive results.
*
* <h4 id="TestHardcodedDirectly">Testing Hardcoded Classes Directly</h4>
*
* Using the configuration from above, if we have the assertion:
*
* <pre>
* <code>MUTABILITY.assertImmutable(ActuallyImmutable.class);</code>
* </pre>
*
* The test case will fail. Even though it's hardcoded, if you test it directly,
* the result will reflect the real analysis. This is a deliberate choice, to
* alert you to the possibility that your choice to hardcode a result may no
* longer be valid. In this case you will want to write a an assertion which
* allows the specific reasons for failure. You can still use the same asserter
* you previously created for this, e.g.:
*
* <pre>
* <code>
* MUTABILITY.assertInstancesOf(ActuallyImmutable.class,
* areImmutable(),
* // configure your "allowed reasons" here
* );</code>
* </pre>
*
*
*
* @author Graham Allan / Grundlefleck at gmail dot com
*
* @see MutabilityMatchers
* @see AllowedReason
* @see AnalysisResult
* @see MutableReasonDetail
* @see IsImmutable
* @see Configuration
* @see Configurations#OUT_OF_THE_BOX_CONFIGURATION
* @see ConfigurationBuilder
* @see MutabilityReason
*/
public final class MutabilityAssert {
private MutabilityAssert() { }
private final static MutabilityAsserter defaultAsserter = MutabilityAsserter.configured(OUT_OF_THE_BOX_CONFIGURATION);
/**
* Checks that the given class is immutable, or fails with an {@link AssertionError}.
* <p>
* Example:
* <pre><code>
* MutabilityAssert.assertImmutable(HopefullyImmutable.class);
* </code></pre>
*
* @see IsImmutable#IMMUTABLE
* @param expectedImmutableClass
*/
public static void assertImmutable(Class<?> expectedImmutableClass) {
defaultAsserter.assertImmutable(expectedImmutableClass);
}
/**
* Checks that the result of analysis satisfies the given {@link Matcher},
* or fails with an {@link AssertionError}.
* <p>
* The given matcher will be invoked with the {@link AnalysisResult}
* produced by Mutability Detector's analysis of the given class. The most
* common matchers can be found at {@link MutabilityMatchers}.
* <p>
* Example:
* <pre><code>
* MutabilityAssert.assertImmutable(HopefullyImmutable.class,
* MutabilityMatchers.areImmutable());
*
* MutabilityAssert.assertImmutable(HopefullyEffectivelyImmutable.class,
* MutabilityMatchers.areEffectivelyImmutable());
*
* </code></pre>
*
* @see MutabilityMatchers#areImmutable()
* @see MutabilityMatchers#areEffectivelyImmutable()
* @see Matcher
* @see AnalysisResult
* @see IsImmutable#IMMUTABLE
* @see IsImmutable#EFFECTIVELY_IMMUTABLE
*/
public static void assertInstancesOf(Class<?> clazz, Matcher<AnalysisResult> mutabilityMatcher) {
defaultAsserter.assertInstancesOf(clazz, mutabilityMatcher);
}
/**
* Checks that the result of analysis satisfies the given {@link Matcher},
* while allowing mismatches in the form of an allowed reason, or fails with
* an {@link AssertionError}.
* <p>
* The given matcher will be invoked with the {@link AnalysisResult}
* produced by Mutability Detector's analysis of the given class. The most
* common matchers can be found at {@link MutabilityMatchers}.
* <p>
* The given allowed reason will be used to determine if any of the
* {@link MutableReasonDetail} attached to the {@link AnalysisResult} have
* been explicitly permitted by the unit test. If any of the reasons have
* not been allowed, an AssertionError will be thrown.
* <p>
* Several out-of-the-box allowed reasons can be found at
* {@link AllowedReason}.
* <p>
* Example:
*
* <pre>
* <code>
* MutabilityAssert.assertImmutable(HopefullyImmutable.class,
* MutabilityMatchers.areImmutable(),
* AllowedReason.allowingForSubclassing());
*
*
* </code>
* </pre>
*
* @see MutableReasonDetail
* @see AllowedReason
* @see AllowedReason#allowingForSubclassing()
* @see MutabilityMatchers#areImmutable()
*/
public static void assertInstancesOf(Class<?> clazz,
Matcher<AnalysisResult> mutabilityMatcher,
Matcher<MutableReasonDetail> allowing) {
defaultAsserter.assertInstancesOf(clazz, mutabilityMatcher, allowing);
}
/**
* Checks that the result of analysis satisfies the given {@link Matcher},
* while allowing mismatches in the form of allowed reasons, or fails with
* an {@link AssertionError}.
* <p>
* The given matcher will be invoked with the {@link AnalysisResult}
* produced by Mutability Detector's analysis of the given class. The most
* common matchers can be found at {@link MutabilityMatchers}.
* <p>
* The given allowed reason will be used to determine if any of the
* {@link MutableReasonDetail} attached to the {@link AnalysisResult} have
* been explicitly permitted by the unit test. If any of the reasons have
* not been allowed, an AssertionError will be thrown.
* <p>
* Several out-of-the-box allowed reasons can be found at
* {@link AllowedReason}.
* <p>
* Example:
* <pre>
* <code>
* MutabilityAssert.assertImmutable(HopefullyImmutable.class,
* MutabilityMatchers.areImmutable(),
* AllowedReason.allowingForSubclassing(),
* AllowedReason.allowingNonFinalFields());
* </code>
* </pre>
*
* @see MutableReasonDetail
* @see AllowedReason
* @see AllowedReason#allowingForSubclassing()
* @see MutabilityMatchers#areImmutable()
*/
public static void assertInstancesOf(Class<?> clazz,
Matcher<AnalysisResult> mutabilityMatcher,
Matcher<MutableReasonDetail> allowingFirst,
Matcher<MutableReasonDetail> allowingSecond) {
defaultAsserter.assertInstancesOf(clazz, mutabilityMatcher, allowingFirst, allowingSecond);
}
/**
* Checks that the result of analysis satisfies the given {@link Matcher},
* while allowing mismatches in the form of allowed reasons, or fails with
* an {@link AssertionError}.
* <p>
* Alternative version of
* {@link #assertInstancesOf(Class, Matcher, Matcher)} which takes more
* allowed reasons.
*
* @see MutableReasonDetail
* @see AllowedReason
* @see AllowedReason#allowingForSubclassing()
* @see AllowedReason#allowingNonFinalFields()
* @see MutabilityMatchers#areImmutable()
*/
public static void assertInstancesOf(Class<?> clazz,
Matcher<AnalysisResult> mutabilityMatcher,
Matcher<MutableReasonDetail> allowingFirst,
Matcher<MutableReasonDetail> allowingSecond,
Matcher<MutableReasonDetail> allowingThird) {
defaultAsserter.assertInstancesOf(clazz, mutabilityMatcher, allowingFirst, allowingSecond, allowingThird);
}
/**
* Checks that the result of analysis satisfies the given {@link Matcher},
* while allowing mismatches in the form of allowed reasons, or fails with
* an {@link AssertionError}.
* <p>
* Alternative version of
* {@link #assertInstancesOf(Class, Matcher, Matcher)} which takes more
* allowed reasons.
*
* @see MutableReasonDetail
* @see AllowedReason
* @see AllowedReason#allowingForSubclassing()
* @see AllowedReason#allowingNonFinalFields()
* @see MutabilityMatchers#areImmutable()
*/
public static void assertInstancesOf(Class<?> clazz,
Matcher<AnalysisResult> mutabilityMatcher,
Matcher<MutableReasonDetail> allowingFirst,
Matcher<MutableReasonDetail> allowingSecond,
Matcher<MutableReasonDetail> allowingThird,
Matcher<MutableReasonDetail>... allowingRest) {
defaultAsserter.assertInstancesOf(clazz,
mutabilityMatcher,
allowingFirst,
allowingSecond,
allowingThird,
allowingRest);
}
/**
* Checks that the result of analysis satisfies the given {@link Matcher},
* while allowing mismatches in the form of allowed reasons, or fails with
* an {@link AssertionError}.
* <p>
* Alternative version of
* {@link #assertInstancesOf(Class, Matcher, Matcher)} which takes an
* iterable of allowed reasons.
*
* @see MutableReasonDetail
* @see AllowedReason
* @see AllowedReason#allowingForSubclassing()
* @see AllowedReason#allowingNonFinalFields()
* @see MutabilityMatchers#areImmutable()
*/
public static void assertInstancesOf(Class<?> clazz,
Matcher<AnalysisResult> mutabilityMatcher,
Iterable<Matcher<MutableReasonDetail>> allowingAll) {
defaultAsserter.assertInstancesOf(clazz, mutabilityMatcher, allowingAll);
}
}