Skip to content

Commit 6052f1e

Browse files
committed
[java] Allow EventFiringDecorator to throw exceptions #16470
WebDriverListener has now the throwsExceptions method to configure its behavior with regard to exception management. By default, it returns false, meaning exceptions are suppressed. If overridden to return true, exceptions occurred in the listener execution will be rethrown, so to allow users to manage them on their side. Fixes #16470
1 parent 912f35e commit 6052f1e

File tree

4 files changed

+203
-6
lines changed

4 files changed

+203
-6
lines changed

java/src/org/openqa/selenium/support/events/EventFiringDecorator.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,10 @@
154154
*
155155
* <p>Just be careful to not block the current thread in a listener method!
156156
*
157-
* <p>Listeners can't affect driver behavior too much. They can't throw any exceptions (they can,
158-
* but the decorator suppresses these exceptions), can't prevent execution of the decorated methods,
159-
* can't modify parameters and results of the methods.
157+
* <p>Listeners can't affect driver behavior too much. They can't prevent execution of the decorated
158+
* methods, can't modify parameters and results of the methods. They can throw exceptions only if
159+
* configured to do so by overriding {@link WebDriverListener#throwsExceptions}. By default,
160+
* exceptions occurred in listeners execution are suppressed.
160161
*
161162
* <p>Decorators that modify the behaviour of the underlying drivers should be implemented by
162163
* extending {@link WebDriverDecorator}, not by creating sophisticated listeners.
@@ -217,6 +218,10 @@ private void fireBeforeEvents(
217218
listener.beforeAnyCall(target.getOriginal(), method, args);
218219
} catch (Throwable t) {
219220
LOG.log(Level.WARNING, t.getMessage(), t);
221+
222+
if (listener.throwsExceptions()) {
223+
throw t;
224+
}
220225
}
221226

222227
try {
@@ -240,6 +245,10 @@ private void fireBeforeEvents(
240245
}
241246
} catch (Throwable t) {
242247
LOG.log(Level.WARNING, t.getMessage(), t);
248+
249+
if (listener.throwsExceptions()) {
250+
throw t;
251+
}
243252
}
244253

245254
String methodName = createEventMethodName("before", method.getName());
@@ -291,12 +300,20 @@ private void fireAfterEvents(
291300
}
292301
} catch (Throwable t) {
293302
LOG.log(Level.WARNING, t.getMessage(), t);
303+
304+
if (listener.throwsExceptions()) {
305+
throw t;
306+
}
294307
}
295308

296309
try {
297310
listener.afterAnyCall(target.getOriginal(), method, args, res);
298311
} catch (Throwable t) {
299312
LOG.log(Level.WARNING, t.getMessage(), t);
313+
314+
if (listener.throwsExceptions()) {
315+
throw t;
316+
}
300317
}
301318
}
302319

@@ -355,6 +372,10 @@ private void callListenerMethod(Method m, WebDriverListener listener, Object[] a
355372
m.invoke(listener, args);
356373
} catch (Throwable t) {
357374
LOG.log(Level.WARNING, t.getMessage(), t);
375+
376+
if (listener.throwsExceptions()) {
377+
throw new WebDriverListenerException(m, t);
378+
}
358379
}
359380
}
360381
}

java/src/org/openqa/selenium/support/events/WebDriverListener.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@
5050
@Beta
5151
public interface WebDriverListener {
5252

53+
// Listener configuration
54+
55+
/**
56+
* This method configures the behavior of the listener with regard to exceptions occurred during
57+
* its execution. By default, exceptions are suppressed.
58+
*
59+
* @return false by default. Override it and return true to throw exceptions instead.
60+
*/
61+
default boolean throwsExceptions() {
62+
return false;
63+
}
64+
5365
// Global
5466

5567
/**
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium.support.events;
19+
20+
import java.lang.reflect.Method;
21+
import java.util.Arrays;
22+
23+
public class WebDriverListenerException extends RuntimeException {
24+
25+
public WebDriverListenerException(Method method, Throwable cause) {
26+
super(
27+
"Exception executing method "
28+
+ method.getName()
29+
+ " with args "
30+
+ Arrays.toString(method.getParameters()),
31+
cause);
32+
}
33+
}

java/test/org/openqa/selenium/support/events/EventFiringDecoratorTest.java

Lines changed: 134 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@
1717

1818
package org.openqa.selenium.support.events;
1919

20-
import static org.assertj.core.api.Assertions.assertThat;
21-
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
22-
import static org.assertj.core.api.Assertions.assertThatNoException;
20+
import static org.assertj.core.api.Assertions.*;
2321
import static org.mockito.ArgumentMatchers.any;
2422
import static org.mockito.Mockito.doNothing;
2523
import static org.mockito.Mockito.mock;
@@ -1050,6 +1048,28 @@ public void beforeAnyCall(Object target, Method method, Object[] args) {
10501048
assertThatNoException().isThrownBy(decorated::getWindowHandle);
10511049
}
10521050

1051+
@Test
1052+
void shouldReThrowExceptionInBeforeAnyCall() {
1053+
WebDriver driver = mock(WebDriver.class);
1054+
WebDriverListener listener =
1055+
new WebDriverListener() {
1056+
1057+
@Override
1058+
public boolean throwsExceptions() {
1059+
return true;
1060+
}
1061+
1062+
@Override
1063+
public void beforeAnyCall(Object target, Method method, Object[] args) {
1064+
throw new RuntimeException("listener");
1065+
}
1066+
};
1067+
1068+
WebDriver decorated = new EventFiringDecorator<>(listener).decorate(driver);
1069+
1070+
assertThatRuntimeException().isThrownBy(decorated::getWindowHandle);
1071+
}
1072+
10531073
@Test
10541074
void shouldSuppressExceptionInBeforeClassMethodCall() {
10551075
WebDriver driver = mock(WebDriver.class);
@@ -1066,6 +1086,28 @@ public void beforeAnyWebDriverCall(WebDriver driver, Method method, Object[] arg
10661086
assertThatNoException().isThrownBy(decorated::getWindowHandle);
10671087
}
10681088

1089+
@Test
1090+
void shouldReThrowExceptionInBeforeClassMethodCall() {
1091+
WebDriver driver = mock(WebDriver.class);
1092+
WebDriverListener listener =
1093+
new WebDriverListener() {
1094+
1095+
@Override
1096+
public boolean throwsExceptions() {
1097+
return true;
1098+
}
1099+
1100+
@Override
1101+
public void beforeAnyWebDriverCall(WebDriver driver, Method method, Object[] args) {
1102+
throw new RuntimeException("listener");
1103+
}
1104+
};
1105+
1106+
WebDriver decorated = new EventFiringDecorator<>(listener).decorate(driver);
1107+
1108+
assertThatRuntimeException().isThrownBy(decorated::getWindowHandle);
1109+
}
1110+
10691111
@Test
10701112
void shouldSuppressExceptionInBeforeMethod() {
10711113
WebDriver driver = mock(WebDriver.class);
@@ -1082,6 +1124,28 @@ public void beforeGetWindowHandle(WebDriver driver) {
10821124
assertThatNoException().isThrownBy(decorated::getWindowHandle);
10831125
}
10841126

1127+
@Test
1128+
void shouldReThrowExceptionInBeforeMethod() {
1129+
WebDriver driver = mock(WebDriver.class);
1130+
WebDriverListener listener =
1131+
new WebDriverListener() {
1132+
1133+
@Override
1134+
public boolean throwsExceptions() {
1135+
return true;
1136+
}
1137+
1138+
@Override
1139+
public void beforeGetWindowHandle(WebDriver driver) {
1140+
throw new RuntimeException("listener");
1141+
}
1142+
};
1143+
1144+
WebDriver decorated = new EventFiringDecorator<>(listener).decorate(driver);
1145+
1146+
assertThatRuntimeException().isThrownBy(decorated::getWindowHandle);
1147+
}
1148+
10851149
@Test
10861150
void shouldSuppressExceptionInAfterAnyCall() {
10871151
WebDriver driver = mock(WebDriver.class);
@@ -1098,6 +1162,28 @@ public void afterAnyCall(Object target, Method method, Object[] args, Object res
10981162
assertThatNoException().isThrownBy(decorated::getWindowHandle);
10991163
}
11001164

1165+
@Test
1166+
void shouldReThrowExceptionInAfterAnyCall() {
1167+
WebDriver driver = mock(WebDriver.class);
1168+
WebDriverListener listener =
1169+
new WebDriverListener() {
1170+
1171+
@Override
1172+
public boolean throwsExceptions() {
1173+
return true;
1174+
}
1175+
1176+
@Override
1177+
public void afterAnyCall(Object target, Method method, Object[] args, Object result) {
1178+
throw new RuntimeException("listener");
1179+
}
1180+
};
1181+
1182+
WebDriver decorated = new EventFiringDecorator<>(listener).decorate(driver);
1183+
1184+
assertThatRuntimeException().isThrownBy(decorated::getWindowHandle);
1185+
}
1186+
11011187
@Test
11021188
void shouldSuppressExceptionInAfterClassMethodCall() {
11031189
WebDriver driver = mock(WebDriver.class);
@@ -1115,6 +1201,29 @@ public void afterAnyWebDriverCall(
11151201
assertThatNoException().isThrownBy(decorated::getWindowHandle);
11161202
}
11171203

1204+
@Test
1205+
void shouldReThrowExceptionInAfterClassMethodCall() {
1206+
WebDriver driver = mock(WebDriver.class);
1207+
WebDriverListener listener =
1208+
new WebDriverListener() {
1209+
1210+
@Override
1211+
public boolean throwsExceptions() {
1212+
return true;
1213+
}
1214+
1215+
@Override
1216+
public void afterAnyWebDriverCall(
1217+
WebDriver driver, Method method, Object[] args, Object result) {
1218+
throw new RuntimeException("listener");
1219+
}
1220+
};
1221+
1222+
WebDriver decorated = new EventFiringDecorator<>(listener).decorate(driver);
1223+
1224+
assertThatRuntimeException().isThrownBy(decorated::getWindowHandle);
1225+
}
1226+
11181227
@Test
11191228
void shouldSuppressExceptionInAfterMethod() {
11201229
WebDriver driver = mock(WebDriver.class);
@@ -1131,6 +1240,28 @@ public void afterGetWindowHandle(WebDriver driver, String result) {
11311240
assertThatNoException().isThrownBy(decorated::getWindowHandle);
11321241
}
11331242

1243+
@Test
1244+
void shouldReThrowExceptionInAfterMethod() {
1245+
WebDriver driver = mock(WebDriver.class);
1246+
WebDriverListener listener =
1247+
new WebDriverListener() {
1248+
1249+
@Override
1250+
public boolean throwsExceptions() {
1251+
return true;
1252+
}
1253+
1254+
@Override
1255+
public void afterGetWindowHandle(WebDriver driver, String result) {
1256+
throw new RuntimeException("listener");
1257+
}
1258+
};
1259+
1260+
WebDriver decorated = new EventFiringDecorator<>(listener).decorate(driver);
1261+
1262+
assertThatRuntimeException().isThrownBy(decorated::getWindowHandle);
1263+
}
1264+
11341265
@Test
11351266
void shouldSuppressExceptionInOnError() {
11361267
WebDriver driver = mock(WebDriver.class);

0 commit comments

Comments
 (0)