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:
Andrew Phillips 2013-01-24 21:38:12 -05:00
parent 4c5cef1be4
commit 2a1fc363e0
2 changed files with 140 additions and 96 deletions

View File

@ -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);
}
}

View File

@ -19,17 +19,12 @@
package org.jclouds.reflect; package org.jclouds.reflect;
import static com.google.common.base.Functions.toStringFunction; 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.constructors;
import static org.jclouds.reflect.Reflection2.method; import static org.jclouds.reflect.Reflection2.method;
import static org.jclouds.reflect.Reflection2.methods; import static org.jclouds.reflect.Reflection2.methods;
import static org.jclouds.reflect.Reflection2.typeToken; import static org.jclouds.reflect.Reflection2.typeToken;
import static org.testng.Assert.assertEquals; 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.HashSet;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
@ -37,9 +32,6 @@ import java.util.SortedSet;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Function; 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.FluentIterable;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.Invokable; import com.google.common.reflect.Invokable;
@ -121,97 +113,9 @@ public class Reflection2Test {
.addAll(setMethods).build()); .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>() { static final Function<Invokable<?, ?>, String> invokableToName = new Function<Invokable<?, ?>, String>() {
public String apply(Invokable<?, ?> input) { public String apply(Invokable<?, ?> input) {
return input.getName(); 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;
}
}
} }