package org.mutabilitydetector.benchmarks;

/*
 * #%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 com.google.common.collect.ImmutableSet;
import org.junit.Ignore;
import org.junit.Test;
import org.mutabilitydetector.AnalysisResult;
import org.mutabilitydetector.Configurations;
import org.mutabilitydetector.MutableReasonDetail;
import org.mutabilitydetector.benchmarks.mutabletofield.CollectionFields;
import org.mutabilitydetector.benchmarks.mutabletofield.CollectionFields.HasImmutableContainerOfGenericType;
import org.mutabilitydetector.benchmarks.mutabletofield.CollectionFields.HasImmutableContainerOfImmutableType;
import org.mutabilitydetector.benchmarks.mutabletofield.CollectionFields.HasImmutableContainerOfMutableType;
import org.mutabilitydetector.benchmarks.mutabletofield.CollectionFields.ImmutableContainer;
import org.mutabilitydetector.benchmarks.mutabletofield.CollectionFields.NestedGenericTypes;
import org.mutabilitydetector.benchmarks.mutabletofield.CollectionFields.SafelyCopiedMapGenericOnImmutableTypeForKey_ManyFields;
import org.mutabilitydetector.benchmarks.mutabletofield.CollectionFields.SafelyCopiedMapGenericOnMutableTypeForKey;
import org.mutabilitydetector.benchmarks.mutabletofield.CollectionFields.SafelyCopiedMap_UsesGenericTypeOfClass;
import org.mutabilitydetector.checkers.AsmMutabilityChecker;
import org.mutabilitydetector.checkers.CollectionWithMutableElementTypeToFieldChecker;
import org.mutabilitydetector.checkers.info.AnalysisInProgress;
import org.mutabilitydetector.checkers.info.CyclicReferences;
import org.mutabilitydetector.checkers.info.MutableTypeInformation;
import org.mutabilitydetector.locations.Dotted;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.mutabilitydetector.MutabilityReason.COLLECTION_FIELD_WITH_MUTABLE_ELEMENT_TYPE;
import static org.mutabilitydetector.TestMatchers.hasNoReasons;
import static org.mutabilitydetector.TestMatchers.hasReasons;
import static org.mutabilitydetector.TestUtil.runChecker;
import static org.mutabilitydetector.TestUtil.testAnalysisSession;
import static org.mutabilitydetector.TestUtil.testingVerifierFactory;
import static org.mutabilitydetector.locations.Dotted.fromClass;
import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;
import static org.mutabilitydetector.unittesting.MutabilityMatchers.areNotImmutable;

public class CollectionWithMutableElementTypeToFieldCheckerTest {

    private final MutableTypeInformation mutableTypeInfo =
            new MutableTypeInformation(
                    testAnalysisSession(),
                    Configurations.NO_CONFIGURATION,
                    CyclicReferences.newEmptyMutableInstance());

    private final AsmMutabilityChecker checker =
            new CollectionWithMutableElementTypeToFieldChecker(
                    mutableTypeInfo,
                    testingVerifierFactory(),
                    ImmutableSet.<Dotted>of(),
                    AnalysisInProgress.noAnalysisUnderway());

    @Test
    public void safelyWrappedCollectionsAreStillMutableIfTheTypeOfListElementsIsMutable() throws Exception {
        AnalysisResult result = runChecker(checker, SafelyCopiedMapGenericOnMutableTypeForKey.class);
        assertThat(result, areNotImmutable());
        assertThat(checker, hasReasons(COLLECTION_FIELD_WITH_MUTABLE_ELEMENT_TYPE));
    }

    @Test
    public void doesNotRaiseErrorWhenElementTypeIsImmutable() throws Exception {
        AnalysisResult result = runChecker(checker, SafelyCopiedMapGenericOnImmutableTypeForKey_ManyFields.class);
        assertThat(result, areImmutable());
        assertThat(checker, hasNoReasons());
    }

    @Test
    public void supportsNestedGenericTypes() throws Exception {
        AnalysisResult result = runChecker(checker, NestedGenericTypes.class);
        assertThat(result, areNotImmutable());
        assertThat(checker, hasReasons(COLLECTION_FIELD_WITH_MUTABLE_ELEMENT_TYPE));
    }

    @Test
    public void raisesErrorWhenCollectionFieldHasElementTypeUsingGenericTypeOfClass() {
        AnalysisResult result = runChecker(checker, SafelyCopiedMap_UsesGenericTypeOfClass.class);
        assertThat(result, areNotImmutable());
        assertThat(checker, hasReasons(COLLECTION_FIELD_WITH_MUTABLE_ELEMENT_TYPE));
        assertThat(checker.checkerResult().reasons.iterator().next().message(),
                containsString("<org.mutabilitydetector.benchmarks.ImmutableExample, SOME_GENERIC_TYPE>"));
    }

    @Test
    public void raisesErrorForHardcodedImmutableContainerTypeThatIsGenericWithMutableType() {
        AsmMutabilityChecker checker = checkerWithHardcodedAsImmutable(ImmutableContainer.class);

        AnalysisResult result = runChecker(checker, HasImmutableContainerOfMutableType.class);
        assertThat(result, areNotImmutable());
    }

    @Test
    public void raisesErrorWhenCollectionFieldTypeIsPrimitiveArray() {
        AnalysisResult result = runChecker(checker, CollectionFields.CollectionWithByteArrayGenericType.class);
        assertThat(result, areNotImmutable());
        assertThat(checker, hasReasons(COLLECTION_FIELD_WITH_MUTABLE_ELEMENT_TYPE));
        assertThat(checker.checkerResult().reasons.iterator().next().message(),
                containsString("(java.util.Collection<[B>)"));
    }


    /**
     * FIXME Wrong error message. {@link org.mutabilitydetector.locations.Dotted#dotted(String)} method removed
     */
    @Test
    @Ignore
    public void raisesErrorWhenCollectionFieldTypeIsObjectArray() {
        AnalysisResult result = runChecker(checker, CollectionFields.CollectionWithStringArrayGenericType.class);
        assertThat(result, areNotImmutable());
        assertThat(checker, hasReasons(COLLECTION_FIELD_WITH_MUTABLE_ELEMENT_TYPE));
        assertThat(checker.checkerResult().reasons.iterator().next().message(),
                containsString("(java.util.Collection<[Ljava.lang.Sring>)"));
    }

    @Test
    public void raisesErrorForHardcodedImmutableContainerTypeThatIsGenericWithVariableGenericParameter() {
        AsmMutabilityChecker checker = checkerWithHardcodedAsImmutable(ImmutableContainer.class);

        AnalysisResult result = runChecker(checker, HasImmutableContainerOfGenericType.class);
        assertThat(result, areNotImmutable());
    }

    @Test
    public void doesNotRaiseErrorForHardcodedImmutableContainerTypeThatIsGenericWithImmutableType() {
        AsmMutabilityChecker checker = checkerWithHardcodedAsImmutable(ImmutableContainer.class);

        AnalysisResult result = runChecker(checker, HasImmutableContainerOfImmutableType.class);
        assertThat(result, areImmutable());
    }

    private AsmMutabilityChecker checkerWithHardcodedAsImmutable(Class<ImmutableContainer> containerClass) {
        return new CollectionWithMutableElementTypeToFieldChecker(
                    mutableTypeInfo,
                    testingVerifierFactory(),
                    ImmutableSet.of(fromClass(containerClass)),
                    AnalysisInProgress.noAnalysisUnderway());
    }

    @Test
    public void descriptionOfCollectionWithMutableElementType() throws Exception {
        AnalysisResult result = runChecker(checker, SafelyCopiedMapGenericOnMutableTypeForKey.class);
        assertThat(result, areNotImmutable());

        MutableReasonDetail reasonDetail = result.reasons.iterator().next();

        assertThat(reasonDetail.message(),
                is("Field can have collection with mutable element type " +
                        "(java.util.Map<java.util.Date, org.mutabilitydetector.benchmarks.ImmutableExample>) assigned to it."));
    }

}

@@ -6618,16 +6618,17 @@
 a.lang.S
+t
 ring%3E)%22)

Fix the typo. Leave test ignored as it's not what is causing the failure, but it was my first guess.