• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2023 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import pytest
6
7import logging
8import os
9import test_utils
10import shutil
11
12from contextlib import contextmanager
13from selenium import webdriver
14from selenium.common.exceptions import WebDriverException
15from selenium.webdriver import ChromeOptions
16
17def pytest_addoption(parser: pytest.Parser):
18  # By default, running on the hosted platform.
19  parser.addoption('--target-platform',
20                   default=test_utils.get_hosted_platform(),
21                   dest='target_platform',
22                   choices=['linux', 'win', 'mac', 'android', 'cros',
23                            'lacros'],
24                   help='If present, run for the target platform, '
25                   'defaults to the host platform.')
26
27  parser.addoption('--channel',
28                   default='dev',
29                   choices=['dev', 'canary', 'beta', 'stable', 'extended'],
30                   help='The channel of Chrome to download.')
31
32  parser.addoption('--chromedriver',
33                   help='The path to the existing chromedriver. '
34                   'This will ignore --channel and skip downloading.')
35
36# pylint: disable=redefined-outer-name
37@pytest.fixture(scope="session")
38def chromedriver_path(pytestconfig: pytest.Config) -> str:
39  """Returns a path to the chromedriver."""
40  if cd_path := pytestconfig.getoption('chromedriver'):
41    cd_path = os.path.abspath(cd_path)
42    if not os.path.isfile(cd_path):
43      raise RuntimeError(f'Given chromedriver doesn\'t exist. ({cd_path})')
44    return cd_path
45
46  platform = pytestconfig.getoption('target_platform')
47  channel = pytestconfig.getoption('channel')
48
49  # https://developer.chrome.com/docs/versionhistory/reference/#platform-identifiers
50  downloaded_dir = None
51  if platform == "linux":
52    ver = test_utils.find_version('linux', channel)
53    downloaded_dir = test_utils.download_chrome_linux(version=str(ver))
54  elif platform == "mac":
55    ver = test_utils.find_version('mac_arm64', channel)
56    downloaded_dir = test_utils.download_chrome_mac(version=str(ver))
57  elif platform == "win":
58    ver = test_utils.find_version('win64', channel)
59    downloaded_dir = test_utils.download_chrome_win(version=str(ver))
60  else:
61    raise RuntimeError(f'Given platform ({platform}) is not supported.')
62
63  return os.path.join(downloaded_dir, 'chromedriver')
64
65@pytest.fixture
66def driver_factory(chromedriver_path: str,
67                   tmp_path_factory: pytest.TempPathFactory):
68  """Returns a factory that creates a webdriver."""
69  @contextmanager
70  def factory(chrome_options = None):
71    # Crashpad is a separate process and its dump locations is set via env
72    # variable.
73    crash_dump_dir = tmp_path_factory.mktemp('crash', True)
74    os.environ['BREAKPAD_DUMP_LOCATION'] = str(crash_dump_dir)
75
76    chrome_options = chrome_options or ChromeOptions()
77    chrome_options.add_argument("--headless")
78    chrome_options.add_experimental_option('excludeSwitches',
79                                          ['disable-background-networking'])
80    driver = None
81    try:
82      driver = webdriver.Chrome(chromedriver_path, options=chrome_options)
83      yield driver
84    except WebDriverException as e:
85      # Report this to be part of test result.
86      if os.listdir(crash_dump_dir):
87        logging.error('Chrome crashed and exited abnormally.\n%s', e)
88      else:
89        logging.error('Uncaught WebDriver exception thrown.\n%s', e)
90      raise
91    finally:
92      if driver:
93        driver.quit()
94      shutil.rmtree(crash_dump_dir, ignore_errors=True)
95
96  return factory
97