commit efcb2ebdb391d0e260022d65a9019792ad455f51
parent 215da31d6b94511a7187f44120f3ba83dca0b386
Author: Sam Sneddon <gsnedders@apple.com>
Date: Mon, 10 Nov 2025 22:18:05 +0000
Bug 1976919 [wpt PR 53724] - Ensure WebDriver sessions end up in a valid state, a=testonly
Automatic update from web-platform-tests
Ensure WebDriver sessions end up in a valid state
At both fixture and client levels, handle exceptions by ensuring we've
ended the session we were just creating.
This has only been seen on the BiDi side, where we'd believe we had an
existing transport, having created it successfully, even if we didn't
manage to actually create the session itself.
But for the sake of resilience, make Classic match this too.
--
wpt-commits: 0526e63789009d18d275d9a33f62a238fe42e13c
wpt-pr: 53724
Diffstat:
3 files changed, 72 insertions(+), 47 deletions(-)
diff --git a/testing/web-platform/tests/tools/webdriver/webdriver/bidi/client.py b/testing/web-platform/tests/tools/webdriver/webdriver/bidi/client.py
@@ -156,11 +156,17 @@ class BidiSession:
loop: Optional[asyncio.AbstractEventLoop] = None) -> None:
"""Connect to the WebDriver BiDi remote via WebSockets"""
- await self.start_transport(loop)
+ try:
+ await self.start_transport(loop)
- if self.session_id is None:
- self.session_id, self.capabilities = await self.session.new( # type: ignore
- capabilities=self.requested_capabilities)
+ if self.session_id is None:
+ self.session_id, self.capabilities = await self.session.new( # type: ignore
+ capabilities=self.requested_capabilities)
+
+ except Exception:
+ # Make sure we end up back in a consistent state.
+ await self.end()
+ raise
def on_transport_closed(self):
for future in self.pending_commands.values():
diff --git a/testing/web-platform/tests/tools/webdriver/webdriver/client.py b/testing/web-platform/tests/tools/webdriver/webdriver/client.py
@@ -447,40 +447,44 @@ class Session:
if self.requested_capabilities is not None:
body["capabilities"] = self.requested_capabilities
- value = self.send_command("POST", "session", body=body)
- assert isinstance(value["sessionId"], str)
- assert isinstance(value["capabilities"], Dict)
+ try:
+ value = self.send_command("POST", "session", body=body)
+ assert isinstance(value["sessionId"], str)
+ assert isinstance(value["capabilities"], Dict)
- self.session_id = value["sessionId"]
- self.capabilities = value["capabilities"]
+ self.session_id = value["sessionId"]
+ self.capabilities = value["capabilities"]
- if "webSocketUrl" in self.capabilities:
- self.bidi_session = BidiSession.from_http(self.session_id,
- self.capabilities)
- elif self.enable_bidi:
- self.end()
- raise error.SessionNotCreatedException(
- "Requested bidi session, but webSocketUrl capability not found")
+ if "webSocketUrl" in self.capabilities:
+ self.bidi_session = BidiSession.from_http(self.session_id,
+ self.capabilities)
+ elif self.enable_bidi:
+ self.end()
+ raise error.SessionNotCreatedException(
+ "Requested bidi session, but webSocketUrl capability not found")
- if self.extension_cls:
- self.extension = self.extension_cls(self)
+ if self.extension_cls:
+ self.extension = self.extension_cls(self)
- return value
+ return value
+
+ except Exception:
+ # Make sure we end up back in a consistent state.
+ self.end()
+ raise
def end(self):
"""Try to close the active session."""
- if self.session_id is None:
- return
-
- if not isinstance(self.session_id, str):
- raise TypeError("Session.session_id must be a str or None")
-
try:
- self.send_command("DELETE", "session/%s" % self.session_id)
+ if self.session_id is not None:
+ self.send_command("DELETE", "session/%s" % self.session_id)
except (OSError, error.InvalidSessionIdException):
pass
finally:
self.session_id = None
+ self.capabilities = None
+ self.bidi_session = None
+ self.extension = None
self.transport.close()
def send_command(self, method, url, body=None, timeout=None):
diff --git a/testing/web-platform/tests/webdriver/tests/support/fixtures.py b/testing/web-platform/tests/webdriver/tests/support/fixtures.py
@@ -179,22 +179,28 @@ async def session(capabilities, configuration):
configuration["port"],
capabilities=caps)
- _current_session.start()
+ try:
+ _current_session.start()
- # Enforce a fixed default window size and position
- if _current_session.capabilities.get("setWindowRect"):
- _current_session.window.size = defaults.WINDOW_SIZE
- _current_session.window.position = defaults.WINDOW_POSITION
+ # Enforce a fixed default window size and position
+ if _current_session.capabilities.get("setWindowRect"):
+ _current_session.window.size = defaults.WINDOW_SIZE
+ _current_session.window.position = defaults.WINDOW_POSITION
- # Set default timeouts
- multiplier = configuration["timeout_multiplier"]
- _current_session.timeouts.implicit = IMPLICIT_WAIT_TIMEOUT * multiplier
- _current_session.timeouts.page_load = PAGE_LOAD_TIMEOUT * multiplier
- _current_session.timeouts.script = SCRIPT_TIMEOUT * multiplier
+ # Set default timeouts
+ multiplier = configuration["timeout_multiplier"]
+ _current_session.timeouts.implicit = IMPLICIT_WAIT_TIMEOUT * multiplier
+ _current_session.timeouts.page_load = PAGE_LOAD_TIMEOUT * multiplier
+ _current_session.timeouts.script = SCRIPT_TIMEOUT * multiplier
- yield _current_session
+ yield _current_session
- cleanup_session(_current_session)
+ cleanup_session(_current_session)
+
+ except Exception:
+ # Make sure we end up in a known state if something goes wrong.
+ _current_session.end()
+ raise
@pytest_asyncio.fixture(scope="function")
@@ -226,18 +232,27 @@ async def bidi_session(capabilities, configuration):
capabilities=caps,
enable_bidi=True)
- _current_session.start()
- await _current_session.bidi_session.start()
+ try:
+ _current_session.start()
+
+ try:
+ await _current_session.bidi_session.start()
- # Enforce a fixed default window size and position
- if _current_session.capabilities.get("setWindowRect"):
- _current_session.window.size = defaults.WINDOW_SIZE
- _current_session.window.position = defaults.WINDOW_POSITION
+ # Enforce a fixed default window size and position
+ if _current_session.capabilities.get("setWindowRect"):
+ _current_session.window.size = defaults.WINDOW_SIZE
+ _current_session.window.position = defaults.WINDOW_POSITION
- yield _current_session.bidi_session
+ yield _current_session.bidi_session
- await _current_session.bidi_session.end()
- cleanup_session(_current_session)
+ finally:
+ await _current_session.bidi_session.end()
+
+ cleanup_session(_current_session)
+
+ except Exception:
+ # Make sure we end up in a known state if something goes wrong.
+ _current_session.end()
@pytest.fixture(scope="function")