package org.mutabilitydetector.benchmarks.escapedthis; /* * #%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.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.mutabilitydetector.IsImmutable.IMMUTABLE; import static org.mutabilitydetector.MutableReasonDetail.newMutableReasonDetail; import static org.mutabilitydetector.TestUtil.runChecker; import static org.mutabilitydetector.unittesting.MutabilityMatchers.areNotImmutable; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.theories.DataPoints; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.rules.MethodRule; import org.junit.runner.RunWith; import org.mutabilitydetector.AnalysisResult; import org.mutabilitydetector.MutabilityReason; import org.mutabilitydetector.MutableReasonDetail; import org.mutabilitydetector.TestUtil; import org.mutabilitydetector.benchmarks.ImmutableExample; import org.mutabilitydetector.benchmarks.escapedthis.PassesThisReferenceToMethodCall.AsFirstOfSeveralParameters; import org.mutabilitydetector.benchmarks.escapedthis.PassesThisReferenceToMethodCall.AsLastOfSeveralParameters; import org.mutabilitydetector.benchmarks.escapedthis.PassesThisReferenceToMethodCall.AsMoreThanOneOfSeveralParameters; import org.mutabilitydetector.benchmarks.escapedthis.PassesThisReferenceToMethodCall.AsOneOfSeveralParameters; import org.mutabilitydetector.benchmarks.escapedthis.PassesThisReferenceToMethodCall.AsOneOfSeveralParametersWithOtherWeirdCode; import org.mutabilitydetector.benchmarks.escapedthis.PassesThisReferenceToMethodCall.AsParameterToPrivateMethod; import org.mutabilitydetector.benchmarks.escapedthis.PassesThisReferenceToMethodCall.AsParameterToStaticMethod; import org.mutabilitydetector.benchmarks.escapedthis.PassesThisReferenceToMethodCall.AsSingleParameter; import org.mutabilitydetector.benchmarks.escapedthis.PassesThisReferenceToMethodCall.InOneConstructorButNotTheOther; import org.mutabilitydetector.benchmarks.escapedthis.Safe.IsMutableForReassigningFieldNotForThisEscaping; import org.mutabilitydetector.benchmarks.escapedthis.Safe.NoThisPassedToOtherObjectAsOneOfManyParametersAndDoesWeirdStuffInNewCall; import org.mutabilitydetector.benchmarks.escapedthis.Safe.PassesThisReferenceAfterConstruction; import org.mutabilitydetector.benchmarks.escapedthis.Unsafe.AliasesThisReferenceBeforeLettingItEscape; import org.mutabilitydetector.benchmarks.escapedthis.Unsafe.PassAnonymousInnerClassWithImplicitReferenceToThis; import org.mutabilitydetector.benchmarks.escapedthis.Unsafe.PassInnerClassWithImplicitReferenceToThis; import org.mutabilitydetector.benchmarks.escapedthis.Unsafe.PassThisReferenceToParameter; import org.mutabilitydetector.benchmarks.escapedthis.Unsafe.PassThisReferenceToStaticObject; import org.mutabilitydetector.benchmarks.escapedthis.Unsafe.SaveThisReferenceAsInstanceFieldOfThisClass; import org.mutabilitydetector.benchmarks.escapedthis.Unsafe.SaveThisReferenceAsStaticFieldOfThisClass; import org.mutabilitydetector.benchmarks.escapedthis.Unsafe.SetThisReferenceAsInstanceFieldOfOtherObject; import org.mutabilitydetector.benchmarks.escapedthis.Unsafe.SetThisReferenceAsStaticFieldOfOtherClass; import org.mutabilitydetector.benchmarks.escapedthis.Unsafe.ThisPassedToPrivateMethodWhichDoesPublishReference; import org.mutabilitydetector.checkers.EscapedThisReferenceChecker; import org.mutabilitydetector.junit.FalsePositive; import org.mutabilitydetector.junit.IncorrectAnalysisRule; import org.mutabilitydetector.locations.CodeLocation.ClassLocation; import org.mutabilitydetector.locations.Dotted; @RunWith(Theories.class) public class EscapedThisReferenceCheckerTest { @Rule public MethodRule rule = new IncorrectAnalysisRule(); @Test public void immutableExampleIsNotRenderedMutable() throws Exception { assertThisDoesNotEscape(ImmutableExample.class); } @Test public void thisReferenceEscapingAfterConstructionDoesNotRenderClassMutable() throws Exception { assertThisDoesNotEscape(PassesThisReferenceAfterConstruction.class); } @Ignore("This fails on Jenkins, but passes locally, no idea why.") @Test public void noThisReferencePassedToConstructorOfOtherObjectWithInExtraWeirdCodeInNewCall() throws Exception { assertThisDoesNotEscape(NoThisPassedToOtherObjectAsOneOfManyParametersAndDoesWeirdStuffInNewCall.class); } @Test public void doesNotRenderMutableForHavingSetterMethod() throws Exception { assertThisDoesNotEscape(IsMutableForReassigningFieldNotForThisEscaping.class); } @Test public void doesNotRenderMutableForNewingUpObjectToAssignToField() throws Exception { assertThisDoesNotEscape(Safe.NewsUpObjectToAssignToField.class); } @Test public void doesNotRenderMutableForImplicitlyInvokingSuper() throws Exception { assertThisDoesNotEscape(Safe.ImplicitCallToSuper.class); } @Test public void doesNotRenderMutableForAssigningNonTopLevelClassToField() throws Exception { assertThisDoesNotEscape(AssignsNonInnerNonTopLevelClassToField.class); } @Test public void doesNotRenderMutableForExplicitlyInvokingSuper() throws Exception { assertThisDoesNotEscape(Safe.ExplicitCallToSuper.class); } @Test public void doesNotRenderMutableForCallingOtherConstructorOfThisClass() throws Exception { assertThisDoesNotEscape(Safe.CallToOtherConstructor.class); } @Test public void doesNotRenderMutableForPassingInitialisedFieldReference() throws Exception { assertThisDoesNotEscape(Safe.PassesInitialisedFieldToOtherMethod.class); } @Test @FalsePositive("Is only assigning this reference to field of same instance") public void doesNotRenderMutableForAssigningThisToInstanceField() throws Exception { assertThisDoesNotEscape(SaveThisReferenceAsInstanceFieldOfThisClass.class); } @Test @FalsePositive("Can't detect this situation.") public void thisReferenceAliasedToSomethingElseWhichEscapesIsReported() throws Exception { thisReferenceEscapingRendersClassMutable(AliasesThisReferenceBeforeLettingItEscape.class); } private void assertThisDoesNotEscape(Class<?> toAnalyse) { AnalysisResult result = runChecker(new EscapedThisReferenceChecker(), toAnalyse); assertEquals(TestUtil.formatReasons(result.reasons), IMMUTABLE, result.isImmutable); } @DataPoints public static Class<?>[] classes = new Class[] { ThisEscape.class, AsSingleParameter.class, AsOneOfSeveralParameters.class, AsOneOfSeveralParametersWithOtherWeirdCode.class, InOneConstructorButNotTheOther.class, AsLastOfSeveralParameters.class, AsFirstOfSeveralParameters.class, AsMoreThanOneOfSeveralParameters.class, AsParameterToPrivateMethod.class, AsParameterToStaticMethod.class, PassThisReferenceToStaticObject.class, ThisPassedToPrivateMethodWhichDoesPublishReference.class, SaveThisReferenceAsStaticFieldOfThisClass.class, SetThisReferenceAsStaticFieldOfOtherClass.class, SetThisReferenceAsInstanceFieldOfOtherObject.class, PassThisReferenceToParameter.class, PassInnerClassWithImplicitReferenceToThis.class, PassAnonymousInnerClassWithImplicitReferenceToThis.class }; @Theory public void thisReferenceEscapingRendersClassMutable(Class<?> passesThisReference) throws Exception { AnalysisResult result = runChecker(new EscapedThisReferenceChecker(), passesThisReference); assertThat(result, areNotImmutable()); assertEquals(reasonDetailFor(passesThisReference), result.reasons.iterator().next()); } private MutableReasonDetail reasonDetailFor(Class<?> clazz) { return newMutableReasonDetail("The 'this' reference is passed outwith the constructor.", ClassLocation.from(Dotted.fromClass(clazz)), MutabilityReason.ESCAPED_THIS_REFERENCE); } }