Skip to content

Commit 38dd4b4

Browse files
committed
Merge branch 'master' into pr/add-watcher-finder
# Conflicts: # src/main/java/com/nordstrom/automation/junit/TestClassWatcher.java
2 parents 5bdadd6 + 5a05d7a commit 38dd4b4

File tree

13 files changed

+425
-446
lines changed

13 files changed

+425
-446
lines changed

README.md

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ The hooks that enable **JUnit Foundation** test lifecycle notifications are inst
109109
<dependency>
110110
<groupId>com.nordstrom.tools</groupId>
111111
<artifactId>junit-foundation</artifactId>
112-
<version>6.2.0</version>
112+
<version>9.0.4</version>
113113
<scope>test</scope>
114114
</dependency>
115115
</dependencies>
@@ -184,23 +184,21 @@ repositories {
184184
mavenCentral()
185185
...
186186
}
187-
configurations {
187+
dependencies {
188188
...
189-
junitAgent
189+
compile 'com.nordstrom.tools:junit-foundation:9.0.4'
190+
}
191+
ext {
192+
junitFoundation = configurations.compile.resolvedConfiguration.resolvedArtifacts.find { it.name == 'junit-foundation' }
190193
}
191194
test.doFirst {
192-
jvmArgs "-javaagent:${configurations.junitAgent.files.iterator().next()}"
195+
jvmArgs "-javaagent:${junitFoundation.file}"
193196
}
194197
test {
195198
// debug true
196199
// not required, but definitely useful
197200
testLogging.showStandardStreams = true
198201
}
199-
dependencies {
200-
...
201-
compile 'com.nordstrom.tools:junit-foundation:6.2.0'
202-
junitAgent 'com.nordstrom.tools:junit-foundation:6.2.0'
203-
}
204202
```
205203

206204
#### IDE Configuration for JUnit Foundation
@@ -234,12 +232,6 @@ The preceding **ServiceLoader** provider configuration files declare a **JUnit F
234232
**RunnerWatcher** provides callbacks for events in the lifecycle of **`ParentRunner`** objects. It receives the following notifications:
235233
* A **`ParentRunner`** object is about to run.
236234
* A **`ParentRunner`** object has finished running.
237-
* [TestClassWatcher](https://github.com/Nordstrom/JUnit-Foundation/blob/master/src/main/java/com/nordstrom/automation/junit/TestClassWatcher.java)
238-
**TestClassWatcher** provides callbacks for events in the lifecycle of **`TestClass`** objects. It receives the following notifications:
239-
* A **`TestClass`** object has been created to represent a JUnit test class or suite. Each **`TestClass`** has a one-to-one relationship with the JUnit runner that created it. (see **NOTE**)
240-
* A **`TestClass`** object has been scheduled to run. This signals that the first child of the JUnit test class or suite is about start.
241-
* A **`TestClass`** object has finished running. This signals that the last child of the JUnit test class or suite is done.
242-
* **NOTE** - This interface supercedes a prior version, adding the runner object to start/finish notifications. Test executers like Maven Surefire create suite runners for their own purposes (e.g. - parallel execution context). This breaks the one-to-one relationship between **`TestClass`** objects and runners. Consequently, the **`TestClass`** object cannot be assumed to represent a unique context.
243235
* [TestObjectWatcher](https://github.com/Nordstrom/JUnit-Foundation/blob/master/src/main/java/com/nordstrom/automation/junit/TestObjectWatcher.java)
244236
**TestObjectWatcher** provides callbacks for events in the lifecycle of Java test class instances. It receives the following notification:
245237
* An instance of a JUnit test class has been created for the execution of a single `atomic test`.
@@ -309,7 +301,13 @@ Note that the implementation in this method watcher uses the annotations attache
309301

310302
### Support for Standard JUnit RunListener Providers
311303

312-
As indicated previously, **JUnit Foundation** will automatically attach standard JUnit **RunListener** providers that are declared in the associated **ServiceLoader** provider configuration file (i.e. - **_org.junit.runner.notification.RunListener_**). Declared run listeners are attached to the **RunNotifier** supplied to the `run()` method of JUnit runners. This feature eliminates behavioral differences between the various test execution environments like Maven, Gradle, and native IDE test runners.
304+
As indicated previously, **JUnit Foundation** will automatically attach standard JUnit **`RunListener`** providers that are declared in the associated **`ServiceLoader`** provider configuration file (i.e. - **_org.junit.runner.notification.RunListener_**). Declared run listeners are attached to the **`RunNotifier`** supplied to the `run()` method of JUnit runners. This feature eliminates behavioral differences between the various test execution environments like Maven, Gradle, and native IDE test runners.
305+
306+
**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).
307+
308+
### Support for Parallel Execution
309+
310+
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.
313311

314312
### Support for Parameterized Tests
315313

pom.xml

Lines changed: 3 additions & 5 deletions
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>8.0.2-SNAPSHOT</version>
5+
<version>9.0.5-SNAPSHOT</version>
66
<packaging>jar</packaging>
77

88
<name>JUnit Foundation</name>
@@ -29,14 +29,14 @@
2929
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
3030
<maven.compiler.source>1.8</maven.compiler.source>
3131
<maven.compiler.target>1.8</maven.compiler.target>
32-
<java-utils.version>1.7.3</java-utils.version>
32+
<java-utils.version>1.8.0</java-utils.version>
3333
<surefire-plugin.version>2.22.0</surefire-plugin.version>
3434
<source-plugin.version>3.0.1</source-plugin.version>
3535
<javadoc-plugin.version>2.10.4</javadoc-plugin.version>
3636
<settings.version>2.2.2</settings.version>
3737
<junit.version>4.12</junit.version>
3838
<testng.version>6.10</testng.version>
39-
<bytebuddy.version>1.9.3</bytebuddy.version>
39+
<bytebuddy.version>1.9.5</bytebuddy.version>
4040
<logback.version>1.2.2</logback.version>
4141
<gpg-plugin.version>1.6</gpg-plugin.version>
4242
<staging-plugin.version>1.6.7</staging-plugin.version>
@@ -182,8 +182,6 @@
182182
<groupId>org.apache.maven.plugins</groupId>
183183
<artifactId>maven-surefire-plugin</artifactId>
184184
<configuration>
185-
<parallel>classes</parallel>
186-
<threadCount>5</threadCount>
187185
<argLine>-javaagent:src/test/resources/test-agent.jar</argLine>
188186
</configuration>
189187
</plugin>
Lines changed: 5 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
package com.nordstrom.automation.junit;
22

3-
import java.util.Collections;
4-
import java.util.HashMap;
53
import java.util.Map;
64
import java.util.Optional;
75

86
import org.junit.runner.Description;
97

8+
import com.nordstrom.common.params.Params;
9+
1010
/**
1111
* This interface enables implementers to provide method-related parameters to the artifact capture framework.
1212
*/
13-
public interface ArtifactParams {
13+
public interface ArtifactParams extends Params {
1414

1515
/**
1616
* Get the JUnit method description object for the current test class instance.
@@ -19,30 +19,14 @@ public interface ArtifactParams {
1919
*/
2020
Description getDescription();
2121

22-
/**
23-
* Get the parameters associated with this test class instance.
24-
*
25-
* @return optional map of named test class parameters
26-
*/
27-
default Optional<Map<String, Object>> getParameters() {
28-
return Optional.empty();
29-
}
30-
3122
/**
3223
* Assemble a map of test class instance parameters.
3324
*
3425
* @param params array of {@link Param} objects; may be {@code null} or empty
3526
* @return optional map of parameters (may be empty)
3627
*/
3728
public static Optional<Map<String, Object>> mapOf(Param... params) {
38-
if ((params == null) || (params.length == 0)) {
39-
return Optional.empty();
40-
}
41-
Map<String, Object> paramMap = new HashMap<>();
42-
for (Param param : params) {
43-
paramMap.put(param.key, param.val);
44-
}
45-
return Optional.of(Collections.unmodifiableMap(paramMap));
29+
return Params.mapOf(params);
4630
}
4731

4832
/**
@@ -53,30 +37,6 @@ public static Optional<Map<String, Object>> mapOf(Param... params) {
5337
* @return test parameter object
5438
*/
5539
public static Param param(String key, Object val) {
56-
return new Param(key, val);
40+
return Params.param(key, val);
5741
}
58-
59-
/**
60-
* This class defines a test parameter object.
61-
*/
62-
static class Param {
63-
64-
private final String key;
65-
private final Object val;
66-
67-
/**
68-
* Constructor for test parameter object.
69-
*
70-
* @param key test parameter key
71-
* @param val test parameter value
72-
*/
73-
public Param(String key, Object val) {
74-
if ((key == null) || key.isEmpty()) {
75-
throw new IllegalArgumentException("[key] must be a non-empty string");
76-
}
77-
this.key = key;
78-
this.val = val;
79-
}
80-
}
81-
8242
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.nordstrom.automation.junit;
2+
3+
import java.util.Map;
4+
import java.util.ServiceLoader;
5+
import java.util.concurrent.Callable;
6+
import java.util.concurrent.ConcurrentHashMap;
7+
8+
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
9+
import net.bytebuddy.implementation.bind.annotation.SuperCall;
10+
import net.bytebuddy.implementation.bind.annotation.This;
11+
12+
/**
13+
* This class declares the interceptor for the {@link org.junit.runners.BlockJUnit4ClassRunner#createTest
14+
* createTest} method.
15+
*/
16+
@SuppressWarnings("squid:S1118")
17+
public class CreateTest {
18+
19+
private static final ServiceLoader<TestObjectWatcher> objectWatcherLoader;
20+
private static final Map<Object, Object> TARGET_TO_RUNNER = new ConcurrentHashMap<>();
21+
private static final Map<Object, Object> RUNNER_TO_TARGET = new ConcurrentHashMap<>();
22+
private static final ThreadLocal<DepthGauge> DEPTH = ThreadLocal.withInitial(DepthGauge::new);
23+
24+
static {
25+
objectWatcherLoader = ServiceLoader.load(TestObjectWatcher.class);
26+
}
27+
28+
/**
29+
* Interceptor for the {@link org.junit.runners.BlockJUnit4ClassRunner#createTest createTest} method.
30+
*
31+
* @param runner target {@link org.junit.runners.BlockJUnit4ClassRunner BlockJUnit4ClassRunner} object
32+
* @param proxy callable proxy for the intercepted method
33+
* @return {@code anything} - JUnit test class instance
34+
* @throws Exception {@code anything} (exception thrown by the intercepted method)
35+
*/
36+
@RuntimeType
37+
public static Object intercept(@This final Object runner,
38+
@SuperCall final Callable<?> proxy) throws Exception {
39+
40+
Object testObj;
41+
try {
42+
DEPTH.get().increaseDepth();
43+
testObj = LifecycleHooks.callProxy(proxy);
44+
} finally {
45+
DEPTH.get().decreaseDepth();
46+
}
47+
48+
TARGET_TO_RUNNER.put(testObj, runner);
49+
RUNNER_TO_TARGET.put(runner, testObj);
50+
LifecycleHooks.applyTimeout(testObj);
51+
52+
if (DEPTH.get().atGroundLevel()) {
53+
synchronized(objectWatcherLoader) {
54+
for (TestObjectWatcher watcher : objectWatcherLoader) {
55+
watcher.testObjectCreated(testObj, runner);
56+
}
57+
}
58+
}
59+
60+
return testObj;
61+
}
62+
63+
/**
64+
* Get the class runner associated with the specified instance.
65+
*
66+
* @param target instance of JUnit test class
67+
* @return {@link org.junit.runners.BlockJUnit4ClassRunner BlockJUnit4ClassRunner} for specified instance
68+
*/
69+
static Object getRunnerForTarget(Object target) {
70+
return TARGET_TO_RUNNER.get(target);
71+
}
72+
73+
/**
74+
* Get the JUnit test class instance for the specified class runner.
75+
*
76+
* @param runner JUnit class runner
77+
* @return JUnit test class instance for specified runner
78+
*/
79+
static Object getTargetForRunner(Object runner) {
80+
return RUNNER_TO_TARGET.get(runner);
81+
}
82+
}

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

Lines changed: 0 additions & 75 deletions
This file was deleted.

0 commit comments

Comments
 (0)