Chromium Code Reviews| Index: tools/chrome_proxy/webdriver/common.py |
| diff --git a/tools/chrome_proxy/webdriver/common.py b/tools/chrome_proxy/webdriver/common.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..9418032e4c7258432699120464f99e352f9bbff4 |
| --- /dev/null |
| +++ b/tools/chrome_proxy/webdriver/common.py |
| @@ -0,0 +1,221 @@ |
| +# Copyright 2016 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +import argparse |
| +import json |
| +import os |
| +import shlex |
| +import sys |
| +import time |
| +import traceback |
| + |
| +sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, |
| + os.pardir, 'third_party', 'webdriver', 'pylib')) |
| +from selenium import webdriver |
| +from selenium.webdriver.chrome.options import Options |
| + |
| +# TODO(robertogden) add logging |
| + |
| + |
| +def ParseFlags(): |
| + """ |
| + Parses the given command line arguments and returns an object with the flags |
| + as properties. |
| + """ |
| + parser = argparse.ArgumentParser() |
| + parser.add_argument('--browser_args', nargs=1, type=str, help='Override ' |
| + 'browser flags in code with these flags') |
| + parser.add_argument('--via_header_matches', metavar='via_header', nargs=1, |
| + default='1.1 Chrome-Compression-Proxy', help='What the via should match to ' |
| + 'be considered valid') |
| + parser.add_argument('--chrome_exec', nargs=1, type=str, help='The path to ' |
| + 'the Chrome or Chromium executable') |
| + parser.add_argument('chrome_driver', nargs=1, type=str, help='The path to ' |
| + 'the ChromeDriver executable') |
| + # TODO(robertogden) make this a logging statement |
| + print 'DEBUG: Args=', json.dumps(vars(parser.parse_args(sys.argv[1:]))) |
| + return parser.parse_args(sys.argv[1:]) |
| + |
| +def HandleException(test_name=None): |
| + """ |
| + Writes the exception being handled and a stack trace to stderr. |
| + """ |
| + sys.stderr.write("**************************************\n") |
| + sys.stderr.write("**************************************\n") |
| + sys.stderr.write("** **\n") |
| + sys.stderr.write("** UNCAUGHT EXCEPTION **\n") |
| + sys.stderr.write("** **\n") |
| + sys.stderr.write("**************************************\n") |
| + sys.stderr.write("**************************************\n") |
| + if test_name: |
| + sys.stderr.write("Failed test: %s" % test_name) |
| + traceback.print_exception(*sys.exc_info()) |
| + sys.exit(1) |
| + |
| +class TestDriver: |
| + """ |
| + This class is the tool that is used by every integration test to interact with |
| + the Chromium browser and validate proper functionality. This class sits on top |
| + of the Selenium Chrome Webdriver with added utility and helper functions for |
| + Chrome-Proxy. This class should be used with Python's 'with' operator. |
| + """ |
| + |
| + def __init__(self): |
| + self._flags = ParseFlags() |
| + self._driver = None |
| + self.chrome_args = {} |
|
RyanSturm
2016/11/29 19:31:46
can this be a set instead of a dict? I don't see m
Robert Ogden
2016/11/29 22:39:36
Done in follow up CL
|
| + self._url = '' |
| + |
| + def __enter__(self): |
| + return self |
| + |
| + def __exit__(self, exc_type, exc_value, tb): |
| + if self._driver: |
| + self._StopDriver() |
| + |
| + def _OverrideChromeArgs(self): |
| + """ |
| + Overrides any given flags in the code with those given on the command line. |
| + """ |
| + if self._flags.browser_args and len(self._flags.browser_args) > 0: |
| + for a in shlex.split(self._flags.browser_args): |
| + self.chrome_args[a] = True |
| + |
| + def _StartDriver(self): |
| + """ |
| + Parses the flags to pass to Chromium, then starts the ChromeDriver. |
| + """ |
| + opts = Options() |
| + for a in self.chrome_args: |
| + opts.add_argument(a) |
| + caps = {'loggingPrefs': {'performance': 'INFO'}} |
| + if self._flags.chrome_exec: |
| + caps['chrome.binary'] = self._flags.chrome_exec |
| + driver = webdriver.Chrome(executable_path=self._flags.chrome_driver[0], |
| + chrome_options=opts, desired_capabilities=caps) |
| + driver.command_executor._commands.update({ |
| + 'getAvailableLogTypes': ('GET', '/session/$sessionId/log/types'), |
| + 'getLog': ('POST', '/session/$sessionId/log')}) |
| + self._driver = driver |
| + |
| + def _StopDriver(self): |
| + """ |
| + Nicely stops the ChromeDriver. |
| + """ |
| + self._driver.quit() |
| + del self._driver |
| + |
| + def AddChromeArgs(self, args): |
| + """ |
| + Adds multiple arguments that will be passed to Chromium at start. |
| + """ |
| + if not self.chrome_args: |
| + self.chrome_args = {} |
| + for a in args: |
| + self.chrome_args[a] = True |
| + |
| + def AddChromeArg(self, arg): |
| + """ |
| + Adds a single argument that will be passed to Chromium at start. |
| + """ |
| + if not self.chrome_args: |
| + self.chrome_args = {} |
| + self.chrome_args[arg] = True |
| + |
| + def RemoveChromeArgs(self, args): |
| + """ |
| + Removes multiple arguments that will no longer be passed to Chromium at |
| + start. |
| + """ |
| + if not self.chrome_args: |
| + self.chrome_args = {} |
| + return |
| + for a in args: |
| + del self.chrome_args[a] |
| + |
| + def RemoveChromeArg(self, arg): |
| + """ |
| + Removes a single argument that will no longer be passed to Chromium at |
| + start. |
| + """ |
| + if not self.chrome_args: |
| + self.chrome_args = {} |
| + return |
| + del self.chrome_args[arg] |
| + |
| + def ClearChromeArgs(self): |
| + """ |
| + Removes all arguments from Chromium at start. |
| + """ |
| + self.chrome_args = {} |
| + |
| + def ClearCache(self): |
| + """ |
| + Clears the browser cache. Important note: ChromeDriver automatically starts |
| + a clean copy of Chrome on every instantiation. |
| + """ |
| + self.ExecuteJavascript('if(window.chrome && chrome.benchmarking && ' |
| + 'chrome.benchmarking.clearCache){chrome.benchmarking.clearCache(); ' |
| + 'chrome.benchmarking.clearPredictorCache();chrome.benchmarking.' |
| + 'clearHostResolverCache();}') |
| + |
| + # TODO(robertogden) use a smart page instead |
| + def SetURL(self, url): |
| + """ |
| + Sets the URL that the browser will navigate to during the test. |
| + """ |
| + self._url = url |
| + |
| + # TODO(robertogden) add timeout |
| + def LoadPage(self): |
| + """ |
| + Starts Chromium with any arguments previously given and navigates to the |
| + previously given URL. |
| + """ |
| + if not self._driver: |
| + self._StartDriver() |
| + self._driver.get(self._url) |
| + |
| + # TODO(robertogden) add timeout |
| + def ExecuteJavascript(self, script): |
| + """ |
| + Executes the given javascript in the browser's current page as if it were on |
| + the console. Returns a string of whatever the evaluation was. |
| + """ |
| + if not self._driver: |
| + self._StartDriver() |
| + return self._driver.execute_script("return " + script) |
|
RyanSturm
2016/11/29 19:31:46
What happens if LoadPage was not called (i.e., _St
Robert Ogden
2016/11/29 22:39:36
Chrome would start but would stay on a blank page.
|
| + |
| + |
| +class IntegrationTest: |
| + """ |
| + A parent class for all integration tests with utility and assertion methods. |
| + All methods starting with the word 'test' (ignoring case) will be called with |
| + the RunAllTests() method which can be used in place of a main method. |
| + """ |
| + def RunAllTests(self): |
| + """ |
| + Runs all methods starting with the word 'test' (ignoring case) in the class. |
| + Can be used in place of a main method to run all tests in a class. |
| + """ |
| + methodList = [method for method in dir(self) if callable(getattr(self, |
| + method)) and method.lower().startswith('test')] |
| + for method in methodList: |
| + try: |
| + getattr(self, method)() |
| + except Exception as e: |
| + HandleException(method) |
| + |
| + # TODO(robertogden) add some nice assertion functions |
| + |
| + def Fail(self, msg): |
| + sys.stderr.write("**************************************\n") |
| + sys.stderr.write("**************************************\n") |
| + sys.stderr.write("** **\n") |
| + sys.stderr.write("** TEST FAILURE **\n") |
| + sys.stderr.write("** **\n") |
| + sys.stderr.write("**************************************\n") |
| + sys.stderr.write("**************************************\n") |
| + sys.stderr.write(msg, '\n') |
| + sys.exit(1) |