OLD | NEW |
---|---|
(Empty) | |
1 # Copyright 2016 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 | |
5 import argparse | |
6 import json | |
7 import os | |
8 import shlex | |
9 import sys | |
10 import time | |
11 import traceback | |
12 | |
13 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, | |
14 os.pardir, 'third_party', 'webdriver', 'pylib')) | |
15 from selenium import webdriver | |
16 from selenium.webdriver.chrome.options import Options | |
17 | |
18 # TODO(robertogden) add logging | |
19 | |
20 | |
21 def ParseFlags(): | |
22 """ | |
23 Parses the given command line arguments and returns an object with the flags | |
24 as properties. | |
25 """ | |
26 parser = argparse.ArgumentParser() | |
27 parser.add_argument('--browser_args', nargs=1, type=str, help='Override ' | |
28 'browser flags in code with these flags') | |
29 parser.add_argument('--via_header_matches', metavar='via_header', nargs=1, | |
RyanSturm
2016/11/29 19:31:46
nit: s/via_header_matchers/via_header_value/
| |
30 default='1.1 Chrome-Compression-Proxy', help='What the via should match to ' | |
31 'be considered valid') | |
32 parser.add_argument('--chrome_exec', nargs=1, type=str, help='The path to ' | |
33 'the Chrome or Chromium executable') | |
RyanSturm
2016/11/29 19:31:46
Can you add that if no chrome_exec is supplied, th
| |
34 parser.add_argument('chrome_driver', nargs=1, type=str, help='The path to ' | |
35 'the ChromeDriver executable') | |
36 # TODO(robertogden) make this a logging statement | |
37 print 'DEBUG: Args=', json.dumps(vars(parser.parse_args(sys.argv[1:]))) | |
38 return parser.parse_args(sys.argv[1:]) | |
39 | |
40 def HandleException(): | |
41 """ | |
42 Writes the exception being handled and a stack trace to stderr. | |
43 """ | |
44 sys.stderr.write("**************************************\n") | |
45 sys.stderr.write("**************************************\n") | |
46 sys.stderr.write("** **\n") | |
47 sys.stderr.write("** UNCAUGHT EXCEPTION **\n") | |
48 sys.stderr.write("** **\n") | |
49 sys.stderr.write("**************************************\n") | |
50 sys.stderr.write("**************************************\n") | |
51 traceback.print_exception(*sys.exc_info()) | |
52 sys.exit(1) | |
53 | |
54 class TestDriver: | |
55 """ | |
56 This class is the tool that is used by every integration test to interact with | |
57 the Chromium browser and validate proper functionality. This class sits on top | |
58 of the Selenium Chrome Webdriver with added utility and helper functions for | |
59 Chrome-Proxy. This class should be used with Python's 'with' operator. | |
60 """ | |
61 | |
62 def __init__(self): | |
63 self._flags = ParseFlags() | |
64 self._driver = None | |
65 self._args = {} | |
tbansal1
2016/11/29 18:38:53
Can this be renamed to chrome_args?
Robert Ogden
2016/11/29 18:53:54
Done.
| |
66 self._url = '' | |
67 | |
68 def __enter__(self): | |
69 return self | |
70 | |
71 def __exit__(self, exc_type, exc_value, tb): | |
72 if self._driver: | |
73 self._StopDriver() | |
74 | |
75 def _OverrideChromeArgs(self): | |
76 """ | |
77 Overrides any given flags in the code with those given on the command line. | |
78 """ | |
79 if self._flags.browser_args and len(self._flags.browser_args) > 0: | |
80 for a in shlex.split(self._flags.browser_args): | |
RyanSturm
2016/11/29 19:31:46
nit: s/a/arg/
| |
81 self._args[a] = True | |
82 | |
83 def _StartDriver(self): | |
84 """ | |
85 Parses the flags to pass to Chromium, then starts the ChromeDriver. | |
86 """ | |
87 opts = Options() | |
RyanSturm
2016/11/29 19:31:46
nit: s/opts/options/
| |
88 for a in self._args: | |
RyanSturm
2016/11/29 19:31:46
nit: s/a/arg/
| |
89 opts.add_argument(a) | |
90 caps = {'loggingPrefs': {'performance': 'INFO'}} | |
RyanSturm
2016/11/29 19:31:46
nit: s/caps/capabilities/
| |
91 if self._flags.chrome_exec: | |
92 caps['chrome.binary'] = self._flags.chrome_exec | |
93 driver = webdriver.Chrome(executable_path=self._flags.chrome_driver[0], | |
94 chrome_options=opts, desired_capabilities=caps) | |
95 driver.command_executor._commands.update({ | |
96 'getAvailableLogTypes': ('GET', '/session/$sessionId/log/types'), | |
97 'getLog': ('POST', '/session/$sessionId/log')}) | |
98 self._driver = driver | |
99 | |
100 def _StopDriver(self): | |
101 """ | |
102 Nicely stops the ChromeDriver. | |
103 """ | |
104 self._driver.quit() | |
105 del self._driver | |
106 | |
107 def AddChromeArgs(self, args): | |
108 """ | |
109 Adds multiple arguments that will be passed to Chromium at start. | |
110 """ | |
111 if not self._args: | |
RyanSturm
2016/11/29 19:31:46
can self._args ever not be a value after __init__?
| |
112 self._args = {} | |
113 for a in args: | |
114 self._args[a] = True | |
115 | |
116 def AddChromeArg(self, arg): | |
tbansal1
2016/11/29 18:38:53
Is this being called somewhere?
Robert Ogden
2016/11/29 18:53:54
Not yet, but I think it is an important method to
| |
117 """ | |
118 Adds a single argument that will be passed to Chromium at start. | |
119 """ | |
120 if not self._args: | |
121 self._args = {} | |
122 self._args[arg] = True | |
123 | |
124 def RemoveChromeArgs(self, args): | |
125 """ | |
126 Removes multiple arguments that will no longer be passed to Chromium at | |
127 start. | |
128 """ | |
129 if not self._args: | |
130 self._args = {} | |
131 return | |
132 for a in args: | |
133 del self._args[a] | |
134 | |
135 def RemoveChromeArg(self, arg): | |
tbansal1
2016/11/29 18:38:53
Is this being called somewhere?
Robert Ogden
2016/11/29 18:53:54
Same as above.
| |
136 """ | |
137 Removes a single argument that will no longer be passed to Chromium at | |
138 start. | |
139 """ | |
140 if not self._args: | |
141 self._args = {} | |
142 return | |
143 del self._args[arg] | |
144 | |
145 def ClearChromeArgs(self): | |
146 """ | |
147 Removes all arguments from Chromium at start. | |
148 """ | |
149 self._args = {} | |
150 | |
151 def ClearCache(self): | |
152 """ | |
153 Clears the browser cache. Important note: ChromeDriver automatically starts | |
154 a clean copy of Chrome on every instantiation. | |
155 """ | |
156 self.ExecuteJavascript('if(window.chrome && chrome.benchmarking && ' | |
157 'chrome.benchmarking.clearCache){chrome.benchmarking.clearCache(); ' | |
158 'chrome.benchmarking.clearPredictorCache();chrome.benchmarking.' | |
159 'clearHostResolverCache();}') | |
160 | |
161 # TODO(robertogden) use a smart page instead | |
162 def SetURL(self, url): | |
163 """ | |
164 Sets the URL that the browser will navigate to during the test. | |
165 """ | |
166 self._url = url | |
167 | |
168 # TODO(robertogden) add timeout | |
169 def LoadPage(self): | |
170 """ | |
171 Starts Chromium with any arguments previously given and navigates to the | |
172 previously given URL. | |
173 """ | |
174 if not self._driver: | |
175 self._StartDriver() | |
176 self._driver.get(self._url) | |
177 | |
178 # TODO(robertogden) add timeout | |
179 def ExecuteJavascript(self, script): | |
180 """ | |
181 Executes the given javascript in the browser's current page as if it were on | |
182 the console. Returns a string of whatever the evaluation was. | |
183 """ | |
184 if not self._driver: | |
185 self._StartDriver() | |
186 return self._driver.execute_script("return " + script) | |
187 | |
188 | |
189 class IntegrationTest: | |
190 """ | |
191 A parent class for all integration tests with utility and assertion methods. | |
192 All methods starting with the word 'test' (ignoring case) will be called with | |
193 the RunAllTests() method which can be used in place of a main method. | |
194 """ | |
195 def RunAllTests(self): | |
196 """ | |
197 Runs all methods starting with the word 'test' (ignoring case) in the class. | |
198 Can be used in place of a main method to run all tests in a class. | |
199 """ | |
200 methodList = [method for method in dir(self) if callable(getattr(self, | |
201 method)) and method.lower().startswith('test')] | |
202 for method in methodList: | |
203 try: | |
204 getattr(self, method)() | |
205 except Exception as e: | |
206 HandleException(e) | |
tbansal1
2016/11/29 18:38:53
Pass the name of the test in the HandleException s
Robert Ogden
2016/11/29 18:53:54
Done.
RyanSturm
2016/11/29 19:31:46
I think tbansal meant pass method and the exceptio
| |
207 | |
208 # TODO(robertogden) add some nice assertion functions | |
209 | |
210 def Fail(self, msg): | |
211 sys.stderr.write("**************************************\n") | |
212 sys.stderr.write("**************************************\n") | |
213 sys.stderr.write("** **\n") | |
214 sys.stderr.write("** TEST FAILURE **\n") | |
215 sys.stderr.write("** **\n") | |
216 sys.stderr.write("**************************************\n") | |
217 sys.stderr.write("**************************************\n") | |
218 sys.stderr.write(msg, '\n') | |
219 sys.exit(1) | |
OLD | NEW |