summaryrefslogtreecommitdiffstats
path: root/o3d
diff options
context:
space:
mode:
authorkkania@google.com <kkania@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-19 18:11:50 +0000
committerkkania@google.com <kkania@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-19 18:11:50 +0000
commit5ad2514a46f2e7b188389ed146494a4bbb7eda81 (patch)
treeca18fad8ded2a62d738de10a8b0c9616ff6c9a4b /o3d
parent0efefd194671acef171dc22dc3af2d0cb4f48736 (diff)
downloadchromium_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_gyp2
-rw-r--r--o3d/tests/lab/ChangeResolution/build.bat21
-rw-r--r--o3d/tests/lab/ChangeResolution/change_resolution.cpp97
-rw-r--r--o3d/tests/lab/ChangeResolution/change_resolution.gyp16
-rw-r--r--o3d/tests/lab/__init__.py0
-rw-r--r--o3d/tests/lab/browsers.py216
-rw-r--r--o3d/tests/lab/configure_ie.py303
-rw-r--r--o3d/tests/lab/run_lab_test.py349
-rw-r--r--o3d/tests/lab/runner_constants.py94
-rw-r--r--o3d/tests/lab/runner_util.py159
-rw-r--r--o3d/tests/lab/util.py379
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