<commit_before># Copyright (c) 2005 rPath, Inc.
#
# This program is distributed under the terms of the Common Public License,
# version 1.0. A copy of this license should have been distributed with this
# source file in a file called LICENSE. If it is not present, the license
# is always available at http://www.opensource.org/licenses/cpl.php.
#
# This program is distributed in the hope that it will be useful, but
# without any waranty; without even the implied warranty of merchantability
# or fitness for a particular purpose. See the Common Public License for
# full details.
#

from conary import trove, versions
from conary.deps import deps
from conary.lib import util
from conary.repository import errors
from conary.build import errors as builderrors
from conary.build import macros
from conary.build import use
from conary.build.recipe import Recipe, RECIPE_TYPE_REDIRECT

import itertools

class RedirectRecipe(Recipe):
    Flags = use.LocalFlags
    _recipeType = RECIPE_TYPE_REDIRECT
    ignore = 1

    def addRedirect(self, name, branchStr = None, sourceFlavor = None,
                    targetFlavor = None, fromTrove = None):
        if (sourceFlavor and not targetFlavor) or \
           (targetFlavor and not sourceFlavor):
            raise builderrors.RecipeFileError, \
                "sourceFlavor and targetFlavor must be specified jointly"

        if sourceFlavor is not None:
            f = deps.parseFlavor(sourceFlavor)
            if f is None:
                raise ValueError, 'invalid flavor %s' % sourceFlavor
            sourceFlavor = f

        if targetFlavor is not None:
            f = deps.parseFlavor(targetFlavor)
            if f is None:
                raise ValueError, 'invalid flavor %s' % targetFlavor
            targetFlavor = f

        if fromTrove is None:
            fromTrove = self.name
        elif fromTrove.find(":") != -1:
            raise ValueError, 'components cannot be individually redirected'
        elif fromTrove.startswith("group-"):
            # how sad
            raise ValueError, "groups cannot be redirected"

        self.addTroveList.append((name, branchStr, sourceFlavor,
                                  targetFlavor, fromTrove))

    def findTroves(self):
        self.size = 0

        validSize = True
        troveList = []

        packageSet = {}

        sourceSearch = {}
        fromRule = {}
        for (name, branchStr, sourceFlavor, targetFlavor, fromTrove) in \
                                self.addTroveList:
            l = fromRule.setdefault(fromTrove, list())
            # the catch-all (with no sourceFlavor) has to be at the end
            if not sourceFlavor:
                l.append((name, branchStr, sourceFlavor, targetFlavor))
            else:
                l.insert(0, (name, branchStr, sourceFlavor, targetFlavor))

            d = sourceSearch.setdefault(fromTrove, { self.branch : None })

        sourceTroveMatches = self.repos.getTroveLeavesByBranch(sourceSearch)

        if len(sourceTroveMatches) != len(sourceSearch):
            missing = set(sourceSearch) - set(sourceTroveMatches)
            raise builderrors.RecipeFileError, \
                    "No troves found with name(s) %s" % " ".join(missing)

        l = []
        for name, d in sourceTroveMatches.iteritems():
            for version, flavorList in d.iteritems():
                l += [ (name, (None, None), (version, x), True)
                                for x in flavorList ]

        trvCsDict = {}
        # We don't need to recurse here since we only support package
        # redirects
        cs = self.repos.createChangeSet(l, recurse = True, withFiles = False)
        for trvCs in cs.iterNewTroveList():
            info = (trvCs.getName(), trvCs.getNewVersion(),
                    trvCs.getNewFlavor())
            trvCsDict[info] = trvCs

        redirMap = {}

        names = sourceTroveMatches.keys()
        additionalNameQueue = util.IterableQueue()
        for name in itertools.chain(names, additionalNameQueue):
            versionDict = sourceTroveMatches.pop(name)

            destSet = fromRule.get(name, None)
            if destSet is None and ':' in name:
                # package redirections imply component redirections
                pkgName, compName = name.split(':')
                destSet = fromRule.get(pkgName, None)
                if destSet is not None:
                    destSet = set(
                        [ (x[0] + ':' + compName,) + x[1:] for x in destSet ])

            if destSet is None:
                raise builderrors.RecipeFileError, \
                    "Cannot find redirection for trove %s" % name

            # XXX the repository operations should be pulled out of all of
            # these loops
            additionalNames = set()
            for (destName, branchStr, sourceFlavorRestriction,
                                      targetFlavorRestriction) in destSet:
                if branchStr[0] == '/':
                    branch = versions.VersionFromString(branchStr)
                    if not isinstance(branch, versions.Branch):
                        raise builderrors.RecipeFileError, \
                            "Redirects must specify branches or labels, " \
                            "not versions"

                    matches = self.repos.getTroveLeavesByBranch(
                                    { destName : { branch : None } })
                else:
                    label = versions.Label(branchStr)
                    matches = self.repos.getTroveLeavesByLabel(
                                    { destName : { label : None } })
                    # if there are multiple versions returned, the label
                    # specified multiple branches
                    if matches and len(matches[destName]) > 1:
                        raise builderrors.RecipeFileError, \
                            "Label %s matched multiple branches." % str(label)

                # use an empty list to indicate notexistance
                if destName not in matches:
                    # We're redirecting to something which doesn't
                    # exist. This is an error if it's the top of a
                    # redirect (a package), but generates an erase
                    # redirect if it's for a component.
                    if name in names:
                        raise builderrors.RecipeFileError, \
                            "Trove %s does not exist" % (destName, targetFlavor)

                    redirMap[(name, sourceFlavor)] = (None, None, None, [])
                    continue

                # Get the flavors and branch available on the target
                targetFlavors = set()
                for version, flavorList in matches[destName].iteritems():
                    targetFlavors.update((version, x) for x in flavorList)
                del matches

                for version, flavorList in versionDict.items():
                    for sourceFlavor in flavorList:
                        if sourceFlavorRestriction is not None and \
                           sourceFlavor != sourceFlavorRestriction: continue

                        match = None
                        for targetVersion, targetFlavor in targetFlavors:
                            if targetFlavorRestriction is not None and \
                               targetFlavor != targetFlavorRestriction:
                                continue

                            if sourceFlavor.score(targetFlavor) is not None:
                                match = targetVersion
                                break

                        if match is not None:
                            # we can redirect this trove. go ahead and
                            # setup the redirect for it, and add any troves
                            # it references to the todo list
                            trvCs = trvCsDict[(name, version, sourceFlavor)]

                            # we can't integrity check here because we got
                            # the trove w/o files
                            trv = trove.Trove(trvCs, skipIntegrityChecks = True)

                            for info in trv.iterTroveList(strongRefs = True):
                                assert(info[1] == version)
                                assert(info[2] == sourceFlavor)
                                additionalNames.add(info[0])
                                d = sourceTroveMatches.setdefault(info[0], {})
                                flavorList = d.setdefault(info[1], [])
                                flavorList.append(info[2])

                            assert((name, sourceFlavor) not in redirMap)
                            redirMap[(name, sourceFlavor)] = \
                                (destName, match.branch(),
                                 targetFlavorRestriction,
                                 [ x[0] for x in
                                    trv.iterTroveList(strongRefs = True) ] )
                        elif targetFlavorRestriction is not None:
                            raise builderrors.RecipeFileError, \
                                "Trove %s does not exist for flavor %s" \
                                % (name, targetFlavor)

            for name in additionalNames:
                additionalNameQueue.add(name)

        self.redirections = redirMap

    def getRedirections(self):
        return self.redirections

    def __init__(self, repos, cfg, branch, flavor, extraMacros={}):
        self.repos = repos
        self.cfg = cfg
        self.redirections = {}
        self.branch = branch
        self.flavor = flavor
        self.addTroveList = []
        self.macros = macros.Macros()
        self.macros.update(extraMacros)

<commit_msg>A bug that caused redirects to fail to build when multiple flavors of a  trove exist has been fixed. * A bug with cooking flavored redirects has been fixed.<commit_after># Copyright (c) 2005 rPath, Inc.
#
# This program is distributed under the terms of the Common Public License,
# version 1.0. A copy of this license should have been distributed with this
# source file in a file called LICENSE. If it is not present, the license
# is always available at http://www.opensource.org/licenses/cpl.php.
#
# This program is distributed in the hope that it will be useful, but
# without any waranty; without even the implied warranty of merchantability
# or fitness for a particular purpose. See the Common Public License for
# full details.
#

from conary import trove, versions
from conary.deps import deps
from conary.lib import util
from conary.repository import errors
from conary.build import errors as builderrors
from conary.build import macros
from conary.build import use
from conary.build.recipe import Recipe, RECIPE_TYPE_REDIRECT

import itertools

class RedirectRecipe(Recipe):
    Flags = use.LocalFlags
    _recipeType = RECIPE_TYPE_REDIRECT
    ignore = 1

    def addRedirect(self, name, branchStr = None, sourceFlavor = None,
                    targetFlavor = None, fromTrove = None):
        if (sourceFlavor and not targetFlavor) or \
           (targetFlavor and not sourceFlavor):
            raise builderrors.RecipeFileError, \
                "sourceFlavor and targetFlavor must be specified jointly"

        if sourceFlavor is not None:
            f = deps.parseFlavor(sourceFlavor)
            if f is None:
                raise ValueError, 'invalid flavor %s' % sourceFlavor
            sourceFlavor = f

        if targetFlavor is not None:
            f = deps.parseFlavor(targetFlavor)
            if f is None:
                raise ValueError, 'invalid flavor %s' % targetFlavor
            targetFlavor = f

        if fromTrove is None:
            fromTrove = self.name
        elif fromTrove.find(":") != -1:
            raise ValueError, 'components cannot be individually redirected'

        if fromTrove.startswith("group-"):
            # how sad
            raise ValueError, "groups cannot be redirected"

        self.addTroveList.append((name, branchStr, sourceFlavor,
                                  targetFlavor, fromTrove))

    def findTroves(self):
        self.size = 0

        validSize = True
        troveList = []

        packageSet = {}

        sourceSearch = {}
        fromRule = {}
        for (name, branchStr, sourceFlavor, targetFlavor, fromTrove) in \
                                self.addTroveList:
            l = fromRule.setdefault(fromTrove, list())
            # the catch-all (with no sourceFlavor) has to be at the end
            if not sourceFlavor:
                l.append((name, branchStr, sourceFlavor, targetFlavor))
            else:
                l.insert(0, (name, branchStr, sourceFlavor, targetFlavor))

            d = sourceSearch.setdefault(fromTrove, { self.branch : None })

        sourceTroveMatches = self.repos.getTroveLeavesByBranch(sourceSearch)

        if len(sourceTroveMatches) != len(sourceSearch):
            missing = set(sourceSearch) - set(sourceTroveMatches)
            raise builderrors.RecipeFileError, \
                    "No troves found with name(s) %s" % " ".join(missing)

        l = []
        for name, d in sourceTroveMatches.iteritems():
            for version, flavorList in d.iteritems():
                l += [ (name, (None, None), (version, x), True)
                                for x in flavorList ]

        trvCsDict = {}
        # We don't need to recurse here since we only support package
        # redirects
        cs = self.repos.createChangeSet(l, recurse = True, withFiles = False)
        for trvCs in cs.iterNewTroveList():
            info = (trvCs.getName(), trvCs.getNewVersion(),
                    trvCs.getNewFlavor())
            trvCsDict[info] = trvCs

        redirMap = {}

        names = sourceTroveMatches.keys()
        additionalNameQueue = util.IterableQueue()
        for name in itertools.chain(names, additionalNameQueue):
            versionDict = sourceTroveMatches.pop(name)

            destSet = fromRule.get(name, None)
            if destSet is None and ':' in name:
                # package redirections imply component redirections
                pkgName, compName = name.split(':')
                destSet = fromRule.get(pkgName, None)
                if destSet is not None:
                    destSet = set(
                        [ (x[0] + ':' + compName,) + x[1:] for x in destSet ])

            if destSet is None:
                raise builderrors.RecipeFileError, \
                    "Cannot find redirection for trove %s" % name

            # XXX the repository operations should be pulled out of all of
            # these loops
            additionalNames = set()
            for (destName, branchStr, sourceFlavorRestriction,
                                      targetFlavorRestriction) in destSet:
                if branchStr[0] == '/':
                    branch = versions.VersionFromString(branchStr)
                    if not isinstance(branch, versions.Branch):
                        raise builderrors.RecipeFileError, \
                            "Redirects must specify branches or labels, " \
                            "not versions"

                    matches = self.repos.getTroveLeavesByBranch(
                                    { destName : { branch : None } })
                else:
                    label = versions.Label(branchStr)
                    matches = self.repos.getTroveLeavesByLabel(
                                    { destName : { label : None } })
                    # if there are multiple versions returned, the label
                    # may have matched multiple branches
                    if matches:
                        branches = set(x.branch() for x in matches[destName])
                        if len(branches) > 1:
                            raise builderrors.RecipeFileError, \
                                "Label %s matched multiple branches." % str(label)

                # use an empty list to indicate notexistance
                if destName not in matches:
                    # We're redirecting to something which doesn't
                    # exist. This is an error if it's the top of a
                    # redirect (a package), but generates an erase
                    # redirect if it's for a component.
                    if name in names:
                        raise builderrors.RecipeFileError, \
                            "Trove %s does not exist" % (destName)

                    redirMap[(name, sourceFlavor)] = (None, None, None, [])
                    continue

                # Get the flavors and branch available on the target
                targetFlavors = set()
                for version, flavorList in matches[destName].iteritems():
                    targetFlavors.update((version, x) for x in flavorList)
                del matches

                for version, flavorList in versionDict.items():
                    for sourceFlavor in flavorList:
                        if sourceFlavorRestriction is not None and \
                           sourceFlavor != sourceFlavorRestriction: continue

                        match = None
                        for targetVersion, targetFlavor in targetFlavors:
                            if targetFlavorRestriction is not None and \
                               targetFlavor != targetFlavorRestriction:
                                continue

                            if (sourceFlavorRestriction
                                or sourceFlavor.score(targetFlavor) is not False):
                                match = targetVersion
                                break

                        if match is not None:
                            # we can redirect this trove. go ahead and
                            # setup the redirect for it, and add any troves
                            # it references to the todo list
                            trvCs = trvCsDict[(name, version, sourceFlavor)]

                            # we can't integrity check here because we got
                            # the trove w/o files
                            trv = trove.Trove(trvCs, skipIntegrityChecks = True)

                            for info in trv.iterTroveList(strongRefs = True):
                                assert(info[1] == version)
                                assert(info[2] == sourceFlavor)
                                additionalNames.add(info[0])
                                d = sourceTroveMatches.setdefault(info[0], {})
                                flavorList = d.setdefault(info[1], [])
                                flavorList.append(info[2])

                            assert((name, sourceFlavor) not in redirMap)
                            redirMap[(name, sourceFlavor)] = \
                                (destName, match.branch(),
                                 targetFlavorRestriction,
                                 [ x[0] for x in
                                    trv.iterTroveList(strongRefs = True) ] )
                        elif targetFlavorRestriction is not None:
                            raise builderrors.RecipeFileError, \
                                "Trove %s does not exist for flavor %s" \
                                % (name, targetFlavor)

            for name in additionalNames:
                additionalNameQueue.add(name)

        self.redirections = redirMap

    def getRedirections(self):
        return self.redirections

    def __init__(self, repos, cfg, branch, flavor, extraMacros={}):
        self.repos = repos
        self.cfg = cfg
        self.redirections = {}
        self.branch = branch
        self.flavor = flavor
        self.addTroveList = []
        self.macros = macros.Macros()
        self.macros.update(extraMacros)

<|endoftext|>


<commit_before>import functools
import time
import glob
import os
import re
from selenium.common.exceptions import ElementNotInteractableException
from selenium.common.exceptions import StaleElementReferenceException
from selenium.common.exceptions import WebDriverException
from selenium.common.exceptions import NoSuchWindowException
from selenium.webdriver.remote.command import Command
from SeleniumLibrary.errors import ElementNotFound
from robot.libraries.BuiltIn import BuiltIn

def set_pdb_trace(pm=False):
    """Start the Python debugger when robotframework is running.

    This makes sure that pdb can use stdin/stdout even though
    robotframework has redirected I/O.
    """
    import sys
    import pdb

    for attr in ("stdin", "stdout", "stderr"):
        setattr(sys, attr, getattr(sys, "__%s__" % attr))
    if pm:
        # Post-mortem debugging of an exception
        pdb.post_mortem()
    else:
        pdb.set_trace()

# This is a list of user actions that are likely to trigger
# Aura actions and/or XHRs. We'll add a step to wait for
# in-flight XHRs to complete after these commands.
COMMANDS_INVOKING_ACTIONS = {Command.CLICK_ELEMENT}

# This script waits for a) Aura to be available and b)
# any in-flight Aura XHRs to be complete.
# We only do this if the page uses Aura, as determined by looking for
# id="auraAppcacheProgress" in the DOM.
# It would be nice if we could inject the function when the page loads
# and then just call it after commands, but I was having trouble
# getting webdriver to add it to the window scope.
WAIT_FOR_AURA_SCRIPT = """
done = arguments[0];
if (document.getElementById('auraAppcacheProgress')) {
    var waitForXHRs = function() {
        if (window.$A && !window.$A.clientService.inFlightXHRs()) {
            done();
        } else {
            setTimeout(waitForXHRs, 100);
        }
    }
    setTimeout(waitForXHRs, 0);
} else {
    done();
}
"""

ALWAYS_RETRY_EXCEPTIONS = (
    ElementNotFound,
    ElementNotInteractableException,
    StaleElementReferenceException,
    NoSuchWindowException,
)

class RetryingSeleniumLibraryMixin(object):

    debug = False

    @property
    def selenium(self):
        selenium = self.builtin.get_library_instance("SeleniumLibrary")

        # Patch the selenium webdriver to add our own functionality
        # to improve stability.
        if not getattr(selenium.driver, "_cumulus_patched", False):
            orig_execute = selenium.driver.execute

            def execute(driver_command, params=None):
                try:
                    if self.retry_selenium:
                        # Retry certain failed commands once
                        result = self.selenium_execute_with_retry(
                            orig_execute, driver_command, params
                        )
                    else:
                        result = orig_execute(driver_command, params)
                except Exception:
                    if driver_command != Command.SCREENSHOT:
                        self.selenium.capture_page_screenshot()
                    if self.debug:
                        self.selenium.log_source()
                    raise

                # Run the "wait for aura" script after commands that are
                # likely to invoke async actions.
                if driver_command in COMMANDS_INVOKING_ACTIONS:
                    self.wait_for_aura()
                return result

            selenium.driver.execute = execute
            selenium.driver._cumulus_patched = True

        return selenium

    def selenium_execute_with_retry(self, execute, command, params):
        """Run a single selenium command and retry once.

        The retry happens for certain errors that are likely to be resolved
        by retrying.
        """
        try:
            return execute(command, params)
        except Exception as e:
            if isinstance(e, ALWAYS_RETRY_EXCEPTIONS) or (
                isinstance(e, WebDriverException)
                and "Other element would receive the click" in str(e)
            ):
                # Retry
                self.builtin.log("Retrying {} command".format(command), level="WARN")
                time.sleep(2)
                return execute(command, params)
            else:
                raise

    def wait_for_aura(self):
        """Run the WAIT_FOR_AURA_SCRIPT.

        This script polls Aura via $A in Javascript to determine when
        all in-flight XHTTP requests have completed before continuing.
        """
        try:
            self.selenium.driver.execute_async_script(WAIT_FOR_AURA_SCRIPT)
        except Exception:
            pass

def selenium_retry(target=None, retry=True):
    """Decorator to turn on automatic retries of flaky selenium failures.

    Decorate a robotframework library class to turn on retries for all
    selenium calls from that library::

        @selenium_retry
        class MyLibrary(object):

            # Decorate a method to turn it back off for that method
            @selenium_retry(False)
            def some_keyword(self):
                self.selenium.click_button('foo')

    Or turn it off by default but turn it on for some methods
    (the class-level decorator is still required)::

        @selenium_retry(False)
        class MyLibrary(object):

            @selenium_retry(True)
            def some_keyword(self):
                self.selenium.click_button('foo')
    """

    if isinstance(target, bool):
        # Decorator was called with a single boolean argument
        retry = target
        target = None

    def decorate(target):
        if isinstance(target, type):
            cls = target
            # Metaclass time.
            # We're going to generate a new subclass that:
            # a) mixes in RetryingSeleniumLibraryMixin
            # b) sets the initial value of `retry_selenium`
            return type(
                cls.__name__,
                (cls, RetryingSeleniumLibraryMixin),
                {"retry_selenium": retry, "__doc__": cls.__doc__},
            )
        func = target

        @functools.wraps(func)
        def run_with_retry(self, *args, **kwargs):
            # Set the retry setting and run the original function.
            old_retry = self.retry_selenium
            self.retry = retry
            try:
                return func(self, *args, **kwargs)
            finally:
                # Restore the previous value
                self.retry_selenium = old_retry

        run_with_retry.is_selenium_retry_decorator = True
        return run_with_retry

    if target is None:
        # Decorator is being used with arguments
        return decorate
    else:
        # Decorator was used without arguments
        return decorate(target)

def capture_screenshot_on_error(func):
    """Decorator for capturing a screenshot if a keyword throws an error

    The name is slightly misleading. While it was designed to capture a
    screenshot on error, it actually ties in to the same feature in
    SeleniumLibrary.

    SeleniumLibrary lets you define any keyword to be run on error,
    though I doubt we would ever use it for anything other than to
    capture screenshots. However, if you do configure SeleniumLibrary
    to do something other than (or in addition to) capturing a
    screenshot, any keyword that uses this decorator will behave just
    like a SeleniumLibrary keyword.

    """

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        try:
            func(*args, **kwargs)
        except Exception:
            selib = BuiltIn().get_library_instance("SeleniumLibrary")
            selib.failure_occurred()
            raise

    return wrapper

def get_locator_module_name(version):
    """Return module name of locator file for the specified version

    If a file for the specified version can't be found, the locator
    file with the highest number will be returned. Passing in None
    effectively means "give me the latest version".
    """

    here = os.path.dirname(__file__)
    if os.path.exists(os.path.join(here, f"locators_{version}.py")):
        # our work here is done.
        locator_filename = f"locators_{version}.py"
    else:

        def by_num(filename):
            """Pull out the number from a filename"""
            nums = re.findall(r"_(\d+)", filename)
            if nums:
                return int(nums[-1])
            return 0

        files = sorted(glob.glob1(here, "locators_*.py"), key=by_num)
        locator_filename = files[-1]

    locator_module_name = f"cumulusci.robotframework.{locator_filename[:-3]}"
    return locator_module_name
<commit_msg>Switch to using pathlib.Path for filename manipulation<commit_after>import functools
import time
import re
from pathlib import Path

from selenium.common.exceptions import ElementNotInteractableException
from selenium.common.exceptions import StaleElementReferenceException
from selenium.common.exceptions import WebDriverException
from selenium.common.exceptions import NoSuchWindowException
from selenium.webdriver.remote.command import Command
from SeleniumLibrary.errors import ElementNotFound
from robot.libraries.BuiltIn import BuiltIn

def set_pdb_trace(pm=False):
    """Start the Python debugger when robotframework is running.

    This makes sure that pdb can use stdin/stdout even though
    robotframework has redirected I/O.
    """
    import sys
    import pdb

    for attr in ("stdin", "stdout", "stderr"):
        setattr(sys, attr, getattr(sys, "__%s__" % attr))
    if pm:
        # Post-mortem debugging of an exception
        pdb.post_mortem()
    else:
        pdb.set_trace()

# This is a list of user actions that are likely to trigger
# Aura actions and/or XHRs. We'll add a step to wait for
# in-flight XHRs to complete after these commands.
COMMANDS_INVOKING_ACTIONS = {Command.CLICK_ELEMENT}

# This script waits for a) Aura to be available and b)
# any in-flight Aura XHRs to be complete.
# We only do this if the page uses Aura, as determined by looking for
# id="auraAppcacheProgress" in the DOM.
# It would be nice if we could inject the function when the page loads
# and then just call it after commands, but I was having trouble
# getting webdriver to add it to the window scope.
WAIT_FOR_AURA_SCRIPT = """
done = arguments[0];
if (document.getElementById('auraAppcacheProgress')) {
    var waitForXHRs = function() {
        if (window.$A && !window.$A.clientService.inFlightXHRs()) {
            done();
        } else {
            setTimeout(waitForXHRs, 100);
        }
    }
    setTimeout(waitForXHRs, 0);
} else {
    done();
}
"""

ALWAYS_RETRY_EXCEPTIONS = (
    ElementNotFound,
    ElementNotInteractableException,
    StaleElementReferenceException,
    NoSuchWindowException,
)

class RetryingSeleniumLibraryMixin(object):

    debug = False

    @property
    def selenium(self):
        selenium = self.builtin.get_library_instance("SeleniumLibrary")

        # Patch the selenium webdriver to add our own functionality
        # to improve stability.
        if not getattr(selenium.driver, "_cumulus_patched", False):
            orig_execute = selenium.driver.execute

            def execute(driver_command, params=None):
                try:
                    if self.retry_selenium:
                        # Retry certain failed commands once
                        result = self.selenium_execute_with_retry(
                            orig_execute, driver_command, params
                        )
                    else:
                        result = orig_execute(driver_command, params)
                except Exception:
                    if driver_command != Command.SCREENSHOT:
                        self.selenium.capture_page_screenshot()
                    if self.debug:
                        self.selenium.log_source()
                    raise

                # Run the "wait for aura" script after commands that are
                # likely to invoke async actions.
                if driver_command in COMMANDS_INVOKING_ACTIONS:
                    self.wait_for_aura()
                return result

            selenium.driver.execute = execute
            selenium.driver._cumulus_patched = True

        return selenium

    def selenium_execute_with_retry(self, execute, command, params):
        """Run a single selenium command and retry once.

        The retry happens for certain errors that are likely to be resolved
        by retrying.
        """
        try:
            return execute(command, params)
        except Exception as e:
            if isinstance(e, ALWAYS_RETRY_EXCEPTIONS) or (
                isinstance(e, WebDriverException)
                and "Other element would receive the click" in str(e)
            ):
                # Retry
                self.builtin.log("Retrying {} command".format(command), level="WARN")
                time.sleep(2)
                return execute(command, params)
            else:
                raise

    def wait_for_aura(self):
        """Run the WAIT_FOR_AURA_SCRIPT.

        This script polls Aura via $A in Javascript to determine when
        all in-flight XHTTP requests have completed before continuing.
        """
        try:
            self.selenium.driver.execute_async_script(WAIT_FOR_AURA_SCRIPT)
        except Exception:
            pass

def selenium_retry(target=None, retry=True):
    """Decorator to turn on automatic retries of flaky selenium failures.

    Decorate a robotframework library class to turn on retries for all
    selenium calls from that library::

        @selenium_retry
        class MyLibrary(object):

            # Decorate a method to turn it back off for that method
            @selenium_retry(False)
            def some_keyword(self):
                self.selenium.click_button('foo')

    Or turn it off by default but turn it on for some methods
    (the class-level decorator is still required)::

        @selenium_retry(False)
        class MyLibrary(object):

            @selenium_retry(True)
            def some_keyword(self):
                self.selenium.click_button('foo')
    """

    if isinstance(target, bool):
        # Decorator was called with a single boolean argument
        retry = target
        target = None

    def decorate(target):
        if isinstance(target, type):
            cls = target
            # Metaclass time.
            # We're going to generate a new subclass that:
            # a) mixes in RetryingSeleniumLibraryMixin
            # b) sets the initial value of `retry_selenium`
            return type(
                cls.__name__,
                (cls, RetryingSeleniumLibraryMixin),
                {"retry_selenium": retry, "__doc__": cls.__doc__},
            )
        func = target

        @functools.wraps(func)
        def run_with_retry(self, *args, **kwargs):
            # Set the retry setting and run the original function.
            old_retry = self.retry_selenium
            self.retry = retry
            try:
                return func(self, *args, **kwargs)
            finally:
                # Restore the previous value
                self.retry_selenium = old_retry

        run_with_retry.is_selenium_retry_decorator = True
        return run_with_retry

    if target is None:
        # Decorator is being used with arguments
        return decorate
    else:
        # Decorator was used without arguments
        return decorate(target)

def capture_screenshot_on_error(func):
    """Decorator for capturing a screenshot if a keyword throws an error

    The name is slightly misleading. While it was designed to capture a
    screenshot on error, it actually ties in to the same feature in
    SeleniumLibrary.

    SeleniumLibrary lets you define any keyword to be run on error,
    though I doubt we would ever use it for anything other than to
    capture screenshots. However, if you do configure SeleniumLibrary
    to do something other than (or in addition to) capturing a
    screenshot, any keyword that uses this decorator will behave just
    like a SeleniumLibrary keyword.

    """

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        try:
            func(*args, **kwargs)
        except Exception:
            selib = BuiltIn().get_library_instance("SeleniumLibrary")
            selib.failure_occurred()
            raise

    return wrapper

def get_locator_module_name(version):
    """Return module name of locator file for the specified version

    If a file for the specified version can't be found, the locator
    file with the highest number will be returned. Passing in None
    effectively means "give me the latest version".
    """

    here = Path(__file__).parent
    if Path(here, f"locators_{version}.py").exists():
        # our work here is done.
        locator_name = f"locators_{version}"
    else:

        def by_num(filename):
            """Pull out the number from a filename, or return zero"""
            nums = re.findall(r"_(\d+)", str(filename))
            if nums:
                return int(nums[-1])
            return 0

        files = sorted(here.glob("locators_*.py"), key=by_num)
        locator_name = files[-1].stem

    locator_module_name = f"cumulusci.robotframework.{locator_name}"
    return locator_module_name
<|endoftext|>


<commit_before>#!/usr/bin/python

# Copyright (c) 2010 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 glob
import logging
import optparse
import os
import shutil
import sys
import tempfile
import zipfile

import pyauto_functional  # Must be imported before pyauto
import pyauto
import pyauto_utils

class ImportsTest(pyauto.PyUITest):
  """Import settings from other browsers.

  Import settings tables below show which items get imported on first run and
  via preferences for different browsers and operating systems.

            Bookmarks   History   SearchEngines   Passwords   Homepage
  Firefox:
  Win/FRUI      N          Y            N             N          Y
  Win/Prefs     Y          Y            Y             Y          N
  Mac&Lin/FRUI  Y          Y            Y             Y          Y
  Mac&Lin/Prefs Y          Y            Y             Y          N

  Safari:
  Mac/FRUI      Y          Y            Y             Y          N
  Mac/Prefs     Y          Y            Y             Y          N
  """
  def setUp(self):
    self._to_import = ['ALL']

    if pyauto.PyUITest.IsMac():
      self._firefox_profiles_path = os.path.join(
          os.environ['HOME'], 'Library','Application Support','Firefox')
      self._firefox_test_profile = os.path.join(
          pyauto.PyUITest.DataDir(), 'import', 'firefox', 'macwin.zip')
      self._safari_profiles_path = os.path.join(
          os.environ['HOME'], 'Library', 'Safari')
      # Set the path here since it can't get set on the browser side and it is
      # necessary for importing passwords on Mac.
      self._old_path = None
      if 'DYLD_FALLBACK_LIBRARY_PATH' in os.environ:
        self._old_path = os.environ['DYLD_FALLBACK_LIBRARY_PATH']
      os.environ['DYLD_FALLBACK_LIBRARY_PATH'] = os.path.join(
          self.DataDir(), 'firefox3_nss_mac')
      # Don't import passwords to avoid Keychain popups. See crbug.com/49378.
      self._to_import = ['HISTORY', 'FAVORITES', 'SEARCH_ENGINES', 'HOME_PAGE']
    elif pyauto.PyUITest.IsWin():
      self._firefox_profiles_path = os.path.join(
          os.getenv('APPDATA'), 'Mozilla', 'Firefox')
      self._firefox_test_profile = os.path.join(
          pyauto.PyUITest.DataDir(), 'import', 'firefox', 'macwin.zip')
    else:
      self._firefox_profiles_path = os.path.join(
          os.environ['HOME'], '.mozilla', 'firefox')
      self._firefox_test_profile = os.path.join(
          pyauto.PyUITest.DataDir(), 'import', 'firefox', 'linux.zip')

    # Expected items for tests.
    self._history_items = ['Google', 'Google News', u'Google \ub3c4\uc11c']
    self._bookmark_bar_items = ['Google News']
    self._bookmark_folder_items = ['Google', u'Google \ub3c4\uc11c']
    self._password_items = ['etouchqa@gmail.com', 'macqa05']
    self._home_page = 'http://news.google.com/'

    self._safari_replacer = None
    self._firefox_replacer = None

    pyauto.PyUITest.setUp(self)

  def tearDown(self):
    pyauto.PyUITest.tearDown(self)
    # Re-set the path to its state before the test.
    if self.IsMac():
      if self._old_path:
        os.environ['DYLD_FALLBACK_LIBRARY_PATH'] = self._old_path
      else:
        os.environ.pop('DYLD_FALLBACK_LIBRARY_PATH')
    # Delete any replacers to restore the original profiles.
    if self._safari_replacer:
      del self._safari_replacer
    if self._firefox_replacer:
      del self._firefox_replacer

  def _UnzipProfileToDir(self, profile_zip, dir):
    """Unzip |profile_zip| into directory |dir|.

    Creates |dir| if it doesn't exist.
    """
    zf = zipfile.ZipFile(profile_zip)
    # Make base.
    pushd = os.getcwd()
    try:
      if not os.path.isdir(dir):
        os.mkdir(dir)
      os.chdir(dir)
      # Extract files.
      for info in zf.infolist():
        name = info.filename
        if name.endswith('/'):  # It's a directory.
          if not os.path.isdir(name):
            os.makedirs(name)
        else:  # It's a file.
          dir = os.path.dirname(name)
          if dir and not os.path.isdir(dir):
            os.makedirs(dir)
          out = open(name, 'wb')
          out.write(zf.read(name))
          out.close()
        # Set permissions.
        os.chmod(name, 0777)
    finally:
      os.chdir(pushd)

  def _SwapFirefoxProfile(self):
    """Swaps the test Firefox profile with the original one."""
    self._firefox_replacer = pyauto_utils.ExistingPathReplacer(
        self._firefox_profiles_path)
    self._UnzipProfileToDir(self._firefox_test_profile,
                            self._firefox_profiles_path)

  def _SwapSafariProfile(self):
    """Swaps the test Safari profile with the original one."""
    self._safari_replacer = pyauto_utils.ExistingPathReplacer(
        self._safari_profiles_path)
    self._UnzipProfileToDir(
        os.path.join(self.DataDir(), 'import', 'safari', 'mac.zip'),
        self._safari_profiles_path)

  def _CheckForBookmarks(self, bookmark_titles, bookmark_bar):
    """Checks that the given bookmarks exist.

    Args:
      bookmark_titles: A set of bookmark title strings.
      bookmark_bar: True if the bookmarks are part of the bookmark bar.
                    False otherwise.
    """
    confirmed_titles = set()
    bookmarks = self.GetBookmarkModel()
    if bookmark_bar:
      node = bookmarks.BookmarkBar()
    else:
      node = bookmarks.Other()
    for title in bookmark_titles:
      self.assertTrue([x for x in bookmark_titles \
                       if bookmarks.FindByTitle(title, [node])])

  def _BookmarkDuplicatesExist(self, bookmark_titles):
    """Returns true if any of the bookmark titles are duplicated.

    Args:
      bookmark_titles: A list of bookmark title strings.
    """
    bookmarks = self.GetBookmarkModel()
    for title in bookmark_titles:
      if len(bookmarks.FindByTitle(title)) > 1:
        return True
    return False

  def _CheckForHistory(self, history_titles):
    """Verifies that the given list of history items are in the history.

    Args:
      history_titles: A list of history title strings.
    """
    history = self.GetHistoryInfo().History()

    for title in history_titles:
      self.assertTrue([x for x in history if x['title'] == title])

  def _CheckForPasswords(self, usernames):
    """Check that password items exist for the given usernames."""
    # Password import automation does not work on Mac. See crbug.com/52124.
    if self.IsMac():
      return
    passwords = self.GetSavedPasswords()
    for username in usernames:
      self.assertTrue([x for x in passwords if x['username_value'] == username])

  def _CheckDefaults(self, bookmarks, history, passwords, home_page,
                     search_engines):
    """Checks the defaults for each of the possible import items.

    All arguments are True if they should be checked, False otherwise."""
    if bookmarks:
      self._CheckForBookmarks(self._bookmark_bar_items, True)
      self._CheckForBookmarks(self._bookmark_folder_items, False)
    if history:
      self._CheckForHistory(self._history_items)
    if passwords:
      self._CheckForPasswords(self._password_items)
    if home_page:
      self.assertEqual(self._home_page, self.GetPrefsInfo().Prefs()['homepage'])
    # TODO(alyssad): Test for search engines after a hook is added.
    # See crbug.com/52009.

  # Tests.
  def testFirefoxImportFromPrefs(self):
    """Verify importing Firefox data through preferences."""
    self._SwapFirefoxProfile()
    self.ImportSettings('Mozilla Firefox', False, self._to_import)
    self._CheckDefaults(bookmarks=True, history=True, passwords=True,
                        home_page=False, search_engines=True)

  def testFirefoxFirstRun(self):
    """Verify importing from Firefox on the first run.

    For Win, only history and homepage will only be imported.
    Mac and Linux can import history, homepage, and bookmarks.
    """
    self._SwapFirefoxProfile()
    self.ImportSettings('Mozilla Firefox', True, self._to_import)
    non_win = not self.IsWin()
    self._CheckDefaults(bookmarks=non_win, history=True, passwords=True,
                        home_page=non_win, search_engines=True)

  def testImportFirefoxDataTwice(self):
    """Verify importing Firefox data twice.

    Bookmarks should be duplicated, but history and passwords should not.
    """
    self._SwapFirefoxProfile()
    self.ImportSettings('Mozilla Firefox', False, self._to_import)
    num_history_orig = len(self.GetHistoryInfo().History())
    num_passwords_orig = len(self.GetSavedPasswords())

    # Re-import and check for duplicates.
    self.ImportSettings('Mozilla Firefox', False, self._to_import)
    self.assertTrue(self._BookmarkDuplicatesExist(
        self._bookmark_bar_items + self._bookmark_folder_items))
    self.assertEqual(num_history_orig, len(self.GetHistoryInfo().History()))
    self.assertEqual(num_passwords_orig, len(self.GetSavedPasswords()))

  def testImportFromFirefoxAndSafari(self):
    """Verify importing from Firefox and then Safari."""
    # This test is for Mac only.
    if not self.IsMac():
      return

    self._SwapSafariProfile()
    self._SwapFirefoxProfile()
    self.ImportSettings('Mozilla Firefox', False, self._to_import)
    self.ImportSettings('Safari', False, self._to_import)

    self._CheckDefaults(bookmarks=True, history=True, passwords=True,
                        home_page=False, search_engines=True)
    self.assertTrue(self._BookmarkDuplicatesExist(
        self._bookmark_bar_items + self._bookmark_folder_items))

  def testSafariImportFromPrefs(self):
    """Verify importing Safari data through preferences."""
    # This test is Mac only.
    if not self.IsMac():
      return
    self._SwapSafariProfile()
    self.ImportSettings('Safari', False, self._to_import)
    self._CheckDefaults(bookmarks=True, history=True, passwords=False,
                        home_page=False, search_engines=True)

  def testSafariFirstRun(self):
    """Verify importing Safari data on the first run."""
    # This test is Mac only.
    if not self.IsMac():
      return
    self._SwapSafariProfile()
    self.ImportSettings('Safari', False, self._to_import)
    self._CheckDefaults(bookmarks=True, history=True, passwords=False,
                        home_page=False, search_engines=False)

  def testImportSafariDataTwice(self):
    """Verify importing Safari data twice.

    Bookmarks should be duplicated, but history and passwords should not."""
    # This test is Mac only.
    if not self.IsMac():
      return
    self._SwapSafariProfile()
    self.ImportSettings('Safari', False, self._to_import)
    num_history_orig = len(self.GetHistoryInfo().History())
    num_passwords_orig = len(self.GetSavedPasswords())

    # Re-import and check for duplicates.
    self.ImportSettings('Safari', False, self._to_import)
    self.assertTrue(self._BookmarkDuplicatesExist(
        self._bookmark_bar_items + self._bookmark_folder_items))
    self.assertEqual(num_history_orig, len(self.GetHistoryInfo().History()))
    self.assertEqual(num_passwords_orig, len(self.GetSavedPasswords()))

if __name__ == '__main__':
  pyauto_functional.Main()
<commit_msg>Undo the changes made to imports test that sets an environment variable BUG=52124<commit_after>#!/usr/bin/python

# Copyright (c) 2010 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 glob
import logging
import optparse
import os
import shutil
import sys
import tempfile
import zipfile

import pyauto_functional  # Must be imported before pyauto
import pyauto
import pyauto_utils

class ImportsTest(pyauto.PyUITest):
  """Import settings from other browsers.

  Import settings tables below show which items get imported on first run and
  via preferences for different browsers and operating systems.

            Bookmarks   History   SearchEngines   Passwords   Homepage
  Firefox:
  Win/FRUI      N          Y            N             N          Y
  Win/Prefs     Y          Y            Y             Y          N
  Mac&Lin/FRUI  Y          Y            Y             Y          Y
  Mac&Lin/Prefs Y          Y            Y             Y          N

  Safari:
  Mac/FRUI      Y          Y            Y             Y          N
  Mac/Prefs     Y          Y            Y             Y          N
  """
  def setUp(self):
    self._to_import = ['ALL']

    if pyauto.PyUITest.IsMac():
      self._firefox_profiles_path = os.path.join(
          os.environ['HOME'], 'Library','Application Support','Firefox')
      self._firefox_test_profile = os.path.join(
          pyauto.PyUITest.DataDir(), 'import', 'firefox', 'macwin.zip')
      self._safari_profiles_path = os.path.join(
          os.environ['HOME'], 'Library', 'Safari')
      # Don't import passwords to avoid Keychain popups. See crbug.com/49378.
      self._to_import = ['HISTORY', 'FAVORITES', 'SEARCH_ENGINES', 'HOME_PAGE']
    elif pyauto.PyUITest.IsWin():
      self._firefox_profiles_path = os.path.join(
          os.getenv('APPDATA'), 'Mozilla', 'Firefox')
      self._firefox_test_profile = os.path.join(
          pyauto.PyUITest.DataDir(), 'import', 'firefox', 'macwin.zip')
    else:
      self._firefox_profiles_path = os.path.join(
          os.environ['HOME'], '.mozilla', 'firefox')
      self._firefox_test_profile = os.path.join(
          pyauto.PyUITest.DataDir(), 'import', 'firefox', 'linux.zip')

    # Expected items for tests.
    self._history_items = ['Google', 'Google News', u'Google \ub3c4\uc11c']
    self._bookmark_bar_items = ['Google News']
    self._bookmark_folder_items = ['Google', u'Google \ub3c4\uc11c']
    self._password_items = ['etouchqa@gmail.com', 'macqa05']
    self._home_page = 'http://news.google.com/'

    self._safari_replacer = None
    self._firefox_replacer = None

    pyauto.PyUITest.setUp(self)

  def tearDown(self):
    pyauto.PyUITest.tearDown(self)
    # Delete any replacers to restore the original profiles.
    if self._safari_replacer:
      del self._safari_replacer
    if self._firefox_replacer:
      del self._firefox_replacer

  def _UnzipProfileToDir(self, profile_zip, dir):
    """Unzip |profile_zip| into directory |dir|.

    Creates |dir| if it doesn't exist.
    """
    zf = zipfile.ZipFile(profile_zip)
    # Make base.
    pushd = os.getcwd()
    try:
      if not os.path.isdir(dir):
        os.mkdir(dir)
      os.chdir(dir)
      # Extract files.
      for info in zf.infolist():
        name = info.filename
        if name.endswith('/'):  # It's a directory.
          if not os.path.isdir(name):
            os.makedirs(name)
        else:  # It's a file.
          dir = os.path.dirname(name)
          if dir and not os.path.isdir(dir):
            os.makedirs(dir)
          out = open(name, 'wb')
          out.write(zf.read(name))
          out.close()
        # Set permissions.
        os.chmod(name, 0777)
    finally:
      os.chdir(pushd)

  def _SwapFirefoxProfile(self):
    """Swaps the test Firefox profile with the original one."""
    self._firefox_replacer = pyauto_utils.ExistingPathReplacer(
        self._firefox_profiles_path)
    self._UnzipProfileToDir(self._firefox_test_profile,
                            self._firefox_profiles_path)

  def _SwapSafariProfile(self):
    """Swaps the test Safari profile with the original one."""
    self._safari_replacer = pyauto_utils.ExistingPathReplacer(
        self._safari_profiles_path)
    self._UnzipProfileToDir(
        os.path.join(self.DataDir(), 'import', 'safari', 'mac.zip'),
        self._safari_profiles_path)

  def _CheckForBookmarks(self, bookmark_titles, bookmark_bar):
    """Checks that the given bookmarks exist.

    Args:
      bookmark_titles: A set of bookmark title strings.
      bookmark_bar: True if the bookmarks are part of the bookmark bar.
                    False otherwise.
    """
    confirmed_titles = set()
    bookmarks = self.GetBookmarkModel()
    if bookmark_bar:
      node = bookmarks.BookmarkBar()
    else:
      node = bookmarks.Other()
    for title in bookmark_titles:
      self.assertTrue([x for x in bookmark_titles \
                       if bookmarks.FindByTitle(title, [node])])

  def _BookmarkDuplicatesExist(self, bookmark_titles):
    """Returns true if any of the bookmark titles are duplicated.

    Args:
      bookmark_titles: A list of bookmark title strings.
    """
    bookmarks = self.GetBookmarkModel()
    for title in bookmark_titles:
      if len(bookmarks.FindByTitle(title)) > 1:
        return True
    return False

  def _CheckForHistory(self, history_titles):
    """Verifies that the given list of history items are in the history.

    Args:
      history_titles: A list of history title strings.
    """
    history = self.GetHistoryInfo().History()

    for title in history_titles:
      self.assertTrue([x for x in history if x['title'] == title])

  def _CheckForPasswords(self, usernames):
    """Check that password items exist for the given usernames."""
    # Password import automation does not work on Mac. See crbug.com/52124.
    if self.IsMac():
      return
    passwords = self.GetSavedPasswords()
    for username in usernames:
      self.assertTrue([x for x in passwords if x['username_value'] == username])

  def _CheckDefaults(self, bookmarks, history, passwords, home_page,
                     search_engines):
    """Checks the defaults for each of the possible import items.

    All arguments are True if they should be checked, False otherwise."""
    if bookmarks:
      self._CheckForBookmarks(self._bookmark_bar_items, True)
      self._CheckForBookmarks(self._bookmark_folder_items, False)
    if history:
      self._CheckForHistory(self._history_items)
    if passwords:
      self._CheckForPasswords(self._password_items)
    if home_page:
      self.assertEqual(self._home_page, self.GetPrefsInfo().Prefs()['homepage'])
    # TODO(alyssad): Test for search engines after a hook is added.
    # See crbug.com/52009.

  # Tests.
  def testFirefoxImportFromPrefs(self):
    """Verify importing Firefox data through preferences."""
    self._SwapFirefoxProfile()
    self.ImportSettings('Mozilla Firefox', False, self._to_import)
    self._CheckDefaults(bookmarks=True, history=True, passwords=True,
                        home_page=False, search_engines=True)

  def testFirefoxFirstRun(self):
    """Verify importing from Firefox on the first run.

    For Win, only history and homepage will only be imported.
    Mac and Linux can import history, homepage, and bookmarks.
    """
    self._SwapFirefoxProfile()
    self.ImportSettings('Mozilla Firefox', True, self._to_import)
    non_win = not self.IsWin()
    self._CheckDefaults(bookmarks=non_win, history=True, passwords=True,
                        home_page=non_win, search_engines=True)

  def testImportFirefoxDataTwice(self):
    """Verify importing Firefox data twice.

    Bookmarks should be duplicated, but history and passwords should not.
    """
    self._SwapFirefoxProfile()
    self.ImportSettings('Mozilla Firefox', False, self._to_import)
    num_history_orig = len(self.GetHistoryInfo().History())
    num_passwords_orig = len(self.GetSavedPasswords())

    # Re-import and check for duplicates.
    self.ImportSettings('Mozilla Firefox', False, self._to_import)
    self.assertTrue(self._BookmarkDuplicatesExist(
        self._bookmark_bar_items + self._bookmark_folder_items))
    self.assertEqual(num_history_orig, len(self.GetHistoryInfo().History()))
    self.assertEqual(num_passwords_orig, len(self.GetSavedPasswords()))

  def testImportFromFirefoxAndSafari(self):
    """Verify importing from Firefox and then Safari."""
    # This test is for Mac only.
    if not self.IsMac():
      return

    self._SwapSafariProfile()
    self._SwapFirefoxProfile()
    self.ImportSettings('Mozilla Firefox', False, self._to_import)
    self.ImportSettings('Safari', False, self._to_import)

    self._CheckDefaults(bookmarks=True, history=True, passwords=True,
                        home_page=False, search_engines=True)
    self.assertTrue(self._BookmarkDuplicatesExist(
        self._bookmark_bar_items + self._bookmark_folder_items))

  def testSafariImportFromPrefs(self):
    """Verify importing Safari data through preferences."""
    # This test is Mac only.
    if not self.IsMac():
      return
    self._SwapSafariProfile()
    self.ImportSettings('Safari', False, self._to_import)
    self._CheckDefaults(bookmarks=True, history=True, passwords=False,
                        home_page=False, search_engines=True)

  def testSafariFirstRun(self):
    """Verify importing Safari data on the first run."""
    # This test is Mac only.
    if not self.IsMac():
      return
    self._SwapSafariProfile()
    self.ImportSettings('Safari', False, self._to_import)
    self._CheckDefaults(bookmarks=True, history=True, passwords=False,
                        home_page=False, search_engines=False)

  def testImportSafariDataTwice(self):
    """Verify importing Safari data twice.

    Bookmarks should be duplicated, but history and passwords should not."""
    # This test is Mac only.
    if not self.IsMac():
      return
    self._SwapSafariProfile()
    self.ImportSettings('Safari', False, self._to_import)
    num_history_orig = len(self.GetHistoryInfo().History())
    num_passwords_orig = len(self.GetSavedPasswords())

    # Re-import and check for duplicates.
    self.ImportSettings('Safari', False, self._to_import)
    self.assertTrue(self._BookmarkDuplicatesExist(
        self._bookmark_bar_items + self._bookmark_folder_items))
    self.assertEqual(num_history_orig, len(self.GetHistoryInfo().History()))
    self.assertEqual(num_passwords_orig, len(self.GetSavedPasswords()))

if __name__ == '__main__':
  pyauto_functional.Main()
<|endoftext|>


<commit_before>import itertools, cStringIO

def grouper(n, iterable):
    ''' list(grouper(2, range(4))) -> [(0, 1), (2, 3)] '''
    args = [iter(iterable)] * n
    return itertools.izip(*args)

def eatlines(s, n):
    """
    >>> eatlines("1\\n2", 1)
    '2'
    >>> eatlines("1\\n2", 2)
    ''
    >>> eatlines("1\\n2", 3)
    ''
    >>> eatlines("1\\n2\\n3", 1)
    '2\\n3'
    """
    cs = cStringIO.StringIO(s)

    for line in cs:
        n -= 1
        if n == 0:
            return cs.read()
    return ''

def cmdbuilder(name, *args, **kwargs):
    """
    A helper for building the command arguments

    args are the positional arguments

    kwargs are the options
    keys that are single lettered are prepended with '-', others with '--',
    underscores are replaced with dashes

    keys with False boolean values are ignored, lists add the key multiple times

    None arguments are skipped

    >>> cmdbuilder('cmd', a=True, b=False, c=None)
    ['cmd', '-a']
    >>> cmdbuilder('cmd', long=True)
    ['cmd', '--long']
    >>> cmdbuilder('cmd', str='s')
    ['cmd', '--str', 's']
    >>> cmdbuilder('cmd', d_ash=True)
    ['cmd', '--d-ash']
    >>> cmdbuilder('cmd', _=True)
    ['cmd', '-']
    >>> cmdbuilder('cmd', list=[1, 2])
    ['cmd', '--list', '1', '--list', '2']
    >>> cmdbuilder('cmd', None)
    ['cmd']
    """
    cmd = [name]
    for arg, val in kwargs.items():
        if val is None:
            continue

        arg = arg.replace('_', '-')
        if arg != '-':
            arg = '-' + arg if len(arg) == 1 else '--' + arg
        if isinstance(val, bool):
            if val:
                cmd.append(arg)
        elif isinstance(val, list):
            for v in val:
                cmd.append(arg)
                cmd.append(str(v))
        else:
            cmd.append(arg)
            cmd.append(str(val))

    for a in args:
        if a is not None:
            cmd.append(a)

    return cmd
<commit_msg>util: introduce skiplines<commit_after>import itertools, cStringIO

def grouper(n, iterable):
    ''' list(grouper(2, range(4))) -> [(0, 1), (2, 3)] '''
    args = [iter(iterable)] * n
    return itertools.izip(*args)

def eatlines(s, n):
    """
    >>> eatlines("1\\n2", 1)
    '2'
    >>> eatlines("1\\n2", 2)
    ''
    >>> eatlines("1\\n2", 3)
    ''
    >>> eatlines("1\\n2\\n3", 1)
    '2\\n3'
    """
    cs = cStringIO.StringIO(s)

    for line in cs:
        n -= 1
        if n == 0:
            return cs.read()
    return ''

def skiplines(s, prefix):
    """
    Skip lines starting with prefix in s

    >>> skiplines('a\\nb\\na\\n', 'a')
    'b\\na\\n'
    >>> skiplines('a\\na\\n', 'a')
    ''
    >>> skiplines('', 'a')
    ''
    >>> skiplines('a\\nb', 'b')
    'a\\nb'
    """
    cs = cStringIO.StringIO(s)

    for line in cs:
        if not line.startswith(prefix):
            return line + cs.read()

    return ''

def cmdbuilder(name, *args, **kwargs):
    """
    A helper for building the command arguments

    args are the positional arguments

    kwargs are the options
    keys that are single lettered are prepended with '-', others with '--',
    underscores are replaced with dashes

    keys with False boolean values are ignored, lists add the key multiple times

    None arguments are skipped

    >>> cmdbuilder('cmd', a=True, b=False, c=None)
    ['cmd', '-a']
    >>> cmdbuilder('cmd', long=True)
    ['cmd', '--long']
    >>> cmdbuilder('cmd', str='s')
    ['cmd', '--str', 's']
    >>> cmdbuilder('cmd', d_ash=True)
    ['cmd', '--d-ash']
    >>> cmdbuilder('cmd', _=True)
    ['cmd', '-']
    >>> cmdbuilder('cmd', list=[1, 2])
    ['cmd', '--list', '1', '--list', '2']
    >>> cmdbuilder('cmd', None)
    ['cmd']
    """
    cmd = [name]
    for arg, val in kwargs.items():
        if val is None:
            continue

        arg = arg.replace('_', '-')
        if arg != '-':
            arg = '-' + arg if len(arg) == 1 else '--' + arg
        if isinstance(val, bool):
            if val:
                cmd.append(arg)
        elif isinstance(val, list):
            for v in val:
                cmd.append(arg)
                cmd.append(str(v))
        else:
            cmd.append(arg)
            cmd.append(str(val))

    for a in args:
        if a is not None:
            cmd.append(a)

    return cmd
<|endoftext|>


<commit_before># Copyright (c) 2013 David Holm <dholmster@gmail.com>
# This file is part of SimpleGUITk - https://github.com/dholm/simpleguitk
# See the file 'COPYING' for copying permission.

from __future__ import division

try:
    import Tkinter as tkinter
except ImportError:
    import tkinter

import sys

from .canvas import Canvas
from .control_objects import Button
from .control_objects import Input
from .control_objects import Label
from .input import InputAdapter
from .timers import destroy as destroy_timers

class Frame(object):
    def _create_root(self, title):
        root = tkinter.Tk()
        root.wm_title(title)
        root.protocol('WM_DELETE_WINDOW', root.quit)
        return root

    def _canvas_init(self, width, height):
        canvas_frame = tkinter.Frame(self._root)
        self._canvas = Canvas(canvas_frame, width, height)
        canvas_frame.grid(row=0, column=1, rowspan=2, padx=5, pady=5,
                          sticky=(tkinter.N, tkinter.S, tkinter.W, tkinter.E))

    def _control_frame_init(self, width):
        self._control_frame = tkinter.Frame(self._root, width=width)
        self._control_frame.grid(row=0, column=0, padx=5, pady=5)

    def _input_init(self):
        status_frame = tkinter.Frame(self._root)
        canvas_widget = self._canvas._get_widget()
        self._input = InputAdapter(status_frame, self._root, canvas_widget)
        status_frame.grid(row=1, column=0, sticky=(tkinter.W, tkinter.E),
                          padx=5, pady=5)

    def __init__(self, title, canvas_width, canvas_height, control_width):
        self._root = self._create_root(title)

        self._canvas_frame = None
        self._canvas = None
        self._canvas_init(canvas_width, canvas_height)

        self._control_frame = None
        self._controls = []
        self._control_frame_init(control_width)

        self._input = None
        self._key_label = None
        self._mouse_label = None
        self._input_init()

    def _shutdown(self):
        destroy_timers()
        self._canvas.destroy()
        self._root.destroy()
        sys.exit(0)

    def start(self):
        try:
            self._root.mainloop()
        except KeyboardInterrupt:
            pass
        finally:
            self._shutdown()

    def set_draw_handler(self, draw_handler):
        self._canvas.set_draw_handler(draw_handler)

    def add_button(self, text, button_handler, width=None):
        button = Button(self._control_frame, text, button_handler, width)
        self._controls.append(button)
        return button

    def add_label(self, text):
        label = Label(self._control_frame, text)
        self._controls.append(label)
        return label

    def add_input(self, text, input_handler, width):
        inp = Input(self._control_frame, text, input_handler, width)
        self._controls.append(inp)
        return inp

    def set_keydown_handler(self, key_handler):
        self._input.set_keydown_handler(key_handler)

    def set_keyup_handler(self, key_handler):
        self._input.set_keyup_handler(key_handler)

    def set_mouseclick_handler(self, mouse_handler):
        self._input.set_mouseclick_handler(mouse_handler)

    def set_mousedrag_handler(self, mouse_handler):
        self._input.set_mousedrag_handler(mouse_handler)

    def set_canvas_background(self, color):
        self._canvas.set_background(color)

    def get_canvas_textwidth(self, text, size, face='serif'):
        return self._canvas.get_textwidth(text, size, face)

def create_frame(title, canvas_width, canvas_height, control_width=200):
    return Frame(title, canvas_width, canvas_height, control_width)
<commit_msg>Fixed an issue where the status_frame could become too small, causing it to pop sporadically when the input text was too large.<commit_after># Copyright (c) 2013 David Holm <dholmster@gmail.com>
# This file is part of SimpleGUITk - https://github.com/dholm/simpleguitk
# See the file 'COPYING' for copying permission.

from __future__ import division

try:
    import Tkinter as tkinter
except ImportError:
    import tkinter

import sys

from .canvas import Canvas
from .control_objects import Button
from .control_objects import Input
from .control_objects import Label
from .input import InputAdapter
from .timers import destroy as destroy_timers

class Frame(object):
    def _create_root(self, title):
        root = tkinter.Tk()
        root.wm_title(title)
        root.protocol('WM_DELETE_WINDOW', root.quit)
        return root

    def _canvas_init(self, width, height):
        canvas_frame = tkinter.Frame(self._root)
        self._canvas = Canvas(canvas_frame, width, height)
        canvas_frame.grid(row=0, column=1, rowspan=2, padx=5, pady=5,
                          sticky=(tkinter.N, tkinter.S, tkinter.W, tkinter.E))

    def _control_frame_init(self, width):
        self._control_frame = tkinter.Frame(self._root, width=width)
        self._control_frame.grid(row=0, column=0, padx=5, pady=5)

    def _input_init(self):
        status_frame = tkinter.Frame(self._root, width=120, height=85)
        status_frame.pack_propagate(0)
        canvas_widget = self._canvas._get_widget()
        self._input = InputAdapter(status_frame, self._root, canvas_widget)
        status_frame.grid(row=1, column=0, sticky=(tkinter.W, tkinter.E),
                          padx=5, pady=5)

    def __init__(self, title, canvas_width, canvas_height, control_width):
        self._root = self._create_root(title)

        self._canvas_frame = None
        self._canvas = None
        self._canvas_init(canvas_width, canvas_height)

        self._control_frame = None
        self._controls = []
        self._control_frame_init(control_width)

        self._input = None
        self._key_label = None
        self._mouse_label = None
        self._input_init()

    def _shutdown(self):
        destroy_timers()
        self._canvas.destroy()
        self._root.destroy()
        sys.exit(0)

    def start(self):
        try:
            self._root.mainloop()
        except KeyboardInterrupt:
            pass
        finally:
            self._shutdown()

    def set_draw_handler(self, draw_handler):
        self._canvas.set_draw_handler(draw_handler)

    def add_button(self, text, button_handler, width=None):
        button = Button(self._control_frame, text, button_handler, width)
        self._controls.append(button)
        return button

    def add_label(self, text):
        label = Label(self._control_frame, text)
        self._controls.append(label)
        return label

    def add_input(self, text, input_handler, width):
        inp = Input(self._control_frame, text, input_handler, width)
        self._controls.append(inp)
        return inp

    def set_keydown_handler(self, key_handler):
        self._input.set_keydown_handler(key_handler)

    def set_keyup_handler(self, key_handler):
        self._input.set_keyup_handler(key_handler)

    def set_mouseclick_handler(self, mouse_handler):
        self._input.set_mouseclick_handler(mouse_handler)

    def set_mousedrag_handler(self, mouse_handler):
        self._input.set_mousedrag_handler(mouse_handler)

    def set_canvas_background(self, color):
        self._canvas.set_background(color)

    def get_canvas_textwidth(self, text, size, face='serif'):
        return self._canvas.get_textwidth(text, size, face)

def create_frame(title, canvas_width, canvas_height, control_width=200):
    return Frame(title, canvas_width, canvas_height, control_width)
<|endoftext|>


<commit_before>from collections import OrderedDict
from operator import attrgetter
from typing import Any, Dict, List, Set

from django.apps import AppConfig
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.db.models.signals import post_migrate
from django.db.utils import OperationalError

from ..utils.projector import register_projector_elements

class CoreAppConfig(AppConfig):
    name = 'openslides.core'
    verbose_name = 'OpenSlides Core'
    angular_site_module = True
    angular_projector_module = True

    def ready(self):
        # Import all required stuff.
        from .config import config
        from ..utils.rest_api import router
        from ..utils.cache import element_cache
        from .projector import get_projector_elements
        from .signals import (
            delete_django_app_permissions,
            get_permission_change_data,
            permission_change,
            post_permission_creation,
        )
        from .views import (
            ChatMessageViewSet,
            ConfigViewSet,
            CountdownViewSet,
            ProjectorMessageViewSet,
            ProjectorViewSet,
            TagViewSet,
        )
        from ..utils.constants import set_constants, get_constants_from_apps
        from .websocket import (
            NotifyWebsocketClientMessage,
            ConstantsWebsocketClientMessage,
            GetElementsWebsocketClientMessage,
            AutoupdateWebsocketClientMessage,
        )
        from ..utils.websocket import register_client_message
        from ..utils.access_permissions import required_user

        # Collect all config variables before getting the constants.
        config.collect_config_variables_from_apps()

        # Set constants
        try:
            set_constants(get_constants_from_apps())
        except (ImproperlyConfigured, OperationalError):
            # Database is not loaded. This happens in tests and migrations.
            pass

        # Define projector elements.
        register_projector_elements(get_projector_elements())

        # Connect signals.
        post_permission_creation.connect(
            delete_django_app_permissions,
            dispatch_uid='delete_django_app_permissions')
        permission_change.connect(
            get_permission_change_data,
            dispatch_uid='core_get_permission_change_data')

        post_migrate.connect(call_save_default_values, sender=self, dispatch_uid='core_save_config_default_values')

        # Register viewsets.
        router.register(self.get_model('Projector').get_collection_string(), ProjectorViewSet)
        router.register(self.get_model('ChatMessage').get_collection_string(), ChatMessageViewSet)
        router.register(self.get_model('Tag').get_collection_string(), TagViewSet)
        router.register(self.get_model('ConfigStore').get_collection_string(), ConfigViewSet, 'config')
        router.register(self.get_model('ProjectorMessage').get_collection_string(), ProjectorMessageViewSet)
        router.register(self.get_model('Countdown').get_collection_string(), CountdownViewSet)

        # Sets the cache
        try:
            element_cache.ensure_cache()
        except (ImproperlyConfigured, OperationalError):
            # This happens in the tests or in migrations. Do nothing
            pass

        # Register client messages
        register_client_message(NotifyWebsocketClientMessage())
        register_client_message(ConstantsWebsocketClientMessage())
        register_client_message(GetElementsWebsocketClientMessage())
        register_client_message(AutoupdateWebsocketClientMessage())

        # register required_users
        required_user.add_collection_string(self.get_model('ChatMessage').get_collection_string(), required_users)

    def get_config_variables(self):
        from .config_variables import get_config_variables
        return get_config_variables()

    def get_startup_elements(self):
        """
        Yields all Cachables required on startup i. e. opening the websocket
        connection.
        """
        for model_name in ('Projector', 'ChatMessage', 'Tag', 'ProjectorMessage', 'Countdown', 'ConfigStore'):
            yield self.get_model(model_name)

    def get_angular_constants(self):
        from .config import config

        constants: Dict[str, Any] = {}

        # Client settings
        client_settings_keys = [
            'MOTION_IDENTIFIER_MIN_DIGITS',
            'MOTION_IDENTIFIER_WITHOUT_BLANKS',
            'MOTIONS_ALLOW_AMENDMENTS_OF_AMENDMENTS'
        ]
        client_settings_dict = {}
        for key in client_settings_keys:
            try:
                client_settings_dict[key] = getattr(settings, key)
            except AttributeError:
                # Settings key does not exist. Do nothing. The client will
                # treat this as undefined.
                pass
        constants['OpenSlidesSettings'] = client_settings_dict

        # Config variables
        config_groups: List[Any] = []
        for config_variable in sorted(config.config_variables.values(), key=attrgetter('weight')):
            if config_variable.is_hidden():
                # Skip hidden config variables. Do not even check groups and subgroups.
                continue
            if not config_groups or config_groups[-1]['name'] != config_variable.group:
                # Add new group.
                config_groups.append(OrderedDict(
                    name=config_variable.group,
                    subgroups=[]))
            if not config_groups[-1]['subgroups'] or config_groups[-1]['subgroups'][-1]['name'] != config_variable.subgroup:
                # Add new subgroup.
                config_groups[-1]['subgroups'].append(OrderedDict(
                    name=config_variable.subgroup,
                    items=[]))
            # Add the config variable to the current group and subgroup.
            config_groups[-1]['subgroups'][-1]['items'].append(config_variable.data)
        constants['OpenSlidesConfigVariables'] = config_groups

        return constants

def call_save_default_values(**kwargs):
    from .config import config
    config.save_default_values()

def required_users(element: Dict[str, Any]) -> Set[int]:
    """
    Returns all user ids that are displayed as chatters.
    """
    return set(element['user_id'])
<commit_msg>fixing #3987 - postgreSQL migration possible again<commit_after>from collections import OrderedDict
from operator import attrgetter
from typing import Any, Dict, List, Set

from django.apps import AppConfig
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.db.models.signals import post_migrate
from django.db.utils import OperationalError, ProgrammingError

from ..utils.projector import register_projector_elements

class CoreAppConfig(AppConfig):
    name = 'openslides.core'
    verbose_name = 'OpenSlides Core'
    angular_site_module = True
    angular_projector_module = True

    def ready(self):
        # Import all required stuff.
        from .config import config
        from ..utils.rest_api import router
        from ..utils.cache import element_cache
        from .projector import get_projector_elements
        from .signals import (
            delete_django_app_permissions,
            get_permission_change_data,
            permission_change,
            post_permission_creation,
        )
        from .views import (
            ChatMessageViewSet,
            ConfigViewSet,
            CountdownViewSet,
            ProjectorMessageViewSet,
            ProjectorViewSet,
            TagViewSet,
        )
        from ..utils.constants import set_constants, get_constants_from_apps
        from .websocket import (
            NotifyWebsocketClientMessage,
            ConstantsWebsocketClientMessage,
            GetElementsWebsocketClientMessage,
            AutoupdateWebsocketClientMessage,
        )
        from ..utils.websocket import register_client_message
        from ..utils.access_permissions import required_user

        # Collect all config variables before getting the constants.
        config.collect_config_variables_from_apps()

        # Set constants
        try:
            set_constants(get_constants_from_apps())
        except (ImproperlyConfigured, OperationalError, ProgrammingError):
            # Database is not loaded. This happens in tests and migrations.
            pass

        # Define projector elements.
        register_projector_elements(get_projector_elements())

        # Connect signals.
        post_permission_creation.connect(
            delete_django_app_permissions,
            dispatch_uid='delete_django_app_permissions')
        permission_change.connect(
            get_permission_change_data,
            dispatch_uid='core_get_permission_change_data')

        post_migrate.connect(call_save_default_values, sender=self, dispatch_uid='core_save_config_default_values')

        # Register viewsets.
        router.register(self.get_model('Projector').get_collection_string(), ProjectorViewSet)
        router.register(self.get_model('ChatMessage').get_collection_string(), ChatMessageViewSet)
        router.register(self.get_model('Tag').get_collection_string(), TagViewSet)
        router.register(self.get_model('ConfigStore').get_collection_string(), ConfigViewSet, 'config')
        router.register(self.get_model('ProjectorMessage').get_collection_string(), ProjectorMessageViewSet)
        router.register(self.get_model('Countdown').get_collection_string(), CountdownViewSet)

        # Sets the cache
        try:
            element_cache.ensure_cache()
        except (ImproperlyConfigured, OperationalError, ProgrammingError):
            # This happens in the tests or in migrations. Do nothing
            pass

        # Register client messages
        register_client_message(NotifyWebsocketClientMessage())
        register_client_message(ConstantsWebsocketClientMessage())
        register_client_message(GetElementsWebsocketClientMessage())
        register_client_message(AutoupdateWebsocketClientMessage())

        # register required_users
        required_user.add_collection_string(self.get_model('ChatMessage').get_collection_string(), required_users)

    def get_config_variables(self):
        from .config_variables import get_config_variables
        return get_config_variables()

    def get_startup_elements(self):
        """
        Yields all Cachables required on startup i. e. opening the websocket
        connection.
        """
        for model_name in ('Projector', 'ChatMessage', 'Tag', 'ProjectorMessage', 'Countdown', 'ConfigStore'):
            yield self.get_model(model_name)

    def get_angular_constants(self):
        from .config import config

        constants: Dict[str, Any] = {}

        # Client settings
        client_settings_keys = [
            'MOTION_IDENTIFIER_MIN_DIGITS',
            'MOTION_IDENTIFIER_WITHOUT_BLANKS',
            'MOTIONS_ALLOW_AMENDMENTS_OF_AMENDMENTS'
        ]
        client_settings_dict = {}
        for key in client_settings_keys:
            try:
                client_settings_dict[key] = getattr(settings, key)
            except AttributeError:
                # Settings key does not exist. Do nothing. The client will
                # treat this as undefined.
                pass
        constants['OpenSlidesSettings'] = client_settings_dict

        # Config variables
        config_groups: List[Any] = []
        for config_variable in sorted(config.config_variables.values(), key=attrgetter('weight')):
            if config_variable.is_hidden():
                # Skip hidden config variables. Do not even check groups and subgroups.
                continue
            if not config_groups or config_groups[-1]['name'] != config_variable.group:
                # Add new group.
                config_groups.append(OrderedDict(
                    name=config_variable.group,
                    subgroups=[]))
            if not config_groups[-1]['subgroups'] or config_groups[-1]['subgroups'][-1]['name'] != config_variable.subgroup:
                # Add new subgroup.
                config_groups[-1]['subgroups'].append(OrderedDict(
                    name=config_variable.subgroup,
                    items=[]))
            # Add the config variable to the current group and subgroup.
            config_groups[-1]['subgroups'][-1]['items'].append(config_variable.data)
        constants['OpenSlidesConfigVariables'] = config_groups

        return constants

def call_save_default_values(**kwargs):
    from .config import config
    config.save_default_values()

def required_users(element: Dict[str, Any]) -> Set[int]:
    """
    Returns all user ids that are displayed as chatters.
    """
    return set(element['user_id'])
<|endoftext|>