/** * Copyright 2013 the original author or authors. * * 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. **/ package io.neba.core.resourcemodels.mapping; import io.neba.api.resourcemodels.AnnotatedFieldMapper; import io.neba.core.resourcemodels.metadata.MappedFieldMetaData; import io.neba.core.util.Annotations; import org.apache.sling.api.resource.Resource; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.List; import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.apache.commons.lang3.reflect.FieldUtils.getDeclaredField; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; /** * @author Olaf Otto */ @RunWith(MockitoJUnitRunner.class) public class AnnotatedFieldMappersTest { private Field field1 = getDeclaredField(TestModel1.class, "resources"); private Field field2 = getDeclaredField(TestModel2.class, "resource"); private CustomAnnotation1 annotation1 = this.field1.getAnnotation(CustomAnnotation1.class); private CustomAnnotation2 annotation2 = this.field2.getAnnotation(CustomAnnotation2.class); private CustomAnnotation1 metaAnnotation1 = CustomAnnotation2.class.getAnnotation(CustomAnnotation1.class); @Retention(RUNTIME) private @interface CustomAnnotation1 {} @CustomAnnotation1 @Retention(RUNTIME) private @interface CustomAnnotation2 {} private static class TestModel1 { @CustomAnnotation1 public List<Resource> resources; } private static class TestModel2 { @CustomAnnotation2 public Resource resource; } @Mock private MappedFieldMetaData metadata1, metadata2; @Mock private Annotations annotations1, annotations2; @Mock private AnnotatedFieldMapper mapper1, mapper2, mapper3; @InjectMocks private AnnotatedFieldMappers testee; @Before public void setUp() throws Exception { doReturn(CustomAnnotation1.class).when(this.mapper1).getAnnotationType(); doReturn(Collection.class).when(this.mapper1).getFieldType(); doReturn(CustomAnnotation2.class).when(this.mapper2).getAnnotationType(); doReturn(Resource.class).when(this.mapper2).getFieldType(); doReturn(CustomAnnotation2.class).when(this.mapper3).getAnnotationType(); doReturn(Resource.class).when(this.mapper3).getFieldType(); doReturn(this.field1).when(this.metadata1).getField(); doReturn(this.field1.getType()).when(this.metadata1).getType(); doReturn(this.field2).when(this.metadata2).getField(); doReturn(this.field2.getType()).when(this.metadata2).getType(); doReturn(this.annotations1).when(this.metadata1).getAnnotations(); doReturn(this.annotations2).when(this.metadata2).getAnnotations(); final List<Annotation> ann1 = new ArrayList<>(); ann1.add(annotation1); doAnswer(invocationOnMock -> ann1.iterator()).when(this.annotations1).iterator(); final List<Annotation> ann2 = new ArrayList<>(); ann2.add(annotation2); ann2.add(metaAnnotation1); doAnswer(invocationOnMock -> ann2.iterator()).when(this.annotations2).iterator(); } @Test(expected = IllegalArgumentException.class) public void testQueryingWithNullMetaData() throws Exception { this.testee.get(null); } @Test(expected = IllegalArgumentException.class) public void testAdditionOfNullMapper() throws Exception { bind(null); } @Test public void testRemovalOfNullMapperDoesNotCauseException() throws Exception { unbind(null); } @Test public void testEmptyMappers() throws Exception { assertNoMapperExistFor(this.metadata1); } @Test public void testMapperResolutionWithDisjointMappers() throws Exception { bind(this.mapper1); bind(this.mapper2); assertMetadataHasMappers(this.metadata1, this.mapper1); assertMetadataHasAnnotations(this.metadata1, this.annotation1); assertMetadataHasMappers(this.metadata2, this.mapper2); assertMetadataHasAnnotations(this.metadata2, this.annotation2); } @Test public void testMapperResolutionWithOverlappingMappers() throws Exception { withMapperSupporting(this.mapper1, Resource.class); bind(this.mapper1); bind(this.mapper2); assertNoMapperExistFor(this.metadata1); assertMetadataHasMappers(this.metadata2, this.mapper1, this.mapper2); assertMetadataHasAnnotations(this.metadata2, this.annotation2, this.metaAnnotation1); } @Test public void testMappersForBoxedTypesAreaAppliedToTheirPrimitiveVariants() throws Exception { bind(this.mapper1); doReturn(Boolean.class).when(this.mapper1).getFieldType(); doReturn(boolean.class).when(this.metadata1).getType(); assertMetadataHasMappers(this.metadata1, this.mapper1); doReturn(Integer.class).when(this.mapper1).getFieldType(); doReturn(int.class).when(this.metadata1).getType(); assertMetadataHasMappers(this.metadata1, this.mapper1); } @Test public void testMapperRemoval() throws Exception { assertNoMapperExistFor(this.metadata1); bind(this.mapper1); assertMetadataHasMappers(this.metadata1, this.mapper1); unbind(this.mapper1); assertNoMapperExistFor(this.metadata1); } @Test public void testLookupCache() throws Exception { bind(this.mapper1); assertMetadataHasMappers(this.metadata1, this.mapper1); assertMetadataHasAnnotations(this.metadata1, this.annotation1); assertMetadataHasMappers(this.metadata1, this.mapper1); assertMetadataHasAnnotations(this.metadata1, this.annotation1); verifyAnnotationsWhereQueriedOnlyOnce(); } @Test public void testAdditionAndRemovalOfMappersForSameAnnotationType() throws Exception { bind(this.mapper2); bind(this.mapper3); assertMetadataHasMappers(this.metadata2, this.mapper2, this.mapper3); unbind(mapper2); assertMetadataHasMappers(this.metadata2, this.mapper3); unbind(mapper3); assertMetadataHasMappers(this.metadata2); } private void verifyAnnotationsWhereQueriedOnlyOnce() { verify(this.metadata1).getAnnotations(); } private void unbind(AnnotatedFieldMapper mapper) { this.testee.unbind(mapper); } private void withMapperSupporting(AnnotatedFieldMapper mapper, Class<?> type) { doReturn(type).when(mapper).getFieldType(); } private void assertNoMapperExistFor(MappedFieldMetaData metadata) { assertThat(this.testee.get(metadata)).isEmpty(); } private void assertMetadataHasMappers(MappedFieldMetaData metadata, AnnotatedFieldMapper... mappers) { assertThat(this.testee.get(metadata)).extracting("mapper").containsOnly(mappers); } private void assertMetadataHasAnnotations(MappedFieldMetaData metadata, Annotation... annotations) { assertThat(this.testee.get(metadata)).extracting("annotation").containsOnly(annotations); } private void bind(AnnotatedFieldMapper mapper) { this.testee.bind(mapper); } }