[KEYCLOAK-17031] - ClientInvalidationClusterTest failing on Quarkus due to unreliable comparison
This commit is contained in:
parent
f4b5942c6c
commit
eb37a1ed69
3 changed files with 125 additions and 1 deletions
|
@ -35,6 +35,7 @@ import java.security.AccessController;
|
|||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -409,6 +410,20 @@ public class Reflections {
|
|||
return member;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the accessibility flag on the {@link AccessibleObject} to false as described in {@link
|
||||
* AccessibleObject#setAccessible(boolean)} within the context of a {link PrivilegedAction}.
|
||||
*
|
||||
* @param <A> member the accessible object type
|
||||
* @param member the accessible object
|
||||
*
|
||||
* @return the accessible object after the accessible flag has been altered
|
||||
*/
|
||||
public static <A extends AccessibleObject> A unsetAccessible(A member) {
|
||||
AccessController.doPrivileged(new UnSetAccessiblePrivilegedAction(member));
|
||||
return member;
|
||||
}
|
||||
|
||||
private static String buildSetFieldValueErrorMessage(Field field, Object obj, Object value) {
|
||||
return String.format("Exception setting [%s] field on object [%s] to value [%s]", field.getName(), obj, value);
|
||||
}
|
||||
|
@ -994,4 +1009,43 @@ public class Reflections {
|
|||
public static <T> T newInstance(final Class<?> type, final String fullQualifiedName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||
return (T) classForName(fullQualifiedName, type.getClassLoader()).newInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Resolves the type of items for a {@link Field} declared as a {@link List}.
|
||||
*
|
||||
* <p>This method will first try to check the parametrized type of the field type. If none is defined, it will try to infer
|
||||
* the type of items by looking at the value of the field for the given {@code instance}.
|
||||
*
|
||||
* <p>Make sure the field is accessible before invoking this method.
|
||||
*
|
||||
* @param field the field declared as {@link List}
|
||||
* @param instance the instance that should be used to obtain infer the type in case no parametrized type is found in the field.
|
||||
* @return if the field is not a {@link List}, it returns null. Otherwise the type of items of the list. If the type for items can not be inferred, the {@link Object} type is returned.
|
||||
* @throws IllegalAccessException in case it fails to obtain the value of the field from the {@code instance}
|
||||
*/
|
||||
public static Class<?> resolveListType(Field field, Object instance) throws IllegalAccessException {
|
||||
if (!List.class.isAssignableFrom(field.getType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Type genericType = field.getGenericType();
|
||||
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
Type[] typeArguments = ParameterizedType.class.cast(genericType)
|
||||
.getActualTypeArguments();
|
||||
|
||||
if (typeArguments[0] instanceof Class) {
|
||||
return (Class<?>) typeArguments[0];
|
||||
}
|
||||
} else if (instance != null) {
|
||||
// just in case the field is not parametrized
|
||||
List item = List.class.cast(field.get(instance));
|
||||
|
||||
if (!item.isEmpty()) {
|
||||
return item.get(0).getClass();
|
||||
}
|
||||
}
|
||||
|
||||
return Object.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* 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.keycloak.common.util.reflections;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
/**
|
||||
* A {@link PrivilegedAction} that calls {@link AccessibleObject#setAccessible(boolean)}
|
||||
*/
|
||||
public class UnSetAccessiblePrivilegedAction implements PrivilegedAction<Void> {
|
||||
|
||||
private final AccessibleObject member;
|
||||
|
||||
public UnSetAccessiblePrivilegedAction(AccessibleObject member) {
|
||||
this.member = member;
|
||||
}
|
||||
|
||||
public Void run() {
|
||||
if (member.isAccessible()) {
|
||||
member.setAccessible(false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -9,10 +9,15 @@ import org.junit.Test;
|
|||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.ContainerInfo;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.keycloak.common.util.reflections.Reflections.resolveListType;
|
||||
import static org.keycloak.common.util.reflections.Reflections.setAccessible;
|
||||
import static org.keycloak.common.util.reflections.Reflections.unsetAccessible;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -132,7 +137,8 @@ public abstract class AbstractInvalidationClusterTest<T, TR> extends AbstractClu
|
|||
boolean entityDiffers = false;
|
||||
for (ContainerInfo survivorNode : getCurrentSurvivorNodes()) {
|
||||
T testEntityOnSurvivorNode = readEntity(testEntityOnFailNode, survivorNode);
|
||||
if (EqualsBuilder.reflectionEquals(testEntityOnSurvivorNode, testEntityOnFailNode, excludedComparisonFields)) {
|
||||
|
||||
if (EqualsBuilder.reflectionEquals(sortFields(testEntityOnSurvivorNode), sortFields(testEntityOnFailNode), excludedComparisonFields)) {
|
||||
log.info(String.format("Verification of %s on survivor %s PASSED", getEntityType(testEntityOnFailNode), survivorNode));
|
||||
} else {
|
||||
entityDiffers = true;
|
||||
|
@ -149,6 +155,29 @@ public abstract class AbstractInvalidationClusterTest<T, TR> extends AbstractClu
|
|||
assertFalse(entityDiffers);
|
||||
}
|
||||
|
||||
private T sortFields(T entity) {
|
||||
for (Field field : entity.getClass().getDeclaredFields()) {
|
||||
try {
|
||||
Class<?> type = resolveListType(field, entity);
|
||||
|
||||
if (type != null && Comparable.class.isAssignableFrom(type)) {
|
||||
setAccessible(field);
|
||||
Object value = field.get(entity);
|
||||
|
||||
if (value != null) {
|
||||
Collections.sort((List) value);
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException cause) {
|
||||
throw new RuntimeException("Failed to sort field [" + field + "]", cause);
|
||||
} finally {
|
||||
unsetAccessible(field);
|
||||
}
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
private void assertEntityOnSurvivorNodesIsDeleted(T testEntityOnFailNode) {
|
||||
// check if deleted from all survivor nodes
|
||||
boolean entityExists = false;
|
||||
|
|
Loading…
Reference in a new issue