Skip to content

Commit 780051d

Browse files
authored
Merge pull request #52 from Nordstrom/pr/fix-more-generics
Pr/fix more generics
2 parents 79e1f0f + 35efafb commit 780051d

File tree

10 files changed

+151
-146
lines changed

10 files changed

+151
-146
lines changed

README.md

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import com.nordstrom.automation.junit.TestClassWatcher;
4242
import org.junit.runners.model.FrameworkMethod;
4343
import org.junit.runners.model.TestClass;
4444

45-
public class ExploringWatcher implements TestClassWatcher, MethodWatcher {
45+
public class ExploringWatcher implements TestClassWatcher, MethodWatcher<FrameworkMethod> {
4646

4747
...
4848

@@ -58,21 +58,23 @@ public class ExploringWatcher implements TestClassWatcher, MethodWatcher {
5858
}
5959

6060
@Override
61-
public void beforeInvocation(Object runner, Object target, FrameworkMethod method, Object... params) {
61+
public void beforeInvocation(Object runner, FrameworkMethod method, ReflectiveCallable callable) {
62+
Object target = LifecycleHooks.getFieldValue(callable, "val$target");
6263
// if target defined
6364
if (target != null) {
6465
// get the test class of the runner
6566
TestClass testClass = LifecycleHooks.getTestClassOf(runner);
67+
...
6668
}
6769
...
6870
}
6971

7072
@Override
71-
public void afterInvocation(Object runner, Object target, FrameworkMethod method, Throwable thrown) {
72-
// if target defined
73-
if (target != null) {
73+
public void afterInvocation(Object runner, FrameworkMethod method, ReflectiveCallable callable, Throwable thrown) {
74+
// if child is a 'before' configuration method
75+
if (null != method.getAnnotation(Before.class)) {
7476
// get the atomic test of the runner
75-
AtomicTest atomicTest = LifecycleHooks.getAtomicTestOf(runner);
77+
AtomicTest<FrameworkMethod> atomicTest = LifecycleHooks.getAtomicTestOf(runner);
7678
// get the "identity" method
7779
FrameworkMethod identity = atomicTest.getIdentity();
7880
...
@@ -97,10 +99,16 @@ Note that some associations are not available for specific context:
9799

98100
**JUnit Foundation** provides several static utility methods that can be useful in your service provider implementation.
99101

102+
* `LifecycleHooks.isParticleMethod(Object child)`
103+
Determines if the specified child is a test or configuration method.
104+
* `LifecycleHooks.getAnnotation(Object object, Class<T> annotationType)`
105+
Returns the annotation of the specified type if the object is tagged with such an annotation.
100106
* `LifecycleHooks.describeChild(Object target, Object child)`
101107
Get a `Description` for the indicated child object from the runner for the specified test class instance.
102108
* `LifecycleHooks.getInstanceClass(Object instance)`
103109
Get class of specified test class instance.
110+
* `LifecycleHooks.getFieldValue(Object target, String name)`
111+
Get the value of the specified field from the supplied object.
104112

105113
### How to Enable Notifications
106114

@@ -124,7 +132,7 @@ The hooks that enable **JUnit Foundation** test lifecycle notifications are inst
124132
<dependency>
125133
<groupId>com.nordstrom.tools</groupId>
126134
<artifactId>junit-foundation</artifactId>
127-
<version>9.4.3</version>
135+
<version>11.0.0</version>
128136
<scope>test</scope>
129137
</dependency>
130138
</dependencies>
@@ -201,7 +209,7 @@ repositories {
201209
}
202210
dependencies {
203211
...
204-
compile 'com.nordstrom.tools:junit-foundation:9.4.3
212+
compile 'com.nordstrom.tools:junit-foundation:11.0.0
205213
'
206214
}
207215
ext {
@@ -276,12 +284,12 @@ import org.junit.runners.model.FrameworkMethod;
276284
import org.slf4j.Logger;
277285
import org.slf4j.LoggerFactory;
278286

279-
public class LoggingWatcher implements MethodWatcher {
287+
public class LoggingWatcher implements MethodWatcher<FrameworkMethod> {
280288

281289
private static final Logger LOGGER = LoggerFactory.getLogger(LoggingWatcher.class);
282290

283291
@Override
284-
public void beforeInvocation(Object target, FrameworkMethod method, Object... params) {
292+
public void beforeInvocation(Object runner, FrameworkMethod method, ReflectiveCallable callable) {
285293
if (null != method.getAnnotation(Test.class)) {
286294
LOGGER.info(">>>>> ENTER 'test' method {}", method.getName());
287295
} else if (null != method.getAnnotation(Before.class)) {
@@ -296,7 +304,7 @@ public class LoggingWatcher implements MethodWatcher {
296304
}
297305

298306
@Override
299-
public void afterInvocation(Object obj, FrameworkMethod method, Object... params) {
307+
public void afterInvocation(Object runner, FrameworkMethod method, ReflectiveCallable callable, Throwable thrown) {
300308
if (null != method.getAnnotation(Test.class)) {
301309
LOGGER.info("<<<<< LEAVE 'test' method {}", method.getName());
302310
} else if (null != method.getAnnotation(Before.class)) {
@@ -321,6 +329,15 @@ As indicated previously, **JUnit Foundation** will automatically attach standard
321329

322330
**JUnit Foundation** uses this feature internally; notifications sent to **`RunWatcher`** service providers are published by an auto-attached **`RunListener`**. This notification-enhancing run listener, named [RunAnnouncer](https://github.com/Nordstrom/JUnit-Foundation/blob/master/src/main/java/com/nordstrom/automation/junit/RunAnnouncer.java), is registered via the aforementioned [**ServiceLoader** provider configuration file](https://github.com/Nordstrom/JUnit-Foundation/blob/master/src/main/resources/META-INF/services/org.junit.runner.notification.RunListener).
323331

332+
### Getting Attached Watchers and Listeners
333+
334+
**JUnit Foundation** attaches service providers that handle published event notifications via the standard **ServiceLoader** facility. You can acquire references to these service providers through the following methods:
335+
336+
* `LifecycleHooks.getAttachedWatcher(Class<T> watcherType)`
337+
Get reference to an instance of the specified watcher type.
338+
* `LifecycleHooks.getAttachedListener(Class<T> listenerType)`
339+
Get reference to an instance of the specified listener type.
340+
324341
### Support for Parallel Execution
325342

326343
The ability to run **JUnit** tests in parallel is provided by the JUnit 4 test runner of the [Maven Surefire plugin](https://maven.apache.org/surefire/maven-surefire-plugin/examples/fork-options-and-parallel-execution.html). This feature utilizes private **JUnit** interfaces and undocuments behaviors, which greatly complicated the task of adding event notification hooks. As of version [9.0.3](https://github.com/Nordstrom/JUnit-Foundation/releases/tag/junit-foundation-9.0.3), **JUnit Foundation** supports parallel execution of both classes and methods.

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<modelVersion>4.0.0</modelVersion>
33
<groupId>com.nordstrom.tools</groupId>
44
<artifactId>junit-foundation</artifactId>
5-
<version>10.0.1-SNAPSHOT</version>
5+
<version>11.0.0-SNAPSHOT</version>
66
<packaging>jar</packaging>
77

88
<name>JUnit Foundation</name>

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

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -302,28 +302,12 @@ static Field getDeclaredField(Object target, String name) throws NoSuchFieldExce
302302
* @throws SecurityException if the request is denied
303303
*/
304304
@SuppressWarnings("unchecked")
305-
static <T> T getFieldValue(Object target, String name) throws IllegalAccessException, NoSuchFieldException, SecurityException {
305+
public static <T> T getFieldValue(Object target, String name) throws IllegalAccessException, NoSuchFieldException, SecurityException {
306306
Field field = getDeclaredField(target, name);
307307
field.setAccessible(true);
308308
return (T) field.get(target);
309309
}
310310

311-
/**
312-
* Set the value of the specified field of the supplied object.
313-
*
314-
* @param target target object
315-
* @param name field name
316-
* @param value value to set in the specified field of the supplied object
317-
* @throws IllegalAccessException if the {@code Field} object is enforcing access control for an inaccessible field
318-
* @throws NoSuchFieldException if a field with the specified name is not found
319-
* @throws SecurityException if the request is denied
320-
*/
321-
static void setFieldValue(Object target, String name, Object value) throws IllegalAccessException, NoSuchFieldException, SecurityException {
322-
Field field = getDeclaredField(target, name);
323-
field.setAccessible(true);
324-
field.set(target, value);
325-
}
326-
327311
/**
328312
* Invoke an intercepted method through its callable proxy.
329313
* <p>
Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,28 @@
11
package com.nordstrom.automation.junit;
22

3-
import org.junit.runners.model.FrameworkMethod;
3+
import org.junit.internal.runners.model.ReflectiveCallable;
44

55
/**
66
* This interface defines the methods implemented by JUnit method watchers.
77
*/
8-
public interface MethodWatcher extends JUnitWatcher {
8+
public interface MethodWatcher<T> extends TypeDiscloser<T>, JUnitWatcher {
99

1010
/**
11-
* Invoked before each test or configuration method is invoked
11+
* Invoked before each test or configuration method is invoked.
1212
*
1313
* @param runner JUnit test runner
14-
* @param target "enhanced" object upon which the method was invoked
15-
* @param method {@link FrameworkMethod} object for the invoked method
16-
* @param params method invocation parameters
14+
* @param child child object of {@code runner} that is being invoked
15+
* @param callable {@link ReflectiveCallable} object being intercepted
1716
*/
18-
void beforeInvocation(Object runner, Object target, FrameworkMethod method, Object... params);
17+
void beforeInvocation(Object runner, T child, ReflectiveCallable callable);
1918

2019
/**
21-
* Invoked after each test or configuration method is invoked
20+
* Invoked after each test or configuration method is invoked.
2221
*
2322
* @param runner JUnit test runner
24-
* @param target "enhanced" object upon which the method was invoked
25-
* @param method {@link FrameworkMethod} object for the invoked method
23+
* @param child child object of {@code runner} that was just invoked
24+
* @param callable {@link ReflectiveCallable} object being intercepted
2625
* @param thrown exception thrown by method; {@code null} on normal completion
2726
*/
28-
void afterInvocation(Object runner, Object target, FrameworkMethod method, Throwable thrown);
27+
void afterInvocation(Object runner, T child, ReflectiveCallable callable, Throwable thrown);
2928
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public static Description proxyFor(Description description, Throwable thrown) {
7575
}
7676
} catch (NoSuchFieldException | SecurityException e) {
7777
throw new UnsupportedOperationException("Failed acquiring [" + ANNOTATIONS
78-
+ "] field of Executable class", e);
78+
+ "] field of test method class", e);
7979
}
8080
throw new IllegalArgumentException("Specified method is not a JUnit @Test: " + description);
8181
}

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

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
import com.google.common.base.Optional;
1515

16-
public class RunAnnouncer<T> extends RunListener {
16+
public class RunAnnouncer extends RunListener {
1717

1818
@SuppressWarnings("rawtypes")
1919
private static final ServiceLoader<RunWatcher> runWatcherLoader;
@@ -28,13 +28,15 @@ public class RunAnnouncer<T> extends RunListener {
2828
* {@inheritDoc}
2929
*/
3030
@Override
31-
@SuppressWarnings("unchecked")
31+
@SuppressWarnings({"rawtypes", "unchecked"})
3232
public void testStarted(Description description) throws Exception {
3333
LOGGER.debug("testStarted: {}", description);
34-
AtomicTest<T> atomicTest = getAtomicTestOf(description);
34+
AtomicTest<?> atomicTest = getAtomicTestOf(description);
3535
synchronized(runWatcherLoader) {
36-
for (RunWatcher<T> watcher : runWatcherLoader) {
37-
watcher.testStarted(atomicTest);
36+
for (RunWatcher watcher : runWatcherLoader) {
37+
if (isSupported(watcher, atomicTest)) {
38+
watcher.testStarted(atomicTest);
39+
}
3840
}
3941
}
4042
}
@@ -43,13 +45,15 @@ public void testStarted(Description description) throws Exception {
4345
* {@inheritDoc}
4446
*/
4547
@Override
46-
@SuppressWarnings("unchecked")
48+
@SuppressWarnings({"rawtypes", "unchecked"})
4749
public void testFinished(Description description) throws Exception {
4850
LOGGER.debug("testFinished: {}", description);
49-
AtomicTest<T> atomicTest = getAtomicTestOf(description);
51+
AtomicTest<?> atomicTest = getAtomicTestOf(description);
5052
synchronized(runWatcherLoader) {
51-
for (RunWatcher<T> watcher : runWatcherLoader) {
52-
watcher.testFinished(atomicTest);
53+
for (RunWatcher watcher : runWatcherLoader) {
54+
if (isSupported(watcher, atomicTest)) {
55+
watcher.testFinished(atomicTest);
56+
}
5357
}
5458
}
5559
}
@@ -58,13 +62,15 @@ public void testFinished(Description description) throws Exception {
5862
* {@inheritDoc}
5963
*/
6064
@Override
61-
@SuppressWarnings("unchecked")
65+
@SuppressWarnings({"rawtypes", "unchecked"})
6266
public void testFailure(Failure failure) throws Exception {
6367
LOGGER.debug("testFailure: {}", failure);
64-
AtomicTest<T> atomicTest = setTestFailure(failure);
68+
AtomicTest<?> atomicTest = setTestFailure(failure);
6569
synchronized(runWatcherLoader) {
66-
for (RunWatcher<T> watcher : runWatcherLoader) {
67-
watcher.testFailure(atomicTest, failure.getException());
70+
for (RunWatcher watcher : runWatcherLoader) {
71+
if (isSupported(watcher, atomicTest)) {
72+
watcher.testFailure(atomicTest, failure.getException());
73+
}
6874
}
6975
}
7076
}
@@ -73,13 +79,15 @@ public void testFailure(Failure failure) throws Exception {
7379
* {@inheritDoc}
7480
*/
7581
@Override
76-
@SuppressWarnings("unchecked")
82+
@SuppressWarnings({"rawtypes", "unchecked"})
7783
public void testAssumptionFailure(Failure failure) {
7884
LOGGER.debug("testAssumptionFailure: {}", failure);
79-
AtomicTest<T> atomicTest = setTestFailure(failure);
85+
AtomicTest<?> atomicTest = setTestFailure(failure);
8086
synchronized(runWatcherLoader) {
81-
for (RunWatcher<T> watcher : runWatcherLoader) {
82-
watcher.testAssumptionFailure(atomicTest, (AssumptionViolatedException) failure.getException());
87+
for (RunWatcher watcher : runWatcherLoader) {
88+
if (isSupported(watcher, atomicTest)) {
89+
watcher.testAssumptionFailure(atomicTest, (AssumptionViolatedException) failure.getException());
90+
}
8391
}
8492
}
8593
}
@@ -88,13 +96,15 @@ public void testAssumptionFailure(Failure failure) {
8896
* {@inheritDoc}
8997
*/
9098
@Override
91-
@SuppressWarnings("unchecked")
99+
@SuppressWarnings({"rawtypes", "unchecked"})
92100
public void testIgnored(Description description) throws Exception {
93101
LOGGER.debug("testIgnored: {}", description);
94-
AtomicTest<T> atomicTest = getAtomicTestOf(description);
102+
AtomicTest<?> atomicTest = getAtomicTestOf(description);
95103
synchronized(runWatcherLoader) {
96-
for (RunWatcher<T> watcher : runWatcherLoader) {
97-
watcher.testIgnored(atomicTest);
104+
for (RunWatcher watcher : runWatcherLoader) {
105+
if (isSupported(watcher, atomicTest)) {
106+
watcher.testIgnored(atomicTest);
107+
}
98108
}
99109
}
100110
}
@@ -117,6 +127,7 @@ static <T> AtomicTest<T> newAtomicTest(Object runner, T identity) {
117127
/**
118128
* Get the atomic test object for the specified class runner or method description.
119129
*
130+
* @param <T> atomic test child object type
120131
* @param testKey JUnit class runner or method description
121132
* @return {@link AtomicTest} object (may be {@code null})
122133
*/
@@ -128,6 +139,7 @@ static <T> AtomicTest<T> getAtomicTestOf(Object testKey) {
128139
/**
129140
* Store the specified failure in the active atomic test.
130141
*
142+
* @param <T> atomic test child object type
131143
* @param failure {@link Failure} object
132144
* @return {@link AtomicTest} object
133145
*/
@@ -137,6 +149,17 @@ private static <T> AtomicTest<T> setTestFailure(Failure failure) {
137149
return atomicTest;
138150
}
139151

152+
/**
153+
* Determine if the run watcher in question supports the specified atomic test.
154+
*
155+
* @param watcher {@link RunWatcher} object
156+
* @param atomicTest {@link AtomicTest} object
157+
* @return {@code true} if the run watcher in question supports the specified atomic test; otherwise {@code false}
158+
*/
159+
private static boolean isSupported(RunWatcher<?> watcher, AtomicTest<?> atomicTest) {
160+
return watcher.supportedType().isInstance(atomicTest.getIdentity());
161+
}
162+
140163
/**
141164
* Get reference to an instance of the specified watcher type.
142165
*

0 commit comments

Comments
 (0)