diff --git a/java/src/org/openqa/selenium/Platform.java b/java/src/org/openqa/selenium/Platform.java index 49c69515bc456..4f950b8239360 100644 --- a/java/src/org/openqa/selenium/Platform.java +++ b/java/src/org/openqa/selenium/Platform.java @@ -416,7 +416,7 @@ public static Platform getCurrent() { * @param osName the operating system name to determine the platform of * @return the most likely platform based on given operating system name */ - public static Platform extractFromSysProperty(String osName) { + public static Platform extractFromSysProperty(@Nullable String osName) { return extractFromSysProperty(osName, System.getProperty("os.version", "")); } @@ -429,7 +429,8 @@ public static Platform extractFromSysProperty(String osName) { * @param osVersion the operating system version to determine the platform of * @return the most likely platform based on given operating system name and version */ - public static Platform extractFromSysProperty(String osName, String osVersion) { + public static Platform extractFromSysProperty( + @Nullable String osName, @Nullable String osVersion) { osName = requireNonNullElse(osName, ""); osVersion = requireNonNullElse(osVersion, ""); osName = osName.toLowerCase(Locale.ENGLISH); @@ -516,6 +517,7 @@ public String[] getPartOfOsName() { * @return true if platforms are approximately similar, false otherwise */ public boolean is(Platform compareWith) { + Platform family = this.family(); return // Any platform is itself this == compareWith @@ -524,7 +526,7 @@ public boolean is(Platform compareWith) { compareWith == ANY || // And any Platform which is not a platform type belongs to the same family - (this.family() != null && this.family().is(compareWith)); + (family != null && family.is(compareWith)); } /** diff --git a/java/src/org/openqa/selenium/bidi/BiDi.java b/java/src/org/openqa/selenium/bidi/BiDi.java index 2f1bdb38bfe5d..50b4ae35ca4f3 100644 --- a/java/src/org/openqa/selenium/bidi/BiDi.java +++ b/java/src/org/openqa/selenium/bidi/BiDi.java @@ -25,9 +25,12 @@ import java.util.Map; import java.util.Set; import java.util.function.Consumer; +import java.util.logging.Logger; +import org.openqa.selenium.WebDriverException; import org.openqa.selenium.internal.Require; public class BiDi implements Closeable { + private static final Logger LOG = Logger.getLogger(BiDi.class.getName()); private final Duration timeout; private final Connection connection; @@ -47,9 +50,19 @@ public BiDi(Connection connection, Duration timeout) { @Override public void close() { - clearListeners(); + try { + clearListeners(); + } catch (WebDriverException e) { + LOG.warning(() -> "Failed to clear BiDi listeners: " + e); + } + disconnectSession(); - connection.close(); + + try { + connection.close(); + } catch (WebDriverException e) { + LOG.warning(() -> "Failed to close BiDi connection: " + e); + } } public void disconnectSession() { diff --git a/java/src/org/openqa/selenium/chrome/ChromeDriverService.java b/java/src/org/openqa/selenium/chrome/ChromeDriverService.java index fa2bee0167044..d19610cf8bf37 100644 --- a/java/src/org/openqa/selenium/chrome/ChromeDriverService.java +++ b/java/src/org/openqa/selenium/chrome/ChromeDriverService.java @@ -104,7 +104,7 @@ public class ChromeDriverService extends DriverService { public ChromeDriverService( @Nullable File executable, int port, - @Nullable Duration timeout, + Duration timeout, @Nullable List args, @Nullable Map environment) throws IOException { @@ -328,7 +328,7 @@ protected List createArgs() { protected ChromeDriverService createDriverService( @Nullable File exe, int port, - @Nullable Duration timeout, + Duration timeout, @Nullable List args, @Nullable Map environment) { try { diff --git a/java/src/org/openqa/selenium/devtools/Connection.java b/java/src/org/openqa/selenium/devtools/Connection.java index 90194266957ab..9a91094d85b8f 100644 --- a/java/src/org/openqa/selenium/devtools/Connection.java +++ b/java/src/org/openqa/selenium/devtools/Connection.java @@ -47,6 +47,7 @@ import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.devtools.idealized.target.model.SessionID; @@ -242,6 +243,7 @@ public void close() { this.isClosed.set(true); } + @NullMarked private class Listener implements WebSocket.Listener { @Override diff --git a/java/src/org/openqa/selenium/edge/EdgeDriverService.java b/java/src/org/openqa/selenium/edge/EdgeDriverService.java index 2ba4459ebb519..689822ea03dce 100644 --- a/java/src/org/openqa/selenium/edge/EdgeDriverService.java +++ b/java/src/org/openqa/selenium/edge/EdgeDriverService.java @@ -100,7 +100,7 @@ public class EdgeDriverService extends DriverService { public EdgeDriverService( @Nullable File executable, int port, - @Nullable Duration timeout, + Duration timeout, @Nullable List args, @Nullable Map environment) throws IOException { @@ -335,7 +335,7 @@ protected List createArgs() { protected EdgeDriverService createDriverService( @Nullable File exe, int port, - @Nullable Duration timeout, + Duration timeout, @Nullable List args, @Nullable Map environment) { try { diff --git a/java/src/org/openqa/selenium/firefox/FirefoxDriverService.java b/java/src/org/openqa/selenium/firefox/FirefoxDriverService.java index 69f7626fe8244..b93abba875c7f 100644 --- a/java/src/org/openqa/selenium/firefox/FirefoxDriverService.java +++ b/java/src/org/openqa/selenium/firefox/FirefoxDriverService.java @@ -38,7 +38,7 @@ public abstract class FirefoxDriverService extends DriverService { protected FirefoxDriverService( @Nullable File executable, int port, - @Nullable Duration timeout, + Duration timeout, @Nullable List args, @Nullable Map environment) throws IOException { diff --git a/java/src/org/openqa/selenium/grid/web/ReverseProxyHandler.java b/java/src/org/openqa/selenium/grid/web/ReverseProxyHandler.java index b68f11317077f..63b09f85a2bb1 100644 --- a/java/src/org/openqa/selenium/grid/web/ReverseProxyHandler.java +++ b/java/src/org/openqa/selenium/grid/web/ReverseProxyHandler.java @@ -67,11 +67,10 @@ public HttpResponse execute(HttpRequest req) throws UncheckedIOException { toUpstream.setAttribute(attributeName, req.getAttribute(attributeName)); } - for (String name : req.getQueryParameterNames()) { - for (String value : req.getQueryParameters(name)) { - toUpstream.addQueryParameter(name, value); - } - } + req.forEachQueryParameter( + (name, value) -> { + toUpstream.addQueryParameter(name, value); + }); req.forEachHeader( (name, value) -> { diff --git a/java/src/org/openqa/selenium/ie/InternetExplorerDriverService.java b/java/src/org/openqa/selenium/ie/InternetExplorerDriverService.java index f02cfa5dd43aa..3a6991d80f8d1 100644 --- a/java/src/org/openqa/selenium/ie/InternetExplorerDriverService.java +++ b/java/src/org/openqa/selenium/ie/InternetExplorerDriverService.java @@ -79,7 +79,7 @@ public class InternetExplorerDriverService extends DriverService { public InternetExplorerDriverService( @Nullable File executable, int port, - @Nullable Duration timeout, + Duration timeout, @Nullable List args, @Nullable Map environment) throws IOException { @@ -240,7 +240,7 @@ protected List createArgs() { protected InternetExplorerDriverService createDriverService( @Nullable File exe, int port, - @Nullable Duration timeout, + Duration timeout, @Nullable List args, @Nullable Map environment) { try { diff --git a/java/src/org/openqa/selenium/json/Json.java b/java/src/org/openqa/selenium/json/Json.java index 554e78854202f..94bbcf6dc459a 100644 --- a/java/src/org/openqa/selenium/json/Json.java +++ b/java/src/org/openqa/selenium/json/Json.java @@ -26,6 +26,7 @@ import java.lang.reflect.Type; import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.internal.Require; /** @@ -116,7 +117,7 @@ public class Json { * @param toConvert the object to be serialized * @return JSON string representing the specified object */ - public String toJson(Object toConvert) { + public String toJson(@Nullable Object toConvert) { return toJson(toConvert, JsonOutput.MAX_DEPTH); } @@ -128,7 +129,7 @@ public String toJson(Object toConvert) { * @return JSON string representing the specified object * @throws JsonException if an I/O exception is encountered */ - public String toJson(Object toConvert, int maxDepth) { + public String toJson(@Nullable Object toConvert, int maxDepth) { try (Writer writer = new StringWriter(); JsonOutput jsonOutput = newOutput(writer)) { jsonOutput.write(toConvert, maxDepth); diff --git a/java/src/org/openqa/selenium/remote/AbstractDriverOptions.java b/java/src/org/openqa/selenium/remote/AbstractDriverOptions.java index 9017c3d4d9f75..2ff349cd97bb1 100644 --- a/java/src/org/openqa/selenium/remote/AbstractDriverOptions.java +++ b/java/src/org/openqa/selenium/remote/AbstractDriverOptions.java @@ -17,6 +17,8 @@ package org.openqa.selenium.remote; +import static java.util.Collections.unmodifiableMap; +import static java.util.Objects.requireNonNull; import static org.openqa.selenium.remote.CapabilityType.ACCEPT_INSECURE_CERTS; import static org.openqa.selenium.remote.CapabilityType.BROWSER_VERSION; import static org.openqa.selenium.remote.CapabilityType.ENABLE_DOWNLOADS; @@ -34,7 +36,6 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.openqa.selenium.MutableCapabilities; import org.openqa.selenium.PageLoadStrategy; @@ -58,7 +59,7 @@ public DO setImplicitWaitTimeout(Duration timeout) { Map timeouts = getTimeouts(); timeouts.put("implicit", timeout.toMillis()); - setCapability(TIMEOUTS, Collections.unmodifiableMap(timeouts)); + setCapability(TIMEOUTS, unmodifiableMap(timeouts)); return self(); } @@ -66,7 +67,7 @@ public DO setPageLoadTimeout(Duration timeout) { Map timeouts = getTimeouts(); timeouts.put("pageLoad", timeout.toMillis()); - setCapability(TIMEOUTS, Collections.unmodifiableMap(timeouts)); + setCapability(TIMEOUTS, unmodifiableMap(timeouts)); return self(); } @@ -74,7 +75,7 @@ public DO setScriptTimeout(Duration timeout) { Map timeouts = getTimeouts(); timeouts.put("script", timeout.toMillis()); - setCapability(TIMEOUTS, Collections.unmodifiableMap(timeouts)); + setCapability(TIMEOUTS, unmodifiableMap(timeouts)); return self(); } @@ -109,7 +110,6 @@ public DO setEnableDownloads(boolean enableDownloads) { return self(); } - @NonNull @SuppressWarnings("unchecked") private DO self() { return (DO) this; @@ -125,7 +125,7 @@ public Set getCapabilityNames() { protected abstract Set getExtraCapabilityNames(); @Override - public Object getCapability(String capabilityName) { + public @Nullable Object getCapability(String capabilityName) { Require.nonNull("Capability name", capabilityName); if (getExtraCapabilityNames().contains(capabilityName)) { @@ -140,8 +140,9 @@ public Object getCapability(String capabilityName) { @Override public Map asMap() { Map toReturn = new TreeMap<>(super.asMap()); - getExtraCapabilityNames().forEach(name -> toReturn.put(name, getCapability(name))); - return Collections.unmodifiableMap(toReturn); + getExtraCapabilityNames() + .forEach(name -> toReturn.put(name, requireNonNull(getCapability(name)))); + return unmodifiableMap(toReturn); } private Map getTimeouts() { diff --git a/java/src/org/openqa/selenium/remote/AddHasAuthentication.java b/java/src/org/openqa/selenium/remote/AddHasAuthentication.java index 9dd0cfdfb31ff..2005ca1cb6e4e 100644 --- a/java/src/org/openqa/selenium/remote/AddHasAuthentication.java +++ b/java/src/org/openqa/selenium/remote/AddHasAuthentication.java @@ -22,7 +22,6 @@ import static org.openqa.selenium.remote.Browser.OPERA; import java.util.function.Predicate; -import java.util.logging.Logger; import org.openqa.selenium.Capabilities; import org.openqa.selenium.HasAuthentication; import org.openqa.selenium.WebDriver; @@ -32,7 +31,6 @@ public class AddHasAuthentication implements AugmenterProvider { - private static final Logger LOG = Logger.getLogger(AddHasAuthentication.class.getName()); private static final Predicate IS_CHROMIUM_BROWSER = name -> CHROME.is(name) || EDGE.is(name) || OPERA.is(name); diff --git a/java/src/org/openqa/selenium/remote/AddHasLogEvents.java b/java/src/org/openqa/selenium/remote/AddHasLogEvents.java index 711ea2d8488ce..e274ef74d1bf1 100644 --- a/java/src/org/openqa/selenium/remote/AddHasLogEvents.java +++ b/java/src/org/openqa/selenium/remote/AddHasLogEvents.java @@ -22,7 +22,6 @@ import static org.openqa.selenium.remote.Browser.OPERA; import java.util.function.Predicate; -import org.jspecify.annotations.NullMarked; import org.openqa.selenium.Capabilities; import org.openqa.selenium.WebDriver; import org.openqa.selenium.devtools.HasDevTools; @@ -48,7 +47,6 @@ public Class getDescribedInterface() { public HasLogEvents getImplementation(Capabilities capabilities, ExecuteMethod executeMethod) { return new HasLogEvents() { @Override - @NullMarked public void onLogEvent(EventType kind) { if (((RemoteExecuteMethod) executeMethod).getWrappedDriver() instanceof HasDevTools) { WebDriver driver = ((RemoteExecuteMethod) executeMethod).getWrappedDriver(); diff --git a/java/src/org/openqa/selenium/remote/Command.java b/java/src/org/openqa/selenium/remote/Command.java index 09610f937d563..0004caafa228a 100644 --- a/java/src/org/openqa/selenium/remote/Command.java +++ b/java/src/org/openqa/selenium/remote/Command.java @@ -22,26 +22,28 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.json.JsonInput; public class Command { - private final SessionId sessionId; + private final @Nullable SessionId sessionId; private final CommandPayload payload; public Command(SessionId sessionId, String name) { this(sessionId, name, new HashMap<>()); } - public Command(SessionId sessionId, String name, Map parameters) { + public Command(@Nullable SessionId sessionId, String name, Map parameters) { this(sessionId, new CommandPayload(name, parameters)); } - public Command(SessionId sessionId, CommandPayload payload) { + public Command(@Nullable SessionId sessionId, CommandPayload payload) { this.sessionId = sessionId; this.payload = payload; } + @Nullable public SessionId getSessionId() { return sessionId; } @@ -105,6 +107,7 @@ private static Command fromJson(JsonInput input) { input.endObject(); + //noinspection DataFlowIssue return new Command(sessionId, name, parameters); } } diff --git a/java/src/org/openqa/selenium/remote/CommandCodec.java b/java/src/org/openqa/selenium/remote/CommandCodec.java index ded0284c2543b..afc5eedd9bc62 100644 --- a/java/src/org/openqa/selenium/remote/CommandCodec.java +++ b/java/src/org/openqa/selenium/remote/CommandCodec.java @@ -17,7 +17,6 @@ package org.openqa.selenium.remote; -import org.jspecify.annotations.NullMarked; import org.openqa.selenium.UnsupportedCommandException; import org.openqa.selenium.remote.http.HttpMethod; @@ -26,7 +25,6 @@ * * @param The type of encoded command. */ -@NullMarked public interface CommandCodec { /** diff --git a/java/src/org/openqa/selenium/remote/CommandInfo.java b/java/src/org/openqa/selenium/remote/CommandInfo.java index 343ec9428d577..7280bf6a849c5 100644 --- a/java/src/org/openqa/selenium/remote/CommandInfo.java +++ b/java/src/org/openqa/selenium/remote/CommandInfo.java @@ -17,10 +17,8 @@ package org.openqa.selenium.remote; -import org.jspecify.annotations.NullMarked; import org.openqa.selenium.remote.http.HttpMethod; -@NullMarked public class CommandInfo { private final String url; private final HttpMethod method; diff --git a/java/src/org/openqa/selenium/remote/CommandPayload.java b/java/src/org/openqa/selenium/remote/CommandPayload.java index fc60c4f3d2a3b..000811208ee87 100644 --- a/java/src/org/openqa/selenium/remote/CommandPayload.java +++ b/java/src/org/openqa/selenium/remote/CommandPayload.java @@ -18,18 +18,17 @@ package org.openqa.selenium.remote; import java.util.Map; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; +import org.openqa.selenium.internal.Require; -@NullMarked public class CommandPayload { private final String name; private final Map parameters; public CommandPayload(String name, Map parameters) { - this.name = name; - this.parameters = parameters; + this.name = Require.nonNull("name", name); + this.parameters = Require.nonNull("parameters", parameters); } public String getName() { diff --git a/java/src/org/openqa/selenium/remote/DesiredCapabilities.java b/java/src/org/openqa/selenium/remote/DesiredCapabilities.java index 518ceb80d8aae..4987149bc6993 100644 --- a/java/src/org/openqa/selenium/remote/DesiredCapabilities.java +++ b/java/src/org/openqa/selenium/remote/DesiredCapabilities.java @@ -96,7 +96,7 @@ public DesiredCapabilities setAcceptInsecureCerts(boolean acceptInsecureCerts) { * @return DesiredCapabilities after the merge */ @Override - public DesiredCapabilities merge(@Nullable Capabilities extraCapabilities) { + public DesiredCapabilities merge(Capabilities extraCapabilities) { Optional.ofNullable(extraCapabilities) .ifPresent(caps -> caps.asMap().forEach(this::setCapability)); return this; diff --git a/java/src/org/openqa/selenium/remote/DriverCommand.java b/java/src/org/openqa/selenium/remote/DriverCommand.java index 7102dfae5df17..56365f8e74428 100644 --- a/java/src/org/openqa/selenium/remote/DriverCommand.java +++ b/java/src/org/openqa/selenium/remote/DriverCommand.java @@ -24,7 +24,6 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.openqa.selenium.Capabilities; import org.openqa.selenium.Cookie; @@ -41,7 +40,6 @@ * * @author jmleyba@gmail.com (Jason Leyba) */ -@NullMarked public interface DriverCommand { String GET_CAPABILITIES = "getCapabilities"; String NEW_SESSION = "newSession"; diff --git a/java/src/org/openqa/selenium/remote/ErrorCodes.java b/java/src/org/openqa/selenium/remote/ErrorCodes.java index a32b758856612..495ab8bd8e033 100644 --- a/java/src/org/openqa/selenium/remote/ErrorCodes.java +++ b/java/src/org/openqa/selenium/remote/ErrorCodes.java @@ -29,6 +29,7 @@ import java.util.Set; import java.util.logging.Logger; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.ElementClickInterceptedException; import org.openqa.selenium.ElementNotInteractableException; import org.openqa.selenium.InvalidArgumentException; @@ -288,7 +289,7 @@ public class ErrorCodes { } } - public String toState(Integer status) { + public String toState(@Nullable Integer status) { if (status == null) { return toState(UNHANDLED_ERROR); } @@ -349,6 +350,7 @@ public int getHttpStatusCode(Throwable throwable) { * @return The exception type that corresponds to the provided status code or {@code null} if * {@code statusCode == 0}. */ + @Nullable public Class getExceptionType(int statusCode) { if (SUCCESS == statusCode) { return null; @@ -375,7 +377,7 @@ public Class getExceptionType(String webdriverStat return onlyElement(possibleMatches, WebDriverException.class); } - public int toStatusCode(Throwable e) { + public int toStatusCode(@Nullable Throwable e) { if (e == null) { return SUCCESS; } @@ -390,7 +392,7 @@ public int toStatusCode(Throwable e) { return onlyElement(possibleMatches, UNHANDLED_ERROR); } - public boolean isMappableError(Throwable rootCause) { + public boolean isMappableError(@Nullable Throwable rootCause) { if (rootCause == null) { return false; } diff --git a/java/src/org/openqa/selenium/remote/ErrorHandler.java b/java/src/org/openqa/selenium/remote/ErrorHandler.java index 63d658824d3b0..6156e89dbf02c 100644 --- a/java/src/org/openqa/selenium/remote/ErrorHandler.java +++ b/java/src/org/openqa/selenium/remote/ErrorHandler.java @@ -25,6 +25,7 @@ import java.util.Objects; import java.util.Optional; import java.util.function.Function; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.UnhandledAlertException; import org.openqa.selenium.WebDriverException; @@ -41,7 +42,7 @@ public class ErrorHandler { private static final String FILE_NAME = "fileName"; private static final String UNKNOWN_CLASS = ""; private static final String UNKNOWN_METHOD = ""; - private static final String UNKNOWN_FILE = null; + private static final @Nullable String UNKNOWN_FILE = null; private final ErrorCodes errorCodes; @@ -186,6 +187,7 @@ public Response throwIfResponseFailed(Response response, long duration) throws R throw toThrow; } + @Nullable @SuppressWarnings("unchecked") private UnhandledAlertException createUnhandledAlertException(Object value) { Map rawErrorData = (Map) value; @@ -226,6 +228,7 @@ private T createThrowable( return null; } + @Nullable private Throwable rebuildServerError(Map rawErrorData, int responseStatus) { if (rawErrorData.get(CLASS) == null && rawErrorData.get(STACK_TRACE) == null) { @@ -252,9 +255,9 @@ private Throwable rebuildServerError(Map rawErrorData, int respo clazz = errorCodes.getExceptionType(responseStatus); } - if (clazz.equals(UnhandledAlertException.class)) { + if (UnhandledAlertException.class.equals(clazz)) { toReturn = createUnhandledAlertException(rawErrorData); - } else if (Throwable.class.isAssignableFrom(clazz)) { + } else if (clazz != null && Throwable.class.isAssignableFrom(clazz)) { @SuppressWarnings({"unchecked"}) Class throwableType = (Class) clazz; toReturn = @@ -297,8 +300,9 @@ private UnknownServerException(String s) { */ private static class FrameInfoToStackFrame implements Function, StackTraceElement> { + @Nullable @Override - public StackTraceElement apply(Map frameInfo) { + public StackTraceElement apply(@Nullable Map frameInfo) { if (frameInfo == null) { return null; } @@ -339,7 +343,8 @@ public StackTraceElement apply(Map frameInfo) { return new StackTraceElement(className, methodName, fileName, lineNumber); } - private static String toStringOrNull(Object o) { + @Nullable + private static String toStringOrNull(@Nullable Object o) { return o == null ? null : o.toString(); } } diff --git a/java/src/org/openqa/selenium/remote/ExecuteMethod.java b/java/src/org/openqa/selenium/remote/ExecuteMethod.java index b65238c0d4262..03c73fbf0b959 100644 --- a/java/src/org/openqa/selenium/remote/ExecuteMethod.java +++ b/java/src/org/openqa/selenium/remote/ExecuteMethod.java @@ -21,14 +21,12 @@ import static java.util.Objects.requireNonNullElse; import java.util.Map; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; /** * An encapsulation of {@link org.openqa.selenium.remote.RemoteWebDriver#executeScript(String, * Object...)}. */ -@NullMarked public interface ExecuteMethod { /** * Execute the given command on the remote webdriver server. Any exceptions will be thrown by the diff --git a/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java b/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java index 19d0ee4295986..e189b874789af 100644 --- a/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java +++ b/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java @@ -20,12 +20,10 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.openqa.selenium.federatedcredentialmanagement.FederatedCredentialManagementAccount; import org.openqa.selenium.federatedcredentialmanagement.FederatedCredentialManagementDialog; -@NullMarked class FedCmDialogImpl implements FederatedCredentialManagementDialog { private final ExecuteMethod executeMethod; diff --git a/java/src/org/openqa/selenium/remote/FileDetector.java b/java/src/org/openqa/selenium/remote/FileDetector.java index 80d3069a61894..0200e704fc0ab 100644 --- a/java/src/org/openqa/selenium/remote/FileDetector.java +++ b/java/src/org/openqa/selenium/remote/FileDetector.java @@ -18,11 +18,9 @@ package org.openqa.selenium.remote; import java.io.File; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; /** Used for identifying whether a sequence of chars represents the path to a file. */ -@NullMarked public interface FileDetector { @Nullable File getLocalFile(CharSequence... keys); } diff --git a/java/src/org/openqa/selenium/remote/HandshakeResponse.java b/java/src/org/openqa/selenium/remote/HandshakeResponse.java index b88e5ec8c2551..e1782be674074 100644 --- a/java/src/org/openqa/selenium/remote/HandshakeResponse.java +++ b/java/src/org/openqa/selenium/remote/HandshakeResponse.java @@ -19,14 +19,16 @@ import java.util.Optional; import java.util.function.Function; +import org.jspecify.annotations.Nullable; interface HandshakeResponse { - Function errorHandler(); + Function errorHandler(); - Function successHandler(); + Function successHandler(); - default Function getResponseFunction() { + default Function + getResponseFunction() { return resp -> Optional.ofNullable(errorHandler().apply(resp)).orElse(successHandler().apply(resp)); } diff --git a/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java b/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java index 1cb56843bfabd..726d5177adb94 100644 --- a/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java +++ b/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java @@ -28,6 +28,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.NoSuchSessionException; import org.openqa.selenium.SessionNotCreatedException; import org.openqa.selenium.UnsupportedCommandException; @@ -49,8 +50,8 @@ public class HttpCommandExecutor implements CommandExecutor, NeedsLocalLogs { public final HttpClient client; protected final HttpClient.Factory httpClientFactory; protected final Map additionalCommands; - protected CommandCodec commandCodec; - protected ResponseCodec responseCodec; + protected @Nullable CommandCodec commandCodec; + protected @Nullable ResponseCodec responseCodec; @SuppressWarnings("deprecation") private LocalLogs logs = LocalLogs.getNullLogger(); diff --git a/java/src/org/openqa/selenium/remote/HttpSessionId.java b/java/src/org/openqa/selenium/remote/HttpSessionId.java index 9e092e683503e..930f2efae20cd 100644 --- a/java/src/org/openqa/selenium/remote/HttpSessionId.java +++ b/java/src/org/openqa/selenium/remote/HttpSessionId.java @@ -18,9 +18,7 @@ package org.openqa.selenium.remote; import java.util.Optional; -import org.jspecify.annotations.NullMarked; -@NullMarked public class HttpSessionId { private HttpSessionId() { diff --git a/java/src/org/openqa/selenium/remote/InvalidResponseException.java b/java/src/org/openqa/selenium/remote/InvalidResponseException.java index b6d3348b3f762..59ddd0d9265a2 100644 --- a/java/src/org/openqa/selenium/remote/InvalidResponseException.java +++ b/java/src/org/openqa/selenium/remote/InvalidResponseException.java @@ -18,9 +18,7 @@ package org.openqa.selenium.remote; import java.util.Map; -import org.jspecify.annotations.NullMarked; -@NullMarked public class InvalidResponseException extends IllegalArgumentException { private static final String w3cErrorFormat = "https://www.w3.org/TR/webdriver2/#errors"; diff --git a/java/src/org/openqa/selenium/remote/JsonToWebElementConverter.java b/java/src/org/openqa/selenium/remote/JsonToWebElementConverter.java index 9514de66d7bb2..cd10f43b43e29 100644 --- a/java/src/org/openqa/selenium/remote/JsonToWebElementConverter.java +++ b/java/src/org/openqa/selenium/remote/JsonToWebElementConverter.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebElement; /** @@ -30,9 +31,9 @@ */ public class JsonToWebElementConverter implements Function { - protected final RemoteWebDriver driver; + @Nullable protected final RemoteWebDriver driver; - public JsonToWebElementConverter(RemoteWebDriver driver) { + public JsonToWebElementConverter(@Nullable RemoteWebDriver driver) { this.driver = driver; } @@ -89,6 +90,7 @@ protected RemoteWebElement setOwner(RemoteWebElement element) { return element; } + @Nullable protected String getElementKey(Map resultAsMap) { for (Dialect d : Dialect.values()) { String elementKeyForDialect = d.getEncodedElementKey(); @@ -99,6 +101,7 @@ protected String getElementKey(Map resultAsMap) { return null; } + @Nullable protected String getShadowRootKey(Map resultAsMap) { for (Dialect d : Dialect.values()) { String shadowRootElementKey = d.getShadowRootElementKey(); diff --git a/java/src/org/openqa/selenium/remote/LocalExecuteMethod.java b/java/src/org/openqa/selenium/remote/LocalExecuteMethod.java index 84a6093dc8928..944b9365d8169 100644 --- a/java/src/org/openqa/selenium/remote/LocalExecuteMethod.java +++ b/java/src/org/openqa/selenium/remote/LocalExecuteMethod.java @@ -18,11 +18,9 @@ package org.openqa.selenium.remote; import java.util.Map; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebDriverException; -@NullMarked class LocalExecuteMethod implements ExecuteMethod { @Nullable @Override diff --git a/java/src/org/openqa/selenium/remote/LocalFileDetector.java b/java/src/org/openqa/selenium/remote/LocalFileDetector.java index 61bcbecac75cf..1873805164863 100644 --- a/java/src/org/openqa/selenium/remote/LocalFileDetector.java +++ b/java/src/org/openqa/selenium/remote/LocalFileDetector.java @@ -19,11 +19,9 @@ import java.io.File; import java.util.logging.Logger; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; /** Detects files on the local disk. */ -@NullMarked public class LocalFileDetector implements FileDetector { private static final Logger LOG = Logger.getLogger(LocalFileDetector.class.getName()); diff --git a/java/src/org/openqa/selenium/remote/NoSuchDriverException.java b/java/src/org/openqa/selenium/remote/NoSuchDriverException.java index c3ad3ea114e00..e001895bef270 100644 --- a/java/src/org/openqa/selenium/remote/NoSuchDriverException.java +++ b/java/src/org/openqa/selenium/remote/NoSuchDriverException.java @@ -17,13 +17,11 @@ package org.openqa.selenium.remote; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.remote.service.DriverFinder; /** Thrown by {@link DriverFinder#getDriverPath()} (DriverService, Capabilities)}. */ -@NullMarked public class NoSuchDriverException extends WebDriverException { private static final String SUPPORT_URL = BASE_SUPPORT_URL + "/driver_location/"; diff --git a/java/src/org/openqa/selenium/remote/ProtocolHandshake.java b/java/src/org/openqa/selenium/remote/ProtocolHandshake.java index f5bca85d9de82..bbf176b9f2b53 100644 --- a/java/src/org/openqa/selenium/remote/ProtocolHandshake.java +++ b/java/src/org/openqa/selenium/remote/ProtocolHandshake.java @@ -30,6 +30,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; import org.openqa.selenium.Proxy; @@ -133,7 +134,7 @@ private Either createSession( public static class Result { - private static final Function massageProxy = + private static final Function massageProxy = obj -> { if (obj instanceof Proxy) { return (Proxy) obj; diff --git a/java/src/org/openqa/selenium/remote/RemoteExecuteMethod.java b/java/src/org/openqa/selenium/remote/RemoteExecuteMethod.java index 9be7a20bfd2be..ac19193fad906 100644 --- a/java/src/org/openqa/selenium/remote/RemoteExecuteMethod.java +++ b/java/src/org/openqa/selenium/remote/RemoteExecuteMethod.java @@ -18,12 +18,10 @@ package org.openqa.selenium.remote; import java.util.Map; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.openqa.selenium.WrapsDriver; import org.openqa.selenium.internal.Require; -@NullMarked public class RemoteExecuteMethod implements ExecuteMethod, WrapsDriver { private final RemoteWebDriver driver; diff --git a/java/src/org/openqa/selenium/remote/RemoteLogs.java b/java/src/org/openqa/selenium/remote/RemoteLogs.java index 22ff5679eaf55..267558e892e14 100644 --- a/java/src/org/openqa/selenium/remote/RemoteLogs.java +++ b/java/src/org/openqa/selenium/remote/RemoteLogs.java @@ -26,7 +26,6 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.openqa.selenium.Beta; import org.openqa.selenium.UnsupportedCommandException; @@ -68,7 +67,6 @@ public RemoteLogs(ExecuteMethod executeMethod, LocalLogs localLogs) { } @Override - @NullMarked @SuppressWarnings("deprecation") public LogEntries get(String logType) { if (LogType.CLIENT.equals(logType)) { diff --git a/java/src/org/openqa/selenium/remote/RemoteNetwork.java b/java/src/org/openqa/selenium/remote/RemoteNetwork.java index 2848895667f1f..2a5004e4d96c8 100644 --- a/java/src/org/openqa/selenium/remote/RemoteNetwork.java +++ b/java/src/org/openqa/selenium/remote/RemoteNetwork.java @@ -29,8 +29,6 @@ import org.openqa.selenium.Beta; import org.openqa.selenium.UsernameAndPassword; import org.openqa.selenium.WebDriver; -import org.openqa.selenium.bidi.BiDi; -import org.openqa.selenium.bidi.HasBiDi; import org.openqa.selenium.bidi.network.AddInterceptParameters; import org.openqa.selenium.bidi.network.BytesValue; import org.openqa.selenium.bidi.network.ContinueRequestParameters; @@ -44,7 +42,6 @@ @Beta class RemoteNetwork implements Network { - private final BiDi biDi; private final org.openqa.selenium.bidi.module.Network network; private final Map authHandlers = new ConcurrentHashMap<>(); @@ -54,7 +51,6 @@ class RemoteNetwork implements Network { private final AtomicLong callBackId = new AtomicLong(1); public RemoteNetwork(WebDriver driver) { - this.biDi = ((HasBiDi) driver).getBiDi(); this.network = new org.openqa.selenium.bidi.module.Network(driver); interceptAuthTraffic(); @@ -193,7 +189,7 @@ public void clearRequestHandlers() { requestHandlers.clear(); } - private class AuthDetails { + private static class AuthDetails { private final Predicate filter; private final UsernameAndPassword usernameAndPassword; @@ -211,7 +207,7 @@ public UsernameAndPassword getUsernameAndPassword() { } } - private class RequestDetails { + private static class RequestDetails { private final Predicate filter; private final UnaryOperator handler; diff --git a/java/src/org/openqa/selenium/remote/RemoteWebDriver.java b/java/src/org/openqa/selenium/remote/RemoteWebDriver.java index 30d8f000adcee..63d3b6bc6297f 100644 --- a/java/src/org/openqa/selenium/remote/RemoteWebDriver.java +++ b/java/src/org/openqa/selenium/remote/RemoteWebDriver.java @@ -18,6 +18,7 @@ package org.openqa.selenium.remote; import static java.util.Collections.singleton; +import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.logging.Level.SEVERE; import static org.openqa.selenium.remote.CapabilityType.PLATFORM_NAME; @@ -46,8 +47,6 @@ import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.jspecify.annotations.NonNull; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.openqa.selenium.AcceptedW3CCapabilityKeys; import org.openqa.selenium.Alert; @@ -133,25 +132,27 @@ public class RemoteWebDriver private final ClientConfig clientConfig; private CommandExecutor executor; protected Capabilities capabilities; - private SessionId sessionId; + private @Nullable SessionId sessionId; private FileDetector fileDetector = new UselessFileDetector(); private final ExecuteMethod executeMethod = new RemoteExecuteMethod(this); - private JsonToWebElementConverter converter; + private JsonToWebElementConverter converter = new JsonToWebElementConverter(this); - private Logs remoteLogs; + private final Logs remoteLogs = new RemoteLogs(executeMethod); @SuppressWarnings("deprecation") private LocalLogs localLogs; - private Script remoteScript; + @Nullable private Script remoteScript; - private Network remoteNetwork; + @Nullable private Network remoteNetwork; // For cglib + @SuppressWarnings("DataFlowIssue") protected RemoteWebDriver() { this.capabilities = init(new ImmutableCapabilities()); this.clientConfig = ClientConfig.defaultConfig(); + this.executor = null; } public RemoteWebDriver(Capabilities capabilities) { @@ -253,12 +254,7 @@ public static RemoteWebDriverBuilder builder() { private Capabilities init(Capabilities capabilities) { capabilities = capabilities == null ? new ImmutableCapabilities() : capabilities; - - converter = new JsonToWebElementConverter(this); - initLocalLogs(); - remoteLogs = new RemoteLogs(executeMethod); - return capabilities; } @@ -274,6 +270,7 @@ private void initLocalLogs() { localLogs = LocalLogs.getCombinedLogsHolder(clientLogs, performanceLogger); } + @Nullable public SessionId getSessionId() { return sessionId; } @@ -360,7 +357,6 @@ protected void setCommandExecutor(CommandExecutor executor) { this.executor = executor; } - @NonNull @Override public Capabilities getCapabilities() { if (capabilities == null) { @@ -413,7 +409,7 @@ public Pdf print(PrintOptions printOptions) throws WebDriverException { Response response = execute(DriverCommand.PRINT_PAGE(printOptions)); Object result = response.getValue(); - return new Pdf((String) result); + return new Pdf((String) requireNonNull(result)); } @Override @@ -530,15 +526,16 @@ public String getWindowHandle() { } @Override - public @Nullable Object executeScript(@NonNull String script, @Nullable Object... args) { + public @Nullable Object executeScript(String script, @Nullable Object... args) { List convertedArgs = Stream.of(args).map(new WebElementToJsonConverter()).collect(Collectors.toList()); return execute(DriverCommand.EXECUTE_SCRIPT(script, convertedArgs)).getValue(); } + @Nullable @Override - public Object executeAsyncScript(String script, Object... args) { + public Object executeAsyncScript(String script, @Nullable Object... args) { List convertedArgs = Stream.of(args).map(new WebElementToJsonConverter()).collect(Collectors.toList()); @@ -592,6 +589,7 @@ public void setLogLevel(Level level) { LOG.setLevel(level); } + @Nullable protected Response execute(CommandPayload payload) { Command command = new Command(sessionId, payload); Response response; @@ -697,7 +695,6 @@ protected ExecuteMethod getExecuteMethod() { } @Override - @NullMarked public void perform(Collection actions) { execute(DriverCommand.ACTIONS(actions)); } @@ -707,7 +704,6 @@ public void resetInputState() { execute(DriverCommand.CLEAR_ACTIONS_STATE); } - @NullMarked @Override public VirtualAuthenticator addVirtualAuthenticator(VirtualAuthenticatorOptions options) { String authenticatorId = @@ -715,7 +711,6 @@ public VirtualAuthenticator addVirtualAuthenticator(VirtualAuthenticatorOptions return new RemoteVirtualAuthenticator(authenticatorId); } - @NullMarked @Override public void removeVirtualAuthenticator(VirtualAuthenticator authenticator) { execute( @@ -837,7 +832,8 @@ public void deleteDownloadableFiles() { * @return the response data from the server * @throws WebDriverException if the event cannot be fired */ - public Map fireSessionEvent(String eventType, Map payload) { + public Map fireSessionEvent( + String eventType, @Nullable Map payload) { Response response = execute(DriverCommand.FIRE_SESSION_EVENT(eventType, payload)); return (Map) response.getValue(); } @@ -864,6 +860,7 @@ public void resetCooldown() { execute(DriverCommand.RESET_COOLDOWN); } + @Nullable @Override public FederatedCredentialManagementDialog getFederatedCredentialManagementDialog() { FederatedCredentialManagementDialog dialog = new FedCmDialogImpl(executeMethod); @@ -884,7 +881,7 @@ public FederatedCredentialManagementDialog getFederatedCredentialManagementDialo * @param toLog any data that might be interesting. * @param when verb tense of "Execute" to prefix message */ - protected void log(SessionId sessionId, String commandName, Object toLog, When when) { + protected void log(@Nullable SessionId sessionId, String commandName, Object toLog, When when) { if (!LOG.isLoggable(level)) { return; } @@ -983,9 +980,7 @@ public FileDetector getFileDetector() { * @see UselessFileDetector */ public void setFileDetector(FileDetector detector) { - if (detector == null) { - throw new WebDriverException("You may not set a file detector that is null"); - } + Require.nonNull("File detector", detector); fileDetector = detector; } @@ -1028,9 +1023,7 @@ public void addCookie(Cookie cookie) { @Override public void deleteCookieNamed(String name) { - if (name == null || name.isBlank()) { - throw new IllegalArgumentException("Cookie name cannot be empty"); - } + Require.nonBlank("Cookie name", name); execute(DriverCommand.DELETE_COOKIE(name)); } @@ -1087,11 +1080,10 @@ public Set getCookies() { return toReturn; } + @Nullable @Override public Cookie getCookieNamed(String name) { - if (name == null || name.isBlank()) { - throw new IllegalArgumentException("Cookie name cannot be empty"); - } + Require.nonBlank("Cookie name", name); Set allCookies = getCookies(); for (Cookie cookie : allCookies) { if (cookie.getName().equals(name)) { @@ -1159,9 +1151,6 @@ public Duration getPageLoadTimeout() { @Beta protected class RemoteWindow implements Window { - - Map rawPoint; - @Override @SuppressWarnings({"unchecked"}) public Dimension getSize() { @@ -1184,7 +1173,7 @@ public void setSize(Dimension targetSize) { @SuppressWarnings("unchecked") public Point getPosition() { Response response = execute(DriverCommand.GET_CURRENT_WINDOW_POSITION()); - rawPoint = (Map) response.getValue(); + Map rawPoint = (Map) response.getValue(); int x = ((Number) rawPoint.get("x")).intValue(); int y = ((Number) rawPoint.get("y")).intValue(); @@ -1362,9 +1351,7 @@ public String getText() { */ @Override public void sendKeys(String keysToSend) { - if (keysToSend == null) { - throw new IllegalArgumentException("Keys to send should be a not null CharSequence"); - } + Require.nonNull("Keys to send", keysToSend, "should be a not null CharSequence"); execute(DriverCommand.SET_ALERT_VALUE(keysToSend)); } } @@ -1381,7 +1368,6 @@ public String getId() { return id; } - @NullMarked @Override public void addCredential(Credential credential) { execute( @@ -1400,13 +1386,11 @@ public List getCredentials() { return response.stream().map(Credential::fromMap).collect(Collectors.toList()); } - @NullMarked @Override public void removeCredential(byte[] credentialId) { removeCredential(Base64.getUrlEncoder().encodeToString(credentialId)); } - @NullMarked @Override public void removeCredential(String credentialId) { execute( diff --git a/java/src/org/openqa/selenium/remote/RemoteWebDriverBuilder.java b/java/src/org/openqa/selenium/remote/RemoteWebDriverBuilder.java index 925466b3da536..036d441f1d733 100644 --- a/java/src/org/openqa/selenium/remote/RemoteWebDriverBuilder.java +++ b/java/src/org/openqa/selenium/remote/RemoteWebDriverBuilder.java @@ -97,9 +97,9 @@ public class RemoteWebDriverBuilder { private final Map metadata = new TreeMap<>(); private HttpClient.Factory clientFactory = HttpClient.Factory.createDefault(); private ClientConfig clientConfig = ClientConfig.defaultConfig(); - private URI remoteHost = null; - private DriverService driverService; - private Credentials credentials = null; + private @Nullable URI remoteHost = null; + private @Nullable DriverService driverService; + private @Nullable Credentials credentials = null; private Augmenter augmenter = new Augmenter(); RemoteWebDriverBuilder() { @@ -308,8 +308,7 @@ public HttpResponse execute(HttpRequest req) throws UncheckedIOException { @Override public java.net.http.HttpResponse sendNative( java.net.http.HttpRequest request, - java.net.http.HttpResponse.BodyHandler handler) - throws java.io.IOException, InterruptedException { + java.net.http.HttpResponse.BodyHandler handler) { throw new UnsupportedOperationException("sendNative is not supported"); } }; @@ -457,6 +456,7 @@ private URI getBaseUri() { return clientConfig.baseUri(); } + @Nullable private DriverService startDriverServiceIfNecessary() { if (driverService == null) { return null; diff --git a/java/src/org/openqa/selenium/remote/RemoteWebElement.java b/java/src/org/openqa/selenium/remote/RemoteWebElement.java index 599f895fd42a0..397dc70f1ac5a 100644 --- a/java/src/org/openqa/selenium/remote/RemoteWebElement.java +++ b/java/src/org/openqa/selenium/remote/RemoteWebElement.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.Beta; import org.openqa.selenium.By; import org.openqa.selenium.Dimension; @@ -42,11 +43,12 @@ import org.openqa.selenium.WrapsElement; import org.openqa.selenium.interactions.Coordinates; import org.openqa.selenium.interactions.Locatable; +import org.openqa.selenium.internal.Require; import org.openqa.selenium.io.Zip; public class RemoteWebElement implements WebElement, Locatable, TakesScreenshot, WrapsDriver { - private String foundBy; + private @Nullable String foundBy; protected String id; protected RemoteWebDriver parent; protected FileDetector fileDetector; @@ -59,6 +61,7 @@ public void setParent(RemoteWebDriver parent) { this.parent = parent; } + @Nullable public String getId() { return id; } @@ -88,18 +91,17 @@ public void submit() { @Override public void sendKeys(CharSequence... keysToSend) { - if (keysToSend == null || keysToSend.length == 0) { + Require.nonNull("Keys to send", keysToSend); + if (keysToSend.length == 0) { throw new IllegalArgumentException("Keys to send should be a not null CharSequence"); } for (CharSequence cs : keysToSend) { - if (cs == null) { - throw new IllegalArgumentException("Keys to send should be a not null CharSequence"); - } + Require.nonNull("Keys to send", cs); } String allKeysToSend = String.join("", keysToSend); - List files = + List<@Nullable File> files = Arrays.stream(allKeysToSend.split("\n")) .map(fileDetector::getLocalFile) .collect(Collectors.toList()); diff --git a/java/src/org/openqa/selenium/remote/Response.java b/java/src/org/openqa/selenium/remote/Response.java index ba54bb90fbc1c..397098110577a 100644 --- a/java/src/org/openqa/selenium/remote/Response.java +++ b/java/src/org/openqa/selenium/remote/Response.java @@ -20,10 +20,8 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -@NullMarked public class Response { private volatile @Nullable Object value; diff --git a/java/src/org/openqa/selenium/remote/ScreenshotException.java b/java/src/org/openqa/selenium/remote/ScreenshotException.java index 936a28d8cc0b6..392074753f908 100644 --- a/java/src/org/openqa/selenium/remote/ScreenshotException.java +++ b/java/src/org/openqa/selenium/remote/ScreenshotException.java @@ -17,22 +17,20 @@ package org.openqa.selenium.remote; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebDriverException; -@NullMarked public class ScreenshotException extends WebDriverException { - public ScreenshotException(@Nullable String message) { + public ScreenshotException(String message) { super(message); } - public ScreenshotException(@Nullable Throwable cause) { + public ScreenshotException(Throwable cause) { super(cause); } - public ScreenshotException(@Nullable String message, @Nullable Throwable cause) { + public ScreenshotException(String message, @Nullable Throwable cause) { super(message, cause); } } diff --git a/java/src/org/openqa/selenium/remote/SessionId.java b/java/src/org/openqa/selenium/remote/SessionId.java index 707b69bc0876b..f4280f7d57358 100644 --- a/java/src/org/openqa/selenium/remote/SessionId.java +++ b/java/src/org/openqa/selenium/remote/SessionId.java @@ -20,12 +20,10 @@ import java.io.Serializable; import java.util.Map; import java.util.UUID; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.openqa.selenium.internal.Require; import org.openqa.selenium.json.JsonException; -@NullMarked public class SessionId implements Serializable { private final String opaqueKey; diff --git a/java/src/org/openqa/selenium/remote/UnreachableBrowserException.java b/java/src/org/openqa/selenium/remote/UnreachableBrowserException.java index a86a83c0711ff..005edb47486a1 100644 --- a/java/src/org/openqa/selenium/remote/UnreachableBrowserException.java +++ b/java/src/org/openqa/selenium/remote/UnreachableBrowserException.java @@ -17,8 +17,6 @@ package org.openqa.selenium.remote; -import org.jspecify.annotations.NullMarked; -import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebDriverException; /** @@ -35,13 +33,12 @@ * * 1) */ -@NullMarked public class UnreachableBrowserException extends WebDriverException { - public UnreachableBrowserException(@Nullable String message) { + public UnreachableBrowserException(String message) { super(message); } - public UnreachableBrowserException(@Nullable String message, @Nullable Throwable cause) { + public UnreachableBrowserException(String message, Throwable cause) { super(message, cause); } } diff --git a/java/src/org/openqa/selenium/remote/UselessFileDetector.java b/java/src/org/openqa/selenium/remote/UselessFileDetector.java index 6a73bb6e8e9ba..96929bfd74ad1 100644 --- a/java/src/org/openqa/selenium/remote/UselessFileDetector.java +++ b/java/src/org/openqa/selenium/remote/UselessFileDetector.java @@ -18,11 +18,9 @@ package org.openqa.selenium.remote; import java.io.File; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; /** A file detector that never finds anything. */ -@NullMarked public class UselessFileDetector implements FileDetector { @Override public @Nullable File getLocalFile(CharSequence... keys) { diff --git a/java/src/org/openqa/selenium/remote/W3CHandshakeResponse.java b/java/src/org/openqa/selenium/remote/W3CHandshakeResponse.java index 7a07b66dab81d..36abf122554f9 100644 --- a/java/src/org/openqa/selenium/remote/W3CHandshakeResponse.java +++ b/java/src/org/openqa/selenium/remote/W3CHandshakeResponse.java @@ -20,12 +20,13 @@ import java.util.Map; import java.util.Optional; import java.util.function.Function; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebDriverException; class W3CHandshakeResponse implements HandshakeResponse { @Override - public Function errorHandler() { + public Function errorHandler() { return tuple -> { Object rawValue = tuple.getData().get("value"); if (!(rawValue instanceof Map)) { @@ -70,7 +71,7 @@ public Function errorHandler } @Override - public Function successHandler() { + public Function successHandler() { return tuple -> { Object rawValue = tuple.getData().get("value"); if (!(rawValue instanceof Map)) { diff --git a/java/src/org/openqa/selenium/remote/WebElementToJsonConverter.java b/java/src/org/openqa/selenium/remote/WebElementToJsonConverter.java index 1030f9612531d..90251c7bc2b7e 100644 --- a/java/src/org/openqa/selenium/remote/WebElementToJsonConverter.java +++ b/java/src/org/openqa/selenium/remote/WebElementToJsonConverter.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.function.Function; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.WrapsElement; /** @@ -37,9 +38,10 @@ * href="https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#webelement-json-object"> * WebDriver JSON Wire Protocol */ -public class WebElementToJsonConverter implements Function { +public class WebElementToJsonConverter implements Function<@Nullable Object, @Nullable Object> { + @Nullable @Override - public Object apply(Object arg) { + public Object apply(@Nullable Object arg) { if (arg == null || arg instanceof String || arg instanceof Boolean || arg instanceof Number) { return arg; } @@ -67,7 +69,7 @@ public Object apply(Object arg) { if (arg instanceof Map) { Map args = (Map) arg; - Map converted = new HashMap<>(args.size()); + Map converted = new HashMap<>(args.size()); for (Map.Entry entry : args.entrySet()) { Object key = entry.getKey(); if (!(key instanceof String)) { diff --git a/java/src/org/openqa/selenium/remote/codec/AbstractHttpCommandCodec.java b/java/src/org/openqa/selenium/remote/codec/AbstractHttpCommandCodec.java index 81d1abac2c401..78f2ea9b7f304 100644 --- a/java/src/org/openqa/selenium/remote/codec/AbstractHttpCommandCodec.java +++ b/java/src/org/openqa/selenium/remote/codec/AbstractHttpCommandCodec.java @@ -92,7 +92,6 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.openqa.selenium.UnsupportedCommandException; import org.openqa.selenium.internal.Require; @@ -110,7 +109,6 @@ * * @see W3C WebDriver spec */ -@NullMarked public abstract class AbstractHttpCommandCodec implements CommandCodec { private static final String SESSION_ID_PARAM = "sessionId"; diff --git a/java/src/org/openqa/selenium/remote/codec/AbstractHttpResponseCodec.java b/java/src/org/openqa/selenium/remote/codec/AbstractHttpResponseCodec.java index 78b18f76e09d3..fd24b55e1a0d2 100644 --- a/java/src/org/openqa/selenium/remote/codec/AbstractHttpResponseCodec.java +++ b/java/src/org/openqa/selenium/remote/codec/AbstractHttpResponseCodec.java @@ -20,11 +20,10 @@ import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR; import static java.net.HttpURLConnection.HTTP_OK; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNullElse; import static org.openqa.selenium.json.Json.JSON_UTF_8; import static org.openqa.selenium.remote.http.Contents.bytes; -import static org.openqa.selenium.remote.http.Contents.string; -import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; import org.openqa.selenium.json.Json; @@ -52,7 +51,8 @@ public abstract class AbstractHttpResponseCodec implements ResponseCodec factory, Response response) { - int status = response.getStatus() == ErrorCodes.SUCCESS ? HTTP_OK : HTTP_INTERNAL_ERROR; + int responseStatus = requireNonNullElse(response.getStatus(), 0); + int status = responseStatus == ErrorCodes.SUCCESS ? HTTP_OK : HTTP_INTERNAL_ERROR; byte[] data = json.toJson(getValueToEncode(response)).getBytes(UTF_8); @@ -71,9 +71,8 @@ public HttpResponse encode(Supplier factory, Response response) { @Override public Response decode(HttpResponse encodedResponse) { - String contentType = - Objects.requireNonNullElse(encodedResponse.getHeader(HttpHeader.ContentType.getName()), ""); - String content = string(encodedResponse).trim(); + String contentType = encodedResponse.getHeader(HttpHeader.ContentType, ""); + String content = encodedResponse.contentAsString().trim(); try { return reconstructValue(json.toType(content, Response.class)); } catch (JsonException e) { @@ -108,18 +107,20 @@ public Response decode(HttpResponse encodedResponse) { response.setValue(content); } - if (response.getStatus() != null && response.getState() == null) { - response.setState(errorCodes.toState(response.getStatus())); - } else if (response.getStatus() == null && response.getState() != null) { - response.setStatus( - errorCodes.toStatus(response.getState(), Optional.of(encodedResponse.getStatus()))); + Integer status = response.getStatus(); + String state = response.getState(); + + if (status != null && state == null) { + response.setState(errorCodes.toState(status)); + } else if (status == null && state != null) { + response.setStatus(errorCodes.toStatus(state, Optional.of(encodedResponse.getStatus()))); } else if (statusCode == 200) { response.setStatus(ErrorCodes.SUCCESS); response.setState(errorCodes.toState(ErrorCodes.SUCCESS)); } - if (response.getStatus() != null) { - response.setState(errorCodes.toState(response.getStatus())); + if (status != null) { + response.setState(errorCodes.toState(status)); } else if (statusCode == 200) { response.setState(errorCodes.toState(ErrorCodes.SUCCESS)); } diff --git a/java/src/org/openqa/selenium/remote/codec/package-info.java b/java/src/org/openqa/selenium/remote/codec/package-info.java new file mode 100644 index 0000000000000..5b4aa35f6c731 --- /dev/null +++ b/java/src/org/openqa/selenium/remote/codec/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.remote.codec; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpCommandCodec.java b/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpCommandCodec.java index 0a02eb79e6878..b7ad8623db030 100644 --- a/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpCommandCodec.java +++ b/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpCommandCodec.java @@ -84,7 +84,6 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.jspecify.annotations.NullMarked; import org.openqa.selenium.InvalidSelectorException; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.remote.WebElementToJsonConverter; @@ -95,7 +94,6 @@ * * @see W3C WebDriver spec */ -@NullMarked public class W3CHttpCommandCodec extends AbstractHttpCommandCodec { private static final ConcurrentHashMap ATOM_SCRIPTS = new ConcurrentHashMap<>(); diff --git a/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpResponseCodec.java b/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpResponseCodec.java index 6d3d58293e435..c8050cfcc6bd4 100644 --- a/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpResponseCodec.java +++ b/java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpResponseCodec.java @@ -31,6 +31,7 @@ import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.UnhandledAlertException; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.json.Json; @@ -76,7 +77,7 @@ public Response decode(HttpResponse encodedResponse) { LOG.log( Level.FINER, "Decoding response (status was: {0}, content type: {1}, content length: {2})", - new Object[] { + new @Nullable Object[] { encodedResponse.getStatus(), encodedResponse.getContentType(), encodedResponse.getContentLength() @@ -167,11 +168,12 @@ public Response decode(HttpResponse encodedResponse) { } } - if (response.getValue() instanceof String) { + Object value = response.getValue(); + if (value instanceof String) { // We normalise to \n because Java will translate this to \r\n // if this is suitable on our platform, and if we have \r\n, java will // turn this into \r\r\n, which would be Bad! - response.setValue(((String) response.getValue()).replace("\r\n", "\n")); + response.setValue(((String) value).replace("\r\n", "\n")); } return response; diff --git a/java/src/org/openqa/selenium/remote/codec/w3c/package-info.java b/java/src/org/openqa/selenium/remote/codec/w3c/package-info.java new file mode 100644 index 0000000000000..b7c3213cdcbe0 --- /dev/null +++ b/java/src/org/openqa/selenium/remote/codec/w3c/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.remote.codec.w3c; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/remote/http/AddSeleniumUserAgent.java b/java/src/org/openqa/selenium/remote/http/AddSeleniumUserAgent.java index 501bc15423858..20b98f08e1c63 100644 --- a/java/src/org/openqa/selenium/remote/http/AddSeleniumUserAgent.java +++ b/java/src/org/openqa/selenium/remote/http/AddSeleniumUserAgent.java @@ -17,21 +17,23 @@ package org.openqa.selenium.remote.http; +import static java.util.Locale.ROOT; +import static java.util.Objects.requireNonNullElse; import static org.openqa.selenium.remote.http.HttpHeader.UserAgent; -import java.util.Locale; import org.openqa.selenium.BuildInfo; import org.openqa.selenium.Platform; public class AddSeleniumUserAgent implements Filter { public static final String USER_AGENT = - String.format( - "selenium/%s (java %s)", - new BuildInfo().getReleaseLabel(), - (Platform.getCurrent().family() == null - ? Platform.getCurrent().toString().toLowerCase(Locale.US) - : Platform.getCurrent().family().toString().toLowerCase(Locale.US))); + String.format("selenium/%s (java %s)", new BuildInfo().getReleaseLabel(), platformLabel()); + + private static String platformLabel() { + Platform platform = Platform.getCurrent(); + Platform family = requireNonNullElse(platform.family(), platform); + return family.toString().toLowerCase(ROOT); + } @Override public HttpHandler apply(HttpHandler next) { diff --git a/java/src/org/openqa/selenium/remote/http/ClientConfig.java b/java/src/org/openqa/selenium/remote/http/ClientConfig.java index 99154b2648d45..69dd88c72b39d 100644 --- a/java/src/org/openqa/selenium/remote/http/ClientConfig.java +++ b/java/src/org/openqa/selenium/remote/http/ClientConfig.java @@ -24,7 +24,9 @@ import java.net.URISyntaxException; import java.net.URL; import java.time.Duration; +import java.util.Optional; import javax.net.ssl.SSLContext; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.Credentials; import org.openqa.selenium.internal.Require; @@ -32,14 +34,14 @@ public class ClientConfig { private static final Filter RETRY_FILTER = new RetryRequest(); private static final Filter DEFAULT_FILTER = new AddSeleniumUserAgent(); - private final URI baseUri; + private final @Nullable URI baseUri; private final Duration connectionTimeout; private final Duration readTimeout; private final Duration wsTimeout; private final Filter filters; - private final Proxy proxy; - private final Credentials credentials; - private final SSLContext sslContext; + private final @Nullable Proxy proxy; + private final @Nullable Credentials credentials; + private final @Nullable SSLContext sslContext; private final String version; protected ClientConfig( @@ -64,14 +66,14 @@ protected ClientConfig( } protected ClientConfig( - URI baseUri, + @Nullable URI baseUri, Duration connectionTimeout, Duration readTimeout, Duration wsTimeout, Filter filters, - Proxy proxy, - Credentials credentials, - SSLContext sslContext, + @Nullable Proxy proxy, + @Nullable Credentials credentials, + @Nullable SSLContext sslContext, String version) { this.baseUri = baseUri; this.connectionTimeout = Require.nonNegative("Connection timeout", connectionTimeout); @@ -133,13 +135,19 @@ public ClientConfig baseUrl(URL baseUrl) { } } + @Nullable public URI baseUri() { return baseUri; } + @Nullable public URL baseUrl() { + return Optional.ofNullable(baseUri()).map(this::toURL).orElse(null); + } + + private URL toURL(URI uri) { try { - return baseUri().toURL(); + return uri.toURL(); } catch (MalformedURLException e) { throw new UncheckedIOException(e); } @@ -240,6 +248,7 @@ public ClientConfig proxy(Proxy proxy) { version); } + @Nullable public Proxy proxy() { return proxy; } @@ -257,6 +266,7 @@ public ClientConfig authenticateAs(Credentials credentials) { version); } + @Nullable public Credentials credentials() { return credentials; } @@ -274,6 +284,7 @@ public ClientConfig sslContext(SSLContext sslContext) { version); } + @Nullable public SSLContext sslContext() { return sslContext; } diff --git a/java/src/org/openqa/selenium/remote/http/CloseMessage.java b/java/src/org/openqa/selenium/remote/http/CloseMessage.java index 01c86c4719f64..3c7e1ac0c19e4 100644 --- a/java/src/org/openqa/selenium/remote/http/CloseMessage.java +++ b/java/src/org/openqa/selenium/remote/http/CloseMessage.java @@ -17,6 +17,8 @@ package org.openqa.selenium.remote.http; +import static java.util.Objects.requireNonNullElse; + public class CloseMessage implements Message { private final int code; @@ -28,7 +30,7 @@ public CloseMessage(int code) { public CloseMessage(int code, String reason) { this.code = code; - this.reason = reason == null ? "" : reason; + this.reason = requireNonNullElse(reason, ""); } public int code() { diff --git a/java/src/org/openqa/selenium/remote/http/ConnectionFailedException.java b/java/src/org/openqa/selenium/remote/http/ConnectionFailedException.java index 18730585e0c39..696bc6705d42e 100644 --- a/java/src/org/openqa/selenium/remote/http/ConnectionFailedException.java +++ b/java/src/org/openqa/selenium/remote/http/ConnectionFailedException.java @@ -17,18 +17,15 @@ package org.openqa.selenium.remote.http; -import org.jspecify.annotations.NullMarked; -import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebDriverException; -@NullMarked public class ConnectionFailedException extends WebDriverException { - public ConnectionFailedException(@Nullable String message) { + public ConnectionFailedException(String message) { super(message); } - public ConnectionFailedException(@Nullable String message, @Nullable Throwable cause) { + public ConnectionFailedException(String message, Throwable cause) { super(message, cause); } } diff --git a/java/src/org/openqa/selenium/remote/http/Contents.java b/java/src/org/openqa/selenium/remote/http/Contents.java index 587867248d881..e21e93ae94e31 100644 --- a/java/src/org/openqa/selenium/remote/http/Contents.java +++ b/java/src/org/openqa/selenium/remote/http/Contents.java @@ -18,6 +18,7 @@ package org.openqa.selenium.remote.http; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNull; import java.io.ByteArrayOutputStream; import java.io.File; @@ -168,7 +169,7 @@ public static Supplier asJson(Object obj) { public static T fromJson(HttpMessage message, Type typeOfT) { try (Reader reader = reader(message); JsonInput input = JSON.newInput(reader)) { - return input.read(typeOfT); + return requireNonNull(input.read(typeOfT)); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/java/src/org/openqa/selenium/remote/http/FileContentSupplier.java b/java/src/org/openqa/selenium/remote/http/FileContentSupplier.java index ae6461333b01d..e2de6122f268e 100644 --- a/java/src/org/openqa/selenium/remote/http/FileContentSupplier.java +++ b/java/src/org/openqa/selenium/remote/http/FileContentSupplier.java @@ -25,11 +25,12 @@ import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.attribute.BasicFileAttributes; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.internal.Require; class FileContentSupplier implements Contents.Supplier { private final File file; - private volatile InputStream inputStream; + private @Nullable InputStream inputStream; FileContentSupplier(File file) { this.file = Require.nonNull("File", file); diff --git a/java/src/org/openqa/selenium/remote/http/HttpMessage.java b/java/src/org/openqa/selenium/remote/http/HttpMessage.java index 0ee7cf9d2dd69..339072258ea6a 100644 --- a/java/src/org/openqa/selenium/remote/http/HttpMessage.java +++ b/java/src/org/openqa/selenium/remote/http/HttpMessage.java @@ -19,6 +19,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.emptyList; +import static java.util.Objects.requireNonNullElse; import java.io.IOException; import java.io.InputStream; @@ -52,6 +53,7 @@ abstract class HttpMessage> { * @param key attribute name * @return attribute object */ + @Nullable public Object getAttribute(String key) { return attributes.get(key); } @@ -118,6 +120,10 @@ public String getHeader(String name) { return !values.isEmpty() ? values.get(0) : null; } + public String getHeader(HttpHeader header, String defaultValue) { + return requireNonNullElse(getHeader(header.getName()), defaultValue); + } + /** * Removes all headers with the {@code name} (case-insensitive) and adds a header with the {@code * value}. diff --git a/java/src/org/openqa/selenium/remote/http/HttpMethod.java b/java/src/org/openqa/selenium/remote/http/HttpMethod.java index fc6622ea40d7f..c6c30e848d90a 100644 --- a/java/src/org/openqa/selenium/remote/http/HttpMethod.java +++ b/java/src/org/openqa/selenium/remote/http/HttpMethod.java @@ -18,6 +18,7 @@ package org.openqa.selenium.remote.http; import java.util.Locale; +import org.openqa.selenium.internal.Require; public enum HttpMethod { DELETE, @@ -31,9 +32,7 @@ public enum HttpMethod { TRACE; public static HttpMethod getHttpMethod(String method) { - if (method == null) { - throw new IllegalArgumentException("Method cannot be null"); - } + Require.nonNull("Method", method); try { return HttpMethod.valueOf(method.toUpperCase(Locale.ENGLISH)); diff --git a/java/src/org/openqa/selenium/remote/http/HttpRequest.java b/java/src/org/openqa/selenium/remote/http/HttpRequest.java index f7ba690c20c11..6ca1f65650584 100644 --- a/java/src/org/openqa/selenium/remote/http/HttpRequest.java +++ b/java/src/org/openqa/selenium/remote/http/HttpRequest.java @@ -17,12 +17,19 @@ package org.openqa.selenium.remote.http; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.stream.Collectors.joining; + import java.net.URI; +import java.net.URLEncoder; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.BiConsumer; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.internal.Require; public class HttpRequest extends HttpMessage { @@ -51,6 +58,7 @@ public HttpMethod getMethod() { /** * Get a query parameter. The implementation will take care of decoding from the percent encoding. */ + @Nullable public String getQueryParameter(String name) { Iterable allParams = getQueryParameters(name); if (allParams == null) { @@ -71,10 +79,37 @@ public HttpRequest addQueryParameter(String name, String value) { return this; } + public void forEachQueryParameter(BiConsumer action) { + for (Map.Entry> parameter : queryParameters.entrySet()) { + for (String value : parameter.getValue()) { + action.accept(parameter.getKey(), value); + } + } + } + + public String getQueryString() { + return queryParameters.entrySet().stream() + .map(param -> toQueryString(param.getKey(), param.getValue())) + .collect(joining("&")); + } + + private String toQueryString(String name, List values) { + return values.stream().map(value -> toQueryString(name, value)).collect(joining("&")); + } + + private String toQueryString(String name, String value) { + return String.format("%s=%s", URLEncoder.encode(name, UTF_8), URLEncoder.encode(value, UTF_8)); + } + + public Map> getQueryParameters() { + return Collections.unmodifiableMap(queryParameters); + } + public Iterable getQueryParameterNames() { return queryParameters.keySet(); } + @Nullable public Iterable getQueryParameters(String name) { return queryParameters.get(name); } diff --git a/java/src/org/openqa/selenium/remote/http/HttpResponse.java b/java/src/org/openqa/selenium/remote/http/HttpResponse.java index 81166829db73e..a7eafb8868d22 100644 --- a/java/src/org/openqa/selenium/remote/http/HttpResponse.java +++ b/java/src/org/openqa/selenium/remote/http/HttpResponse.java @@ -19,6 +19,8 @@ import static java.net.HttpURLConnection.HTTP_OK; +import org.jspecify.annotations.Nullable; + public class HttpResponse extends HttpMessage { public static final String HTTP_TARGET_HOST = "http.target.host"; @@ -53,6 +55,7 @@ public HttpResponse setTargetHost(String host) { * * @return originating host */ + @Nullable public String getTargetHost() { return (String) getAttribute(HTTP_TARGET_HOST); } diff --git a/java/src/org/openqa/selenium/remote/http/Route.java b/java/src/org/openqa/selenium/remote/http/Route.java index d65f175ef8027..f6a8c2f1cd62e 100644 --- a/java/src/org/openqa/selenium/remote/http/Route.java +++ b/java/src/org/openqa/selenium/remote/http/Route.java @@ -137,7 +137,7 @@ public static Route combine(Iterable routes) { return new CombinedRoute(StreamSupport.stream(routes.spliterator(), false)); } - public static class TemplatizedRouteConfig { + public static final class TemplatizedRouteConfig { private final Predicate predicate; private final UrlTemplate template; @@ -158,7 +158,7 @@ public Route to(Function, HttpHandler> handlerFunc) { } } - private static class TemplatizedRoute extends Route { + private static final class TemplatizedRoute extends Route { private final UrlTemplate template; private final Predicate predicate; @@ -301,13 +301,10 @@ private HttpRequest transform(HttpRequest request) { .collect(Collectors.toUnmodifiableList()); toForward.setAttribute(ROUTE_PREFIX_KEY, prefixes); - request - .getQueryParameterNames() - .forEach( - name -> - request - .getQueryParameters(name) - .forEach(value -> toForward.addQueryParameter(name, value))); + request.forEachQueryParameter( + (name, value) -> { + toForward.addQueryParameter(name, value); + }); toForward.setContent(request.getContent()); @@ -315,7 +312,7 @@ private HttpRequest transform(HttpRequest request) { } } - private static class CombinedRoute extends Route { + private static final class CombinedRoute extends Route { private final List allRoutes; @@ -362,7 +359,7 @@ protected HttpResponse handle(HttpRequest req) { } } - public static class PredicatedConfig { + public static final class PredicatedConfig { private final Predicate predicate; private PredicatedConfig(Predicate predicate) { @@ -374,7 +371,7 @@ public Route to(Supplier handler) { } } - private static class PredicatedRoute extends Route { + private static final class PredicatedRoute extends Route { private final Predicate predicate; private final Supplier supplier; @@ -391,10 +388,7 @@ public boolean matches(HttpRequest httpRequest) { @Override protected HttpResponse handle(HttpRequest req) { - HttpHandler handler = supplier.get(); - if (handler == null) { - throw new IllegalStateException("No handler available."); - } + HttpHandler handler = Require.nonNull("Handler", supplier.get()); return handler.execute(req); } } diff --git a/java/src/org/openqa/selenium/remote/http/UrlTemplate.java b/java/src/org/openqa/selenium/remote/http/UrlTemplate.java index 427204c0a9498..81f9b45efdb42 100644 --- a/java/src/org/openqa/selenium/remote/http/UrlTemplate.java +++ b/java/src/org/openqa/selenium/remote/http/UrlTemplate.java @@ -25,17 +25,17 @@ import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jspecify.annotations.Nullable; +import org.openqa.selenium.internal.Require; /** A bad implementation of URL Templates, but enough for our needs. */ public class UrlTemplate { - private static final Pattern GROUP_NAME = Pattern.compile("(\\{\\p{Alnum}+\\})"); - private final Function compiled; + private static final Pattern GROUP_NAME = Pattern.compile("(\\{\\p{Alnum}+})"); + private final Function compiled; public UrlTemplate(String template) { - if (template == null || template.isEmpty()) { - throw new IllegalArgumentException("Template must not be 0 length"); - } + Require.nonEmpty("Template", template); // ^ start of string StringBuilder regex = new StringBuilder("^"); @@ -132,7 +132,7 @@ public UrlTemplate(String template) { /** * @return A {@link Match} with all parameters filled if successful, null otherwise. */ - public UrlTemplate.Match match(String matchAgainst) { + public UrlTemplate.@Nullable Match match(@Nullable String matchAgainst) { if (matchAgainst == null) { return null; } @@ -144,7 +144,7 @@ public UrlTemplate.Match match(String matchAgainst) { * @return A {@link Match} with all parameters filled if successful, null otherwise. Remove * subPath from matchAgainst before matching. */ - public UrlTemplate.Match match(String matchAgainst, String prefix) { + public UrlTemplate.@Nullable Match match(@Nullable String matchAgainst, @Nullable String prefix) { if (matchAgainst == null || prefix == null) { return null; } @@ -155,7 +155,7 @@ public UrlTemplate.Match match(String matchAgainst, String prefix) { } @SuppressWarnings("InnerClassMayBeStatic") - public class Match { + public static final class Match { private final String url; private final Map parameters; diff --git a/java/src/org/openqa/selenium/remote/http/jdk/JdkHttpClient.java b/java/src/org/openqa/selenium/remote/http/jdk/JdkHttpClient.java index e02a9613d8a1c..24f09edd3fc4e 100644 --- a/java/src/org/openqa/selenium/remote/http/jdk/JdkHttpClient.java +++ b/java/src/org/openqa/selenium/remote/http/jdk/JdkHttpClient.java @@ -54,6 +54,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import javax.net.ssl.SSLContext; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.Credentials; import org.openqa.selenium.TimeoutException; import org.openqa.selenium.UsernameAndPassword; @@ -109,20 +110,10 @@ public class JdkHttpClient implements HttpClient { .executor(executorService); Credentials credentials = config.credentials(); - String info = config.baseUri().getUserInfo(); + URI baseUri = config.baseUri(); + String info = baseUri != null ? baseUri.getUserInfo() : null; if (info != null && !info.trim().isEmpty()) { - String[] parts = info.split(":", 2); - String username = parts[0]; - String password = parts.length > 1 ? parts[1] : null; - - Authenticator authenticator = - new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password.toCharArray()); - } - }; - builder = builder.authenticator(authenticator); + builder = builder.authenticator(new PasswordAuthenticator(info)); } else if (credentials != null) { if (!(credentials instanceof UsernameAndPassword)) { throw new IllegalArgumentException( @@ -187,6 +178,7 @@ public WebSocket openSocket(HttpRequest request, WebSocket.Listener listener) { final StringBuilder builder = new StringBuilder(); final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + @Nullable @Override public CompletionStage onText( java.net.http.WebSocket webSocket, CharSequence data, boolean last) { @@ -206,6 +198,7 @@ public CompletionStage onText( return null; } + @Nullable @Override public CompletionStage onBinary( java.net.http.WebSocket webSocket, ByteBuffer data, boolean last) { @@ -231,6 +224,7 @@ public CompletionStage onBinary( return null; } + @Nullable @Override public CompletionStage onClose( java.net.http.WebSocket webSocket, int statusCode, String reason) { @@ -629,9 +623,6 @@ public HttpProxySelector(Proxy proxy) { @Override public List select(URI uri) { - if (proxy == null) { - return List.of(); - } if (uri.getScheme().toLowerCase(Locale.ENGLISH).startsWith("http")) { return List.of(proxy); } diff --git a/java/src/org/openqa/selenium/remote/http/jdk/JdkHttpMessages.java b/java/src/org/openqa/selenium/remote/http/jdk/JdkHttpMessages.java index 21fafceac7af8..d0c3afac0aea7 100644 --- a/java/src/org/openqa/selenium/remote/http/jdk/JdkHttpMessages.java +++ b/java/src/org/openqa/selenium/remote/http/jdk/JdkHttpMessages.java @@ -17,7 +17,6 @@ package org.openqa.selenium.remote.http.jdk; -import static java.nio.charset.StandardCharsets.UTF_8; import static org.openqa.selenium.remote.http.HttpHeader.UserAgent; import com.google.common.net.MediaType; @@ -25,14 +24,11 @@ import java.io.InputStream; import java.io.UncheckedIOException; import java.net.URI; -import java.net.URLEncoder; import java.net.http.HttpRequest.BodyPublisher; import java.net.http.HttpRequest.BodyPublishers; import java.util.List; import java.util.Locale; import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; import org.openqa.selenium.io.Read; import org.openqa.selenium.remote.http.AddSeleniumUserAgent; import org.openqa.selenium.remote.http.ClientConfig; @@ -55,19 +51,7 @@ public java.net.http.HttpRequest createRequest(HttpRequest req, HttpMethod metho String rawUrl = rawUri.toString(); // Add query string if necessary - String queryString = - StreamSupport.stream(req.getQueryParameterNames().spliterator(), false) - .map( - name -> { - return StreamSupport.stream(req.getQueryParameters(name).spliterator(), false) - .map( - value -> - String.format( - "%s=%s", - URLEncoder.encode(name, UTF_8), URLEncoder.encode(value, UTF_8))) - .collect(Collectors.joining("&")); - }) - .collect(Collectors.joining("&")); + String queryString = req.getQueryString(); if (!queryString.isEmpty()) { rawUrl = rawUrl + "?" + queryString; @@ -147,6 +131,10 @@ public URI getRawUri(HttpRequest req) { || uri.startsWith("https://")) { rawUrl = uri; } else { + if (baseUrl == null) { + throw new IllegalStateException( + "Unable to resolve relative URI " + uri + ": base URI is not set in ClientConfig"); + } String base = baseUrl.toString(); if (base.endsWith("/")) { rawUrl = base.substring(0, base.length() - 1) + uri; diff --git a/java/src/org/openqa/selenium/remote/http/jdk/PasswordAuthenticator.java b/java/src/org/openqa/selenium/remote/http/jdk/PasswordAuthenticator.java new file mode 100644 index 0000000000000..67dfaa6d2380a --- /dev/null +++ b/java/src/org/openqa/selenium/remote/http/jdk/PasswordAuthenticator.java @@ -0,0 +1,37 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.remote.http.jdk; + +import java.net.Authenticator; +import java.net.PasswordAuthentication; + +class PasswordAuthenticator extends Authenticator { + private final String username; + private final String password; + + public PasswordAuthenticator(String userInfo) { + String[] parts = userInfo.split(":", 2); + username = parts[0]; + password = parts.length > 1 ? parts[1] : ""; + } + + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password.toCharArray()); + } +} diff --git a/java/src/org/openqa/selenium/remote/http/jdk/package-info.java b/java/src/org/openqa/selenium/remote/http/jdk/package-info.java new file mode 100644 index 0000000000000..97118380adc47 --- /dev/null +++ b/java/src/org/openqa/selenium/remote/http/jdk/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.remote.http.jdk; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/remote/http/package-info.java b/java/src/org/openqa/selenium/remote/http/package-info.java new file mode 100644 index 0000000000000..df2bf49c11da2 --- /dev/null +++ b/java/src/org/openqa/selenium/remote/http/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.remote.http; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/remote/locators/BUILD.bazel b/java/src/org/openqa/selenium/remote/locators/BUILD.bazel index a671552c6cdfd..285c5dce062e1 100644 --- a/java/src/org/openqa/selenium/remote/locators/BUILD.bazel +++ b/java/src/org/openqa/selenium/remote/locators/BUILD.bazel @@ -1,4 +1,5 @@ -load("//java:defs.bzl", "java_library") +load("@rules_jvm_external//:defs.bzl", "artifact") +load("//java:defs.bzl", "artifact", "java_library") java_library( name = "locators", @@ -9,5 +10,6 @@ java_library( deps = [ "//java/src/org/openqa/selenium:core", "//java/src/org/openqa/selenium/json", + artifact("org.jspecify:jspecify"), ], ) diff --git a/java/src/org/openqa/selenium/remote/locators/package-info.java b/java/src/org/openqa/selenium/remote/locators/package-info.java new file mode 100644 index 0000000000000..37bf62cea3ee6 --- /dev/null +++ b/java/src/org/openqa/selenium/remote/locators/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.remote.locators; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/remote/package-info.java b/java/src/org/openqa/selenium/remote/package-info.java new file mode 100644 index 0000000000000..7d1692584b03a --- /dev/null +++ b/java/src/org/openqa/selenium/remote/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.remote; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/remote/service/DriverCommandExecutor.java b/java/src/org/openqa/selenium/remote/service/DriverCommandExecutor.java index 610fbf2589872..177e89d2f076e 100644 --- a/java/src/org/openqa/selenium/remote/service/DriverCommandExecutor.java +++ b/java/src/org/openqa/selenium/remote/service/DriverCommandExecutor.java @@ -31,6 +31,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.remote.Command; import org.openqa.selenium.remote.CommandInfo; @@ -119,7 +120,7 @@ public Response execute(Command command) throws IOException { } if (DriverCommand.QUIT.equals(command.getName())) { - CompletableFuture commandComplete = + CompletableFuture<@Nullable Response> commandComplete = CompletableFuture.supplyAsync( () -> { try { @@ -141,7 +142,7 @@ public Response execute(Command command) throws IOException { }, executorService); - CompletableFuture processFinished = + CompletableFuture<@Nullable Response> processFinished = CompletableFuture.supplyAsync( () -> { try { diff --git a/java/src/org/openqa/selenium/remote/service/DriverFinder.java b/java/src/org/openqa/selenium/remote/service/DriverFinder.java index 6e7f12586a61f..4263a12e8c195 100644 --- a/java/src/org/openqa/selenium/remote/service/DriverFinder.java +++ b/java/src/org/openqa/selenium/remote/service/DriverFinder.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.Capabilities; import org.openqa.selenium.Proxy; import org.openqa.selenium.WebDriverException; @@ -38,7 +39,7 @@ public class DriverFinder { private final Capabilities options; private final SeleniumManager seleniumManager; private boolean offline; - private Result result; + private @Nullable Result result; public DriverFinder(DriverService service, Capabilities options) { this(service, options, SeleniumManager.getInstance()); @@ -171,6 +172,7 @@ private List toArguments() { * @param options browser options used to start the session * @return the browser binary path when present, only Chrome/Firefox/Edge */ + @Nullable private static String getBrowserBinary(Capabilities options) { List vendorOptionsCapabilities = List.of("moz:firefoxOptions", "goog:chromeOptions", "ms:edgeOptions"); diff --git a/java/src/org/openqa/selenium/remote/service/DriverService.java b/java/src/org/openqa/selenium/remote/service/DriverService.java index 3276498d3a315..8a5194eb7e872 100644 --- a/java/src/org/openqa/selenium/remote/service/DriverService.java +++ b/java/src/org/openqa/selenium/remote/service/DriverService.java @@ -61,7 +61,7 @@ * In addition to this, it is supposed that the driver server implements /shutdown hook that is used * to stop the server. */ -public class DriverService implements Closeable { +public abstract class DriverService implements Closeable { public static final String LOG_NULL = "/dev/null"; public static final String LOG_STDERR = "/dev/stderr"; @@ -84,7 +84,7 @@ public class DriverService implements Closeable { private final URL url; /** Controls access to {@link #process}. */ - private String executable; + private @Nullable String executable; private final ReentrantLock lock = new ReentrantLock(); private final Duration timeout; @@ -95,7 +95,7 @@ public class DriverService implements Closeable { * A reference to the current child process. Will be {@code null} whenever this service is not * running. Protected by {@link #lock}. */ - protected ExternalProcess process = null; + @Nullable protected ExternalProcess process = null; private OutputStream outputStream = System.err; @@ -110,7 +110,7 @@ public class DriverService implements Closeable { protected DriverService( @Nullable File executable, int port, - @Nullable Duration timeout, + Duration timeout, @Nullable List args, @Nullable Map environment) throws IOException { @@ -124,6 +124,7 @@ protected DriverService( this.url = getUrl(port); } + @Nullable public String getExecutable() { return executable; } @@ -152,13 +153,9 @@ protected Capabilities getDefaultDriverOptions() { return null; } - public @Nullable String getDriverProperty() { - return null; - } + public abstract String getDriverProperty(); - public String getDriverEnvironmentVariable() { - return null; - } + protected abstract String getDriverEnvironmentVariable(); protected @Nullable File getDriverExecutable() { return null; @@ -222,7 +219,7 @@ public void start() throws IOException { }, executorService); - CompletableFuture processFinished = + CompletableFuture<@Nullable StartOrDie> processFinished = CompletableFuture.supplyAsync( () -> { try { @@ -368,7 +365,7 @@ public abstract static class Builder environment = emptyMap(); private File logFile; - private Duration timeout; + private Duration timeout = DEFAULT_TIMEOUT; private OutputStream logOutputStream; /** diff --git a/java/src/org/openqa/selenium/remote/service/package-info.java b/java/src/org/openqa/selenium/remote/service/package-info.java new file mode 100644 index 0000000000000..306b5bce451d0 --- /dev/null +++ b/java/src/org/openqa/selenium/remote/service/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.remote.service; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/remote/tracing/BUILD.bazel b/java/src/org/openqa/selenium/remote/tracing/BUILD.bazel index eec1e94ee6654..2ee35a84b93c7 100644 --- a/java/src/org/openqa/selenium/remote/tracing/BUILD.bazel +++ b/java/src/org/openqa/selenium/remote/tracing/BUILD.bazel @@ -28,5 +28,6 @@ java_library( "//java/src/org/openqa/selenium:core", "//java/src/org/openqa/selenium/remote/http", artifact("io.opentelemetry:opentelemetry-api"), + artifact("org.jspecify:jspecify"), ], ) diff --git a/java/src/org/openqa/selenium/remote/tracing/HttpTracing.java b/java/src/org/openqa/selenium/remote/tracing/HttpTracing.java index b3157a6fad414..1a2849c3e65a0 100644 --- a/java/src/org/openqa/selenium/remote/tracing/HttpTracing.java +++ b/java/src/org/openqa/selenium/remote/tracing/HttpTracing.java @@ -19,6 +19,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.internal.Require; import org.openqa.selenium.remote.http.HttpRequest; @@ -48,7 +49,7 @@ public static Span newSpanAsChildOf(Tracer tracer, HttpRequest request, String n return parent.createSpan(name); } - public static void inject(Tracer tracer, TraceContext context, HttpRequest request) { + public static void inject(Tracer tracer, @Nullable TraceContext context, HttpRequest request) { if (context == null) { // Do nothing. return; diff --git a/java/src/org/openqa/selenium/remote/tracing/Propagator.java b/java/src/org/openqa/selenium/remote/tracing/Propagator.java index f2f6af3ce500b..ccd8347c6cc20 100644 --- a/java/src/org/openqa/selenium/remote/tracing/Propagator.java +++ b/java/src/org/openqa/selenium/remote/tracing/Propagator.java @@ -18,13 +18,14 @@ package org.openqa.selenium.remote.tracing; import java.util.function.BiFunction; +import org.jspecify.annotations.Nullable; public interface Propagator { void inject(TraceContext toInject, C carrier, Setter setter); TraceContext extractContext( - TraceContext existing, C carrier, BiFunction getter); + TraceContext existing, C carrier, BiFunction getter); interface Setter { void set(C carrier, String key, String value); diff --git a/java/src/org/openqa/selenium/remote/tracing/empty/BUILD.bazel b/java/src/org/openqa/selenium/remote/tracing/empty/BUILD.bazel index 2b48801268704..20bdfc8f959a8 100644 --- a/java/src/org/openqa/selenium/remote/tracing/empty/BUILD.bazel +++ b/java/src/org/openqa/selenium/remote/tracing/empty/BUILD.bazel @@ -1,4 +1,5 @@ -load("//java:defs.bzl", "java_library") +load("@rules_jvm_external//:defs.bzl", "artifact") +load("//java:defs.bzl", "artifact", "java_library") java_library( name = "empty", @@ -9,5 +10,6 @@ java_library( deps = [ "//java/src/org/openqa/selenium:core", "//java/src/org/openqa/selenium/remote/tracing:tracing-lib", + artifact("org.jspecify:jspecify"), ], ) diff --git a/java/src/org/openqa/selenium/remote/tracing/empty/NullPropagator.java b/java/src/org/openqa/selenium/remote/tracing/empty/NullPropagator.java index 3ec041228dd4b..5699ac6403d6d 100644 --- a/java/src/org/openqa/selenium/remote/tracing/empty/NullPropagator.java +++ b/java/src/org/openqa/selenium/remote/tracing/empty/NullPropagator.java @@ -18,6 +18,7 @@ package org.openqa.selenium.remote.tracing.empty; import java.util.function.BiFunction; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.remote.tracing.Propagator; import org.openqa.selenium.remote.tracing.TraceContext; @@ -27,7 +28,7 @@ public void inject(TraceContext toInject, C carrier, Setter setter) {} @Override public TraceContext extractContext( - TraceContext existing, C carrier, BiFunction getter) { + TraceContext existing, C carrier, BiFunction getter) { return existing; } } diff --git a/java/src/org/openqa/selenium/remote/tracing/empty/package-info.java b/java/src/org/openqa/selenium/remote/tracing/empty/package-info.java new file mode 100644 index 0000000000000..3ec3c64568550 --- /dev/null +++ b/java/src/org/openqa/selenium/remote/tracing/empty/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.remote.tracing.empty; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/remote/tracing/opentelemetry/BUILD.bazel b/java/src/org/openqa/selenium/remote/tracing/opentelemetry/BUILD.bazel index daf3748939450..8a3728d61c6df 100644 --- a/java/src/org/openqa/selenium/remote/tracing/opentelemetry/BUILD.bazel +++ b/java/src/org/openqa/selenium/remote/tracing/opentelemetry/BUILD.bazel @@ -21,5 +21,6 @@ java_library( artifact("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi"), artifact("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure"), artifact("io.opentelemetry:opentelemetry-sdk-trace"), + artifact("org.jspecify:jspecify"), ], ) diff --git a/java/src/org/openqa/selenium/remote/tracing/opentelemetry/OpenTelemetryPropagator.java b/java/src/org/openqa/selenium/remote/tracing/opentelemetry/OpenTelemetryPropagator.java index bc294ebc501f1..e908fd2288376 100644 --- a/java/src/org/openqa/selenium/remote/tracing/opentelemetry/OpenTelemetryPropagator.java +++ b/java/src/org/openqa/selenium/remote/tracing/opentelemetry/OpenTelemetryPropagator.java @@ -24,6 +24,7 @@ import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.context.propagation.TextMapSetter; import java.util.function.BiFunction; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.internal.Require; import org.openqa.selenium.remote.tracing.Propagator; import org.openqa.selenium.remote.tracing.TraceContext; @@ -53,22 +54,23 @@ public void inject(TraceContext toInject, C carrier, Setter setter) { @Override public OpenTelemetryContext extractContext( - TraceContext existing, C carrier, BiFunction getter) { + TraceContext existing, C carrier, BiFunction<@Nullable C, String, @Nullable String> getter) { Require.nonNull("Trace context to extract from", existing); Require.nonNull("Carrier", carrier); Require.nonNull("Getter", getter); Require.argument("Trace context", existing).instanceOf(OpenTelemetryContext.class); TextMapGetter propagatorGetter = - new TextMapGetter() { + new TextMapGetter<>() { @Override public Iterable keys(C carrier) { return null; } + @Nullable @Override - public String get(C carrier, String key) { + public String get(@Nullable C carrier, String key) { return getter.apply(carrier, key); } }; diff --git a/java/src/org/openqa/selenium/remote/tracing/opentelemetry/OpenTelemetryTracer.java b/java/src/org/openqa/selenium/remote/tracing/opentelemetry/OpenTelemetryTracer.java index 5b7389636d554..e8e68a334635f 100644 --- a/java/src/org/openqa/selenium/remote/tracing/opentelemetry/OpenTelemetryTracer.java +++ b/java/src/org/openqa/selenium/remote/tracing/opentelemetry/OpenTelemetryTracer.java @@ -23,6 +23,7 @@ import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import java.util.logging.Logger; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.internal.Require; import org.openqa.selenium.remote.tracing.AttributeMap; import org.openqa.selenium.remote.tracing.Propagator; @@ -40,7 +41,7 @@ public class OpenTelemetryTracer implements org.openqa.selenium.remote.tracing.T // humanity. This implies that we're never going to need to configure // tracing more than once for the entire JVM, so we're never going to be // adding unit tests for this. - private static volatile OpenTelemetryTracer singleton; + @Nullable private static volatile OpenTelemetryTracer singleton; public static void setHttpLogs(boolean value) { HTTP_LOGS = value; @@ -92,7 +93,7 @@ private static OpenTelemetryTracer createTracer() { private final Tracer tracer; private final OpenTelemetryPropagator telemetryPropagator; - private Context context; + private @Nullable Context context; public OpenTelemetryTracer(Tracer tracer, TextMapPropagator propagator) { this.tracer = Require.nonNull("Tracer", tracer); diff --git a/java/src/org/openqa/selenium/remote/tracing/opentelemetry/package-info.java b/java/src/org/openqa/selenium/remote/tracing/opentelemetry/package-info.java new file mode 100644 index 0000000000000..b3ccb73a5fcfb --- /dev/null +++ b/java/src/org/openqa/selenium/remote/tracing/opentelemetry/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.remote.tracing.opentelemetry; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/remote/tracing/package-info.java b/java/src/org/openqa/selenium/remote/tracing/package-info.java new file mode 100644 index 0000000000000..c7e4ae986d24a --- /dev/null +++ b/java/src/org/openqa/selenium/remote/tracing/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.remote.tracing; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/safari/ConnectionClosedException.java b/java/src/org/openqa/selenium/safari/ConnectionClosedException.java index 696b8696a3fb8..35bb49b9d41b4 100644 --- a/java/src/org/openqa/selenium/safari/ConnectionClosedException.java +++ b/java/src/org/openqa/selenium/safari/ConnectionClosedException.java @@ -17,12 +17,10 @@ package org.openqa.selenium.safari; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebDriverException; /** Exception thrown when the connection to the SafariDriver is lost. */ -@NullMarked public class ConnectionClosedException extends WebDriverException { public ConnectionClosedException(@Nullable String message) { diff --git a/java/src/org/openqa/selenium/safari/SafariDriver.java b/java/src/org/openqa/selenium/safari/SafariDriver.java index 406a341e50e85..2a4a570b79c97 100644 --- a/java/src/org/openqa/selenium/safari/SafariDriver.java +++ b/java/src/org/openqa/selenium/safari/SafariDriver.java @@ -43,7 +43,7 @@ public class SafariDriver extends RemoteWebDriver implements HasPermissions, Has private final HasPermissions permissions; private final HasDebugger debugger; - /** Initializes a new SafariDriver} class with default {@link SafariOptions}. */ + /** Initializes a new SafariDriver class with default {@link SafariOptions}. */ public SafariDriver() { this(new SafariOptions()); } diff --git a/java/src/org/openqa/selenium/safari/SafariDriverService.java b/java/src/org/openqa/selenium/safari/SafariDriverService.java index 52d3d7bc00f96..dc46bfbcf0990 100644 --- a/java/src/org/openqa/selenium/safari/SafariDriverService.java +++ b/java/src/org/openqa/selenium/safari/SafariDriverService.java @@ -64,7 +64,7 @@ public class SafariDriverService extends DriverService { public SafariDriverService( @Nullable File executable, int port, - @Nullable Duration timeout, + Duration timeout, @Nullable List args, @Nullable Map environment) throws IOException { @@ -166,7 +166,7 @@ protected List createArgs() { protected SafariDriverService createDriverService( @Nullable File exe, int port, - @Nullable Duration timeout, + Duration timeout, @Nullable List args, @Nullable Map environment) { try { diff --git a/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverService.java b/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverService.java index d6d03d8f172b9..dda3df3f35f7d 100644 --- a/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverService.java +++ b/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverService.java @@ -78,6 +78,11 @@ public String getDriverProperty() { return TP_SAFARI_DRIVER_EXE_PROPERTY; } + @Override + protected String getDriverEnvironmentVariable() { + return ""; + } + public File getDriverExecutable() { return TP_SAFARI_DRIVER_EXECUTABLE; } diff --git a/java/src/org/openqa/selenium/support/AbstractFindByBuilder.java b/java/src/org/openqa/selenium/support/AbstractFindByBuilder.java index c80dfea948081..f8071585d7dab 100644 --- a/java/src/org/openqa/selenium/support/AbstractFindByBuilder.java +++ b/java/src/org/openqa/selenium/support/AbstractFindByBuilder.java @@ -20,6 +20,7 @@ import java.lang.reflect.Field; import java.util.HashSet; import java.util.Set; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.By; public abstract class AbstractFindByBuilder { @@ -37,6 +38,7 @@ protected By buildByFromFindBy(FindBy findBy) { return ans; } + @Nullable protected By buildByFromShortFindBy(FindBy findBy) { if (!"".equals(findBy.className())) { return By.className(findBy.className()); diff --git a/java/src/org/openqa/selenium/support/Color.java b/java/src/org/openqa/selenium/support/Color.java index ef75b0810ecb1..63164916b5142 100644 --- a/java/src/org/openqa/selenium/support/Color.java +++ b/java/src/org/openqa/selenium/support/Color.java @@ -100,7 +100,7 @@ public String toString() { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (other == null) { return false; } diff --git a/java/src/org/openqa/selenium/support/PageFactoryFinder.java b/java/src/org/openqa/selenium/support/PageFactoryFinder.java index c4698f492fb66..c9b07740396af 100644 --- a/java/src/org/openqa/selenium/support/PageFactoryFinder.java +++ b/java/src/org/openqa/selenium/support/PageFactoryFinder.java @@ -22,5 +22,5 @@ @Retention(RetentionPolicy.RUNTIME) public @interface PageFactoryFinder { - Class value(); + Class> value(); } diff --git a/java/src/org/openqa/selenium/support/decorators/BUILD.bazel b/java/src/org/openqa/selenium/support/decorators/BUILD.bazel index 5ad1eb0cd9fd5..cec272fdf9fae 100644 --- a/java/src/org/openqa/selenium/support/decorators/BUILD.bazel +++ b/java/src/org/openqa/selenium/support/decorators/BUILD.bazel @@ -12,5 +12,6 @@ java_library( deps = [ "//java/src/org/openqa/selenium:core", artifact("net.bytebuddy:byte-buddy"), + artifact("org.jspecify:jspecify"), ], ) diff --git a/java/src/org/openqa/selenium/support/decorators/package-info.java b/java/src/org/openqa/selenium/support/decorators/package-info.java new file mode 100644 index 0000000000000..b8396c98384ce --- /dev/null +++ b/java/src/org/openqa/selenium/support/decorators/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.support.decorators; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/support/events/BUILD.bazel b/java/src/org/openqa/selenium/support/events/BUILD.bazel index 8cb56480e0102..ea5a29d688954 100644 --- a/java/src/org/openqa/selenium/support/events/BUILD.bazel +++ b/java/src/org/openqa/selenium/support/events/BUILD.bazel @@ -1,4 +1,4 @@ -load("//java:defs.bzl", "java_library") +load("//java:defs.bzl", "artifact", "java_library") java_library( name = "events", @@ -10,5 +10,6 @@ java_library( "//java/src/org/openqa/selenium:core", "//java/src/org/openqa/selenium/remote", "//java/src/org/openqa/selenium/support/decorators", + artifact("org.jspecify:jspecify"), ], ) diff --git a/java/src/org/openqa/selenium/support/events/EventFiringDecorator.java b/java/src/org/openqa/selenium/support/events/EventFiringDecorator.java index bccb47b00a09b..896f434469b43 100644 --- a/java/src/org/openqa/selenium/support/events/EventFiringDecorator.java +++ b/java/src/org/openqa/selenium/support/events/EventFiringDecorator.java @@ -24,6 +24,7 @@ import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.Alert; import org.openqa.selenium.Beta; import org.openqa.selenium.WebDriver; @@ -322,6 +323,7 @@ private String createEventMethodName(String prefix, String originalMethodName) { + originalMethodName.substring(1); } + @Nullable private Method findMatchingMethod(WebDriverListener listener, String methodName, Object[] args) { for (Method m : listener.getClass().getMethods()) { if (m.getName().equals(methodName) && parametersMatch(m, args)) { diff --git a/java/src/org/openqa/selenium/support/events/package-info.java b/java/src/org/openqa/selenium/support/events/package-info.java new file mode 100644 index 0000000000000..f0b528551ba24 --- /dev/null +++ b/java/src/org/openqa/selenium/support/events/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.support.events; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/support/locators/BUILD.bazel b/java/src/org/openqa/selenium/support/locators/BUILD.bazel index 6a43703d981c0..0d204a6eeed2d 100644 --- a/java/src/org/openqa/selenium/support/locators/BUILD.bazel +++ b/java/src/org/openqa/selenium/support/locators/BUILD.bazel @@ -1,5 +1,5 @@ load("//common:defs.bzl", "copy_file") -load("//java:defs.bzl", "java_library") +load("//java:defs.bzl", "artifact", "java_library") java_library( name = "locators", @@ -15,6 +15,7 @@ java_library( "//java:auto-service", "//java/src/org/openqa/selenium/json", "//java/src/org/openqa/selenium/remote", + artifact("org.jspecify:jspecify"), ], ) diff --git a/java/src/org/openqa/selenium/support/locators/RelativeLocator.java b/java/src/org/openqa/selenium/support/locators/RelativeLocator.java index 486ce4682b1f6..a2e2346394611 100644 --- a/java/src/org/openqa/selenium/support/locators/RelativeLocator.java +++ b/java/src/org/openqa/selenium/support/locators/RelativeLocator.java @@ -17,6 +17,7 @@ package org.openqa.selenium.support.locators; +import static java.util.Objects.requireNonNull; import static org.openqa.selenium.json.Json.MAP_TYPE; import static org.openqa.selenium.support.locators.RelativeLocatorScript.FIND_ELEMENTS; @@ -236,7 +237,7 @@ public List findElements(SearchContext context) { @SuppressWarnings("unchecked") List elements = (List) js.executeScript(FIND_ELEMENTS, asAtomLocatorParameter(this)); - return elements; + return requireNonNull(elements); } private RelativeBy simpleDirection(String direction, Object locator) { diff --git a/java/src/org/openqa/selenium/support/locators/RelativeLocatorScript.java b/java/src/org/openqa/selenium/support/locators/RelativeLocatorScript.java index 9768e1d32ac0d..456ed03aaffd3 100644 --- a/java/src/org/openqa/selenium/support/locators/RelativeLocatorScript.java +++ b/java/src/org/openqa/selenium/support/locators/RelativeLocatorScript.java @@ -17,6 +17,8 @@ package org.openqa.selenium.support.locators; +import static java.util.Objects.requireNonNull; + import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; @@ -35,6 +37,7 @@ class RelativeLocatorScript { String rawFunction; try (InputStream stream = RelativeLocator.class.getResourceAsStream(location)) { + requireNonNull(stream, () -> "Resource not found: " + location); rawFunction = new String(stream.readAllBytes(), StandardCharsets.UTF_8); } diff --git a/java/src/org/openqa/selenium/support/locators/RelativeLocatorServerSide.java b/java/src/org/openqa/selenium/support/locators/RelativeLocatorServerSide.java index 6b58e457a716f..1727c97e3886d 100644 --- a/java/src/org/openqa/selenium/support/locators/RelativeLocatorServerSide.java +++ b/java/src/org/openqa/selenium/support/locators/RelativeLocatorServerSide.java @@ -17,6 +17,7 @@ package org.openqa.selenium.support.locators; +import static java.util.Objects.requireNonNull; import static org.openqa.selenium.support.locators.RelativeLocatorScript.FIND_ELEMENTS; import com.google.auto.service.AutoService; @@ -65,7 +66,7 @@ public List findElements(SearchContext context) { @SuppressWarnings("unchecked") List elements = (List) js.executeScript(FIND_ELEMENTS, Map.of("relative", converted)); - return elements; + return requireNonNull(elements); } throw new InvalidArgumentException("Unable to find element"); diff --git a/java/src/org/openqa/selenium/support/locators/package-info.java b/java/src/org/openqa/selenium/support/locators/package-info.java new file mode 100644 index 0000000000000..00775bd1db41a --- /dev/null +++ b/java/src/org/openqa/selenium/support/locators/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.support.locators; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/support/package-info.java b/java/src/org/openqa/selenium/support/package-info.java new file mode 100644 index 0000000000000..776335208172f --- /dev/null +++ b/java/src/org/openqa/selenium/support/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.support; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/support/pagefactory/AjaxElementLocator.java b/java/src/org/openqa/selenium/support/pagefactory/AjaxElementLocator.java index 74a1e62d7bf7f..8ab4e0bf189f1 100644 --- a/java/src/org/openqa/selenium/support/pagefactory/AjaxElementLocator.java +++ b/java/src/org/openqa/selenium/support/pagefactory/AjaxElementLocator.java @@ -22,6 +22,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.SearchContext; import org.openqa.selenium.WebElement; @@ -134,8 +135,8 @@ protected boolean isElementUsable(WebElement element) { } private class SlowLoadingElement extends SlowLoadableComponent { - private NoSuchElementException lastException; - private WebElement element; + private @Nullable NoSuchElementException lastException; + private @Nullable WebElement element; public SlowLoadingElement(Clock clock, int timeOutInSeconds) { super(clock, Duration.ofSeconds(timeOutInSeconds)); @@ -165,18 +166,20 @@ protected void isLoaded() throws Error { } } + @Nullable public NoSuchElementException getLastException() { return lastException; } + @Nullable public WebElement getElement() { return element; } } private class SlowLoadingElementList extends SlowLoadableComponent { - private NoSuchElementException lastException; - private List elements; + private @Nullable NoSuchElementException lastException; + private @Nullable List elements; public SlowLoadingElementList(Clock clock, int timeOutInSeconds) { super(clock, Duration.ofSeconds(timeOutInSeconds)); @@ -211,10 +214,12 @@ protected void isLoaded() throws Error { } } + @Nullable public NoSuchElementException getLastException() { return lastException; } + @Nullable public List getElements() { return elements; } diff --git a/java/src/org/openqa/selenium/support/pagefactory/ByAll.java b/java/src/org/openqa/selenium/support/pagefactory/ByAll.java index 520c52bf88078..8ee9e0bee71d5 100644 --- a/java/src/org/openqa/selenium/support/pagefactory/ByAll.java +++ b/java/src/org/openqa/selenium/support/pagefactory/ByAll.java @@ -20,7 +20,6 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import org.jspecify.annotations.NullMarked; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.SearchContext; @@ -37,7 +36,6 @@ * will find all elements that match by1 and then all elements that match by2. * This means that the list of elements returned may not be in document order. */ -@NullMarked public class ByAll extends By implements Serializable { private static final long serialVersionUID = 4573668832699497306L; diff --git a/java/src/org/openqa/selenium/support/pagefactory/ByChained.java b/java/src/org/openqa/selenium/support/pagefactory/ByChained.java index 7f67a6bcafebb..2beb51681c1b5 100644 --- a/java/src/org/openqa/selenium/support/pagefactory/ByChained.java +++ b/java/src/org/openqa/selenium/support/pagefactory/ByChained.java @@ -22,7 +22,6 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.jspecify.annotations.NullMarked; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.SearchContext; @@ -39,7 +38,6 @@ * will find all elements that match by2 and appear under an element that matches * by1. */ -@NullMarked public class ByChained extends By implements Serializable { private static final long serialVersionUID = 1563769051170172451L; diff --git a/java/src/org/openqa/selenium/support/pagefactory/DefaultElementLocator.java b/java/src/org/openqa/selenium/support/pagefactory/DefaultElementLocator.java index a779ebf5ddda8..8b56b99d1157b 100644 --- a/java/src/org/openqa/selenium/support/pagefactory/DefaultElementLocator.java +++ b/java/src/org/openqa/selenium/support/pagefactory/DefaultElementLocator.java @@ -19,6 +19,7 @@ import java.lang.reflect.Field; import java.util.List; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.By; import org.openqa.selenium.SearchContext; import org.openqa.selenium.WebElement; @@ -33,8 +34,8 @@ public class DefaultElementLocator implements ElementLocator { private final SearchContext searchContext; private final boolean shouldCache; private final By by; - private WebElement cachedElement; - private List cachedElementList; + private @Nullable WebElement cachedElement; + private @Nullable List cachedElementList; /** * Creates a new element locator. diff --git a/java/src/org/openqa/selenium/support/pagefactory/DefaultFieldDecorator.java b/java/src/org/openqa/selenium/support/pagefactory/DefaultFieldDecorator.java index 2c8f36c594a90..790175e63f473 100644 --- a/java/src/org/openqa/selenium/support/pagefactory/DefaultFieldDecorator.java +++ b/java/src/org/openqa/selenium/support/pagefactory/DefaultFieldDecorator.java @@ -23,6 +23,7 @@ import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.util.List; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebElement; import org.openqa.selenium.WrapsElement; import org.openqa.selenium.interactions.Locatable; @@ -46,6 +47,7 @@ public DefaultFieldDecorator(ElementLocatorFactory factory) { this.factory = factory; } + @Nullable @Override public Object decorate(ClassLoader loader, Field field) { if (!(WebElement.class.isAssignableFrom(field.getType()) || isDecoratableList(field))) { diff --git a/java/src/org/openqa/selenium/support/pagefactory/FieldDecorator.java b/java/src/org/openqa/selenium/support/pagefactory/FieldDecorator.java index 31991b314e1b8..da20eae8efdad 100644 --- a/java/src/org/openqa/selenium/support/pagefactory/FieldDecorator.java +++ b/java/src/org/openqa/selenium/support/pagefactory/FieldDecorator.java @@ -18,6 +18,7 @@ package org.openqa.selenium.support.pagefactory; import java.lang.reflect.Field; +import org.jspecify.annotations.Nullable; /** Allows the PageFactory to decorate fields. */ public interface FieldDecorator { @@ -29,5 +30,5 @@ public interface FieldDecorator { * @return Value to decorate the field with or null if it shouldn't be decorated. If non-null, * must be assignable to the field. */ - Object decorate(ClassLoader loader, Field field); + @Nullable Object decorate(ClassLoader loader, Field field); } diff --git a/java/src/org/openqa/selenium/support/pagefactory/internal/package-info.java b/java/src/org/openqa/selenium/support/pagefactory/internal/package-info.java new file mode 100644 index 0000000000000..2ea226387d050 --- /dev/null +++ b/java/src/org/openqa/selenium/support/pagefactory/internal/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.support.pagefactory.internal; + +import org.jspecify.annotations.NullMarked; diff --git a/java/src/org/openqa/selenium/support/pagefactory/package-info.java b/java/src/org/openqa/selenium/support/pagefactory/package-info.java new file mode 100644 index 0000000000000..d0ec86f8e3a19 --- /dev/null +++ b/java/src/org/openqa/selenium/support/pagefactory/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.support.pagefactory; + +import org.jspecify.annotations.NullMarked; diff --git a/java/test/org/openqa/selenium/CookieImplementationTest.java b/java/test/org/openqa/selenium/CookieImplementationTest.java index b5839ef1d21b1..e3194cba0e011 100644 --- a/java/test/org/openqa/selenium/CookieImplementationTest.java +++ b/java/test/org/openqa/selenium/CookieImplementationTest.java @@ -485,20 +485,20 @@ public void testDeleteNotExistedCookie() { public void testDeleteEmptyNamedCookie() { assertThatThrownBy(() -> driver.manage().deleteCookieNamed("")) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Cookie name cannot be empty"); + .hasMessage("Cookie name must not be blank"); assertThatThrownBy(() -> driver.manage().deleteCookieNamed(" ")) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Cookie name cannot be empty"); + .hasMessage("Cookie name must not be blank"); } @Test public void testGetEmptyNamedCookie() { assertThatThrownBy(() -> driver.manage().getCookieNamed("")) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Cookie name cannot be empty"); + .hasMessage("Cookie name must not be blank"); assertThatThrownBy(() -> driver.manage().getCookieNamed(" ")) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Cookie name cannot be empty"); + .hasMessage("Cookie name must not be blank"); } @Test diff --git a/java/test/org/openqa/selenium/ReferrerTest.java b/java/test/org/openqa/selenium/ReferrerTest.java index 7023af1de0ef8..6170237787e9f 100644 --- a/java/test/org/openqa/selenium/ReferrerTest.java +++ b/java/test/org/openqa/selenium/ReferrerTest.java @@ -35,6 +35,7 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; +import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -220,6 +221,7 @@ public int hashCode() { } } + @NullMarked private static class RecordingHandler implements HttpHandler { private final List requests = new CopyOnWriteArrayList<>(); diff --git a/java/test/org/openqa/selenium/bidi/BUILD.bazel b/java/test/org/openqa/selenium/bidi/BUILD.bazel index 4cd38e6a10df1..902f5eed785b2 100644 --- a/java/test/org/openqa/selenium/bidi/BUILD.bazel +++ b/java/test/org/openqa/selenium/bidi/BUILD.bazel @@ -28,5 +28,6 @@ java_selenium_test_suite( artifact("com.google.guava:guava"), artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/bidi/browser/BUILD.bazel b/java/test/org/openqa/selenium/bidi/browser/BUILD.bazel index 506d1a4e1eed2..086f05b3bf117 100644 --- a/java/test/org/openqa/selenium/bidi/browser/BUILD.bazel +++ b/java/test/org/openqa/selenium/bidi/browser/BUILD.bazel @@ -24,5 +24,6 @@ java_selenium_test_suite( artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.junit.jupiter:junit-jupiter-params"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java b/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java index 5307c436a98bf..eb196ca12a034 100644 --- a/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java +++ b/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.function.Function; import java.util.function.Supplier; +import java.util.logging.Logger; import java.util.stream.Stream; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -48,13 +49,17 @@ class BrowserCommandsTest extends JupiterTestBase { + private static final Logger LOG = Logger.getLogger(BrowserCommandsTest.class.getName()); private final Path tmpDir = - TemporaryFilesystem.getDefaultTmpFS().createTempDir("downloads", "test").toPath(); + TemporaryFilesystem.getDefaultTmpFS() + .createTempDir("selenium-", "-BrowserCommandsTest") + .toPath(); private Browser browser; @BeforeEach final void setUp() { browser = new Browser(driver); + LOG.info(() -> "Created temp dir: " + tmpDir.toAbsolutePath()); } @AfterEach @@ -64,6 +69,7 @@ final void resetDownloadBehavior() { @AfterEach final void deleteTempDir() { + LOG.info(() -> "Deleting temp dir: " + tmpDir.toAbsolutePath()); TemporaryFilesystem.getDefaultTmpFS().deleteTempDir(tmpDir.toFile()); } @@ -221,7 +227,27 @@ private FileIsFound(Path dir, String expectedFileName) { @Override public Boolean apply(WebDriver driver) { foundFiles = files(dir); - return foundFiles.contains(expectedFileName); + boolean result = foundFiles.contains(expectedFileName); + if (result) { + LOG.info( + () -> + "Found file: " + + expectedFileName + + " in temp dir: " + + dir.toAbsolutePath() + + ". All found files: " + + foundFiles); + } else { + LOG.info( + () -> + "Not found file: " + + expectedFileName + + " in temp dir: " + + dir.toAbsolutePath() + + ". All found files: " + + foundFiles); + } + return result; } @Override diff --git a/java/test/org/openqa/selenium/bidi/browsingcontext/BUILD.bazel b/java/test/org/openqa/selenium/bidi/browsingcontext/BUILD.bazel index 30734c42debd4..71590c2ed47c8 100644 --- a/java/test/org/openqa/selenium/bidi/browsingcontext/BUILD.bazel +++ b/java/test/org/openqa/selenium/bidi/browsingcontext/BUILD.bazel @@ -29,5 +29,6 @@ java_selenium_test_suite( artifact("com.google.guava:guava"), artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/bidi/emulation/BUILD.bazel b/java/test/org/openqa/selenium/bidi/emulation/BUILD.bazel index 42d2ca75acec6..b9119abab92ce 100644 --- a/java/test/org/openqa/selenium/bidi/emulation/BUILD.bazel +++ b/java/test/org/openqa/selenium/bidi/emulation/BUILD.bazel @@ -29,5 +29,6 @@ java_selenium_test_suite( artifact("com.google.guava:guava"), artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/bidi/input/BUILD.bazel b/java/test/org/openqa/selenium/bidi/input/BUILD.bazel index 7523b242897e4..e8cc1dbb23272 100644 --- a/java/test/org/openqa/selenium/bidi/input/BUILD.bazel +++ b/java/test/org/openqa/selenium/bidi/input/BUILD.bazel @@ -35,5 +35,6 @@ java_selenium_test_suite( artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), artifact("org.mockito:mockito-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/bidi/log/BUILD.bazel b/java/test/org/openqa/selenium/bidi/log/BUILD.bazel index 4cd38e6a10df1..902f5eed785b2 100644 --- a/java/test/org/openqa/selenium/bidi/log/BUILD.bazel +++ b/java/test/org/openqa/selenium/bidi/log/BUILD.bazel @@ -28,5 +28,6 @@ java_selenium_test_suite( artifact("com.google.guava:guava"), artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/bidi/network/BUILD.bazel b/java/test/org/openqa/selenium/bidi/network/BUILD.bazel index 6f53356b7e7ab..500188b682fe8 100644 --- a/java/test/org/openqa/selenium/bidi/network/BUILD.bazel +++ b/java/test/org/openqa/selenium/bidi/network/BUILD.bazel @@ -27,5 +27,6 @@ java_selenium_test_suite( "//java/test/org/openqa/selenium/testing/drivers", artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/bidi/permissions/BUILD.bazel b/java/test/org/openqa/selenium/bidi/permissions/BUILD.bazel index 7443c5321097a..50e0d61aee2ec 100644 --- a/java/test/org/openqa/selenium/bidi/permissions/BUILD.bazel +++ b/java/test/org/openqa/selenium/bidi/permissions/BUILD.bazel @@ -33,5 +33,6 @@ java_selenium_test_suite( artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), artifact("org.mockito:mockito-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/bidi/script/BUILD.bazel b/java/test/org/openqa/selenium/bidi/script/BUILD.bazel index 4cd38e6a10df1..902f5eed785b2 100644 --- a/java/test/org/openqa/selenium/bidi/script/BUILD.bazel +++ b/java/test/org/openqa/selenium/bidi/script/BUILD.bazel @@ -28,5 +28,6 @@ java_selenium_test_suite( artifact("com.google.guava:guava"), artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/bidi/speculation/BUILD.bazel b/java/test/org/openqa/selenium/bidi/speculation/BUILD.bazel index 5d579439ef43a..bab747b9071b7 100644 --- a/java/test/org/openqa/selenium/bidi/speculation/BUILD.bazel +++ b/java/test/org/openqa/selenium/bidi/speculation/BUILD.bazel @@ -27,5 +27,6 @@ java_selenium_test_suite( "//java/test/org/openqa/selenium/testing/drivers", artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/bidi/storage/BUILD.bazel b/java/test/org/openqa/selenium/bidi/storage/BUILD.bazel index fc44690ac23f1..464df9e21931b 100644 --- a/java/test/org/openqa/selenium/bidi/storage/BUILD.bazel +++ b/java/test/org/openqa/selenium/bidi/storage/BUILD.bazel @@ -24,5 +24,6 @@ java_selenium_test_suite( "//java/test/org/openqa/selenium/testing/drivers", artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/bidi/webextension/BUILD.bazel b/java/test/org/openqa/selenium/bidi/webextension/BUILD.bazel index 6f27baa64eb30..736035d4a2ad3 100644 --- a/java/test/org/openqa/selenium/bidi/webextension/BUILD.bazel +++ b/java/test/org/openqa/selenium/bidi/webextension/BUILD.bazel @@ -31,5 +31,6 @@ java_selenium_test_suite( artifact("com.google.guava:guava"), artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/chrome/BUILD.bazel b/java/test/org/openqa/selenium/chrome/BUILD.bazel index ec02a2dd376c4..33a79f5725904 100644 --- a/java/test/org/openqa/selenium/chrome/BUILD.bazel +++ b/java/test/org/openqa/selenium/chrome/BUILD.bazel @@ -55,5 +55,6 @@ java_selenium_test_suite( artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), artifact("org.mockito:mockito-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/chromium/BUILD.bazel b/java/test/org/openqa/selenium/chromium/BUILD.bazel index a49920bc0d80d..622cc784dfb69 100644 --- a/java/test/org/openqa/selenium/chromium/BUILD.bazel +++ b/java/test/org/openqa/selenium/chromium/BUILD.bazel @@ -20,5 +20,6 @@ java_selenium_test_suite( "//java/test/org/openqa/selenium/testing:test-base", artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/edge/BUILD.bazel b/java/test/org/openqa/selenium/edge/BUILD.bazel index a6ed551b9ae7f..0019de09d4bd1 100644 --- a/java/test/org/openqa/selenium/edge/BUILD.bazel +++ b/java/test/org/openqa/selenium/edge/BUILD.bazel @@ -54,5 +54,6 @@ java_selenium_test_suite( artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), artifact("org.mockito:mockito-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/environment/BUILD.bazel b/java/test/org/openqa/selenium/environment/BUILD.bazel index cf1bc87e82091..753930f4560bb 100644 --- a/java/test/org/openqa/selenium/environment/BUILD.bazel +++ b/java/test/org/openqa/selenium/environment/BUILD.bazel @@ -57,6 +57,7 @@ java_library( artifact("com.google.guava:guava"), artifact("dev.failsafe:failsafe"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ], ) @@ -97,6 +98,7 @@ java_library( "//java/test/org/openqa/selenium/testing/drivers", artifact("org.assertj:assertj-core"), artifact("org.junit.jupiter:junit-jupiter-api"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) @@ -115,5 +117,6 @@ java_test_suite( ":environment", ":test-base", artifact("org.junit.jupiter:junit-jupiter-api"), + artifact("org.jspecify:jspecify"), ] + firefox["deps"] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/environment/GlobalTestEnvironment.java b/java/test/org/openqa/selenium/environment/GlobalTestEnvironment.java index 6f20bf073067c..e726595601248 100644 --- a/java/test/org/openqa/selenium/environment/GlobalTestEnvironment.java +++ b/java/test/org/openqa/selenium/environment/GlobalTestEnvironment.java @@ -17,21 +17,24 @@ package org.openqa.selenium.environment; +import static java.util.Objects.requireNonNull; + import java.util.logging.Level; import java.util.logging.Logger; +import org.jspecify.annotations.Nullable; /** Used to hold a TestEnvironment in a static class-level field. */ public class GlobalTestEnvironment { private static final Logger LOG = Logger.getLogger(GlobalTestEnvironment.class.getName()); - private static TestEnvironment environment; + @Nullable private static TestEnvironment environment = null; public static boolean isSetUp() { return environment != null; } public static TestEnvironment get() { - return environment; + return requireNonNull(environment, "Environment not created"); } public static synchronized TestEnvironment getOrCreate(boolean needsSecureServer) { diff --git a/java/test/org/openqa/selenium/environment/package-info.java b/java/test/org/openqa/selenium/environment/package-info.java new file mode 100644 index 0000000000000..4b4b9abdb3e1c --- /dev/null +++ b/java/test/org/openqa/selenium/environment/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.environment; + +import org.jspecify.annotations.NullMarked; diff --git a/java/test/org/openqa/selenium/environment/webserver/AppServer.java b/java/test/org/openqa/selenium/environment/webserver/AppServer.java index f3e8c8ac2cc08..9e07524fbae3a 100644 --- a/java/test/org/openqa/selenium/environment/webserver/AppServer.java +++ b/java/test/org/openqa/selenium/environment/webserver/AppServer.java @@ -17,6 +17,7 @@ package org.openqa.selenium.environment.webserver; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.net.NetworkUtils; @@ -24,7 +25,7 @@ public interface AppServer { String getHostName(); - String getAlternateHostName(); + @Nullable String getAlternateHostName(); String whereIs(String relativeUrl); @@ -47,6 +48,7 @@ static String detectHostname() { return hostnameFromProperty == null ? "localhost" : hostnameFromProperty; } + @Nullable static String detectAlternateHostname() { String alternativeHostnameFromProperty = System.getenv("ALTERNATIVE_HOSTNAME"); if (alternativeHostnameFromProperty != null) { diff --git a/java/test/org/openqa/selenium/environment/webserver/AppServerTestBase.java b/java/test/org/openqa/selenium/environment/webserver/AppServerTestBase.java index 758194dace09c..e51c7e6255b8e 100644 --- a/java/test/org/openqa/selenium/environment/webserver/AppServerTestBase.java +++ b/java/test/org/openqa/selenium/environment/webserver/AppServerTestBase.java @@ -28,6 +28,7 @@ import java.net.URL; import java.nio.file.Files; import java.time.Duration; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -45,7 +46,7 @@ public abstract class AppServerTestBase { private static final String APPCACHE_MIME_TYPE = "text/cache-manifest"; private AppServer server; - private static WebDriver driver; + private static @Nullable WebDriver driver = null; @BeforeAll public static void startDriver() { @@ -67,7 +68,10 @@ public void stopServer() { @AfterAll public static void quitDriver() { - driver.quit(); + if (driver != null) { + driver.quit(); + driver = null; + } } @Test diff --git a/java/test/org/openqa/selenium/environment/webserver/CookieHandler.java b/java/test/org/openqa/selenium/environment/webserver/CookieHandler.java index 86ffa597033a2..5bed7e7598a95 100644 --- a/java/test/org/openqa/selenium/environment/webserver/CookieHandler.java +++ b/java/test/org/openqa/selenium/environment/webserver/CookieHandler.java @@ -32,6 +32,7 @@ import java.util.List; import java.util.function.Function; import java.util.stream.StreamSupport; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.Cookie; import org.openqa.selenium.remote.http.Contents; import org.openqa.selenium.remote.http.HttpHandler; @@ -99,7 +100,8 @@ public HttpResponse execute(HttpRequest request) throws UncheckedIOException { return response; } - private void append(StringBuilder builder, X fromCookie, Function value) { + private void append( + StringBuilder builder, @Nullable X fromCookie, Function value) { if (fromCookie == null) { return; } @@ -195,7 +197,7 @@ private Cookie parse(String cookieString) { return builder.build(); } - private String escapeCookieValue(String value) { + private String escapeCookieValue(@Nullable String value) { if (value == null || value.isEmpty()) { return ""; } diff --git a/java/test/org/openqa/selenium/environment/webserver/NettyAppServer.java b/java/test/org/openqa/selenium/environment/webserver/NettyAppServer.java index d05aff62161bb..9adcc40777393 100644 --- a/java/test/org/openqa/selenium/environment/webserver/NettyAppServer.java +++ b/java/test/org/openqa/selenium/environment/webserver/NettyAppServer.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.grid.config.CompoundConfig; import org.openqa.selenium.grid.config.Config; import org.openqa.selenium.grid.config.MapConfig; @@ -56,8 +57,8 @@ public class NettyAppServer implements AppServer { private static final Config sslConfig = new MapConfig(singletonMap("server", singletonMap("https-self-signed", true))); private static final Logger LOG = Logger.getLogger(NettyAppServer.class.getName()); - private Server server; - private Server secure; + private @Nullable Server server; + private @Nullable Server secure; private final RetryPolicy retryPolicy = RetryPolicy.builder() .withMaxAttempts(5) @@ -165,7 +166,9 @@ public void start() { @Override public void stop() { - server.stop(); + if (server != null) { + server.stop(); + } if (secure != null) { secure.stop(); } @@ -231,6 +234,7 @@ public String getHostName() { return AppServer.detectHostname(); } + @Nullable @Override public String getAlternateHostName() { return AppServer.detectAlternateHostname(); diff --git a/java/test/org/openqa/selenium/environment/webserver/Page.java b/java/test/org/openqa/selenium/environment/webserver/Page.java index 09d2253a6eb4e..ecbd8e99912a6 100644 --- a/java/test/org/openqa/selenium/environment/webserver/Page.java +++ b/java/test/org/openqa/selenium/environment/webserver/Page.java @@ -17,14 +17,16 @@ package org.openqa.selenium.environment.webserver; +import org.jspecify.annotations.Nullable; + public class Page { private String title = ""; private String[] scripts = {}; private String[] styles = {}; private String[] bodyParts = {}; - private String onLoad; - private String onBeforeUnload; + private @Nullable String onLoad; + private @Nullable String onBeforeUnload; public Page withTitle(String title) { this.title = title; diff --git a/java/test/org/openqa/selenium/environment/webserver/SleepingHandler.java b/java/test/org/openqa/selenium/environment/webserver/SleepingHandler.java index a76645ce07b85..97292efd5985e 100644 --- a/java/test/org/openqa/selenium/environment/webserver/SleepingHandler.java +++ b/java/test/org/openqa/selenium/environment/webserver/SleepingHandler.java @@ -17,6 +17,7 @@ package org.openqa.selenium.environment.webserver; +import static java.util.Objects.requireNonNull; import static org.openqa.selenium.remote.http.Contents.utf8String; import java.io.UncheckedIOException; @@ -31,7 +32,8 @@ public class SleepingHandler implements HttpHandler { @Override public HttpResponse execute(HttpRequest req) throws UncheckedIOException { - String duration = req.getQueryParameter("time"); + String duration = + requireNonNull(req.getQueryParameter("time"), "Required query parameter 'time' is missing"); long timeout = Long.parseLong(duration) * 1000; reallySleep(timeout); diff --git a/java/test/org/openqa/selenium/environment/webserver/package-info.java b/java/test/org/openqa/selenium/environment/webserver/package-info.java new file mode 100644 index 0000000000000..be0c2c0b41842 --- /dev/null +++ b/java/test/org/openqa/selenium/environment/webserver/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.environment.webserver; + +import org.jspecify.annotations.NullMarked; diff --git a/java/test/org/openqa/selenium/federatedcredentialmanagement/BUILD.bazel b/java/test/org/openqa/selenium/federatedcredentialmanagement/BUILD.bazel index f422fd230dc17..2812a7ecbc033 100644 --- a/java/test/org/openqa/selenium/federatedcredentialmanagement/BUILD.bazel +++ b/java/test/org/openqa/selenium/federatedcredentialmanagement/BUILD.bazel @@ -15,5 +15,6 @@ java_selenium_test_suite( "//java/test/org/openqa/selenium/testing/drivers", artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/firefox/BUILD.bazel b/java/test/org/openqa/selenium/firefox/BUILD.bazel index 7d29fa850c18e..d0ac609213bda 100644 --- a/java/test/org/openqa/selenium/firefox/BUILD.bazel +++ b/java/test/org/openqa/selenium/firefox/BUILD.bazel @@ -39,6 +39,7 @@ java_test_suite( artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), artifact("org.mockito:mockito-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) @@ -78,6 +79,7 @@ java_selenium_test_suite( artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), artifact("org.mockito:mockito-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) @@ -108,6 +110,7 @@ java_selenium_test_suite( "//java/test/org/openqa/selenium/testing/drivers", artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/grid/distributor/local/LocalDistributorTest.java b/java/test/org/openqa/selenium/grid/distributor/local/LocalDistributorTest.java index 22253bdbf1cb6..67357220380cd 100644 --- a/java/test/org/openqa/selenium/grid/distributor/local/LocalDistributorTest.java +++ b/java/test/org/openqa/selenium/grid/distributor/local/LocalDistributorTest.java @@ -44,6 +44,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.openqa.selenium.Capabilities; @@ -293,7 +294,7 @@ public HttpResponse execute(HttpRequest req) { Map.of(), Map.of()); - List> callables = new ArrayList<>(); + List> callables = new ArrayList<>(); for (int i = 0; i < 3; i++) { callables.add( () -> { diff --git a/java/test/org/openqa/selenium/grid/router/HandleSessionReadTimeoutTest.java b/java/test/org/openqa/selenium/grid/router/HandleSessionReadTimeoutTest.java index 56d68e1269665..aa4d0950d058b 100644 --- a/java/test/org/openqa/selenium/grid/router/HandleSessionReadTimeoutTest.java +++ b/java/test/org/openqa/selenium/grid/router/HandleSessionReadTimeoutTest.java @@ -26,6 +26,7 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; @@ -105,7 +106,7 @@ void longerPageLoadTimeout_overridesNodeSessionTimeout() throws Exception { // The Node reports sessionTimeout = 5 min (300 s) via /se/grid/node/status. long nodeSessionTimeoutMs = 300_000L; - AtomicReference capturedTimeout = new AtomicReference<>(); + AtomicReference<@Nullable Duration> capturedTimeout = new AtomicReference<>(); HttpClient.Factory factory = config -> { Duration rt = config.readTimeout(); @@ -140,7 +141,7 @@ void longerNodeTimeout_usedAsFloor() throws Exception { // Node reports a 10-min (600 s) session timeout — operator has extended it. long nodeSessionTimeoutMs = 600_000L; - AtomicReference capturedTimeout = new AtomicReference<>(); + AtomicReference<@Nullable Duration> capturedTimeout = new AtomicReference<>(); HttpClient.Factory factory = config -> { Duration rt = config.readTimeout(); @@ -174,7 +175,7 @@ void noPageLoadCapability_nodeTimeoutUsedAsFloor() throws Exception { // Node reports a 10-min (600 s) session timeout. long nodeSessionTimeoutMs = 600_000L; - AtomicReference capturedTimeout = new AtomicReference<>(); + AtomicReference<@Nullable Duration> capturedTimeout = new AtomicReference<>(); HttpClient.Factory factory = config -> { Duration rt = config.readTimeout(); @@ -199,8 +200,7 @@ void noPageLoadCapability_nodeTimeoutUsedAsFloor() throws Exception { /** * Registers a session in a local SessionMap and executes one GET command through HandleSession. */ - private void runSingleRequest(HttpClient.Factory factory, URI nodeUri, Capabilities caps) - throws Exception { + private void runSingleRequest(HttpClient.Factory factory, URI nodeUri, Capabilities caps) { Tracer tracer = DefaultTestTracer.createTracer(); LocalSessionMap sessions = new LocalSessionMap(tracer, new GuavaEventBus()); diff --git a/java/test/org/openqa/selenium/grid/router/RemoteWebDriverDownloadTest.java b/java/test/org/openqa/selenium/grid/router/RemoteWebDriverDownloadTest.java index 2f15652e6b62c..1d8309a7ddc23 100644 --- a/java/test/org/openqa/selenium/grid/router/RemoteWebDriverDownloadTest.java +++ b/java/test/org/openqa/selenium/grid/router/RemoteWebDriverDownloadTest.java @@ -133,6 +133,7 @@ void canDownloadFiles( localDriver = createWebdriver(capabilities, mode); localDriver.get(appServer.whereIs("downloads/download.html")); + assertThat(localDriver.findElement(selector).isDisplayed()).isTrue(); localDriver.findElement(selector).click(); waitForDownloadedFiles(localDriver, 1); diff --git a/java/test/org/openqa/selenium/grid/router/TunnelWebsocketTest.java b/java/test/org/openqa/selenium/grid/router/TunnelWebsocketTest.java index 625f78704c165..19dc399f8b893 100644 --- a/java/test/org/openqa/selenium/grid/router/TunnelWebsocketTest.java +++ b/java/test/org/openqa/selenium/grid/router/TunnelWebsocketTest.java @@ -36,6 +36,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -119,7 +121,7 @@ private Function> createResolver() { } private Server createEchoBackend( - String response, CountDownLatch receivedLatch, AtomicReference received) { + String response, CountDownLatch receivedLatch, AtomicReference<@Nullable String> received) { return new NettyServer( new BaseServerOptions(emptyConfig), nullHandler, @@ -139,7 +141,7 @@ private Server createEchoBackend( @Test void shouldForwardTextMessageToBackend() throws URISyntaxException, InterruptedException { - AtomicReference received = new AtomicReference<>(); + AtomicReference<@Nullable String> received = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); backendServer = createEchoBackend("", latch, received); @@ -199,7 +201,7 @@ void shouldForwardTextMessageFromBackendToClient() HttpClient.Factory factory = HttpClient.Factory.createDefault(); CountDownLatch latch = new CountDownLatch(1); - AtomicReference reply = new AtomicReference<>(); + AtomicReference<@Nullable String> reply = new AtomicReference<>(); try (WebSocket socket = factory @@ -349,7 +351,7 @@ void shouldTunnelWebSocketThroughHttpsNode() throws URISyntaxException, Interrup // The tunnel handler detects the https scheme and adds a TLS handler on the node-side channel. MapConfig httpsConfig = new MapConfig(Map.of("server", Map.of("https-self-signed", true, "hostname", "localhost"))); - AtomicReference received = new AtomicReference<>(); + AtomicReference<@Nullable String> received = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); backendServer = @@ -467,9 +469,9 @@ void shouldSupportMultipleMessagesOnSameConnection() void shouldTunnelBiDiThroughFullGridSessionLifecycle() throws URISyntaxException, InterruptedException { // Stub backend — simulates a Node's BiDi WebSocket endpoint. Echoes a fixed reply. - AtomicReference received = new AtomicReference<>(); + AtomicReference<@Nullable String> received = new AtomicReference<>(); CountDownLatch receivedLatch = new CountDownLatch(1); - AtomicReference reply = new AtomicReference<>(); + AtomicReference<@Nullable String> reply = new AtomicReference<>(); CountDownLatch replyLatch = new CountDownLatch(1); backendServer = @@ -508,7 +510,7 @@ void shouldTunnelBiDiThroughFullGridSessionLifecycle() // routerUrl is set after tunnelServer starts so the TestSessionFactory can embed the Router's // WebSocket URL in the returned webSocketUrl capability (the real Grid does the same). - AtomicReference routerUrl = new AtomicReference<>(); + AtomicReference<@Nullable URL> routerUrl = new AtomicReference<>(); // TestSessionFactory: session URI → backendServer (so the TCP tunnel connects there). // The returned capabilities include webSocketUrl pointing to the Router's BiDi endpoint, @@ -654,7 +656,7 @@ private Server createProxyRouter() { @Test void proxyPath_shouldForwardTextMessageToBackend() throws URISyntaxException, InterruptedException { - AtomicReference received = new AtomicReference<>(); + AtomicReference<@Nullable String> received = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); backendServer = createEchoBackend("", latch, received); @@ -685,6 +687,7 @@ void proxyPath_shouldForwardTextMessageToBackend() } @Test + @NullMarked void proxyPath_shouldForwardReplyFromBackendToClient() throws URISyntaxException, InterruptedException { backendServer = createEchoBackend("proxy-pong", new CountDownLatch(1), new AtomicReference<>()); @@ -702,7 +705,7 @@ void proxyPath_shouldForwardReplyFromBackendToClient() HttpClient.Factory factory = HttpClient.Factory.createDefault(); CountDownLatch latch = new CountDownLatch(1); - AtomicReference reply = new AtomicReference<>(); + AtomicReference<@Nullable String> reply = new AtomicReference<>(); try (WebSocket socket = factory @@ -771,6 +774,7 @@ void proxyPath_shouldForwardBinaryMessages() throws URISyntaxException, Interrup } @Test + @NullMarked void proxyPath_shouldSupportMultipleMessagesAndBidirectionalFlow() throws URISyntaxException, InterruptedException { // Backend echoes every text message back with a ">" prefix to distinguish direction. @@ -835,7 +839,7 @@ public void onText(CharSequence data) { @Test void proxyPath_shouldRelayNodeInitiatedClose() throws URISyntaxException, InterruptedException { CountDownLatch closeLatch = new CountDownLatch(1); - AtomicReference closeCode = new AtomicReference<>(); + AtomicReference<@Nullable Integer> closeCode = new AtomicReference<>(); // Backend sends one text message, then immediately closes the connection. backendServer = diff --git a/java/test/org/openqa/selenium/grid/sessionmap/jdbc/BUILD.bazel b/java/test/org/openqa/selenium/grid/sessionmap/jdbc/BUILD.bazel index dd22c0893e7ff..8577646701bf9 100644 --- a/java/test/org/openqa/selenium/grid/sessionmap/jdbc/BUILD.bazel +++ b/java/test/org/openqa/selenium/grid/sessionmap/jdbc/BUILD.bazel @@ -15,5 +15,6 @@ java_test_suite( artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), artifact("org.hsqldb:hsqldb"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/grid/sessionmap/redis/BUILD.bazel b/java/test/org/openqa/selenium/grid/sessionmap/redis/BUILD.bazel index 1711f50939ec5..593b2775023b0 100644 --- a/java/test/org/openqa/selenium/grid/sessionmap/redis/BUILD.bazel +++ b/java/test/org/openqa/selenium/grid/sessionmap/redis/BUILD.bazel @@ -14,5 +14,6 @@ java_test_suite( artifact("it.ozimov:embedded-redis"), artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/grid/sessionqueue/local/BUILD.bazel b/java/test/org/openqa/selenium/grid/sessionqueue/local/BUILD.bazel index cbfcc4a54508a..ff32115c928fb 100644 --- a/java/test/org/openqa/selenium/grid/sessionqueue/local/BUILD.bazel +++ b/java/test/org/openqa/selenium/grid/sessionqueue/local/BUILD.bazel @@ -29,5 +29,6 @@ java_test_suite( artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.junit.jupiter:junit-jupiter-params"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/grid/testing/BUILD.bazel b/java/test/org/openqa/selenium/grid/testing/BUILD.bazel index 7bf6844f10cae..25ffd43693624 100644 --- a/java/test/org/openqa/selenium/grid/testing/BUILD.bazel +++ b/java/test/org/openqa/selenium/grid/testing/BUILD.bazel @@ -12,5 +12,6 @@ java_library( "//java/src/org/openqa/selenium/grid/node", "//java/src/org/openqa/selenium/remote", artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ], ) diff --git a/java/test/org/openqa/selenium/grid/testing/PassthroughHttpClient.java b/java/test/org/openqa/selenium/grid/testing/PassthroughHttpClient.java index d77faab75b4ca..f6d7be864d2eb 100644 --- a/java/test/org/openqa/selenium/grid/testing/PassthroughHttpClient.java +++ b/java/test/org/openqa/selenium/grid/testing/PassthroughHttpClient.java @@ -56,8 +56,7 @@ public java.util.concurrent.CompletableFuture> @Override public java.net.http.HttpResponse sendNative( - java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler handler) - throws java.io.IOException, InterruptedException { + java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler handler) { throw new UnsupportedOperationException("sendNative"); } diff --git a/java/test/org/openqa/selenium/grid/testing/package-info.java b/java/test/org/openqa/selenium/grid/testing/package-info.java new file mode 100644 index 0000000000000..e1d820db6ecf1 --- /dev/null +++ b/java/test/org/openqa/selenium/grid/testing/package-info.java @@ -0,0 +1,21 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@NullMarked +package org.openqa.selenium.grid.testing; + +import org.jspecify.annotations.NullMarked; diff --git a/java/test/org/openqa/selenium/ie/BUILD.bazel b/java/test/org/openqa/selenium/ie/BUILD.bazel index e7ffccc2b9878..81c545e6c4bbf 100644 --- a/java/test/org/openqa/selenium/ie/BUILD.bazel +++ b/java/test/org/openqa/selenium/ie/BUILD.bazel @@ -41,5 +41,6 @@ java_selenium_test_suite( "//java/test/org/openqa/selenium/testing/drivers", artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/interactions/BUILD.bazel b/java/test/org/openqa/selenium/interactions/BUILD.bazel index 32571095fba45..4f69af51bc2d4 100644 --- a/java/test/org/openqa/selenium/interactions/BUILD.bazel +++ b/java/test/org/openqa/selenium/interactions/BUILD.bazel @@ -53,5 +53,6 @@ java_selenium_test_suite( artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), artifact("org.mockito:mockito-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/interactions/CombinedInputActionsTest.java b/java/test/org/openqa/selenium/interactions/CombinedInputActionsTest.java index 15346594217c0..7d69a9742c974 100644 --- a/java/test/org/openqa/selenium/interactions/CombinedInputActionsTest.java +++ b/java/test/org/openqa/selenium/interactions/CombinedInputActionsTest.java @@ -22,6 +22,7 @@ import static org.openqa.selenium.WaitingConditions.elementTextToEqual; import static org.openqa.selenium.WaitingConditions.elementValueToEqual; import static org.openqa.selenium.WaitingConditions.windowHandleCountToBe; +import static org.openqa.selenium.support.ui.ExpectedConditions.elementToBeClickable; import static org.openqa.selenium.support.ui.ExpectedConditions.presenceOfElementLocated; import static org.openqa.selenium.support.ui.ExpectedConditions.titleIs; import static org.openqa.selenium.support.ui.ExpectedConditions.visibilityOf; @@ -246,7 +247,7 @@ void canMoveMouseToAnElementInAnIframeAndClick() { wait.until(presenceOfElementLocated(By.id("ifr"))); driver.switchTo().frame("ifr"); - WebElement link = wait.until(presenceOfElementLocated(By.id("link"))); + WebElement link = wait.until(elementToBeClickable(By.id("link"))); new Actions(driver).moveToElement(link).click().perform(); diff --git a/java/test/org/openqa/selenium/javascript/BUILD.bazel b/java/test/org/openqa/selenium/javascript/BUILD.bazel index ff4b12f6bc24d..40f477894d9b5 100644 --- a/java/test/org/openqa/selenium/javascript/BUILD.bazel +++ b/java/test/org/openqa/selenium/javascript/BUILD.bazel @@ -15,5 +15,6 @@ java_library( artifact("com.google.guava:guava"), artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/logging/BUILD.bazel b/java/test/org/openqa/selenium/logging/BUILD.bazel index aeeee6ec316d5..ca046b0eba537 100644 --- a/java/test/org/openqa/selenium/logging/BUILD.bazel +++ b/java/test/org/openqa/selenium/logging/BUILD.bazel @@ -33,5 +33,6 @@ java_selenium_test_suite( "//java/test/org/openqa/selenium/testing/drivers", artifact("org.assertj:assertj-core"), artifact("org.junit.jupiter:junit-jupiter-api"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/net/BUILD.bazel b/java/test/org/openqa/selenium/net/BUILD.bazel index 3a968fd8c59c1..a5d45ea423f51 100644 --- a/java/test/org/openqa/selenium/net/BUILD.bazel +++ b/java/test/org/openqa/selenium/net/BUILD.bazel @@ -26,5 +26,6 @@ java_test_suite( "//java/test/org/openqa/selenium/testing:test-base", artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/netty/server/BUILD.bazel b/java/test/org/openqa/selenium/netty/server/BUILD.bazel index 29362519a0ebe..d6bfcefcad06a 100644 --- a/java/test/org/openqa/selenium/netty/server/BUILD.bazel +++ b/java/test/org/openqa/selenium/netty/server/BUILD.bazel @@ -15,6 +15,7 @@ java_test_suite( artifact("io.netty:netty-transport"), artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) @@ -38,6 +39,7 @@ java_test_suite( artifact("io.netty:netty-transport"), artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/netty/server/WebSocketServingTest.java b/java/test/org/openqa/selenium/netty/server/WebSocketServingTest.java index d204cb943026e..c4643d4cf19d3 100644 --- a/java/test/org/openqa/selenium/netty/server/WebSocketServingTest.java +++ b/java/test/org/openqa/selenium/netty/server/WebSocketServingTest.java @@ -17,7 +17,6 @@ package org.openqa.selenium.netty.server; -import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -29,11 +28,10 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiFunction; import java.util.function.Consumer; +import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.openqa.selenium.grid.config.MapConfig; @@ -76,6 +74,7 @@ void clientShouldThrowAnExceptionIfUnableToConnectToAWebSocketEndPoint() { } @Test + @NullMarked void shouldUseUriToChooseWhichWebSocketHandlerToUse() throws InterruptedException { AtomicBoolean foo = new AtomicBoolean(false); AtomicBoolean bar = new AtomicBoolean(false); @@ -159,6 +158,7 @@ void shouldPropagateCloseMessage() throws InterruptedException { } @Test + @NullMarked void webSocketHandlersShouldBeAbleToFireMoreThanOneMessage() { server = new NettyServer( @@ -189,49 +189,6 @@ public void onText(CharSequence data) { new FluentWait<>(messages).until(msgs -> msgs.size() == 2); } - public void serverShouldBeAbleToPushAMessageWithoutNeedingTheClientToSendAMessage() - throws InterruptedException { - class MyHandler implements Consumer { - - private final Consumer sink; - private final ScheduledExecutorService executor = - Executors.newSingleThreadScheduledExecutor(); - - public MyHandler(Consumer sink) { - this.sink = sink; - - // Send a message every 250ms - executor.scheduleAtFixedRate( - () -> sink.accept(new TextMessage("Calling home.")), 100, 250, MILLISECONDS); - } - - @Override - public void accept(Message message) { - // Do nothing - } - } - - server = - new NettyServer( - defaultOptions(), - req -> new HttpResponse(), - (uri, sink) -> Optional.of(new MyHandler(sink))) - .start(); - - CountDownLatch latch = new CountDownLatch(2); - HttpClient client = HttpClient.Factory.createDefault().createClient(server.getUrl()); - client.openSocket( - new HttpRequest(GET, "/pushit"), - new WebSocket.Listener() { - @Override - public void onText(CharSequence data) { - latch.countDown(); - } - }); - - latch.await(2, SECONDS); - } - private BaseServerOptions defaultOptions() { return new BaseServerOptions( new MapConfig(Map.of("server", Map.of("port", PortProber.findFreePort())))); diff --git a/java/test/org/openqa/selenium/os/BUILD.bazel b/java/test/org/openqa/selenium/os/BUILD.bazel index 6b7523a2f2262..d85fdc11c22c4 100644 --- a/java/test/org/openqa/selenium/os/BUILD.bazel +++ b/java/test/org/openqa/selenium/os/BUILD.bazel @@ -16,6 +16,7 @@ java_test_suite( artifact("org.assertj:assertj-core"), artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.mockito:mockito-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/remote/AugmenterTest.java b/java/test/org/openqa/selenium/remote/AugmenterTest.java index e7543092ca39d..8c2d329a1fb9a 100644 --- a/java/test/org/openqa/selenium/remote/AugmenterTest.java +++ b/java/test/org/openqa/selenium/remote/AugmenterTest.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.openqa.selenium.By; @@ -47,6 +48,7 @@ import org.openqa.selenium.support.events.WebDriverListener; @Tag("UnitTests") +@NullMarked class AugmenterTest { private Augmenter getAugmenter() { @@ -281,6 +283,7 @@ public interface FindByMagic { WebElement findByMagic(String magicWord); } + @NullMarked protected static class StubExecutor implements CommandExecutor { private final Capabilities capabilities; @@ -353,6 +356,7 @@ public Capabilities getCapabilities() { return caps; } + @NullMarked @Override public WebElement findElement(By locator) { if (locator instanceof By.Remotable) { @@ -360,7 +364,7 @@ public WebElement findElement(By locator) { throw new NoSuchElementException("Boom"); } } - return null; + throw new NoSuchElementException("Element not found by " + locator); } } @@ -392,6 +396,7 @@ public Capabilities getCapabilities() { } } + @NullMarked private static class ModifyTitleWebDriverDecorator extends WebDriverDecorator { @Override diff --git a/java/test/org/openqa/selenium/remote/FakeWebDriverInfo.java b/java/test/org/openqa/selenium/remote/FakeWebDriverInfo.java index 8ecb6b77ad3de..7758de98b7839 100644 --- a/java/test/org/openqa/selenium/remote/FakeWebDriverInfo.java +++ b/java/test/org/openqa/selenium/remote/FakeWebDriverInfo.java @@ -21,6 +21,7 @@ import com.google.auto.service.AutoService; import java.util.Optional; +import org.jspecify.annotations.NullMarked; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; import org.openqa.selenium.SessionNotCreatedException; @@ -28,6 +29,7 @@ import org.openqa.selenium.remote.http.ClientConfig; @AutoService(WebDriverInfo.class) +@NullMarked public class FakeWebDriverInfo implements WebDriverInfo { static final String FAKE_BROWSER = "selenium-test"; diff --git a/java/test/org/openqa/selenium/remote/ProtocolHandshakeTest.java b/java/test/org/openqa/selenium/remote/ProtocolHandshakeTest.java index 36371660d2429..e1d6e2899b94b 100644 --- a/java/test/org/openqa/selenium/remote/ProtocolHandshakeTest.java +++ b/java/test/org/openqa/selenium/remote/ProtocolHandshakeTest.java @@ -22,10 +22,10 @@ import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.openqa.selenium.remote.http.Contents.string; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.openqa.selenium.remote.http.Contents.utf8String; import java.io.IOException; @@ -35,6 +35,8 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.openqa.selenium.Capabilities; @@ -47,6 +49,7 @@ @SuppressWarnings("unchecked") @Tag("UnitTests") +@NullMarked class ProtocolHandshakeTest { @Test @@ -55,11 +58,7 @@ void requestShouldIncludeSpecCompliantW3CCapabilities() throws IOException { singletonMap("capabilities", singleton(new ImmutableCapabilities())); Command command = new Command(null, DriverCommand.NEW_SESSION, params); - HttpResponse response = new HttpResponse(); - response.setStatus(HTTP_OK); - response.setContent( - utf8String("{\"value\": {\"sessionId\": \"23456789\", \"capabilities\": {}}}")); - RecordingHttpClient client = new RecordingHttpClient(response); + RecordingHttpClient client = createClient(); new ProtocolHandshake().createSession(client, command); @@ -76,11 +75,7 @@ void shouldParseW3CNewSessionResponse() throws IOException { singletonMap("capabilities", singleton(new ImmutableCapabilities())); Command command = new Command(null, DriverCommand.NEW_SESSION, params); - HttpResponse response = new HttpResponse(); - response.setStatus(HTTP_OK); - response.setContent( - utf8String("{\"value\": {\"sessionId\": \"23456789\", \"capabilities\": {}}}")); - RecordingHttpClient client = new RecordingHttpClient(response); + RecordingHttpClient client = createClient(); ProtocolHandshake.Result result = new ProtocolHandshake().createSession(client, command); assertThat(result.getDialect()).isEqualTo(Dialect.W3C); @@ -96,11 +91,7 @@ void shouldNotIncludeNonProtocolExtensionKeys() throws IOException { Map params = singletonMap("capabilities", singleton(caps)); Command command = new Command(null, DriverCommand.NEW_SESSION, params); - HttpResponse response = new HttpResponse(); - response.setStatus(HTTP_OK); - response.setContent( - utf8String("{\"value\": {\"sessionId\": \"23456789\", \"capabilities\": {}}}")); - RecordingHttpClient client = new RecordingHttpClient(response); + RecordingHttpClient client = createClient(); new ProtocolHandshake().createSession(client, command); @@ -125,7 +116,7 @@ void shouldNotIncludeNonProtocolExtensionKeys() throws IOException { } @Test - void doesNotCreateFirstMatchForNonW3CCaps() throws IOException { + void doesNotCreateFirstMatchForNonW3CCaps() { Capabilities caps = new ImmutableCapabilities( "cheese", EMPTY_MAP, @@ -136,10 +127,12 @@ void doesNotCreateFirstMatchForNonW3CCaps() throws IOException { Command command = new Command(null, DriverCommand.NEW_SESSION, params); ProtocolHandshake handshake = new ProtocolHandshake(); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> handshake.createSession(null, command)); + assertThatThrownBy(() -> handshake.createSession(createClient(), command)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Illegal key values seen in w3c capabilities: [cheese]"); } + @Nullable private List> mergeW3C(Map caps) { Map capabilities = (Map) caps.get("capabilities"); if (capabilities == null) { @@ -163,13 +156,13 @@ private List> mergeW3C(Map caps) { } private Map getRequestPayloadAsMap(RecordingHttpClient client) { - return new Json().toType(client.getRequestPayload(), Map.class); + return new Json().toType(requireNonNull(client.getRequestPayload()), Map.class); } - class RecordingHttpClient implements HttpClient { + private static class RecordingHttpClient implements HttpClient { private final HttpResponse response; - private String payload; + private @Nullable String payload; RecordingHttpClient(HttpResponse response) { this.response = response; @@ -177,11 +170,11 @@ class RecordingHttpClient implements HttpClient { @Override public HttpResponse execute(HttpRequest request) { - payload = string(request); + payload = request.contentAsString(); return response; } - String getRequestPayload() { + @Nullable String getRequestPayload() { return payload; } @@ -199,9 +192,16 @@ java.util.concurrent.CompletableFuture> sendAsyncN @Override public java.net.http.HttpResponse sendNative( - java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler handler) - throws java.io.IOException, InterruptedException { + java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler handler) { throw new UnsupportedOperationException("sendNative"); } } + + private static RecordingHttpClient createClient() { + HttpResponse response = new HttpResponse(); + response.setStatus(HTTP_OK); + response.setContent( + utf8String("{\"value\": {\"sessionId\": \"23456789\", \"capabilities\": {}}}")); + return new RecordingHttpClient(response); + } } diff --git a/java/test/org/openqa/selenium/remote/RemoteWebDriverBuilderTest.java b/java/test/org/openqa/selenium/remote/RemoteWebDriverBuilderTest.java index dbfd0395fcfde..f01a601f7ffa4 100644 --- a/java/test/org/openqa/selenium/remote/RemoteWebDriverBuilderTest.java +++ b/java/test/org/openqa/selenium/remote/RemoteWebDriverBuilderTest.java @@ -42,6 +42,8 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.openqa.selenium.Capabilities; @@ -238,6 +240,7 @@ void ifARemoteUrlIsGivenThatIsUsedForTheSession() { } @Test + @NullMarked void shouldUseGivenDriverServiceForUrlIfProvided() throws IOException { URI uri = URI.create("http://localhost:9898"); URL url = uri.toURL(); @@ -250,7 +253,7 @@ public URL getUrl() { } }; - AtomicReference seen = new AtomicReference<>(); + AtomicReference<@Nullable URI> seen = new AtomicReference<>(); RemoteWebDriver.builder() .oneOf(new FirefoxOptions()) .withDriverService(service) @@ -425,6 +428,7 @@ void shouldAugmentDriverIfPossible() { } @Test + @NullMarked void shouldAugmentDriverWhenUsingDriverService() throws IOException { URI uri = URI.create("http://localhost:9898"); URL url = uri.toURL(); @@ -467,6 +471,7 @@ public URL getUrl() { } @Test + @NullMarked void shouldAugmentWithDevToolsWhenUsingDriverService() throws IOException { URI uri = URI.create("http://localhost:9898"); URL url = uri.toURL(); @@ -521,6 +526,7 @@ private List listCapabilities(HttpRequest request) { .collect(Collectors.toList()); } + @NullMarked static class FakeDriverService extends DriverService { private boolean started; @@ -542,6 +548,16 @@ public boolean isRunning() { protected void waitUntilAvailable() { // return immediately } + + @Override + public String getDriverProperty() { + return ""; + } + + @Override + protected String getDriverEnvironmentVariable() { + return ""; + } } private Function on(Consumer callback) { diff --git a/java/test/org/openqa/selenium/remote/http/BUILD.bazel b/java/test/org/openqa/selenium/remote/http/BUILD.bazel index a3dec01d6abd2..744750e82d177 100644 --- a/java/test/org/openqa/selenium/remote/http/BUILD.bazel +++ b/java/test/org/openqa/selenium/remote/http/BUILD.bazel @@ -23,5 +23,6 @@ java_test_suite( artifact("org.assertj:assertj-core"), artifact("com.google.guava:guava"), artifact("org.junit.jupiter:junit-jupiter-api"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/remote/http/HttpClientFactoryTest.java b/java/test/org/openqa/selenium/remote/http/HttpClientFactoryTest.java index 961c3bf92fdb7..b4d07878dccdc 100644 --- a/java/test/org/openqa/selenium/remote/http/HttpClientFactoryTest.java +++ b/java/test/org/openqa/selenium/remote/http/HttpClientFactoryTest.java @@ -21,6 +21,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import com.google.auto.service.AutoService; +import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; @@ -68,10 +69,11 @@ void canCreateCustomClientFactoryByName() { @AutoService(HttpClient.Factory.class) @HttpClientName("cheesy") @SuppressWarnings("unused") + @NullMarked public static class CheesyFactory implements HttpClient.Factory { @Override public HttpClient createClient(ClientConfig config) { - return null; + throw new UnsupportedOperationException("No cheese, sorry"); } } @@ -96,21 +98,23 @@ void canDetectHttpClientFactoriesWithSameName() { @AutoService(HttpClient.Factory.class) @HttpClientName("duplicated") + @NullMarked @SuppressWarnings("unused") public static class Factory1 implements HttpClient.Factory { @Override public HttpClient createClient(ClientConfig config) { - return null; + throw new UnsupportedOperationException(); } } @AutoService(HttpClient.Factory.class) @HttpClientName("duplicated") + @NullMarked @SuppressWarnings("unused") public static class Factory2 implements HttpClient.Factory { @Override public HttpClient createClient(ClientConfig config) { - return null; + throw new UnsupportedOperationException(); } } } diff --git a/java/test/org/openqa/selenium/remote/http/HttpRequestTest.java b/java/test/org/openqa/selenium/remote/http/HttpRequestTest.java new file mode 100644 index 0000000000000..c1d6be6881f50 --- /dev/null +++ b/java/test/org/openqa/selenium/remote/http/HttpRequestTest.java @@ -0,0 +1,43 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.remote.http; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.openqa.selenium.remote.http.HttpMethod.GET; + +import org.junit.jupiter.api.Test; + +class HttpRequestTest { + @Test + void getQueryString() { + HttpRequest request = new HttpRequest(GET, "http://localhost/"); + request.addQueryParameter("user", "john"); + request.addQueryParameter("user", "smith"); + request.addQueryParameter("user", "bob"); + request.addQueryParameter("password", "secret"); + + assertThat(request.getQueryString()).isEqualTo("user=john&user=smith&user=bob&password=secret"); + } + + @Test + void getQueryString_noQueryParameters() { + HttpRequest request = new HttpRequest(GET, "http://localhost/"); + + assertThat(request.getQueryString()).isEqualTo(""); + } +} diff --git a/java/test/org/openqa/selenium/remote/http/NativeHttpClientMethodsTest.java b/java/test/org/openqa/selenium/remote/http/NativeHttpClientMethodsTest.java index 9defc7aa51e74..a82ea2a3d4877 100644 --- a/java/test/org/openqa/selenium/remote/http/NativeHttpClientMethodsTest.java +++ b/java/test/org/openqa/selenium/remote/http/NativeHttpClientMethodsTest.java @@ -28,6 +28,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -166,6 +167,7 @@ void testBodyHandlers_variations() { * responses with predictable data - UnsupportedOperationException for methods not relevant to * native API testing */ + @NullMarked private static class TestHttpClient implements HttpClient { private final boolean shouldFail; diff --git a/java/test/org/openqa/selenium/remote/http/jdk/PasswordAuthenticatorTest.java b/java/test/org/openqa/selenium/remote/http/jdk/PasswordAuthenticatorTest.java new file mode 100644 index 0000000000000..0028ea9c9ad8c --- /dev/null +++ b/java/test/org/openqa/selenium/remote/http/jdk/PasswordAuthenticatorTest.java @@ -0,0 +1,41 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.remote.http.jdk; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.PasswordAuthentication; +import org.junit.jupiter.api.Test; + +class PasswordAuthenticatorTest { + @Test + void usernameAndPassword() { + PasswordAuthentication authentication = + new PasswordAuthenticator("bob:smith").getPasswordAuthentication(); + assertThat(authentication.getUserName()).isEqualTo("bob"); + assertThat(new String(authentication.getPassword())).isEqualTo("smith"); + } + + @Test + void usernameWithoutPassword() { + PasswordAuthentication authentication = + new PasswordAuthenticator("bob").getPasswordAuthentication(); + assertThat(authentication.getUserName()).isEqualTo("bob"); + assertThat(new String(authentication.getPassword())).isEmpty(); + } +} diff --git a/java/test/org/openqa/selenium/remote/internal/BUILD.bazel b/java/test/org/openqa/selenium/remote/internal/BUILD.bazel index 123fbce3e36a0..b8d24cf1183e3 100644 --- a/java/test/org/openqa/selenium/remote/internal/BUILD.bazel +++ b/java/test/org/openqa/selenium/remote/internal/BUILD.bazel @@ -24,6 +24,7 @@ java_library( artifact("com.google.guava:guava"), artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), artifact("io.netty:netty-buffer"), artifact("io.netty:netty-codec-http"), artifact("io.netty:netty-handler"), diff --git a/java/test/org/openqa/selenium/remote/internal/HttpClientTestBase.java b/java/test/org/openqa/selenium/remote/internal/HttpClientTestBase.java index 72a4e7cc14db6..a452d32254f13 100644 --- a/java/test/org/openqa/selenium/remote/internal/HttpClientTestBase.java +++ b/java/test/org/openqa/selenium/remote/internal/HttpClientTestBase.java @@ -30,7 +30,6 @@ import java.time.Duration; import java.util.List; import java.util.Map; -import java.util.TreeMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -335,11 +334,7 @@ private HttpResponse getQueryParameterResponse(HttpRequest request) { return executeWithinServer( request, req -> { - Map> params = new TreeMap<>(); - req.getQueryParameterNames() - .forEach(name -> params.put(name, req.getQueryParameters(name))); - - return new HttpResponse().setContent(Contents.asJson(params)); + return new HttpResponse().setContent(Contents.asJson(req.getQueryParameters())); }); } diff --git a/java/test/org/openqa/selenium/remote/internal/WebSocketTestBase.java b/java/test/org/openqa/selenium/remote/internal/WebSocketTestBase.java index a00bff979af7e..37e8162ec210b 100644 --- a/java/test/org/openqa/selenium/remote/internal/WebSocketTestBase.java +++ b/java/test/org/openqa/selenium/remote/internal/WebSocketTestBase.java @@ -26,6 +26,7 @@ import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; +import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -100,6 +101,7 @@ void shouldBeAbleToSendATextMessage() throws InterruptedException { WebSocket.Listener listener = new WebSocket.Listener() { @Override + @NullMarked public void onText(CharSequence data) { message.set(data.toString()); latch.countDown(); @@ -122,6 +124,7 @@ void shouldBeAbleToSendABinaryMessage() throws InterruptedException { WebSocket.Listener listener = new WebSocket.Listener() { @Override + @NullMarked public void onBinary(byte[] data) { message.set(data); latch.countDown(); diff --git a/java/test/org/openqa/selenium/safari/BUILD.bazel b/java/test/org/openqa/selenium/safari/BUILD.bazel index 209d3a858ea38..922fe72480a33 100644 --- a/java/test/org/openqa/selenium/safari/BUILD.bazel +++ b/java/test/org/openqa/selenium/safari/BUILD.bazel @@ -44,5 +44,6 @@ java_selenium_test_suite( artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), artifact("org.mockito:mockito-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/support/decorators/BUILD.bazel b/java/test/org/openqa/selenium/support/decorators/BUILD.bazel index 5e7b9be8b7fc6..fb44fd2dafb4d 100644 --- a/java/test/org/openqa/selenium/support/decorators/BUILD.bazel +++ b/java/test/org/openqa/selenium/support/decorators/BUILD.bazel @@ -19,5 +19,6 @@ java_test_suite( artifact("com.google.guava:guava"), artifact("org.assertj:assertj-core"), artifact("org.mockito:mockito-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/support/decorators/IntegrationTest.java b/java/test/org/openqa/selenium/support/decorators/IntegrationTest.java index a52aa933b0436..26e59301b3232 100644 --- a/java/test/org/openqa/selenium/support/decorators/IntegrationTest.java +++ b/java/test/org/openqa/selenium/support/decorators/IntegrationTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.when; import java.lang.reflect.Method; +import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.openqa.selenium.By; @@ -33,6 +34,7 @@ @Tag("UnitTests") class IntegrationTest { + @NullMarked static class CountCalls extends WebDriverDecorator { int counterBefore = 0; diff --git a/java/test/org/openqa/selenium/support/events/BUILD.bazel b/java/test/org/openqa/selenium/support/events/BUILD.bazel index fbbda0428eb6a..d5e26ed1024d5 100644 --- a/java/test/org/openqa/selenium/support/events/BUILD.bazel +++ b/java/test/org/openqa/selenium/support/events/BUILD.bazel @@ -15,5 +15,6 @@ java_test_suite( artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), artifact("org.mockito:mockito-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/support/events/EventFiringDecoratorTest.java b/java/test/org/openqa/selenium/support/events/EventFiringDecoratorTest.java index e29496f112a47..5ed19f4c2be8c 100644 --- a/java/test/org/openqa/selenium/support/events/EventFiringDecoratorTest.java +++ b/java/test/org/openqa/selenium/support/events/EventFiringDecoratorTest.java @@ -36,6 +36,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.openqa.selenium.Alert; @@ -56,6 +57,7 @@ import org.openqa.selenium.remote.RemoteWebDriver; @Tag("UnitTests") +@NullMarked class EventFiringDecoratorTest { static class CollectorListener implements WebDriverListener { @@ -1337,6 +1339,7 @@ public void onError( } @Test + @NullMarked public void ensureListenersAreInvokedWhenUsingDecoratedSubClasses() { RemoteWebDriver originalDriver = mock(RemoteWebDriver.class); doNothing().when(originalDriver).get(any()); diff --git a/java/test/org/openqa/selenium/support/locators/BUILD.bazel b/java/test/org/openqa/selenium/support/locators/BUILD.bazel index 6fa17d756423e..7d8de75b77811 100644 --- a/java/test/org/openqa/selenium/support/locators/BUILD.bazel +++ b/java/test/org/openqa/selenium/support/locators/BUILD.bazel @@ -22,5 +22,6 @@ java_selenium_test_suite( "//java/test/org/openqa/selenium/testing:test-base", artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/support/pagefactory/AnnotationsTest.java b/java/test/org/openqa/selenium/support/pagefactory/AnnotationsTest.java index 601987066f10d..8ea232be360f2 100644 --- a/java/test/org/openqa/selenium/support/pagefactory/AnnotationsTest.java +++ b/java/test/org/openqa/selenium/support/pagefactory/AnnotationsTest.java @@ -17,6 +17,7 @@ package org.openqa.selenium.support.pagefactory; +import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -26,6 +27,7 @@ import java.lang.annotation.Target; import java.lang.reflect.Field; import java.util.List; +import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.openqa.selenium.By; @@ -95,14 +97,15 @@ class AnnotationsTest { @PageFactoryFinder(FindByXXXX.FindByXXXXBuilder.class) public @interface FindByXXXX { - class FindByXXXXBuilder extends AbstractFindByBuilder { + @NullMarked + class FindByXXXXBuilder extends AbstractFindByBuilder { @Override public By buildIt(Object annotation, Field field) { return new By() { @Override public List findElements(SearchContext context) { - return null; + return emptyList(); } @Override diff --git a/java/test/org/openqa/selenium/support/pagefactory/BUILD.bazel b/java/test/org/openqa/selenium/support/pagefactory/BUILD.bazel index f1a62b70fcebf..e8fc7cf65ebab 100644 --- a/java/test/org/openqa/selenium/support/pagefactory/BUILD.bazel +++ b/java/test/org/openqa/selenium/support/pagefactory/BUILD.bazel @@ -23,6 +23,7 @@ java_test_suite( artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), artifact("org.mockito:mockito-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) @@ -38,5 +39,6 @@ java_selenium_test_suite( artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), artifact("org.mockito:mockito-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/support/pagefactory/DefaultFieldDecoratorTest.java b/java/test/org/openqa/selenium/support/pagefactory/DefaultFieldDecoratorTest.java index 99d107ec8fb45..567bf80dcb1e4 100644 --- a/java/test/org/openqa/selenium/support/pagefactory/DefaultFieldDecoratorTest.java +++ b/java/test/org/openqa/selenium/support/pagefactory/DefaultFieldDecoratorTest.java @@ -22,9 +22,7 @@ import java.util.List; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; -import org.openqa.selenium.WrapsElement; import org.openqa.selenium.support.FindAll; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.FindBys; @@ -159,19 +157,4 @@ void doesNotDecorateNullLocator() throws Exception { assertThat(decorator.decorate(getClass().getClassLoader(), getClass().getDeclaredField("num"))) .isNull(); } - - private interface AllDriver extends WebDriver { - // Place holder - } - - private interface AllElement - extends WebElement, WrapsElement, org.openqa.selenium.interactions.Locatable { - // Place holder - } - - private static class Page { - - @FindBy(id = "foo") - public WebElement foo; - } } diff --git a/java/test/org/openqa/selenium/support/pagefactory/UsingPageFactoryTest.java b/java/test/org/openqa/selenium/support/pagefactory/UsingPageFactoryTest.java index 0954c3b331d6b..14fe1acd934c9 100644 --- a/java/test/org/openqa/selenium/support/pagefactory/UsingPageFactoryTest.java +++ b/java/test/org/openqa/selenium/support/pagefactory/UsingPageFactoryTest.java @@ -81,7 +81,7 @@ void testDecoratedElementsShouldBeUnwrapped() { assertThat(seen).isEqualTo(expected); } - class PublicPage { + static class PublicPage { public WebElement element; } diff --git a/java/test/org/openqa/selenium/testing/SeleniumExtension.java b/java/test/org/openqa/selenium/testing/SeleniumExtension.java index 4f9ce598b01f2..5c9ee64d74688 100644 --- a/java/test/org/openqa/selenium/testing/SeleniumExtension.java +++ b/java/test/org/openqa/selenium/testing/SeleniumExtension.java @@ -206,8 +206,6 @@ public void testSuccessful(ExtensionContext context) { public void testFailed(ExtensionContext context, Throwable cause) { TestWatcher.super.testFailed(context, cause); - logWebdriverDetails(context); - // ManageDriverRule.failed Browser current = Objects.requireNonNull(Browser.detect()); Optional testMethod = context.getTestMethod(); @@ -219,6 +217,8 @@ public void testFailed(ExtensionContext context, Throwable cause) { System.out.println("Restarting driver after failed test " + context.getDisplayName()); removeDriver(); } + } else { + logWebdriverDetails(context); } captureLoggingRule.endLogCapture(Level.ALL); diff --git a/java/test/org/openqa/selenium/testing/drivers/BUILD.bazel b/java/test/org/openqa/selenium/testing/drivers/BUILD.bazel index 8106f93669900..c9900fa4d18c4 100644 --- a/java/test/org/openqa/selenium/testing/drivers/BUILD.bazel +++ b/java/test/org/openqa/selenium/testing/drivers/BUILD.bazel @@ -56,6 +56,7 @@ java_library( "//java/test/org/openqa/selenium/build", artifact("com.google.guava:guava"), artifact("org.junit.jupiter:junit-jupiter-api"), + artifact("org.jspecify:jspecify"), "@bazel_tools//tools/java/runfiles", ] + JUNIT5_DEPS, ) diff --git a/java/test/org/openqa/selenium/testing/drivers/OutOfProcessSeleniumServer.java b/java/test/org/openqa/selenium/testing/drivers/OutOfProcessSeleniumServer.java index 6077aaaa9988c..32f35195141d8 100644 --- a/java/test/org/openqa/selenium/testing/drivers/OutOfProcessSeleniumServer.java +++ b/java/test/org/openqa/selenium/testing/drivers/OutOfProcessSeleniumServer.java @@ -34,6 +34,7 @@ import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.Platform; import org.openqa.selenium.chrome.ChromeDriverService; import org.openqa.selenium.edge.EdgeDriverService; @@ -48,8 +49,8 @@ class OutOfProcessSeleniumServer { private static final Logger LOG = Logger.getLogger(OutOfProcessSeleniumServer.class.getName()); - private String baseUrl; - private ExternalProcess process; + private @Nullable String baseUrl = null; + private @Nullable ExternalProcess process = null; @SuppressWarnings("unused") private boolean captureLogs = false; @@ -93,7 +94,7 @@ public OutOfProcessSeleniumServer start(String mode, String... extraFlags) { EdgeDriverService.createDefaultService(), ChromeDriverService.createDefaultService()) .map(DriverService::getDriverProperty) - .filter(Objects::nonNull) + .filter(prop -> !prop.isEmpty()) .map(System::getProperty) .anyMatch(Objects::nonNull); diff --git a/java/test/org/openqa/selenium/virtualauthenticator/BUILD.bazel b/java/test/org/openqa/selenium/virtualauthenticator/BUILD.bazel index 4d2199634574b..adc7872ebb57b 100644 --- a/java/test/org/openqa/selenium/virtualauthenticator/BUILD.bazel +++ b/java/test/org/openqa/selenium/virtualauthenticator/BUILD.bazel @@ -14,5 +14,6 @@ java_selenium_test_suite( "//java/test/org/openqa/selenium/testing/drivers", artifact("org.junit.jupiter:junit-jupiter-api"), artifact("org.assertj:assertj-core"), + artifact("org.jspecify:jspecify"), ] + JUNIT5_DEPS, )