Skip to content

Commit

Permalink
[WIP] Proper support for @TestPropertySource w/o validation
Browse files Browse the repository at this point in the history
This commit also includes several experiments that will be later
deleted. These will remain here in the commit history so that they do
not get lost in case parts of them are needed at a later date.

See gh-19930
  • Loading branch information
sbrannen committed Oct 12, 2020
1 parent a71df12 commit c09cc2c
Show file tree
Hide file tree
Showing 7 changed files with 936 additions and 430 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2020 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.
Expand All @@ -16,6 +16,10 @@

package org.springframework.test.context.support;

import java.util.Arrays;

import org.springframework.core.style.ToStringCreator;
import org.springframework.lang.Nullable;
import org.springframework.test.context.TestPropertySource;
import org.springframework.util.Assert;

Expand Down Expand Up @@ -76,4 +80,55 @@ String[] getProperties() {
return this.properties;
}

/**
* Determine if the supplied object is equal to this {@code MergedTestPropertySources}
* instance by comparing both object's {@linkplain #getLocations() locations}
* and {@linkplain #getProperties() properties}.
* @since 5.3
*/
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (other == null || other.getClass() != getClass()) {
return false;
}

MergedTestPropertySources that = (MergedTestPropertySources) other;
if (!Arrays.equals(this.locations, that.locations)) {
return false;
}
if (!Arrays.equals(this.properties, that.properties)) {
return false;
}

return true;
}

/**
* Generate a unique hash code for all properties of this
* {@code MergedTestPropertySources} instance.
* @since 5.3
*/
@Override
public int hashCode() {
int result = Arrays.hashCode(this.locations);
result = 31 * result + Arrays.hashCode(this.properties);
return result;
}

/**
* Provide a String representation of this {@code MergedTestPropertySources}
* instance.
* @since 5.3
*/
@Override
public String toString() {
return new ToStringCreator(this)
.append("locations", Arrays.toString(this.locations))
.append("properties", Arrays.toString(this.properties))
.toString();
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
Expand Down Expand Up @@ -155,7 +155,8 @@ private void mergePropertiesAndLocations(MergedAnnotation<TestPropertySource> an
* @param elements the elements to add to the list
*/
private void addAll(boolean prepend, List<String> list, String... elements) {
list.addAll((prepend ? 0 : list.size()), Arrays.asList(elements));
// list.addAll((prepend ? 0 : list.size()), Arrays.asList(elements));
list.addAll(Arrays.asList(elements));
}

private String detectDefaultPropertiesFile(MergedAnnotation<TestPropertySource> annotation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@

import java.io.IOException;
import java.io.StringReader;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -75,35 +77,150 @@ public abstract class TestPropertySourceUtils {

static MergedTestPropertySources buildMergedTestPropertySources(Class<?> testClass) {
SearchStrategy searchStrategy = MetaAnnotationUtils.getSearchStrategy(testClass);
MergedAnnotations mergedAnnotations = MergedAnnotations.from(testClass, searchStrategy);
return (mergedAnnotations.isPresent(TestPropertySource.class) ?
// MergedAnnotations mergedAnnotations = MergedAnnotations.from(testClass, searchStrategy);
List<MergedAnnotation<TestPropertySource>> mergedAnnotations = findRepeatableAnnotations(testClass, TestPropertySource.class);
// MergedTestPropertySources mergedTestPropertySources = mergedAnnotations.isPresent(TestPropertySource.class) ?
// mergeTestPropertySources(mergedAnnotations, searchStrategy) :
// MergedTestPropertySources.empty();
// MergedTestPropertySources mergedTestPropertySources_XXX = buildMergedTestPropertySources_XXX(testClass);

MergedTestPropertySources mergedTestPropertySources = !mergedAnnotations.isEmpty() ?
mergeTestPropertySources(mergedAnnotations, searchStrategy) :
MergedTestPropertySources.empty());
MergedTestPropertySources.empty();


// System.err.println("OLD: " + mergedTestPropertySources);
// System.err.println("NEW: " + mergedTestPropertySources_XXX);
// if (!mergedTestPropertySources.equals(mergedTestPropertySources_XXX)) {
// System.err.println(">>> NOT equal");
// }
// System.err.println("============================================================================");
return mergedTestPropertySources;
}

static MergedTestPropertySources buildMergedTestPropertySources_XXX(Class<?> testClass) {

// // Set<TestPropertySource> annotations = new LinkedHashSet<>();
// RepeatableAnnotationDescriptor<TestPropertySource> descriptor =
// MetaAnnotationUtils.findRepeatableAnnotationDescriptor(testClass, TestPropertySource.class);
// while (descriptor != null) {
// // annotations.addAll(descriptor.findAllLocalMergedAnnotations());
// System.err.println(descriptor.getDeclaringClass().getSimpleName() + " : " + Arrays.toString(descriptor.getAnnotations()));
// descriptor = descriptor.next();
// }

// List<MergedAnnotation<TestPropertySource>> annotations = new ArrayList<>();
// List<MergedAnnotation<TestPropertySource>> currentAnnotations = findRepeatableAnnotations(testClass, TestPropertySource.class);
// annotations.forEach(anno -> System.err.println(anno.synthesize()));


SearchStrategy searchStrategy = MetaAnnotationUtils.getSearchStrategy(testClass);

// MergedAnnotations mergedAnnotations = MergedAnnotations.from(testClass, searchStrategy);
System.err.println("==================================================================");
List<MergedAnnotation<TestPropertySource>> mergedAnnotations = findRepeatableAnnotations(testClass, TestPropertySource.class);
// .forEach(annotation -> System.err.println(">>> " + declaringClass(annotation).getSimpleName() + " : " + annotation.synthesize()));
System.err.println("==================================================================");

// MergedTestPropertySources mergedTestPropertySources = mergedAnnotations.isPresent(TestPropertySource.class) ?
// mergeTestPropertySources(mergedAnnotations, searchStrategy) :
// MergedTestPropertySources.empty();
MergedTestPropertySources mergedTestPropertySources = !mergedAnnotations.isEmpty() ?
mergeTestPropertySources(mergedAnnotations, searchStrategy) :
MergedTestPropertySources.empty();
return mergedTestPropertySources;
}

static <T extends Annotation> List<MergedAnnotation<T>> findRepeatableAnnotations(Class<?> clazz, Class<T> annotationType) {
List<List<MergedAnnotation<T>>> listOfLists = new ArrayList<>();
findRepeatableAnnotations(clazz, annotationType, listOfLists, 0);
// list = list.stream()
// .sorted(highAggregateIndexesFirst())
// .collect(Collectors.toList());
// Collections.reverse(listOfLists);
listOfLists.forEach(list -> list.forEach(annotation -> System.err.println(">>> " + declaringClass(annotation).getSimpleName() + " : " + annotation.synthesize())));
List<MergedAnnotation<T>> result = new ArrayList<>();
listOfLists.forEach(result::addAll);
return result;
}

static <T extends Annotation> void findRepeatableAnnotations(
Class<?> clazz, Class<T> annotationType, List<List<MergedAnnotation<T>>> listOfLists, int aggregateIndex) {

MergedAnnotations.from(clazz, SearchStrategy.DIRECT)
.stream(annotationType)
.sorted(highMetaDistancesFirst())
// .sorted(highAggregateIndexesFirst())
// .forEach(list::add);
.forEach(annotation -> {
List<MergedAnnotation<T>> current = null;
if (listOfLists.size() < aggregateIndex + 1) {
current = new ArrayList<>();
listOfLists.add(current);
}
else {
current = listOfLists.get(aggregateIndex);
}
current.add(0, annotation);
});

// Declared on an interface?
for (Class<?> ifc : clazz.getInterfaces()) {
findRepeatableAnnotations(ifc, annotationType, listOfLists, aggregateIndex + 1);
}

// Declared on a superclass?
Class<?> superclass = clazz.getSuperclass();
if (superclass != null & superclass != Object.class) {
findRepeatableAnnotations(superclass, annotationType, listOfLists, aggregateIndex + 1);
}

// Declared on an enclosing class of an inner class?
if (MetaAnnotationUtils.searchEnclosingClass(clazz)) {
Class<?> enclosingClass = clazz.getEnclosingClass();
if (enclosingClass != null) {
findRepeatableAnnotations(enclosingClass, annotationType, listOfLists, aggregateIndex + 1);
}
}
}

private static <A extends Annotation> Comparator<MergedAnnotation<A>> highMetaDistancesFirst() {
return Comparator.<MergedAnnotation<A>> comparingInt(MergedAnnotation::getDistance).reversed();
}

private static <A extends Annotation> Comparator<MergedAnnotation<A>> highAggregateIndexesFirst() {
return Comparator.<MergedAnnotation<A>> comparingInt(
MergedAnnotation::getAggregateIndex).reversed();
}

private static MergedTestPropertySources mergeTestPropertySources(
MergedAnnotations mergedAnnotations, SearchStrategy searchStrategy) {
List<MergedAnnotation<TestPropertySource>> mergedAnnotations, SearchStrategy searchStrategy) {

List<TestPropertySourceAttributes> attributesList = resolveTestPropertySourceAttributes(mergedAnnotations, searchStrategy);
return new MergedTestPropertySources(mergeLocations(attributesList), mergeProperties(attributesList));
}

private static List<TestPropertySourceAttributes> resolveTestPropertySourceAttributes(
MergedAnnotations mergedAnnotations, SearchStrategy searchStrategy) {
List<MergedAnnotation<TestPropertySource>> mergedAnnotations, SearchStrategy searchStrategy) {

if (searchStrategy == SearchStrategy.TYPE_HIERARCHY_AND_ENCLOSING_CLASSES) {
return resolveTestPropertySourceAttributesWithEnclosingClassStrategy(mergedAnnotations);
}
// mergedAnnotations.stream(TestPropertySource.class)
// .forEach(annotation -> System.err.println(">>> " + declaringClass(annotation).getSimpleName() + " : " + annotation.synthesize()));
// System.err.println("----------------------------------------------------");

// if (searchStrategy == SearchStrategy.TYPE_HIERARCHY_AND_ENCLOSING_CLASSES) {
// return resolveTestPropertySourceAttributesWithEnclosingClassStrategy(mergedAnnotations);
// }
// else default semantics
return resolveTestPropertySourceAttributesWithTypeHierarchyStrategy(mergedAnnotations);
}

private static List<TestPropertySourceAttributes> resolveTestPropertySourceAttributesWithTypeHierarchyStrategy(
MergedAnnotations mergedAnnotations) {
List<MergedAnnotation<TestPropertySource>> mergedAnnotations) {

List<TestPropertySourceAttributes> attributesList = new ArrayList<>();
mergedAnnotations.stream(TestPropertySource.class)
.forEach(annotation -> addOrMergeTestPropertySourceAttributes(attributesList, annotation));
// mergedAnnotations.stream(TestPropertySource.class)
// .forEach(annotation -> addOrMergeTestPropertySourceAttributes(attributesList, annotation));
mergedAnnotations.forEach(annotation -> addOrMergeTestPropertySourceAttributes(attributesList, annotation));
return attributesList;
}

Expand Down Expand Up @@ -156,18 +273,22 @@ private static Class<?> declaringClass(MergedAnnotation<?> mergedAnnotation) {
private static void addOrMergeTestPropertySourceAttributes(List<TestPropertySourceAttributes> attributesList,
MergedAnnotation<TestPropertySource> current) {

if (attributesList.isEmpty()) {
attributesList.add(new TestPropertySourceAttributes(current));
}
else {
TestPropertySourceAttributes previous = attributesList.get(attributesList.size() - 1);
if (previous.canMergeWith(current)) {
previous.mergeWith(current);
}
else {
attributesList.add(new TestPropertySourceAttributes(current));
}
}
TestPropertySourceAttributes attributes = new TestPropertySourceAttributes(current);
System.err.println(attributes);
attributesList.add(attributes);

// if (attributesList.isEmpty()) {
// attributesList.add(new TestPropertySourceAttributes(current));
// }
// else {
// TestPropertySourceAttributes previous = attributesList.get(attributesList.size() - 1);
// if (previous.canMergeWith(current)) {
// previous.mergeWith(current);
// }
// else {
// attributesList.add(new TestPropertySourceAttributes(current));
// }
// }
}

private static String[] mergeLocations(List<TestPropertySourceAttributes> attributesList) {
Expand All @@ -179,6 +300,7 @@ private static String[] mergeLocations(List<TestPropertySourceAttributes> attrib
String[] locationsArray = TestContextResourceUtils.convertToClasspathResourcePaths(
attrs.getDeclaringClass(), true, attrs.getLocations());
locations.addAll(0, Arrays.asList(locationsArray));
// locations.addAll(Arrays.asList(locationsArray));
if (!attrs.isInheritLocations()) {
break;
}
Expand All @@ -194,6 +316,7 @@ private static String[] mergeProperties(List<TestPropertySourceAttributes> attri
}
String[] attrProps = attrs.getProperties();
properties.addAll(0, Arrays.asList(attrProps));
// properties.addAll(Arrays.asList(attrProps));
if (!attrs.isInheritProperties()) {
break;
}
Expand Down
Loading

0 comments on commit c09cc2c

Please sign in to comment.