Example #1
0
def tweak_webdriver_manager():
    """webdriver-manager (version 13.0.0) uses `os.arch()` to determine the
    architecture of the operating system, however, this function can only be
    used to determine the architecture of the machine that compiled `node`.
    In the case of Windows, we are using the portable version,
    which was compiled on `ia32` machine so that is the value returned by this
    `os.arch` function. Unfortunately, webdriver-manager seems to assume that
    Windows wouldn't run on the ia32 architecture, so its help function used to
    determine download link returns null for this, which means that the
    application has no idea about where to download the correct version.

    https://github.com/angular/webdriver-manager/blob/b7539a5a3897a8a76abae7245f0de8175718b142/lib/provider/chromedriver.ts#L16
    https://github.com/angular/webdriver-manager/blob/b7539a5a3897a8a76abae7245f0de8175718b142/lib/provider/geckodriver.ts#L21
    https://github.com/angular/webdriver-manager/blob/b7539a5a3897a8a76abae7245f0de8175718b142/lib/provider/chromedriver.ts#L167
    https://github.com/nodejs/node/issues/17036
    """
    try:
        if common.is_windows_os():
            regex_pattern = PATTERN_FOR_REPLACE_WEBDRIVER_CODE
            arch = 'x64' if common.is_x64_architecture() else 'x86'
            replace = 'this.osArch = "%s";' % arch
            common.inplace_replace_file(CHROME_PROVIDER_FILE_PATH,
                                        regex_pattern, replace)
            common.inplace_replace_file(GECKO_PROVIDER_FILE_PATH,
                                        regex_pattern, replace)
        yield
    finally:
        if common.is_windows_os():
            undo_webdriver_tweak()
Example #2
0
def managed_redis_server():
    """Run the redis server within a context manager that ends it gracefully."""
    if common.is_windows_os():
        raise Exception(
            'The redis command line interface is not installed because your '
            'machine is on the Windows operating system. The redis server '
            'cannot start.')

    # Check if a redis dump file currently exists. This file contains residual
    # data from a previous run of the redis server. If it exists, removes the
    # dump file so that the redis server starts with a clean slate.
    if os.path.exists(common.REDIS_DUMP_PATH):
        os.remove(common.REDIS_DUMP_PATH)

    # OK to use shell=True here because we are passing string literals and
    # constants, so there is no risk of a shell-injection attack.
    proc_context = managed_process(
        [common.REDIS_SERVER_PATH, common.REDIS_CONF_PATH],
        human_readable_name='Redis Server', shell=True)
    with proc_context as proc:
        common.wait_for_port_to_be_in_use(feconf.REDISPORT)
        try:
            yield proc
        finally:
            subprocess.check_call([common.REDIS_CLI_PATH, 'shutdown', 'nosave'])
Example #3
0
def cleanup():
    """Kill the running subprocesses and server fired in this program, set
    constants back to default values.
    """
    google_app_engine_path = '%s/' % common.GOOGLE_APP_ENGINE_SDK_HOME
    webdriver_download_path = '%s/selenium' % WEBDRIVER_HOME_PATH
    if common.is_windows_os():
        # In windows system, the java command line will use absolute path.
        webdriver_download_path = os.path.abspath(webdriver_download_path)
    processes_to_kill = [
        '.*%s.*' % re.escape(google_app_engine_path),
        '.*%s.*' % re.escape(webdriver_download_path)
    ]
    for p in SUBPROCESSES:
        _kill_process(p)

    for p in processes_to_kill:
        common.kill_processes_based_on_regex(p)

    build.set_constants_to_default()
    common.stop_redis_server()

    for port in [OPPIA_SERVER_PORT, GOOGLE_APP_ENGINE_PORT]:
        if not common.wait_for_port_to_be_closed(port):
            raise RuntimeError(
                'Port {} failed to close within {} seconds.'.format(
                    port, common.MAX_WAIT_TIME_FOR_PORT_TO_CLOSE_SECS))
Example #4
0
def install_hook():
    """Installs the pre_commit_hook script and makes it executable.
    It ensures that oppia/ is the root folder.

    Raises:
        ValueError. If chmod command fails.
    """
    oppia_dir = os.getcwd()
    hooks_dir = os.path.join(oppia_dir, '.git', 'hooks')
    pre_commit_file = os.path.join(hooks_dir, 'pre-commit')
    chmod_cmd = ['chmod', '+x', pre_commit_file]
    if os.path.islink(pre_commit_file):
        python_utils.PRINT('Symlink already exists')
    else:
        # This is needed, because otherwise some systems symlink/copy the .pyc
        # file instead of the .py file.
        this_file = __file__.replace('pyc', 'py')
        try:
            os.symlink(os.path.abspath(this_file), pre_commit_file)
            python_utils.PRINT('Created symlink in .git/hooks directory')
        # Raises AttributeError on windows, OSError added as failsafe.
        except (OSError, AttributeError):
            shutil.copy(this_file, pre_commit_file)
            python_utils.PRINT('Copied file to .git/hooks directory')

    python_utils.PRINT('Making pre-commit hook file executable ...')
    if not common.is_windows_os():
        _, err_chmod_cmd = start_subprocess_for_result(chmod_cmd)

        if not err_chmod_cmd:
            python_utils.PRINT('pre-commit hook file is now executable!')
        else:
            raise ValueError(err_chmod_cmd)
Example #5
0
def cleanup():
    """Kill the running subprocesses and server fired in this program."""
    google_app_engine_path = '%s/' % common.GOOGLE_APP_ENGINE_HOME
    webdriver_download_path = '%s/downloads' % WEBDRIVER_HOME_PATH
    if common.is_windows_os():
        # In windows system, the java command line will use absolute path.
        webdriver_download_path = os.path.abspath(webdriver_download_path)
    processes_to_kill = [
        '.*%s.*' % re.escape(google_app_engine_path),
        '.*%s.*' % re.escape(webdriver_download_path)
    ]
    for p in SUBPROCESSES:
        p.kill()

    for p in processes_to_kill:
        common.kill_processes_based_on_regex(p)
Example #6
0
def cleanup():
    """Kill the running subprocesses and server fired in this program, set
    constants back to default values.
    """
    google_app_engine_path = '%s/' % common.GOOGLE_APP_ENGINE_SDK_HOME
    webdriver_download_path = '%s/selenium' % WEBDRIVER_HOME_PATH
    if common.is_windows_os():
        # In windows system, the java command line will use absolute path.
        webdriver_download_path = os.path.abspath(webdriver_download_path)
    processes_to_kill = [
        '.*%s.*' % re.escape(google_app_engine_path),
        '.*%s.*' % re.escape(webdriver_download_path)
    ]
    for p in SUBPROCESSES:
        p.kill()

    for p in processes_to_kill:
        common.kill_processes_based_on_regex(p)
    build.set_constants_to_default()
    common.stop_redis_server()
Example #7
0
def managed_webdriver_server(chrome_version=None):
    """Returns context manager to start/stop the Webdriver server gracefully.

    This context manager updates Google Chrome before starting the server.

    Args:
        chrome_version: str|None. The version of Google Chrome to run the tests
            on. If None, then the currently-installed version of Google Chrome
            is used instead.

    Yields:
        psutil.Process. The Webdriver process.
    """
    if chrome_version is None:
        # Although there are spaces between Google and Chrome in the path, we
        # don't need to escape them for Popen (as opposed to on the terminal, in
        # which case we would need to escape them for the command to run).
        chrome_command = (
            '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
            if common.is_mac_os() else 'google-chrome')
        try:
            output = subprocess.check_output([chrome_command, '--version'])
        except OSError:
            # For the error message on macOS, we need to add the backslashes in.
            # This is because it is likely that a user will try to run the
            # command on their terminal and, as mentioned above, the macOS
            # chrome version command has spaces in the path which need to be
            # escaped for successful terminal use.
            raise Exception(
                'Failed to execute "%s --version" command. This is used to '
                'determine the chromedriver version to use. Please set the '
                'chromedriver version manually using the '
                '--chrome_driver_version flag. To determine the '
                'chromedriver version to be used, please follow the '
                'instructions mentioned in the following URL:\n'
                'https://chromedriver.chromium.org/downloads/version-selection'
                % chrome_command.replace(' ', r'\ '))

        installed_version_parts = b''.join(re.findall(rb'[0-9.]', output))
        installed_version = '.'.join(
            installed_version_parts.decode('utf-8').split('.')[:-1])
        response = utils.url_open(
            'https://chromedriver.storage.googleapis.com/LATEST_RELEASE_%s' % (
                installed_version))
        chrome_version = response.read().decode('utf-8')

    print('\n\nCHROME VERSION: %s' % chrome_version)
    subprocess.check_call([
        common.NODE_BIN_PATH, common.WEBDRIVER_MANAGER_BIN_PATH, 'update',
        '--versions.chrome', chrome_version,
    ])

    with contextlib.ExitStack() as exit_stack:
        if common.is_windows_os():
            # NOTE: webdriver-manager (version 13.0.0) uses `os.arch()` to
            # determine the architecture of the operating system, however, this
            # function can only be used to determine the architecture of the
            # machine that compiled `node`. In the case of Windows, we are using
            # the portable version, which was compiled on `ia32` machine so that
            # is the value returned by this `os.arch` function. Unfortunately,
            # webdriver-manager seems to assume that Windows wouldn't run on the
            # ia32 architecture, so its help function used to determine download
            # link returns null for this, which means that the application has
            # no idea about where to download the correct version.
            #
            # https://github.com/angular/webdriver-manager/blob/b7539a5a3897a8a76abae7245f0de8175718b142/lib/provider/chromedriver.ts#L16
            # https://github.com/angular/webdriver-manager/blob/b7539a5a3897a8a76abae7245f0de8175718b142/lib/provider/geckodriver.ts#L21
            # https://github.com/angular/webdriver-manager/blob/b7539a5a3897a8a76abae7245f0de8175718b142/lib/provider/chromedriver.ts#L167
            # https://github.com/nodejs/node/issues/17036
            regex_pattern = re.escape('this.osArch = os.arch();')
            arch = 'x64' if common.is_x64_architecture() else 'x86'
            replacement_string = 'this.osArch = "%s";' % arch
            exit_stack.enter_context(common.inplace_replace_file_context(
                common.CHROME_PROVIDER_FILE_PATH, regex_pattern,
                replacement_string))
            exit_stack.enter_context(common.inplace_replace_file_context(
                common.GECKO_PROVIDER_FILE_PATH, regex_pattern,
                replacement_string))

        # OK to use shell=True here because we are passing string literals and
        # constants, so there is no risk of a shell-injection attack.
        proc = exit_stack.enter_context(managed_process([
            common.NODE_BIN_PATH, common.WEBDRIVER_MANAGER_BIN_PATH, 'start',
            '--versions.chrome', chrome_version, '--quiet', '--standalone',
        ], human_readable_name='Webdriver manager', shell=True))

        common.wait_for_port_to_be_in_use(4444)

        yield proc