testing/talos/talos/utils.py
author Sebastian Hengst <archaeopteryx@coole-files.de>
Sat, 04 Feb 2017 13:47:33 +0100
changeset 478969 ec5148f280e4684b9b4010e295684f3aa0bb871d
parent 370626 e6fce92905c8c40ff06a0cb14d4d0af4453c1a94
child 602696 03b30db3fd23f0da3a1f7a7f81c9ecb2101c1ec1
permissions -rwxr-xr-x
Bug 1336712 - Drop test annotations and checks for Windows XP, Vista OS X < 10.9 and Android Gingerbread (2.3): testing/talos r?RyanVM MozReview-Commit-ID: 8WwibE50Cer

# 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 http://mozilla.org/MPL/2.0/.

"""Utility functions for Talos"""

import os
import time
import urlparse
import string
import urllib
import json
import re
import platform

from mozlog import get_proxy_logger

# directory of this file for use with interpolatePath()
here = os.path.dirname(os.path.realpath(__file__))
LOG = get_proxy_logger()


def _get_platform():
    # get the platform we're interested in. Note that the values
    # are used in TTest historically, this is why they are not really friendly.
    # TODO: give some user friendly values
    if platform.system() == "Linux":
        return 'linux_'
    elif platform.system() in ("Windows", "Microsoft"):
        if '6.1' in platform.version():  # w7
            return 'w7_'
        elif '6.2' in platform.version():  # w8
            return 'w8_'
        # Bug 1264325 - FIXME: with python 2.7.11: reports win8 instead of 8.1
        elif '6.3' in platform.version():
            return 'w8_'
        # Bug 1264325 - FIXME: with python 2.7.11: reports win8 instead of 10
        elif '10.0' in platform.version():
            return 'w8_'
        else:
            raise TalosError('unsupported windows version')
    elif platform.system() == "Darwin":
        return 'mac_'


PLATFORM_TYPE = _get_platform()


class Timer(object):
    def __init__(self):
        self._start_time = 0
        self.start()

    def start(self):
        self._start_time = time.time()

    def elapsed(self):
        seconds = time.time() - self._start_time
        return time.strftime("%H:%M:%S", time.gmtime(seconds))


class TalosError(Exception):
    "Errors found while running the talos harness."


class TalosRegression(Exception):
    """When a regression is detected at runtime, report it properly
       Currently this is a simple definition so we can detect the class type
    """


class TalosCrash(Exception):
    """Exception type where we want to report a crash and stay
       compatible with tbpl while allowing us to continue on.

       https://bugzilla.mozilla.org/show_bug.cgi?id=829734
    """


def interpolate(template, **kwargs):
    """
    Use string.Template to substitute variables in a string.

    The placeholder ${talos} is always defined and will be replaced by the
    folder containing this file (global variable 'here').

    You can add placeholders using kwargs.
    """
    kwargs.setdefault('talos', here)
    return string.Template(template).safe_substitute(**kwargs)


def findall(string, token):
    """find all occurences in a string"""
    return [m.start() for m in re.finditer(re.escape(token), string)]


def tokenize(string, start, end):
    """
    tokenize a string by start + end tokens,
    returns parts and position of last token
    """
    assert end not in start, \
        "End token '%s' is contained in start token '%s'" % (end, start)
    assert start not in end, \
        "Start token '%s' is contained in end token '%s'" % (start, end)
    _start = findall(string, start)
    _end = findall(string, end)
    if not _start and not _end:
        return [], -1
    assert len(_start), "Could not find start token: '%s'" % start
    assert len(_end), "Could not find end token: '%s'" % end
    assert len(_start) == len(_end), \
        ("Unmatched number of tokens found: '%s' (%d) vs '%s' (%d)"
         % (start, len(_start), end, len(_end)))
    for i in range(len(_start)):
        assert _end[i] > _start[i], \
            "End token '%s' occurs before start token '%s'" % (end, start)
    parts = []
    for i in range(len(_start)):
        parts.append(string[_start[i] + len(start):_end[i]])
    return parts, _end[-1]


def urlsplit(url, default_scheme='file'):
    """front-end to urlparse.urlsplit"""

    if '://' not in url:
        url = '%s://%s' % (default_scheme, url)

    if url.startswith('file://'):
        # file:// URLs do not play nice with windows
        # https://bugzilla.mozilla.org/show_bug.cgi?id=793875
        return ['file', '', url[len('file://'):], '', '']

    # split the URL and return a list
    return [i for i in urlparse.urlsplit(url)]


def parse_pref(value):
    """parse a preference value from a string"""
    from mozprofile.prefs import Preferences
    return Preferences.cast(value)


def GenerateBrowserCommandLine(browser_path, extra_args, profile_dir,
                               url, profiling_info=None):
    # TODO: allow for spaces in file names on Windows

    command_args = [browser_path.strip()]
    if platform.system() == "Darwin":
        command_args.extend(['-foreground'])

    if isinstance(extra_args, list):
        command_args.extend(extra_args)

    elif extra_args.strip():
        command_args.extend([extra_args])

    command_args.extend(['-profile', profile_dir])

    if profiling_info:
        # For pageloader, buildCommandLine() puts the -tp* command line
        # options into the url argument.
        # It would be better to assemble all -tp arguments in one place,
        # but we don't have the profiling information in buildCommandLine().
        if url.find(' -tp') != -1:
            command_args.extend(['-tpprofilinginfo',
                                 json.dumps(profiling_info)])
        elif url.find('?') != -1:
            url += '&' + urllib.urlencode(profiling_info)
        else:
            url += '?' + urllib.urlencode(profiling_info)

    command_args.extend(url.split(' '))

    return command_args


def indexed_items(itr):
    """
    Generator that allows us to figure out which item is the last one so
    that we can serialize this data properly
    """
    prev_i, prev_val = 0, itr.next()
    for i, val in enumerate(itr, start=1):
        yield prev_i, prev_val
        prev_i, prev_val = i, val
    yield -1, prev_val