Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(19)

Unified Diff: tools/chrome_proxy/webdriver/common.py

Issue 2528483004: Adds skeleton of new integration test framework. (Closed)
Patch Set: Fix nits. Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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)

Powered by Google App Engine
This is Rietveld 408576698