#!/usr/bin/env python3 # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. from contextlib import contextmanager import json import os from selenium import webdriver from selenium.common.exceptions import TimeoutException import sys @contextmanager def create_gecko_session(): try: firefox_binary = os.environ['FIREFOX_BIN'] except KeyError: print("+=============================================================+") print("| You must set the path to your firefox binary to FIREFOX_BIN |") print("+=============================================================+") sys.exit() driver = webdriver.Firefox(firefox_binary=firefox_binary) yield driver # driver.quit() gives an "'NoneType' object has no attribute 'path'" error. # Fixed in # https://github.com/SeleniumHQ/selenium/commit/9157c7071f9900c2608f5ca40ae4f518ed373b96 driver.quit() def generate_placeholder(testcase): # We need to still include the failed tests, otherwise Treeherder will # consider the result to be a new test series, and thus a new graph. So we # use a placeholder with values = -1 to make Treeherder happy, and still be # able to identify failed tests (successful tests have time >=0). timings = { "testcase": testcase, "title": "" } timing_names = [ "navigationStart", "unloadEventStart", "domLoading", "fetchStart", "responseStart", "loadEventEnd", "connectStart", "domainLookupStart", "redirectStart", "domContentLoadedEventEnd", "requestStart", "secureConnectionStart", "connectEnd", "loadEventStart", "domInteractive", "domContentLoadedEventStart", "redirectEnd", "domainLookupEnd", "unloadEventEnd", "responseEnd", "domComplete", ] for name in timing_names: timings[name] = 0 if name == "navigationStart" else -1 return [timings] def run_gecko_test(testcase, url, date, timeout, is_async): with create_gecko_session() as driver: driver.set_page_load_timeout(timeout) try: driver.get(url) except TimeoutException: print("Timeout!") return generate_placeholder(testcase) try: timings = { "testcase": testcase, "title": driver.title.replace(",", ",") } timings.update(json.loads( driver.execute_script( "return JSON.stringify(performance.timing)" ) )) except Exception: # We need to return a timing object no matter what happened. # See the comment in generate_placeholder() for explanation print("Failed to get a valid timing measurement.") return generate_placeholder(testcase) if is_async: # TODO: the timeout is hardcoded driver.implicitly_wait(5) # sec driver.find_element_by_id("GECKO_TEST_DONE") timings.update(json.loads( driver.execute_script( "return JSON.stringify(window.customTimers)" ) )) return [timings] if __name__ == '__main__': # Just for manual testing from pprint import pprint url = "http://localhost:8000/page_load_test/tp5n/dailymail.co.uk/www.dailymail.co.uk/ushome/index.html" pprint(run_gecko_test(url, 15))