/*
* Copyright 2002-2016 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 org.springframework.core.annotation;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.annotation.AnnotationUtilsTests.ImplicitAliasesContextConfig;
import org.springframework.core.annotation.AnnotationUtilsTests.RequestMethod;
import org.springframework.core.annotation.AnnotationUtilsTests.WebMapping;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.core.annotation.AnnotationUtilsTests.*;
/**
* Unit tests for {@link MapAnnotationAttributeExtractor}.
*
* @author Sam Brannen
* @since 4.2.1
*/
@SuppressWarnings("serial")
public class MapAnnotationAttributeExtractorTests extends AbstractAliasAwareAnnotationAttributeExtractorTestCase {
@Before
public void clearCachesBeforeTests() {
AnnotationUtilsTests.clearCaches();
}
@Test
public void enrichAndValidateAttributesWithImplicitAliasesAndMinimalAttributes() {
Map<String, Object> attributes = new HashMap<>();
Map<String, Object> expectedAttributes = new HashMap<String, Object>() {{
put("groovyScript", "");
put("xmlFile", "");
put("value", "");
put("location1", "");
put("location2", "");
put("location3", "");
put("nonAliasedAttribute", "");
put("configClass", Object.class);
}};
assertEnrichAndValidateAttributes(attributes, expectedAttributes);
}
@Test
public void enrichAndValidateAttributesWithImplicitAliases() {
Map<String, Object> attributes = new HashMap<String, Object>() {{
put("groovyScript", "groovy!");
}};
Map<String, Object> expectedAttributes = new HashMap<String, Object>() {{
put("groovyScript", "groovy!");
put("xmlFile", "groovy!");
put("value", "groovy!");
put("location1", "groovy!");
put("location2", "groovy!");
put("location3", "groovy!");
put("nonAliasedAttribute", "");
put("configClass", Object.class);
}};
assertEnrichAndValidateAttributes(attributes, expectedAttributes);
}
@Test
public void enrichAndValidateAttributesWithSingleElementThatOverridesAnArray() {
// @formatter:off
Map<String, Object> attributes = new HashMap<String, Object>() {{
// Intentionally storing 'value' as a single String instead of an array.
// put("value", asArray("/foo"));
put("value", "/foo");
put("name", "test");
}};
Map<String, Object> expected = new HashMap<String, Object>() {{
put("value", asArray("/foo"));
put("path", asArray("/foo"));
put("name", "test");
put("method", new RequestMethod[0]);
}};
// @formatter:on
MapAnnotationAttributeExtractor extractor = new MapAnnotationAttributeExtractor(attributes, WebMapping.class, null);
Map<String, Object> enriched = extractor.getSource();
assertEquals("attribute map size", expected.size(), enriched.size());
expected.forEach((attr, expectedValue) -> assertThat("for attribute '" + attr + "'", enriched.get(attr), is(expectedValue)));
}
@SuppressWarnings("unchecked")
private void assertEnrichAndValidateAttributes(Map<String, Object> sourceAttributes, Map<String, Object> expected) {
Class<? extends Annotation> annotationType = ImplicitAliasesContextConfig.class;
// Since the ordering of attribute methods returned by the JVM is
// non-deterministic, we have to rig the attributeAliasesCache in AnnotationUtils
// so that the tests consistently fail in case enrichAndValidateAttributes() is
// buggy.
//
// Otherwise, these tests would intermittently pass even for an invalid
// implementation.
Map<Class<? extends Annotation>, MultiValueMap<String, String>> attributeAliasesCache =
(Map<Class<? extends Annotation>, MultiValueMap<String, String>>) AnnotationUtilsTests.getCache("attributeAliasesCache");
// Declare aliases in an order that will cause enrichAndValidateAttributes() to
// fail unless it considers all aliases in the set of implicit aliases.
MultiValueMap<String, String> aliases = new LinkedMultiValueMap<>();
aliases.put("xmlFile", Arrays.asList("value", "groovyScript", "location1", "location2", "location3"));
aliases.put("groovyScript", Arrays.asList("value", "xmlFile", "location1", "location2", "location3"));
aliases.put("value", Arrays.asList("xmlFile", "groovyScript", "location1", "location2", "location3"));
aliases.put("location1", Arrays.asList("xmlFile", "groovyScript", "value", "location2", "location3"));
aliases.put("location2", Arrays.asList("xmlFile", "groovyScript", "value", "location1", "location3"));
aliases.put("location3", Arrays.asList("xmlFile", "groovyScript", "value", "location1", "location2"));
attributeAliasesCache.put(annotationType, aliases);
MapAnnotationAttributeExtractor extractor = new MapAnnotationAttributeExtractor(sourceAttributes, annotationType, null);
Map<String, Object> enriched = extractor.getSource();
assertEquals("attribute map size", expected.size(), enriched.size());
expected.forEach((attr, expectedValue) -> assertThat("for attribute '" + attr + "'", enriched.get(attr), is(expectedValue)));
}
@Override
protected AnnotationAttributeExtractor<?> createExtractorFor(Class<?> clazz, String expected, Class<? extends Annotation> annotationType) {
Map<String, Object> attributes = Collections.singletonMap(expected, expected);
return new MapAnnotationAttributeExtractor(attributes, annotationType, clazz);
}
}