Browse Source
The `RuntimeHints` API allows to describe hints for the reflection, proxies and resources behavior at runtime. The need for a particular behavior can be covered by several types of hints, at different levels. This knowledge can be important in several cases: * before contributing additional hints, infrastructure can check if an existing hint already covers the behavior * this can be used in test suites and test infrastructure This commit adds a new RuntimeHintsPredicates that generates `Predicate` instances for testing `RuntimeHints` against a desired runtime behavior for reflection, resources or proxies. Closes gh-28555pull/28613/head
13 changed files with 1318 additions and 55 deletions
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
/* |
||||
* Copyright 2002-2022 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 |
||||
* |
||||
* https://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.aot.hint; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.function.Predicate; |
||||
|
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* Generator of {@link ProxyHints} predicates, testing whether the given hints |
||||
* match the expected behavior for proxies. |
||||
* @author Brian Clozel |
||||
* @since 6.0 |
||||
*/ |
||||
public class ProxyHintsPredicates { |
||||
|
||||
ProxyHintsPredicates() { |
||||
} |
||||
|
||||
/** |
||||
* Return a predicate that checks whether a {@link org.springframework.aot.hint.JdkProxyHint} |
||||
* is registered for the given interfaces. |
||||
* <p>Note that the order in which interfaces are given matters. |
||||
* @param interfaces the proxied interfaces |
||||
* @return the {@link RuntimeHints} predicate |
||||
* @see java.lang.reflect.Proxy |
||||
*/ |
||||
public Predicate<RuntimeHints> forInterfaces(Class<?>... interfaces) { |
||||
Assert.notEmpty(interfaces, "'interfaces' should not be empty"); |
||||
return forInterfaces(Arrays.stream(interfaces).map(TypeReference::of).toArray(TypeReference[]::new)); |
||||
} |
||||
|
||||
/** |
||||
* Return a predicate that checks whether a {@link org.springframework.aot.hint.JdkProxyHint} |
||||
* is registered for the given interfaces. |
||||
* <p>Note that the order in which interfaces are given matters. |
||||
* @param interfaces the proxied interfaces as type references |
||||
* @return the {@link RuntimeHints} predicate |
||||
* @see java.lang.reflect.Proxy |
||||
*/ |
||||
public Predicate<RuntimeHints> forInterfaces(TypeReference... interfaces) { |
||||
Assert.notEmpty(interfaces, "'interfaces' should not be empty"); |
||||
return hints -> hints.proxies().jdkProxies().anyMatch(proxyHint -> |
||||
proxyHint.getProxiedInterfaces().equals(Arrays.asList(interfaces))); |
||||
} |
||||
} |
||||
@ -0,0 +1,380 @@
@@ -0,0 +1,380 @@
|
||||
/* |
||||
* Copyright 2002-2022 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 |
||||
* |
||||
* https://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.aot.hint; |
||||
|
||||
import java.lang.reflect.Constructor; |
||||
import java.lang.reflect.Executable; |
||||
import java.lang.reflect.Field; |
||||
import java.lang.reflect.Method; |
||||
import java.lang.reflect.Modifier; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
import java.util.function.Predicate; |
||||
|
||||
import org.springframework.core.MethodIntrospector; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ReflectionUtils; |
||||
|
||||
/** |
||||
* Generator of {@link ReflectionHints} predicates, testing whether the given hints |
||||
* match the expected behavior for reflection. |
||||
* @author Brian Clozel |
||||
* @since 6.0 |
||||
*/ |
||||
public class ReflectionHintsPredicates { |
||||
|
||||
ReflectionHintsPredicates() { |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Return a predicate that checks whether a reflection hint is registered for the given type. |
||||
* <p>The returned type exposes additional methods that refine the predicate behavior. |
||||
* @param typeReference the type |
||||
* @return the {@link RuntimeHints} predicate |
||||
*/ |
||||
public TypeHintPredicate onType(TypeReference typeReference) { |
||||
Assert.notNull(typeReference, "'typeReference' should not be null"); |
||||
return new TypeHintPredicate(typeReference); |
||||
} |
||||
|
||||
/** |
||||
* Return a predicate that checks whether a reflection hint is registered for the given type. |
||||
* <p>The returned type exposes additional methods that refine the predicate behavior. |
||||
* @param type the type |
||||
* @return the {@link RuntimeHints} predicate |
||||
*/ |
||||
public TypeHintPredicate onType(Class<?> type) { |
||||
Assert.notNull(type, "'type' should not be null"); |
||||
return new TypeHintPredicate(TypeReference.of(type)); |
||||
} |
||||
|
||||
/** |
||||
* Return a predicate that checks whether a reflection hint is registered for the given constructor. |
||||
* By default, both introspection and invocation hints match. |
||||
* <p>The returned type exposes additional methods that refine the predicate behavior. |
||||
* @param constructor the constructor |
||||
* @return the {@link RuntimeHints} predicate |
||||
*/ |
||||
public ConstructorHintPredicate onConstructor(Constructor<?> constructor) { |
||||
Assert.notNull(constructor, "'constructor' should not be null"); |
||||
return new ConstructorHintPredicate(constructor); |
||||
} |
||||
|
||||
/** |
||||
* Return a predicate that checks whether a reflection hint is registered for the given method. |
||||
* By default, both introspection and invocation hints match. |
||||
* <p>The returned type exposes additional methods that refine the predicate behavior. |
||||
* @param method the method |
||||
* @return the {@link RuntimeHints} predicate |
||||
*/ |
||||
public MethodHintPredicate onMethod(Method method) { |
||||
Assert.notNull(method, "'method' should not be null"); |
||||
return new MethodHintPredicate(method); |
||||
} |
||||
|
||||
/** |
||||
* Return a predicate that checks whether a reflection hint is registered for the method that matches the given selector. |
||||
* This looks up a method on the given type with the expected name, if unique. |
||||
* By default, both introspection and invocation hints match. |
||||
* <p>The returned type exposes additional methods that refine the predicate behavior. |
||||
* @param type the type holding the method |
||||
* @param methodName the method name |
||||
* @return the {@link RuntimeHints} predicate |
||||
* @throws IllegalArgumentException if the method cannot be found or if multiple methods are found with the same name. |
||||
*/ |
||||
public MethodHintPredicate onMethod(Class<?> type, String methodName) { |
||||
Assert.notNull(type, "'type' should not be null"); |
||||
Assert.hasText(methodName, "'methodName' should not be null"); |
||||
return new MethodHintPredicate(getMethod(type, methodName)); |
||||
} |
||||
|
||||
private Method getMethod(Class<?> type, String methodName) { |
||||
ReflectionUtils.MethodFilter selector = method -> methodName.equals(method.getName()); |
||||
Set<Method> methods = MethodIntrospector.selectMethods(type, selector); |
||||
if (methods.size() == 1) { |
||||
return methods.iterator().next(); |
||||
} |
||||
else if (methods.size() > 1) { |
||||
throw new IllegalArgumentException(String.format("Found multiple methods named '%s' on class %s", methodName, type.getName())); |
||||
} |
||||
else { |
||||
throw new IllegalArgumentException("No method named '" + methodName + "' on class " + type.getName()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Return a predicate that checks whether a reflection hint is registered for the field that matches the given selector. |
||||
* This looks up a field on the given type with the expected name, if present. |
||||
* By default, unsafe or write access are not considered. |
||||
* <p>The returned type exposes additional methods that refine the predicate behavior. |
||||
* @param type the type holding the field |
||||
* @param fieldName the field name |
||||
* @return the {@link RuntimeHints} predicate |
||||
* @throws IllegalArgumentException if a field cannot be found with the given name. |
||||
*/ |
||||
public FieldHintPredicate onField(Class<?> type, String fieldName) { |
||||
Assert.notNull(type, "'type' should not be null"); |
||||
Assert.hasText(fieldName, "'fieldName' should not be empty"); |
||||
Field field = ReflectionUtils.findField(type, fieldName); |
||||
if (field == null) { |
||||
throw new IllegalArgumentException("No field named '" + fieldName + "' on class " + type.getName()); |
||||
} |
||||
return new FieldHintPredicate(field); |
||||
} |
||||
|
||||
/** |
||||
* Return a predicate that checks whether a reflection hint is registered for the given field. |
||||
* By default, unsafe or write access are not considered. |
||||
* <p>The returned type exposes additional methods that refine the predicate behavior. |
||||
* @param field the field |
||||
* @return the {@link RuntimeHints} predicate |
||||
*/ |
||||
public FieldHintPredicate onField(Field field) { |
||||
Assert.notNull(field, "'field' should not be null"); |
||||
return new FieldHintPredicate(field); |
||||
} |
||||
|
||||
public static class TypeHintPredicate implements Predicate<RuntimeHints> { |
||||
|
||||
private final TypeReference type; |
||||
|
||||
TypeHintPredicate(TypeReference type) { |
||||
this.type = type; |
||||
} |
||||
|
||||
@Nullable |
||||
private TypeHint getTypeHint(RuntimeHints hints) { |
||||
return hints.reflection().getTypeHint(this.type); |
||||
} |
||||
|
||||
@Override |
||||
public boolean test(RuntimeHints hints) { |
||||
return getTypeHint(hints) != null; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Refine the current predicate to only match if the given {@link MemberCategory} is present. |
||||
* @param memberCategory the member category |
||||
* @return the refined {@link RuntimeHints} predicate |
||||
*/ |
||||
public Predicate<RuntimeHints> withMemberCategory(MemberCategory memberCategory) { |
||||
Assert.notNull(memberCategory, "'memberCategory' should not be null"); |
||||
return this.and(hints -> getTypeHint(hints).getMemberCategories().contains(memberCategory)); |
||||
} |
||||
|
||||
/** |
||||
* Refine the current predicate to match if any of the given {@link MemberCategory categories} is present. |
||||
* @param memberCategories the member categories |
||||
* @return the refined {@link RuntimeHints} predicate |
||||
*/ |
||||
public Predicate<RuntimeHints> withAnyMemberCategory(MemberCategory... memberCategories) { |
||||
Assert.notEmpty(memberCategories, "'memberCategories' should not be empty"); |
||||
return this.and(hints -> Arrays.stream(memberCategories) |
||||
.anyMatch(memberCategory -> getTypeHint(hints).getMemberCategories().contains(memberCategory))); |
||||
} |
||||
|
||||
} |
||||
|
||||
public abstract static class ExecutableHintPredicate<T extends Executable> implements Predicate<RuntimeHints> { |
||||
|
||||
protected final T executable; |
||||
|
||||
protected ExecutableMode executableMode = ExecutableMode.INTROSPECT; |
||||
|
||||
ExecutableHintPredicate(T executable) { |
||||
this.executable = executable; |
||||
} |
||||
|
||||
/** |
||||
* Refine the current predicate to match for reflection introspection on the current type. |
||||
* @return the refined {@link RuntimeHints} predicate |
||||
*/ |
||||
public ExecutableHintPredicate<T> introspect() { |
||||
this.executableMode = ExecutableMode.INTROSPECT; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Refine the current predicate to match for reflection invocation on the current type. |
||||
* @return the refined {@link RuntimeHints} predicate |
||||
*/ |
||||
public ExecutableHintPredicate<T> invoke() { |
||||
this.executableMode = ExecutableMode.INVOKE; |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public boolean test(RuntimeHints runtimeHints) { |
||||
return (new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass())) |
||||
.withAnyMemberCategory(getPublicMemberCategories()) |
||||
.and(hints -> Modifier.isPublic(this.executable.getModifiers()))) |
||||
.or(new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass())).withAnyMemberCategory(getDeclaredMemberCategories())) |
||||
.or(exactMatch()).test(runtimeHints); |
||||
} |
||||
|
||||
abstract MemberCategory[] getPublicMemberCategories(); |
||||
|
||||
abstract MemberCategory[] getDeclaredMemberCategories(); |
||||
|
||||
abstract Predicate<RuntimeHints> exactMatch(); |
||||
|
||||
/** |
||||
* Indicate whether the first {@code ExecutableHint} covers the reflection needs for the other one. |
||||
* For that, both hints must apply to the same member (same type, name and parameters) |
||||
* and the configured {@code ExecutableMode} of the first must cover the second. |
||||
*/ |
||||
static boolean includes(ExecutableHint hint, ExecutableHint other) { |
||||
return hint.getName().equals(other.getName()) |
||||
&& hint.getParameterTypes().equals(other.getParameterTypes()) |
||||
&& (hint.getModes().contains(ExecutableMode.INVOKE) |
||||
|| !other.getModes().contains(ExecutableMode.INVOKE)); |
||||
} |
||||
} |
||||
|
||||
public static class ConstructorHintPredicate extends ExecutableHintPredicate<Constructor<?>> { |
||||
|
||||
ConstructorHintPredicate(Constructor<?> constructor) { |
||||
super(constructor); |
||||
} |
||||
|
||||
MemberCategory[] getPublicMemberCategories() { |
||||
if (this.executableMode == ExecutableMode.INTROSPECT) { |
||||
return new MemberCategory[] {MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS, |
||||
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS}; |
||||
} |
||||
return new MemberCategory[] {MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS}; |
||||
} |
||||
|
||||
MemberCategory[] getDeclaredMemberCategories() { |
||||
if (this.executableMode == ExecutableMode.INTROSPECT) { |
||||
return new MemberCategory[] {MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS, |
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS}; |
||||
} |
||||
return new MemberCategory[] {MemberCategory.INVOKE_DECLARED_CONSTRUCTORS}; |
||||
} |
||||
|
||||
@Override |
||||
Predicate<RuntimeHints> exactMatch() { |
||||
return hints -> hints.reflection().getTypeHint(this.executable.getDeclaringClass()).constructors().anyMatch(executableHint -> { |
||||
List<TypeReference> parameters = Arrays.stream(this.executable.getParameterTypes()).map(TypeReference::of).toList(); |
||||
ExecutableHint syntheticHint = ExecutableHint.ofConstructor(parameters) |
||||
.setModes(this.executableMode).build(); |
||||
return includes(executableHint, syntheticHint); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
public static class MethodHintPredicate extends ExecutableHintPredicate<Method> { |
||||
|
||||
|
||||
MethodHintPredicate(Method method) { |
||||
super(method); |
||||
} |
||||
|
||||
MemberCategory[] getPublicMemberCategories() { |
||||
if (this.executableMode == ExecutableMode.INTROSPECT) { |
||||
return new MemberCategory[] {MemberCategory.INTROSPECT_PUBLIC_METHODS, |
||||
MemberCategory.INVOKE_PUBLIC_METHODS}; |
||||
} |
||||
return new MemberCategory[] {MemberCategory.INVOKE_PUBLIC_METHODS}; |
||||
} |
||||
|
||||
MemberCategory[] getDeclaredMemberCategories() { |
||||
|
||||
if (this.executableMode == ExecutableMode.INTROSPECT) { |
||||
return new MemberCategory[] {MemberCategory.INTROSPECT_DECLARED_METHODS, |
||||
MemberCategory.INVOKE_DECLARED_METHODS}; |
||||
} |
||||
return new MemberCategory[] {MemberCategory.INVOKE_DECLARED_METHODS}; |
||||
} |
||||
|
||||
@Override |
||||
Predicate<RuntimeHints> exactMatch() { |
||||
return hints -> (hints.reflection().getTypeHint(this.executable.getDeclaringClass()) != null) && |
||||
hints.reflection().getTypeHint(this.executable.getDeclaringClass()).methods().anyMatch(executableHint -> { |
||||
List<TypeReference> parameters = Arrays.stream(this.executable.getParameterTypes()).map(TypeReference::of).toList(); |
||||
ExecutableHint syntheticHint = ExecutableHint.ofMethod(this.executable.getName(), parameters) |
||||
.setModes(this.executableMode).build(); |
||||
return includes(executableHint, syntheticHint); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
public static class FieldHintPredicate implements Predicate<RuntimeHints> { |
||||
|
||||
private final Field field; |
||||
|
||||
private boolean allowWrite; |
||||
|
||||
private boolean allowUnsafeAccess; |
||||
|
||||
FieldHintPredicate(Field field) { |
||||
this.field = field; |
||||
} |
||||
|
||||
/** |
||||
* Refine the current predicate to match if write access is allowed on the field. |
||||
* @return the refined {@link RuntimeHints} predicate |
||||
* @see FieldHint#isAllowWrite() |
||||
*/ |
||||
public FieldHintPredicate allowWrite() { |
||||
this.allowWrite = true; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Refine the current predicate to match if unsafe access is allowed on the field. |
||||
* @return the refined {@link RuntimeHints} predicate |
||||
* @see FieldHint#isAllowUnsafeAccess() () |
||||
*/ |
||||
public FieldHintPredicate allowUnsafeAccess() { |
||||
this.allowUnsafeAccess = true; |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public boolean test(RuntimeHints runtimeHints) { |
||||
TypeHint typeHint = runtimeHints.reflection().getTypeHint(this.field.getDeclaringClass()); |
||||
if (typeHint == null) { |
||||
return false; |
||||
} |
||||
return memberCategoryMatch(typeHint) || exactMatch(typeHint); |
||||
} |
||||
|
||||
private boolean memberCategoryMatch(TypeHint typeHint) { |
||||
if (Modifier.isPublic(this.field.getModifiers())) { |
||||
return typeHint.getMemberCategories().contains(MemberCategory.PUBLIC_FIELDS) |
||||
|| typeHint.getMemberCategories().contains(MemberCategory.DECLARED_FIELDS); |
||||
} |
||||
else { |
||||
return typeHint.getMemberCategories().contains(MemberCategory.DECLARED_FIELDS); |
||||
} |
||||
} |
||||
|
||||
private boolean exactMatch(TypeHint typeHint) { |
||||
return typeHint.fields().anyMatch(fieldHint -> |
||||
this.field.getName().equals(fieldHint.getName()) |
||||
&& (!this.allowWrite || this.allowWrite == fieldHint.isAllowWrite()) |
||||
&& (!this.allowUnsafeAccess || this.allowUnsafeAccess == fieldHint.isAllowUnsafeAccess())); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,92 @@
@@ -0,0 +1,92 @@
|
||||
/* |
||||
* Copyright 2002-2022 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 |
||||
* |
||||
* https://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.aot.hint; |
||||
|
||||
import java.util.function.Predicate; |
||||
import java.util.regex.Pattern; |
||||
|
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ConcurrentLruCache; |
||||
|
||||
/** |
||||
* Generator of {@link ResourceHints} predicates, testing whether the given hints |
||||
* match the expected behavior for resources. |
||||
* @author Brian Clozel |
||||
* @since 6.0 |
||||
*/ |
||||
public class ResourceHintsPredicates { |
||||
|
||||
private static final ConcurrentLruCache<String, Pattern> CACHED_RESOURCE_PATTERNS = new ConcurrentLruCache<>(32, Pattern::compile); |
||||
|
||||
ResourceHintsPredicates() { |
||||
} |
||||
|
||||
/** |
||||
* Return a predicate that checks whether a resource hint is registered for the given bundle name. |
||||
* @param bundleName the resource bundle name |
||||
* @return the {@link RuntimeHints} predicate |
||||
*/ |
||||
public Predicate<RuntimeHints> forBundle(String bundleName) { |
||||
Assert.hasText(bundleName, "resource bundle name should not be empty"); |
||||
return runtimeHints -> runtimeHints.resources().resourceBundles() |
||||
.anyMatch(bundleHint -> bundleName.equals(bundleHint.getBaseName())); |
||||
} |
||||
|
||||
/** |
||||
* Return a predicate that checks whether a resource hint is registered for the given |
||||
* resource name, located in the given type's package. |
||||
* <p>For example, {@code forResource(org.example.MyClass, "myResource.txt")} |
||||
* will match for {@code "/org/example/myResource.txt"}. |
||||
* @param type the type's package where to look for the resource |
||||
* @param resourceName the resource name |
||||
* @return the {@link RuntimeHints} predicate |
||||
*/ |
||||
public Predicate<RuntimeHints> forResource(TypeReference type, String resourceName) { |
||||
String absoluteName = resolveAbsoluteResourceName(type, resourceName); |
||||
return forResource(absoluteName); |
||||
} |
||||
|
||||
private String resolveAbsoluteResourceName(TypeReference type, String resourceName) { |
||||
if (resourceName.startsWith("/")) { |
||||
return resourceName; |
||||
} |
||||
else { |
||||
return "/" + type.getPackageName().replace('.', '/') |
||||
+ "/" + resourceName; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Return a predicate that checks whether a resource hint is registered for |
||||
* the given resource name. |
||||
* @param resourceName the full resource name |
||||
* @return the {@link RuntimeHints} predicate |
||||
*/ |
||||
public Predicate<RuntimeHints> forResource(String resourceName) { |
||||
return hints -> hints.resources().resourcePatterns().reduce(ResourcePatternHints::merge) |
||||
.map(hint -> { |
||||
boolean isExcluded = hint.getExcludes().stream() |
||||
.anyMatch(excluded -> CACHED_RESOURCE_PATTERNS.get(excluded.getPattern()).matcher(resourceName).matches()); |
||||
if (isExcluded) { |
||||
return false; |
||||
} |
||||
return hint.getIncludes().stream() |
||||
.anyMatch(included -> CACHED_RESOURCE_PATTERNS.get(included.getPattern()).matcher(resourceName).matches()); |
||||
}).orElse(false); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,69 @@
@@ -0,0 +1,69 @@
|
||||
/* |
||||
* Copyright 2002-2022 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 |
||||
* |
||||
* https://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.aot.hint; |
||||
|
||||
/** |
||||
* Static generator of predicates that test whether the given {@link RuntimeHints} |
||||
* instance matches the expected behavior for reflection, resource or proxy generation. |
||||
* <p>This utility class can be used by {@link RuntimeHintsRegistrar} to conditionally |
||||
* register hints depending on what's present already. This can also be used as a |
||||
* testing utility for checking proper registration of hints: |
||||
* <pre class="code"> |
||||
* Predicate<RuntimeHints> predicate = RuntimeHintsPredicates.reflection().onMethod(MyClass.class, "someMethod").invoke(); |
||||
* assertThat(predicate).accepts(runtimeHints); |
||||
* </pre> |
||||
* @author Brian Clozel |
||||
* @since 6.0 |
||||
*/ |
||||
public abstract class RuntimeHintsPredicates { |
||||
|
||||
private static final ReflectionHintsPredicates reflection = new ReflectionHintsPredicates(); |
||||
|
||||
private static final ResourceHintsPredicates resource = new ResourceHintsPredicates(); |
||||
|
||||
private static final ProxyHintsPredicates proxies = new ProxyHintsPredicates(); |
||||
|
||||
|
||||
private RuntimeHintsPredicates() { |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Return a predicate generator for {@link ReflectionHints reflection hints}. |
||||
* @return the predicate generator |
||||
*/ |
||||
public static ReflectionHintsPredicates reflection() { |
||||
return reflection; |
||||
} |
||||
|
||||
/** |
||||
* Return a predicate generator for {@link ResourceHints resource hints}. |
||||
* @return the predicate generator |
||||
*/ |
||||
public static ResourceHintsPredicates resource() { |
||||
return resource; |
||||
} |
||||
|
||||
/** |
||||
* Return a predicate generator for {@link ProxyHints proxy hints}. |
||||
* @return the predicate generator |
||||
*/ |
||||
public static ProxyHintsPredicates proxies() { |
||||
return proxies; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,76 @@
@@ -0,0 +1,76 @@
|
||||
/* |
||||
* Copyright 2002-2022 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 |
||||
* |
||||
* https://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.aot.hint; |
||||
|
||||
import java.util.function.Predicate; |
||||
|
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
|
||||
/** |
||||
* Tests for {@link ProxyHintsPredicates}. |
||||
* |
||||
* @author Brian Clozel |
||||
*/ |
||||
class ProxyHintsPredicatesTests { |
||||
|
||||
private final ProxyHintsPredicates proxy = new ProxyHintsPredicates(); |
||||
|
||||
private RuntimeHints runtimeHints; |
||||
|
||||
@BeforeEach |
||||
void setup() { |
||||
this.runtimeHints = new RuntimeHints(); |
||||
} |
||||
|
||||
@Test |
||||
void shouldFailForEmptyInterfacesArray() { |
||||
assertThatThrownBy(() -> this.proxy.forInterfaces(new Class<?>[] {})).isInstanceOf(IllegalArgumentException.class); |
||||
} |
||||
|
||||
@Test |
||||
void proxyForInterfacesMatchesProxyHint() { |
||||
this.runtimeHints.proxies().registerJdkProxy(FirstTestInterface.class, SecondTestInterface.class); |
||||
assertPredicateMatches(this.proxy.forInterfaces(FirstTestInterface.class, SecondTestInterface.class)); |
||||
} |
||||
|
||||
@Test |
||||
void proxyForInterfacesDoesNotMatchProxyHintDifferentOrder() { |
||||
this.runtimeHints.proxies().registerJdkProxy(SecondTestInterface.class, FirstTestInterface.class); |
||||
assertPredicateDoesNotMatch(this.proxy.forInterfaces(FirstTestInterface.class, SecondTestInterface.class)); |
||||
} |
||||
|
||||
interface FirstTestInterface { |
||||
|
||||
} |
||||
|
||||
interface SecondTestInterface { |
||||
|
||||
} |
||||
|
||||
private void assertPredicateMatches(Predicate<RuntimeHints> predicate) { |
||||
assertThat(predicate.test(this.runtimeHints)).isTrue(); |
||||
} |
||||
|
||||
private void assertPredicateDoesNotMatch(Predicate<RuntimeHints> predicate) { |
||||
assertThat(predicate.test(this.runtimeHints)).isFalse(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,509 @@
@@ -0,0 +1,509 @@
|
||||
/* |
||||
* Copyright 2002-2022 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 |
||||
* |
||||
* https://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.aot.hint; |
||||
|
||||
|
||||
import java.lang.reflect.Constructor; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.function.Predicate; |
||||
|
||||
import org.junit.jupiter.api.BeforeAll; |
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
|
||||
/** |
||||
* Tests for {@link ReflectionHintsPredicates} |
||||
* |
||||
* @author Brian Clozel |
||||
*/ |
||||
class ReflectionHintsPredicatesTests { |
||||
|
||||
private static Constructor<?> privateConstructor; |
||||
|
||||
private static Constructor<?> publicConstructor; |
||||
|
||||
private final ReflectionHintsPredicates reflection = new ReflectionHintsPredicates(); |
||||
|
||||
private RuntimeHints runtimeHints; |
||||
|
||||
|
||||
@BeforeAll |
||||
static void setupAll() throws Exception { |
||||
privateConstructor = SampleClass.class.getDeclaredConstructor(String.class); |
||||
publicConstructor = SampleClass.class.getConstructor(); |
||||
} |
||||
|
||||
@BeforeEach |
||||
void setup() { |
||||
this.runtimeHints = new RuntimeHints(); |
||||
} |
||||
|
||||
// Reflection on type
|
||||
|
||||
@Test |
||||
void shouldFailForNullType() { |
||||
assertThatThrownBy(() -> reflection.onType((TypeReference) null)).isInstanceOf(IllegalArgumentException.class); |
||||
} |
||||
|
||||
@Test |
||||
void reflectionOnClassShouldMatchIntrospection() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> { |
||||
}); |
||||
assertPredicateMatches(reflection.onType(SampleClass.class)); |
||||
} |
||||
|
||||
@Test |
||||
void reflectionOnTypeReferenceShouldMatchIntrospection() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> { |
||||
}); |
||||
assertPredicateMatches(reflection.onType(TypeReference.of(SampleClass.class))); |
||||
} |
||||
|
||||
@Test |
||||
void reflectionOnDifferentClassShouldNotMatchIntrospection() { |
||||
this.runtimeHints.reflection().registerType(Integer.class, builder -> { |
||||
}); |
||||
assertPredicateDoesNotMatch(reflection.onType(TypeReference.of(SampleClass.class))); |
||||
} |
||||
|
||||
@Test |
||||
void typeWithMemberCategoryFailsWithNullCategory() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> builder.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS)); |
||||
assertThatThrownBy(() -> reflection.onType(SampleClass.class).withMemberCategory(null)).isInstanceOf(IllegalArgumentException.class); |
||||
} |
||||
|
||||
@Test |
||||
void typeWithMemberCategoryMatchesCategory() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> builder.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS)); |
||||
assertPredicateMatches(reflection.onType(SampleClass.class).withMemberCategory(MemberCategory.INTROSPECT_PUBLIC_METHODS)); |
||||
} |
||||
|
||||
@Test |
||||
void typeWithMemberCategoryDoesNotMatchOtherCategory() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> builder.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS)); |
||||
assertPredicateDoesNotMatch(reflection.onType(SampleClass.class).withMemberCategory(MemberCategory.INVOKE_PUBLIC_METHODS)); |
||||
} |
||||
|
||||
@Test |
||||
void typeWithAnyMemberCategoryFailsWithNullCategories() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> builder.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS)); |
||||
assertThatThrownBy(() -> reflection.onType(SampleClass.class).withAnyMemberCategory(new MemberCategory[]{})).isInstanceOf(IllegalArgumentException.class); |
||||
} |
||||
|
||||
@Test |
||||
void typeWithAnyMemberCategoryMatchesCategory() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> builder.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS)); |
||||
assertPredicateMatches(reflection.onType(SampleClass.class).withAnyMemberCategory(MemberCategory.INTROSPECT_PUBLIC_METHODS)); |
||||
} |
||||
|
||||
@Test |
||||
void typeWithAnyMemberCategoryDoesNotMatchOtherCategory() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, builder -> builder.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS)); |
||||
assertPredicateDoesNotMatch(reflection.onType(SampleClass.class).withAnyMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)); |
||||
} |
||||
|
||||
// Reflection on constructor
|
||||
|
||||
@Test |
||||
void constructorIntrospectionMatchesConstructorHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withConstructor(Collections.emptyList(), constructorHint -> { |
||||
})); |
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void constructorIntrospectionMatchesIntrospectPublicConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS)); |
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void constructorIntrospectionMatchesInvokePublicConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS)); |
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void constructorIntrospectionMatchesIntrospectDeclaredConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS)); |
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void constructorIntrospectionMatchesInvokeDeclaredConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)); |
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void constructorInvocationDoesNotMatchConstructorHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withConstructor(Collections.emptyList(), constructorHint -> { |
||||
})); |
||||
assertPredicateDoesNotMatch(reflection.onConstructor(publicConstructor).invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void constructorInvocationMatchesConstructorInvocationHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withConstructor(Collections.emptyList(), constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE))); |
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void constructorInvocationDoesNotMatchIntrospectPublicConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS)); |
||||
assertPredicateDoesNotMatch(reflection.onConstructor(publicConstructor).invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void constructorInvocationMatchesInvokePublicConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS)); |
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void constructorInvocationDoesNotMatchIntrospectDeclaredConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS)); |
||||
assertPredicateDoesNotMatch(reflection.onConstructor(publicConstructor).invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void constructorInvocationMatchesInvokeDeclaredConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)); |
||||
assertPredicateMatches(reflection.onConstructor(publicConstructor).invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void privateConstructorIntrospectionMatchesConstructorHint() { |
||||
List<TypeReference> parameterTypes = Collections.singletonList(TypeReference.of(String.class)); |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withConstructor(parameterTypes, constructorHint -> { |
||||
})); |
||||
assertPredicateMatches(reflection.onConstructor(privateConstructor).introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void privateConstructorIntrospectionDoesNotMatchIntrospectPublicConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS)); |
||||
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void privateConstructorIntrospectionDoesNotMatchInvokePublicConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS)); |
||||
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void privateConstructorIntrospectionMatchesIntrospectDeclaredConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS)); |
||||
assertPredicateMatches(reflection.onConstructor(privateConstructor).introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void privateConstructorIntrospectionMatchesInvokeDeclaredConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)); |
||||
assertPredicateMatches(reflection.onConstructor(privateConstructor).introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void privateConstructorInvocationDoesNotMatchConstructorHint() { |
||||
List<TypeReference> parameterTypes = Collections.singletonList(TypeReference.of(String.class)); |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withConstructor(parameterTypes, constructorHint -> { |
||||
})); |
||||
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void privateConstructorInvocationMatchesConstructorInvocationHint() { |
||||
List<TypeReference> parameterTypes = Collections.singletonList(TypeReference.of(String.class)); |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withConstructor(parameterTypes, constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE))); |
||||
assertPredicateMatches(reflection.onConstructor(privateConstructor).invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void privateConstructorInvocationDoesNotMatchIntrospectPublicConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS)); |
||||
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void privateConstructorInvocationDoesNotMatchInvokePublicConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS)); |
||||
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void privateConstructorInvocationDoesNotMatchIntrospectDeclaredConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS)); |
||||
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void privateConstructorInvocationMatchesInvokeDeclaredConstructors() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)); |
||||
assertPredicateMatches(reflection.onConstructor(privateConstructor).invoke()); |
||||
} |
||||
|
||||
// Reflection on method
|
||||
|
||||
@Test |
||||
void methodIntrospectionMatchesMethodHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("publicMethod", Collections.emptyList(), methodHint -> { |
||||
})); |
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void methodIntrospectionMatchesIntrospectPublicMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS)); |
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void methodIntrospectionMatchesInvokePublicMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); |
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void methodIntrospectionMatchesIntrospectDeclaredMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS)); |
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void methodIntrospectionMatchesInvokeDeclaredMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS)); |
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void methodInvocationDoesNotMatchMethodHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("publicMethod", Collections.emptyList(), methodHint -> { |
||||
})); |
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "publicMethod").invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void methodInvocationMatchesMethodInvocationHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("publicMethod", Collections.emptyList(), methodHint -> methodHint.withMode(ExecutableMode.INVOKE))); |
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void methodInvocationDoesNotMatchIntrospectPublicMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS)); |
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "publicMethod").invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void methodInvocationMatchesInvokePublicMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); |
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void methodInvocationDoesNotMatchIntrospectDeclaredMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS)); |
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "publicMethod").invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void methodInvocationMatchesInvokeDeclaredMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS)); |
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void privateMethodIntrospectionMatchesMethodHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("privateMethod", Collections.emptyList(), methodHint -> { |
||||
})); |
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void privateMethodIntrospectionDoesNotMatchIntrospectPublicMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS)); |
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void privateMethodIntrospectionDoesNotMatchInvokePublicMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); |
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void privateMethodIntrospectionMatchesIntrospectDeclaredMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS)); |
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void privateMethodIntrospectionMatchesInvokeDeclaredMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS)); |
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").introspect()); |
||||
} |
||||
|
||||
@Test |
||||
void privateMethodInvocationDoesNotMatchMethodHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("privateMethod", Collections.emptyList(), methodHint -> { |
||||
})); |
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void privateMethodInvocationMatchesMethodInvocationHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("privateMethod", Collections.emptyList(), methodHint -> methodHint.withMode(ExecutableMode.INVOKE))); |
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void privateMethodInvocationDoesNotMatchIntrospectPublicMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS)); |
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void privateMethodInvocationDoesNotMatchInvokePublicMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); |
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void privateMethodInvocationDoesNotMatchIntrospectDeclaredMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS)); |
||||
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").invoke()); |
||||
} |
||||
|
||||
@Test |
||||
void privateMethodInvocationMatchesInvokeDeclaredMethods() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS)); |
||||
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").invoke()); |
||||
} |
||||
|
||||
// Reflection on field
|
||||
|
||||
@Test |
||||
void shouldFailForMissingField() { |
||||
assertThatThrownBy(() -> reflection.onField(SampleClass.class, "missingField")).isInstanceOf(IllegalArgumentException.class); |
||||
} |
||||
|
||||
@Test |
||||
void fieldReflectionMatchesFieldHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField", fieldHint -> { |
||||
})); |
||||
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField")); |
||||
} |
||||
|
||||
@Test |
||||
void fieldWriteReflectionDoesNotMatchFieldHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField", fieldHint -> { |
||||
})); |
||||
assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "publicField").allowWrite()); |
||||
} |
||||
|
||||
@Test |
||||
void fieldUnsafeReflectionDoesNotMatchFieldHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField", fieldHint -> { |
||||
})); |
||||
assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "publicField").allowUnsafeAccess()); |
||||
} |
||||
|
||||
@Test |
||||
void fieldWriteReflectionMatchesFieldHintWithWrite() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> |
||||
typeHint.withField("publicField", fieldHint -> fieldHint.allowWrite(true))); |
||||
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField").allowWrite()); |
||||
} |
||||
|
||||
@Test |
||||
void fieldUnsafeReflectionMatchesFieldHintWithUnsafe() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, |
||||
typeHint -> typeHint.withField("publicField", fieldHint -> fieldHint.allowUnsafeAccess(true))); |
||||
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField").allowUnsafeAccess()); |
||||
} |
||||
|
||||
@Test |
||||
void fieldReflectionMatchesPublicFieldsHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.PUBLIC_FIELDS)); |
||||
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField")); |
||||
} |
||||
|
||||
@Test |
||||
void fieldReflectionMatchesDeclaredFieldsHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.DECLARED_FIELDS)); |
||||
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField")); |
||||
} |
||||
|
||||
@Test |
||||
void privateFieldReflectionMatchesFieldHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("privateField", fieldHint -> { |
||||
})); |
||||
assertPredicateMatches(reflection.onField(SampleClass.class, "privateField")); |
||||
} |
||||
|
||||
@Test |
||||
void privateFieldReflectionDoesNotMatchPublicFieldsHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.PUBLIC_FIELDS)); |
||||
assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "privateField")); |
||||
} |
||||
|
||||
@Test |
||||
void privateFieldReflectionMatchesDeclaredFieldsHint() { |
||||
this.runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMembers(MemberCategory.DECLARED_FIELDS)); |
||||
assertPredicateMatches(reflection.onField(SampleClass.class, "privateField")); |
||||
} |
||||
|
||||
|
||||
private void assertPredicateMatches(Predicate<RuntimeHints> predicate) { |
||||
assertThat(predicate).accepts(this.runtimeHints); |
||||
} |
||||
|
||||
private void assertPredicateDoesNotMatch(Predicate<RuntimeHints> predicate) { |
||||
assertThat(predicate).rejects(this.runtimeHints); |
||||
} |
||||
|
||||
|
||||
static class SampleClass { |
||||
|
||||
private String privateField; |
||||
|
||||
public String publicField; |
||||
|
||||
public SampleClass() { |
||||
|
||||
} |
||||
|
||||
private SampleClass(String message) { |
||||
|
||||
} |
||||
|
||||
public void publicMethod() { |
||||
|
||||
} |
||||
|
||||
private void privateMethod() { |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,88 @@
@@ -0,0 +1,88 @@
|
||||
/* |
||||
* Copyright 2002-2022 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 |
||||
* |
||||
* https://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.aot.hint; |
||||
|
||||
import java.util.function.Predicate; |
||||
|
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link ReflectionHintsPredicates}. |
||||
* |
||||
* @author Brian Clozel |
||||
*/ |
||||
class ResourceHintsPredicatesTests { |
||||
|
||||
private final ResourceHintsPredicates resources = new ResourceHintsPredicates(); |
||||
|
||||
private RuntimeHints runtimeHints; |
||||
|
||||
|
||||
@BeforeEach |
||||
void setup() { |
||||
this.runtimeHints = new RuntimeHints(); |
||||
} |
||||
|
||||
@Test |
||||
void resourcePatternMatchesResourceName() { |
||||
this.runtimeHints.resources().registerPattern("/test/spring.*"); |
||||
assertPredicateMatches(resources.forResource("/test/spring.properties")); |
||||
} |
||||
|
||||
@Test |
||||
void resourcePatternDoesNotMatchResourceName() { |
||||
this.runtimeHints.resources().registerPattern("/test/spring.*"); |
||||
assertPredicateDoesNotMatch(resources.forResource("/test/other.properties")); |
||||
} |
||||
|
||||
@Test |
||||
void resourcePatternMatchesTypeAndResourceName() { |
||||
this.runtimeHints.resources().registerPattern("/org/springframework/aot/hint/spring.*"); |
||||
assertPredicateMatches(resources.forResource(TypeReference.of(getClass()), "spring.properties")); |
||||
} |
||||
|
||||
@Test |
||||
void resourcePatternDoesNotMatchTypeAndResourceName() { |
||||
this.runtimeHints.resources().registerPattern("/spring.*"); |
||||
assertPredicateDoesNotMatch(resources.forResource(TypeReference.of(getClass()), "spring.properties")); |
||||
} |
||||
|
||||
@Test |
||||
void resourceBundleMatchesBundleName() { |
||||
this.runtimeHints.resources().registerResourceBundle("spring"); |
||||
assertPredicateMatches(resources.forBundle("spring")); |
||||
} |
||||
|
||||
@Test |
||||
void resourceBundleDoesNotMatchBundleName() { |
||||
this.runtimeHints.resources().registerResourceBundle("spring"); |
||||
assertPredicateDoesNotMatch(resources.forBundle("other")); |
||||
} |
||||
|
||||
|
||||
private void assertPredicateMatches(Predicate<RuntimeHints> predicate) { |
||||
assertThat(predicate.test(this.runtimeHints)).isTrue(); |
||||
} |
||||
|
||||
private void assertPredicateDoesNotMatch(Predicate<RuntimeHints> predicate) { |
||||
assertThat(predicate.test(this.runtimeHints)).isFalse(); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue