Skip to content

Commit 64cd646

Browse files
authored
Re-implement recording of 'callable' objects (#68)
1 parent 71c08a1 commit 64cd646

File tree

4 files changed

+62
-30
lines changed

4 files changed

+62
-30
lines changed

src/main/java/com/nordstrom/automation/junit/AtomIdentity.java

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ public class AtomIdentity extends TestWatcher implements ArtifactParams {
1818

1919
private final Object instance;
2020
private Description description;
21-
private ReflectiveCallable callable;
22-
21+
2322
public AtomIdentity(Object instance) {
2423
this.instance = instance;
2524
}
@@ -41,24 +40,6 @@ public Object getInstance() {
4140
return instance;
4241
}
4342

44-
/**
45-
* Set the {@link ReflectiveCallable} object for the current test class instance.
46-
*
47-
* @param callable {@link ReflectiveCallable} object
48-
*/
49-
public void setCallable(ReflectiveCallable callable) {
50-
this.callable = callable;
51-
}
52-
53-
/**
54-
* Get the {@link ReflectiveCallable} object for the current test class instance.
55-
*
56-
* @return {@link ReflectiveCallable} object
57-
*/
58-
public ReflectiveCallable getCallable() {
59-
return callable;
60-
}
61-
6243
/**
6344
* {@inheritDoc}
6445
*/

src/main/java/com/nordstrom/automation/junit/LifecycleHooks.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.lang.instrument.Instrumentation;
77
import java.lang.reflect.Field;
88
import java.lang.reflect.InvocationTargetException;
9+
import java.lang.reflect.Method;
910
import java.util.AbstractList;
1011
import java.util.ArrayList;
1112
import java.util.Arrays;
@@ -15,6 +16,7 @@
1516
import java.util.concurrent.ConcurrentMap;
1617

1718
import org.apache.commons.lang3.reflect.MethodUtils;
19+
import org.junit.internal.runners.model.ReflectiveCallable;
1820
import org.junit.runner.Description;
1921
import org.junit.runner.notification.RunListener;
2022
import org.junit.runners.model.TestClass;
@@ -267,7 +269,35 @@ public static TestClass getTestClassOf(Object runner) {
267269
public static <T> AtomicTest<T> getAtomicTestOf(Object runner) {
268270
return RunAnnouncer.getAtomicTestOf(runner);
269271
}
270-
272+
273+
/**
274+
* Get the {@link ReflectiveCallable} object for the specified class runner or method description.
275+
*
276+
* @param runner JUnit class runner
277+
* @param child JUnit child object (runner or framework method)
278+
* @return <b>ReflectiveCallable</b> object (may be {@code null})
279+
*/
280+
public static ReflectiveCallable getCallableOf(Object runner, Object child) {
281+
return RunReflectiveCall.getCallableOf(runner, child);
282+
}
283+
284+
/**
285+
* Synthesize a {@link ReflectiveCallable} closure with the specified parameters.
286+
*
287+
* @param method {@link Method} object to be invoked
288+
* @param target test class instance to target
289+
* @param params method invocation parameters
290+
* @return <b>ReflectiveCallable</b> object as specified
291+
*/
292+
public static ReflectiveCallable encloseCallable(final Method method, final Object target, final Object... params) {
293+
return new ReflectiveCallable() {
294+
@Override
295+
protected Object runReflectiveCall() throws Throwable {
296+
return method.invoke(target, params);
297+
}
298+
};
299+
}
300+
271301
/**
272302
* Get the description of the indicated child object from the runner for the specified test class instance.
273303
*

src/main/java/com/nordstrom/automation/junit/RunAnnouncer.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,15 @@ public void testAssumptionFailure(Failure failure) {
9191
@SuppressWarnings({"rawtypes", "unchecked"})
9292
public void testIgnored(Description description) throws Exception {
9393
LOGGER.debug("testIgnored: {}", description);
94+
// determine if retrying a failed invocation
9495
AtomicTest<?> atomicTest = getAtomicTestOf(description);
96+
97+
// if actually ignored
98+
if (atomicTest == null) {
99+
// create new atomic test object
100+
atomicTest = newAtomicTest(description);
101+
}
102+
95103
for (RunWatcher watcher : LifecycleHooks.getRunWatchers()) {
96104
if (isSupported(watcher, atomicTest)) {
97105
watcher.testIgnored(atomicTest);

src/main/java/com/nordstrom/automation/junit/RunReflectiveCall.java

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22

33
import static com.nordstrom.automation.junit.LifecycleHooks.getFieldValue;
44

5+
import java.lang.reflect.Method;
6+
import java.util.Map;
7+
import java.util.Objects;
58
import java.util.concurrent.Callable;
69
import java.util.concurrent.ConcurrentHashMap;
710
import java.util.concurrent.ConcurrentMap;
811
import java.lang.IllegalAccessException;
12+
13+
import com.google.common.base.Optional;
914
import org.junit.internal.runners.model.ReflectiveCallable;
1015
import org.slf4j.Logger;
1116
import org.slf4j.LoggerFactory;
@@ -23,7 +28,8 @@
2328
*/
2429
@SuppressWarnings("squid:S1118")
2530
public class RunReflectiveCall {
26-
31+
32+
private static final Map<Object, ReflectiveCallable> CHILD_TO_CALLABLE = new ConcurrentHashMap<>();
2733
private static final ThreadLocal<ConcurrentMap<Integer, DepthGauge>> methodDepth;
2834
private static final Function<Integer, DepthGauge> newInstance;
2935
private static final Logger LOGGER = LoggerFactory.getLogger(RunReflectiveCall.class);
@@ -57,11 +63,9 @@ public static Object intercept(@This final ReflectiveCallable callable, @SuperCa
5763
throws Exception {
5864

5965
Object child = null;
60-
Object target = null;
6166

6267
try {
6368
child = getFieldValue(callable, "this$0");
64-
target = getFieldValue(callable, "val$target");
6569
} catch (IllegalAccessException | NoSuchFieldException | SecurityException | IllegalArgumentException e) {
6670
// handled below
6771
}
@@ -71,10 +75,8 @@ public static Object intercept(@This final ReflectiveCallable callable, @SuperCa
7175
runner = Run.getThreadRunner();
7276
}
7377

74-
if (ArtifactParams.class.isInstance(target)) {
75-
((ArtifactParams) target).getAtomIdentity().setCallable(callable);
76-
}
77-
78+
CHILD_TO_CALLABLE.put(Objects.hash(runner, child), callable);
79+
7880
Object result = null;
7981
Throwable thrown = null;
8082

@@ -93,7 +95,18 @@ public static Object intercept(@This final ReflectiveCallable callable, @SuperCa
9395

9496
return result;
9597
}
96-
98+
99+
/**
100+
* Get the {@link ReflectiveCallable} object for the specified class runner or method description.
101+
*
102+
* @param runner JUnit class runner
103+
* @param child JUnit child object (runner or framework method)
104+
* @return <b>ReflectiveCallable</b> object (may be {@code null})
105+
*/
106+
static ReflectiveCallable getCallableOf(Object runner, Object child) {
107+
return CHILD_TO_CALLABLE.get(Objects.hash(runner, child));
108+
}
109+
97110
/**
98111
* Fire the {@link MethodWatcher#beforeInvocation(Object, Object, ReflectiveCallable) event.
99112
* <p>
@@ -128,7 +141,7 @@ private static boolean fireBeforeInvocation(Object runner, Object child, Reflect
128141
}
129142

130143
/**
131-
* Fire the {@link MethodWatcher#afterInvocation(Object, Object, ReflectiveCallable) event.
144+
* Fire the {@link MethodWatcher#afterInvocation(Object, Object, ReflectiveCallable, Throwable) event.
132145
* <p>
133146
* If the {@code afterInvocation} event for the specified method has already been fired, do nothing.
134147
*

0 commit comments

Comments
 (0)