1# This Source Code Form is subject to the terms of the Mozilla Public 2# License, v. 2.0. If a copy of the MPL was not distributed with this 3# file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5"""Utility functions for Talos""" 6from __future__ import absolute_import 7 8import os 9import platform 10import re 11from sys import stdout 12import urllib.parse 13import string 14import time 15 16try: 17 from mozlog import get_proxy_logger 18 19 LOG = get_proxy_logger() 20except ModuleNotFoundError: 21 LOG = stdout 22 23# directory of this file for use with interpolatePath() 24here = os.path.dirname(os.path.realpath(__file__)) 25 26 27class Timer(object): 28 def __init__(self): 29 self._start_time = 0 30 self.start() 31 32 def start(self): 33 self._start_time = time.time() 34 35 def elapsed(self): 36 seconds = time.time() - self._start_time 37 return time.strftime("%H:%M:%S", time.gmtime(seconds)) 38 39 40class TalosError(Exception): 41 "Errors found while running the talos harness." 42 43 44class TalosRegression(Exception): 45 """When a regression is detected at runtime, report it properly 46 Currently this is a simple definition so we can detect the class type 47 """ 48 49 50class TalosCrash(Exception): 51 """Exception type where we want to report a crash and stay 52 compatible with tbpl while allowing us to continue on. 53 54 https://bugzilla.mozilla.org/show_bug.cgi?id=829734 55 """ 56 57 58def interpolate(template, **kwargs): 59 """ 60 Use string.Template to substitute variables in a string. 61 62 The placeholder ${talos} is always defined and will be replaced by the 63 folder containing this file (global variable 'here'). 64 65 You can add placeholders using kwargs. 66 """ 67 kwargs.setdefault("talos", here) 68 return string.Template(template).safe_substitute(**kwargs) 69 70 71def findall(string, token): 72 """find all occurences in a string""" 73 return [m.start() for m in re.finditer(re.escape(token), string)] 74 75 76def tokenize(string, start, end): 77 """ 78 tokenize a string by start + end tokens, 79 returns parts and position of last token 80 """ 81 assert end not in start, "End token '%s' is contained in start token '%s'" % ( 82 end, 83 start, 84 ) 85 assert start not in end, "Start token '%s' is contained in end token '%s'" % ( 86 start, 87 end, 88 ) 89 _start = findall(string, start) 90 _end = findall(string, end) 91 if not _start and not _end: 92 return [], -1 93 assert len(_start), "Could not find start token: '%s'" % start 94 assert len(_end), "Could not find end token: '%s'" % end 95 assert len(_start) == len( 96 _end 97 ), "Unmatched number of tokens found: '%s' (%d) vs '%s' (%d)" % ( 98 start, 99 len(_start), 100 end, 101 len(_end), 102 ) 103 for i in range(len(_start)): 104 assert _end[i] > _start[i], "End token '%s' occurs before start token '%s'" % ( 105 end, 106 start, 107 ) 108 parts = [] 109 for i in range(len(_start)): 110 parts.append(string[_start[i] + len(start) : _end[i]]) 111 return parts, _end[-1] 112 113 114def urlsplit(url, default_scheme="file"): 115 """front-end to urlparse.urlsplit""" 116 117 if "://" not in url: 118 url = "%s://%s" % (default_scheme, url) 119 120 if url.startswith("file://"): 121 # file:// URLs do not play nice with windows 122 # https://bugzilla.mozilla.org/show_bug.cgi?id=793875 123 return ["file", "", url[len("file://") :], "", ""] 124 125 # split the URL and return a list 126 return [i for i in urllib.parse.urlsplit(url)] 127 128 129def parse_pref(value): 130 """parse a preference value from a string""" 131 from mozprofile.prefs import Preferences 132 133 return Preferences.cast(value) 134 135 136def GenerateBrowserCommandLine( 137 browser_path, extra_args, profile_dir, url, profiling_info=None 138): 139 # TODO: allow for spaces in file names on Windows 140 command_args = [browser_path.strip()] 141 142 if platform.system() == "Darwin": 143 command_args.extend(["-foreground"]) 144 145 if isinstance(extra_args, list): 146 command_args.extend(extra_args) 147 148 elif extra_args.strip(): 149 command_args.extend([extra_args]) 150 151 command_args.extend(["-profile", profile_dir]) 152 153 if profiling_info: 154 # pageloader tests use a tpmanifest browser pref instead of passing in a manifest url 155 # profiling info is handled differently for pageloader vs non-pageloader (startup) tests 156 # for pageloader the profiling info was mirrored already in an env var; so here just 157 # need to setup profiling info for startup / non-pageloader tests 158 if url is not None: 159 # for non-pageloader/non-manifest tests the profiling info is added to the test url 160 if url.find("?") != -1: 161 url += "&" + urllib.parse.urlencode(profiling_info) 162 else: 163 url += "?" + urllib.parse.urlencode(profiling_info) 164 command_args.extend(url.split(" ")) 165 166 # if there's a url i.e. startup test / non-manifest test, add it to the cmd line args 167 if url is not None: 168 command_args.extend(url.split(" ")) 169 170 return command_args 171 172 173def run_in_debug_mode(browser_config): 174 if ( 175 browser_config.get("debug") 176 or browser_config.get("debugger") 177 or browser_config.get("debugg_args") 178 ): 179 return True 180 return False 181