Source code

Revision control

Copy as Markdown

Other Tools

# mypy: allow-untyped-defs
import logging
import os
import inspect
import requests
import subprocess
import sys
from unittest import mock
import pytest
from packaging.specifiers import SpecifierSet
from tools.wpt import browser
logger = logging.getLogger()
def test_all_browser_abc():
# Make sure all subclasses of Browser implement all abstract methods
# (except some known base classes). This is a basic sanity test in case
# we change the ABC interface of Browser as we only instantiate some
# products in unit tests.
classes = inspect.getmembers(browser)
for name, cls in classes:
if cls in (browser.Browser, browser.ChromeAndroidBase):
continue
if inspect.isclass(cls) and issubclass(cls, browser.Browser):
assert not inspect.isabstract(cls), "%s is abstract" % name
def test_edgechromium_webdriver_supports_browser():
# MSEdgeDriver binary cannot be called.
edge = browser.EdgeChromium(logger)
edge.webdriver_version = mock.MagicMock(return_value=None)
assert not edge.webdriver_supports_browser('/usr/bin/edgedriver', '/usr/bin/edge', 'stable')
# Browser binary cannot be called.
edge = browser.EdgeChromium(logger)
edge.webdriver_version = mock.MagicMock(return_value='70.0.1')
edge.version = mock.MagicMock(return_value=None)
assert edge.webdriver_supports_browser('/usr/bin/edgedriver', '/usr/bin/edge', 'stable')
# Browser version matches.
edge = browser.EdgeChromium(logger)
# Versions should be an exact match to be compatible.
edge.webdriver_version = mock.MagicMock(return_value='70.1.5')
edge.version = mock.MagicMock(return_value='70.1.5')
assert edge.webdriver_supports_browser('/usr/bin/edgedriver', '/usr/bin/edge', 'stable')
# Browser version doesn't match.
edge = browser.EdgeChromium(logger)
edge.webdriver_version = mock.MagicMock(return_value='70.0.1')
edge.version = mock.MagicMock(return_value='69.0.1')
assert not edge.webdriver_supports_browser('/usr/bin/edgedriver', '/usr/bin/edge', 'stable')
# MSEdgeDriver version should match for MAJOR.MINOR.BUILD version.
edge = browser.EdgeChromium(logger)
edge.webdriver_version = mock.MagicMock(return_value='70.0.1.0')
edge.version = mock.MagicMock(return_value='70.0.1.1 dev')
assert edge.webdriver_supports_browser('/usr/bin/edgedriver', '/usr/bin/edge', 'dev')
# Mismatching minor version should not match.
edge.webdriver_version = mock.MagicMock(return_value='70.9.1')
assert not edge.webdriver_supports_browser('/usr/bin/edgedriver', '/usr/bin/edge', 'dev')
# On Windows, webdriver_version directly calls _get_fileversion, so there is no
# logic to test there.
@pytest.mark.skipif(sys.platform.startswith('win'), reason='just uses _get_fileversion on Windows')
@mock.patch('tools.wpt.browser.call')
def test_edgechromium_webdriver_version(mocked_call):
edge = browser.EdgeChromium(logger)
webdriver_binary = '/usr/bin/edgedriver'
# Working cases.
mocked_call.return_value = 'Microsoft Edge WebDriver 84.0.4147.30'
assert edge.webdriver_version(webdriver_binary) == '84.0.4147.30'
mocked_call.return_value = 'Microsoft Edge WebDriver 87.0.1 (abcd1234-refs/branch-heads/4147@{#310})'
assert edge.webdriver_version(webdriver_binary) == '87.0.1'
# Various invalid version strings
mocked_call.return_value = 'Edge 84.0.4147.30 (dev)'
assert edge.webdriver_version(webdriver_binary) is None
mocked_call.return_value = 'Microsoft Edge WebDriver New 84.0.4147.30'
assert edge.webdriver_version(webdriver_binary) is None
mocked_call.return_value = ''
assert edge.webdriver_version(webdriver_binary) is None
# The underlying subprocess call throws.
mocked_call.side_effect = subprocess.CalledProcessError(5, 'cmd', output='Call failed')
assert edge.webdriver_version(webdriver_binary) is None
def test_chrome_webdriver_supports_browser():
# ChromeDriver binary cannot be called.
chrome = browser.Chrome(logger)
chrome.webdriver_version = mock.MagicMock(return_value=None)
assert not chrome.webdriver_supports_browser('/usr/bin/chromedriver', '/usr/bin/chrome', 'stable')
# Browser binary cannot be called.
chrome = browser.Chrome(logger)
chrome.webdriver_version = mock.MagicMock(return_value='70.0.1')
chrome.version = mock.MagicMock(return_value=None)
assert chrome.webdriver_supports_browser('/usr/bin/chromedriver', '/usr/bin/chrome', 'stable')
# Browser version matches.
chrome = browser.Chrome(logger)
# Versions should be an exact match to be compatible.
chrome.webdriver_version = mock.MagicMock(return_value='70.1.5')
chrome.version = mock.MagicMock(return_value='70.1.5')
assert chrome.webdriver_supports_browser('/usr/bin/chromedriver', '/usr/bin/chrome', 'stable')
# Browser version doesn't match.
chrome = browser.Chrome(logger)
chrome.webdriver_version = mock.MagicMock(return_value='70.0.1')
chrome.version = mock.MagicMock(return_value='69.0.1')
assert not chrome.webdriver_supports_browser('/usr/bin/chromedriver', '/usr/bin/chrome', 'stable')
# ChromeDriver version should match for MAJOR.MINOR.BUILD version.
chrome = browser.Chrome(logger)
chrome.webdriver_version = mock.MagicMock(return_value='70.0.1.0')
chrome.version = mock.MagicMock(return_value='70.0.1.1 dev')
assert chrome.webdriver_supports_browser('/usr/bin/chromedriver', '/usr/bin/chrome', 'dev')
# Matching major version should match.
chrome.webdriver_version = mock.MagicMock(return_value='70.9.1')
assert chrome.webdriver_supports_browser('/usr/bin/chromedriver', '/usr/bin/chrome', 'dev')
def test_chromium_webdriver_supports_browser():
# ChromeDriver binary cannot be called.
chromium = browser.Chromium(logger)
chromium.webdriver_version = mock.MagicMock(return_value=None)
assert not chromium.webdriver_supports_browser('/usr/bin/chromedriver', '/usr/bin/chrome')
# Browser binary cannot be called.
chromium = browser.Chromium(logger)
chromium.webdriver_version = mock.MagicMock(return_value='70.0.1')
chromium.version = mock.MagicMock(return_value=None)
assert chromium.webdriver_supports_browser('/usr/bin/chromedriver', '/usr/bin/chrome')
# Browser version matches.
chromium = browser.Chromium(logger)
chromium.webdriver_version = mock.MagicMock(return_value='70.0.1')
chromium.version = mock.MagicMock(return_value='70.0.1')
assert chromium.webdriver_supports_browser('/usr/bin/chromedriver', '/usr/bin/chrome')
# Browser version doesn't match.
chromium = browser.Chromium(logger)
chromium.webdriver_version = mock.MagicMock(return_value='70.0.1')
chromium.version = mock.MagicMock(return_value='69.0.1')
assert not chromium.webdriver_supports_browser('/usr/bin/chromedriver', '/usr/bin/chrome', 'stable')
# On Windows, webdriver_version directly calls _get_fileversion, so there is no
# logic to test there.
@pytest.mark.skipif(sys.platform.startswith('win'), reason='just uses _get_fileversion on Windows')
@mock.patch('tools.wpt.browser.call')
def test_chrome_webdriver_version(mocked_call):
chrome = browser.Chrome(logger)
webdriver_binary = '/usr/bin/chromedriver'
# Working cases.
mocked_call.return_value = 'ChromeDriver 84.0.4147.30'
assert chrome.webdriver_version(webdriver_binary) == '84.0.4147.30'
mocked_call.return_value = 'ChromeDriver 87.0.1 (abcd1234-refs/branch-heads/4147@{#310})'
assert chrome.webdriver_version(webdriver_binary) == '87.0.1'
# Various invalid version strings
mocked_call.return_value = 'Chrome 84.0.4147.30 (dev)'
assert chrome.webdriver_version(webdriver_binary) is None
mocked_call.return_value = 'ChromeDriver New 84.0.4147.30'
assert chrome.webdriver_version(webdriver_binary) is None
mocked_call.return_value = ''
assert chrome.webdriver_version(webdriver_binary) is None
# The underlying subprocess call throws.
mocked_call.side_effect = subprocess.CalledProcessError(5, 'cmd', output='Call failed')
assert chrome.webdriver_version(webdriver_binary) is None
@mock.patch('subprocess.check_output')
def test_safari_version(mocked_check_output):
safari = browser.Safari(logger)
# Safari
mocked_check_output.return_value = b'Included with Safari 12.1 (14607.1.11)'
assert safari.version(webdriver_binary="safaridriver") == '12.1 (14607.1.11)'
# Safari Technology Preview
mocked_check_output.return_value = b'Included with Safari Technology Preview (Release 67, 13607.1.9.0.1)'
assert safari.version(webdriver_binary="safaridriver") == 'Technology Preview (Release 67, 13607.1.9.0.1)'
@mock.patch('subprocess.check_output')
def test_safari_version_errors(mocked_check_output):
safari = browser.Safari(logger)
# No webdriver_binary
assert safari.version() is None
# `safaridriver --version` return gibberish
mocked_check_output.return_value = b'gibberish'
assert safari.version(webdriver_binary="safaridriver") is None
# `safaridriver --version` fails (as it does for Safari <=12.0)
mocked_check_output.return_value = b'dummy'
mocked_check_output.side_effect = subprocess.CalledProcessError(1, 'cmd')
assert safari.version(webdriver_binary="safaridriver") is None
@pytest.mark.parametrize(
"page_path",
sorted(
p.path
for p in os.scandir(os.path.join(os.path.dirname(__file__), "safari-downloads"))
if p.name.endswith(".html")
),
)
@mock.patch("tools.wpt.browser.get")
def test_safari_find_downloads_stp(mocked_get, page_path):
safari = browser.Safari(logger)
# Setup mock
response = requests.models.Response()
response.status_code = 200
response.encoding = "utf-8"
with open(page_path, "rb") as fp:
response._content = fp.read()
mocked_get.return_value = response
downloads = safari._find_downloads()
if page_path.endswith(
(
"2022-07-05.html",
)
):
# occasionally STP is only shipped for a single OS version
assert len(downloads) == 1
else:
assert len(downloads) == 2
@mock.patch("tools.wpt.browser.get")
def test_safari_find_downloads_stp_20180517(mocked_get):
safari = browser.Safari(logger)
page_path = os.path.join(os.path.dirname(__file__), "safari-downloads", "2018-05-17.html")
# Setup mock
response = requests.models.Response()
response.status_code = 200
response.encoding = "utf-8"
with open(page_path, "rb") as fp:
response._content = fp.read()
mocked_get.return_value = response
downloads = safari._find_downloads()
assert len(downloads) == 2
assert downloads[0][0] == SpecifierSet("==10.13.*")
assert "10.12" not in downloads[0][0]
assert "10.13" in downloads[0][0]
assert "10.13.3" in downloads[0][0]
assert "10.14" not in downloads[0][0]
assert downloads[1][0] == SpecifierSet("~=10.12.6")
assert "10.12" not in downloads[1][0]
assert "10.12.6" in downloads[1][0]
assert "10.12.9" in downloads[1][0]
assert "10.13" not in downloads[1][0]
@mock.patch("tools.wpt.browser.get")
def test_safari_find_downloads_stp_20220529(mocked_get):
safari = browser.Safari(logger)
page_path = os.path.join(os.path.dirname(__file__), "safari-downloads", "2022-05-29.html")
# Setup mock
response = requests.models.Response()
response.status_code = 200
response.encoding = "utf-8"
with open(page_path, "rb") as fp:
response._content = fp.read()
mocked_get.return_value = response
downloads = safari._find_downloads()
assert len(downloads) == 2
assert downloads[0][0] == SpecifierSet("==12.*")
assert "11.4" not in downloads[0][0]
assert "12.0" in downloads[0][0]
assert "12.5" in downloads[0][0]
assert "13.0" not in downloads[0][0]
assert downloads[1][0] == SpecifierSet("==11.*")
assert "10.15.7" not in downloads[1][0]
assert "11.0.1" in downloads[1][0]
assert "11.3" in downloads[1][0]
assert "11.5" in downloads[1][0]
assert "12.0" not in downloads[1][0]
@mock.patch("tools.wpt.browser.get")
def test_safari_find_downloads_stp_20220707(mocked_get):
safari = browser.Safari(logger)
page_path = os.path.join(os.path.dirname(__file__), "safari-downloads", "2022-07-07.html")
# Setup mock
response = requests.models.Response()
response.status_code = 200
response.encoding = "utf-8"
with open(page_path, "rb") as fp:
response._content = fp.read()
mocked_get.return_value = response
downloads = safari._find_downloads()
assert len(downloads) == 2
assert downloads[0][0] == SpecifierSet("==13.*")
assert "12.4" not in downloads[0][0]
assert "13.0" in downloads[0][0]
assert "13.5" in downloads[0][0]
assert "14.0" not in downloads[0][0]
assert downloads[1][0] == SpecifierSet("~=12.3")
assert "11.5" not in downloads[1][0]
assert "12.2" not in downloads[1][0]
assert "12.3" in downloads[1][0]
assert "12.5" in downloads[1][0]
assert "13.0" not in downloads[1][0]
@mock.patch('subprocess.check_output')
def test_webkitgtk_minibrowser_version(mocked_check_output):
webkitgtk_minibrowser = browser.WebKitGTKMiniBrowser(logger)
# stable version
mocked_check_output.return_value = b'WebKitGTK 2.26.1\n'
assert webkitgtk_minibrowser.version(binary='MiniBrowser') == '2.26.1'
# nightly version
mocked_check_output.return_value = b'WebKitGTK 2.27.1 (r250823)\n'
assert webkitgtk_minibrowser.version(binary='MiniBrowser') == '2.27.1 (r250823)'
@mock.patch('subprocess.check_output')
def test_webkitgtk_minibrowser_version_errors(mocked_check_output):
webkitgtk_minibrowser = browser.WebKitGTKMiniBrowser(logger)
# No binary
assert webkitgtk_minibrowser.version() is None
# `MiniBrowser --version` return gibberish
mocked_check_output.return_value = b'gibberish'
assert webkitgtk_minibrowser.version(binary='MiniBrowser') is None
# `MiniBrowser --version` fails (as it does for MiniBrowser <= 2.26.0)
mocked_check_output.return_value = b'dummy'
mocked_check_output.side_effect = subprocess.CalledProcessError(1, 'cmd')
assert webkitgtk_minibrowser.version(binary='MiniBrowser') is None
# The test below doesn't work on Windows because find_binary()
# on Windows only works if the binary name ends with a ".exe" suffix.
# But, WebKitGTK itself doesn't support Windows, so lets skip the test.
@pytest.mark.skipif(sys.platform.startswith('win'), reason='test not needed on Windows')
@mock.patch('os.access', return_value=True)
@mock.patch('os.path.exists')
def test_webkitgtk_minibrowser_find_binary(mocked_os_path_exists, _mocked_os_access):
webkitgtk_minibrowser = browser.WebKitGTKMiniBrowser(logger)
# No MiniBrowser found
mocked_os_path_exists.side_effect = lambda path: path == '/etc/passwd'
assert webkitgtk_minibrowser.find_binary() is None
# Found on the default Fedora path
fedora_minibrowser_path = '/usr/libexec/webkit2gtk-4.0/MiniBrowser'
mocked_os_path_exists.side_effect = lambda path: path == fedora_minibrowser_path
assert webkitgtk_minibrowser.find_binary() == fedora_minibrowser_path
# Found on the default Debian path for AMD64 (gcc not available)
debian_minibrowser_path_amd64 = '/usr/lib/x86_64-linux-gnu/webkit2gtk-4.0/MiniBrowser'
mocked_os_path_exists.side_effect = lambda path: path == debian_minibrowser_path_amd64
assert webkitgtk_minibrowser.find_binary() == debian_minibrowser_path_amd64
# Found on the default Debian path for AMD64 (gcc available but gives an error)
debian_minibrowser_path_amd64 = '/usr/lib/x86_64-linux-gnu/webkit2gtk-4.0/MiniBrowser'
mocked_os_path_exists.side_effect = lambda path: path in [debian_minibrowser_path_amd64, '/usr/bin/gcc']
with mock.patch('subprocess.check_output', return_value = b'error', side_effect = subprocess.CalledProcessError(1, 'cmd')):
assert webkitgtk_minibrowser.find_binary() == debian_minibrowser_path_amd64
# Found on the default Debian path for ARM64 (gcc available)
debian_minibrowser_path_arm64 = '/usr/lib/aarch64-linux-gnu/webkit2gtk-4.0/MiniBrowser'
mocked_os_path_exists.side_effect = lambda path: path in [debian_minibrowser_path_arm64, '/usr/bin/gcc']
with mock.patch('subprocess.check_output', return_value = b'aarch64-linux-gnu'):
assert webkitgtk_minibrowser.find_binary() == debian_minibrowser_path_arm64