package org.mutabilitydetector.benchmarks.settermethod; /* * #%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.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.mutabilitydetector.TestMatchers.hasReasons; import static org.mutabilitydetector.TestUtil.runChecker; import static org.mutabilitydetector.checkers.CheckerRunner.ExceptionPolicy.FAIL_FAST; import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable; import static org.mutabilitydetector.unittesting.MutabilityMatchers.areNotImmutable; import java.math.BigDecimal; import org.hamcrest.Matchers; import org.junit.Before; 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.AnalysisSession; import org.mutabilitydetector.TestUtil; import org.mutabilitydetector.benchmarks.ImmutableExample; import org.mutabilitydetector.benchmarks.settermethod.SetsFieldsOfDifferentTypes.SetsBoolean; import org.mutabilitydetector.benchmarks.settermethod.SetsFieldsOfDifferentTypes.SetsByte; import org.mutabilitydetector.benchmarks.settermethod.SetsFieldsOfDifferentTypes.SetsChar; import org.mutabilitydetector.benchmarks.settermethod.SetsFieldsOfDifferentTypes.SetsDouble; import org.mutabilitydetector.benchmarks.settermethod.SetsFieldsOfDifferentTypes.SetsFloat; import org.mutabilitydetector.benchmarks.settermethod.SetsFieldsOfDifferentTypes.SetsInt; import org.mutabilitydetector.benchmarks.settermethod.SetsFieldsOfDifferentTypes.SetsLong; import org.mutabilitydetector.benchmarks.settermethod.SetsFieldsOfDifferentTypes.SetsObjectArray; import org.mutabilitydetector.benchmarks.settermethod.SetsFieldsOfDifferentTypes.SetsObjectArrayArray; import org.mutabilitydetector.benchmarks.settermethod.SetsFieldsOfDifferentTypes.SetsReference; import org.mutabilitydetector.benchmarks.settermethod.SetsFieldsOfDifferentTypes.SetsShort; import org.mutabilitydetector.benchmarks.types.EnumType; import org.mutabilitydetector.checkers.CheckerRunner; import org.mutabilitydetector.checkers.OldSetterMethodChecker; import org.mutabilitydetector.checkers.info.InformationRetrievalRunner; import org.mutabilitydetector.checkers.info.PrivateMethodInvocationInformation; import org.mutabilitydetector.junit.FalsePositive; import org.mutabilitydetector.junit.IncorrectAnalysisRule; import org.mutabilitydetector.locations.CodeLocation; import org.mutabilitydetector.locations.CodeLocation.FieldLocation; @RunWith(Theories.class) public final class SetterMethodCheckerTest { @Rule public MethodRule rule = new IncorrectAnalysisRule(); private OldSetterMethodChecker checker; @DataPoints public static Class<?>[] classes = new Class[] { SetsBoolean.class, SetsByte.class, SetsChar.class, SetsDouble.class, SetsFloat.class, SetsInt.class, SetsLong.class, SetsObjectArray.class, SetsObjectArrayArray.class, SetsReference.class, SetsShort.class }; @Before public void setUp() { CheckerRunner checkerRunner = CheckerRunner.createWithCurrentClasspath(FAIL_FAST); AnalysisSession analysisSession = TestUtil.testAnalysisSession(); PrivateMethodInvocationInformation info = new PrivateMethodInvocationInformation(new InformationRetrievalRunner(analysisSession, checkerRunner)); checker = OldSetterMethodChecker.newSetterMethodChecker(info, TestUtil.testingVerifierFactory()); } private AnalysisResult doCheck(Class<?> toCheck) { return TestUtil.runChecker(checker, toCheck); } @Test public void immutableExamplePassesCheck() throws Exception { assertThat(doCheck(ImmutableExample.class), areImmutable()); } @Test public void mutableByHavingSetterMethodFailsCheck() throws Exception { assertThat(doCheck(MutableByHavingSetterMethod.class), areNotImmutable()); } @Test public void integerClassPassesCheck() throws Exception { assertThat(doCheck(Integer.class), areImmutable()); } @Test public void enumTypePassesCheck() throws Exception { assertThat(doCheck(EnumType.class), areImmutable()); } @FalsePositive("Field [myField] can be reassigned within method [setPrivateFieldOnInstanceOfSelf]" + "Field [primitiveField] can be reassigned within method [setPrivateFieldOnInstanceOfSelf]") @Test public void settingFieldOfOtherInstanceDoesNotRenderClassMutable() throws Exception { assertThat(doCheck(ImmutableButSetsPrivateFieldOfInstanceOfSelf.class), areImmutable()); } @Test public void settingFieldOfOtherInstanceAndThisInstanceRendersClassMutable() throws Exception { assertThat(doCheck(MutableBySettingFieldOnThisInstanceAndOtherInstance.class), areNotImmutable()); } @Test public void fieldsSetInPrivateMethodCalledOnlyFromConstructorIsImmutable() { assertThat(doCheck(ImmutableUsingPrivateFieldSettingMethod.class), areImmutable()); } @FalsePositive("Field [reassignable] can be reassigned within method [setFieldOnParameter]") @Test public void settingFieldOfObjectPassedAsParameterDoesNotRenderClassMutable() throws Exception { assertThat(doCheck(ImmutableButSetsFieldOfOtherClass.class), areImmutable()); } @Test public void settingFieldOfMutableFieldRendersClassMutable() throws Exception { assertThat(doCheck(MutableBySettingFieldOfField.class), areNotImmutable()); } @FalsePositive("Does not create any reasons.") @Test public void subclassOfSettingFieldOfMutableFieldRendersClassMutable() throws Exception { AnalysisResult result = doCheck(StillMutableSubclass.class); assertThat(checker, hasReasons()); assertThat(result, areNotImmutable()); } @FalsePositive("Field [precision] can be reassigned within method [precision]" + "Field [stringCache] can be reassigned within method [toString]" + "Field [intVal] can be reassigned within method [inflate]") @Test public void bigDecimalDoesNotFailCheck() throws Exception { assertThat(doCheck(BigDecimal.class), areImmutable()); } @FalsePositive("Field [hash] can be reassigned within method [hashCode]") @Test public void stringDoesNotFailCheck() throws Exception { assertThat(doCheck(String.class), areImmutable()); } @Test public void fieldReassignmentInPublicStaticMethodMakesClassMutable() throws Exception { AnalysisResult result = doCheck(MutableByAssigningFieldOnInstanceWithinStaticMethod.class); assertThat(result, areNotImmutable()); } @FalsePositive("Field can be reassigned.") @Test public void reassignmentOfStackConfinedObjectDoesNotFailCheck() throws Exception { assertThat(doCheck(ImmutableWithMutatingStaticFactoryMethod.class), areImmutable()); } @Test public void reassigningFieldWithNewedUpObjectShouldBeMutable() { assertThat(doCheck(MutableByAssigningFieldToNewedUpObject.class), areNotImmutable()); } @Test public void codeLocationOfReasonIsAFieldLocation() throws Exception { CodeLocation<?> location = doCheck(ReassignsSingleField.class).reasons.iterator().next().codeLocation(); assertThat(location, Matchers.instanceOf(FieldLocation.class)); assertThat(((FieldLocation)location).fieldName(), is("reassigned")); } @Theory public void settingFieldsOfAnyTypeShouldBeMutable(Class<?> mutableSettingField) throws Exception { AnalysisResult result = runChecker(checker, mutableSettingField); assertThat(result, areNotImmutable()); } }