package org.mutabilitydetector.unittesting.matchers.reasons;
/*
* #%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.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.mutabilitydetector.Configurations.OUT_OF_THE_BOX_CONFIGURATION;
import static org.mutabilitydetector.IsImmutable.NOT_IMMUTABLE;
import static org.mutabilitydetector.TestUtil.analysisDatabase;
import static org.mutabilitydetector.TestUtil.testAnalysisSession;
import static org.mutabilitydetector.TestUtil.testingVerifierFactory;
import static org.mutabilitydetector.checkers.info.AnalysisDatabase.TYPE_STRUCTURE;
import static org.mutabilitydetector.unittesting.AllowedReason.assumingFields;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.mutabilitydetector.AnalysisResult;
import org.mutabilitydetector.MutableReasonDetail;
import org.mutabilitydetector.TestUtil;
import org.mutabilitydetector.checkers.ArrayFieldMutabilityChecker;
import org.mutabilitydetector.checkers.AsmMutabilityChecker;
import org.mutabilitydetector.checkers.CanSubclassChecker;
import org.mutabilitydetector.checkers.MutableTypeToFieldChecker;
import org.mutabilitydetector.checkers.PublishedNonFinalFieldChecker;
import org.mutabilitydetector.checkers.info.AnalysisInProgress;
import org.mutabilitydetector.checkers.info.CyclicReferences;
import org.mutabilitydetector.checkers.info.MutableTypeInformation;
import org.mutabilitydetector.checkers.info.TypeStructureInformation;
import org.mutabilitydetector.locations.Dotted;
import java.util.Collections;
import java.util.Set;
@SuppressWarnings("unused")
public class AssumingArrayFieldsTest {
private final MutableTypeInformation mutableTypeInfo = new MutableTypeInformation(
testAnalysisSession(),
OUT_OF_THE_BOX_CONFIGURATION,
CyclicReferences.newEmptyMutableInstance());
private final TypeStructureInformation typeStructureInfo = analysisDatabase().requestInformation(TYPE_STRUCTURE);
private final Set<Dotted> immutableContainerClasses = Collections.emptySet();
private final AnalysisInProgress analysisInProgress = AnalysisInProgress.noAnalysisUnderway();
private final AsmMutabilityChecker mutableTypeToFieldChecker = new MutableTypeToFieldChecker(
typeStructureInfo,
mutableTypeInfo,
testingVerifierFactory(),
immutableContainerClasses,
analysisInProgress);
private final AsmMutabilityChecker arrayFieldChecker = new ArrayFieldMutabilityChecker();
@Test
public void matchesWhenGivenFieldNameIsLinkedToArrayFieldReason() throws Exception {
MutableReasonDetail reason = getOnlyReasonFromRunningChecker(arrayFieldChecker, ArrayFieldUsedSafely.class);
assertThat(reason, assumingFields("myArrayField").areNotModifiedAndDoNotEscape());
}
@Test
public void doesNotMatchWhenGivenIncorrectFieldName() throws Exception {
MutableReasonDetail reason = getOnlyReasonFromRunningChecker(arrayFieldChecker, ArrayFieldUsedSafely.class);
assertThat(reason, not(assumingFields("myArrayFieldNOTCALLEDTHIS").areNotModifiedAndDoNotEscape()));
}
@Test
public void matchesWhenArrayFieldIsConsideredAsAnAssignmentOfMutableTypeToField() throws Exception {
MutableReasonDetail reason = getOnlyReasonFromRunningChecker(mutableTypeToFieldChecker, ArrayFieldUsedSafely.class);
assertThat(reason, assumingFields("myArrayField").areNotModifiedAndDoNotEscape());
}
@Test
public void doesNotMatchForReasonWhereUnsafeToAssumeNotModifyingTheFieldLocally() throws Exception {
MutableReasonDetail reason = getOnlyReasonFromRunningChecker(new PublishedNonFinalFieldChecker(), MutableForIrrelevantReason.class);
assertThat(reason, not(assumingFields("reassignMe").areNotModifiedAndDoNotEscape()));
}
@Test
public void doesNotMatchForReasonWhichDoesNotOriginateFromAField() throws Exception {
MutableReasonDetail reason = getOnlyReasonFromRunningChecker(new CanSubclassChecker(), CanSubclass.class);
assertThat(reason, not(assumingFields("mutabilityIsNothingToDoWithThisField").areNotModifiedAndDoNotEscape()));
}
private MutableReasonDetail getOnlyReasonFromRunningChecker(AsmMutabilityChecker mutabilityChecker, Class<?> toAnalyse) {
AnalysisResult result = TestUtil.runChecker(mutabilityChecker, toAnalyse);
assertThat(result.isImmutable, is(NOT_IMMUTABLE));
assertThat(result.reasons, Matchers.hasSize(1));
return result.reasons.iterator().next();
}
private static final class ArrayFieldUsedSafely {
private final int[] myArrayField = new int[] { 1, 2 };
public int getFirst() {
return myArrayField[0];
}
}
private static final class MutableForIrrelevantReason {
public String reassignMe;
}
protected static class CanSubclass {
private final String mutabilityIsNothingToDoWithThisField = "unchangeable";
}
}