diff options
author | dtu@chromium.org <dtu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-31 06:49:22 +0000 |
---|---|---|
committer | dtu@chromium.org <dtu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-31 06:49:22 +0000 |
commit | f3835b4e4f91f6f924259da893860d8eea20ff11 (patch) | |
tree | 3cad0b2c9699bea79c263ffac5e80491c75c1856 /tools | |
parent | 4321b18bf08725bd21026ffc4f65bccd842810b3 (diff) | |
download | chromium_src-f3835b4e4f91f6f924259da893860d8eea20ff11.zip chromium_src-f3835b4e4f91f6f924259da893860d8eea20ff11.tar.gz chromium_src-f3835b4e4f91f6f924259da893860d8eea20ff11.tar.bz2 |
Devtools-based scroll test.
BUG=
Review URL: https://chromiumcodereview.appspot.com/10871097
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@154398 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools')
23 files changed, 821 insertions, 63 deletions
diff --git a/tools/chrome_remote_control/chrome_remote_control/__init__.py b/tools/chrome_remote_control/chrome_remote_control/__init__.py index 37227f4..ebc83d2 100644 --- a/tools/chrome_remote_control/chrome_remote_control/__init__.py +++ b/tools/chrome_remote_control/chrome_remote_control/__init__.py @@ -8,3 +8,4 @@ from browser_finder import * from browser_options import * from browser import * from tab import * +from util import * diff --git a/tools/chrome_remote_control/chrome_remote_control/browser.py b/tools/chrome_remote_control/chrome_remote_control/browser.py index f7ba1b9..925cec9 100644 --- a/tools/chrome_remote_control/chrome_remote_control/browser.py +++ b/tools/chrome_remote_control/chrome_remote_control/browser.py @@ -34,8 +34,8 @@ class Browser(object): def num_tabs(self): return self._backend.num_tabs - def GetNthTabURL(self, index): - return self._backend.GetNthTabURL(index) + def GetNthTabUrl(self, index): + return self._backend.GetNthTabUrl(index) def ConnectToNthTab(self, index): return self._backend.ConnectToNthTab(index) 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 1144bca..62e235b 100644 --- a/tools/chrome_remote_control/chrome_remote_control/browser_finder.py +++ b/tools/chrome_remote_control/chrome_remote_control/browser_finder.py @@ -10,13 +10,10 @@ import desktop_browser_backend """Finds 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 PossibleBrowser(object): """A browser that can be controlled. - Call connect() to launch the browser and begin manipulating it.. + Call Create() to launch the browser and begin manipulating it. """ def __init__(self, type, options, executable): @@ -34,10 +31,8 @@ class PossibleBrowser(object): return browser.Browser(backend) raise Exception("Not implemented.") -def FindBestPossibleBrowser(options, - os = real_os, - sys = real_sys, - subprocess = real_subprocess): +def FindBestPossibleBrowser(options, os=real_os, sys=real_sys, + subprocess=real_subprocess): """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.""" @@ -55,10 +50,8 @@ def GetAllAvailableBrowserTypes(options): type_list.sort() return type_list -def FindAllPossibleBrowsers(options, - os = real_os, - sys = real_sys, - subprocess = real_subprocess): +def FindAllPossibleBrowsers(options, os=real_os, sys=real_sys, + subprocess=real_subprocess): """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.""" @@ -74,10 +67,8 @@ def FindAllPossibleBrowsers(options, selected_browsers.sort(compare_browsers_on_priority) return selected_browsers -def _UnsortedFindAllLocalBrowserPossibilities(options, - os = real_os, - sys = real_sys, - subprocess = real_subprocess): +def _UnsortedFindAllLocalBrowserPossibilities(options, os=real_os, sys=real_sys, + subprocess=real_subprocess): browsers = [] # Add the explicit browser executable if given. 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 9176841..5b3a466 100644 --- a/tools/chrome_remote_control/chrome_remote_control/browser_options.py +++ b/tools/chrome_remote_control/chrome_remote_control/browser_options.py @@ -3,29 +3,27 @@ # found in the LICENSE file. import optparse -import browser_finder +ALL_BROWSER_TYPES = 'exact,release,debug,canary,system' +DEFAULT_BROWSER_TYPES_TO_RUN = 'exact,release,canary,system' class BrowserOptions(object): @staticmethod def CreateParser(usage=None): parser = optparse.OptionParser(usage=usage) - 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', action="store_true", - dest="browser_executable", - help="The exact browser to run.") - parser.add_option('--browser-types-to-use', action="store_true", - 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', action="store_true", - dest="chrome_root", - help="Where to look for chrome builds." - "Defaults to searching parent dirs by default.") + 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' % + DEFAULT_BROWSER_TYPES_TO_RUN) + parser.add_option('--chrome-root', dest='chrome_root', + help='Where to look for chrome builds. ' + 'Defaults to searching parent dirs.') real_parse = parser.parse_args - def ParseArgs(args = None): + def ParseArgs(args=None): options = BrowserOptions() ret = real_parse(args, options) return ret @@ -36,8 +34,7 @@ class BrowserOptions(object): 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_types_to_use = DEFAULT_BROWSER_TYPES_TO_RUN self.chrome_root = None self.extra_browser_args = [] @@ -47,5 +44,5 @@ class BrowserOptions(object): @browser_types_to_use.setter def browser_types_to_use(self, value): - self._browser_types_to_use.split(",") + self._browser_types_to_use = value.split(',') 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 07714c3..a602a93 100644 --- a/tools/chrome_remote_control/chrome_remote_control/browser_unittest.py +++ b/tools/chrome_remote_control/chrome_remote_control/browser_unittest.py @@ -9,8 +9,8 @@ import browser_options class BrowserTest(unittest.TestCase): def testBasic(self): options = browser_options.BrowserOptions() - options.browser_to_use = browser_finder.ALL_BROWSER_TYPES + options.browser_to_use = browser_options.ALL_BROWSER_TYPES browser_to_create = browser_finder.FindBestPossibleBrowser(options) with browser_to_create.Create() as b: self.assertEquals(1, b.num_tabs) - self.assertEquals("chrome://newtab/", b.GetNthTabURL(0)) + self.assertEquals("chrome://newtab/", b.GetNthTabUrl(0)) 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 aa8563a..5bba710 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 @@ -12,6 +12,7 @@ import json import browser_finder import inspector_backend import tab +import util DEFAULT_PORT = 9273 @@ -56,12 +57,14 @@ class DesktopBrowserBackend(object): self.Close() def _WaitForBrowserToComeUp(self): - while True: + def IsBrowserUp(): try: self._ListTabs() - break - except urllib2.URLError, ex: - pass + except urllib2.URLError: + return False + else: + return True + util.WaitFor(IsBrowserUp) def _ListTabs(self): req = urllib2.urlopen("http://localhost:%i/json" % self._port) @@ -72,7 +75,7 @@ class DesktopBrowserBackend(object): def num_tabs(self): return len(self._ListTabs()) - def GetNthTabURL(self, index): + def GetNthTabUrl(self, index): return self._ListTabs()[index]["url"] def ConnectToNthTab(self, index): 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 8494bdf..e7889e5 100644 --- a/tools/chrome_remote_control/chrome_remote_control/inspector_backend.py +++ b/tools/chrome_remote_control/chrome_remote_control/inspector_backend.py @@ -6,6 +6,9 @@ import time import websocket import socket +class InspectorException(Exception): + pass + class InspectorBackend(object): def __init__(self, backend, descriptor): self._backend = backend diff --git a/tools/chrome_remote_control/chrome_remote_control/tab.py b/tools/chrome_remote_control/chrome_remote_control/tab.py index e7863d8..d4766f6 100644 --- a/tools/chrome_remote_control/chrome_remote_control/tab.py +++ b/tools/chrome_remote_control/chrome_remote_control/tab.py @@ -2,11 +2,12 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import json -import time import websocket import socket +import time import tab_runtime +import util class Tab(object): def __init__(self, inspector_backend): @@ -27,26 +28,31 @@ class Tab(object): def __exit__(self, *args): self.Close() - def BeginToLoadURL(self, url): + 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';") - while self.runtime.Evaluate("document.location.href") != "about:blank": - time.sleep(0.01) + 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') - self.runtime.Evaluate("document.location = '%s';" % url) - while self.runtime.Evaluate("document.location.href") == "about:blank": - time.sleep(0.01) + def LoadUrl(self, url): + self.BeginToLoadUrl(url) + # TODO(dtu): Detect HTTP redirects. + time.sleep(2) # Wait for unpredictable redirects. + self.WaitForDocumentReadyStateToBeInteractiveOrBetter() def WaitForDocumentReadyStateToBeComplete(self): - while self.runtime.Evaluate("document.readyState") != 'complete': - time.sleep(0.01) + util.WaitFor( + lambda: self.runtime.Evaluate('document.readyState') == 'complete') def WaitForDocumentReadyStateToBeInteractiveOrBetter(self): - while True: - rs = self.runtime.Evaluate("document.readyState") - if rs == 'complete' or rs == 'interactive': - break - time.sleep(0.01) + def IsReadyStateInteractiveOrBetter(): + rs = self.runtime.Evaluate('document.readyState') + return rs == 'complete' or rs == 'interactive' + util.WaitFor(IsReadyStateInteractiveOrBetter) 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 c78b123..4d3fc13 100644 --- a/tools/chrome_remote_control/chrome_remote_control/tab_runtime.py +++ b/tools/chrome_remote_control/chrome_remote_control/tab_runtime.py @@ -1,6 +1,7 @@ # 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 inspector_backend class EvaluateException(Exception): pass @@ -29,7 +30,7 @@ class TabRuntime(object): } res = self._inspector_backend.SyncRequest(request) if "error" in res: - raise EvaluateException(res["error"]["message"]) + raise inspector_backend.InspectorException(res["error"]["message"]) if res["result"]["wasThrown"]: # TODO(nduca): propagate stacks from javascript up to the python diff --git a/tools/chrome_remote_control/chrome_remote_control/util.py b/tools/chrome_remote_control/chrome_remote_control/util.py new file mode 100644 index 0000000..ec2df2b --- /dev/null +++ b/tools/chrome_remote_control/chrome_remote_control/util.py @@ -0,0 +1,34 @@ +# 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 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): + assert isinstance(condition, type(lambda: None)) # is function + start_time = time.time() + while not condition(): + 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)) + time.sleep(0.01) diff --git a/tools/chrome_remote_control/examples/rendering_microbenchmark_test.py b/tools/chrome_remote_control/examples/rendering_microbenchmark_test.py index 01220b5..6139977 100755 --- a/tools/chrome_remote_control/examples/rendering_microbenchmark_test.py +++ b/tools/chrome_remote_control/examples/rendering_microbenchmark_test.py @@ -38,7 +38,7 @@ 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.BeginToLoadUrl("http://www.google.com") tab.WaitForDocumentReadyStateToBeComplete() if tab.runtime.Evaluate("window.chrome.gpuBenchmarking === undefined"): print "Browser does not support gpu benchmarks API." @@ -64,7 +64,7 @@ def Main(args): print ",".join(cols) for u in urls: - tab.BeginToLoadURL(u) + tab.BeginToLoadUrl(u) tab.WaitForDocumentReadyStateToBeInteractiveOrBetter() results = tab.runtime.Evaluate( "window.chrome.gpuBenchmarking.runRenderingBenchmarks();") diff --git a/tools/gpu/OWNERS b/tools/gpu/OWNERS new file mode 100644 index 0000000..77fa7f3 --- /dev/null +++ b/tools/gpu/OWNERS @@ -0,0 +1,4 @@ +set noparent +nduca@chromium.org +alokp@chromium.org +dtu@chromium.org diff --git a/tools/gpu/data/scrollable_page.html b/tools/gpu/data/scrollable_page.html new file mode 100644 index 0000000..5dcdd4f --- /dev/null +++ b/tools/gpu/data/scrollable_page.html @@ -0,0 +1,9 @@ +<!doctype html> +<html> + <head> + <style type="text/css"> + body { height: 10000px; } + </style> + </head> + <body></body> +</html> diff --git a/tools/gpu/data/urls.txt b/tools/gpu/data/urls.txt new file mode 100644 index 0000000..051228b --- /dev/null +++ b/tools/gpu/data/urls.txt @@ -0,0 +1,218 @@ +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/pylib/__init__.py b/tools/gpu/pylib/__init__.py new file mode 100644 index 0000000..e20abd1 --- /dev/null +++ b/tools/gpu/pylib/__init__.py @@ -0,0 +1,9 @@ +# 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')) diff --git a/tools/gpu/pylib/browser.py b/tools/gpu/pylib/browser.py new file mode 100644 index 0000000..0882bac7 --- /dev/null +++ b/tools/gpu/pylib/browser.py @@ -0,0 +1,15 @@ +# 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/file_server.py b/tools/gpu/pylib/file_server.py new file mode 100644 index 0000000..4056619 --- /dev/null +++ b/tools/gpu/pylib/file_server.py @@ -0,0 +1,28 @@ +# 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 subprocess + +class FileServer(object): + def __init__(self, path, port=8000): + assert os.path.exists(path) + if os.path.isdir(path): + self._path = path + else: + self._path = os.path.dirname(path) + self._port = port + + def __enter__(self): + self._server = subprocess.Popen( + ['python', '-m', 'SimpleHTTPServer', str(self._port)], + cwd=self._path) + return 'http://localhost:%d' % self._port + + def __exit__(self, *args): + self._server.kill() + self._server = None + + def __del__(self): + if self._server: + self._server.kill() diff --git a/tools/gpu/pylib/scroll.js b/tools/gpu/pylib/scroll.js new file mode 100644 index 0000000..48688d0 --- /dev/null +++ b/tools/gpu/pylib/scroll.js @@ -0,0 +1,253 @@ +// 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. + +// Inject this script on any page to measure framerate as the page is scrolled +// from top to bottom. +// +// Usage: +// 1. Define a callback that takes the results array as a parameter. +// 2. To start the test, call new __ScrollTest(callback). +// 3a. When the test is complete, the callback will be called. +// 3b. If no callback is specified, the results are sent to the console. + +(function() { + var getTimeMs = (function() { + if (window.performance) + return (performance.now || + performance.mozNow || + performance.msNow || + performance.oNow || + performance.webkitNow).bind(window.performance); + else + return function() { return new Date().getTime(); }; + })(); + + var requestAnimationFrame = (function() { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback) { + window.setTimeout(callback, 1000 / 60); + }; + })().bind(window); + + /** + * Scrolls a given element down a certain amount to emulate user scrolling. + * Uses smooth scrolling capabilities provided by the platform, if available. + * @constructor + */ + function SmoothScrollDownGesture(opt_element, opt_isGmailTest) { + this.element_ = opt_element || document.body; + this.isGmailTest_ = opt_isGmailTest; + }; + + SmoothScrollDownGesture.prototype.start = function(callback) { + this.callback_ = callback; + if (chrome && + chrome.gpuBenchmarking && + chrome.gpuBenchmarking.beginSmoothScrollDown) { + chrome.gpuBenchmarking.beginSmoothScrollDown(true, function() { + callback(); + }); + return; + } + + if (this.isGmailTest_) { + this.element_.scrollByLines(1); + requestAnimationFrame(callback); + return; + } + + var SCROLL_DELTA = 100; + this.element_.scrollTop += SCROLL_DELTA; + requestAnimationFrame(callback); + } + + /** + * Tracks rendering performance using the gpuBenchmarking.renderingStats API. + * @constructor + */ + function GpuBenchmarkingRenderingStats() { + } + + GpuBenchmarkingRenderingStats.prototype.start = function() { + this.initialStats_ = this.getRenderingStats_(); + } + GpuBenchmarkingRenderingStats.prototype.stop = function() { + this.finalStats_ = this.getRenderingStats_(); + } + + GpuBenchmarkingRenderingStats.prototype.getResult = function() { + if (!this.initialStats_) + throw new Error("Start not called."); + + if (!this.finalStats_) + throw new Error("Stop was not called."); + + var stats = this.finalStats_; + for (var key in stats) + stats[key] -= this.initialStats_[key]; + return stats; + }; + + GpuBenchmarkingRenderingStats.prototype.getRenderingStats_ = function() { + var stats = chrome.gpuBenchmarking.renderingStats(); + stats.totalTimeInSeconds = getTimeMs() / 1000; + return stats; + }; + + /** + * Tracks rendering performance using requestAnimationFrame. + * @constructor + */ + function RafRenderingStats() { + this.recording_ = false; + this.frameTimes_ = []; + } + + RafRenderingStats.prototype.start = function() { + if (this.recording_) + throw new Error("Already started."); + this.recording_ = true; + requestAnimationFrame(this.recordFrameTime_.bind(this)); + } + + RafRenderingStats.prototype.stop = function() { + this.recording_ = false; + } + + RafRenderingStats.prototype.getResult = function() { + var result = {}; + result.numAnimationFrames = this.frameTimes_.length - 1; + result.droppedFrameCount = this.getDroppedFrameCount_(this.frameTimes_); + result.totalTimeInSeconds = (this.frameTimes_[this.frameTimes_.length - 1] - + this.frameTimes_[0]) / 1000; + return result; + }; + + RafRenderingStats.prototype.recordFrameTime_ = function(timestamp) { + if (!this.recording_) + return; + + this.frameTimes_.push(timestamp); + requestAnimationFrame(this.recordFrameTime_.bind(this)); + }; + + RafRenderingStats.prototype.getDroppedFrameCount_ = function(frameTimes) { + var droppedFrameCount = 0; + for (var i = 1; i < frameTimes.length; i++) { + var frameTime = frameTimes[i] - frameTimes[i-1]; + if (frameTime > 1000 / 55) + droppedFrameCount++; + } + return droppedFrameCount; + }; + + // This class scrolls a page from the top to the bottom a given number of + // times. + // + // Each full transit of the page is called a "pass." + // + // The page is scrolled down by a set of scroll gestures. These gestures + // correspond to a reading gesture on that platform. + // + // i.e. for TOTAL_ITERATIONS_ = 2, we do + // start_ -> startPass_ -> ...scrolling... -> onGestureComplete_ -> + // -> startPass_ -> .. scrolling... -> onGestureComplete_ -> callback_ + // + // TODO(nduca): This test starts in its constructor. That is strange. We + // should change it to start explicitly. + function ScrollTest(opt_callback, opt_isGmailTest) { + var self = this; + + this.TOTAL_ITERATIONS_ = 1; + + this.callback_ = opt_callback; + this.isGmailTest_ = opt_isGmailTest; + this.iteration_ = 0; + + this.results_ = [] + + if (this.isGmailTest_) { + gmonkey.load('2.0', function(api) { + self.start_(api.getScrollableElement()); + }); + } else { + if (document.readyState == 'interactive' || + document.readyState == 'complete') + this.start_(); + else + window.addEventListener('load', function() { self.start_(); }); + } + } + + ScrollTest.prototype.start_ = function(opt_element) { + // Assign this.element_ here instead of constructor, because the constructor + // ensures this method will be called after the document is loaded. + this.element_ = opt_element || document.body; + // Some pages load more content when you scroll to the bottom. Record + // the original element height here and only scroll to that point. + this.scrollHeight_ = this.element_.scrollHeight + requestAnimationFrame(this.startPass_.bind(this)); + }; + + ScrollTest.prototype.startPass_ = function() { + this.element_.scrollTop = 0; + if (window.chrome && chrome.gpuBenchmarking) + this.renderingStats_ = new GpuBenchmarkingRenderingStats(); + else + this.renderingStats_ = new RafRenderingStats(); + this.renderingStats_.start(); + + this.gesture_ = new SmoothScrollDownGesture(this.element_, + this.isGmailTest_); + this.gesture_.start(this.onGestureComplete_.bind(this)); + }; + + ScrollTest.prototype.onGestureComplete_ = function(timestamp) { + // clientHeight is "special" for the body element. + var clientHeight; + if (this.element_ == document.body) + clientHeight = window.innerHeight; + else + clientHeight = this.element_.clientHeight; + + // If the scrollHeight went down, only scroll to the new scrollHeight. + this.scrollHeight_ = Math.min(this.scrollHeight_, + this.element_.scrollHeight); + + var isPassComplete = + this.element_.scrollTop + clientHeight >= this.scrollHeight_; + + if (!isPassComplete) { + this.gesture_.start(this.onGestureComplete_.bind(this)); + return; + } + + this.endPass_(); + + var isTestComplete = this.iteration_ >= this.TOTAL_ITERATIONS_; + if (!isTestComplete) { + this.startPass_(); + return; + } + + // Send results. + if (this.callback_) + this.callback_(this.results_); + else + console.log(this.results_); + }; + + ScrollTest.prototype.endPass_ = function() { + this.renderingStats_.stop(); + this.results_.push(this.renderingStats_.getResult()); + this.iteration_++; + }; + + + window.__ScrollTest = ScrollTest; +})(); diff --git a/tools/gpu/pylib/scroll.py b/tools/gpu/pylib/scroll.py new file mode 100644 index 0000000..fea5e3e --- /dev/null +++ b/tools/gpu/pylib/scroll.py @@ -0,0 +1,33 @@ +# 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 chrome_remote_control +import scroll_results + +def Scroll(tab): + scroll_js_path = os.path.join(os.path.dirname(__file__), 'scroll.js') + scroll_js = open(scroll_js_path, 'r').read() + + # Run scroll test. + tab.runtime.Evaluate(scroll_js) + tab.runtime.Evaluate(""" + window.__scrollTestResult = null; + new __ScrollTest(function(result) { + window.__scrollTestResult = result; + }); + null; // Avoid returning the entire __ScrollTest object. + """) + + # Poll for scroll result. + chrome_remote_control.WaitFor( + lambda: tab.runtime.Evaluate('window.__scrollTestResult')) + + # Get scroll results. + url = tab.runtime.Evaluate('document.location.href') + first_paint_secs = tab.runtime.Evaluate( + 'chrome.loadTimes().firstPaintTime - chrome.loadTimes().startLoadTime') + results_list = tab.runtime.Evaluate('window.__scrollTestResult') + + return scroll_results.ScrollResults(url, first_paint_secs, results_list) diff --git a/tools/gpu/pylib/scroll_results.py b/tools/gpu/pylib/scroll_results.py new file mode 100644 index 0000000..ea0ac19 --- /dev/null +++ b/tools/gpu/pylib/scroll_results.py @@ -0,0 +1,41 @@ +# 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. +class ScrollResults(object): + """Container for ScrollTest results.""" + + def __init__(self, url, first_paint_seconds, results_list): + self._url = url + self._first_paint_time = first_paint_seconds + self._results_list = results_list + + def DidScroll(self): + for index in xrange(len(self._results_list)): + if self.GetFrameCount(index) > 0: + return True + return False + + def GetFirstPaintTime(self): + return self._first_paint_time + + def GetFps(self, index): + return (self.GetFrameCount(index) / + self.GetResult(index)['totalTimeInSeconds']) + + def GetFrameCount(self, index): + results = self.GetResult(index) + return results.get('numFramesSentToScreen', results['numAnimationFrames']) + + def GetMeanFrameTime(self, index): + return (self.GetResult(index)['totalTimeInSeconds'] / + self.GetFrameCount(index)) + + def GetPercentBelow60Fps(self, index): + return (float(self.GetResult(index)['droppedFrameCount']) / + self.GetFrameCount(index)) + + def GetResult(self, index): + return self._results_list[index] + + def GetUrl(self): + return self._url diff --git a/tools/gpu/pylib/url_test.py b/tools/gpu/pylib/url_test.py new file mode 100644 index 0000000..c3a5ed2 --- /dev/null +++ b/tools/gpu/pylib/url_test.py @@ -0,0 +1,48 @@ +# 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_scroll_test.py b/tools/gpu/run_scroll_test.py new file mode 100644 index 0000000..a6405a1 --- /dev/null +++ b/tools/gpu/run_scroll_test.py @@ -0,0 +1,34 @@ +#!/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 new file mode 100644 index 0000000..e2c295f --- /dev/null +++ b/tools/gpu/scroll_test_unittest.py @@ -0,0 +1,30 @@ +#!/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() |