1515import net .bytebuddy .implementation .bind .annotation .SuperCall ;
1616import net .bytebuddy .implementation .bind .annotation .This ;
1717
18+ import static com .nordstrom .automation .junit .LifecycleHooks .toMapKey ;
19+
1820/**
1921 * This class declares the interceptor for the {@link org.junit.runners.ParentRunner#run run} method.
2022 */
2123@ SuppressWarnings ("squid:S1118" )
2224public class Run {
23- private static final ThreadLocal <Deque <Object >> runnerStack ;
24- private static final Set <String > startNotified = new CopyOnWriteArraySet <>();
25- private static final Set <String > finishNotified = new CopyOnWriteArraySet <>();
26- private static final Map <Object , Object > CHILD_TO_PARENT = new ConcurrentHashMap <>();
27- private static final Map <Object , RunNotifier > RUNNER_TO_NOTIFIER = new ConcurrentHashMap <>();
28- private static final Set <RunNotifier > NOTIFIERS = new CopyOnWriteArraySet <>();
25+ private static final ThreadLocal <Deque <Object >> RUNNER_STACK ;
26+ private static final Set <String > START_NOTIFIED = new CopyOnWriteArraySet <>();
27+ private static final Set <String > FINISH_NOTIFIED = new CopyOnWriteArraySet <>();
28+ private static final Map <String , Object > CHILD_TO_PARENT = new ConcurrentHashMap <>();
29+ private static final Map <String , RunNotifier > RUNNER_TO_NOTIFIER = new ConcurrentHashMap <>();
30+ private static final Set <String > NOTIFIERS = new CopyOnWriteArraySet <>();
2931 private static final Logger LOGGER = LoggerFactory .getLogger (Run .class );
3032
3133 static {
32- runnerStack = new ThreadLocal <Deque <Object >>() {
34+ RUNNER_STACK = new ThreadLocal <Deque <Object >>() {
3335 @ Override
3436 protected Deque <Object > initialValue () {
3537 return new ArrayDeque <>();
@@ -48,17 +50,17 @@ protected Deque<Object> initialValue() {
4850 public static void intercept (@ This final Object runner , @ SuperCall final Callable <?> proxy ,
4951 @ Argument (0 ) final RunNotifier notifier ) throws Exception {
5052
51- RUNNER_TO_NOTIFIER .put (runner , notifier );
52-
5353 attachRunListeners (runner , notifier );
5454
5555 try {
56+ RUNNER_TO_NOTIFIER .put (toMapKey (runner ), notifier );
5657 pushThreadRunner (runner );
5758 fireRunStarted (runner );
5859 LifecycleHooks .callProxy (proxy );
5960 } finally {
6061 fireRunFinished (runner );
6162 popThreadRunner ();
63+ RUNNER_TO_NOTIFIER .remove (toMapKey (runner ));
6264 }
6365 }
6466
@@ -69,7 +71,7 @@ public static void intercept(@This final Object runner, @SuperCall final Callabl
6971 * @return {@code ParentRunner} object that owns the specified child ({@code null} for root objects)
7072 */
7173 static Object getParentOf (final Object child ) {
72- return CHILD_TO_PARENT .get (child );
74+ return CHILD_TO_PARENT .get (toMapKey ( child ) );
7375 }
7476
7577 /**
@@ -79,7 +81,7 @@ static Object getParentOf(final Object child) {
7981 * @return <b>RunNotifier</b> object (may be {@code null})
8082 */
8183 static RunNotifier getNotifierOf (final Object runner ) {
82- return RUNNER_TO_NOTIFIER .get (runner );
84+ return RUNNER_TO_NOTIFIER .get (toMapKey ( runner ) );
8385 }
8486
8587 /**
@@ -92,7 +94,7 @@ static RunNotifier getNotifierOf(final Object runner) {
9294 * @throws Exception if {@code run-started} notification
9395 */
9496 static void attachRunListeners (Object runner , final RunNotifier notifier ) throws Exception {
95- if (NOTIFIERS .add (notifier )) {
97+ if (NOTIFIERS .add (toMapKey ( notifier ) )) {
9698 Description description = LifecycleHooks .invoke (runner , "getDescription" );
9799 for (RunListener listener : LifecycleHooks .getRunListeners ()) {
98100 notifier .addListener (listener );
@@ -107,7 +109,7 @@ static void attachRunListeners(Object runner, final RunNotifier notifier) throws
107109 * @param runner JUnit test runner
108110 */
109111 static void pushThreadRunner (final Object runner ) {
110- runnerStack .get ().push (runner );
112+ RUNNER_STACK .get ().push (runner );
111113 }
112114
113115 /**
@@ -117,7 +119,7 @@ static void pushThreadRunner(final Object runner) {
117119 * @throws EmptyStackException if called outside the scope of an active runner
118120 */
119121 static Object popThreadRunner () {
120- return runnerStack .get ().pop ();
122+ return RUNNER_STACK .get ().pop ();
121123 }
122124
123125 /**
@@ -126,7 +128,7 @@ static Object popThreadRunner() {
126128 * @return active {@code ParentRunner} object
127129 */
128130 static Object getThreadRunner () {
129- return runnerStack .get ().peek ();
131+ return RUNNER_STACK .get ().peek ();
130132 }
131133
132134 /**
@@ -137,10 +139,10 @@ static Object getThreadRunner() {
137139 * @return {@code true} if event the {@code runStarted} was fired; otherwise {@code false}
138140 */
139141 static boolean fireRunStarted (Object runner ) {
140- if (startNotified .add (runner . toString ( ))) {
141- List <?> grandchildren = LifecycleHooks .invoke (runner , "getChildren" );
142- for (Object grandchild : grandchildren ) {
143- CHILD_TO_PARENT .put (grandchild , runner );
142+ if (START_NOTIFIED .add (toMapKey ( runner ))) {
143+ List <?> children = LifecycleHooks .invoke (runner , "getChildren" );
144+ for (Object child : children ) {
145+ CHILD_TO_PARENT .put (toMapKey ( child ) , runner );
144146 }
145147 LOGGER .debug ("runStarted: {}" , runner );
146148 for (RunnerWatcher watcher : LifecycleHooks .getRunnerWatchers ()) {
@@ -155,11 +157,12 @@ static boolean fireRunStarted(Object runner) {
155157 * Fire the {@link RunnerWatcher#runFinished(Object)} event for the specified runner.
156158 * <p>
157159 * <b>NOTE</b>: If {@code runFinished} for the specified runner has already been fired, do nothing.
160+ *
158161 * @param runner JUnit test runner
159162 * @return {@code true} if event the {@code runFinished} was fired; otherwise {@code false}
160163 */
161164 static boolean fireRunFinished (Object runner ) {
162- if (finishNotified .add (runner . toString ( ))) {
165+ if (FINISH_NOTIFIED .add (toMapKey ( runner ))) {
163166 LOGGER .debug ("runFinished: {}" , runner );
164167 for (RunnerWatcher watcher : LifecycleHooks .getRunnerWatchers ()) {
165168 watcher .runFinished (runner );
@@ -168,4 +171,16 @@ static boolean fireRunFinished(Object runner) {
168171 }
169172 return false ;
170173 }
174+
175+ /**
176+ * Release runner/child mappings.
177+ *
178+ * @param runner JUnit test runner
179+ */
180+ static void releaseChidrenOf (Object runner ) {
181+ List <?> children = LifecycleHooks .invoke (runner , "getChildren" );
182+ for (Object child : children ) {
183+ CHILD_TO_PARENT .remove (toMapKey (child ));
184+ }
185+ }
171186}
0 commit comments