diff options
author | kkania@google.com <kkania@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-19 18:11:50 +0000 |
---|---|---|
committer | kkania@google.com <kkania@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-19 18:11:50 +0000 |
commit | 5ad2514a46f2e7b188389ed146494a4bbb7eda81 (patch) | |
tree | ca18fad8ded2a62d738de10a8b0c9616ff6c9a4b /o3d | |
parent | 0efefd194671acef171dc22dc3af2d0cb4f48736 (diff) | |
download | chromium_src-5ad2514a46f2e7b188389ed146494a4bbb7eda81.zip chromium_src-5ad2514a46f2e7b188389ed146494a4bbb7eda81.tar.gz chromium_src-5ad2514a46f2e7b188389ed146494a4bbb7eda81.tar.bz2 |
Relocate some of the lab testing scripts.
Review URL: http://codereview.chromium.org/302006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29422 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d')
-rw-r--r-- | o3d/DEPS_gyp | 2 | ||||
-rw-r--r-- | o3d/tests/lab/ChangeResolution/build.bat | 21 | ||||
-rw-r--r-- | o3d/tests/lab/ChangeResolution/change_resolution.cpp | 97 | ||||
-rw-r--r-- | o3d/tests/lab/ChangeResolution/change_resolution.gyp | 16 | ||||
-rw-r--r-- | o3d/tests/lab/__init__.py | 0 | ||||
-rw-r--r-- | o3d/tests/lab/browsers.py | 216 | ||||
-rw-r--r-- | o3d/tests/lab/configure_ie.py | 303 | ||||
-rw-r--r-- | o3d/tests/lab/run_lab_test.py | 349 | ||||
-rw-r--r-- | o3d/tests/lab/runner_constants.py | 94 | ||||
-rw-r--r-- | o3d/tests/lab/runner_util.py | 159 | ||||
-rw-r--r-- | o3d/tests/lab/util.py | 379 |
11 files changed, 1635 insertions, 1 deletions
diff --git a/o3d/DEPS_gyp b/o3d/DEPS_gyp index cae722b..ca922ad 100644 --- a/o3d/DEPS_gyp +++ b/o3d/DEPS_gyp @@ -156,6 +156,6 @@ hooks = [ { # A change to a .gyp, .gypi, or to GYP itself should run the generator. "pattern": "\\.gypi?$|[/\\\\]src[/\\\\]tools[/\\\\]gyp[/\\\\]|[/\\\\]src[/\\\\]o3d[/\\\\]build[/\\\\]gyp_o3d$|MANIFEST$", - "action": ["python", "o3d/build/gyp_o3d", "o3d/build/o3d.gyp"], + "action": ["python", "o3d/build/gyp_o3d", "o3d/build/o3d.gyp", "o3d/tests/lab/ChangeResolution/change_resolution.gyp"], }, ] diff --git a/o3d/tests/lab/ChangeResolution/build.bat b/o3d/tests/lab/ChangeResolution/build.bat new file mode 100644 index 0000000..d0ddcf9 --- /dev/null +++ b/o3d/tests/lab/ChangeResolution/build.bat @@ -0,0 +1,21 @@ +@echo off
+REM Builds the change resolution project.
+
+:vs2005
+reg query HKLM\Software\Microsoft\VisualStudio\8.0 /v InstallDir || goto vs2008
+set vs_reg=HKLM\Software\Microsoft\VisualStudio\8.0
+goto getkey
+
+:vs2008
+reg query HKLM\Software\Microsoft\VisualStudio\9.0 /v InstallDir || goto vsNone
+set vs_reg=HKLM\Software\Microsoft\VisualStudio\9.0
+goto getkey
+
+:vsNone
+echo Visual Studio path cannot be found in registry. Are you using 2005 or 2008?
+exit
+
+:getkey
+for /f "tokens=2,*" %%a in ('reg query %vs_reg% /v InstallDir ^| findstr InstallDir') do set vs_dir=%%b
+
+"%vs_dir%devenv.com" change_resolution.sln /build
\ No newline at end of file diff --git a/o3d/tests/lab/ChangeResolution/change_resolution.cpp b/o3d/tests/lab/ChangeResolution/change_resolution.cpp new file mode 100644 index 0000000..8d7cdb4 --- /dev/null +++ b/o3d/tests/lab/ChangeResolution/change_resolution.cpp @@ -0,0 +1,97 @@ +// @@REWRITE(insert c-copyright) +// @@REWRITE(delete-start) +// Copyright 2008 Google Inc. All Rights Reserved. +// Author: thomaslewis@google.com (Thomas Lewis) +// @@REWRITE(delete-end) + +// Tool to change the screen resolution on Windows. + +#include <stdio.h> +#include <wchar.h> +#include "Windows.h" + + +int wmain(int argc, wchar_t* argv[]) { + if (argc == 1) { + // Output current resolution and bits per pixel. + DEVMODE dmode; + memset(&dmode, 0, sizeof(DEVMODE)); + if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dmode)) { + printf("Current settings (X resolution, Y resolution, bits per " + "pixel):\n"); + printf("%lu\n%lu\n%lu\n", dmode.dmPelsWidth, dmode.dmPelsHeight, + dmode.dmBitsPerPel); + return 0; + } else { + printf("Failed to retrieve current resolution.\n"); + return 1; + } + } else if (argc != 4) { + printf("Pass 3 args: X resolution, Y resolution, and bits per pixel to " + "change to or no args to view current settings."); + return 1; + } + + // Read and validate arguments. + int width = 0; + if (!swscanf_s((const wchar_t *)argv[1], L"%d", &width)) { + printf("Invalid value for width. It must be a number."); + return 1; + } + int height = 0; + if (!swscanf_s((const wchar_t *)argv[2], L"%d", &height)) { + printf("Invalid value for height. It must be a number."); + return 1; + } + int depth = 0; + if (!swscanf_s((const wchar_t *)argv[3], L"%d", &depth)) { + printf("Invalid value for bits per pixel. It must be a number."); + return 1; + } + + // Find matching mode. + DEVMODE dmode; + memset(&dmode, 0, sizeof(DEVMODE)); + dmode.dmSize = sizeof(dmode); + + bool foundMode = false; + for (int i = 0; !foundMode && EnumDisplaySettings(NULL, i, &dmode); ++i) { + foundMode = (dmode.dmPelsWidth == (DWORD)width) && + (dmode.dmPelsHeight == (DWORD)height) && + (dmode.dmBitsPerPel == (DWORD)depth); + } + if (!foundMode) { + printf("No screen mode found with given resolution and bits per pixel.\n"); + return 1; + } + + // Change to matched mode. + dmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + + LONG result = ChangeDisplaySettings(&dmode, CDS_UPDATEREGISTRY); + if (result == DISP_CHANGE_SUCCESSFUL) { + printf("Resolution changes successfully.\n"); + return 0; + } else if (result == DISP_CHANGE_BADDUALVIEW) { + printf("Windows XP: The settings change was unsuccessful because system " + "is DualView capable.\n"); + } else if (result == DISP_CHANGE_BADFLAGS) { + printf("An invalid set of flags was passed in.\n"); + } else if (result == DISP_CHANGE_BADMODE) { + printf("The graphics mode is not supported.\n"); + } else if (result == DISP_CHANGE_BADPARAM) { + printf("An invalid parameter was passed in. This can include an invalid " + "flag or combination of flags.\n"); + } else if (result == DISP_CHANGE_FAILED) { + printf("The display driver failed the specified graphics mode.\n"); + } else if (result == DISP_CHANGE_NOTUPDATED) { + printf("Windows NT/2000/XP: Unable to write settings to the registry.\n"); + } else if (result == DISP_CHANGE_RESTART) { + printf("The computer must be restarted in order for the graphics mode to " + "work.\n"); + } else { + printf("Unknown error.\n"); + } + + return 1; +} diff --git a/o3d/tests/lab/ChangeResolution/change_resolution.gyp b/o3d/tests/lab/ChangeResolution/change_resolution.gyp new file mode 100644 index 0000000..3e2ffa3 --- /dev/null +++ b/o3d/tests/lab/ChangeResolution/change_resolution.gyp @@ -0,0 +1,16 @@ +# Copyright (c) 2009 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. + +{ + 'targets': [ + { + 'target_name': 'ChangeResolution', + 'type': 'executable', + 'defines': ['_WIN32_WINNT=0x0501'], # for ChangeDisplaySettings + 'sources': [ + 'change_resolution.cpp', + ], + }, + ], +} diff --git a/o3d/tests/lab/__init__.py b/o3d/tests/lab/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/o3d/tests/lab/__init__.py diff --git a/o3d/tests/lab/browsers.py b/o3d/tests/lab/browsers.py new file mode 100644 index 0000000..f5172d7 --- /dev/null +++ b/o3d/tests/lab/browsers.py @@ -0,0 +1,216 @@ +#!/usr/bin/python +# Copyright (c) 2006-2008 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. +# WINDOWS ONLY + +"""Installs a specified browser.""" + +import ConfigParser +import copy +import os +import logging +import time + +import util + + +BROWSERS = [{'name': 'ie6', + 'version': '6', + 'family': 'ie', + 'selenium_name': '*iexplore', + }, + {'name': 'ie7', + 'version': '7', + 'family': 'ie', + 'selenium_name': '*iexplore', + }, + {'name': 'ie8', + 'version': '8', + 'family': 'ie', + 'selenium_name': '*iexplore', + }, + {'name': 'ff3', + 'version': '3', + 'family': 'firefox', + 'selenium_name': '*firefox' + }, + {'name': 'ff3.5', + 'version': '3.5', + 'family': 'firefox', + 'selenium_name': '*firefox' + }, + {'name': 'ff2', + 'version': '2', + 'family': 'firefox', + 'selenium_name': '*firefox' + }, + {'name': 'chrome-dev', + 'version': '4', + 'family': 'chrome', + 'selenium_name': '*googlechrome' + }, + {'name': 'chrome-beta', + 'version': '3', + 'family': 'chrome', + 'selenium_name': '*googlechrome' + }, + {'name': 'chrome-stable', + 'version': '3', + 'family': 'chrome', + 'selenium_name': '*googlechrome' + }, + {'name': 'saf3', + 'family': 'safari', + 'selenium_name': '*safari' + }, + {'name': 'saf4', + 'family': 'safari', + 'selenium_name': '*safari' + }] + +if util.IsXP(): + HOME_PATH = 'C:\\Documents and Settings\\testing' +elif util.IsVista() or util.IsWin7(): + HOME_PATH = 'C:\\Users\\testing' + +SERVER = 'http://tsqaserver/mac/o3d/imaging/software/browsers' + +SOFTWARE_PATH = 'C:\\imaging\\software' + +_FIREFOX_VERSIONS = {'ff3': SERVER + '/ff3.0.13.exe', + 'ff2': SERVER + '/ff2.0.11.exe', + 'ff3.5': SERVER + '/ff3.5.2.exe'} + +_CHROME_VERSIONS = {'chrome-stable': SERVER + '/chrome_stable.exe', + 'chrome-dev': SERVER + '/chrome_dev.exe', + 'chrome-beta': SERVER + '/chrome_beta.exe'} + +_IE_7_URLS = {'xp': SERVER + '/IE7-WindowsXP-x86-enu.exe', + 'xp_64': SERVER + '/IE7-WindowsServer2003-x64-enu.exe'} + +_IE_8_URLS = {'xp': SERVER + '/IE8-WindowsXP-x86-ENU.exe', + 'xp_64': SERVER + '/IE8-WindowsServer2003-x64-ENU.exe', + 'vista': SERVER + '/IE8-WindowsVista-x86-ENU.exe', + 'vista_64': SERVER + '/IE8-WindowsVista-x64-ENU.exe'} + + +def GetVersionNumber(family): + """Get the version number of the installed browser in |family|. Returns + None if browser is not installed.""" + if family == 'ie': + path = r'HKLM\SOFTWARE\Microsoft\Internet Explorer' + version = util.RegQuery(path, 'Version') + if version is not None: + return version[0:1] + + elif family == 'firefox': + path = r'HKLM\SOFTWARE\Mozilla\Mozilla Firefox' + version = util.RegQuery(path, 'CurrentVersion') + if version is not None: + if version.startswith('3.0'): + return '3' + elif version.startswith('3.5'): + return '3.5' + elif version.startswith('2'): + return '2' + + elif family == 'chrome': + chrome_dir = (HOME_PATH + r'\Local Settings\Application Data\Google' + + r'\Chrome\Application') + files = os.listdir(chrome_dir) + + for file in files: + # The Chrome application directory has a folder named with the version #. + if file.startswith('2.'): + return '2' + elif file.startswith('3.'): + return '3' + elif file.startswith('4.'): + return '4' + + return None + + +def IsBrowserInstalled(browser): + """Checks if |browser| is installed.""" + + return GetVersionNumber(browser['family']).startswith(browser['version']) + +def Install(browser): + """Installs |browser|, if necessary. It is not possible to install + an older version of the already installed browser currently. + + Args: + browser: specific browst to install. + Returns: + whether browser is installed. + """ + + # Only dynamic installation of browsers for Windows now. + if not util.IsWindows(): + return True + + logging.info('Wants to install ' + browser['name']) + version = GetVersionNumber(browser['family']) + if version is None: + logging.info('No version of %s is installed' % browser['family']) + else: + logging.info('Version %s of %s is installed already' + % (version, browser['family'])) + + if not IsBrowserInstalled(browser): + + install_cmd = None + + # Download browser. + logging.info('Downloading ' + browser['name']) + if browser['family'] == 'ie': + + if browser['name'] == 'ie7': + install_cmd = util.Download(_IE_7_URLS[util.GetOSPrefix()], + SOFTWARE_PATH) + elif browser['name'] == 'ie8': + install_cmd = util.Download(_IE_8_URLS[util.GetOSPrefix()], + SOFTWARE_PATH) + + install_cmd += ' /passive /no-default' + + elif browser['family'] == 'firefox': + if util.IsWindows(): + install = util.Download(_FIREFOX_VERSIONS[browser['name']], + SOFTWARE_PATH) + install_cmd = install + ' -ms' + elif browser['family'] == 'chrome': + if util.IsWindows(): + install_cmd = util.Download(_CHROME_VERSIONS[browser['name']], + SOFTWARE_PATH) + else: + logging.error('Browser %s is not currently supported' % browser['name']) + + + # Run installation. + if install_cmd is not None: + logging.info('Installing browser: ' + install_cmd) + if install_cmd is None or util.RunStr(install_cmd) != 0: + logging.error('Could not install %s' % browser['name']) + return False + + # Do post installation things. + if browser['family'] == 'chrome': + first_run = file(HOME_PATH + '\\Local Settings\\' + 'Application Data\\Google\\Chrome\\Application\\' + 'First Run', 'w') + first_run.close() + + # Wait for Chrome to install. Reboot to get rid of that first run UI. + time.sleep(90) + util.Reboot() + logging.error('Could not reboot. Needed for Chrome installation.') + return False + + else: + logging.info(browser['name'] + ' already installed') + + return True + diff --git a/o3d/tests/lab/configure_ie.py b/o3d/tests/lab/configure_ie.py new file mode 100644 index 0000000..6279308 --- /dev/null +++ b/o3d/tests/lab/configure_ie.py @@ -0,0 +1,303 @@ +#!/usr/bin/python +# Copyright (c) 2006-2008 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. + +"""Manages configuring Microsoft Internet Explorer.""" + +import logging +import os +import re +import subprocess +import urllib +import urlparse +import util + + +def _ConfigureSecurityKeyForZone(zone_number, key, value): + """Set key under the Zone settings in Security. + + Args: + zone_number: Integer identifier of zone to edit. + key: key under "HKEY_CURRENT_USER\Software\Microsoft\Windows\ + CurrentVersion\Internet Settings\Zones\<Zone>" + to edit. + value: new value for key. + + Returns: + True on success. + """ + if not util.RegAdd(('HKEY_CURRENT_USER\\Software\\Microsoft\\' + 'Windows\\CurrentVersion\\Internet Settings\\' + 'Zones\\%s' % zone_number), key, util.REG_DWORD, value): + return False + return True + + +def GetInstalledVersion(): + """Reads the version of IE installed by checking the registry. + + The complete version is returned. For example: 6.0.2900.55.5512. + + Returns: + String for version or None on failure. + """ + return util.RegQuery('HKLM\\Software\\Microsoft\\Internet Explorer','Version') + + +def ConfigurePopupBlocker(active): + """Configure if popup blocker is active for IE 8. + + Args: + active: new active state for popup blocker. + + Returns: + True on success. + """ + if active: + value = '1' + else: + value = '0' + + # IE 8 + if not util.RegAdd(('HKEY_LOCAL_MACHINE\\Software\\Microsoft' + '\\Internet Explorer\\Main\\FeatureControl\\' + 'FEATURE_WEBOC_POPUPMANAGEMENT'), 'iexplore.exe', + util.REG_DWORD, value): + return False + + # IE 6 + if active: + value = 'yes' + else: + value = 'no' + + if not util.RegAdd(('HKEY_CURRENT_USER\\Software\\Microsoft\\' + 'Internet Explorer\\New Windows'), + 'PopupMgr', util.REG_SZ, value): + return False + + return True + + +def DisableDefaultBrowserCheck(active): + """Turn off the check to be set as default browser on start up. + + Args: + active: if True then disables browser check, otherwise enables. + + Returns: + True on success. + """ + if active: + value = 'no' + else: + value = 'yes' + + path = 'HKEY_CURRENT_USER\\Software\\Microsoft\\Internet Explorer\\Main' + + if not util.RegAdd(path, 'Check_Associations', util.REG_SZ, value): + return False + + return True + + +def DisableWelcomeWindowForInternetExplorer8(): + """Turn off the window which asks user to configure IE8 on start up. + + Returns: + True on success. + """ + path = 'HKEY_CURRENT_USER\\Software\\Microsoft\\Internet Explorer\\Main' + + if not util.RegAdd(path, 'IE8RunOnceLastShown', util.REG_DWORD, '1'): + return False + + if not util.RegAdd(path, 'IE8RunOnceLastShown_TIMESTAMP', util.REG_BINARY, + '79a29ba0bbb7c901'): + return False + + if not util.RegAdd(path, 'IE8RunOnceCompletionTime', util.REG_BINARY, + 'f98da5b2bbb7c901'): + return False + + if not util.RegAdd(path, 'IE8TourShown', util.REG_DWORD, '1'): + return False + + if not util.RegAdd(path, 'IE8TourShownTime', util.REG_BINARY, + '1ada825779b6c901'): + return False + + if not util.RegAdd(path, 'DisableFirstRunCustomize', util.REG_DWORD, '1'): + return False + + return True + + +def DisableInformationBarIntro(): + """Turn off the dialog which tells you about information bar. + + Returns: + True on success. + """ + path = (r'HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer' + r'\InformationBar') + + if not util.RegAdd(path, 'FirstTime', util.REG_DWORD, '0'): + return False + + return True + + +def DisableInternetExplorerPhishingFilter(): + """Turn off the dialog for Phishing filter which blocks tests on IE 7. + + Returns: + True on success. + """ + path = ('HKEY_CURRENT_USER\\Software\\Microsoft\\' + 'Internet Explorer\\PhishingFilter') + + if not util.RegAdd(path, 'Enabled', util.REG_DWORD, '1'): + return False + + if not util.RegAdd(path, 'ShownVerifyBalloon', util.REG_DWORD, '1'): + return False + + if not util.RegAdd(path, 'EnabledV8', util.REG_DWORD, '1'): + return False + + return True + + +def ConfigureAllowActiveX(activate): + """Configure internet zone to use Active X. + + Args: + activate: Turn on if True, otherwise turn off. + + Returns: + True on success. + """ + internet_zone = 3 + + if activate: + value = '00000000' + else: + value = '00000003' + + return _ConfigureSecurityKeyForZone(internet_zone, '1200', value) + + +def ConfigureAllowPreviouslyUnusedActiveXControlsToRun(activate): + """Configure internet zone to allow new Active X controls to run. + + Args: + activate: Turn on if True, otherwise turn off. + + Returns: + True on success. + """ + internet_zone = 3 + + if activate: + value = '00000000' + else: + value = '00000003' + + return _ConfigureSecurityKeyForZone(internet_zone, '1208', value) + + +def ConfigureAllowAllDomainsToUseActiveX(activate): + """Configure internet zone to allow all domains to use Active X. + + Args: + activate: Turn on if True, otherwise turn off. + + Returns: + True on success. + """ + internet_zone = 3 + + if activate: + value = '00000000' + else: + value = '00000003' + + return _ConfigureSecurityKeyForZone(internet_zone, '120B', value) + + +def ConfigureAllowActiveContentFromMyComputer(activate): + """Allow Active X to run from local files loaded from My Computer. + + This option is shown under Security of the Advanced tab of Internet Options. + + Args: + activate: Turn on if True, otherwise turn off. + + Returns: + True on success. + """ + if activate: + value = '0' + else: + value = '1' + + if not util.RegAdd(('HKEY_CURRENT_USER\\Software\\Microsoft\\' + 'Internet Explorer\\Main\\FeatureControl\\' + 'FEATURE_LOCALMACHINE_LOCKDOWN'), + 'iexplore.exe', util.REG_DWORD, value): + return False + if not util.RegAdd(('HKLM\\SOFTWARE\\Microsoft\\Internet Explorer\\Main\\' + 'FeatureControl\\FEATURE_LOCALMACHINE_LOCKDOWN'), + 'iexplore.exe', util.REG_DWORD, value): + return False + + return True + + +def ConfigureIE(): + """Calls the other methods to allow testing with Internet Explorer. + + Returns: + True on success. + """ + on = True + logging.info('Configuring IE...') + logging.info('Allowing all active domains to use ActiveX...') + if not ConfigureAllowAllDomainsToUseActiveX(on): + return False + + logging.info('Allowing ActiveX...') + if not ConfigureAllowActiveX(on): + return False + + logging.info('Allowing previously unused ActiveX to run...') + if not ConfigureAllowPreviouslyUnusedActiveXControlsToRun(on): + return False + + logging.info('Disabling default browser check...') + if not DisableDefaultBrowserCheck(on): + return False + + logging.info('Allowing active content from my computer...') + if not ConfigureAllowActiveContentFromMyComputer(on): + return False + + logging.info('Disabling popup blocker...') + if not ConfigurePopupBlocker(False): + return False + + logging.info('Disabling Phishing Filter...') + if not DisableInternetExplorerPhishingFilter(): + return False + + logging.info('Disabling welcome window for IE 8...') + if not DisableWelcomeWindowForInternetExplorer8(): + return False + + logging.info('Disabling info bar intro...') + if not DisableInformationBarIntro(): + return False + + return True diff --git a/o3d/tests/lab/run_lab_test.py b/o3d/tests/lab/run_lab_test.py new file mode 100644 index 0000000..9ab486a --- /dev/null +++ b/o3d/tests/lab/run_lab_test.py @@ -0,0 +1,349 @@ +#!/usr/bin/python2.6.2 +# Copyright 2009, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +"""Prepares to run the selenium lab tests and finally invokes the selenium +runner main.py. + +Usage: + run_lab_test.py [test_config_path] + +Args: + browser_config_path: path to test config file (optional) +""" + +import copy +import logging +import optparse +import os +import shutil +import subprocess +import sys +import threading +import time + +import browsers +import configure_ie +import runner_constants as const +import runner_util as run_util +import util + +# Resolution to configure video card to before running tests. +SCREEN_WIDTH = 1280 +SCREEN_HEIGHT = 1024 +SCREEN_BPP = 32 + +join = os.path.join + +if util.IsWindows(): + IMAGE_DIFF_PATH = join(const.O3D_PATH, 'third_party', 'pdiff', 'files', + 'bin', 'win', 'perceptualdiff.exe') +elif util.IsMac(): + IMAGE_DIFF_PATH = join(const.O3D_PATH, 'third_party', 'pdiff', 'files', + 'bin', 'mac', 'perceptualdiff') +else: + IMAGE_DIFF_PATH = join(const.O3D_PATH, 'third_party', 'pdiff', 'files', + 'bin', 'linux', 'perceptualdiff') + +SELENIUM_TEST_RUNNER_PATH = join(const.O3D_PATH, 'o3d', 'tests', 'selenium', + 'main.py') + +SELENIUM_JAR_PATH = join(const.O3D_PATH, 'third_party', 'selenium_rc', 'files', + 'selenium-server', 'selenium-server.jar') + +O3D_REFERENCE_IMAGES_PATH = join(const.O3D_PATH, 'o3d', 'o3d_assets', 'tests', + 'screenshots') + +SCREENSHOTS_PATH = join(const.RESULTS_PATH,'screenshots') + +SELENIUM_BROWSERS = ['*iexplore', '*firefox', '*googlechrome', '*safari'] + +# Set total test timeout to 90 minutes. +TEST_TIMEOUT_SECS = 60 * 90.0 + + +class TestRunningThread(threading.Thread): + """Runs test in separate thread. Allows timeout if test blocks.""" + + def __init__(self, command): + threading.Thread.__init__(self) + # Make the test running thread a daemon thread. It blocks waiting for + # output from the test. By being a daemon this program can exit even + # if the runner thread is deadlocked waiting for output from the test. + self.setDaemon(True) + self.command = command + self.return_code = None + self.has_started_event = threading.Event() + self.finished_event = threading.Event() + + def run(self): + logging.info('Running tests:') + logging.info(' '.join(self.command)) + self.test_process = subprocess.Popen(args=self.command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + + self.has_started_event.set() + logging.info('Output from running test follows:') + + while True: + line = self.test_process.stdout.readline() + if line: + print line.strip('\n') + else: + break + + self.finished_event.set() + + + def HasFinished(self): + # The main thread must poll the test thread because it is possible + # for test_process.readline() to hang. This can happen when a debug + # window opens from a crashing program. This way, we can know when + # our process has exited, and then try to kill all the windows. + if not self.has_started_event.isSet(): + # The tests cannot have finished if they have not even started. + return False + + self.test_process.poll() + + code = self.test_process.returncode + + if code is not None and util.IsWindows(): + # Wait for the test runner to exit safely, if it is able. + time.sleep(5) + + if not self.finished_event.isSet(): + # Something is preventing proper exiting of test runner. + # Try to kill all the debug windows. + logging.info('Trying to clean up after the test. Ignore errors here.') + os.system('TASKKILL /F /IM dwwin.exe') + os.system('TASKKILL /F /IM iedw.exe') + + # Windows Error Reporting (on Vista) + os.system('TASKKILL /F /IM WerFault.exe') + + # Browsers. + os.system('TASKKILL /F /IM chrome.exe') + os.system('TASKKILL /F /IM iexplore.exe') + os.system('TASKKILL /F /IM firefox.exe') + + # Wait and see if the test is allowed to finish now. + time.sleep(5) + + if not self.finished_event.isSet(): + logging.error('Could not kill all the extra processes created by the' + + 'test run.') + else: + logging.info('Test process exited succesfully.') + + return code is not None + + + def GetReturnCode(self): + """Returns the exit code from the test runner, or special code 128 if + test runner did not exit.""" + + if not self.has_started_event.isSet(): + code = 128 + else: + code = self.test_process.returncode + if code is None: + code = 128 + return code + + + +def RunTest(browser): + """Runs tests on |browser|. + Args: + browser: the browser to test. + Returns: + True on success. + """ + # Run selenium test. + os.chdir(const.AUTO_PATH) + + if util.IsWindows(): + if not run_util.EnsureWindowsScreenResolution(SCREEN_WIDTH, SCREEN_HEIGHT, + SCREEN_BPP): + logging.error('Failed to configure screen resolution.') + return False + + + + # Clear all screenshots. + logging.info('** Deleting previous screenshots.') + if os.path.exists(SCREENSHOTS_PATH): + shutil.rmtree(SCREENSHOTS_PATH) + + os.makedirs(SCREENSHOTS_PATH) + + logging.info('** Running selenium tests...') + + # -u for unbuffered output. + # Use Python2.4 for two reasons. First, this is more or less the standard. + # Second, if we use Python2.6 or later, we must manually shutdown the + # httpserver, or the next run will overlap ports, which causes + # some strange problems/exceptions. + args = [const.PYTHON, '-u', SELENIUM_TEST_RUNNER_PATH] + + args.append('--browser=' + browser) + + args.append('--servertimeout=80') + args.append('--product_dir=' + const.PRODUCT_DIR_PATH) + args.append('--verbose=0') + args.append('--screenshots') + args.append('--screencompare=' + IMAGE_DIFF_PATH) + args.append('--screenshotsdir=' + SCREENSHOTS_PATH) + args.append('--referencedir=' + O3D_REFERENCE_IMAGES_PATH) + args.append('--selenium_server=' + SELENIUM_JAR_PATH) + args.append('--testprefix=Test') + args.append('--testsuffixes=small,medium,large') + + runner = TestRunningThread(args) + runner.start() + + timeout_time = time.time() + TEST_TIMEOUT_SECS + while not runner.HasFinished(): + if time.time() > timeout_time: + break + time.sleep(5) + + return runner.GetReturnCode() + + +def main(argv): + logfile = os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'log_run_lab_test.txt') + util.ConfigureLogging(logfile) + logging.info('Running on machine: ' + util.IpAddress()) + + if len(argv) > 2: + logging.error('Usage: run_lab_test.py [test_config_file]') + return 1 + + if len(argv) == 2: + # Use given config file. + config_path = argv[1] + else: + # Use default config file. + config_path = os.path.join(const.HOME_PATH, 'test_config.txt') + + # Copy this config file so we can erase entries without destroying the + # defaults. + copy_config_path = os.path.join(const.SCRIPTS_PATH, '_test_progress.txt') + if os.path.exists(copy_config_path): + os.remove(copy_config_path) + shutil.copyfile(config_path, copy_config_path) + config_path = copy_config_path + + # Add self to startup. + # This is needed because we may restart during browser installation, and we + # need to start again if this happens. + util.AddToStartup('run_lab_test', const.PYTHON + ' ' + + os.path.abspath(__file__) + ' ' + config_path) + + if not run_util.UninstallO3DPlugin(): + logging.error('Could not successfully uninstall O3D. Tests will not be run.') + return 1 + if not run_util.InstallO3DPlugin(): + logging.error('Unable to install O3D plugin. Tests will not be run.') + return 1 + + # Grab test configuration info from config file. + if not os.path.exists(config_path): + logging.error('Browser config file "%s" could not be found.' % config_path) + return 1 + else: + config_file = open(config_path, 'rU') + test_browsers = [] + while True: + browser = config_file.readline().strip() + if len(browser) == 0: + # No more lines in the file, go ahead and break. + break + test_browsers += [browser] + browsers_in_file = copy.deepcopy(test_browsers) + config_file.close() + + + # Test browsers. + test_passed = False + for browser in test_browsers: + + for profile in browsers.BROWSERS: + if profile['name'] == browser: + # Install browser. + if not browsers.Install(profile): + logging.error('Failed to install ' + profile['name']) + test_passed = False + break + + # Configure IE. + if profile['family'] == 'ie': + if not configure_ie.ConfigureIE(): + logging.error('Failed to configure IE.') + test_passed = False + break + + # Run tests. + if RunTest(profile['selenium_name']) != 0: + logging.info('Selenium tests failed.') + test_passed = False + elif browser == test_browsers[0]: + # Only set test_passed to True if first browser. We want to fail if + # just one fails. + logging.info('Selenium tests passed.') + test_passed = True + break + else: + logging.error('"%s" in given config file "%s" is not a browser name' + % (browser, config_path)) + test_passed = False + + # Remove browser from test list + browsers_in_file.remove(browser) + config_file = open(config_path, 'w') + for b in browsers_in_file: + config_file.write(b + '\n') + config_file.close() + + if test_passed: + return 0 + else: + return 1 + +if __name__ == '__main__': + code = main(sys.argv) + # Remove self from startup. + util.RemoveFromStartup('run_lab_test') + sys.exit(code) diff --git a/o3d/tests/lab/runner_constants.py b/o3d/tests/lab/runner_constants.py new file mode 100644 index 0000000..c0e2d0d --- /dev/null +++ b/o3d/tests/lab/runner_constants.py @@ -0,0 +1,94 @@ +#!/usr/bin/python2.6.2
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+"""Defines common O3D test runner constants.
+
+"""
+
+import os
+import sys
+
+import util
+
+join = os.path.join
+
+if util.IsWindows():
+ AUTO_PATH = r'C:\auto'
+ PYTHON = r'C:\Python24\python.exe'
+ if util.IsXP():
+ HOME_PATH = r'C:\Documents and Settings\testing'
+ else:
+ HOME_PATH = r'C:\Users\testing'
+
+elif util.IsMac():
+ AUTO_PATH = '/Users/testing/auto'
+ PYTHON = 'python'
+ HOME_PATH = '/Users/testing'
+
+elif util.IsLinux():
+ AUTO_PATH = '/home/testing/auto'
+ PYTHON = 'python'
+ HOME_PATH = '/home/testing'
+
+else:
+ print 'Only Windows, Mac, and Linux are supported.'
+ sys.exit(1)
+
+O3D_PATH = join(AUTO_PATH, 'o3d')
+SCRIPTS_PATH = join(AUTO_PATH, 'scripts')
+RESULTS_PATH = join(AUTO_PATH, 'results')
+SOFTWARE_PATH = join(AUTO_PATH, 'software')
+
+# Build directories.
+if util.IsWindows():
+ BUILD_PATH = join(O3D_PATH, 'o3d', 'build')
+elif util.IsMac():
+ BUILD_PATH = join(O3D_PATH, 'xcodebuild')
+else:
+ BUILD_PATH = join(O3D_PATH, 'sconsbuild')
+
+if os.path.exists(join(BUILD_PATH, 'Debug')):
+ PRODUCT_DIR_PATH = join(BUILD_PATH, 'Debug')
+else:
+ PRODUCT_DIR_PATH = join(BUILD_PATH, 'Release')
+
+# Plugin locations.
+INSTALL_PATHS = []
+if util.IsWindows():
+ INSTALL_PATHS += [join(HOME_PATH, 'Application Data', 'Mozilla',
+ 'plugins', 'npo3dautoplugin.dll')]
+ INSTALL_PATHS += [join(HOME_PATH, 'Application Data', 'Google', 'O3D',
+ 'o3d_host.dll')]
+elif util.IsMac():
+ INSTALL_PATHS += ['/Library/Internet Plug-Ins/O3D.plugin']
+else:
+ INSTALL_PATHS += [join(HOME_PATH, '.mozilla', 'plugins',
+ 'libnpo3dautoplugin.so')]
diff --git a/o3d/tests/lab/runner_util.py b/o3d/tests/lab/runner_util.py new file mode 100644 index 0000000..44026db --- /dev/null +++ b/o3d/tests/lab/runner_util.py @@ -0,0 +1,159 @@ +#!/usr/bin/python2.6.2 +# Copyright 2009, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +"""Utility functions for running the lab tests. + +""" + +import logging +import os +import subprocess +import shutil +import sys +import time + +import runner_constants as const +import util + +CHANGE_RESOLUTION_PATH = (const.O3D_PATH + '/o3d/tests/lab' + '/ChangeResolution/Default/changeresolution.exe') + +def EnsureWindowsScreenResolution(width, height, bpp): + """Performs all steps needed to configure system for testing on Windows. + + Args: + width: new screen resolution width + height: new screen resolution height + bpp: new screen resolution bytes per pixel + Returns: + True on success. + """ + + command = 'call "%s" %d %d %d' % (CHANGE_RESOLUTION_PATH, width, height, bpp) + + + our_process = subprocess.Popen(command, + shell=True, + stdout=None, + stderr=None, + universal_newlines=True) + + our_process.wait() + + return our_process.returncode == 0 + + +def AddPythonPath(path): + """Add path to PYTHONPATH in environment.""" + try: + os.environ['PYTHONPATH'] = path + os.pathsep + os.environ['PYTHONPATH'] + except KeyError: + os.environ['PYTHONPATH'] = path + + # Need to put at front of sys.path so python will try to import modules from + # path before locations further down the sys.path. + sys.path = [path] + sys.path + + +def InstallO3DPlugin(): + """Installs O3D plugin.""" + + if util.IsWindows(): + installer_path = os.path.join(const.PRODUCT_DIR_PATH, 'o3d.msi') + elif util.IsMac(): + dmg_path = os.path.join(const.PRODUCT_DIR_PATH, 'o3d.dmg') + volumes_path = util.MountDiskImage(dmg_path) + if volumes_path is None: + return False + else: + installer_path = os.path.join(volumes_path, 'O3D.mpkg') + else: + plugin_path = os.path.join(const.PRODUCT_DIR_PATH, 'libnpo3dautoplugin.so') + plugin_dst_dir = os.path.expanduser('~/.mozilla/plugins') + try: + os.makedirs(plugin_dst_dir) + except os.error: + pass + + plugin_dst = os.path.join(plugin_dst_dir, 'libnpo3dautoplugin.so') + shutil.copyfile(plugin_path, plugin_dst) + return True + + logging.info('Installing plugin:"%s"', installer_path) + + if not os.path.exists(installer_path): + logging.error('Installer path not found, %s' % installer_path) + return False + + if util.IsWindows(): + install_command = 'msiexec.exe /i "%s"' % installer_path + elif util.IsMac(): + admin_password = 'g00gl3' + install_command = ('echo %s | sudo -S /usr/sbin/installer -pkg ' + '"%s" -target /' % (admin_password, installer_path)) + + logging.info('Installing...') + result = os.system(install_command) + if result: + logging.error('Install failed.') + return False + logging.info('Installed.') + + if util.IsMac(): + util.UnmountDiskImage(volumes_path) + + return True + +def UninstallO3DPlugin(): + """Uninstalls O3D. + + Returns: + True, if O3D is no longer installed.""" + + if util.IsWindows(): + installer_path = os.path.join(const.PRODUCT_DIR_PATH, 'o3d.msi') + os.system('msiexec.exe /x "%s" /q' % installer_path) + + else: + for path in const.INSTALL_PATHS: + if os.path.exists(path): + os.remove(path) + + return not DoesAnO3DPluginExist() + +def DoesAnO3DPluginExist(): + for path in const.INSTALL_PATHS: + if os.path.exists(path): + return True + return False + + +
\ No newline at end of file diff --git a/o3d/tests/lab/util.py b/o3d/tests/lab/util.py new file mode 100644 index 0000000..03a989f --- /dev/null +++ b/o3d/tests/lab/util.py @@ -0,0 +1,379 @@ +#!/usr/bin/python2.6.2 +# Copyright 2009, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +"""Common utilities for O3D's lab test runner. + +""" + +import logging +import logging.handlers +import os +import platform +import subprocess +import sys +import socket +import time +import urllib +import urlparse + + +# Logging utilities. +def ConfigureLogging(logging_file_path=None): + logging_format = '%(levelname)8s -- %(message)s' + + root_logger = logging.getLogger('') + root_logger.setLevel(logging.NOTSET) + + # Log to file. + if logging_file_path is not None: + file_handler = logging.handlers.RotatingFileHandler(logging_file_path, + maxBytes=5000000) + formatter = logging.Formatter(logging_format) + file_handler.setFormatter(formatter) + file_handler.setLevel(logging.NOTSET) + root_logger.addHandler(file_handler) + + if not os.path.exists(logging_file_path): + print 'Failed to create log. Can\'t continue:"%s"' % (logging_file_path) + sys.exit(1) + + logging.info('Appending log output to file:"%s"', logging_file_path) + + # Log to stdout. + stdout_handler = logging.StreamHandler(sys.stdout) + formatter = logging.Formatter(logging_format) + stdout_handler.setFormatter(formatter) + stdout_handler.setLevel(logging.NOTSET) + root_logger.addHandler(stdout_handler) + + + + +# OS Detection utilities. +def IsMac(): + return platform.system() == 'Darwin' + +def IsWindows(): + return platform.system() == 'Windows' + +def IsLinux(): + return platform.system() == 'Linux' + +def IsWin7(): + return IsWindows() and platform.release() == 'post2008Server' + +def IsWin7_64(): + return IsWin7() and os.path.exists(r'C:\Program Files (x86)') + +def IsVista(): + return IsWindows() and platform.release() == 'Vista' + +def IsVista_64(): + return IsVista() and os.path.exists(r'C:\Program Files (x86)') + +def IsXP(): + return (IsWindows() and platform.release() == 'XP') or IsXP_64() + +def IsXP_64(): + return (IsWindows() and platform.release() == '2003Server' and + os.path.exists(r'C:\Program Files (x86)')) + +def GetOSName(): + return platform.platform() + +def GetOSPrefix(): + if IsWin7_64(): + prefix = 'win7_64' + elif IsWin7(): + prefix = 'win7' + elif IsVista_64(): + prefix = 'vista_64' + elif IsVista(): + prefix = 'vista' + elif IsXP_64(): + prefix = 'xp_64' + elif IsXP(): + prefix = 'xp' + elif IsMac(): + prefix = 'mac' + elif IsLinux(): + prefix = 'linux' + else: + prefix = None + return prefix + + +NONE = 0 +WARN = 1 +ERROR = 2 +FATAL = 3 +def RunCommand(cmd_string, level=ERROR, info=None): + # Execute command. + if info == None: + info = cmd_string + if level != NONE: + logging.info(info + '...') + + + our_process = subprocess.Popen(cmd_string, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + + # Read output so that process isn't blocked with a filled buffer. + (output,err_output) = our_process.communicate() + + return_code = our_process.returncode + + + if level != NONE: + if return_code: + # Command failed. Take appropriate action. + if level == WARN: + logging.warn('Failed: ' + info) + elif level == ERROR or level == FATAL: + logging.error('Failed: ' + info) + + logging.info(' Details for ' + cmd_string + ':') + logging.info(' Failed with return code:%d', return_code) + for line in output.split('\n'): + logging.info(' ' + line) + + if level == FATAL: + sys.exit(1) + + # Command succeeded. Report success. + else: + logging.info('Success.') + + return (return_code, output) + + +def RunStr(cmd_string, level=ERROR, info=None): + return RunCommand(cmd_string, level, info)[0] + + +def RunWithOutput(command, level=ERROR, info=None): + return RunCommand(' '.join(command), level, info) + + +def Run(command, level=ERROR, info=None): + return RunWithOutput(command, level, info)[0] + + +REG_DWORD = 'REG_DWORD' +REG_SZ = 'REG_SZ' +REG_BINARY = 'REG_BINARY' +def RegAdd(path, key, type, value, level=ERROR, info=None): + + cmd = ['REG', 'ADD', '"' + path + '"', '/V', key, + '/t', type, '/d', '"' + value + '"', '/F'] + + return Run(cmd, level, info) == 0 + + +def RegDoesExist(path, key, level=NONE): + cmd = ['REG', 'QUERY', '"' + path + '"', '/V', key] + + return Run(cmd, level) == 0 + + +def RegDel(path, key, level=ERROR): + cmd = ['REG', 'DELETE', '"' + path + '"', '/V', key, '/F'] + + return Run(cmd, level) == 0 + + +def RegMaybeDel(path, key, level): + if RegQuery(path, key): + return RegDel(path, key, level) + + return True + + +def RegQuery(path, key): + """Read value from registry. + + Args: + path: full path into registry for parent of value to read. + key: key under parent to read. + + Returns: + Value on success and None on failure. + """ + arguments = ['REG', 'QUERY', path, + '/V', key] + reg_process = subprocess.Popen(args=arguments, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + + value = None + + lines = reg_process.stdout.readlines() + for line in lines: + line = line.strip('\n') + check_key = line.lstrip() + if check_key.startswith(key): + type_and_value = check_key[len(key):].lstrip() + for i in range(len(type_and_value)): + if type_and_value[i] == '\t' or type_and_value[i] == ' ': + value = type_and_value[i:].lstrip() + break + + reg_process.wait() + if reg_process.returncode: + return None + return value + + +# Other utilities. +def IpAddress(): + """Get IP address of this machine.""" + address_info = socket.gethostbyname_ex(socket.gethostname()) + if len(address_info) == 3: + ip = address_info[2] + if ip: + ip = ip[0] + return ip + + logging.error('Failed to get local machine IP.') + return None + + +def Reboot(): + """Reboots machine.""" + if IsWindows(): + cmd = 'shutdown -r -t 1 -f' + elif IsMac(): + cmd = 'shutdown -r now' + + logging.info('Rebooting machine...') + os.system(cmd) + time.sleep(10) + logging.error('Failed: Could not reboot') + + +def Download(url, prefix_dir): + """Downloads single file at |url| to |prefix_dir|. + + Returns: + local_path: the path to the downloaded file, or None if download was + unsuccessful.""" + + parsed_url = urlparse.urlparse(url) + path = parsed_url[2] + cut = path.count('/') - 1 + name = path.rsplit('/', 1)[1] + local_path = os.path.join(prefix_dir, name) + + if not os.path.exists(prefix_dir): + os.mkdir(prefix_dir) + urllib.urlretrieve(url, local_path) + + if not os.path.exists(local_path): + logging.error('Could not download %s to %s' % (url, local_path)) + return None + + return local_path + + +def AddToStartup(name, command): + if IsWindows(): + path = r'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run' + RegAdd(path, name, REG_SZ, command) + + +def RemoveFromStartup(name): + if IsWindows(): + path = r'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run' + RegMaybeDel(path, name, ERROR) + + +def MountDiskImage(dmg_path): + """Mounts disk image. + + Args: + dmg_path: path to image that will be mounted. + + Returns: + Path to mounted disk on success or None on failure. + """ + mount_path = None + logging.info('Mounting %s...' % dmg_path) + + cmd = ['hdiutil', 'attach', '"' + dmg_path + '"'] + (return_code, output) = RunWithOutput(cmd) + + if return_code == 0 and output: + # Attempt to grab the mounted path from the command output. + # This should be safe regardless of actual output. + mount_path = output.strip().split('\n')[-1].split('\t')[-1] + + logging.info('Disk image mounted at %s' % mount_path) + + # Wait for mounting operation to complete. + time.sleep(10) + if os.path.exists(mount_path): + logging.info('Mount point is accessible.') + else: + mount_path = None + + if mount_path is None: + logging.error('Could not mount properly.') + + return mount_path + + +def UnmountDiskImage(mount_path): + """Unmounts disk image. + + Args: + mount_path: path to unmount. + + Returns: + True on success. + """ + logging.info('Unmounting %s...' % mount_path) + + if not os.path.exists(mount_path): + logging.warn('Nothing is mounted at this path.') + return True + + Run(['umount', '"' + mount_path + '"']) + + time.sleep(10) + if os.path.exists(mount_path): + logging.error('Image is still mounted at path:"%s"', mount_path) + return False + else: + return True |