crx_smoke_tests.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. # Copyright 2021 The Chromium Authors. All rights reserved.
  2. # Use of this source code is governed by a BSD-style license that can be
  3. # found in the LICENSE file.
  4. import json
  5. import logging
  6. import os
  7. import posixpath
  8. import sys
  9. import time
  10. import zipfile
  11. from collections import namedtuple
  12. from devil.android import apk_helper
  13. from devil.android import logcat_monitor
  14. from py_utils.tempfile_ext import NamedTemporaryDirectory
  15. from telemetry.core import util
  16. from telemetry.testing import serially_executed_browser_test_case
  17. logger = logging.getLogger(__name__)
  18. ComponentData = namedtuple('ComponentData', ['component_id', 'browser_args'])
  19. _SET_CONDITION_HTML = """
  20. var test_harness = {}
  21. test_harness.test_succeeded = true;
  22. window.webview_smoke_test_harness = test_harness;
  23. """
  24. _COMPONENT_NAME_TO_DATA = {
  25. 'WebViewAppsPackageNamesAllowlist': ComponentData(
  26. component_id = 'aemllinfpjdgcldgaelcgakpjmaekbai',
  27. browser_args = ['--vmodule=*_allowlist_component_*=2'])
  28. }
  29. _LOGCAT_FILTERS = [
  30. 'chromium:v',
  31. 'cr_*:v',
  32. 'DEBUG:I',
  33. 'StrictMode:D',
  34. 'WebView*:v'
  35. ]
  36. class WebViewCrxSmokeTests(
  37. serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase):
  38. _device = None
  39. _device_components_dir = None
  40. _logcat_monitor = None
  41. @classmethod
  42. def Name(cls):
  43. return 'webview_crx_smoke_tests'
  44. @classmethod
  45. def SetBrowserOptions(cls, finder_options):
  46. """Sets browser command line arguments
  47. Args:
  48. finder_options: Command line arguments for starting the browser"""
  49. finder_options_copy = finder_options.Copy()
  50. finder_options_copy.browser_options.AppendExtraBrowserArgs(
  51. _COMPONENT_NAME_TO_DATA[finder_options.component_name].browser_args)
  52. super(WebViewCrxSmokeTests, cls).SetBrowserOptions(finder_options_copy)
  53. cls._device = cls._browser_to_create._platform_backend.device
  54. @classmethod
  55. def StartBrowser(cls):
  56. """Start browser and wait for it's pid to appear in the processes list"""
  57. super(WebViewCrxSmokeTests, cls).StartBrowser()
  58. # Wait until the browser is up and running
  59. for _ in range(60):
  60. logger.info('Waiting 1 second for the browser to start running')
  61. time.sleep(1)
  62. if cls.browser._browser_backend.processes:
  63. break
  64. assert cls.browser._browser_backend.processes, 'Browser did not start'
  65. logger.info('Browser is running')
  66. @classmethod
  67. def SetUpProcess(cls):
  68. """Prepares the test device"""
  69. super(WebViewCrxSmokeTests, cls).SetUpProcess()
  70. assert cls._finder_options.crx_file, '--crx-file is required'
  71. assert cls._finder_options.component_name, '--component-name is required'
  72. cls.SetBrowserOptions(cls._finder_options)
  73. webview_package_name = cls._finder_options.webview_package_name
  74. if not webview_package_name:
  75. webview_provider_apk = (cls._browser_to_create
  76. .settings.GetApkName(cls._device))
  77. webview_apk_path = util.FindLatestApkOnHost(
  78. cls._finder_options.chrome_root, webview_provider_apk)
  79. webview_package_name = apk_helper.GetPackageName(webview_apk_path)
  80. cls._device_components_dir = ('/data/data/%s/app_webview/components' %
  81. webview_package_name)
  82. if cls.child.artifact_output_dir:
  83. logcat_output_dir = os.path.dirname(cls.child.artifact_output_dir)
  84. else:
  85. logcat_output_dir = os.getcwd()
  86. # Set up a logcat monitor
  87. cls._logcat_monitor = logcat_monitor.LogcatMonitor(
  88. cls._device.adb,
  89. output_file=os.path.join(logcat_output_dir,
  90. '%s_logcat.txt' % cls.Name()),
  91. filter_specs=_LOGCAT_FILTERS)
  92. cls._logcat_monitor.Start()
  93. cls._MaybeClearOutComponentsDir()
  94. component_id = _COMPONENT_NAME_TO_DATA.get(
  95. cls._finder_options.component_name).component_id
  96. with zipfile.ZipFile(cls._finder_options.crx_file) as crx_archive, \
  97. NamedTemporaryDirectory() as tmp_dir, \
  98. crx_archive.open('manifest.json') as manifest:
  99. crx_device_dir = posixpath.join(
  100. cls._device_components_dir, 'cps',
  101. component_id, '1_%s' % json.loads(manifest.read())['version'])
  102. try:
  103. # Create directory on the test device for the CRX files
  104. logger.info('Creating directory %r on device' % crx_device_dir)
  105. output = cls._device.RunShellCommand(
  106. ['mkdir', '-p', crx_device_dir])
  107. logger.debug('Recieved the following output from adb: %s' % output)
  108. except Exception as e:
  109. logger.exception('Exception %r was raised' % str(e))
  110. raise
  111. # Move CRX files to the device directory
  112. crx_archive.extractall(tmp_dir)
  113. cls._MoveNewCrxToDevice(tmp_dir, crx_device_dir)
  114. # Start the browser after the device is in a clean state and the CRX
  115. # files are loaded onto the device
  116. cls.StartBrowser()
  117. @classmethod
  118. def _MoveNewCrxToDevice(cls, src_dir, crx_device_dir):
  119. """Pushes the CRX files onto the test device
  120. Args:
  121. src_dir: Source directory containing CRX files
  122. crx_device_dir: Destination directory for CRX files on the test device"""
  123. for f in os.listdir(src_dir):
  124. src_path = os.path.join(src_dir, f)
  125. dest_path = posixpath.join(crx_device_dir, f)
  126. assert os.path.isfile(src_path), '%r is not a file' % src_path
  127. logger.info('Pushing %r to %r' % (src_path, dest_path))
  128. cls._device.adb.Push(src_path, dest_path)
  129. @classmethod
  130. def _MaybeClearOutComponentsDir(cls):
  131. """Clears out CRX files on test device so that
  132. the test starts with a clean state """
  133. try:
  134. output = cls._device.RemovePath(cls._device_components_dir,
  135. recursive=True,
  136. force=True)
  137. logger.debug('Recieved the following output from adb: %s' % output)
  138. except Exception as e:
  139. logger.exception('Exception %r was raised' % str(e))
  140. raise
  141. @classmethod
  142. def AddCommandlineArgs(cls, parser):
  143. """Adds test suite specific command line arguments"""
  144. parser.add_option('--crx-file', action='store', help='Path to CRX file')
  145. parser.add_option('--component-name', action='store', help='Component name',
  146. choices=list(_COMPONENT_NAME_TO_DATA.keys()))
  147. parser.add_option('--webview-package-name', action='store',
  148. help='WebView package name')
  149. def Test_run_webview_smoke_test(self):
  150. """Test waits for a javascript condition to be set on the WebView shell"""
  151. browser_tab = self.browser.tabs[0]
  152. browser_tab.action_runner.Navigate(
  153. 'about:blank', script_to_evaluate_on_commit=_SET_CONDITION_HTML)
  154. # Wait 5 minutes for the javascript condition. If the condition is not
  155. # set within 5 minutes then an exception will be raised which will cause
  156. # the test to fail.
  157. browser_tab.action_runner.WaitForJavaScriptCondition(
  158. 'window.webview_smoke_test_harness.test_succeeded', timeout=300)
  159. @classmethod
  160. def TearDownProcess(cls):
  161. super(WebViewCrxSmokeTests, cls).TearDownProcess()
  162. cls._logcat_monitor.Stop()
  163. def load_tests(*_):
  164. return serially_executed_browser_test_case.LoadAllTestsInModule(sys.modules[__name__])