Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

import json
import tempfile
import time
from copy import deepcopy
from pathlib import Path
import pytest
from webdriver import error
def test_content_process(configuration, geckodriver):
def trigger_crash(driver):
# The crash is delayed and happens after this command finished.
driver.session.url = "about:crashcontent"
# Bug 1943038: geckodriver fails to detect minidump files for content
# crashes when the next command is sent immediately.
time.sleep(1)
# Send another command that should fail
with pytest.raises(error.UnknownErrorException):
driver.session.url
run_crash_test(configuration, geckodriver, crash_callback=trigger_crash)
def test_parent_process(configuration, geckodriver):
def trigger_crash(driver):
with pytest.raises(error.UnknownErrorException):
driver.session.url = "about:crashparent"
run_crash_test(configuration, geckodriver, crash_callback=trigger_crash)
def run_crash_test(configuration, geckodriver, crash_callback):
config = deepcopy(configuration)
config["capabilities"]["webSocketUrl"] = True
with tempfile.TemporaryDirectory() as tmpdirname:
# Use a custom temporary minidump save path to only see
# the minidump files related to this test
driver = geckodriver(
config=config, extra_env={"MINIDUMP_SAVE_PATH": tmpdirname}
)
driver.new_session()
profile_minidump_path = (
Path(driver.session.capabilities["moz:profile"]) / "minidumps"
)
crash_callback(driver)
tmp_minidump_dir = Path(tmpdirname)
file_map = verify_minidump_files(tmp_minidump_dir)
# Check that for both Marionette and Remote Agent the annotations are present
extra_data = read_extra_file(file_map[".extra"])
assert (
extra_data.get("Marionette") == "1"
), "Marionette entry is missing or invalid"
assert (
extra_data.get("RemoteAgent") == "1"
), "RemoteAgent entry is missing or invalid"
# Remove original minidump files from the profile directory
remove_files(profile_minidump_path, file_map.values())
def read_extra_file(path):
"""Read and parse the minidump's .extra file."""
try:
with path.open("rb") as file:
data = file.read()
# Try to decode first and replace invalid utf-8 characters.
decoded = data.decode("utf-8", errors="replace")
return json.loads(decoded)
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON in {path}: {e}")
def remove_files(directory, files):
"""Safely remove a list of files."""
for file in files:
file_path = Path(directory) / file.name
try:
file_path.unlink()
except FileNotFoundError:
print(f"File not found: {file_path}")
except PermissionError as e:
raise ValueError(f"Permission error removing {file_path}: {e}")
def verify_minidump_files(directory):
"""Verify that .dmp and .extra files exist and return their paths."""
minidump_files = list(Path(directory).iterdir())
assert len(minidump_files) == 2, f"Expected 2 files, found {minidump_files}."
required_extensions = {".dmp", ".extra"}
file_map = {
file.suffix: file
for file in minidump_files
if file.suffix in required_extensions
}
missing_extensions = required_extensions - file_map.keys()
assert (
not missing_extensions
), f"Missing required files with extensions: {missing_extensions}"
return file_map