diff options
author | nduca@chromium.org <nduca@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-04 15:07:36 +0000 |
---|---|---|
committer | nduca@chromium.org <nduca@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-04 15:07:36 +0000 |
commit | 8b46deaa1f0ab3794fa08f08f314c481584e591a (patch) | |
tree | a60786693fa569a34572fc7767929eb2b44a1bf1 /tools | |
parent | 218c1f36a7bc72ef661faf5047a40cc81369ac72 (diff) | |
download | chromium_src-8b46deaa1f0ab3794fa08f08f314c481584e591a.zip chromium_src-8b46deaa1f0ab3794fa08f08f314c481584e591a.tar.gz chromium_src-8b46deaa1f0ab3794fa08f08f314c481584e591a.tar.bz2 |
Polish to chrome_remote_control to reduce flake and improve modularity.
- Make android work
- Overhaul tools/gpu to be more component-y
- Fix error where pexpect missing breaks all browser discovery
- Use page.navigate and get rid of the strange timeout logic in InspectorBackend
- Make browser selection explicit. You have to select browsers explicitly.
- Add --repeat-count to run_tests
- Move sleep-after-each-load to gpu_tools.url_test
- Make timeouts explicit
- Fix handling of optparse defaults and counters
- Add is_content_shell property on backends
- Set up content-shell command line on android
- Override user-data-dir only for non-content-shell backends
- If browser fails to die on sigterm, send it a sigkill
- Move gpu tools browser creation to toplevel test
- Make non-jsonizable evaluate test independent of window for perf reasons
- Add runtime.execute to avoid hacks of runtime.evaluate
TBR=dtu@chromium.org
Review URL: https://chromiumcodereview.appspot.com/10912078
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@154754 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools')
48 files changed, 1017 insertions, 551 deletions
diff --git a/tools/chrome_remote_control/chrome_remote_control/android_commands.py b/tools/chrome_remote_control/chrome_remote_control/adb_commands.py index db67a43..908a5a3 100644 --- a/tools/chrome_remote_control/chrome_remote_control/android_commands.py +++ b/tools/chrome_remote_control/chrome_remote_control/adb_commands.py @@ -14,11 +14,18 @@ import sys # Get build/android scripts into our path. sys.path.append( - os.path.join(os.path.dirname(__file__), - "../../../build/android")) -from pylib import android_commands as real_android_commands -from pylib import cmd_helper as real_cmd_helper - + os.path.abspath( + os.path.join(os.path.dirname(__file__), + "../../../build/android"))) +try: + from pylib import android_commands as real_android_commands + from pylib import cmd_helper as real_cmd_helper +except: + import traceback; traceback.print_exc() + real_android_commands = None + +def IsAndroidSupported(): + return real_android_commands def GetAttachedDevices(): """Returns a list of attached, online android devices. @@ -27,7 +34,7 @@ def GetAttachedDevices(): the returned list.""" return real_android_commands.GetAttachedDevices() -class AndroidCommands(object): +class ADBCommands(object): """A thin wrapper around ADB""" def __init__(self, device): @@ -98,3 +105,6 @@ class AndroidCommands(object): action, category, data, extras, trace_file_name) + + def Push(self, local, remote): + return self._adb.Adb().Push(local, remote) diff --git a/tools/chrome_remote_control/chrome_remote_control/android_browser_backend.py b/tools/chrome_remote_control/chrome_remote_control/android_browser_backend.py index 5f0e25d2..58249cb 100644 --- a/tools/chrome_remote_control/chrome_remote_control/android_browser_backend.py +++ b/tools/chrome_remote_control/chrome_remote_control/android_browser_backend.py @@ -18,9 +18,9 @@ class AndroidBrowserBackend(browser_backend.BrowserBackend): """The backend for controlling a browser instance running on Android. """ def __init__(self, type, options, adb, - package, cmdline_file, activity, + package, is_content_shell, cmdline_file, activity, devtools_remote_port): - super(AndroidBrowserBackend, self).__init__() + super(AndroidBrowserBackend, self).__init__(is_content_shell) # Initialize fields so that an explosion during init doesn't break in Close. self._options = options self._adb = adb @@ -30,18 +30,33 @@ class AndroidBrowserBackend(browser_backend.BrowserBackend): self._port = 9222 self._devtools_remote_port = devtools_remote_port - args = [type, - "--disable-fre", - "--remote-debugging-port=%i" % 9222] - if not options.dont_override_profile: - logging.warning("Overriding of profile is not yet supported on android.") - args.extend(options.extra_browser_args) + # Beginnings of a basic command line. + if is_content_shell: + pseudo_exec_name = "content_shell" + else: + pseudo_exec_name = "chrome" + args = [pseudo_exec_name, + "--disable-fre", "--no-first-run"] # Kill old broser. self._adb.KillAll(self._package) self._adb.KillAll('forawrder') self._adb.Forward('tcp:9222', self._devtools_remote_port) + # Set up temporary dir if needed. As far as we can tell, content_shell + # doesn't have persisted data, so --user-data-dir isn't needed. + if not is_content_shell and not options.dont_override_profile: + self._tmpdir = "/sdcard/chrome_remote_control_data" + self._adb.RunShellCommand('rm -r %s' % self._tmpdir) + args.append("--user-data-dir=%s" % self._tmpdir) + + # Set up the command line. + args.extend(options.extra_browser_args) + with tempfile.NamedTemporaryFile() as f: + f.write(" ".join(args)) + f.flush() + self._adb.Push(f.name, cmdline_file) + # Start it up! self._adb.StartActivity(self._package, self._activity, diff --git a/tools/chrome_remote_control/chrome_remote_control/android_browser_finder.py b/tools/chrome_remote_control/chrome_remote_control/android_browser_finder.py index 6c24202..8889883 100644 --- a/tools/chrome_remote_control/chrome_remote_control/android_browser_finder.py +++ b/tools/chrome_remote_control/chrome_remote_control/android_browser_finder.py @@ -6,8 +6,8 @@ import sys as real_sys import subprocess as real_subprocess import logging -import android_browser_backend -import android_commands as real_android_commands +import chrome_remote_control.android_browser_backend as android_browser_backend +import chrome_remote_control.adb_commands as real_adb_commands import browser import possible_browser @@ -15,7 +15,6 @@ import possible_browser """Finds android browsers that can be controlled by chrome_remote_control.""" ALL_BROWSER_TYPES = 'android-content-shell' -DEFAULT_BROWSER_TYPES_TO_RUN = 'android-content-shell' # Commmand line # content-shell: /data/local/tmp/content-shell-command-line @@ -40,7 +39,7 @@ CHROME_DEVTOOLS_REMOTE_PORT = 'localabstract:chrome_devtools-remote' CONTENT_SHELL_PACKAGE = 'org.chromium.content_shell' CONTENT_SHELL_ACTIVITY = '.ContentShellActivity' -CONTENT_SHELL_COMMAND_LINE = '/data/local/chrome-command-line' +CONTENT_SHELL_COMMAND_LINE = '/data/local/tmp/content-shell-command-line' CONTENT_SHELL_DEVTOOLS_REMOTE_PORT = ( 'localabstract:content_shell_devtools_remote') @@ -67,8 +66,10 @@ class PossibleAndroidBrowser(possible_browser.PossibleBrowser): def FindAllAvailableBrowsers(options, subprocess = real_subprocess, - android_commands = real_android_commands): + adb_commands = real_adb_commands): """Finds all the desktop browsers available on this machine.""" + if not adb_commands.IsAndroidSupported(): + return [] browsers = [] # See if adb even works. @@ -82,7 +83,7 @@ def FindAllAvailableBrowsers(options, device = None if not options.android_device: - devices = android_commands.GetAttachedDevices() + devices = adb_commands.GetAttachedDevices() else: devices = [] @@ -97,13 +98,13 @@ def FindAllAvailableBrowsers(options, device = devices[0] - adb = android_commands.AndroidCommands(device=device) + adb = adb_commands.ADBCommands(device=device) packages = adb.RunShellCommand('pm list packages') if 'package:' + CONTENT_SHELL_PACKAGE in packages: b = PossibleAndroidBrowser('android-content-shell', options, adb, - CONTENT_SHELL_PACKAGE, + CONTENT_SHELL_PACKAGE, True, CONTENT_SHELL_COMMAND_LINE, CONTENT_SHELL_ACTIVITY, CONTENT_SHELL_DEVTOOLS_REMOTE_PORT) diff --git a/tools/chrome_remote_control/chrome_remote_control/android_browser_finder_unittest.py b/tools/chrome_remote_control/chrome_remote_control/android_browser_finder_unittest.py index bcef902..be0c6e3 100644 --- a/tools/chrome_remote_control/chrome_remote_control/android_browser_finder_unittest.py +++ b/tools/chrome_remote_control/chrome_remote_control/android_browser_finder_unittest.py @@ -19,7 +19,7 @@ class StubSubprocess(object): raise Exception('Should not be reached.') return self.call_hook(*args, **kwargs) -class StubAndroidCommands(object): +class StubADBCommands(object): def __init__(self, module, device): self._module = module self._device = device @@ -31,14 +31,17 @@ class StubAndroidCommands(object): handler = self._module.shell_command_handlers[args[0]] return handler(args) -class StubAndroidCommandsModule(object): +class StubADBCommandsModule(object): def __init__(self): self.attached_devices = [] self.shell_command_handlers = {} - def StubAndroidCommandsConstructor(device=None): - return StubAndroidCommands(self, device) - self.AndroidCommands = StubAndroidCommandsConstructor + def StubADBCommandsConstructor(device=None): + return StubADBCommands(self, device) + self.ADBCommands = StubADBCommandsConstructor + + def IsAndroidSupported(self): + return True def GetAttachedDevices(self): return self.attached_devices @@ -60,9 +63,9 @@ class AndroidBrowserFinderTest(unittest.TestCase): subprocess_stub = StubSubprocess() subprocess_stub.call_hook = lambda *args, **kargs: 0 - android_commands_module_stub = StubAndroidCommandsModule() + adb_commands_module_stub = StubADBCommandsModule() browsers = android_browser_finder.FindAllAvailableBrowsers( - options, subprocess_stub, android_commands_module_stub) + options, subprocess_stub, adb_commands_module_stub) self.assertEquals(0, len(browsers)) def test_adb_two_devices(self): @@ -70,8 +73,8 @@ class AndroidBrowserFinderTest(unittest.TestCase): subprocess_stub = StubSubprocess() subprocess_stub.call_hook = lambda *args, **kargs: 0 - android_commands_module_stub = StubAndroidCommandsModule() - android_commands_module_stub.attached_devices = ['015d14fec128220c', + adb_commands_module_stub = StubADBCommandsModule() + adb_commands_module_stub.attached_devices = ['015d14fec128220c', '015d14fec128220d'] warnings = [] @@ -86,7 +89,7 @@ class AndroidBrowserFinderTest(unittest.TestCase): logger.addFilter(temp_filter) browsers = android_browser_finder.FindAllAvailableBrowsers( - options, subprocess_stub, android_commands_module_stub) + options, subprocess_stub, adb_commands_module_stub) finally: logger.removeFilter(temp_filter) self.assertEquals(1, len(warnings)) @@ -97,8 +100,8 @@ class AndroidBrowserFinderTest(unittest.TestCase): subprocess_stub = StubSubprocess() subprocess_stub.call_hook = lambda *args, **kargs: 0 - android_commands_module_stub = StubAndroidCommandsModule() - android_commands_module_stub.attached_devices = ['015d14fec128220c'] + adb_commands_module_stub = StubADBCommandsModule() + adb_commands_module_stub.attached_devices = ['015d14fec128220c'] def OnPM(args): assert args[0] == 'pm' @@ -107,8 +110,8 @@ class AndroidBrowserFinderTest(unittest.TestCase): return ['package:org.chromium.content_shell', 'package.com.google.android.setupwizard'] - android_commands_module_stub.shell_command_handlers['pm'] = OnPM + adb_commands_module_stub.shell_command_handlers['pm'] = OnPM browsers = android_browser_finder.FindAllAvailableBrowsers( - options, subprocess_stub, android_commands_module_stub) + options, subprocess_stub, adb_commands_module_stub) self.assertEquals(1, len(browsers)) diff --git a/tools/chrome_remote_control/chrome_remote_control/browser.py b/tools/chrome_remote_control/chrome_remote_control/browser.py index 925cec9..eb033ed 100644 --- a/tools/chrome_remote_control/chrome_remote_control/browser.py +++ b/tools/chrome_remote_control/chrome_remote_control/browser.py @@ -13,11 +13,11 @@ import browser_finder class Browser(object): """A running browser instance that can be controled in a limited way. - To create a browser instance, use browser_finder.FindBestPossibleBrowser. + To create a browser instance, use browser_finder.FindBrowser. Be sure to clean up after yourself by calling Close() when you are done with the browser. Or better yet: - browser_to_create = FindBestPossibleBrowser(options) + browser_to_create = FindBrowser(options) with browser_to_create.Create() as browser: ... do all your operations on browser here """ diff --git a/tools/chrome_remote_control/chrome_remote_control/browser_backend.py b/tools/chrome_remote_control/chrome_remote_control/browser_backend.py index bfe01a9..e041690 100644 --- a/tools/chrome_remote_control/chrome_remote_control/browser_backend.py +++ b/tools/chrome_remote_control/chrome_remote_control/browser_backend.py @@ -15,8 +15,8 @@ class BrowserGoneException(Exception): class BrowserBackend(object): """A base class for broser backends. Provides basic functionality once a remote-debugger port has been established.""" - def __init__(self): - pass + def __init__(self, is_content_shell): + self.is_content_shell = is_content_shell def __del__(self): self.Close() @@ -31,7 +31,10 @@ class BrowserBackend(object): return False else: return True - util.WaitFor(IsBrowserUp) + try: + util.WaitFor(IsBrowserUp, timeout=15) + except util.TimeoutException: + raise BrowserGoneException() def _ListTabs(self, timeout=None): if timeout: diff --git a/tools/chrome_remote_control/chrome_remote_control/browser_finder.py b/tools/chrome_remote_control/chrome_remote_control/browser_finder.py index e22635d..9588c35 100644 --- a/tools/chrome_remote_control/chrome_remote_control/browser_finder.py +++ b/tools/chrome_remote_control/chrome_remote_control/browser_finder.py @@ -1,6 +1,8 @@ # Copyright (c) 2012 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 logging + import android_browser_finder import desktop_browser_finder @@ -10,19 +12,42 @@ ALL_BROWSER_TYPES = ( desktop_browser_finder.ALL_BROWSER_TYPES + "," + android_browser_finder.ALL_BROWSER_TYPES) -DEFAULT_BROWSER_TYPES_TO_RUN = ( - desktop_browser_finder.DEFAULT_BROWSER_TYPES_TO_RUN + "," + - android_browser_finder.DEFAULT_BROWSER_TYPES_TO_RUN) +class BrowserTypeRequiredException(Exception): + pass -def FindBestPossibleBrowser(options): +def FindBrowser(options): """Finds the best PossibleBrowser object to run given the provided BrowserOptions object. The returned possiblity object can then be used to connect to and control the located browser.""" + if options.browser_type == 'exact' and options.browser_executable == None: + raise Exception("browser_type=exact requires browser_executable be set.") - browsers = FindAllPossibleBrowsers(options) - if len(browsers): - return browsers[0] - return None + if options.browser_type != 'exact' and options.browser_executable != None: + raise Exception("browser_executable requires browser_executable=exact.") + + if options.browser_type == None: + raise BrowserTypeRequiredException("browser_type must be specified") + + browsers = [] + browsers.extend(desktop_browser_finder.FindAllAvailableBrowsers(options)) + browsers.extend(android_browser_finder.FindAllAvailableBrowsers(options)) + + if options.browser_type == 'any': + if len(browsers) >= 1: + return browsers[0] + else: + return None + + matching_browsers = [b for b in browsers if b.type == options.browser_type] + + if len(matching_browsers) == 1: + return matching_browsers[0] + elif len(matching_browsers) > 1: + logging.warning('Multiple browsers of the same type found: %s' % ( + repr(matching_browsers))) + return matching_browsers[0] + else: + return None def GetAllAvailableBrowserTypes(options): """Returns an array of browser types supported on this system.""" @@ -36,10 +61,6 @@ def FindAllPossibleBrowsers(options): """Finds all browsers that can be created given the options. Returns an array of PossibleBrowser objects, sorted and filtered by options.browser_types_to_use.""" - browsers = [] - browsers.extend(desktop_browser_finder.FindAllAvailableBrowsers(options)) - browsers.extend(android_browser_finder.FindAllAvailableBrowsers(options)) - selected_browsers = [browser for browser in browsers if browser.type in options.browser_types_to_use] diff --git a/tools/chrome_remote_control/chrome_remote_control/browser_options.py b/tools/chrome_remote_control/chrome_remote_control/browser_options.py index 55c90ba..db4fc15 100644 --- a/tools/chrome_remote_control/chrome_remote_control/browser_options.py +++ b/tools/chrome_remote_control/chrome_remote_control/browser_options.py @@ -2,34 +2,37 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import optparse +import sys + import browser_finder -class BrowserOptions(object): +class BrowserOptions(optparse.Values): """Options to be used for disocvering and launching browsers.""" def __init__(self): + optparse.Values.__init__(self) self.dont_override_profile = False self.hide_stdout = True self.browser_executable = None - self._browser_types_to_use = ( - browser_finder.DEFAULT_BROWSER_TYPES_TO_RUN.split(",")) + self._browser_type = None self.chrome_root = None self.android_device = None self.extra_browser_args = [] def CreateParser(self, *args, **kwargs): parser = optparse.OptionParser(*args, **kwargs) + parser.add_option('--browser', + dest='browser_type', + default=None, + help='Browser type to run, ' + 'in order of priority. Supported values: %s' % + browser_finder.ALL_BROWSER_TYPES) parser.add_option('--dont-override-profile', action='store_true', dest='dont_override_profile', help='Uses the regular user profile instead of a clean one') parser.add_option('--browser-executable', dest='browser_executable', help='The exact browser to run.') - parser.add_option('--browser-types-to-use', - dest='browser_types_to_use', - help='Comma-separated list of browsers to run, ' - 'in order of priority. Possible values: %s' % - browser_finder.DEFAULT_BROWSER_TYPES_TO_RUN) parser.add_option('--chrome-root', dest='chrome_root', help='Where to look for chrome builds.' @@ -40,18 +43,21 @@ class BrowserOptions(object): 'If not specified, only 0 or 1 connected devcies are supported.') real_parse = parser.parse_args def ParseArgs(args=None): + defaults = parser.get_default_values() + for k, v in defaults.__dict__.items(): + if k in self.__dict__: + continue + self.__dict__[k] = v ret = real_parse(args, self) + if self.browser_executable and not self.browser_type: + self.browser_type = 'exact' + if not self.browser_executable and not self.browser_type: + sys.stderr.write("Must provide --browser=<type>\n") + sys.exit(1) return ret parser.parse_args = ParseArgs return parser - def __getattr__(self, name): - return None - - def __setattr__(self, name, value): - object.__setattr__(self, name, value) - return value - @property def browser_types_to_use(self): return self._browser_types_to_use diff --git a/tools/chrome_remote_control/chrome_remote_control/browser_options_unittest.py b/tools/chrome_remote_control/chrome_remote_control/browser_options_unittest.py index 520122e..d8e3d62 100644 --- a/tools/chrome_remote_control/chrome_remote_control/browser_options_unittest.py +++ b/tools/chrome_remote_control/chrome_remote_control/browser_options_unittest.py @@ -6,20 +6,47 @@ import unittest import browser_options class BrowserOptionsTest(unittest.TestCase): - def testDirectMutability(self): + def testDefaults(self): options = browser_options.BrowserOptions() - # Should be possible to add new fields to the options object. - options.x = 3 - self.assertEquals(3, options.x) + parser = options.CreateParser() + parser.add_option('-x', action='store', default=3) + parser.parse_args(['--browser', 'any']) + self.assertEquals(options.x, 3) + + def testDefaultsPlusOverride(self): + options = browser_options.BrowserOptions() + parser = options.CreateParser() + parser.add_option('-x', action='store', default=3) + parser.parse_args(['--browser', 'any', '-x', 10]) + self.assertEquals(options.x, 10) + + def testDefaultsDontClobberPresetValue(self): + options = browser_options.BrowserOptions() + setattr(options, 'x', 7) + parser = options.CreateParser() + parser.add_option('-x', action='store', default=3) + parser.parse_args(['--browser', 'any']) + self.assertEquals(options.x, 7) + + def testCount0(self): + options = browser_options.BrowserOptions() + parser = options.CreateParser() + parser.add_option('-v', action='count', dest='v') + parser.parse_args(['--browser', 'any']) + self.assertEquals(options.v, None) - # Unset fields on the options object should default to None. - self.assertEquals(None, options.y) + def testCount2(self): + options = browser_options.BrowserOptions() + parser = options.CreateParser() + parser.add_option('-v', action='count', dest='v') + parser.parse_args(['--browser', 'any', '-vv']) + self.assertEquals(options.v, 2) def testOptparseMutabilityWhenSpecified(self): options = browser_options.BrowserOptions() parser = options.CreateParser() - parser.add_option("-v", dest="verbosity", action="store_true") - options_ret, args = parser.parse_args(["-v"]) + parser.add_option('-v', dest='verbosity', action='store_true') + options_ret, args = parser.parse_args(['--browser', 'any', '-v']) self.assertEquals(options_ret, options) self.assertTrue(options.verbosity) @@ -27,7 +54,7 @@ class BrowserOptionsTest(unittest.TestCase): options = browser_options.BrowserOptions() parser = options.CreateParser() - parser.add_option("-v", dest="verbosity", action="store_true") - options_ret, args = parser.parse_args([]) + parser.add_option('-v', dest='verbosity', action='store_true') + options_ret, args = parser.parse_args(['--browser', 'any']) self.assertEquals(options_ret, options) self.assertFalse(options.verbosity) diff --git a/tools/chrome_remote_control/chrome_remote_control/browser_unittest.py b/tools/chrome_remote_control/chrome_remote_control/browser_unittest.py index c193059..0c4b8d2 100644 --- a/tools/chrome_remote_control/chrome_remote_control/browser_unittest.py +++ b/tools/chrome_remote_control/chrome_remote_control/browser_unittest.py @@ -10,7 +10,7 @@ class BrowserTest(unittest.TestCase): def testBasic(self): options = browser_options.options_for_unittests options.browser_to_use = browser_finder.ALL_BROWSER_TYPES - browser_to_create = browser_finder.FindBestPossibleBrowser(options) + browser_to_create = browser_finder.FindBrowser(options) if not browser_to_create: raise Exception('No browser found, cannot continue test.') with browser_to_create.Create() as b: @@ -18,3 +18,26 @@ class BrowserTest(unittest.TestCase): # Different browsers boot up to different things assert b.GetNthTabUrl(0) + + def testCommandLineOverriding(self): + # This test starts the browser with --enable-benchmarking, which should + # create a chrome.Interval namespace. This tests whether the command line is + # being set. + options = browser_options.options_for_unittests + options.browser_to_use = browser_finder.ALL_BROWSER_TYPES + testJS = ("window.chrome.gpuBenchmarking !== undefined ||" + + "chrome.Interval !== undefined") + + flag1 = "--enable-benchmarking" + flag2 = "--enable-gpu-benchmarking" + options.extra_browser_args.append(flag1) + options.extra_browser_args.append(flag2) + + try: + browser_to_create = browser_finder.FindBrowser(options) + with browser_to_create.Create() as b: + with b.ConnectToNthTab(0) as t: + self.assertTrue(t.runtime.Evaluate(testJS)) + finally: + options.extra_browser_args.remove(flag2) + options.extra_browser_args.remove(flag1) diff --git a/tools/chrome_remote_control/chrome_remote_control/desktop_browser_backend.py b/tools/chrome_remote_control/chrome_remote_control/desktop_browser_backend.py index d52f0c0..712dc07 100644 --- a/tools/chrome_remote_control/chrome_remote_control/desktop_browser_backend.py +++ b/tools/chrome_remote_control/chrome_remote_control/desktop_browser_backend.py @@ -11,6 +11,7 @@ import browser_backend import browser_finder import inspector_backend import tab +import util DEFAULT_PORT = 9273 @@ -18,8 +19,8 @@ class DesktopBrowserBackend(browser_backend.BrowserBackend): """The backend for controlling a locally-executed browser instance, on Linux, Mac or Windows. """ - def __init__(self, options, executable): - super(DesktopBrowserBackend, self).__init__() + def __init__(self, options, executable, is_content_shell): + super(DesktopBrowserBackend, self).__init__(is_content_shell) # Initialize fields so that an explosion during init doesn't break in Close. self._proc = None @@ -30,12 +31,12 @@ class DesktopBrowserBackend(browser_backend.BrowserBackend): if not self._executable: raise Exception("Cannot create browser, no executable found!") - self._tmpdir = tempfile.mkdtemp() self._port = DEFAULT_PORT args = [self._executable, "--no-first-run", "--remote-debugging-port=%i" % self._port] if not options.dont_override_profile: + self._tmpdir = tempfile.mkdtemp() args.append("--user-data-dir=%s" % self._tmpdir) args.extend(options.extra_browser_args) if options.hide_stdout: @@ -60,12 +61,34 @@ class DesktopBrowserBackend(browser_backend.BrowserBackend): def Close(self): if self._proc: + + def IsClosed(): + if not self._proc: + return True + return self._proc.poll() != None + + # Try to politely shutdown, first. self._proc.terminate() - self._proc.wait() - self._proc = None + try: + util.WaitFor(IsClosed, timeout=1) + self._proc = None + except util.TimeoutException: + pass - if os.path.exists(self._tmpdir): + # Kill it. + if not IsClosed(): + self._proc.kill() + try: + util.WaitFor(IsClosed, timeout=5) + self._proc = None + except util.TimeoutException: + self._proc = None + raise Exception("Could not shutdown the browser.") + + if self._tmpdir and os.path.exists(self._tmpdir): shutil.rmtree(self._tmpdir, ignore_errors=True) + self._tmpdir = None if self._devnull: self._devnull.close() + self._devnull = None diff --git a/tools/chrome_remote_control/chrome_remote_control/desktop_browser_finder.py b/tools/chrome_remote_control/chrome_remote_control/desktop_browser_finder.py index 76cbbc3..b4ca1a2 100644 --- a/tools/chrome_remote_control/chrome_remote_control/desktop_browser_finder.py +++ b/tools/chrome_remote_control/chrome_remote_control/desktop_browser_finder.py @@ -13,21 +13,21 @@ import possible_browser """Finds desktop browsers that can be controlled by chrome_remote_control.""" ALL_BROWSER_TYPES = "exact,release,debug,canary,system" -DEFAULT_BROWSER_TYPES_TO_RUN = "exact,release,canary,system" class PossibleDesktopBrowser(possible_browser.PossibleBrowser): """A desktop browser that can be controlled.""" - def __init__(self, type, options, executable): + def __init__(self, type, options, executable, is_content_shell): super(PossibleDesktopBrowser, self).__init__(type, options) self._local_executable = executable + self._is_content_shell = is_content_shell def __repr__(self): return "PossibleDesktopBrowser(type=%s)" % self.type def Create(self): backend = desktop_browser_backend.DesktopBrowserBackend( - self._options, self._local_executable) + self._options, self._local_executable, self._is_content_shell) return browser.Browser(backend) def FindAllAvailableBrowsers(options, @@ -46,7 +46,7 @@ def FindAllAvailableBrowsers(options, if options.browser_executable: if os.path.exists(options.browser_executable): browsers.append(PossibleDesktopBrowser("exact", options, - options.browser_executable)) + options.browser_executable, False)) # Look for a browser in the standard chrome build locations. if options.chrome_root: @@ -70,11 +70,13 @@ def FindAllAvailableBrowsers(options, debug_app = os.path.join(chrome_root, build_dir, "Debug", app_name) if os.path.exists(debug_app): - browsers.append(PossibleDesktopBrowser("debug", options, debug_app)) + browsers.append(PossibleDesktopBrowser("debug", options, + debug_app, False)) release_app = os.path.join(chrome_root, build_dir, "Release", app_name) if os.path.exists(release_app): - browsers.append(PossibleDesktopBrowser("release", options, release_app)) + browsers.append(PossibleDesktopBrowser("release", options, + release_app, False)) # Mac-specific options. if sys.platform == 'darwin': @@ -82,10 +84,12 @@ def FindAllAvailableBrowsers(options, "Contents/MacOS/Google Chrome Canary") mac_system = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" if os.path.exists(mac_canary): - browsers.append(PossibleDesktopBrowser("canary", options, mac_canary)) + browsers.append(PossibleDesktopBrowser("canary", options, + mac_canary, False)) if os.path.exists(mac_system): - browsers.append(PossibleDesktopBrowser("system", options, mac_system)) + browsers.append(PossibleDesktopBrowser("system", options, + mac_system, False)) # Linux specific options. if sys.platform.startswith('linux'): @@ -99,7 +103,8 @@ def FindAllAvailableBrowsers(options, pass if found: browsers.append( - PossibleDesktopBrowser("system", options, 'google-chrome')) + PossibleDesktopBrowser("system", options, + 'google-chrome', False)) # Win32-specific options. if sys.platform.startswith('win') and os.getenv('LOCALAPPDATA'): @@ -109,10 +114,12 @@ def FindAllAvailableBrowsers(options, win_system = os.path.join(local_app_data, 'Google\\Chrome\\Application\\chrome.exe') if os.path.exists(win_canary): - browsers.append(PossibleDesktopBrowser("canary", options, win_canary)) + browsers.append(PossibleDesktopBrowser("canary", options, + win_canary, False)) if os.path.exists(win_system): - browsers.append(PossibleDesktopBrowser("system", options, win_system)) + browsers.append(PossibleDesktopBrowser("system", options, + win_system, False)) if len(browsers) and not has_display: logging.warning('Found (%s), but you have a DISPLAY environment set.' % diff --git a/tools/chrome_remote_control/chrome_remote_control/desktop_browser_finder_unittest.py b/tools/chrome_remote_control/chrome_remote_control/desktop_browser_finder_unittest.py index 9eed53f..8cb8ea8 100644 --- a/tools/chrome_remote_control/chrome_remote_control/desktop_browser_finder_unittest.py +++ b/tools/chrome_remote_control/chrome_remote_control/desktop_browser_finder_unittest.py @@ -145,7 +145,6 @@ class LinuxFindTest(FindTestBase): self.assertEquals([], self.DoFindAllTypes()) def testFindUsingRelease(self): - self._options.browser_types_to_use.append("debug") self.assertTrue("release" in self.DoFindAllTypes()) class WinFindTest(FindTestBase): diff --git a/tools/chrome_remote_control/chrome_remote_control/inspector_backend.py b/tools/chrome_remote_control/chrome_remote_control/inspector_backend.py index e7889e5..ea85910 100644 --- a/tools/chrome_remote_control/chrome_remote_control/inspector_backend.py +++ b/tools/chrome_remote_control/chrome_remote_control/inspector_backend.py @@ -2,9 +2,10 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import json +import logging +import socket import time import websocket -import socket class InspectorException(Exception): pass @@ -15,7 +16,6 @@ class InspectorBackend(object): self._descriptor = descriptor self._socket = websocket.create_connection( descriptor["webSocketDebuggerUrl"]); - self._socket.settimeout(0.5) self._next_request_id = 0 self._domain_handlers = {} @@ -28,19 +28,44 @@ class InspectorBackend(object): self._socket = None self._backend = None - def SyncRequest(self, req): + def DispatchNotifications(self): + try: + data = self._socket.recv() + except socket.error: + return None + + res = json.loads(data) + logging.debug("got [%s]", data) + if "method" not in res: + return + + mname = res["method"] + dot_pos = mname.find(".") + domain_name = mname[:dot_pos] + if domain_name in self._domain_handlers: + try: + self._domain_handlers[domain_name][0](res) + except: + import traceback + traceback.print_exc() + + def SendAndIgnoreResponse(self, req): + req["id"] = self._next_request_id + self._next_request_id += 1 + self._socket.send(json.dumps(req)) + + def SyncRequest(self, req, timeout=60): + # TODO(nduca): Listen to the timeout argument + # self._socket.settimeout(timeout) req["id"] = self._next_request_id self._next_request_id += 1 self._socket.send(json.dumps(req)) + while True: - try: - data = self._socket.recv() - except socket.error: - req["id"] = self._next_request_id - self._next_request_id += 1 - self._socket.send(json.dumps(req)) - continue + data = self._socket.recv() + res = json.loads(data) + logging.debug("got [%s]", data) if "method" in res: mname = res["method"] dot_pos = mname.find(".") @@ -54,6 +79,7 @@ class InspectorBackend(object): continue if res["id"] != req["id"]: + logging.debug("Dropped reply: %s", json.dumps(res)) continue return res diff --git a/tools/chrome_remote_control/chrome_remote_control/run_tests.py b/tools/chrome_remote_control/chrome_remote_control/run_tests.py index 7f68ee9..40505dd 100644 --- a/tools/chrome_remote_control/chrome_remote_control/run_tests.py +++ b/tools/chrome_remote_control/chrome_remote_control/run_tests.py @@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import fnmatch +import logging import os import sys import traceback @@ -15,7 +16,6 @@ def Discover(start_dir, pattern = "test*.py", top_level_dir = None): pattern, top_level_dir) - # TODO(nduca): Do something with top_level_dir non-None modules = [] for (dirpath, dirnames, filenames) in os.walk(start_dir): for filename in filenames: @@ -28,7 +28,9 @@ def Discover(start_dir, pattern = "test*.py", top_level_dir = None): if filename.startswith('.') or filename.startswith('_'): continue name,ext = os.path.splitext(filename) - fqn = dirpath.replace('/', '.') + '.' + name + + relpath = os.path.relpath(dirpath, top_level_dir) + fqn = relpath.replace('/', '.') + '.' + name # load the module try: @@ -67,43 +69,53 @@ def FilterSuite(suite, predicate): return new_suite -def DiscoverAndRunTests(args): - dir_name = os.path.join(os.path.dirname(__file__), "..") - olddir = os.getcwd() - try: - os.chdir(dir_name) - suite = Discover("chrome_remote_control", "*_unittest.py", ".") +def DiscoverAndRunTests(dir_name, args, top_level_dir): + suite = Discover(dir_name, "*_unittest.py", top_level_dir) - def IsTestSelected(test): - if len(args) == 0: + def IsTestSelected(test): + if len(args) == 0: + return True + for name in args: + if str(test).find(name) != -1: return True - for name in args: - if str(test).find(name) != -1: - return True - return False + return False - filtered_suite = FilterSuite(suite, IsTestSelected) - runner = unittest.TextTestRunner(verbosity = 2) - test_result = runner.run(filtered_suite) - return len(test_result.errors) + len(test_result.failures) + filtered_suite = FilterSuite(suite, IsTestSelected) + runner = unittest.TextTestRunner(verbosity = 2) + test_result = runner.run(filtered_suite) + return len(test_result.errors) + len(test_result.failures) - finally: - os.chdir(olddir) - return 1 - -def Main(args): +def Main(args, start_dir, top_level_dir): """Unit test suite that collects all test cases for chrome_remote_control.""" default_options = browser_options.BrowserOptions() + default_options.browser_type = 'any' + parser = default_options.CreateParser("run_tests [options] [test names]") + parser.add_option( + '-v', '--verbose', action='count', dest="verbosity", + help='Increase verbosity level (repeat as needed)') + parser.add_option('--repeat-count', dest='run_test_repeat_count', + type='int', default=1, + help="Repeats each a provided number of times.") + _, args = parser.parse_args(args) + if default_options.verbosity >= 2: + logging.basicConfig(level=logging.DEBUG) + elif default_options.verbosity: + logging.basicConfig(level=logging.INFO) + else: + logging.basicConfig(level=logging.WARNING) + browser_options.options_for_unittests = default_options + olddir = os.getcwd() + num_errors = 0 try: - DiscoverAndRunTests(args) + os.chdir(top_level_dir) + for i in range(default_options.run_test_repeat_count): + num_errors += DiscoverAndRunTests(start_dir, args, top_level_dir) finally: + os.chdir(olddir) browser_options.options_for_unittests = None - - -if __name__ == "__main__": - sys.exit(Main(sys.argv[1:])) + return num_errors diff --git a/tools/chrome_remote_control/chrome_remote_control/tab.py b/tools/chrome_remote_control/chrome_remote_control/tab.py index d4766f6..976eee0 100644 --- a/tools/chrome_remote_control/chrome_remote_control/tab.py +++ b/tools/chrome_remote_control/chrome_remote_control/tab.py @@ -6,18 +6,25 @@ import websocket import socket import time +import tab_page import tab_runtime import util +DEFAULT_TAB_TIMEOUT=60 + class Tab(object): def __init__(self, inspector_backend): self._inspector_backend = inspector_backend + self.page = tab_page.TabPage(self._inspector_backend) self.runtime = tab_runtime.TabRuntime(self._inspector_backend) + def __del__(self): self.Close() def Close(self): + self.page = None + self.runtime = None if self._inspector_backend: self._inspector_backend.Close() self._inspector_backend = None @@ -28,31 +35,13 @@ class Tab(object): def __exit__(self, *args): self.Close() - def BeginToLoadUrl(self, url): - # In order to tell when the document has actually changed, - # we go to about:blank first and wait. When that has happened, we - # to go the new URL and detect the document being non-about:blank as - # indication that the new document is loading. - self.runtime.Evaluate('document.location = "about:blank";') - util.WaitFor(lambda: - self.runtime.Evaluate('document.location.href') == 'about:blank') - - self.runtime.Evaluate('document.location = "%s";' % url) - util.WaitFor(lambda: - self.runtime.Evaluate('document.location.href') != 'about:blank') - - def LoadUrl(self, url): - self.BeginToLoadUrl(url) - # TODO(dtu): Detect HTTP redirects. - time.sleep(2) # Wait for unpredictable redirects. - self.WaitForDocumentReadyStateToBeInteractiveOrBetter() - - def WaitForDocumentReadyStateToBeComplete(self): + def WaitForDocumentReadyStateToBeComplete(self, timeout=60): util.WaitFor( - lambda: self.runtime.Evaluate('document.readyState') == 'complete') + lambda: self.runtime.Evaluate('document.readyState') == 'complete', + timeout) - def WaitForDocumentReadyStateToBeInteractiveOrBetter(self): + def WaitForDocumentReadyStateToBeInteractiveOrBetter(self, timeout=60): def IsReadyStateInteractiveOrBetter(): rs = self.runtime.Evaluate('document.readyState') return rs == 'complete' or rs == 'interactive' - util.WaitFor(IsReadyStateInteractiveOrBetter) + util.WaitFor(IsReadyStateInteractiveOrBetter, timeout) diff --git a/tools/chrome_remote_control/chrome_remote_control/tab_page.py b/tools/chrome_remote_control/chrome_remote_control/tab_page.py new file mode 100644 index 0000000..fbf7dea --- /dev/null +++ b/tools/chrome_remote_control/chrome_remote_control/tab_page.py @@ -0,0 +1,67 @@ +# Copyright (c) 2012 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 json +import logging +import urlparse + +import inspector_backend +import util + +class TabPage(object): + def __init__(self, inspector_backend): + self._inspector_backend = inspector_backend + self._inspector_backend.RegisterDomain( + 'Page', + self._OnNotification, + self._OnClose) + self._pending_navigate_url = None + + def _OnNotification(self, msg): + logging.debug("Notification: %s", json.dumps(msg, indent=2)) + if msg["method"] == "Page.frameNavigated" and self._pending_navigate_url: + url = msg["params"]["frame"]["url"] + if not url == "chrome://newtab/": + # Marks the navigation as complete and unblocks the navigate call. + self._pending_navigate_url = None + + def _OnClose(self): + pass + + def Navigate(self, url, timeout=60): + """Navigates to url""" + # Turn on notifications. We need them to get the Page.frameNavigated event. + request = { + "method": "Page.enable" + } + res = self._inspector_backend.SyncRequest(request, timeout) + assert len(res["result"].keys()) == 0 + + # Navigate the page. However, there seems to be a bug in chrome devtools + # protocol where the request id for this event gets held on the browser side + # pretty much indefinitely. + # + # So, instead of waiting for the event to actually complete, wait for the + # Page.frameNavigated event. + request = { + "method": "Page.navigate", + "params": { + "url": url, + } + } + res = self._inspector_backend.SendAndIgnoreResponse(request) + + self._pending_navigate_url = url + def IsNavigationDone(): + self._inspector_backend.DispatchNotifications() + return self._pending_navigate_url == None + + util.WaitFor(IsNavigationDone, timeout) + + # Turn off notifications. + request = { + "method": "Page.disable" + } + res = self._inspector_backend.SyncRequest(request, timeout) + assert len(res["result"].keys()) == 0 + diff --git a/tools/chrome_remote_control/chrome_remote_control/tab_page_unittest.py b/tools/chrome_remote_control/chrome_remote_control/tab_page_unittest.py new file mode 100644 index 0000000..3586c06 --- /dev/null +++ b/tools/chrome_remote_control/chrome_remote_control/tab_page_unittest.py @@ -0,0 +1,43 @@ +# Copyright (c) 2012 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 browser +import browser_finder +import browser_options +import tab_page +import unittest + +class TabPageTest(unittest.TestCase): + def setUp(self): + self._browser = None + self._tab = None + options = browser_options.options_for_unittests + browser_to_create = browser_finder.FindBrowser(options) + if not browser_to_create: + raise Exception('No browser found, cannot continue test.') + try: + self._browser = browser_to_create.Create() + self._tab = self._browser.ConnectToNthTab(0) + except: + self.tearDown() + raise + + def tearDown(self): + if self._tab: + self._tab.Close() + if self._browser: + self._browser.Close() + + def testPageNavigateToNormalUrl(self): + res = self._tab.page.Navigate("http://www.google.com") + self._tab.WaitForDocumentReadyStateToBeComplete() + + def testPageNavigateToUrlChanger(self): + # The Url that we actually load is http://www.youtube.com/. + res = self._tab.page.Navigate("http://youtube.com/") + + self._tab.WaitForDocumentReadyStateToBeComplete() + + def testPageNavigateToImpossibleURL(self): + res = self._tab.page.Navigate("http://23f09f0f9fsdflajsfaldfkj2f3f.com") + self._tab.WaitForDocumentReadyStateToBeComplete() diff --git a/tools/chrome_remote_control/chrome_remote_control/tab_runtime.py b/tools/chrome_remote_control/chrome_remote_control/tab_runtime.py index de73892..827e1c28 100644 --- a/tools/chrome_remote_control/chrome_remote_control/tab_runtime.py +++ b/tools/chrome_remote_control/chrome_remote_control/tab_runtime.py @@ -20,14 +20,14 @@ class TabRuntime(object): def _OnClose(self): pass - def Execute(self, expr): + def Execute(self, expr, timeout=60): """Executes expr If the expression failed to evaluate, EvaluateException will be raised. """ - self.Evaluate(expr + "; 0;"); + self.Evaluate(expr + "; 0;", timeout=60); - def Evaluate(self, expr): + def Evaluate(self, expr, timeout=60): """Evalutes expr and returns the JSONized result. Consider using Execute for cases where the result of the expression is not @@ -46,7 +46,7 @@ class TabRuntime(object): "returnByValue": True } } - res = self._inspector_backend.SyncRequest(request) + res = self._inspector_backend.SyncRequest(request, timeout) if "error" in res: raise EvaluateException(res["error"]["message"]) diff --git a/tools/chrome_remote_control/chrome_remote_control/tab_runtime_unittest.py b/tools/chrome_remote_control/chrome_remote_control/tab_runtime_unittest.py index a43a327..65f3a3a 100644 --- a/tools/chrome_remote_control/chrome_remote_control/tab_runtime_unittest.py +++ b/tools/chrome_remote_control/chrome_remote_control/tab_runtime_unittest.py @@ -12,7 +12,7 @@ class TabRuntimeTest(unittest.TestCase): self._browser = None self._tab = None options = browser_options.options_for_unittests - browser_to_create = browser_finder.FindBestPossibleBrowser(options) + browser_to_create = browser_finder.FindBrowser(options) if not browser_to_create: raise Exception('No browser found, cannot continue test.') try: @@ -37,13 +37,18 @@ class TabRuntimeTest(unittest.TestCase): lambda: self._tab.runtime.Evaluate("fsdfsdfsf")) def testRuntimeEvaluateOfSomethingThatCantJSONize(self): - # TODO(nduca): This fails on Android. I wonder why? - self.assertRaises(tab_runtime.EvaluateException, - lambda: self._tab.runtime.Evaluate("window")) - pass + + def test(): + self._tab.runtime.Evaluate(""" + var cur = {} + var root = {next:cur}; + for(var i = 0; i < 1000; i++) { + next = {}; + cur.next = next; + cur = next; + } + root;""") + self.assertRaises(tab_runtime.EvaluateException, test) def testRuntimeExecuteOfSomethingThatCantJSONize(self): self._tab.runtime.Execute("window"); - - def testRuntimeLoadUrl(self): - self._tab.BeginToLoadUrl("http://www.google.com") diff --git a/tools/chrome_remote_control/chrome_remote_control/tab_unittest.py b/tools/chrome_remote_control/chrome_remote_control/tab_unittest.py index c5e1bef..289efe0 100644 --- a/tools/chrome_remote_control/chrome_remote_control/tab_unittest.py +++ b/tools/chrome_remote_control/chrome_remote_control/tab_unittest.py @@ -12,7 +12,7 @@ class TabTest(unittest.TestCase): self._browser = None self._tab = None options = browser_options.options_for_unittests - browser_to_create = browser_finder.FindBestPossibleBrowser(options) + browser_to_create = browser_finder.FindBrowser(options) if not browser_to_create: raise Exception('No browser found, cannot continue test.') try: @@ -28,10 +28,10 @@ class TabTest(unittest.TestCase): if self._browser: self._browser.Close() - def testLoadUrlAndWaitToForCompleteState(self): - self._tab.BeginToLoadUrl("http://www.google.com") + def testNavigateAndWaitToForCompleteState(self): + self._tab.page.Navigate("http://www.google.com") self._tab.WaitForDocumentReadyStateToBeComplete() - def testLoadUrlAndWaitToForInteractiveState(self): - self._tab.BeginToLoadUrl("http://www.google.com") + def testNavigateAndWaitToForInteractiveState(self): + self._tab.page.Navigate("http://www.google.com") self._tab.WaitForDocumentReadyStateToBeInteractiveOrBetter() diff --git a/tools/chrome_remote_control/chrome_remote_control/util.py b/tools/chrome_remote_control/chrome_remote_control/util.py index ec2df2b..5ea4fbd 100644 --- a/tools/chrome_remote_control/chrome_remote_control/util.py +++ b/tools/chrome_remote_control/chrome_remote_control/util.py @@ -4,31 +4,18 @@ import inspect import time -_timeout = 60 - class TimeoutException(Exception): pass -class TimeoutChanger(object): - def __init__(self, new_timeout): - self._timeout = new_timeout - - def __enter__(self): - _timeout, self._timeout = self._timeout, _timeout - return self - - def __exit__(self, *args): - _timeout = self._timeout - -def WaitFor(condition): +def WaitFor(condition, timeout): assert isinstance(condition, type(lambda: None)) # is function start_time = time.time() while not condition(): - if time.time() - start_time > _timeout: + if time.time() - start_time > timeout: if condition.__name__ == '<lambda>': condition_string = inspect.getsource(condition).strip() else: condition_string = condition.__name__ raise TimeoutException('Timed out while waiting %ds for %s.' % - (_timeout, condition_string)) + (timeout, condition_string)) time.sleep(0.01) diff --git a/tools/chrome_remote_control/chrome_remote_control/util_unittest.py b/tools/chrome_remote_control/chrome_remote_control/util_unittest.py new file mode 100644 index 0000000..7050975 --- /dev/null +++ b/tools/chrome_remote_control/chrome_remote_control/util_unittest.py @@ -0,0 +1,17 @@ +# Copyright (c) 2012 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 unittest + +import util + +class TestWait(unittest.TestCase): + def testNonTimeout(self): + def test(): + return True + util.WaitFor(test, 0.1) + + def testTimeout(self): + def test(): + return False + self.assertRaises(util.TimeoutException, lambda: util.WaitFor(test, 0.1)) diff --git a/tools/chrome_remote_control/chrome_remote_control/websocket.py b/tools/chrome_remote_control/chrome_remote_control/websocket.py index 6f31fb9..99c5b84 100644 --- a/tools/chrome_remote_control/chrome_remote_control/websocket.py +++ b/tools/chrome_remote_control/chrome_remote_control/websocket.py @@ -12,4 +12,6 @@ def __init__(): sys.path.append(ws_path) __init__() + from websocket import * + diff --git a/tools/chrome_remote_control/examples/rendering_microbenchmark_test.py b/tools/chrome_remote_control/examples/rendering_microbenchmark_test.py index 000032c..3fa7976 100755 --- a/tools/chrome_remote_control/examples/rendering_microbenchmark_test.py +++ b/tools/chrome_remote_control/examples/rendering_microbenchmark_test.py @@ -29,7 +29,7 @@ def Main(args): urls.append(url) options.extra_browser_args.append("--enable-gpu-benchmarking") - browser_to_create = chrome_remote_control.FindBestPossibleBrowser(options) + browser_to_create = chrome_remote_control.FindBrowser(options) if not browser_to_create: sys.stderr.write("No browser found! Supported types: %s" % chrome_remote_control.GetAllAvailableBrowserTypes()) @@ -37,7 +37,8 @@ def Main(args): with browser_to_create.Create() as b: with b.ConnectToNthTab(0) as tab: # Check browser for benchmark API. Can only be done on non-chrome URLs. - tab.BeginToLoadUrl("http://www.google.com") + tab.page.Navigate("http://www.google.com") + import time; time.sleep(2) tab.WaitForDocumentReadyStateToBeComplete() if tab.runtime.Evaluate("window.chrome.gpuBenchmarking === undefined"): print "Browser does not support gpu benchmarks API." @@ -63,7 +64,7 @@ def Main(args): print ",".join(cols) for u in urls: - tab.BeginToLoadUrl(u) + tab.page.Navigate(u) tab.WaitForDocumentReadyStateToBeInteractiveOrBetter() results = tab.runtime.Evaluate( "window.chrome.gpuBenchmarking.runRenderingBenchmarks();") diff --git a/tools/chrome_remote_control/examples/top1k b/tools/chrome_remote_control/examples/top1k index a11f26e..f32a38c 100644 --- a/tools/chrome_remote_control/examples/top1k +++ b/tools/chrome_remote_control/examples/top1k @@ -1,12 +1,12 @@ youtube.com -yahoo.com -baidu.com wikipedia.org live.com twitter.com +baidu.com qq.com amazon.com blogspot.com +yahoo.com linkedin.com taobao.com yahoo.co.jp diff --git a/tools/chrome_remote_control/run_tests b/tools/chrome_remote_control/run_tests index 4775c69..36b4198 100755 --- a/tools/chrome_remote_control/run_tests +++ b/tools/chrome_remote_control/run_tests @@ -1,5 +1,16 @@ -#!/bin/sh +#!/usr/bin/env python # Copyright (c) 2012 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. -/usr/bin/env python -m chrome_remote_control.run_tests $* +import os +import sys + +import chrome_remote_control.run_tests + +if __name__ == "__main__": + top_level_dir = os.path.abspath( + os.path.dirname(__file__)) + start_dir = "chrome_remote_control" + ret = chrome_remote_control.run_tests.Main( + sys.argv[1:], start_dir, top_level_dir) + sys.exit(ret) diff --git a/tools/gpu/data/urls.txt b/tools/gpu/data/urls.txt deleted file mode 100644 index 051228b..0000000 --- a/tools/gpu/data/urls.txt +++ /dev/null @@ -1,218 +0,0 @@ -http://www.facebook.com/barackobama -https://www.google.com/search?q=barack%20obama -http://youtube.com -http://yahoo.com -http://www.baidu.com/s?wd=barack+obama -http://en.wikipedia.org/wiki/Wikipedia -http://qq.com -http://www.amazon.com/Kindle-Fire-Amazon-Tablet/dp/B0051VVOB2 -http://googleblog.blogspot.com/ -http://taobao.com -http://www.linkedin.com/in/linustorvalds -http://yahoo.co.jp -http://sina.com.cn -http://msn.com -http://yandex.ru/yandsearch?text=barack+obama -http://translation.babylon.com/ -http://www.bing.com/search?q=barack+obama -http://wordpress.org/news/ -http://www.ebay.com/sch/i.html?_nkw=antiques -http://163.com -http://www.soso.com/q?w=barack+obama -http://www.microsoft.com/en-us/default.aspx -http://go.mail.ru/search?mailru=1&mg=1&q=barack+obama -http://vk.com/id118712387 -http://staff.tumblr.com/ -http://sohu.com -http://sfbay.craigslist.org/mis/ -http://www.ask.com/web?q=barack+obama&search=&qsrc=0&o=0&l=dir -http://www.apple.com/ipodtouch/ -http://blog.pinterest.com/ -http://pinterest.com/backdrophome/ -http://paypal.com -http://bbc.co.uk -http://www.avg.com/us-en/avg-premium-security -http://googlesystem.blogspot.com/ -http://tudou.com -http://blog.fc2.com/en/ -http://imdb.com -http://youku.com -http://www.flickr.com/photos/thomashawk/ -http://www.flickr.com/photos/thomashawk/sets/72157600284219965/detail/ -http://search.yahoo.com/search?ei=UTF-8&trackingType=go_search_home&p=barack+obama&fr=hsusgo1&sa.x=0&sa.y=0 -http://www.conduit.com/ -http://ifeng.com -http://tmall.com -http://hao123.com -http://aol.com -http://zedo.com -http://search.mywebsearch.com/mywebsearch/GGmain.jhtml?searchfor=barack+obama -http://cnn.com -http://portal.ebay.de/deutschland-schraubt-angebote -http://www.adobe.com/products/photoshopfamily.html?promoid=JOLIW -http://global.rakuten.com/us/ -http://laundry.about.com/od/kidsandlaundry/f/How-Do-I-Wash-A-Backpack.htm -http://thepiratebay.se/search/barack%20obama/0/99/0 -http://360buy.com -http://huffingtonpost.com -http://alibaba.com -http://chinaz.com -http://www.sogou.com/web?query=barack+obama -http://www.amazon.de/gp/product/B0051QVF7A/ref=amb_link_170625867_1/275-4711375-4099801?ie=UTF8&nav_sdd=aps&pf_rd_m=A3JWKAKR8XB7XF&pf_rd_s=center-1&pf_rd_r=1C0XDBPB12WHDM63V11R&pf_rd_t=101&pf_rd_p=320475427&pf_rd_i=301128 -http://google.pl -http://mediafire.com -http://espn.go.com -http://uol.com.br -http://www.godaddy.com/products/secure-hosting.aspx?ci=72738 -http://imgur.com/gallery/b90ZE -http://home.alipay.com/bank/paymentKJ.htm -http://amazon.co.jp -http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array -http://www.google.com/doubleclick/ -http://search.4shared.com/q/CCAD/1/barack%20obama -http://dailymotion.com -http://globo.com -http://instagram.com/developer/ -http://livedoor.com -http://wordpress.org/showcase/ -http://bp.blogspot.com -http://wigetmedia.com/advertisers -http://www.search-results.com/web?&q=barack%20obama -http://cnet.com -http://nytimes.com -http://torrentz.eu/search?f=barack+obama -http://livejournal.com -http://douban.com -http://www.weather.com/weather/right-now/Mountain+View+CA+94043 -http://dailymail.co.uk -http://www.tianya.cn/bbs/index.shtml -http://ehow.com -http://theproject.badoo.com/final.phtml -http://www.bankofamerica.com/deposits/checksave/index.cfm?template=check_eBanking -http://vimeo.com -http://360.cn -http://indiatimes.com -http://deviantart.com -http://reddit.com -http://aweber.com -http://warriorforum.com -http://spiegel.de -http://pconline.com.cn -http://mozilla.org -http://booking.com -http://goo.ne.jp -https://www.chase.com/online/Home-Lending/mortgages.htm -http://addthis.com -http://56.com -http://news.blogfa.com/ -http://www.stumbleupon.com/jobs -https://www.dropbox.com/about -http://www.clicksor.com/publishers/adformat -http://answers.com -http://en.softonic.com/ -http://walmart.com -http://pengyou.com -http://outbrain.com -http://comcast.net -http://foxnews.com -http://photobucket.com/findstuff/photography%20styles/ -http://bleach.wikia.com/?redirect=no -http://sourceforge.net/projects/xoops/?source=frontpage&position=1 -http://onet.pl -http://guardian.co.uk -https://www.wellsfargo.com/jump/enterprise/doublediscount?msc=5589&mplx=10918-70119-3408-64 -http://wikimediafoundation.org/wiki/Home -http://xunlei.com -http://as.58.com/shuma/ -http://skype.com -http://etsy.com -http://bild.de -http://search.naver.com/search.naver?where=nexearch&query=barack+obama&sm=top_hty&fbm=0&ie=utf8 -http://statcounter.com/features/?PHPSESSID=bbjcvjr681bcul4vqvgq2qgmo7 -http://iqiyi.com -http://fbcdn.net -http://www.myspace.com/browse/people -http://allegro.pl/antyki-i-sztuka -http://yesky.com -http://justbeenpaid.com -http://adultfriendfinder.com -http://fiverr.com -http://www.leboncoin.fr/annonces/offres/centre/ -http://dictionary.reference.com/ -http://realtime.rediff.com/instasearch#!barack%20obama -http://zol.com.cn -http://optmd.com -http://www.filestube.com/search.html?q=barack+obama&select=All -http://xinhuanet.com -http://www.salesforce.com/sales-cloud/overview/ -http://www.squidoo.com/make-cards-and-gift-bags-with-antique-photos -http://www.domaintools.com/research/ -http://download.cnet.com/windows/?tag=hdr;brandnav -https://rapidshare.com/#!shop -http://people.com.cn -http://ucoz.ru -http://free.fr -http://nicovideo.jp -http://www.yelp.com/search?find_desc=food&find_loc=San+Jose%2C+CA&ns=1 -http://slideshare.net -http://archive.org/web/web.php -http://www.cntv.cn/index.shtml -http://english.cntv.cn/01/index.shtml -http://abonnez-vous.orange.fr/residentiel/accueil/accueil.aspx -http://v.it168.com/ -http://nbcolympics.com -http://hootsuite.com -http://www.scribd.com/doc/52210329/The-Masters-Augusta-National-s-Amen-Corner-up-close -http://themeforest.net -http://4399.com -http://www.soku.com/v?keyword=barack%20obama -http://google.se -http://funmoods.com -http://csdn.net -http://telegraph.co.uk -http://taringa.net -http://www.tripadvisor.com/Tourism-g32701-Mendocino_California-Vacations.html -http://pof.com -http://wp.pl -http://soundcloud.com/flosstradamus/tracks -http://w3schools.com/html/default.asp -http://ameblo.jp/staff/ -http://wsj.com -http://web.de -http://sweetim.com -http://rambler.ru -http://gmx.net -http://www.indeed.com/jobs?q=software&l=Mountain+View%2C+CA -http://ilivid.com -http://www.xing.com/search/people?search%5Bq%5D=lufthansa -http://reuters.com -http://hostgator.com -http://www.ikea.com/us/en/catalog/categories/departments/living_room/ -http://www.kaixin001.com/award2012/wenming/index.php -http://ku6.com -http://libero.it -http://samsung.com -http://hudong.com -http://espncricinfo.com -http://china.com -http://www.ups.com/content/us/en/bussol/browse/smallbiz/new-to-ups.html?WT.svl=SolExp -http://letv.com -http://ero-advertising.com -http://mashable.com -http://iminent.com -http://rutracker.org -http://www.shopping.hp.com/en_US/home-office/-/products/Laptops/Laptops -http://www.clickbank.com/buy_products.htm?dores=true&mainCategoryId=1340&sortField=POPULARITY&b1=1340 -http://b.hatena.ne.jp/ -http://www.youdao.com/search?q=barack+obama&ue=utf8&keyfrom=web.index -http://forbes.com -http://nbcnews.com -http://bitauto.com -http://php.net -http://www.target.com/c/women/-/N-5xtd3#?lnk=nav_t_spc_1_0 -http://dianxin.cn -http://www.aizhan.com/siteall/www.youboy.com/ -http://veiculos-home.mercadolivre.com.br/ -http://kakaku.com -http://flipkart.com -http://paipai.com diff --git a/tools/gpu/gpu_tools/__init__.py b/tools/gpu/gpu_tools/__init__.py new file mode 100644 index 0000000..ec38091 --- /dev/null +++ b/tools/gpu/gpu_tools/__init__.py @@ -0,0 +1,16 @@ +# Copyright (c) 2012 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. +"""A library for Chrome GPU test code.""" +import os +import sys + +def Init(): + crc_path = os.path.join(os.path.dirname(__file__), + '..', '..', 'chrome_remote_control') + absolute_crc_path = os.path.abspath(crc_path) + sys.path.append(absolute_crc_path) + + import chrome_remote_control + +Init() diff --git a/tools/gpu/pylib/file_server.py b/tools/gpu/gpu_tools/file_server.py index 4056619..0f2bafc 100644 --- a/tools/gpu/pylib/file_server.py +++ b/tools/gpu/gpu_tools/file_server.py @@ -6,6 +6,8 @@ import subprocess class FileServer(object): def __init__(self, path, port=8000): + self._server = None + self._devnull = None assert os.path.exists(path) if os.path.isdir(path): self._path = path @@ -13,16 +15,30 @@ class FileServer(object): self._path = os.path.dirname(path) self._port = port + def __enter__(self): + self._devnull = open(os.devnull, 'w') self._server = subprocess.Popen( ['python', '-m', 'SimpleHTTPServer', str(self._port)], - cwd=self._path) + cwd=self._path, + stdout=self._devnull, stderr=self._devnull) + return self + + @property + def url(self): return 'http://localhost:%d' % self._port def __exit__(self, *args): - self._server.kill() - self._server = None + if self._server: + self._server.kill() + self._server = None + if self._devnull: + self._devnull.close() + self._devnull = None def __del__(self): if self._server: self._server.kill() + if self._devnull: + self._devnull.close() + self._devnull = None diff --git a/tools/gpu/gpu_tools/page_scroller.py b/tools/gpu/gpu_tools/page_scroller.py new file mode 100644 index 0000000..0dbb909 --- /dev/null +++ b/tools/gpu/gpu_tools/page_scroller.py @@ -0,0 +1,50 @@ +# Copyright (c) 2012 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. + +# The test takes a list of URLs through stdin and prints results in CSV format. +# Example: python run_scroll_test.py < data/urls.txt > test_results.csv +import csv +import logging +import os +import sys + +import chrome_remote_control + +import scroll +import page_set +import url_test + + +def Main(args): + options = chrome_remote_control.BrowserOptions() + parser = options.CreateParser("page_scroller <pageset>") + _, args = parser.parse_args(args) + if len(args) != 1: + parser.print_usage() + import page_sets + sys.stderr.write("Available pagesets:\n%s\n\n" % ",\n".join( + [os.path.relpath(f) for f in page_sets.GetAllPageSetFilenames()])) + sys.exit(1) + + ps = page_set.PageSet() + ps.LoadFromFile(args[0]) + return Run(sys.stdout, options, ps) + +def Run(output_stream, options, ps): + results_writer = csv.writer(output_stream) + results_writer.writerow(['URL', 'FPS', 'Mean Frame Time (s)', + '% Dropped Frames', 'First Paint Time (s)']) + possible_browser = chrome_remote_control.FindBrowser(options) + with possible_browser.Create() as b: + for result in url_test.UrlTest(b, scroll.Scroll, ps): + if not result.DidScroll(): + # Most likely the page was shorter than the window height. + logging.warning('Page did not scroll: %s', result.GetUrl()) + continue + + results_writer.writerow([result.GetUrl(), result.GetFps(0), + result.GetMeanFrameTime(0), + result.GetPercentBelow60Fps(0), + result.GetFirstPaintTime()]) + sys.stdout.flush() diff --git a/tools/gpu/gpu_tools/page_scroller_unittest.py b/tools/gpu/gpu_tools/page_scroller_unittest.py new file mode 100644 index 0000000..1129223 --- /dev/null +++ b/tools/gpu/gpu_tools/page_scroller_unittest.py @@ -0,0 +1,39 @@ +# Copyright (c) 2012 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 csv +import os +import unittest +import StringIO + +import file_server +import page_set +import page_scroller +import tempfile + +import chrome_remote_control.browser_options + +class ScrollTestUnitTest(unittest.TestCase): + + def testBasicFunctionality(self): + scrollable_page_path = os.path.join( + os.path.dirname(__file__), + '..', 'unittest_data', 'scrollable_page.html') + + result_count = 0 + output = StringIO.StringIO() + with file_server.FileServer(scrollable_page_path) as server: + url = '%s/%s' % (server.url, os.path.basename(scrollable_page_path)) + + ps = page_set.PageSet() + ps.pages.append(page_set.Page(url)) + options = chrome_remote_control.browser_options.options_for_unittests + page_scroller.Run(output, options, ps) + + output.seek(0) + raw_rows = list(csv.reader(output)) + + header = raw_rows[0] + results = raw_rows[1:] + self.assertEqual(len(results), 1) + self.assertEqual(results[0][0], url, 'Did not navigate to right URL.') diff --git a/tools/gpu/gpu_tools/page_set.py b/tools/gpu/gpu_tools/page_set.py new file mode 100644 index 0000000..0fedb44 --- /dev/null +++ b/tools/gpu/gpu_tools/page_set.py @@ -0,0 +1,37 @@ +# Copyright (c) 2012 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 json +import re + +class Page(object): + def __init__(self, url=None): + self.url = url + + def __str__(self): + return self.url + +class PageSet(object): + def __init__(self): + self.pages = [] + self.description = "" + + def LoadFromFile(self, page_set_filename): + with open(page_set_filename, 'r') as f: + contents = f.read() + data = json.loads(contents) + self.LoadFromDict(data) + + def LoadFromDict(self, data): + self.description = data["description"] + for p in data["pages"]: + page = Page() + for k,v in p.items(): + if k == "url": + if not re.match('(.+)://', v): + v = 'http://%s' % v + setattr(page, k, v) + self.pages.append(page) + + def __iter__(self): + return self.pages.__iter__() diff --git a/tools/gpu/gpu_tools/page_set_unittest.py b/tools/gpu/gpu_tools/page_set_unittest.py new file mode 100644 index 0000000..3050162 --- /dev/null +++ b/tools/gpu/gpu_tools/page_set_unittest.py @@ -0,0 +1,27 @@ +# Copyright (c) 2012 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 tempfile +import unittest + +import page_set + +set1=""" +{"description": "hello", + "pages": [ + {"url": "http://www.foo.com/"} + ] +} +""" + +class TestPageSet(unittest.TestCase): + def testSet1(self): + with tempfile.NamedTemporaryFile() as f: + f.write(set1) + f.flush() + ps = page_set.PageSet() + ps.LoadFromFile(f.name) + + self.assertEquals("hello", ps.description) + self.assertEquals(1, len(ps.pages)) + self.assertEquals("http://www.foo.com/", ps.pages[0].url) diff --git a/tools/gpu/pylib/scroll.js b/tools/gpu/gpu_tools/scroll.js index 48688d0..48688d0 100644 --- a/tools/gpu/pylib/scroll.js +++ b/tools/gpu/gpu_tools/scroll.js diff --git a/tools/gpu/pylib/scroll.py b/tools/gpu/gpu_tools/scroll.py index fea5e3e..4a7effc 100644 --- a/tools/gpu/pylib/scroll.py +++ b/tools/gpu/gpu_tools/scroll.py @@ -6,7 +6,7 @@ import os import chrome_remote_control import scroll_results -def Scroll(tab): +def Scroll(page, tab): scroll_js_path = os.path.join(os.path.dirname(__file__), 'scroll.js') scroll_js = open(scroll_js_path, 'r').read() @@ -22,7 +22,7 @@ def Scroll(tab): # Poll for scroll result. chrome_remote_control.WaitFor( - lambda: tab.runtime.Evaluate('window.__scrollTestResult')) + lambda: tab.runtime.Evaluate('window.__scrollTestResult'), 60) # Get scroll results. url = tab.runtime.Evaluate('document.location.href') diff --git a/tools/gpu/pylib/scroll_results.py b/tools/gpu/gpu_tools/scroll_results.py index ea0ac19..ea0ac19 100644 --- a/tools/gpu/pylib/scroll_results.py +++ b/tools/gpu/gpu_tools/scroll_results.py diff --git a/tools/gpu/gpu_tools/url_test.py b/tools/gpu/gpu_tools/url_test.py new file mode 100644 index 0000000..8475089 --- /dev/null +++ b/tools/gpu/gpu_tools/url_test.py @@ -0,0 +1,44 @@ +# Copyright (c) 2012 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 logging +import re +import time +import traceback + +import chrome_remote_control + +def UrlTest(browser, test_method, page_set): + skipped_pages = [] + with browser.ConnectToNthTab(0) as tab: + for page in page_set: + try: + logging.info('Loading %s...', page.url) + tab.page.Navigate(page.url) + # TODO(dtu): Detect HTTP redirects. + time.sleep(2) # Wait for unpredictable redirects. + tab.WaitForDocumentReadyStateToBeInteractiveOrBetter() + logging.info('Loaded page: %s', page.url) + except chrome_remote_control.TimeoutException: + logging.warning('Timed out while loading: %s', page.url) + continue + + try: + logging.info('Running test on %s...', page.url) + result = test_method(page, tab) + logging.info('Ran test:', page.url) + except: + logging.error('Failed on URL: %s', page.url) + traceback.print_exc() + skipped_pages.append(page) + continue + + if not result: + logging.warning('No result for URL: %s', page.url) + skipped_pages.append(page) + continue + + yield result + + if len(skipped_pages) > 0: + logging.warning('Skipped pages: %s', skipped_page) diff --git a/tools/gpu/page_sets/Q32012.json b/tools/gpu/page_sets/Q32012.json new file mode 100644 index 0000000..d5b6b39 --- /dev/null +++ b/tools/gpu/page_sets/Q32012.json @@ -0,0 +1,223 @@ +{ + "description": "Pages hand-picked from top-lists in Q32012.", + "pages": [ + {"url": "http://www.facebook.com/barackobama"}, + {"url": "https://www.google.com/search?q=barack%20obama"}, + {"url": "http://youtube.com"}, + {"url": "http://yahoo.com"}, + {"url": "http://www.baidu.com/s?wd=barack+obama"}, + {"url": "http://en.wikipedia.org/wiki/Wikipedia"}, + {"url": "http://qq.com"}, + {"url": "http://www.amazon.com/Kindle-Fire-Amazon-Tablet/dp/B0051VVOB2"}, + {"url": "http://googleblog.blogspot.com/"}, + {"url": "http://taobao.com"}, + {"url": "http://www.linkedin.com/in/linustorvalds"}, + {"url": "http://yahoo.co.jp"}, + {"url": "http://sina.com.cn"}, + {"url": "http://msn.com"}, + {"url": "http://yandex.ru/yandsearch?text=barack+obama"}, + {"url": "http://translation.babylon.com/"}, + {"url": "http://www.bing.com/search?q=barack+obama"}, + {"url": "http://wordpress.org/news/"}, + {"url": "http://www.ebay.com/sch/i.html?_nkw=antiques"}, + {"url": "http://163.com"}, + {"url": "http://www.soso.com/q?w=barack+obama"}, + {"url": "http://www.microsoft.com/en-us/default.aspx"}, + {"url": "http://go.mail.ru/search?mailru=1&mg=1&q=barack+obama"}, + {"url": "http://vk.com/id118712387"}, + {"url": "http://staff.tumblr.com/"}, + {"url": "http://sohu.com"}, + {"url": "http://sfbay.craigslist.org/mis/"}, + {"url": "http://www.ask.com/web?q=barack+obama&search=&qsrc=0&o=0&l=dir"}, + {"url": "http://www.apple.com/ipodtouch/"}, + {"url": "http://blog.pinterest.com/"}, + {"url": "http://pinterest.com/backdrophome/"}, + {"url": "http://paypal.com"}, + {"url": "http://bbc.co.uk"}, + {"url": "http://www.avg.com/us-en/avg-premium-security"}, + {"url": "http://googlesystem.blogspot.com/"}, + {"url": "http://tudou.com"}, + {"url": "http://blog.fc2.com/en/"}, + {"url": "http://imdb.com"}, + {"url": "http://youku.com"}, + {"url": "http://www.flickr.com/photos/thomashawk/"}, + {"url": "http://www.flickr.com/photos/thomashawk/sets/72157600284219965/detail/"}, + {"url": "http://search.yahoo.com/search?ei=UTF-8&trackingType=go_search_home&p=barack+obama&fr=hsusgo1&sa.x=0&sa.y=0"}, + {"url": "http://www.conduit.com/"}, + {"url": "http://ifeng.com"}, + {"url": "http://tmall.com"}, + {"url": "http://hao123.com"}, + {"url": "http://aol.com"}, + {"url": "http://zedo.com"}, + {"url": "http://search.mywebsearch.com/mywebsearch/GGmain.jhtml?searchfor=barack+obama"}, + {"url": "http://cnn.com"}, + {"url": "http://portal.ebay.de/deutschland-schraubt-angebote"}, + {"url": "http://www.adobe.com/products/photoshopfamily.html?promoid=JOLIW"}, + {"url": "http://global.rakuten.com/us/"}, + {"url": "http://laundry.about.com/od/kidsandlaundry/f/How-Do-I-Wash-A-Backpack.htm"}, + {"url": "http://thepiratebay.se/search/barack%20obama/0/99/0"}, + {"url": "http://360buy.com"}, + {"url": "http://huffingtonpost.com"}, + {"url": "http://alibaba.com"}, + {"url": "http://chinaz.com"}, + {"url": "http://www.sogou.com/web?query=barack+obama"}, + {"url": "http://www.amazon.de/gp/product/B0051QVF7A/ref=amb_link_170625867_1/275-4711375-4099801?ie=UTF8&nav_sdd=aps&pf_rd_m=A3JWKAKR8XB7XF&pf_rd_s=center-1&pf_rd_r=1C0XDBPB12WHDM63V11R&pf_rd_t=101&pf_rd_p=320475427&pf_rd_i=301128"}, + {"url": "http://google.pl"}, + {"url": "http://mediafire.com"}, + {"url": "http://espn.go.com"}, + {"url": "http://uol.com.br"}, + {"url": "http://www.godaddy.com/products/secure-hosting.aspx?ci=72738"}, + {"url": "http://imgur.com/gallery/b90ZE"}, + {"url": "http://home.alipay.com/bank/paymentKJ.htm"}, + {"url": "http://amazon.co.jp"}, + {"url": "http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array"}, + {"url": "http://www.google.com/doubleclick/"}, + {"url": "http://search.4shared.com/q/CCAD/1/barack%20obama"}, + {"url": "http://dailymotion.com"}, + {"url": "http://globo.com"}, + {"url": "http://instagram.com/developer/"}, + {"url": "http://livedoor.com"}, + {"url": "http://wordpress.org/showcase/"}, + {"url": "http://bp.blogspot.com"}, + {"url": "http://wigetmedia.com/advertisers"}, + {"url": "http://www.search-results.com/web?&q=barack%20obama"}, + {"url": "http://cnet.com"}, + {"url": "http://nytimes.com"}, + {"url": "http://torrentz.eu/search?f=barack+obama"}, + {"url": "http://livejournal.com"}, + {"url": "http://douban.com"}, + {"url": "http://www.weather.com/weather/right-now/Mountain+View+CA+94043"}, + {"url": "http://dailymail.co.uk"}, + {"url": "http://www.tianya.cn/bbs/index.shtml"}, + {"url": "http://ehow.com"}, + {"url": "http://theproject.badoo.com/final.phtml"}, + {"url": "http://www.bankofamerica.com/deposits/checksave/index.cfm?template=check_eBanking"}, + {"url": "http://vimeo.com"}, + {"url": "http://360.cn"}, + {"url": "http://indiatimes.com"}, + {"url": "http://deviantart.com"}, + {"url": "http://reddit.com"}, + {"url": "http://aweber.com"}, + {"url": "http://warriorforum.com"}, + {"url": "http://spiegel.de"}, + {"url": "http://pconline.com.cn"}, + {"url": "http://mozilla.org"}, + {"url": "http://booking.com"}, + {"url": "http://goo.ne.jp"}, + {"url": "https://www.chase.com/online/Home-Lending/mortgages.htm"}, + {"url": "http://addthis.com"}, + {"url": "http://56.com"}, + {"url": "http://news.blogfa.com/"}, + {"url": "http://www.stumbleupon.com/jobs"}, + {"url": "https://www.dropbox.com/about"}, + {"url": "http://www.clicksor.com/publishers/adformat"}, + {"url": "http://answers.com"}, + {"url": "http://en.softonic.com/"}, + {"url": "http://walmart.com"}, + {"url": "http://pengyou.com"}, + {"url": "http://outbrain.com"}, + {"url": "http://comcast.net"}, + {"url": "http://foxnews.com"}, + {"url": "http://photobucket.com/findstuff/photography%20styles/"}, + {"url": "http://bleach.wikia.com/?redirect=no"}, + {"url": "http://sourceforge.net/projects/xoops/?source=frontpage&position=1"}, + {"url": "http://onet.pl"}, + {"url": "http://guardian.co.uk"}, + {"url": "https://www.wellsfargo.com/jump/enterprise/doublediscount?msc=5589&mplx=10918-70119-3408-64"}, + {"url": "http://wikimediafoundation.org/wiki/Home"}, + {"url": "http://xunlei.com"}, + {"url": "http://as.58.com/shuma/"}, + {"url": "http://skype.com"}, + {"url": "http://etsy.com"}, + {"url": "http://bild.de"}, + {"url": "http://search.naver.com/search.naver?where=nexearch&query=barack+obama&sm=top_hty&fbm=0&ie=utf8"}, + {"url": "http://statcounter.com/features/?PHPSESSID=bbjcvjr681bcul4vqvgq2qgmo7"}, + {"url": "http://iqiyi.com"}, + {"url": "http://fbcdn.net"}, + {"url": "http://www.myspace.com/browse/people"}, + {"url": "http://allegro.pl/antyki-i-sztuka"}, + {"url": "http://yesky.com"}, + {"url": "http://justbeenpaid.com"}, + {"url": "http://adultfriendfinder.com"}, + {"url": "http://fiverr.com"}, + {"url": "http://www.leboncoin.fr/annonces/offres/centre/"}, + {"url": "http://dictionary.reference.com/"}, + {"url": "http://realtime.rediff.com/instasearch#!barack%20obama"}, + {"url": "http://zol.com.cn"}, + {"url": "http://optmd.com"}, + {"url": "http://www.filestube.com/search.html?q=barack+obama&select=All"}, + {"url": "http://xinhuanet.com"}, + {"url": "http://www.salesforce.com/sales-cloud/overview/"}, + {"url": "http://www.squidoo.com/make-cards-and-gift-bags-with-antique-photos"}, + {"url": "http://www.domaintools.com/research/"}, + {"url": "http://download.cnet.com/windows/?tag=hdr;brandnav"}, + {"url": "https://rapidshare.com/#!shop"}, + {"url": "http://people.com.cn"}, + {"url": "http://ucoz.ru"}, + {"url": "http://free.fr"}, + {"url": "http://nicovideo.jp"}, + {"url": "http://www.yelp.com/search?find_desc=food&find_loc=San+Jose%2C+CA&ns=1"}, + {"url": "http://slideshare.net"}, + {"url": "http://archive.org/web/web.php"}, + {"url": "http://www.cntv.cn/index.shtml"}, + {"url": "http://english.cntv.cn/01/index.shtml"}, + {"url": "http://abonnez-vous.orange.fr/residentiel/accueil/accueil.aspx"}, + {"url": "http://v.it168.com/"}, + {"url": "http://nbcolympics.com"}, + {"url": "http://hootsuite.com"}, + {"url": "http://www.scribd.com/doc/52210329/The-Masters-Augusta-National-s-Amen-Corner-up-close"}, + {"url": "http://themeforest.net"}, + {"url": "http://4399.com"}, + {"url": "http://www.soku.com/v?keyword=barack%20obama"}, + {"url": "http://google.se"}, + {"url": "http://funmoods.com"}, + {"url": "http://csdn.net"}, + {"url": "http://telegraph.co.uk"}, + {"url": "http://taringa.net"}, + {"url": "http://www.tripadvisor.com/Tourism-g32701-Mendocino_California-Vacations.html"}, + {"url": "http://pof.com"}, + {"url": "http://wp.pl"}, + {"url": "http://soundcloud.com/flosstradamus/tracks"}, + {"url": "http://w3schools.com/html/default.asp"}, + {"url": "http://ameblo.jp/staff/"}, + {"url": "http://wsj.com"}, + {"url": "http://web.de"}, + {"url": "http://sweetim.com"}, + {"url": "http://rambler.ru"}, + {"url": "http://gmx.net"}, + {"url": "http://www.indeed.com/jobs?q=software&l=Mountain+View%2C+CA"}, + {"url": "http://ilivid.com"}, + {"url": "http://www.xing.com/search/people?search%5Bq%5D=lufthansa"}, + {"url": "http://reuters.com"}, + {"url": "http://hostgator.com"}, + {"url": "http://www.ikea.com/us/en/catalog/categories/departments/living_room/"}, + {"url": "http://www.kaixin001.com/award2012/wenming/index.php"}, + {"url": "http://ku6.com"}, + {"url": "http://libero.it"}, + {"url": "http://samsung.com"}, + {"url": "http://hudong.com"}, + {"url": "http://espncricinfo.com"}, + {"url": "http://china.com"}, + {"url": "http://www.ups.com/content/us/en/bussol/browse/smallbiz/new-to-ups.html?WT.svl=SolExp"}, + {"url": "http://letv.com"}, + {"url": "http://ero-advertising.com"}, + {"url": "http://mashable.com"}, + {"url": "http://iminent.com"}, + {"url": "http://rutracker.org"}, + {"url": "http://www.shopping.hp.com/en_US/home-office/-/products/Laptops/Laptops"}, + {"url": "http://www.clickbank.com/buy_products.htm?dores=true&mainCategoryId=1340&sortField=POPULARITY&b1=1340"}, + {"url": "http://b.hatena.ne.jp/"}, + {"url": "http://www.youdao.com/search?q=barack+obama&ue=utf8&keyfrom=web.index"}, + {"url": "http://forbes.com"}, + {"url": "http://nbcnews.com"}, + {"url": "http://bitauto.com"}, + {"url": "http://php.net"}, + {"url": "http://www.target.com/c/women/-/N-5xtd3#?lnk=nav_t_spc_1_0"}, + {"url": "http://dianxin.cn"}, + {"url": "http://www.aizhan.com/siteall/www.youboy.com/"}, + {"url": "http://veiculos-home.mercadolivre.com.br/"}, + {"url": "http://kakaku.com"}, + {"url": "http://flipkart.com"}, + {"url": "http://paipai.com"} + ] +} diff --git a/tools/gpu/page_sets/__init__.py b/tools/gpu/page_sets/__init__.py new file mode 100644 index 0000000..8de2699 --- /dev/null +++ b/tools/gpu/page_sets/__init__.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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 os +def GetAllPageSetFilenames(): + results = [] + start_dir = os.path.dirname(__file__) + for (dirpath, dirnames, filenames) in os.walk(start_dir): + for f in filenames: + if os.path.splitext(f)[1] != ".json": + continue + filename = os.path.join(dirpath, f) + results.append(filename) + return results diff --git a/tools/gpu/page_sets/page_sets_unittest.py b/tools/gpu/page_sets/page_sets_unittest.py new file mode 100644 index 0000000..a33d9f7 --- /dev/null +++ b/tools/gpu/page_sets/page_sets_unittest.py @@ -0,0 +1,17 @@ +# Copyright (c) 2012 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 unittest + +import gpu_tools.page_set +import page_sets + +class PageSetsUnittest(unittest.TestCase): + "Verfies that all the pagesets in this directory are syntactically valid.""" + + def testPageSetsParseCorrectly(self): + filenames = page_sets.GetAllPageSetFilenames() + for filename in filenames: + ps = gpu_tools.page_set.PageSet() + ps.LoadFromFile(filename) + diff --git a/tools/gpu/pylib/browser.py b/tools/gpu/pylib/browser.py deleted file mode 100644 index 0882bac7..0000000 --- a/tools/gpu/pylib/browser.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2012 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 chrome_remote_control - -def StartBrowser(): - parser = chrome_remote_control.BrowserOptions.CreateParser() - options, _ = parser.parse_args() - - possible_browser = chrome_remote_control.FindBestPossibleBrowser(options) - if possible_browser is None: - raise Exception('No browsers found of the following types: %s.' % - options.browser_types_to_use) - - return possible_browser.Create() diff --git a/tools/gpu/pylib/url_test.py b/tools/gpu/pylib/url_test.py deleted file mode 100644 index c3a5ed2..0000000 --- a/tools/gpu/pylib/url_test.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2012 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 logging -import re -import traceback - -import browser -import chrome_remote_control - -def UrlTest(test_method, url_list): - skipped_urls = [] - with browser.StartBrowser() as b: - with b.ConnectToNthTab(0) as tab: - for url in url_list: - url = url.strip() - if not url: - continue - if not re.match('(.+)://', url): - url = 'http://%s' % url - - try: - logging.info('Loading %s...', url) - tab.LoadUrl(url) - logging.info('Loaded page: %s', url) - except chrome_remote_control.TimeoutException: - logging.warning('Timed out while loading: %s', url) - continue - - try: - logging.info('Running test on %s...', url) - result = test_method(tab) - logging.info('Ran test:', url) - except: - logging.error('Failed on URL: %s', url) - traceback.print_exc() - skipped_urls.append(url) - continue - - if not result: - logging.warning('No result for URL: %s', url) - skipped_urls.append(url) - continue - - yield result - - if len(skipped_urls) > 0: - logging.warning('Skipped URLs: %s', skipped_urls) diff --git a/tools/gpu/run_gpu_tools_unittests b/tools/gpu/run_gpu_tools_unittests new file mode 100755 index 0000000..222ae09 --- /dev/null +++ b/tools/gpu/run_gpu_tools_unittests @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. +""" +This script runs tests to verify that the gpu tools are working. + +The actual performance tools are not run by this script. +""" +import os +import sys + +import gpu_tools +import chrome_remote_control.run_tests + +if __name__ == "__main__": + top_level_dir = os.path.abspath( + os.path.dirname(__file__)) + start_dir = "gpu_tools" + ret = chrome_remote_control.run_tests.Main( + sys.argv[1:], start_dir, top_level_dir) + + start_dir = "page_sets" + ret = chrome_remote_control.run_tests.Main( + sys.argv[1:], start_dir, top_level_dir) + + sys.exit(ret) diff --git a/tools/gpu/pylib/__init__.py b/tools/gpu/run_page_scroller index e20abd1..c580609 100644..100755 --- a/tools/gpu/pylib/__init__.py +++ b/tools/gpu/run_page_scroller @@ -1,9 +1,11 @@ +#!/usr/bin/env python # Copyright (c) 2012 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. -"""A library for Chrome-based tests.""" import os import sys -sys.path.append(os.path.join(os.path.dirname(__file__), - '..', '..', 'chrome_remote_control')) +import gpu_tools.page_scroller + +if __name__ == "__main__": + sys.exit(gpu_tools.page_scroller.Main(sys.argv[1:])) diff --git a/tools/gpu/run_scroll_test.py b/tools/gpu/run_scroll_test.py deleted file mode 100755 index a6405a1..0000000 --- a/tools/gpu/run_scroll_test.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -# The test takes a list of URLs through stdin and prints results in CSV format. -# Example: python run_scroll_test.py < data/urls.txt > test_results.csv -import csv -import logging -import sys - -import pylib.scroll -import pylib.url_test - -def Main(): - results_writer = csv.writer(sys.stdout) - results_writer.writerow(['URL', 'FPS', 'Mean Frame Time (s)', - '% Dropped Frames', 'First Paint Time (s)']) - sys.stdout.flush() - - for result in pylib.url_test.UrlTest(pylib.scroll.Scroll, sys.stdin): - if not result.DidScroll(): - # Most likely the page was shorter than the window height. - logging.warning('Page did not scroll: %s', result.GetUrl()) - continue - - results_writer.writerow([result.GetUrl(), result.GetFps(0), - result.GetMeanFrameTime(0), - result.GetPercentBelow60Fps(0), - result.GetFirstPaintTime()]) - sys.stdout.flush() - -if __name__ == '__main__': - Main() diff --git a/tools/gpu/scroll_test_unittest.py b/tools/gpu/scroll_test_unittest.py deleted file mode 100755 index e2c295f..0000000 --- a/tools/gpu/scroll_test_unittest.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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 os -import unittest - -import pylib.file_server -import pylib.scroll -import pylib.url_test - -class ScrollTestUnitTest(unittest.TestCase): - - def runTest(self): - test_file_path = os.path.join(os.path.dirname(__file__), - 'data', 'scrollable_page.html') - - result_count = 0 - with pylib.file_server.FileServer(test_file_path) as root_url: - url = '%s/%s' % (root_url, os.path.basename(test_file_path)) - for result in pylib.url_test.UrlTest(pylib.scroll.Scroll, [url]): - result_count += 1 - - self.assertEqual(result_count, 1, 'Wrong number of test results.') - self.assertEqual(result.GetUrl(), url, 'Did not navigate to right URL.') - self.assertTrue(result.GetFirstPaintTime() < 1, 'The page load was slow.') - self.assertTrue(result.GetFps(0) > 55, 'Scroll seemed kind of slow.') - -if __name__ == '__main__': - unittest.main() diff --git a/tools/gpu/data/scrollable_page.html b/tools/gpu/unittest_data/scrollable_page.html index 5dcdd4f..5dcdd4f 100644 --- a/tools/gpu/data/scrollable_page.html +++ b/tools/gpu/unittest_data/scrollable_page.html |