mirror of https://github.com/apache/jclouds.git
Moving the complex test for 93e2f24c62
into a separate file and allowing it to run in parallel with other tests
This commit is contained in:
parent
4c5cef1be4
commit
2a1fc363e0
|
@ -0,0 +1,140 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds licenses this file
|
||||
* to you 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.jclouds.reflect;
|
||||
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.cache.ForwardingLoadingCache.SimpleForwardingLoadingCache;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* Tests {@link Reflection2#method(Class, String, Class...)} where
|
||||
* the method overrides one in parent classes and so bridge methods
|
||||
* need to be ignored.
|
||||
*
|
||||
* This test has been separated out into a separate class as it requires
|
||||
* reflection to modify the internal static caches of {@code Reflection2}
|
||||
* and needs to perform cleanup to avoid affecting other tests.
|
||||
*
|
||||
* @author Andrew Phillips
|
||||
*/
|
||||
@Test
|
||||
public class Reflection2OverriddenMethodTest {
|
||||
private LoadingCache<TypeToken<?>, Set<Invokable<?, ?>>> originalMethodsForTypeToken;
|
||||
|
||||
@BeforeClass
|
||||
public void backupMethodsForTypeToken() {
|
||||
originalMethodsForTypeToken = getStaticField(Reflection2.class, "methodsForTypeToken");
|
||||
}
|
||||
|
||||
private static class ParentWithMethod {
|
||||
@SuppressWarnings("unused")
|
||||
public Set<Object> method() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ChildOverridesAndNarrowsMethod extends ParentWithMethod {
|
||||
@Override
|
||||
public SortedSet<Object> method() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void testOverriddenMethodWithNarrowedReturnType() throws NoSuchMethodException {
|
||||
// expecting two methods: the declared "method" and the bridge version with return type Set
|
||||
final Method[] methods = ChildOverridesAndNarrowsMethod.class.getDeclaredMethods();
|
||||
/*
|
||||
* Force Reflection2.methodsForTypeToken to reflect the fact that the declared methods
|
||||
* of a class are not returned in any particular order.
|
||||
*/
|
||||
setStaticField(Reflection2.class, "methodsForTypeToken", keyOverridingCache(
|
||||
TypeToken.of(ChildOverridesAndNarrowsMethod.class),
|
||||
ImmutableSet.<Invokable<?, ?>>of(Invokable.from(methods[0]), Invokable.from(methods[1]))));
|
||||
// getMethod returns the method with the *narrowest* return type if one exists
|
||||
Invokable<?, Object> mostSpecificMethod = Invokable.from(ChildOverridesAndNarrowsMethod.class.getMethod("method"));
|
||||
assertEquals(Reflection2.method(ChildOverridesAndNarrowsMethod.class, "method"),
|
||||
mostSpecificMethod);
|
||||
|
||||
// now the opposite order
|
||||
Reflection2OverriddenMethodTest.<LoadingCache<?, ?>>
|
||||
getStaticField(Reflection2.class, "methodForParams").invalidateAll();
|
||||
setStaticField(Reflection2.class, "methodsForTypeToken", keyOverridingCache(
|
||||
TypeToken.of(ChildOverridesAndNarrowsMethod.class),
|
||||
ImmutableSet.<Invokable<?, ?>>of(Invokable.from(methods[1]), Invokable.from(methods[0]))));
|
||||
assertEquals(Reflection2.method(ChildOverridesAndNarrowsMethod.class, "method"),
|
||||
mostSpecificMethod);
|
||||
}
|
||||
|
||||
private LoadingCache<TypeToken<?>, Set<Invokable<?, ?>>> keyOverridingCache(
|
||||
final TypeToken<?> overriddenKey, final Set<Invokable<?, ?>> value) {
|
||||
return new SimpleForwardingLoadingCache<TypeToken<?>, Set<Invokable<?, ?>>>(originalMethodsForTypeToken) {
|
||||
@Override
|
||||
public Set<Invokable<?, ?>> get(TypeToken<?> key) throws ExecutionException {
|
||||
return (key.equals(overriddenKey) ? value : super.get(key));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T getStaticField(Class<?> declaringClass, String fieldName) {
|
||||
try {
|
||||
Field field = declaringClass.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
// static field
|
||||
return (T) field.get(null);
|
||||
} catch (NoSuchFieldException exception) {
|
||||
throw propagate(exception);
|
||||
} catch (IllegalAccessException exception) {
|
||||
throw propagate(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setStaticField(Class<?> declaringClass, String fieldName, Object value) {
|
||||
try {
|
||||
Field field = declaringClass.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
// static field
|
||||
field.set(null, value);
|
||||
} catch (NoSuchFieldException exception) {
|
||||
throw propagate(exception);
|
||||
} catch (IllegalAccessException exception) {
|
||||
throw propagate(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass(alwaysRun = true)
|
||||
public void restoreMethodsForTypeToken() {
|
||||
setStaticField(Reflection2.class, "methodsForTypeToken", originalMethodsForTypeToken);
|
||||
}
|
||||
}
|
|
@ -19,17 +19,12 @@
|
|||
package org.jclouds.reflect;
|
||||
|
||||
import static com.google.common.base.Functions.toStringFunction;
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
import static java.lang.String.format;
|
||||
import static org.jclouds.reflect.Reflection2.constructors;
|
||||
import static org.jclouds.reflect.Reflection2.method;
|
||||
import static org.jclouds.reflect.Reflection2.methods;
|
||||
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.fail;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
|
@ -37,9 +32,6 @@ import java.util.SortedSet;
|
|||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.Invokable;
|
||||
|
@ -121,97 +113,9 @@ public class Reflection2Test {
|
|||
.addAll(setMethods).build());
|
||||
}
|
||||
|
||||
public void testOverriddenMethodWithNarrowedReturnType() throws NoSuchMethodException {
|
||||
LoadingCache<TypeToken<?>, Set<Invokable<?, ?>>> methodsForTypeTokenBackup =
|
||||
getStaticField(Reflection2.class, "methodsForTypeToken");
|
||||
// expecting two methods: the declared "method" and the bridge version with return type Set
|
||||
final Method[] methods = ChildOverridesAndNarrowsMethod.class.getDeclaredMethods();
|
||||
try {
|
||||
/*
|
||||
* Force Reflection2.methodsForTypeToken to reflect the fact that the declared methods
|
||||
* of a class are not returned in any particular order.
|
||||
*/
|
||||
setStaticField(Reflection2.class, "methodsForTypeToken", CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<TypeToken<?>, Set<Invokable<?, ?>>>() {
|
||||
@Override
|
||||
public Set<Invokable<?, ?>> load(TypeToken<?> key) throws Exception {
|
||||
if (!key.equals(TypeToken.of(ChildOverridesAndNarrowsMethod.class))) {
|
||||
fail(format("expected only key %s to be requested, but was %s",
|
||||
TypeToken.of(ChildOverridesAndNarrowsMethod.class), key));
|
||||
}
|
||||
return ImmutableSet.<Invokable<?, ?>>of(Invokable.from(methods[0]), Invokable.from(methods[1]));
|
||||
}
|
||||
}));
|
||||
// getMethod returns the method with the *narrowest* return type if one exists
|
||||
assertEquals(Reflection2.method(ChildOverridesAndNarrowsMethod.class, "method"),
|
||||
Invokable.from(ChildOverridesAndNarrowsMethod.class.getMethod("method")));
|
||||
|
||||
// now the opposite order
|
||||
Reflection2Test.<LoadingCache<?, ?>>
|
||||
getStaticField(Reflection2.class, "methodForParams").invalidateAll();
|
||||
setStaticField(Reflection2.class, "methodsForTypeToken", CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<TypeToken<?>, Set<Invokable<?, ?>>>() {
|
||||
@Override
|
||||
public Set<Invokable<?, ?>> load(TypeToken<?> key) throws Exception {
|
||||
if (!key.equals(TypeToken.of(ChildOverridesAndNarrowsMethod.class))) {
|
||||
fail(format("expected only key %s to be requested, but was %s",
|
||||
TypeToken.of(ChildOverridesAndNarrowsMethod.class), key));
|
||||
}
|
||||
return ImmutableSet.<Invokable<?, ?>>of(Invokable.from(methods[1]), Invokable.from(methods[0]));
|
||||
}
|
||||
}));
|
||||
// getMethod returns the method with the *narrowest* return type if one exists
|
||||
assertEquals(Reflection2.method(ChildOverridesAndNarrowsMethod.class, "method"),
|
||||
Invokable.from(ChildOverridesAndNarrowsMethod.class.getMethod("method")));
|
||||
} finally {
|
||||
setStaticField(Reflection2.class, "methodsForTypeToken", methodsForTypeTokenBackup);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T getStaticField(Class<?> declaringClass, String fieldName) {
|
||||
try {
|
||||
Field field = declaringClass.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
// static field
|
||||
return (T) field.get(null);
|
||||
} catch (NoSuchFieldException exception) {
|
||||
throw propagate(exception);
|
||||
} catch (IllegalAccessException exception) {
|
||||
throw propagate(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setStaticField(Class<?> declaringClass, String fieldName, Object value) {
|
||||
try {
|
||||
Field field = declaringClass.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
// static field
|
||||
field.set(null, value);
|
||||
} catch (NoSuchFieldException exception) {
|
||||
throw propagate(exception);
|
||||
} catch (IllegalAccessException exception) {
|
||||
throw propagate(exception);
|
||||
}
|
||||
}
|
||||
|
||||
static final Function<Invokable<?, ?>, String> invokableToName = new Function<Invokable<?, ?>, String>() {
|
||||
public String apply(Invokable<?, ?> input) {
|
||||
return input.getName();
|
||||
}
|
||||
};
|
||||
|
||||
private static class ParentWithMethod {
|
||||
@SuppressWarnings("unused")
|
||||
public Set<Object> method() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ChildOverridesAndNarrowsMethod extends ParentWithMethod {
|
||||
@Override
|
||||
public SortedSet<Object> method() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue