def Close(self): super(DesktopBrowserBackend, self).Close() if self._proc: def IsClosed(): if not self._proc: return True return self._proc.poll() != None # Try to politely shutdown, first. self._proc.terminate() try: util.WaitFor(IsClosed, timeout=1) self._proc = None except util.TimeoutException: pass # 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._tmp_output_file: self._tmp_output_file.close() self._tmp_output_file = None
def testActivateTab(self): self.assertTrue(_IsDocumentVisible(self._tab)) new_tab = self._browser.tabs.New() util.WaitFor(lambda: _IsDocumentVisible(new_tab), timeout=5) self.assertFalse(_IsDocumentVisible(self._tab)) self._tab.Activate() util.WaitFor(lambda: _IsDocumentVisible(self._tab), timeout=5) self.assertFalse(_IsDocumentVisible(new_tab))
def MeasurePage(self, page, tab, results): js_is_done = 'window.document.cookie.indexOf("__done=1") >= 0' def _IsDone(): return bool(tab.runtime.Evaluate(js_is_done)) util.WaitFor(_IsDone, 600, poll_interval=5) js_get_results = 'JSON.stringify(window.automation.GetResults())' print js_get_results score = eval(tab.runtime.Evaluate(js_get_results)) def Escape(k): chars = [' ', '-', '/', '(', ')', '*'] for c in chars: k = k.replace(c, '_') return k suffix = page.url[page.url.index('?') + 1:page.url.index('&')] for k, v in score.iteritems(): data_type = 'unimportant' if k == suffix: data_type = 'default' results.Add('score', 'runs/s', v, chart_name=Escape(k), data_type=data_type)
def MeasurePage(self, _, tab, results): js_is_done = """ completed && !document.getElementById("progress-bar-container")""" def _IsDone(): return bool(tab.runtime.Evaluate(js_is_done)) util.WaitFor(_IsDone, 300, poll_interval=5) js_get_results = """ var results = {} var result_divs = document.querySelectorAll('.p-result'); for (var r in result_divs) { if (result_divs[r].id && result_divs[r].id.indexOf('Result-') == 0) var key = result_divs[r].id.replace('Result-', ''); results[key] = result_divs[r].innerHTML; } var main_banner = document.getElementById("main-banner").innerHTML; var octane_score = main_banner.substr(main_banner.lastIndexOf(':') + 2); results['score'] = octane_score; JSON.stringify(results); """ result_dict = eval(tab.runtime.Evaluate(js_get_results)) for key, value in result_dict.iteritems(): if value == '...': continue data_type = 'unimportant' if key == 'score': data_type = 'default' results.Add(key, 'score (bigger is better)', value, data_type=data_type)
def WaitForDocumentReadyStateToBeInteractiveOrBetter( self, timeout=DEFAULT_TAB_TIMEOUT): def IsReadyStateInteractiveOrBetter(): rs = self._runtime.Evaluate('document.readyState') return rs == 'complete' or rs == 'interactive' util.WaitFor(IsReadyStateInteractiveOrBetter, timeout)
def _WaitForFormToLoad(form_id, tab): def IsFormLoaded(): return tab.runtime.Evaluate( 'document.querySelector("#%s")!== null' % form_id) # Wait until the form is submitted and the page completes loading. util.WaitFor(lambda: IsFormLoaded(), 60) # pylint: disable=W0108
def SetBrowser(self, browser): super(CrOSBrowserBackend, self).SetBrowser(browser) # TODO(hartmanng): crbug.com/166886 (Remove these temporary hacks when # _ListTabs is fixed) # Wait for the oobe login screen to disappear. Unfortunately, once it does, # our TabController needs to be refreshed to point at the new non-login tab. tab_url = None # When tab_url is None, we have to create or refresh the TabController # and wait for the oobe login screen to disappear. while tab_url is None: self.tabs = browser_backend.TabController(browser, self) if len(self.tabs) == 0: break # Wait for the login screen to disappear. This can cause tab_url to be # None or to not be 'chrome://oobe/login'. def IsTabNoneOrOobeLogin(): tab_url = self.tabs[0].url return tab_url is None or tab_url != 'chrome://oobe/login' util.WaitFor(lambda: IsTabNoneOrOobeLogin(), 60) # pylint: disable=W0108 # Refresh our tab_url variable with the current tab[0].url. If it is None # at this point, we need to continue the loop to refresh TabController. tab_url = self.tabs[0].url # Once we're sure that the login screen is gone, we can close all open tabs # to make sure the first-start window doesn't interfere. for _ in range(len(self.tabs)): self.tabs[0].Close()
def PerformActionAndWaitForNavigate(self, action_function, timeout=60): """Executes action_function, and waits for the navigation to complete. action_function is expect to result in a navigation. This function returns when the navigation is complete or when the timeout has been exceeded. """ # 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 def DisablePageNotifications(): request = {'method': 'Page.disable'} res = self._inspector_backend.SyncRequest(request, timeout) assert len(res['result'].keys()) == 0 self._navigation_pending = True try: action_function() except: DisablePageNotifications() raise def IsNavigationDone(time_left): self._inspector_backend.DispatchNotifications(time_left) return not self._navigation_pending util.WaitFor(IsNavigationDone, timeout, pass_time_left_to_func=True) DisablePageNotifications()
def Wait(self, timeout=1): if not self._pid: raise Exception('Closed') def IsDone(): return not self.IsAlive() util.WaitFor(IsDone, timeout) self._pid = None
def PerformInteraction(self, page, 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.Execute(scroll_js) with tab.browser.platform.GetSurfaceCollector(''): start_scroll_js = """ window.__renderingStatsDeltas = null; new __ScrollTest(function(rendering_stats_deltas) { window.__renderingStatsDeltas = rendering_stats_deltas; }).start(element); """ # scrollable_element_function is a function that passes the scrollable # element on the page to a callback. For example: # function (callback) { # callback(document.getElementById('foo')); # } if hasattr(self, 'scrollable_element_function'): tab.runtime.Execute( '(%s)(function(element) { %s });' % (self.scrollable_element_function, start_scroll_js)) else: tab.runtime.Execute( '(function() { var element = document.body; %s})();' % start_scroll_js) # Poll for scroll benchmark completion. util.WaitFor( lambda: tab.runtime.Evaluate('window.__renderingStatsDeltas'), 60)
def __init__(self, cri, forwarding_flag, *ports): self._proc = None self._host_port = ports[0][0] port_pairs = [] for port in ports: if port[1] is None: port_pairs.append((port[0], cri.GetRemotePort())) else: port_pairs.append(port) if forwarding_flag == 'R': self._device_port = port_pairs[0][0] else: self._device_port = port_pairs[0][1] self._proc = subprocess.Popen( cri.FormSSHCommandLine( ['sleep', '999999999'], ['-%s%i:localhost:%i' % (forwarding_flag, from_port, to_port) for from_port, to_port in port_pairs]), stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, shell=False) util.WaitFor(lambda: cri.IsHTTPServerRunningOnPort(self._device_port), 60)
def MeasurePage(self, _, tab, results): js_is_done = """ window.location.pathname.indexOf('sunspider-results') >= 0""" def _IsDone(): return tab.runtime.Evaluate(js_is_done) util.WaitFor(_IsDone, 300, poll_interval=5) js_get_results = 'JSON.stringify(output);' js_results = json.loads(tab.runtime.Evaluate(js_get_results)) r = collections.defaultdict(list) totals = [] # js_results is: [{'foo': v1, 'bar': v2}, # {'foo': v3, 'bar': v4}, # ...] for result in js_results: total = 0 for key, value in result.iteritems(): r[key].append(value) total += value totals.append(total) for key, values in r.iteritems(): results.Add('t', 'ms', values, chart_name=key, data_type='unimportant') results.Add('t', 'ms', totals, chart_name='total')
def PerformInteraction(self, page, tab): def DoClick(): assert hasattr(self, 'selector') or hasattr(self, 'text') if hasattr(self, 'selector'): code = 'document.querySelector(\'' + self.selector + '\').click();' try: tab.runtime.Execute(code) except inspector_runtime.EvaluateException: raise page_interaction.PageInteractionFailed( 'Cannot find element with selector ' + self.selector) else: callback_code = 'function(element) { element.click(); }' try: util.FindElementAndPerformAction(tab, self.text, callback_code) except inspector_runtime.EvaluateException: raise page_interaction.PageInteractionFailed( 'Cannot find element with text ' + self.text) if hasattr(self, 'wait_for_navigate'): tab.page.PerformActionAndWaitForNavigate(DoClick) elif hasattr(self, 'wait_for_href_change'): old_url = tab.runtime.Evaluate('document.location.href') DoClick() util.WaitFor( lambda: tab.runtime.Evaluate('document.location.href') != old_url, 60) else: DoClick() page_module.Page.WaitForPageToLoad(self, tab, 60) tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
def __init__(self, browser_backend, path): self._server = None self._devnull = None self._path = path self._forwarder = None tmp = socket.socket() tmp.bind(('', 0)) port = tmp.getsockname()[1] tmp.close() self._host_port = port assert os.path.exists(path), path assert os.path.isdir(path), path self._devnull = open(os.devnull, 'w') self._server = subprocess.Popen( [sys.executable, '-m', 'SimpleHTTPServer', str(self._host_port)], cwd=self._path, stdout=self._devnull, stderr=self._devnull) self._forwarder = browser_backend.CreateForwarder( util.PortPair(self._host_port, None)) def IsServerUp(): return not socket.socket().connect_ex( ('localhost', self._host_port)) util.WaitFor(IsServerUp, 5)
def MeasurePage(self, _, tab, results): def _IsDone(): return tab.page.GetCookieByName('__pc_done') == '1' util.WaitFor(_IsDone, 1200, poll_interval=5) print 'Pages: [%s]' % tab.page.GetCookieByName('__pc_pages') # TODO(tonyg): Get IO and memory statistics. for histogram in MEMORY_HISTOGRAMS: name = histogram['name'] data = tab.runtime.Evaluate( 'window.domAutomationController.getHistogram("%s")' % name) results.Add(name, histogram['units'], data, data_type='histogram') def _IsNavigatedToReport(): return tab.page.GetCookieByName('__navigated_to_report') == '1' util.WaitFor(_IsNavigatedToReport, 60, poll_interval=5) timings = tab.runtime.Evaluate('__get_timings()').split(',') results.Add('t', 'ms', [int(t) for t in timings], chart_name='times')
def _SubmitFormAndWait(form_id, tab): js = 'document.getElementById("%s").submit();' % form_id tab.runtime.Execute(js) def IsLoginStillHappening(): return tab.runtime.Evaluate( 'document.querySelector("#%s")!== null' % form_id) # Wait until the form is submitted and the page completes loading. util.WaitFor(lambda: not IsLoginStillHappening(), 60)
def MeasurePage(self, _, tab, results): util.WaitFor( lambda: tab.runtime.Evaluate( '!document.getElementById("start-performance-tests").disabled' ), 60) tab.runtime.Execute(""" window.__results = {}; window.console.log = function(str) { if (!str) return; var key_val = str.split(': '); if (!key_val.length == 2) return; __results[key_val[0]] = key_val[1]; }; document.getElementById('start-performance-tests').click(); """) js_get_results = 'JSON.stringify(window.__results)' num_tests_complete = [0] # A list to work around closure issue. def _IsDone(): num_tests_in_benchmark = 24 num_results = len(eval(tab.runtime.Evaluate(js_get_results))) if num_results > num_tests_complete[0]: num_tests_complete[0] = num_results logging.info('Completed benchmark %d of %d' % (num_tests_complete[0], num_tests_in_benchmark)) return num_tests_complete[0] >= num_tests_in_benchmark util.WaitFor(_IsDone, 1200, poll_interval=5) result_dict = eval(tab.runtime.Evaluate(js_get_results)) for key in result_dict: chart, trace = key.split('.', 1) results.Add(trace, 'objects (bigger is better)', float(result_dict[key]), chart_name=chart, data_type='unimportant') results.Add('Overall', 'objects (bigger is better)', [float(x) for x in result_dict.values()])
def MeasurePage(self, _, tab, results): tab.runtime.Execute('ToggleRoboHornet()') done = 'document.getElementById("results").innerHTML.indexOf("Total") != -1' def _IsDone(): return tab.runtime.Evaluate(done) util.WaitFor(_IsDone, 60) result = int(tab.runtime.Evaluate('stopTime - startTime')) results.Add('Total', 'ms', result)
def _WaitForBrowserToComeUp(self, timeout=None): def IsBrowserUp(): try: self.Request('', timeout=timeout) except (socket.error, httplib.BadStatusLine, urllib2.URLError): return False else: return True try: util.WaitFor(IsBrowserUp, timeout=30) except util.TimeoutException: raise browser_gone_exception.BrowserGoneException()
def MeasurePage(self, _, tab, results): tab.runtime.Execute('UI.call({}, "perftest")') js_is_done = 'document.getElementById("perfscore0") != null' def _IsDone(): return bool(tab.runtime.Evaluate(js_is_done)) util.WaitFor(_IsDone, 1200) js_get_results = 'document.getElementById("perfscore0").innerHTML' result = int(tab.runtime.Evaluate(js_get_results)) results.Add('Score', 'score (bigger is better)', result)
def WaitForPageToLoad(obj, tab, timeout, poll_interval=0.1): """Waits for various wait conditions present in obj.""" if hasattr(obj, 'post_navigate_javascript_to_execute'): tab.runtime.Evaluate(obj.post_navigate_javascript_to_execute) if hasattr(obj, 'wait_seconds'): time.sleep(obj.wait_seconds) if hasattr(obj, 'wait_for_element_with_text'): callback_code = 'function(element) { return element != null; }' util.WaitFor( lambda: util.FindElementAndPerformAction( tab, obj.wait_for_element_with_text, callback_code), timeout, poll_interval) if hasattr(obj, 'wait_for_element_with_selector'): util.WaitFor( lambda: tab.runtime.Evaluate('document.querySelector(\'' + obj. wait_for_element_with_selector + '\') != null'), timeout, poll_interval) if hasattr(obj, 'wait_for_javascript_expression'): util.WaitFor( lambda: tab.runtime.Evaluate(obj.wait_for_javascript_expression ), timeout, poll_interval)
def MeasurePage(self, _, tab, results): def _IsDone(): return tab.runtime.Evaluate('isDone') with tab.timeline.Recorder(tab.timeline): tab.runtime.Execute('runBenchmark()') util.WaitFor(_IsDone, 60) iterations = tab.runtime.Evaluate('minIterations') decode_image = tab.timeline.timeline_events.GetAllOfType('DecodeImage') elapsed_times = [d.elapsed_time for d in decode_image[-iterations:]] if not elapsed_times: results.Add('ImageDecoding_avg', 'ms', 'unsupported') return image_decoding_avg = sum(elapsed_times) / len(elapsed_times) results.Add('ImageDecoding_avg', 'ms', image_decoding_avg)
def MeasurePage(self, _, tab, results): util.WaitFor( lambda: tab.runtime.Evaluate( '!document.getElementById("start-performance-tests").disabled' ), 60) tab.runtime.Execute(""" window.__results = {}; window.console.log = function(str) { if (!str) return; var key_val = str.split(': '); if (!key_val.length == 2) return; __results[key_val[0]] = key_val[1]; }; document.getElementById('start-performance-tests').click(); """) js_get_results = 'JSON.stringify(window.__results)' def _IsDone(): num_tests_in_benchmark = 24 result_dict = eval(tab.runtime.Evaluate(js_get_results)) return num_tests_in_benchmark == len(result_dict) util.WaitFor(_IsDone, 1200) result_dict = eval(tab.runtime.Evaluate(js_get_results)) for key in result_dict: chart, trace = key.split('.', 1) results.Add(trace, 'objects (bigger is better)', float(result_dict[key]), chart_name=chart, data_type='unimportant') results.Add('Overall', 'objects (bigger is better)', [float(x) for x in result_dict.values()])
def PerformInteraction(self, page, tab): duration = 10 if hasattr(self, 'duration'): duration = self.duration wait_js = """ window.__renderingStatsDeltas = null; 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 getRenderingStats = function() { var renderingStats = {}; if (chrome && chrome.gpuBenchmarking && chrome.gpuBenchmarking.renderingStats) renderingStats = chrome.gpuBenchmarking.renderingStats(); renderingStats.totalTimeInSeconds = getTimeMs() / 1000; return renderingStats; } var initialStats = getRenderingStats(); var waitFinishedCallback = function(init) { return function() { var final = getRenderingStats(); for (var key in final) final[key] -= init[key]; window.__renderingStatsDeltas = final; }; } window.setTimeout(waitFinishedCallback(initialStats), %d); """ % (duration * 1000) tab.runtime.Evaluate(wait_js) # Poll for scroll benchmark completion. util.WaitFor( lambda: tab.runtime.Evaluate('window.__renderingStatsDeltas'), 60)
def CloseTab(self, debugger_url, timeout=None): # TODO(dtu): crbug.com/160946, allow closing the last tab on some platforms. # For now, just create a new tab before closing the last tab. if len(self) <= 1: self.New() tab_id = debugger_url.split('/')[-1] try: response = self._browser_backend.Request('close/%s' % tab_id, timeout=timeout) except urllib2.HTTPError: raise Exception('Unable to close tab, tab id not found: %s' % tab_id) assert response == 'Target is closing' util.WaitFor(lambda: not self._FindTabInfo(debugger_url), timeout=5) self._UpdateTabList()
def CalcFirstPaintTimeResults(results, tab): if tab.browser.is_content_shell: results.Add('first_paint', 'ms', 'unsupported') return tab.runtime.Execute(""" window.__rafFired = false; window.webkitRequestAnimationFrame(function() { window.__rafFired = true; }); """) util.WaitFor(lambda: tab.runtime.Evaluate('window.__rafFired'), 60) first_paint_secs = tab.runtime.Evaluate( 'window.chrome.loadTimes().firstPaintTime - ' + 'window.chrome.loadTimes().startLoadTime') results.Add('first_paint', 'ms', round(first_paint_secs * 1000, 1))
def Screenshot(self, timeout=DEFAULT_SCREENSHOT_TIMEOUT): """Capture a screenshot of the window for rendering validation""" if self._tab.runtime.Evaluate( 'window.chrome.gpuBenchmarking === undefined'): raise Exception( "Browser was not started with --enable-gpu-benchmarking") if self._tab.runtime.Evaluate( 'window.chrome.gpuBenchmarking.beginWindowSnapshotPNG === undefined' ): raise Exception("Browser does not support window snapshot API.") self._tab.runtime.Evaluate(""" if(!window.__telemetry) { window.__telemetry = {} } window.__telemetry.snapshotComplete = false; window.__telemetry.snapshotData = null; window.chrome.gpuBenchmarking.beginWindowSnapshotPNG( function(snapshot) { window.__telemetry.snapshotData = snapshot; window.__telemetry.snapshotComplete = true; } ); """) def IsSnapshotComplete(): return self._tab.runtime.Evaluate( 'window.__telemetry.snapshotComplete') util.WaitFor(IsSnapshotComplete, timeout) snap = self._tab.runtime.Evaluate(""" (function() { var data = window.__telemetry.snapshotData; delete window.__telemetry.snapshotComplete; delete window.__telemetry.snapshotData; return data; })() """) if snap: return png_bitmap.PngBitmap(snap['data']) return None
def PerformInteraction(self, page, tab): def DoClick(): assert hasattr(self, 'selector') or hasattr(self, 'text') if hasattr(self, 'selector'): code = 'document.querySelector(\'' + self.selector + '\').click();' try: tab.runtime.Execute(code) except inspector_runtime.EvaluateException: raise page_interaction.PageInteractionFailed( 'Cannot find element with selector ' + self.selector) else: click_element = """ function clickElement(element, text) { if (element.innerHTML == text) { element.click(); return true; } for (var i in element.childNodes) { if (clickElement(element.childNodes[i], text)) return true; } return false; }""" tab.runtime.Execute(click_element) code = 'clickElement(document, "' + self.text + '");' if not tab.runtime.Evaluate(code): raise page_interaction.PageInteractionFailed( 'Cannot find element with text ' + self.text) if hasattr(self, 'wait_for_navigate'): tab.page.PerformActionAndWaitForNavigate(DoClick) elif hasattr(self, 'wait_for_href_change'): old_url = tab.runtime.Evaluate('document.location.href') DoClick() util.WaitFor(lambda: tab.runtime.Evaluate( 'document.location.href') != old_url, 60) elif hasattr(self, 'wait_seconds'): time.sleep(self.wait_seconds) DoClick() else: DoClick() tab.WaitForDocumentReadyStateToBeComplete()
def MeasurePage(self, _, tab, results): js_is_done = """ document.title.indexOf("Results") != -1 && document.readyState == "complete" """ def _IsDone(): return bool(tab.runtime.Evaluate(js_is_done)) util.WaitFor(_IsDone, 500, poll_interval=5) js_get_results = """ var formElement = document.getElementsByTagName("input")[0]; decodeURIComponent(formElement.value.split("?")[1]); """ result_dict = eval(tab.runtime.Evaluate(js_get_results)) total = 0 for key in result_dict: if key == 'v': continue results.Add(key, 'ms', result_dict[key], data_type='unimportant') total += _Mean(result_dict[key]) results.Add('Total', 'ms', total)
def __init__(self, cri, forwarding_flag, *port_pairs): self._proc = None new_port_pairs = [] for port_pair in port_pairs: if port_pair.remote_port is None: new_port_pairs.append( util.PortPair(port_pair.local_port, cri.GetRemotePort())) else: new_port_pairs.append(port_pair) if forwarding_flag == 'R': self._host_port = new_port_pairs[0].remote_port command_line = [ '-%s%i:localhost:%i' % (forwarding_flag, port_pair.remote_port, port_pair.local_port) for port_pair in new_port_pairs ] else: self._host_port = new_port_pairs[0].local_port command_line = [ '-%s%i:localhost:%i' % (forwarding_flag, port_pair.local_port, port_pair.remote_port) for port_pair in new_port_pairs ] self._device_port = new_port_pairs[0].remote_port self._proc = subprocess.Popen(cri.FormSSHCommandLine( ['sleep', '999999999'], command_line), stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, shell=False) util.WaitFor(lambda: cri.IsHTTPServerRunningOnPort(self._device_port), 60)