1#!/usr/bin/env python
2# Copyright 2013 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""End to end tests for ChromeDriver."""
7
8import base64
9import json
10import math
11import optparse
12import os
13import socket
14import subprocess
15import sys
16import tempfile
17import threading
18import time
19import unittest
20import urllib2
21
22_THIS_DIR = os.path.abspath(os.path.dirname(__file__))
23sys.path.insert(1, os.path.join(_THIS_DIR, os.pardir))
24sys.path.insert(1, os.path.join(_THIS_DIR, os.pardir, 'client'))
25sys.path.insert(1, os.path.join(_THIS_DIR, os.pardir, 'server'))
26
27import chrome_paths
28import chromedriver
29import unittest_util
30import util
31import server
32from webelement import WebElement
33import webserver
34
35_TEST_DATA_DIR = os.path.join(chrome_paths.GetTestData(), 'chromedriver')
36
37if util.IsLinux():
38  sys.path.insert(0, os.path.join(chrome_paths.GetSrc(), 'build', 'android'))
39  from pylib import android_commands
40  from pylib import constants
41  from pylib import forwarder
42  from pylib import valgrind_tools
43  from pylib.device import device_utils
44
45
46_NEGATIVE_FILTER = [
47    # https://code.google.com/p/chromedriver/issues/detail?id=213
48    'ChromeDriverTest.testClickElementInSubFrame',
49    # This test is flaky since it uses setTimeout.
50    # Re-enable once crbug.com/177511 is fixed and we can remove setTimeout.
51    'ChromeDriverTest.testAlert',
52]
53
54_VERSION_SPECIFIC_FILTER = {}
55_VERSION_SPECIFIC_FILTER['HEAD'] = [
56    # https://code.google.com/p/chromedriver/issues/detail?id=815
57    'ChromeDriverTest.testShouldHandleNewWindowLoadingProperly',
58]
59
60_OS_SPECIFIC_FILTER = {}
61_OS_SPECIFIC_FILTER['win'] = [
62    # https://code.google.com/p/chromedriver/issues/detail?id=214
63    'ChromeDriverTest.testCloseWindow',
64    # https://code.google.com/p/chromedriver/issues/detail?id=299
65    'ChromeLogPathCapabilityTest.testChromeLogPath',
66]
67_OS_SPECIFIC_FILTER['linux'] = [
68    # Xvfb doesn't support maximization.
69    'ChromeDriverTest.testWindowMaximize',
70    # https://code.google.com/p/chromedriver/issues/detail?id=302
71    'ChromeDriverTest.testWindowPosition',
72    'ChromeDriverTest.testWindowSize',
73]
74_OS_SPECIFIC_FILTER['mac'] = [
75    # https://code.google.com/p/chromedriver/issues/detail?id=304
76    'ChromeDriverTest.testGoBackAndGoForward',
77]
78
79_DESKTOP_NEGATIVE_FILTER = [
80    # Desktop doesn't support touch (without --touch-events).
81    'ChromeDriverTest.testSingleTapElement',
82    'ChromeDriverTest.testTouchDownUpElement',
83    'ChromeDriverTest.testTouchFlickElement',
84    'ChromeDriverTest.testTouchMovedElement',
85    'ChromeDriverAndroidTest.*',
86]
87
88
89def _GetDesktopNegativeFilter(version_name):
90  filter = _NEGATIVE_FILTER + _DESKTOP_NEGATIVE_FILTER
91  os = util.GetPlatformName()
92  if os in _OS_SPECIFIC_FILTER:
93    filter += _OS_SPECIFIC_FILTER[os]
94  if version_name in _VERSION_SPECIFIC_FILTER:
95    filter += _VERSION_SPECIFIC_FILTER[version_name]
96  return filter
97
98_ANDROID_NEGATIVE_FILTER = {}
99_ANDROID_NEGATIVE_FILTER['chrome'] = (
100    _NEGATIVE_FILTER + [
101        # TODO(chrisgao): fix hang of tab crash test on android.
102        'ChromeDriverTest.testTabCrash',
103        # Android doesn't support switches and extensions.
104        'ChromeSwitchesCapabilityTest.*',
105        'ChromeExtensionsCapabilityTest.*',
106        'MobileEmulationCapabilityTest.*',
107        # https://crbug.com/274650
108        'ChromeDriverTest.testCloseWindow',
109        # https://code.google.com/p/chromedriver/issues/detail?id=270
110        'ChromeDriverTest.testPopups',
111        # https://code.google.com/p/chromedriver/issues/detail?id=298
112        'ChromeDriverTest.testWindowPosition',
113        'ChromeDriverTest.testWindowSize',
114        'ChromeDriverTest.testWindowMaximize',
115        'ChromeLogPathCapabilityTest.testChromeLogPath',
116        'RemoteBrowserTest.*',
117        # Don't enable perf testing on Android yet.
118        'PerfTest.testSessionStartTime',
119        'PerfTest.testSessionStopTime',
120        'PerfTest.testColdExecuteScript',
121        # https://code.google.com/p/chromedriver/issues/detail?id=459
122        'ChromeDriverTest.testShouldHandleNewWindowLoadingProperly',
123    ]
124)
125_ANDROID_NEGATIVE_FILTER['chrome_stable'] = (
126    _ANDROID_NEGATIVE_FILTER['chrome'])
127_ANDROID_NEGATIVE_FILTER['chrome_beta'] = (
128    _ANDROID_NEGATIVE_FILTER['chrome'])
129_ANDROID_NEGATIVE_FILTER['chrome_shell'] = (
130    _ANDROID_NEGATIVE_FILTER['chrome'] + [
131        # ChromeShell doesn't support multiple tabs.
132        'ChromeDriverTest.testGetWindowHandles',
133        'ChromeDriverTest.testSwitchToWindow',
134        'ChromeDriverTest.testShouldHandleNewWindowLoadingProperly',
135    ]
136)
137_ANDROID_NEGATIVE_FILTER['chromedriver_webview_shell'] = (
138    _ANDROID_NEGATIVE_FILTER['chrome_shell'])
139
140
141class ChromeDriverBaseTest(unittest.TestCase):
142  """Base class for testing chromedriver functionalities."""
143
144  def __init__(self, *args, **kwargs):
145    super(ChromeDriverBaseTest, self).__init__(*args, **kwargs)
146    self._drivers = []
147
148  def tearDown(self):
149    for driver in self._drivers:
150      try:
151        driver.Quit()
152      except:
153        pass
154
155  def CreateDriver(self, server_url=None, **kwargs):
156    if server_url is None:
157      server_url = _CHROMEDRIVER_SERVER_URL
158
159    android_package = None
160    android_activity = None
161    android_process = None
162    if _ANDROID_PACKAGE_KEY:
163      android_package = constants.PACKAGE_INFO[_ANDROID_PACKAGE_KEY].package
164      if _ANDROID_PACKAGE_KEY == 'chromedriver_webview_shell':
165        android_activity = constants.PACKAGE_INFO[_ANDROID_PACKAGE_KEY].activity
166        android_process = '%s:main' % android_package
167
168    driver = chromedriver.ChromeDriver(server_url,
169                                       chrome_binary=_CHROME_BINARY,
170                                       android_package=android_package,
171                                       android_activity=android_activity,
172                                       android_process=android_process,
173                                       **kwargs)
174    self._drivers += [driver]
175    return driver
176
177
178class ChromeDriverTest(ChromeDriverBaseTest):
179  """End to end tests for ChromeDriver."""
180
181  @staticmethod
182  def GlobalSetUp():
183    ChromeDriverTest._http_server = webserver.WebServer(
184        chrome_paths.GetTestData())
185    ChromeDriverTest._sync_server = webserver.SyncWebServer()
186    if _ANDROID_PACKAGE_KEY:
187      ChromeDriverTest._device = device_utils.DeviceUtils(
188          android_commands.GetAttachedDevices()[0])
189      http_host_port = ChromeDriverTest._http_server._server.server_port
190      sync_host_port = ChromeDriverTest._sync_server._server.server_port
191      forwarder.Forwarder.Map(
192          [(http_host_port, http_host_port), (sync_host_port, sync_host_port)],
193          ChromeDriverTest._device)
194
195  @staticmethod
196  def GlobalTearDown():
197    if _ANDROID_PACKAGE_KEY:
198      forwarder.Forwarder.UnmapAllDevicePorts(ChromeDriverTest._device)
199    ChromeDriverTest._http_server.Shutdown()
200
201  @staticmethod
202  def GetHttpUrlForFile(file_path):
203    return ChromeDriverTest._http_server.GetUrl() + file_path
204
205  def setUp(self):
206    self._driver = self.CreateDriver()
207
208  def testStartStop(self):
209    pass
210
211  def testLoadUrl(self):
212    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
213
214  def testGetCurrentWindowHandle(self):
215    self._driver.GetCurrentWindowHandle()
216
217  def _WaitForNewWindow(self, old_handles):
218    """Wait for at least one new window to show up in 20 seconds.
219
220    Args:
221      old_handles: Handles to all old windows before the new window is added.
222
223    Returns:
224      Handle to a new window. None if timeout.
225    """
226    timeout = time.time() + 20
227    while time.time() < timeout:
228      new_handles = self._driver.GetWindowHandles()
229      if len(new_handles) > len(old_handles):
230        for index, old_handle in enumerate(old_handles):
231          self.assertEquals(old_handle, new_handles[index])
232        return new_handles[len(old_handles)]
233      time.sleep(0.01)
234    return None
235
236  def testCloseWindow(self):
237    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/page_test.html'))
238    old_handles = self._driver.GetWindowHandles()
239    self._driver.FindElement('id', 'link').Click()
240    new_window_handle = self._WaitForNewWindow(old_handles)
241    self.assertNotEqual(None, new_window_handle)
242    self._driver.SwitchToWindow(new_window_handle)
243    self.assertEquals(new_window_handle, self._driver.GetCurrentWindowHandle())
244    self.assertRaises(chromedriver.NoSuchElement,
245                      self._driver.FindElement, 'id', 'link')
246    self._driver.CloseWindow()
247    self.assertRaises(chromedriver.NoSuchWindow,
248                      self._driver.GetCurrentWindowHandle)
249    new_handles = self._driver.GetWindowHandles()
250    for old_handle in old_handles:
251      self.assertTrue(old_handle in new_handles)
252    for handle in new_handles:
253      self._driver.SwitchToWindow(handle)
254      self.assertEquals(handle, self._driver.GetCurrentWindowHandle())
255      self._driver.CloseWindow()
256
257  def testGetWindowHandles(self):
258    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/page_test.html'))
259    old_handles = self._driver.GetWindowHandles()
260    self._driver.FindElement('id', 'link').Click()
261    self.assertNotEqual(None, self._WaitForNewWindow(old_handles))
262
263  def testSwitchToWindow(self):
264    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/page_test.html'))
265    self.assertEquals(
266        1, self._driver.ExecuteScript('window.name = "oldWindow"; return 1;'))
267    window1_handle = self._driver.GetCurrentWindowHandle()
268    old_handles = self._driver.GetWindowHandles()
269    self._driver.FindElement('id', 'link').Click()
270    new_window_handle = self._WaitForNewWindow(old_handles)
271    self.assertNotEqual(None, new_window_handle)
272    self._driver.SwitchToWindow(new_window_handle)
273    self.assertEquals(new_window_handle, self._driver.GetCurrentWindowHandle())
274    self.assertRaises(chromedriver.NoSuchElement,
275                      self._driver.FindElement, 'id', 'link')
276    self._driver.SwitchToWindow('oldWindow')
277    self.assertEquals(window1_handle, self._driver.GetCurrentWindowHandle())
278
279  def testEvaluateScript(self):
280    self.assertEquals(1, self._driver.ExecuteScript('return 1'))
281    self.assertEquals(None, self._driver.ExecuteScript(''))
282
283  def testEvaluateScriptWithArgs(self):
284    script = ('document.body.innerHTML = "<div>b</div><div>c</div>";'
285              'return {stuff: document.querySelectorAll("div")};')
286    stuff = self._driver.ExecuteScript(script)['stuff']
287    script = 'return arguments[0].innerHTML + arguments[1].innerHTML'
288    self.assertEquals(
289        'bc', self._driver.ExecuteScript(script, stuff[0], stuff[1]))
290
291  def testEvaluateInvalidScript(self):
292    self.assertRaises(chromedriver.ChromeDriverException,
293                      self._driver.ExecuteScript, '{{{')
294
295  def testExecuteAsyncScript(self):
296    self._driver.SetTimeout('script', 3000)
297    self.assertRaises(
298        chromedriver.ScriptTimeout,
299        self._driver.ExecuteAsyncScript,
300        'var callback = arguments[0];'
301        'setTimeout(function(){callback(1);}, 10000);')
302    self.assertEquals(
303        2,
304        self._driver.ExecuteAsyncScript(
305            'var callback = arguments[0];'
306            'setTimeout(function(){callback(2);}, 300);'))
307
308  def testSwitchToFrame(self):
309    self._driver.ExecuteScript(
310        'var frame = document.createElement("iframe");'
311        'frame.id="id";'
312        'frame.name="name";'
313        'document.body.appendChild(frame);')
314    self.assertTrue(self._driver.ExecuteScript('return window.top == window'))
315    self._driver.SwitchToFrame('id')
316    self.assertTrue(self._driver.ExecuteScript('return window.top != window'))
317    self._driver.SwitchToMainFrame()
318    self.assertTrue(self._driver.ExecuteScript('return window.top == window'))
319    self._driver.SwitchToFrame('name')
320    self.assertTrue(self._driver.ExecuteScript('return window.top != window'))
321    self._driver.SwitchToMainFrame()
322    self.assertTrue(self._driver.ExecuteScript('return window.top == window'))
323    self._driver.SwitchToFrameByIndex(0)
324    self.assertTrue(self._driver.ExecuteScript('return window.top != window'))
325    self._driver.SwitchToMainFrame()
326    self.assertTrue(self._driver.ExecuteScript('return window.top == window'))
327    self._driver.SwitchToFrame(self._driver.FindElement('tag name', 'iframe'))
328    self.assertTrue(self._driver.ExecuteScript('return window.top != window'))
329
330  def testSwitchToParentFrame(self):
331    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/nested.html'))
332    self.assertTrue('One' in self._driver.GetPageSource())
333    self._driver.SwitchToFrameByIndex(0)
334    self.assertTrue('Two' in self._driver.GetPageSource())
335    self._driver.SwitchToFrameByIndex(0)
336    self.assertTrue('Three' in self._driver.GetPageSource())
337    self._driver.SwitchToParentFrame()
338    self.assertTrue('Two' in self._driver.GetPageSource())
339    self._driver.SwitchToParentFrame()
340    self.assertTrue('One' in self._driver.GetPageSource())
341
342  def testExecuteInRemovedFrame(self):
343    self._driver.ExecuteScript(
344        'var frame = document.createElement("iframe");'
345        'frame.id="id";'
346        'frame.name="name";'
347        'document.body.appendChild(frame);'
348        'window.addEventListener("message",'
349        '    function(event) { document.body.removeChild(frame); });')
350    self.assertTrue(self._driver.ExecuteScript('return window.top == window'))
351    self._driver.SwitchToFrame('id')
352    self.assertTrue(self._driver.ExecuteScript('return window.top != window'))
353    self._driver.ExecuteScript('parent.postMessage("remove", "*");')
354    self.assertTrue(self._driver.ExecuteScript('return window.top == window'))
355
356  def testGetTitle(self):
357    script = 'document.title = "title"; return 1;'
358    self.assertEquals(1, self._driver.ExecuteScript(script))
359    self.assertEquals('title', self._driver.GetTitle())
360
361  def testGetPageSource(self):
362    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/page_test.html'))
363    self.assertTrue('Link to empty.html' in self._driver.GetPageSource())
364
365  def testFindElement(self):
366    self._driver.ExecuteScript(
367        'document.body.innerHTML = "<div>a</div><div>b</div>";')
368    self.assertTrue(
369        isinstance(self._driver.FindElement('tag name', 'div'), WebElement))
370
371  def testFindElements(self):
372    self._driver.ExecuteScript(
373        'document.body.innerHTML = "<div>a</div><div>b</div>";')
374    divs = self._driver.FindElements('tag name', 'div')
375    self.assertTrue(isinstance(divs, list))
376    self.assertEquals(2, len(divs))
377    for div in divs:
378      self.assertTrue(isinstance(div, WebElement))
379
380  def testFindChildElement(self):
381    self._driver.ExecuteScript(
382        'document.body.innerHTML = "<div><br><br></div><div><a></a></div>";')
383    element = self._driver.FindElement('tag name', 'div')
384    self.assertTrue(
385        isinstance(element.FindElement('tag name', 'br'), WebElement))
386
387  def testFindChildElements(self):
388    self._driver.ExecuteScript(
389        'document.body.innerHTML = "<div><br><br></div><div><br></div>";')
390    element = self._driver.FindElement('tag name', 'div')
391    brs = element.FindElements('tag name', 'br')
392    self.assertTrue(isinstance(brs, list))
393    self.assertEquals(2, len(brs))
394    for br in brs:
395      self.assertTrue(isinstance(br, WebElement))
396
397  def testHoverOverElement(self):
398    div = self._driver.ExecuteScript(
399        'document.body.innerHTML = "<div>old</div>";'
400        'var div = document.getElementsByTagName("div")[0];'
401        'div.addEventListener("mouseover", function() {'
402        '  document.body.appendChild(document.createElement("br"));'
403        '});'
404        'return div;')
405    div.HoverOver()
406    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
407
408  def testClickElement(self):
409    div = self._driver.ExecuteScript(
410        'document.body.innerHTML = "<div>old</div>";'
411        'var div = document.getElementsByTagName("div")[0];'
412        'div.addEventListener("click", function() {'
413        '  div.innerHTML="new<br>";'
414        '});'
415        'return div;')
416    div.Click()
417    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
418
419  def testSingleTapElement(self):
420    div = self._driver.ExecuteScript(
421        'document.body.innerHTML = "<div>old</div>";'
422        'var div = document.getElementsByTagName("div")[0];'
423        'div.addEventListener("touchend", function() {'
424        '  div.innerHTML="new<br>";'
425        '});'
426        'return div;')
427    div.SingleTap()
428    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
429
430  def testTouchDownUpElement(self):
431    div = self._driver.ExecuteScript(
432        'document.body.innerHTML = "<div>old</div>";'
433        'var div = document.getElementsByTagName("div")[0];'
434        'div.addEventListener("touchend", function() {'
435        '  div.innerHTML="new<br>";'
436        '});'
437        'return div;')
438    loc = div.GetLocation()
439    self._driver.TouchDown(loc['x'], loc['y'])
440    self._driver.TouchUp(loc['x'], loc['y'])
441    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
442
443  def testTouchFlickElement(self):
444    dx = 3
445    dy = 4
446    speed = 5
447    flickTouchEventsPerSecond = 30
448    moveEvents = int(
449        math.sqrt(dx * dx + dy * dy) * flickTouchEventsPerSecond / speed)
450    div = self._driver.ExecuteScript(
451        'document.body.innerHTML = "<div>old</div>";'
452        'var div = document.getElementsByTagName("div")[0];'
453        'div.addEventListener("touchstart", function() {'
454        '  div.innerHTML = "preMove0";'
455        '});'
456        'div.addEventListener("touchmove", function() {'
457        '  res = div.innerHTML.match(/preMove(\d+)/);'
458        '  if (res != null) {'
459        '    div.innerHTML = "preMove" + (parseInt(res[1], 10) + 1);'
460        '  }'
461        '});'
462        'div.addEventListener("touchend", function() {'
463        '  if (div.innerHTML == "preMove' + str(moveEvents) + '") {'
464        '    div.innerHTML = "new<br>";'
465        '  }'
466        '});'
467        'return div;')
468    self._driver.TouchFlick(div, dx, dy, speed)
469    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
470
471  def testTouchMovedElement(self):
472    div = self._driver.ExecuteScript(
473        'document.body.innerHTML = "<div>old</div>";'
474        'var div = document.getElementsByTagName("div")[0];'
475        'div.addEventListener("touchmove", function() {'
476        '  div.innerHTML="new<br>";'
477        '});'
478        'return div;')
479    loc = div.GetLocation()
480    self._driver.TouchDown(loc['x'], loc['y'])
481    self._driver.TouchMove(loc['x'] + 1, loc['y'] + 1)
482    self._driver.TouchUp(loc['x'] + 1, loc['y'] + 1)
483    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
484
485  def testClickElementInSubFrame(self):
486    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/frame_test.html'))
487    frame = self._driver.FindElement('tag name', 'iframe')
488    self._driver.SwitchToFrame(frame)
489    # Test clicking element in the sub frame.
490    self.testClickElement()
491
492  def testClearElement(self):
493    text = self._driver.ExecuteScript(
494        'document.body.innerHTML = \'<input type="text" value="abc">\';'
495        'var input = document.getElementsByTagName("input")[0];'
496        'input.addEventListener("change", function() {'
497        '  document.body.appendChild(document.createElement("br"));'
498        '});'
499        'return input;')
500    text.Clear()
501    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
502
503  def testSendKeysToElement(self):
504    text = self._driver.ExecuteScript(
505        'document.body.innerHTML = \'<input type="text">\';'
506        'var input = document.getElementsByTagName("input")[0];'
507        'input.addEventListener("change", function() {'
508        '  document.body.appendChild(document.createElement("br"));'
509        '});'
510        'return input;')
511    text.SendKeys('0123456789+-*/ Hi')
512    text.SendKeys(', there!')
513    value = self._driver.ExecuteScript('return arguments[0].value;', text)
514    self.assertEquals('0123456789+-*/ Hi, there!', value)
515
516  def testGetCurrentUrl(self):
517    self.assertEquals('data:,', self._driver.GetCurrentUrl())
518
519  def testGoBackAndGoForward(self):
520    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
521    self._driver.GoBack()
522    self._driver.GoForward()
523
524  def testRefresh(self):
525    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
526    self._driver.Refresh()
527
528  def testMouseMoveTo(self):
529    div = self._driver.ExecuteScript(
530        'document.body.innerHTML = "<div>old</div>";'
531        'var div = document.getElementsByTagName("div")[0];'
532        'div.style["width"] = "100px";'
533        'div.style["height"] = "100px";'
534        'div.addEventListener("mouseover", function() {'
535        '  var div = document.getElementsByTagName("div")[0];'
536        '  div.innerHTML="new<br>";'
537        '});'
538        'return div;')
539    self._driver.MouseMoveTo(div, 10, 10)
540    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
541
542  def testMouseClick(self):
543    div = self._driver.ExecuteScript(
544        'document.body.innerHTML = "<div>old</div>";'
545        'var div = document.getElementsByTagName("div")[0];'
546        'div.style["width"] = "100px";'
547        'div.style["height"] = "100px";'
548        'div.addEventListener("click", function() {'
549        '  var div = document.getElementsByTagName("div")[0];'
550        '  div.innerHTML="new<br>";'
551        '});'
552        'return div;')
553    self._driver.MouseMoveTo(div)
554    self._driver.MouseClick()
555    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
556
557  def testMouseButtonDownAndUp(self):
558    self._driver.ExecuteScript(
559        'document.body.innerHTML = "<div>old</div>";'
560        'var div = document.getElementsByTagName("div")[0];'
561        'div.style["width"] = "100px";'
562        'div.style["height"] = "100px";'
563        'div.addEventListener("mousedown", function() {'
564        '  var div = document.getElementsByTagName("div")[0];'
565        '  div.innerHTML="new1<br>";'
566        '});'
567        'div.addEventListener("mouseup", function() {'
568        '  var div = document.getElementsByTagName("div")[0];'
569        '  div.innerHTML="new2<a></a>";'
570        '});')
571    self._driver.MouseMoveTo(None, 50, 50)
572    self._driver.MouseButtonDown()
573    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
574    self._driver.MouseButtonUp()
575    self.assertEquals(1, len(self._driver.FindElements('tag name', 'a')))
576
577  def testMouseDoubleClick(self):
578    div = self._driver.ExecuteScript(
579        'document.body.innerHTML = "<div>old</div>";'
580        'var div = document.getElementsByTagName("div")[0];'
581        'div.style["width"] = "100px";'
582        'div.style["height"] = "100px";'
583        'div.addEventListener("dblclick", function() {'
584        '  var div = document.getElementsByTagName("div")[0];'
585        '  div.innerHTML="new<br>";'
586        '});'
587        'return div;')
588    self._driver.MouseMoveTo(div, 1, 1)
589    self._driver.MouseDoubleClick()
590    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
591
592  def testAlert(self):
593    self.assertFalse(self._driver.IsAlertOpen())
594    self._driver.ExecuteScript(
595        'window.setTimeout('
596        '    function() { window.confirmed = confirm(\'HI\'); },'
597        '    0);')
598    self.assertTrue(self._driver.IsAlertOpen())
599    self.assertEquals('HI', self._driver.GetAlertMessage())
600    self._driver.HandleAlert(False)
601    self.assertFalse(self._driver.IsAlertOpen())
602    self.assertEquals(False,
603                      self._driver.ExecuteScript('return window.confirmed'))
604
605  def testShouldHandleNewWindowLoadingProperly(self):
606    """Tests that ChromeDriver determines loading correctly for new windows."""
607    self._http_server.SetDataForPath(
608        '/newwindow',
609        """
610        <html>
611        <body>
612        <a href='%s' target='_blank'>new window/tab</a>
613        </body>
614        </html>""" % self._sync_server.GetUrl())
615    self._driver.Load(self._http_server.GetUrl() + '/newwindow')
616    old_windows = self._driver.GetWindowHandles()
617    self._driver.FindElement('tagName', 'a').Click()
618    new_window = self._WaitForNewWindow(old_windows)
619    self.assertNotEqual(None, new_window)
620
621    self.assertFalse(self._driver.IsLoading())
622    self._driver.SwitchToWindow(new_window)
623    self.assertTrue(self._driver.IsLoading())
624    self._sync_server.RespondWithContent('<html>new window</html>')
625    self._driver.ExecuteScript('return 1')  # Shouldn't hang.
626
627  def testPopups(self):
628    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
629    old_handles = self._driver.GetWindowHandles()
630    self._driver.ExecuteScript('window.open("about:blank")')
631    new_window_handle = self._WaitForNewWindow(old_handles)
632    self.assertNotEqual(None, new_window_handle)
633
634  def testNoSuchFrame(self):
635    self.assertRaises(chromedriver.NoSuchFrame,
636                      self._driver.SwitchToFrame, 'nosuchframe')
637    self.assertRaises(chromedriver.NoSuchFrame,
638                      self._driver.SwitchToFrame,
639                      self._driver.FindElement('tagName', 'body'))
640
641  def testWindowPosition(self):
642    position = self._driver.GetWindowPosition()
643    self._driver.SetWindowPosition(position[0], position[1])
644    self.assertEquals(position, self._driver.GetWindowPosition())
645
646    # Resize so the window isn't moved offscreen.
647    # See https://code.google.com/p/chromedriver/issues/detail?id=297.
648    self._driver.SetWindowSize(300, 300)
649
650    self._driver.SetWindowPosition(100, 200)
651    self.assertEquals([100, 200], self._driver.GetWindowPosition())
652
653  def testWindowSize(self):
654    size = self._driver.GetWindowSize()
655    self._driver.SetWindowSize(size[0], size[1])
656    self.assertEquals(size, self._driver.GetWindowSize())
657
658    self._driver.SetWindowSize(600, 400)
659    self.assertEquals([600, 400], self._driver.GetWindowSize())
660
661  def testWindowMaximize(self):
662    self._driver.SetWindowPosition(100, 200)
663    self._driver.SetWindowSize(600, 400)
664    self._driver.MaximizeWindow()
665
666    self.assertNotEqual([100, 200], self._driver.GetWindowPosition())
667    self.assertNotEqual([600, 400], self._driver.GetWindowSize())
668    # Set size first so that the window isn't moved offscreen.
669    # See https://code.google.com/p/chromedriver/issues/detail?id=297.
670    self._driver.SetWindowSize(600, 400)
671    self._driver.SetWindowPosition(100, 200)
672    self.assertEquals([100, 200], self._driver.GetWindowPosition())
673    self.assertEquals([600, 400], self._driver.GetWindowSize())
674
675  def testConsoleLogSources(self):
676    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/console_log.html'))
677    logs = self._driver.GetLog('browser')
678    self.assertEquals(len(logs), 2)
679    self.assertEquals(logs[0]['source'], 'network')
680    self.assertEquals(logs[1]['source'], 'javascript')
681
682  def testAutoReporting(self):
683    self.assertFalse(self._driver.IsAutoReporting())
684    self._driver.SetAutoReporting(True)
685    self.assertTrue(self._driver.IsAutoReporting())
686    url = self.GetHttpUrlForFile('/chromedriver/console_log.html')
687    self.assertRaisesRegexp(chromedriver.UnknownError,
688                            '.*(404|Failed to load resource).*',
689                            self._driver.Load,
690                            url)
691
692  def testContextMenuEventFired(self):
693    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/context_menu.html'))
694    self._driver.MouseMoveTo(self._driver.FindElement('tagName', 'div'))
695    self._driver.MouseClick(2)
696    self.assertTrue(self._driver.ExecuteScript('return success'))
697
698  def testHasFocusOnStartup(self):
699    # Some pages (about:blank) cause Chrome to put the focus in URL bar.
700    # This breaks tests depending on focus.
701    self.assertTrue(self._driver.ExecuteScript('return document.hasFocus()'))
702
703  def testTabCrash(self):
704    # If a tab is crashed, the session will be deleted.
705    # When 31 is released, will reload the tab instead.
706    # https://code.google.com/p/chromedriver/issues/detail?id=547
707    self.assertRaises(chromedriver.UnknownError,
708                      self._driver.Load, 'chrome://crash')
709    self.assertRaises(chromedriver.NoSuchSession,
710                      self._driver.GetCurrentUrl)
711
712  def testDoesntHangOnDebugger(self):
713    self._driver.ExecuteScript('debugger;')
714
715  def testMobileEmulationDisabledByDefault(self):
716    self.assertFalse(self._driver.capabilities['mobileEmulationEnabled'])
717
718  def testChromeDriverSendLargeData(self):
719    script = 's = ""; for (i = 0; i < 10e6; i++) s += "0"; return s;'
720    lots_of_data = self._driver.ExecuteScript(script)
721    self.assertEquals('0'.zfill(int(10e6)), lots_of_data)
722
723  def testChromeDriverRecieveAndSendLargeData(self):
724    lots_of_data = '1'.zfill(int(10e6))
725    result = self._driver.ExecuteScript('return "%s"' % lots_of_data)
726    self.assertEquals(lots_of_data, result)
727
728
729class ChromeDriverAndroidTest(ChromeDriverBaseTest):
730  """End to end tests for Android-specific tests."""
731
732  def testLatestAndroidAppInstalled(self):
733    if ('stable' not in _ANDROID_PACKAGE_KEY and
734        'beta' not in _ANDROID_PACKAGE_KEY):
735      return
736
737    self._driver = self.CreateDriver()
738
739    try:
740      omaha_list = json.loads(
741          urllib2.urlopen('http://omahaproxy.appspot.com/all.json').read())
742      for l in omaha_list:
743        if l['os'] != 'android':
744          continue
745        for v in l['versions']:
746          if (('stable' in v['channel'] and 'stable' in _ANDROID_PACKAGE_KEY) or
747              ('beta' in v['channel'] and 'beta' in _ANDROID_PACKAGE_KEY)):
748            omaha = map(int, v['version'].split('.'))
749            device = map(int, self._driver.capabilities['version'].split('.'))
750            self.assertTrue(omaha <= device)
751            return
752      raise RuntimeError('Malformed omaha JSON')
753    except urllib2.URLError as e:
754      print 'Unable to fetch current version info from omahaproxy (%s)' % e
755
756  def testDeviceManagement(self):
757    self._drivers = [self.CreateDriver() for x in
758                     android_commands.GetAttachedDevices()]
759    self.assertRaises(chromedriver.UnknownError, self.CreateDriver)
760    self._drivers[0].Quit()
761    self._drivers[0] = self.CreateDriver()
762
763
764class ChromeSwitchesCapabilityTest(ChromeDriverBaseTest):
765  """Tests that chromedriver properly processes chromeOptions.args capabilities.
766
767  Makes sure the switches are passed to Chrome.
768  """
769
770  def testSwitchWithoutArgument(self):
771    """Tests that switch --dom-automation can be passed to Chrome.
772
773    Unless --dom-automation is specified, window.domAutomationController
774    is undefined.
775    """
776    driver = self.CreateDriver(chrome_switches=['dom-automation'])
777    self.assertNotEqual(
778        None,
779        driver.ExecuteScript('return window.domAutomationController'))
780
781
782class ChromeExtensionsCapabilityTest(ChromeDriverBaseTest):
783  """Tests that chromedriver properly processes chromeOptions.extensions."""
784
785  def _PackExtension(self, ext_path):
786    return base64.b64encode(open(ext_path, 'rb').read())
787
788  def testExtensionsInstall(self):
789    """Checks that chromedriver can take the extensions in crx format."""
790    crx_1 = os.path.join(_TEST_DATA_DIR, 'ext_test_1.crx')
791    crx_2 = os.path.join(_TEST_DATA_DIR, 'ext_test_2.crx')
792    self.CreateDriver(chrome_extensions=[self._PackExtension(crx_1),
793                                         self._PackExtension(crx_2)])
794
795  def testExtensionsInstallZip(self):
796    """Checks that chromedriver can take the extensions in zip format."""
797    zip_1 = os.path.join(_TEST_DATA_DIR, 'ext_test_1.zip')
798    self.CreateDriver(chrome_extensions=[self._PackExtension(zip_1)])
799
800  def testWaitsForExtensionToLoad(self):
801    did_load_event = threading.Event()
802    server = webserver.SyncWebServer()
803    def RunServer():
804      time.sleep(5)
805      server.RespondWithContent('<html>iframe</html>')
806      did_load_event.set()
807
808    thread = threading.Thread(target=RunServer)
809    thread.daemon = True
810    thread.start()
811    crx = os.path.join(_TEST_DATA_DIR, 'ext_slow_loader.crx')
812    driver = self.CreateDriver(
813        chrome_switches=['user-agent=' + server.GetUrl()],
814        chrome_extensions=[self._PackExtension(crx)])
815    self.assertTrue(did_load_event.is_set())
816
817
818class ChromeLogPathCapabilityTest(ChromeDriverBaseTest):
819  """Tests that chromedriver properly processes chromeOptions.logPath."""
820
821  LOG_MESSAGE = 'Welcome to ChromeLogPathCapabilityTest!'
822
823  def testChromeLogPath(self):
824    """Checks that user can specify the path of the chrome log.
825
826    Verifies that a log message is written into the specified log file.
827    """
828    tmp_log_path = tempfile.NamedTemporaryFile()
829    driver = self.CreateDriver(chrome_log_path=tmp_log_path.name)
830    driver.ExecuteScript('console.info("%s")' % self.LOG_MESSAGE)
831    driver.Quit()
832    self.assertTrue(self.LOG_MESSAGE in open(tmp_log_path.name).read())
833
834
835class MobileEmulationCapabilityTest(ChromeDriverBaseTest):
836  """Tests that ChromeDriver processes chromeOptions.mobileEmulation.
837
838  Makes sure the device metrics are overridden in DevTools and user agent is
839  overridden in Chrome.
840  """
841
842  @staticmethod
843  def GlobalSetUp():
844    def respondWithUserAgentString(request):
845      return request.GetHeader('User-Agent')
846
847    MobileEmulationCapabilityTest._http_server = webserver.WebServer(
848        chrome_paths.GetTestData())
849    MobileEmulationCapabilityTest._http_server.SetCallbackForPath(
850        '/userAgent', respondWithUserAgentString)
851
852  @staticmethod
853  def GlobalTearDown():
854    MobileEmulationCapabilityTest._http_server.Shutdown()
855
856  def testDeviceMetrics(self):
857    driver = self.CreateDriver(
858        mobile_emulation = {
859            'deviceMetrics': {'width': 360, 'height': 640, 'pixelRatio': 3}})
860    self.assertTrue(driver.capabilities['mobileEmulationEnabled'])
861    self.assertEqual(360, driver.ExecuteScript('return window.innerWidth'))
862    self.assertEqual(640, driver.ExecuteScript('return window.innerHeight'))
863
864  def testUserAgent(self):
865    driver = self.CreateDriver(
866        mobile_emulation = {'userAgent': 'Agent Smith'})
867    driver.Load(self._http_server.GetUrl() + '/userAgent')
868    body_tag = driver.FindElement('tag name', 'body')
869    self.assertEqual("Agent Smith", body_tag.GetText())
870
871  def testDeviceName(self):
872    driver = self.CreateDriver(
873        mobile_emulation = {'deviceName': 'Google Nexus 5'})
874    driver.Load(self._http_server.GetUrl() + '/userAgent')
875    self.assertEqual(360, driver.ExecuteScript('return window.innerWidth'))
876    self.assertEqual(640, driver.ExecuteScript('return window.innerHeight'))
877    body_tag = driver.FindElement('tag name', 'body')
878    self.assertEqual(
879        'Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleW'
880        'ebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/53'
881        '5.19',
882        body_tag.GetText())
883
884  def testSendKeysToElement(self):
885    driver = self.CreateDriver(
886        mobile_emulation = {'deviceName': 'Google Nexus 5'})
887    text = driver.ExecuteScript(
888        'document.body.innerHTML = \'<input type="text">\';'
889        'var input = document.getElementsByTagName("input")[0];'
890        'input.addEventListener("change", function() {'
891        '  document.body.appendChild(document.createElement("br"));'
892        '});'
893        'return input;')
894    text.SendKeys('0123456789+-*/ Hi')
895    text.SendKeys(', there!')
896    value = driver.ExecuteScript('return arguments[0].value;', text)
897    self.assertEquals('0123456789+-*/ Hi, there!', value)
898
899  def testHoverOverElement(self):
900    driver = self.CreateDriver(
901        mobile_emulation = {'deviceName': 'Google Nexus 5'})
902    div = driver.ExecuteScript(
903        'document.body.innerHTML = "<div>old</div>";'
904        'var div = document.getElementsByTagName("div")[0];'
905        'div.addEventListener("mouseover", function() {'
906        '  document.body.appendChild(document.createElement("br"));'
907        '});'
908        'return div;')
909    div.HoverOver()
910    self.assertEquals(1, len(driver.FindElements('tag name', 'br')))
911
912  def testClickElement(self):
913    driver = self.CreateDriver(
914        mobile_emulation = {'deviceName': 'Google Nexus 5'})
915    div = driver.ExecuteScript(
916        'document.body.innerHTML = "<div>old</div>";'
917        'var div = document.getElementsByTagName("div")[0];'
918        'div.addEventListener("click", function() {'
919        '  div.innerHTML="new<br>";'
920        '});'
921        'return div;')
922    div.Click()
923    self.assertEquals(1, len(driver.FindElements('tag name', 'br')))
924
925  def testSingleTapElement(self):
926    driver = self.CreateDriver(
927        mobile_emulation = {'deviceName': 'Google Nexus 5'})
928    div = driver.ExecuteScript(
929        'document.body.innerHTML = "<div>old</div>";'
930        'var div = document.getElementsByTagName("div")[0];'
931        'div.addEventListener("touchend", function() {'
932        '  div.innerHTML="new<br>";'
933        '});'
934        'return div;')
935    div.SingleTap()
936    self.assertEquals(1, len(driver.FindElements('tag name', 'br')))
937
938  def testTouchDownUpElement(self):
939    driver = self.CreateDriver(
940        mobile_emulation = {'deviceName': 'Google Nexus 5'})
941    div = driver.ExecuteScript(
942        'document.body.innerHTML = "<div>old</div>";'
943        'var div = document.getElementsByTagName("div")[0];'
944        'div.addEventListener("touchend", function() {'
945        '  div.innerHTML="new<br>";'
946        '});'
947        'return div;')
948    loc = div.GetLocation()
949    driver.TouchDown(loc['x'], loc['y'])
950    driver.TouchUp(loc['x'], loc['y'])
951    self.assertEquals(1, len(driver.FindElements('tag name', 'br')))
952
953
954class ChromeDriverLogTest(unittest.TestCase):
955  """Tests that chromedriver produces the expected log file."""
956
957  UNEXPECTED_CHROMEOPTION_CAP = 'unexpected_chromeoption_capability'
958  LOG_MESSAGE = 'unrecognized chrome option: %s' % UNEXPECTED_CHROMEOPTION_CAP
959
960  def testChromeDriverLog(self):
961    _, tmp_log_path = tempfile.mkstemp(prefix='chromedriver_log_')
962    chromedriver_server = server.Server(
963        _CHROMEDRIVER_BINARY, log_path=tmp_log_path)
964    try:
965      driver = chromedriver.ChromeDriver(
966          chromedriver_server.GetUrl(), chrome_binary=_CHROME_BINARY,
967          experimental_options={ self.UNEXPECTED_CHROMEOPTION_CAP : 1 })
968      driver.Quit()
969    except chromedriver.ChromeDriverException, e:
970      self.assertTrue(self.LOG_MESSAGE in e.message)
971    finally:
972      chromedriver_server.Kill()
973    with open(tmp_log_path, 'r') as f:
974      self.assertTrue(self.LOG_MESSAGE in f.read())
975
976
977class PerformanceLoggerTest(ChromeDriverBaseTest):
978  """Tests chromedriver tracing support and Inspector event collection."""
979
980  def testPerformanceLogger(self):
981    driver = self.CreateDriver(
982        experimental_options={'perfLoggingPrefs': {
983            'enableTimeline': True,
984            'traceCategories': 'webkit.console,blink.console'
985          }}, performance_log_level='ALL')
986    driver.Load(
987        ChromeDriverTest._http_server.GetUrl() + '/chromedriver/empty.html')
988    # Mark the timeline; later we will verify the marks appear in the trace.
989    driver.ExecuteScript('console.time("foobar")')
990    driver.ExecuteScript('console.timeEnd("foobar")')
991    logs = driver.GetLog('performance')
992    driver.Quit()
993
994    marked_timeline_events = []
995    seen_log_domains = {}
996    for entry in logs:
997      devtools_message = json.loads(entry['message'])['message']
998      method = devtools_message['method']
999      domain = method[:method.find('.')]
1000      seen_log_domains[domain] = True
1001      if method != 'Tracing.dataCollected':
1002        continue
1003      self.assertTrue('params' in devtools_message)
1004      self.assertTrue(isinstance(devtools_message['params'], dict))
1005      cat = devtools_message['params'].get('cat', '')
1006      # Depending on Chrome version, the events may occur for the webkit.console
1007      # or blink.console category. They will only occur for one of them.
1008      if (cat == 'blink.console' or cat == 'webkit.console'):
1009        self.assertTrue(devtools_message['params']['name'] == 'foobar')
1010        marked_timeline_events.append(devtools_message)
1011    self.assertEquals(2, len(marked_timeline_events))
1012    self.assertEquals({'Network', 'Page', 'Timeline', 'Tracing'},
1013                      set(seen_log_domains.keys()))
1014
1015
1016class SessionHandlingTest(ChromeDriverBaseTest):
1017  """Tests for session operations."""
1018  def testQuitASessionMoreThanOnce(self):
1019    driver = self.CreateDriver()
1020    driver.Quit()
1021    driver.Quit()
1022
1023
1024class RemoteBrowserTest(ChromeDriverBaseTest):
1025  """Tests for ChromeDriver remote browser capability."""
1026  def setUp(self):
1027    self.assertTrue(_CHROME_BINARY is not None,
1028                    'must supply a chrome binary arg')
1029
1030  def testConnectToRemoteBrowser(self):
1031    port = self.FindFreePort()
1032    temp_dir = util.MakeTempDir()
1033    process = subprocess.Popen([_CHROME_BINARY,
1034                                '--remote-debugging-port=%d' % port,
1035                                '--user-data-dir=%s' % temp_dir])
1036    if process is None:
1037      raise RuntimeError('Chrome could not be started with debugging port')
1038    try:
1039      driver = self.CreateDriver(debugger_address='127.0.0.1:%d' % port)
1040      driver.ExecuteScript('console.info("%s")' % 'connecting at %d!' % port)
1041      driver.Quit()
1042    finally:
1043      process.terminate()
1044
1045  def FindFreePort(self):
1046    for port in range(10000, 10100):
1047      try:
1048        socket.create_connection(('127.0.0.1', port), 0.2).close()
1049      except socket.error:
1050        return port
1051    raise RuntimeError('Cannot find open port')
1052
1053class PerfTest(ChromeDriverBaseTest):
1054  """Tests for ChromeDriver perf."""
1055  def setUp(self):
1056    self.assertTrue(_REFERENCE_CHROMEDRIVER is not None,
1057                    'must supply a reference-chromedriver arg')
1058
1059  def _RunDriverPerfTest(self, name, test_func):
1060    """Runs a perf test comparing a reference and new ChromeDriver server.
1061
1062    Args:
1063      name: The name of the perf test.
1064      test_func: Called with the server url to perform the test action. Must
1065                 return the time elapsed.
1066    """
1067    class Results(object):
1068      ref = []
1069      new = []
1070
1071    ref_server = server.Server(_REFERENCE_CHROMEDRIVER)
1072    results = Results()
1073    result_url_pairs = zip([results.new, results.ref],
1074                           [_CHROMEDRIVER_SERVER_URL, ref_server.GetUrl()])
1075    for iteration in range(30):
1076      for result, url in result_url_pairs:
1077        result += [test_func(url)]
1078      # Reverse the order for the next run.
1079      result_url_pairs = result_url_pairs[::-1]
1080
1081    def PrintResult(build, result):
1082      mean = sum(result) / len(result)
1083      avg_dev = sum([abs(sample - mean) for sample in result]) / len(result)
1084      print 'perf result', build, name, mean, avg_dev, result
1085      util.AddBuildStepText('%s %s: %.3f+-%.3f' % (
1086          build, name, mean, avg_dev))
1087
1088    # Discard first result, which may be off due to cold start.
1089    PrintResult('new', results.new[1:])
1090    PrintResult('ref', results.ref[1:])
1091
1092  def testSessionStartTime(self):
1093    def Run(url):
1094      start = time.time()
1095      driver = self.CreateDriver(url)
1096      end = time.time()
1097      driver.Quit()
1098      return end - start
1099    self._RunDriverPerfTest('session start', Run)
1100
1101  def testSessionStopTime(self):
1102    def Run(url):
1103      driver = self.CreateDriver(url)
1104      start = time.time()
1105      driver.Quit()
1106      end = time.time()
1107      return end - start
1108    self._RunDriverPerfTest('session stop', Run)
1109
1110  def testColdExecuteScript(self):
1111    def Run(url):
1112      driver = self.CreateDriver(url)
1113      start = time.time()
1114      driver.ExecuteScript('return 1')
1115      end = time.time()
1116      driver.Quit()
1117      return end - start
1118    self._RunDriverPerfTest('cold exe js', Run)
1119
1120if __name__ == '__main__':
1121  parser = optparse.OptionParser()
1122  parser.add_option(
1123      '', '--chromedriver',
1124      help='Path to chromedriver server (REQUIRED!)')
1125  parser.add_option(
1126      '', '--log-path',
1127      help='Output verbose server logs to this file')
1128  parser.add_option(
1129      '', '--reference-chromedriver',
1130      help='Path to the reference chromedriver server')
1131  parser.add_option(
1132      '', '--chrome', help='Path to a build of the chrome binary')
1133  parser.add_option(
1134      '', '--chrome-version', default='HEAD',
1135      help='Version of chrome. Default is \'HEAD\'.')
1136  parser.add_option(
1137      '', '--filter', type='string', default='*',
1138      help=('Filter for specifying what tests to run, "*" will run all. E.g., '
1139            '*testStartStop'))
1140  parser.add_option(
1141      '', '--android-package',
1142      help=('Android package key. Possible values: ' +
1143            str(_ANDROID_NEGATIVE_FILTER.keys())))
1144  options, args = parser.parse_args()
1145
1146  options.chromedriver = util.GetAbsolutePathOfUserPath(options.chromedriver)
1147  if not options.chromedriver or not os.path.exists(options.chromedriver):
1148    parser.error('chromedriver is required or the given path is invalid.' +
1149                 'Please run "%s --help" for help' % __file__)
1150
1151  global _CHROMEDRIVER_BINARY
1152  _CHROMEDRIVER_BINARY = options.chromedriver
1153
1154  if (options.android_package and
1155      options.android_package not in _ANDROID_NEGATIVE_FILTER):
1156    parser.error('Invalid --android-package')
1157
1158  chromedriver_server = server.Server(_CHROMEDRIVER_BINARY, options.log_path)
1159  global _CHROMEDRIVER_SERVER_URL
1160  _CHROMEDRIVER_SERVER_URL = chromedriver_server.GetUrl()
1161
1162  global _REFERENCE_CHROMEDRIVER
1163  _REFERENCE_CHROMEDRIVER = util.GetAbsolutePathOfUserPath(
1164      options.reference_chromedriver)
1165
1166  global _CHROME_BINARY
1167  if options.chrome:
1168    _CHROME_BINARY = util.GetAbsolutePathOfUserPath(options.chrome)
1169  else:
1170    _CHROME_BINARY = None
1171
1172  global _ANDROID_PACKAGE_KEY
1173  _ANDROID_PACKAGE_KEY = options.android_package
1174
1175  if options.filter == '*':
1176    if _ANDROID_PACKAGE_KEY:
1177      negative_filter = _ANDROID_NEGATIVE_FILTER[_ANDROID_PACKAGE_KEY]
1178    else:
1179      negative_filter = _GetDesktopNegativeFilter(options.chrome_version)
1180    options.filter = '*-' + ':__main__.'.join([''] + negative_filter)
1181
1182  all_tests_suite = unittest.defaultTestLoader.loadTestsFromModule(
1183      sys.modules[__name__])
1184  tests = unittest_util.FilterTestSuite(all_tests_suite, options.filter)
1185  ChromeDriverTest.GlobalSetUp()
1186  MobileEmulationCapabilityTest.GlobalSetUp()
1187  result = unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run(tests)
1188  ChromeDriverTest.GlobalTearDown()
1189  MobileEmulationCapabilityTest.GlobalTearDown()
1190  sys.exit(len(result.failures) + len(result.errors))
1191