/** * Copyright (c) 2012-2017, jcabi.com * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: 1) Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. 2) Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. 3) Neither the name of the jcabi.com nor * the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.jcabi.aspects; import com.jcabi.aspects.version.Version; import java.util.regex.Pattern; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; /** * Test case for {@link Immutable} annotation and its implementation. * * @author Yegor Bugayenko (yegor@tpc2.com) * @version $Id: 749376021adc36e00e3389d60082261e7bcecb30 $ * @checkstyle ConstantUsageCheck (500 lines) */ @SuppressWarnings ( { "PMD.UnusedPrivateField", "PMD.UnusedLocalVariable", "PMD.FinalFieldCouldBeStatic" } ) public final class ImmutableTest { /** * Exception rule. */ @Rule // @checkstyle VisibilityModifierCheck (1 line) public final transient ExpectedException thrown; /** * Ctor. */ public ImmutableTest() { this.thrown = ExpectedException.none(); } /** * Immutable can catch mutable classes. */ @Test(expected = IllegalStateException.class) public void catchedMutableTypes() { new Mutable(); } /** * ImmutabilityChecker can catch mutable classes with arrays. */ @Test(expected = IllegalStateException.class) public void catchedMutableTypesWithArrays() { new MutableWithArray(); } /** * Immutable can pass immutable classes. */ @Test public void passesImmutableObjects() { final Object obj = new TruelyImmutable( new TruelyImmutableWithNonPrivateFields() ); } /** * Immutable can pass immutable classes. */ @Test public void passesImmutableObjectsWithNonPrivateFields() { new TruelyImmutableWithNonPrivateFields(); } /** * Immutable can catch mutable classes with interfaces. */ @Test(expected = IllegalStateException.class) public void catchesTypesMutableByClassInheritance() { new MutableByInheritance(); } /** * Informs version on error. */ @Test public void informsVersionOnError() { this.thrown.expect(IllegalStateException.class); this.thrown.expectMessage(Version.CURRENT.projectVersion()); this.thrown.expectMessage(Version.CURRENT.buildNumber()); new Mutable(); } /** * Supposedly immutable class. */ @Immutable private static final class Mutable { /** * Mutable class member. */ @SuppressWarnings("PMD.ImmutableField") private transient String data = "hello"; } /** * Mutable class because of array. */ @Immutable private static final class MutableWithArray { /** * Mutable class member. */ private final transient int[] data = null; } /** * Other vague interface. */ @Immutable private interface ImmutableInterface { /** * This function seems to be harmless. * @param input An input */ void willBreakImmutability(int input); } /** * Truely immutable class. */ @Immutable private static final class TruelyImmutable { /** * Something static final. */ private static final Pattern PATTERN = Pattern.compile(".?"); /** * Something just static. */ private static Pattern ptrn = Pattern.compile("\\d+"); /** * Immutable class member. */ private final transient String data; /** * Another immutable class member. */ private final transient int number; /** * Another immutable class member. */ private final transient String text; /** * An immutable array member. */ @Immutable.Array private final transient String[] texts; /** * Immutable iface. */ private final transient ImmutableInterface iface; /** * Ctor. */ public TruelyImmutable() { this("Hello, world!"); } /** * Ctor. * @param ipt Input */ public TruelyImmutable(final TruelyImmutableWithNonPrivateFields ipt) { this(ipt.text); } /** * Ctor. * @param ipt Input */ @SuppressWarnings("PMD.NullAssignment") public TruelyImmutable(final String ipt) { this.text = ipt; this.texts = new String[] {"foo"}; this.iface = null; this.data = null; this.number = 2; } } /** * Truely immutable class with non-private fields. * * @checkstyle VisibilityModifier (25 lines) */ @Immutable private static final class TruelyImmutableWithNonPrivateFields { /** * Something static final. */ public static final Pattern PATTERN = Pattern.compile(".*"); /** * Something just static. */ public static final Pattern PTRN = Pattern.compile(".+"); /** * Immutable class member. */ public final String data = null; /** * Another immutable class member. */ public final int number = 2; /** * Another immutable class member. */ public final String text = "Hello!"; } /** * Almost immutable class. It can be inherited, because it is non-final; * thus methods in the child class can return nonsensical values (e.g. * getters that do no return the original value of their corresponding * fields). Moreover, immutability cannot be forced to a subclass. * See <a href= * "http://marxsoftware.blogspot.se/2009/09/ * is-java-immutable-class-always-final.html"> * Is java immutable class always final?</a> */ @Immutable private static class MutableByInheritance { /** * Immutable class member. */ private final transient String data = null; /** * Could be overloaded by a child of the class and then return * nonsensical value. * * @return A value that could differ from what is expected if returned by an overriding method */ public String getData() { return this.data; } } }