diff options
author | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-03 00:27:35 +0000 |
---|---|---|
committer | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-03 00:27:35 +0000 |
commit | 3682c7736e9cb6cf952c644cf967591bebd5f93a (patch) | |
tree | 52d82d11acc398a145fb6424c6b79c5b7ba4f134 | |
parent | 307f211eff77a5bfcba3485996ce6b77e73aaeab (diff) | |
download | chromium_src-3682c7736e9cb6cf952c644cf967591bebd5f93a.zip chromium_src-3682c7736e9cb6cf952c644cf967591bebd5f93a.tar.gz chromium_src-3682c7736e9cb6cf952c644cf967591bebd5f93a.tar.bz2 |
Remove pyauto tests.
BUG=224072
R=avi@chromium.org
Review URL: https://codereview.chromium.org/222873002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@261240 0039d316-1c4b-4281-b951-d872f2087c98
170 files changed, 0 insertions, 34442 deletions
diff --git a/chrome/test/functional.DEPS/DEPS b/chrome/test/functional.DEPS/DEPS deleted file mode 100644 index 60bfa47..0000000 --- a/chrome/test/functional.DEPS/DEPS +++ /dev/null @@ -1,25 +0,0 @@ - - -deps = { - 'src/chrome/test/data': '/trunk/src/chrome/test/data', - 'src/chrome/test/functional': '/trunk/src/chrome/test/functional', - 'src/chrome/test/pyautolib': '/trunk/src/chrome/test/pyautolib', - 'src/content/test/data': '/trunk/src/content/test/data', - 'src/net/data/ssl/certificates': '/trunk/src/net/data/ssl/certificates', - 'src/net/tools/testserver': '/trunk/src/net/tools/testserver', - 'src/third_party/pyftpdlib/src': 'http://pyftpdlib.googlecode.com/svn/trunk', - 'src/third_party/pywebsocket/src': - 'http://pywebsocket.googlecode.com/svn/trunk/src@662', - 'src/third_party/simplejson': '/trunk/src/third_party/simplejson', - 'src/third_party/tlslite': '/trunk/src/third_party/tlslite', - 'src/third_party/webdriver/pylib/selenium': - 'http://selenium.googlecode.com/svn/trunk/py/selenium@18337', - 'src/third_party/webpagereplay': - 'http://web-page-replay.googlecode.com/svn/trunk@492', -} - -deps_os = { - 'win': { - 'src/third_party/python_26': '/trunk/tools/third_party/python_26@66685', - } -} diff --git a/chrome/test/functional/OWNERS b/chrome/test/functional/OWNERS deleted file mode 100644 index 54bfe2c..0000000 --- a/chrome/test/functional/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -stgao@chromium.org - -# for media stuff -dalecurtis@chromium.org diff --git a/chrome/test/functional/PYAUTO_TESTS b/chrome/test/functional/PYAUTO_TESTS deleted file mode 100644 index b2346b2..0000000 --- a/chrome/test/functional/PYAUTO_TESTS +++ /dev/null @@ -1,500 +0,0 @@ -# Copyright 2013 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. -# -# This file lists the pyauto tests that run as a part of the functional test -# suite. -# -# Tests can be enabled on a per-platform basis. Tests not listed here will -# not be run. -# -# Within each platform, tests are specified in up to 3 groups: (1) enabled -# tests; (2) tests that are permanently disabled because they do not apply -# to the given platform and hence should not be run; and (3) disabled tests -# that need to be investigated/fixed. Tests are listed alphabetically within -# each group. -# -# Test names can be specified in any of the following ways: -# 1. as a module, in which case all tests in that module will be run -# example: 'test_basic' -# 2. or as a test case, in which case all tests in that test case will be run -# example: 'test_basic.SimpleTest' -# 3. or as an individual test -# example: 'test_basic.SimpleTest.testCanOpenGoogle' -# -# Tests beginning with '-' will be excluded. This can be used to enforce -# exclusions for a particular platform. -# Names beginning with '@' will be treated as suite names and will be -# recursively expanded. - -{ - # This suite gets run on 'Google Chrome' builds. - 'FULL': { - 'all': [ - 'about_plugins_ui.AboutPluginsUITest', - 'about_plugins_ui.ChromeAboutPluginsUITest', - 'apptest', - 'autofill', - 'codesign', - 'crash_reporter', - 'execute_javascript', - 'extensions', - 'fullscreen_mouselock', - 'gtalk.test_basic', - 'infobars', - 'media.media_basic_playback', - 'multiprofile', - 'nacl_sdk', - 'ntp', - 'omnibox', - 'passwords', - 'prefs', - 'prefs_ui', - 'pyauto_webdriver', - 'search_engines', - 'special_tabs', - 'test_pyauto', - - # =========================== - # Permanently-disabled tests. - # =========================== - - # ================================================== - # Disabled tests that need to be investigated/fixed. - # ================================================== - # crbug.com/145006 - '-about_plugins_ui.ChromeAboutPluginsUITest.testEnableAndDisableFlashPlugin', - # crbug.com/100365 - '-autofill.AutofillTest.testDisplayLineItemForEntriesWithNoCCNum', - # crbug.com/171828 - '-autofill.AutofillTest.testNoDuplicatePhoneNumsInPrefs', - # The source is behind. Waiting for dev to automate the update. - # crbug.com/109160 - '-execute_javascript.ExecuteJavascriptTest.testExecuteJavascriptInExtension', - # crbug.com/123396 - '-fullscreen_mouselock.FullscreenMouselockTest.testPatternsForFSAndML', - # crbug.com/132665 - '-fullscreen_mouselock.FullscreenMouselockTest.testTabFSExitWhenNavBackToPrevPage', - '-fullscreen_mouselock.FullscreenMouselockTest.testTabFSExitWhenNavToNewPage', - '-fullscreen_mouselock.FullscreenMouselockTest.testMLExitWhenNavBackToPrevPage', - '-fullscreen_mouselock.FullscreenMouselockTest.testMLExitWhenNavToNewPage', - # crbug.com/140460 - '-fullscreen_mouselock.FullscreenMouselockTest.testNoTabFSExitWhenJSExitMouseLock', - # crosbug.com/136875 - '-fullscreen_mouselock.FullscreenMouselockTest.testTabFSDoesNotExitForAnchorLinks', - # crbug.com/179263 - '-gtalk.test_basic.BasicTest.testCurrentVersion', - '-gtalk.test_basic.BasicTest.testRCVersion', - # crbug.com/165796 - '-infobars.OneClickInfobarTest', - # crbug.com/131874 - '-infobars.OneClickInfobarTest.testNoOneClickInfobarAfterCancel', - # crbug.com/133315 - '-infobars.OneClickInfobarTest.testDisplayOneClickInfobarAfterDismiss', - # Mysteriously broken? - # crbug.com/138857 - '-multiprofile.MultiprofileTest.testMakeSearchEngineDefaultInMultiprofile', - # crbug.com/179268 - '-ntp.NTPTest.testCannotUninstallWebStore', - '-ntp.NTPTest.testGetAppsInNewProfile', - '-ntp.NTPTest.testGetAppsWhenInstallApp', - '-ntp.NTPTest.testGetAppsWhenInstallNonApps', - '-ntp.NTPTest.testUninstallApp', - # crbug.com/143308 - '-omnibox.OmniboxLiveTest.testGoogleSearch', - # crbug.com/71715 - '-omnibox.OmniboxTest.testHistoryResult', - # crbug.com/123019 - '-omnibox.OmniboxTest.testAutoCompleteForNonAsciiSearch', - # crbug.com/137041 - '-omnibox.OmniboxTest.testDifferentTypesOfResults', - # crbug.com/162341 - '-prefs.PrefsTest.testAllowSelectedGeoTracking', - '-prefs.PrefsTest.testDismissedInfobarSavesNoEntry', - '-prefs.PrefsTest.testGeolocationBlockedWhenTrackingDenied', - '-prefs.PrefsTest.testGeolocationPref', - # crbug.com/85600 - '-prefs.PrefsTest.testNavigationStateOnSessionRestore', - # crbug.com/157271 - '-prefs_ui.BasicSettingsUITest.testCancelStartupURLSetting', - '-prefs_ui.BasicSettingsUITest.testSetStartupPages', - '-prefs_ui.BasicSettingsUITest.testUseCurrentPagesForStartup', - # crbug.com/162341 - '-prefs_ui.PrefsUITest.testBehaviorValueCorrectlyDisplayed', - # crbug.com/112051 - '-prefs_ui.PrefsUITest.testChangeExceptionBehaviorUI', - # crbug.com/157271 - '-prefs_ui.PrefsUITest.testDeleteExceptionUI', - # crbug.com/151973 - '-prefs_ui.PrefsUITest.testInitialLineEntryInIncognitoUI', - # crbug.com/162341 - '-prefs_ui.PrefsUITest.testLocationSettingOptionsUI', - '-prefs_ui.PrefsUITest.testNoInitialLineEntryInUI', - # crbug.com/132285 - '-special_tabs.SpecialTabsTest.testSpecialURLTabs', - # crbug.com/168081 - '-ntp.NTPTest.testCloseOneTab', - ], - - 'win': [ - 'gpu', - # =========================== - # Permanently-disabled tests. - # =========================== - - # ================================================== - # Disabled tests that need to be investigated/fixed. - # ================================================== - # crbug.com/105948 - '-autofill.AutofillTest.testPostalCodeAndStateLabelsBasedOnCountry', - # crbug.com/111289 - '-extensions.ExtensionsTest.testAllowAccessFileURLs', - # crbug.com/113090 - '-extensions.ExtensionsTest.testAllowIncognitoExtension', - # crbug.com/171490 - '-passwords.PasswordTest.testPasswdInfoNotStoredWhenAutocompleteOff', - # crbug.com/117569 - '-passwords.PasswordTest.testSavedPasswordInTabsAndWindows', - # crbug.com/98526 - '-pyauto_webdriver.PyAutoWebDriverTest.testCanConnectToRestartedBrowser', - '-pyauto_webdriver.PyAutoWebDriverTest.testTypeIntoTextBox', - ], - - 'mac': [ - # =========================== - # Permanently-disabled tests. - # =========================== - - # ================================================== - # Disabled tests that need to be investigated/fixed. - # ================================================== - # Keychain popups make autofill/password tests difficult: crbug.com/49378 - '-prefs_ui.PrefsUITest.testSetPasswordAndDelete', - # codesign tests should run *after* signing. crbug.com/50481 - '-codesign', - # crbug.com/124922 - '-fullscreen_mouselock.FullscreenMouselockTest.testMouseLockExitWhenBrowserLoseFocus', - # crbug.com/125989 - '-fullscreen_mouselock.FullscreenMouselockTest.testMouseLockExitWhenAlertDialogShow', - # Fails on chrome-mac-10_7-qa only: crbug.com/124886 - '-fullscreen_mouselock.FullscreenMouselockTest.testPrefsForFullscreenExit', - '-fullscreen_mouselock.FullscreenMouselockTest.testNoMouseLockWhenCancelFS', - # crbug.com/121484 - '-multiprofile.MultiprofileTest.test20NewProfiles', - '-ntp.NTPTest.testLaunchAppNewWindow', # crbug.com/79812 - # crbug.com/70437 - '-omnibox.OmniboxTest.testHistoryResult', - # crbug.com/91617 - '-omnibox.OmniboxTest.testContentHistory', - # Keychain popups make autofill/password tests difficult: crbug.com/49378 - '-passwords', - # crbug.com/69619 - '-search_engines.SearchEnginesTest.testDiscoverSearchEngine', - # crbug.com/98526 - '-pyauto_webdriver.PyAutoWebDriverTest.testCanConnectToRestartedBrowser', - '-pyauto_webdriver.PyAutoWebDriverTest.testTypeIntoTextBox', - ], - - 'linux': [ - 'test_clean_exit', - - # =========================== - # Permanently-disabled tests. - # =========================== - # System password manager obstructs password automation. - '-passwords', - - # ================================================== - # Disabled tests that need to be investigated/fixed. - # ================================================== - # crbug.com/111289 - '-extensions.ExtensionsTest.testAllowAccessFileURLs', - # crbug.com/91033 - '-omnibox.OmniboxTest.testOmniboxSearchHistory', - ], - - 'chromeos': [ - 'chromeos_basic', - 'chromeos_browser', - 'chromeos_crosh', - 'chromeos_power', - 'chromeos_prefs', - 'chromeos_security', - 'chromeos_time', - 'secure_shell', - 'youtube', - - # =========================== - # Permanently-disabled tests. - # =========================== - # No codesign verification on ChromeOS. - '-codesign', - # Sync is already signed in with the login account. - # So one-click infobar tests do tno apply to chromeos. - '-infobars.OneClickInfobarTest', - # Multi-profile doesn't apply to chromeos yet. - '-multiprofile', - '-ntp.NTPTest.testDifferentProfileNotAppearInMostVisited', - # No NaCl support on ChromeOS. - '-nacl_sdk', - - # ================================================== - # Disabled tests that need to be investigated/fixed. - # ================================================== - # crbug.com/132337 - '-autofill.AutofillTest.testTabOrderForEditAddress', - # crosbug.com/19556 - '-extensions.ExtensionsTest.testAllowAccessFileURLs', - '-extensions.ExtensionsTest.testAllowIncognitoExtension', - '-extensions.ExtensionsTest.testDisableEnableExtension', - # crbug.com/134593 - '-gtalk.test_basic.BasicTest.testCurrentVersion', - '-gtalk.test_basic.BasicTest.testRCVersion', - # crosbug.com/24496 - '-infobars.InfobarTest.testPluginCrashForMultiTabs', - # crbug.com/109035 - '-infobars.InfobarTest.testPluginCrashInfobar', - # crosbug.com/14256 - '-ntp.NTPTest.testLaunchAppFullScreen', - # Content history broken in omnibox. crosbug.com/14416 - '-omnibox.OmniboxTest.testContentHistory', - # crbug.com/91033 - '-omnibox.OmniboxTest.testOmniboxSearchHistory', - # crosbug.com/19760 - '-passwords.PasswordTest.testClearFetchedCredForNewUserName', - '-passwords.PasswordTest.testSavedPasswordInTabsAndWindows', - # onunload popups get created in the same window on chromeos - # Session restore not working with PyAuto. crosbug.com/12648 - '-prefs.PrefsTest.testNavigationStateOnSessionRestore', - '-prefs.PrefsTest.testSessionRestoreURLs', - '-prefs.PrefsTest.testSessionRestore', - # Deal with i18n chars. crosbug.com/12639 - '-omnibox.OmniboxTest.testCrazyFilenames', - # crosbug.com/20025 - '-chromeos_browser.ChromeosBrowserTest.testFullScreen', - # Chrome driver does not work in Chrome OS. - # crosbug.com/19556 - '-prefs_ui', - '-prefs.PrefsTest.testGeolocationBlockedWhenTrackingDenied', - '-prefs.PrefsTest.testGeolocationPref', - '-pyauto_webdriver', - ], - }, - - # Performance tests. - 'PERFORMANCE': { - 'all': [ - 'perf', - - # ================================================== - # Disabled tests that need to be investigated/fixed. - # ================================================== - '-perf.HTML5BenchmarkTest', # crbug.com/134476 - '-perf.LiveWebappLoadTest.testNewTabGmail', # crbug.com/136554 - '-perf.ScrollTest.testGmailScroll', # crbug.com/136554 - '-perf.WebGLTest.testWebGLField', # crbug.com/132797 - - # =========================== - # Permanently-disabled tests. - # =========================== - # Invoked outside of the pyauto_tests framework. - '-perf.BenchmarkPerfTest.testSpaceport', - '-perf.PopularSitesScrollTest.test2012Q3', - ], - 'win': [ - # ================================================== - # Disabled tests that need to be investigated/fixed. - # ================================================== - '-perf.GPUPerfTest', # Fails. Discuss with prachij@. - '-perf.PageCyclerNetSimTest', # Dependence missing: crbug.com/132559 - '-perf.LiveGamePerfTest', # Requires linux /proc/stat. - '-perf.YoutubePerfTest', # AttributeError: AssertPlayingState. - - # =========================== - # Permanently-disabled tests. - # =========================== - '-perf.MemoryTest', # Designed only for ChromeOS. - '-perf.NetflixPerfTest', # Designed only for ChromeOS. - ], - 'mac': [ - # ================================================== - # Disabled tests that need to be investigated/fixed. - # ================================================== - '-perf.GPUPerfTest', # Fails. Discuss with prachij@. - '-perf.PageCyclerNetSimTest', # Dependence missing: crbug.com/132559 - '-perf.LiveGamePerfTest', # Requires linux /proc/stat. - '-perf.YoutubePerfTest', # AttributeError: AssertPlayingState. - - # =========================== - # Permanently-disabled tests. - # =========================== - '-perf.MemoryTest', # Designed only for ChromeOS. - '-perf.NetflixPerfTest', # Designed only for ChromeOS. - ], - 'linux': [ - # ================================================== - # Disabled tests that need to be investigated/fixed. - # ================================================== - '-perf.GPUPerfTest', # Fails. Discuss with prachij@. - '-perf.PageCyclerNetSimTest', # Dependence missing: crbug.com/132559 - '-perf.WebGLTest', # May not render WebGL; need to verify on QA bot. - '-perf.YoutubePerfTest', # AttributeError: AssertPlayingState. - - # =========================== - # Permanently-disabled tests. - # =========================== - '-perf.MemoryTest', # Designed only for ChromeOS. - '-perf.NetflixPerfTest', # Designed only for ChromeOS. - ], - 'chromeos': [ - # ================================================== - # Disabled tests that need to be investigated/fixed. - # ================================================== - '-perf.NetflixPerfTest', # crosbug.com/32320. - '-perf.YoutubePerfTest', # crbug.com/233706 - - # =========================== - # Permanently-disabled tests. - # =========================== - '-perf.GPUPerfTest', # Designed only for Chrome desktop. - ], - }, - - 'PERFORMANCE_LAB': { - 'all': [ - 'perf.BenchmarkPerfTest.testV8BenchmarkSuite', - 'perf.FlashTest.testFlashGaming', - 'perf.ScrollTest.testGooglePlusScroll', - 'perf.TabPerfTest.test20Tabs', - 'perf.WebGLTest.testWebGLSpaceRocks', - ], - }, - - 'EMPTY': { - }, - - # ChromeOS flash tests. - 'CHROMEOS_FLASH': { - 'chromeos': [ - 'flash', - ], - }, - - # ChromeOS volume tests. - 'CHROMEOS_VOLUME': { - 'chromeos': [ - 'chromeos_volume', - ], - }, - - # ChromeOS Accessibility tests. - 'CHROMEOS_ACCESSIBILITY': { - 'chromeos': [ - 'chromeos_accessibility', - ], - }, - - # PGO performance suite. - 'PGO': { - 'chromeos': [ - 'perf.BenchmarkPerfTest.testV8BenchmarkSuite', - 'perf.PageCyclerTest', - 'perf.ScrollTest.testGooglePlusScroll', - ], - }, - - # Subset of ChromeOS performance tests for seaboard. - 'CHROMEOS_PERF_SEABOARD': { - 'chromeos': [ - 'perf.ScrollTest.testBlankPageScroll', - 'perf.ScrollTest.testGooglePlusScroll', - 'perf.ScrollTest.testTextScroll', - ], - }, - - # HTML5 media performance tests. - 'AV_PERF': { - 'linux': [ - 'media.audio_latency_perf', - 'media.audio_playback_perf', - 'media.media_constrained_network_perf', - 'media.media_scrub_perf', - 'media.media_seek_perf', - 'media.media_stat_perf', - 'media.mixed_audio_latency_perf', - ], - 'win': [ - 'media.audio_latency_perf', - 'media.audio_playback_perf', - 'media.media_constrained_network_perf', - 'media.media_scrub_perf', - 'media.media_seek_perf', - 'media.media_stat_perf', - 'media.mixed_audio_latency_perf', - ], - }, - - # Trace event tests. - 'TRACING': { - 'all': [ - 'tracing.tracing_smoke_test', - ], - }, - - # Chromoting tests. - 'CHROMOTING': { - 'all': [ - 'chromoting.auth', - 'chromoting.it2me_basic', - 'chromoting.me2me_connect', - 'chromoting.me2me_enable', - ], - 'linux': [ - # TODO(yihongg): Me2me test against Linux is not working yet. - '-chromoting.me2me_connect', - '-chromoting.me2me_enable', - ], - 'chromeos': [ - # ChromeOS doesn't yet support the chromoting host. - '-chromoting.auth', - '-chromoting.it2me_basic', - '-chromoting.me2me_connect', - '-chromoting.me2me_enable', - ], - }, - - # Pyauto functional tests running on coverage bots. - 'CODE_COVERAGE': { - 'all': [ - '@FULL', - ], - 'linux': [ - # These tests fail on coverage bots. Disabling for now. - '-test_clean_exit', - '-about_plugins_ui', - '-autofill', - '-pyauto_webdriver', - '-extensions', - '-flash', - '-fullscreen_mouselock', - '-gtalk.test_basic', - '-infobars', - '-multiprofile', - '-prefs', - '-prefs_ui', - '-apptest', - '-plugins', - '-omnibox', - '-special_tabs', - '-ntp.NTPTest.testLaunchAppFullScreen', - '-media.media_basic_playback.MediaBasicPlaybackTest.testBasicPlaybackMatrix', - '-search_engines.SearchEnginesTest.testDiscoverSearchEngine', - '-ntp.NTPTest.testUninstallApp', - '-ntp.NTPTest.testLaunchAppWithDefaultSettings', - '-ntp.NTPTest.testLaunchAppRegularTab', - '-ntp.NTPTest.testLaunchAppPinnedTab', - '-ntp.NTPTest.testGetAppsWhenInstallApp', - '-ntp.NTPTest.testLaunchAppNewWindow', - ], - }, -} diff --git a/chrome/test/functional/about_plugins_ui.py b/chrome/test/functional/about_plugins_ui.py deleted file mode 100755 index 0ebde00..0000000 --- a/chrome/test/functional/about_plugins_ui.py +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import os -import re - -import pyauto_functional # Must be imported before pyauto -import pyauto -import pyauto_utils - - -class AboutPluginsUITest(pyauto.PyUITest): - """Testcase for chrome://plugins UI.""" - - def testAboutPluginDetailInfo(self): - """Verify chrome://plugins page shows plugin details.""" - self.NavigateToURL('chrome://plugins/') - driver = self.NewWebDriver() - detail_link = driver.find_element_by_id('details-link') - self.assertTrue(self.WaitUntil(lambda: detail_link.is_displayed()), - msg='Details link could not be found.') - detail_link.click() - # Verify that detail info for Remote Viewer plugin shows up. - # Remote Viewer plugin is expected to be present on all platforms. - self.assertTrue(self.WaitUntil(lambda: len(driver.find_elements_by_xpath( - '//*[@jscontent="path"][text()="internal-remoting-viewer"]')))) - - -class ChromeAboutPluginsUITest(pyauto.PyUITest): - """Testcase for official build only plugins in chrome://plugins UI.""" - - def Debug(self): - """chrome://plugins test debug method. - - This method will not run automatically. - """ - self.NavigateToURL('chrome://plugins/') - driver = self.NewWebDriver() - import pdb - pdb.set_trace() - - def _IsEnabled(self, driver, plugin_name): - """Checks if plugin is enabled. - - Args: - driver: A Chrome driver object. - plugin_name: Plugin name to verify. - - Returns: - True, if plugin is enabled, or False otherwise. - """ - check_plugin_enabled_js = 'return navigator.plugins["%s"] != undefined' % \ - plugin_name - return driver.execute_script(check_plugin_enabled_js) - - def _ExpandDetailInfoLink(self, driver): - """Expand detail info link. - - Args: - driver: A Chrome driver object. - """ - detail_link = driver.find_element_by_id('details-link') - self.assertTrue(self.WaitUntil(lambda: detail_link.is_displayed()), - msg='Details link could not be found.') - detail_link.click() - - def _OverridePluginPageAnimation(self, driver): - """Override the animation for expanding detail info to make sure element - remain at the same location where web driver found it. - - Args: - driver: A Chrome driver object. - """ - override_animation_style_js = """ - style = document.createElement('style'); - style.innerHTML = "* { -webkit-transition: all 0s ease-in !important}"; - document.head.appendChild(style); - """ - driver.execute_script(override_animation_style_js) - - def testAboutPluginEnableAndDisablePDFPlugin(self): - """Verify enable and disable pdf plugins from about:plugins page.""" - self.NavigateToURL('chrome://plugins/') - driver = self.NewWebDriver() - - self._OverridePluginPageAnimation(driver) - self._ExpandDetailInfoLink(driver) - - pdf_disable_path = '//*[@class="plugin-name"][text()="Chrome PDF Viewer"' \ - ']//ancestor::*[@class="plugin-text"]//a[text()="Disable"]' - pdf_enable_path = '//*[@class="plugin-name"][text()="Chrome PDF Viewer"' \ - ']//ancestor::*[@class="plugin-text"]//a[text()="Enable"]' - - # Confirm Chrome PDF Viewer plugin is found and find disable PDF link. - pdf_disable_link = pyauto_utils.WaitForDomElement(self, driver, - pdf_disable_path) - - # Disable PDF viewer plugin in about:plugins. - pdf_disable_link.click() - self.assertTrue(self.WaitUntil(lambda: not - self._IsEnabled(driver, 'Chrome PDF Viewer'))) - - # Re-enable PDF viewer plugin. - pdf_enable_link = driver.find_element_by_xpath(pdf_enable_path) - pdf_enable_link.click() - self.assertTrue(self.WaitUntil(lambda: - self._IsEnabled(driver, 'Chrome PDF Viewer'))) - - def testEnableAndDisableFlashPlugin(self): - """Verify enable and disable flash plugins from about:plugins page.""" - self.NavigateToURL('chrome://plugins/') - driver = self.NewWebDriver() - - self._OverridePluginPageAnimation(driver) - self._ExpandDetailInfoLink(driver) - flash_plugins_elem = driver.find_element_by_xpath( - '//*[@jscontent="name"][text()="Flash"]//ancestor' \ - '::*[@class="plugin-text"]') - - # Disable flash plugin from flash detail info. - flash_disable_link = flash_plugins_elem.find_element_by_xpath( - './/a[text()="Disable"]') - flash_disable_link.click() - self.assertTrue(self.WaitUntil(lambda: not - self._IsEnabled(driver, 'Shockwave Flash'))) - - # Re-enable Flash plugin from flash detail info. - flash_enable_link = flash_plugins_elem.find_element_by_xpath( - './/a[text()="Enable"]') - flash_enable_link.click() - self.assertTrue(self.WaitUntil(lambda: - self._IsEnabled(driver, 'Shockwave Flash'))) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/ap_lab/ap_configurator.py b/chrome/test/functional/ap_lab/ap_configurator.py deleted file mode 100644 index b356028..0000000 --- a/chrome/test/functional/ap_lab/ap_configurator.py +++ /dev/null @@ -1,355 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import copy -import logging -import os - -import pyauto_ap_configurator -import pyauto - -import selenium.common.exceptions -from selenium.webdriver.support.ui import WebDriverWait - - -class APConfigurator(object): - """Base class for objects to configure access points using webdriver.""" - - def __init__(self, pyauto_instance): - self.pyauto_instance = pyauto_instance - self._driver = pyauto_instance.NewWebDriver() - # Any call to wait.until() will raise an exception if the timeout is hit. - self._wait = WebDriverWait(self._driver, timeout=5) - - # Possible bands - self.band_2ghz = '2.4GHz' - self.band_5ghz = '5GHz' - - # Possible modes - self.mode_a = 0x0001 - self.mode_b = 0x0010 - self.mode_g = 0x0100 - self.mode_n = 0x1000 - - # Possible security settings - self.security_disabled = 'Disabled' - self.security_wep = 'WEP' - self.security_wpawpsk = 'WPA-Personal' - self.security_wpa2wpsk = 'WPA2-Personal' - self.security_wpa8021x = 'WPA-Enterprise' - self.security_wpa28021x = 'WPA2-Enterprise' - - self.wep_authentication_open = 'Open' - self.wep_authentication_shared = 'Shared Key' - - self._command_list = [] - - def _WaitForObjectByXPath(self, xpath): - """Waits for an object to appear.""" - try: - self._wait.until(lambda _: self._driver.find_element_by_xpath(xpath)) - except selenium.common.exceptions.TimeoutException, e: - logging.exception('Unable to find the wait for object by xpath: %s\n' - 'WebDriver exception: %s', xpath, str(e)) - - def SelectItemFromPopupByID(self, item, element_id, wait_for_xpath=None): - """Selects an item from a popup, by passing the element ID. - - Args: - item: the item to select from the popup - element_id: the html ID of the item - wait_for_xpath: an item to wait for before returning - """ - xpath = 'id("%s")' % element_id - self.SelectItemFromPopupByXPath(item, xpath, wait_for_xpath) - - def SelectItemFromPopupByXPath(self, item, xpath, wait_for_xpath=None): - """Selects an item from a popup, by passing the xpath of the popup. - - Args: - item: the item to select from the popup - xpath: the xpath of the popup - wait_for_xpath: an item to wait for before returning - """ - popup = self._driver.find_element_by_xpath(xpath) - for option in popup.find_elements_by_tag_name('option'): - if option.text == item: - option.click() - break - if wait_for_xpath: self._WaitForObjectByXPath(wait_for_xpath) - - def SetContentOfTextFieldByID(self, content, text_field_id, - wait_for_xpath=None): - """Sets the content of a textfield, by passing the element ID. - - Args: - content: the content to apply to the textfield - text_field_id: the html ID of the textfield - wait_for_xpath: an item to wait for before returning - """ - xpath = 'id("%s")' % text_field_id - self.SetConentsOfTextFieldByXPath(content, xpath, wait_for_xpath) - - def SetConentsOfTextFieldByXPath(self, content, xpath, wait_for_xpath=None): - """Sets the content of a textfield, by passing the xpath. - - Args: - content: the content to apply to the textfield - xpath: the xpath of the textfield - wait_for_xpath: an item to wait for before returning - """ - # When we can get the value we know the text field is ready. - text_field = self._driver.find_element_by_xpath(xpath) - try: - self._wait.until(lambda _: text_field.get_attribute('value')) - except selenium.common.exceptions.TimeoutException, e: - logging.exception('Unable to obtain the value of the text field %s.\n' - 'WebDriver exception: %s', wait_for_xpath, str(e)) - text_field = self._driver.find_element_by_xpath(xpath) - text_field.clear() - text_field.send_keys(content) - if wait_for_xpath: self._WaitForObjectByXPath(wait_for_xpath) - - def SetCheckBoxSelectedByID(self, check_box_id, selected=True, - wait_for_xpath=None): - """Sets the state of a checkbox, by passing the ID. - - Args: - check_box_id: the html id of the checkbox - selected: True to enable the checkbox; False otherwise - wait_for_xpath: an item to wait for before returning - """ - xpath = 'id("%s")' % check_box_id - self.SetCheckBoxSelectedByXPath(xpath, selected, wait_for_xpath) - - def SetCheckBoxSelectedByXPath(self, xpath, selected=True, - wait_for_xpath=None): - """Sets the state of a checkbox, by passing the xpath. - - Args: - xpath: the xpath of the checkbox - selected: True to enable the checkbox; False otherwise - wait_for_xpath: an item to wait for before returning - """ - check_box = self._driver.find_element_by_xpath(xpath) - value = check_box.get_attribute('value') - if (value == '1' and not selected) or (value == '0' and selected): - check_box.click() - if wait_for_xpath: self._WaitForObjectByXPath(wait_for_xpath) - - def AddItemToCommandList(self, method, args, page, priority): - """Adds commands to be executed against the AP web UI. - - Args: - method: the method to run - args: the arguments for the method you want executed - page: the page on the web ui where the method should be run against - priority: the priority of the method - """ - self._command_list.append({'method': method, - 'args': copy.copy(args), - 'page': page, - 'priority': priority}) - - def GetRouterName(self): - """Returns a string to describe the router. - - Note: The derived class must implement this method. - """ - raise NotImplementedError - - def GetRouterShortName(self): - """Returns a short string to describe the router. - - Note: The derived class must implement this method. - """ - raise NotImplementedError - - def GetNumberOfPages(self): - """Returns the number of web pages used to configure the router. - - Note: This is used internally by applySettings, and this method must be - implemented by the derived class. - """ - raise NotImplementedError - - def GetSupportedBands(self): - """Returns a list of dictionaries describing the supported bands. - - Example: returned is a dictionary of band and a list of channels. The band - object returned must be one of those defined in the __init___ of - this class. - - supported_bands = [{'band' : self.band_2GHz, - 'channels' : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]}, - {'band' : self.band_5ghz, - 'channels' : [26, 40, 44, 48, 149, 153, 157, 161, 165]}] - - Returns: - A list of dictionaries as described above - - Note: The derived class must implement this method. - """ - raise NotImplementedError - - def GetSupportedModes(self): - """Returns a list of dictionaries describing the supported modes. - - Example: returned is a dictionary of band and a list of modess. The band - and modes objects returned must be one of those defined in the - __init___ of this class. - - supported_modes = [{'band' : self.band_2GHz, - 'modes' : [mode_b, mode_b | mode_g]}, - {'band' : self.band_5ghz, - 'modes' : [mode_a, mode_n, mode_a | mode_n]}] - - Returns: - A list of dictionaries as described above - - Note: The derived class must implement this method. - """ - raise NotImplementedError - - def NavigateToPage(self, page_number): - """Navigates to the page corresponding to the given page number. - - This method performs the translation between a page number and a url to - load. This is used internally by applySettings. - - Args: - page_number: Page number of the page to load - - Returns: - True if navigation is successful; False otherwise. - - Note: The derived class must implement this method. - """ - raise NotImplementedError - - def SavePage(self, page_number): - """Saves the given page. - - Args: - page_number: Page number of the page to save. - - Returns: - True if navigation is successful; False otherwise. - - Note: The derived class must implement this method. - """ - raise NotImplementedError - - def SetMode(self, mode, band=None): - """Sets the mode. - - Args: - mode: must be one of the modes listed in __init__() - band: the band to select - - Note: The derived class must implement this method - """ - raise NotImplementedError - - def SetRadio(self, enabled=True): - """Turns the radio on and off. - - Args: - enabled: True to turn on the radio; False otherwise - - Note: The derived class must implement this method. - """ - raise NotImplementedError - - def SetSSID(self, ssid): - """Sets the SSID of the wireless network. - - Args: - ssid: Name of the wireless network - - Note: The derived class must implement this method. - """ - raise NotImplementedError - - def SetChannel(self, channel): - """Sets the channel of the wireless network. - - Args: - channel: Integer value of the channel - - Note: The derived class must implement this method. - """ - raise NotImplementedError - - def SetBand(self, band): - """Sets the band of the wireless network. - - Currently there are only two possible values for band 2kGHz and 5kGHz. - - Args: - band: Constant describing the band type - - Note: The derived class must implement this method. - """ - raise NotImplementedError - - def SetSecurityDisabled(self): - """Disables the security of the wireless network. - - Note: The derived class must implement this method. - """ - raise NotImplementedError - - def SetSecurityWEP(self, key_value, authentication): - """Enabled WEP security for the wireless network. - - Args: - key_value: encryption key to use - authentication: one of two supported authentication types: - wep_authentication_open or wep_authentication_shared - - Note: The derived class must implement this method. - """ - raise NotImplementedError - - def SetSecurityWPAPSK(self, shared_key, update_interval=1800): - """Enabled WPA using a private security key for the wireless network. - - Args: - shared_key: shared encryption key to use - update_interval: number of seconds to wait before updating - - Note: The derived class must implement this method. - """ - raise NotImplementedError - - def SetVisibility(self, visible=True): - """Set the visibility of the wireless network. - - Args: - visible: True for visible; False otherwise - - Note: The derived class must implement this method. - """ - raise NotImplementedError - - def ApplySettings(self): - """Apply all settings to the access point.""" - # Pull items by page and then sort - if self.GetNumberOfPages() == -1: - self.fail(msg='Number of pages is not set.') - page_range = range(1, self.GetNumberOfPages() + 1) - for i in page_range: - page_commands = [] - for command in self._command_list: - if command['page'] == i: - page_commands.append(command) - # Sort the commands in this page by priority - sorted_page_commands = sorted(page_commands, key=lambda k: k['priority']) - if sorted_page_commands and self.NavigateToPage(i): - for command in sorted_page_commands: - command['method'](*command['args']) - self.SavePage(i) - self._command_list = [] - diff --git a/chrome/test/functional/ap_lab/ap_configurator_factory.py b/chrome/test/functional/ap_lab/ap_configurator_factory.py deleted file mode 100644 index aa8dcd6..0000000 --- a/chrome/test/functional/ap_lab/ap_configurator_factory.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import os - -import dlink_ap_configurator -import linksys_ap_configurator - -import pyauto_ap_configurator # must preceed pyauto -import pyauto - - -class APConfiguratorFactory(object): - """Class that instantiates all available APConfigurators.""" - - def __init__(self, pyauto_instance, config_dict_file_path=None): - if not config_dict_file_path: - # Load the default dictionary file - config_dict_file_path = os.path.join(pyauto_instance.DataDir(), - 'pyauto_private', 'chromeos', - 'network', 'wifi_compat_config') - assert os.path.exists(config_dict_file_path), ('%s missing' % - config_dict_file_path) - ap_dict = pyauto_instance.EvalDataFrom(config_dict_file_path) - self.ap_list = [] - self.ap_list.append(linksys_ap_configurator.LinksysAPConfigurator( - pyauto_instance, ap_dict['LinksysAPConfigurator'])) - self.ap_list.append(dlink_ap_configurator.DLinkAPConfigurator( - pyauto_instance, ap_dict['DLinkAPConfigurator'])) - - def GetAPConfigurators(self): - return self.ap_list - - def GetAPConfiguratorByShortName(self, name): - for ap in self.ap_list: - if ap.GetRouterShortName() == name: - return ap - return None diff --git a/chrome/test/functional/ap_lab/ap_configurator_test.py b/chrome/test/functional/ap_lab/ap_configurator_test.py deleted file mode 100644 index 92daa90..0000000 --- a/chrome/test/functional/ap_lab/ap_configurator_test.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import unittest - -import ap_configurator_factory -import dlink_ap_configurator -import linksys_ap_configurator - -import pyauto_ap_configurator # must preceed pyauto -import pyauto - - -class ConfiguratorTest(pyauto.PyUITest): - """This test needs to be run against the UI interface. - - The purpose of this test is to act as a basic acceptance test when developing - a new AP configurator class. Use this to make sure all core functionality is - implemented. - - This test does not verify that everything works. - - """ - - def setUp(self): - pyauto.PyUITest.setUp(self) - factory = ap_configurator_factory.APConfiguratorFactory(self) - # Set self.ap to the one you want to test against. - self.ap = factory.GetAPConfiguratorByShortName('DAP-1522') - - def testMakeNoChanges(self): - """Test saving with no changes doesn't throw an error.""" - # Set to a known state. - self.ap.SetRadio(True) - self.ap.ApplySettings() - # Set the same setting again. - self.ap.SetRadio(True) - self.ap.ApplySettings() - - def testRadio(self): - """Test we can adjust the radio setting.""" - self.ap.SetRadio(True) - self.ap.ApplySettings() - self.ap.SetRadio(False) - self.ap.ApplySettings() - - def testChannel(self): - """Test adjusting the channel.""" - self.ap.SetRadio(4) - self.ap.ApplySettings() - - def testVisibility(self): - """Test adjusting the visibility.""" - self.ap.SetVisibility(False) - self.ap.ApplySettings() - self.ap.SetVisibility(True) - self.ap.ApplySettings() - - def testSSID(self): - """Test setting the SSID.""" - self.ap.SetSSID('AP-automated-ssid') - self.ap.ApplySettings() - - def testSecurityWEP(self): - """Test configuring WEP security.""" - self.ap.SetSecurityWEP('45678', self.ap.wep_authentication_open) - self.ap.ApplySettings() - self.ap.SetSecurityWEP('90123', self.ap.wep_authentication_shared) - self.ap.ApplySettings() - - def testPrioritySets(self): - """Test that commands are run in the right priority.""" - self.ap.SetRadio(False) - self.ap.SetVisibility(True) - self.ap.SetSSID('priority_test') - self.ap.ApplySettings() - - def testSecurityAndGeneralSettings(self): - """Test updating settings that are general and security related.""" - self.ap.SetRadio(False) - self.ap.SetVisibility(True) - self.ap.SetSecurityWEP('88888', self.ap.wep_authentication_open) - self.ap.SetSSID('sec&gen_test') - self.ap.ApplySettings() - - def testModes(self): - """Tests switching modes.""" - modes_info = self.ap.GetSupportedModes() - self.assertFalse(not modes_info, - msg='Returned an invalid mode list. Is this method' - ' implemented?') - for band_modes in modes_info: - for mode in band_modes['modes']: - self.ap.SetMode(mode) - self.ap.ApplySettings() - - def testModesWithBand(self): - """Tests switching modes that support adjusting the band.""" - # Check if we support self.kModeN across multiple bands - modes_info = self.ap.GetSupportedModes() - n_bands = [] - for band_modes in modes_info: - if self.ap.mode_n in band_modes['modes']: - n_bands.append(band_modes['band']) - if len(n_bands) > 1: - for n_band in n_bands: - self.ap.SetMode(self.ap.mode_n, band=n_band) - self.ap.ApplySettings() - - def testFastCycleSecurity(self): - """Mini stress for changing security settings rapidly.""" - self.ap.SetRadio(True) - self.ap.SetSecurityWEP('77777', self.ap.wep_authentication_open) - self.ap.SetSecurityDisabled() - self.ap.SetSecurityWPAPSK('qwertyuiolkjhgfsdfg') - self.ap.ApplySettings() - - def testCycleSecurity(self): - """Test switching between different security settings.""" - self.ap.SetRadio(True) - self.ap.SetSecurityWEP('77777', self.ap.wep_authentication_open) - self.ap.ApplySettings() - self.ap.SetSecurityDisabled() - self.ap.ApplySettings() - self.ap.SetSecurityWPAPSK('qwertyuiolkjhgfsdfg') - self.ap.ApplySettings() - - def testActionsWhenRadioDisabled(self): - """Test making changes when the radio is diabled.""" - self.ap.SetRadio(False) - self.ap.ApplySettings() - self.ap.SetSecurityWEP('77777', self.ap.wep_authentication_open) - self.ap.SetRadio(False) - self.ap.ApplySettings() - - -if __name__ == '__main__': - pyauto_ap_configurator.Main() diff --git a/chrome/test/functional/ap_lab/dlink_ap_configurator.py b/chrome/test/functional/ap_lab/dlink_ap_configurator.py deleted file mode 100644 index 12c7d2d..0000000 --- a/chrome/test/functional/ap_lab/dlink_ap_configurator.py +++ /dev/null @@ -1,291 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import os - -import ap_configurator -import selenium.common.exceptions - - -class DLinkAPConfigurator(ap_configurator.APConfigurator): - """Derived class to control the DLink DAP-1522.""" - - def __init__(self, pyauto_instance, admin_interface_url): - super(DLinkAPConfigurator, self).__init__(pyauto_instance) - # Override constants - self.security_disabled = 'Disable Wireless Security (not recommended)' - self.security_wep = 'WEP' - self.security_wpapsk = 'WPA-Personal' - self.security_wpa2psk = 'WPA-Personal' - self.security_wpa8021x = 'WPA-Enterprise' - self.security_wpa28021x = 'WPA2-Enterprise' - - self.admin_interface_url = admin_interface_url - - def _OpenLandingPage(self): - self.pyauto_instance.NavigateToURL('http://%s/index.php' % - self.admin_interface_url) - page_name = os.path.basename(self.pyauto_instance.GetActiveTabURL().spec()) - if page_name == 'login.php' or page_name == 'index.php': - try: - self._wait.until(lambda _: self._driver.find_element_by_xpath( - '//*[@name="login"]')) - except selenium.common.exceptions.TimeoutException, e: - # Maybe we were re-routes to the configuration page - if (os.path.basename(self.pyauto_instance.GetActiveTabURL().spec()) == - 'bsc_wizard.php'): - return - logging.exception('WebDriver exception: %s', str(e)) - login_button = self._driver.find_element_by_xpath('//*[@name="login"]') - login_button.click() - - def _OpenConfigurationPage(self): - self._OpenLandingPage() - if (os.path.basename(self.pyauto_instance.GetActiveTabURL().spec()) != - 'bsc_wizard.php'): - self.fail(msg='Taken to an unknown page %s' % - self.pyauto_instance.GetActiveTabURL().spec()) - - # Else we are being logged in automatically to the landing page - wlan = '//*[@name="wlan_wireless"]' - try: - self._wait.until(lambda _: self._driver.find_element_by_xpath(wlan)) - except selenium.common.exceptions.TimeoutException, e: - logging.exception('WebDriver exception: %s', str(e)) - - wlan_button = self._driver.find_element_by_xpath(wlan) - wlan_button.click() - # Wait for the main configuration page, look for the radio button - try: - self._wait.until(lambda _: self._driver.find_element_by_xpath( - 'id("enable")')) - except selenium.common.exceptions.TimeoutException, e: - logging.exception('Unable to find the radio button on the main landing ' - 'page.\nWebDriver exception: %s', str(e)) - - def GetRouterName(self): - return 'Router Name: DAP-1522; Class: DLinkAPConfigurator' - - def GetRouterShortName(self): - return 'DAP-1522' - - def GetNumberOfPages(self): - return 1 - - def GetSupportedBands(self): - return [{'band': self.band_2ghz, - 'channels': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]}, - {'band': self.band_5ghz, - 'channels': [26, 40, 44, 48, 149, 153, 157, 161, 165]}] - - def GetSupportedModes(self): - return [{'band': self.band_2ghz, - 'modes': [self.mode_b, self.mode_g, self.mode_n, - self.mode_b | self.mode_g, self.mode_g | self.mode_n]}, - {'band': self.band_5ghz, - 'modes': [self.mode_a, self.mode_n, self.mode_a | self.mode_n]}] - - def NavigateToPage(self, page_number): - # All settings are on the same page, so we always open the config page - self._OpenConfigurationPage() - return True - - def SavePage(self, page_number): - # All settings are on the same page, we can ignore page_number - button = self._driver.find_element_by_xpath('//input[@name="apply"]') - button.click() - # If we did not make changes so we are sent to the continue screen. - continue_screen = True - button_xpath = '//input[@name="bt"]' - try: - self._wait.until(lambda _: - self._driver.find_element_by_xpath(button_xpath)) - except selenium.common.exceptions.TimeoutException, e: - continue_screen = False - if continue_screen: - button = self._driver.find_element_by_xpath(button_xpath) - button.click() - # We will be returned to the landing page when complete - try: - self._wait.until(lambda _: - self._driver.find_element_by_xpath('id("enable")')) - except selenium.common.exceptions.TimeoutException, e: - logging.exception('Unable to find the radio button on the main landing ' - 'page.\nWebDriver exception: %s', str(e)) - return False - return True - - def SetMode(self, mode, band=None): - # Mode overrides the band. So if a band change is made after a mode change - # it may make an incompatible pairing. - self.AddItemToCommandList(self._SetMode, (mode, band), 1, 800) - - def _SetMode(self, mode, band=None): - # Create the mode to popup item mapping - mode_mapping = {self.mode_b: '802.11b Only', self.mode_g: '802.11g Only', - self.mode_n: '802.11n Only', - self.mode_b | self.mode_g: 'Mixed 802.11g and 802.11b', - self.mode_n | self.mode_g: 'Mixed 802.11n and 802.11g', - self.mode_n | self.mode_g | self.mode_b: - 'Mixed 802.11n, 802.11g, and 802.11b', - self.mode_n | self.mode_g | self.mode_b: - 'Mixed 802.11n, 802.11g, and 802.11b', - self.mode_a: '802.11a Only', - self.mode_n | self.mode_a: 'Mixed 802.11n and 802.11a'} - band_value = self.band_2ghz - if mode in mode_mapping.keys(): - popup_value = mode_mapping[mode] - # If the mode contains 802.11a we use 5Ghz - if mode & self.mode_a == self.mode_a: - band_value = self.band_5ghz - # If the mode is 802.11n mixed with 802.11a it must be 5Ghz - elif mode & (self.mode_n | self.mode_a) == (self.mode_n | self.mode_a): - band_value = self.band_5ghz - # If the mode is 802.11n mixed with something other than 802.11a its 2Ghz - elif mode & self.mode_n == self.mode_n and mode ^ self.mode_n > 0: - band_value = self.band_2ghz - # If the mode is 802.11n then we default to 5Ghz unless there is a band - elif mode == self.mode_n: - band_value = self.band_5ghz - if band: - band_value = band - else: - logging.exception('The mode selected %d is not supported by router %s.', - hex(mode), self.getRouterName()) - # Set the band first - self._SetBand(band_value) - popup_id = 'mode_80211_11g' - if band_value == self.band_5ghz: - popup_id = 'mode_80211_11a' - self.SelectItemFromPopupByID(popup_value, popup_id) - - def SetRadio(self, enabled=True): - # If we are enabling we are activating all other UI components, do it first. - # Otherwise we are turning everything off so do it last. - if enabled: - weight = 1 - else: - weight = 1000 - # This disables all UI so it should be the last item to be changed - self.AddItemToCommandList(self._SetRadio, (enabled,), 1, weight) - - def _SetRadio(self, enabled=True): - # The radio checkbox for this router always has a value of 1. So we need to - # use other methods to determine if the radio is on or not. Check if the - # ssid textfield is disabled. - ssid = self._driver.find_element_by_xpath('//input[@name="ssid"]') - if ssid.get_attribute('disabled') == 'true': - radio_enabled = False - else: - radio_enabled = True - if radio_enabled == enabled: - # Nothing to do - return - self.SetCheckBoxSelectedByID('enable', selected=False, - wait_for_xpath='id("security_type_ap")') - - def SetSSID(self, ssid): - # Can be done as long as it is enabled - self.AddItemToCommandList(self._SetSSID, (ssid,), 1, 900) - - def _SetSSID(self, ssid): - self._SetRadio(enabled=True) - self.SetContentOfTextFieldByID(ssid, 'ssid') - - def SetChannel(self, channel): - self.AddItemToCommandList(self._SetChannel, (channel,), 1, 900) - - def _SetChannel(self, channel): - self._SetRadio(enabled=True) - self.SetCheckBoxSelectedByID('autochann', selected=False) - self.SelectItemFromPopupByID(str(channel), 'channel_g') - - # Experimental - def GetBand(self): - # The radio buttons do more than run a script that adjusts the possible - # channels. We will just check the channel to popup. - self.setRadioSetting(enabled=True) - xpath = ('id("channel_g")') - self._OpenConfigurationPage() - try: - self._wait.until(lambda _: self._driver.find_element_by_xpath(xpath)) - except selenium.common.exceptions.TimeoutException, e: - logging.exception('WebDriver exception: %s', str(e)) - element = self._driver.find_element_by_xpath(xpath) - if element.find_elements_by_tag_name('option')[0].text == '1': - return self.band_2ghz - return self.band_5ghz - - def SetBand(self, band): - if band != self.band_2GHz or band != self.band_5ghz: - self.fail(msg='Invalid band sent %s' % band) - self.AddItemToCommandList(self._SetBand, (band,), 1, 900) - - def _SetBand(self, band): - self._SetRadio(enabled=True) - if band == self.band_2ghz: - int_value = 0 - wait_for_xpath = 'id("mode_80211_11g")' - elif band == self.band_5ghz: - int_value = 1 - wait_for_xpath = 'id("mode_80211_11a")' - xpath = ('//*[contains(@class, "l_tb")]/input[@value="%d" and @name="band"]' - % int_value) - element = self._driver.find_element_by_xpath(xpath) - element.click() - try: - self._wait.until(lambda _: - self._driver.find_element_by_xpath(wait_for_xpath)) - except selenium.common.exceptions.TimeoutException, e: - logging.exception('The appropriate mode popup could not be found after ' - 'adjusting the band. WebDriver exception: %s', str(e)) - - def SetSecurityDisabled(self): - self.AddItemToCommandList(self._SetSecurityDisabled, (), 1, 900) - - def _SetSecurityDisabled(self): - self._SetRadio(enabled=True) - self.SelectItemFromPopupByID(self.security_disabled, 'security_type_ap') - - def SetSecurityWEP(self, key_value, authentication): - self.AddItemToCommandList(self._SetSecurityWEP, (key_value, authentication), - 1, 900) - - def _SetSecurityWEP(self, key_value, authentication): - self._SetRadio(enabled=True) - self.SelectItemFromPopupByID(self.security_wep, 'security_type_ap', - wait_for_xpath='id("auth_type")') - self.SelectItemFromPopupByID(authentication, 'auth_type', - wait_for_xpath='id("wep_key_value")') - self.SetContentOfTextFieldByID(key_value, 'wep_key_value') - self.SetContentOfTextFieldByID(key_value, 'verify_wep_key_value') - - def SetSecurityWPAPSK(self, shared_key, update_interval=1800): - self.AddItemToCommandList(self._SetSecurityWPAPSK, - (shared_key, update_interval), 1, 900) - - def _SetSecurityWPAPSK(self, shared_key, update_interval=1800): - self._SetRadio(enabled=True) - self.SelectItemFromPopupByID(self.security_wpapsk, 'security_type_ap', - wait_for_xpath='id("wpa_mode")') - self.SelectItemFromPopupByID('WPA Only', 'wpa_mode', - wait_for_xpath='id("grp_key_interval")') - self.SetContentOfTextFieldByID(str(update_interval), 'grp_key_interval') - self.SetContentOfTextFieldByID(shared_key, 'wpapsk1') - - def SetVisibility(self, visible=True): - self.AddItemToCommandList(self._SetVisibility, (visible,), 1, 900) - - def _SetVisibility(self, visible=True): - self._SetRadio(enabled=True) - # value=0 is visible; value=1 is invisible - int_value = 0 - if not visible: - int_value = 1 - xpath = ('//*[contains(@class, "l_tb")]/input[@value="%d" ' - 'and @name="visibility_status"]' % int_value) - element = self._driver.find_element_by_xpath(xpath) - element.click() - diff --git a/chrome/test/functional/ap_lab/linksys_ap_configurator.py b/chrome/test/functional/ap_lab/linksys_ap_configurator.py deleted file mode 100644 index bdff34d..0000000 --- a/chrome/test/functional/ap_lab/linksys_ap_configurator.py +++ /dev/null @@ -1,195 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import os - -import ap_configurator -import selenium.common.exceptions - - -class LinksysAPConfigurator(ap_configurator.APConfigurator): - - def __init__(self, pyauto_instance, admin_interface_url): - super(LinksysAPConfigurator, self).__init__(pyauto_instance) - # Override constants - self.security_disabled = 'Disabled' - self.security_wep = 'WEP' - self.security_wpapsk = 'WPA Personal' - self.security_wpa2psk = 'WPA2 Personal' - self.security_wpa8021x = 'WPA Enterprise' - self.security_wpa28021x = 'WPA2 Enterprise' - - self.admin_interface_url = admin_interface_url - - def GetRouterName(self): - return 'Router Name: WRT54G2; Class: LinksysAPConfigurator' - - def GetRouterShortName(self): - return 'WRT54G2' - - def GetNumberOfPages(self): - return 2 - - def GetSupportedBands(self): - return [{'band': self.k2GHz, - 'channels': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]}] - - def GetSupportedModes(self): - return [{'band': self.band_2ghz, - 'modes': [self.mode_b, self.mode_g, self.mode_b | self.mode_g]}] - - def NavigateToPage(self, page_number): - if page_number == 1: - self.pyauto_instance.NavigateToURL('http://%s/wireless.htm' - % self.admin_interface_url) - elif page_number == 2: - self.pyauto_instance.NavigateToURL('http://%s/WSecurity.htm' - % self.admin_interface_url) - else: - logging.exception('Invalid page number passed. Number of pages %d, ' - 'page value sent was %d', self.GetNumberOfPages(), - page_number) - return False - return True - - def SavePage(self, page_number): - try: - self._wait.until(lambda _: - self._driver.find_element_by_xpath('id("divBT1")')) - except selenium.common.exceptions.TimeoutException, e: - logging.exception('Unable to locate the save button.\nWebDriver' - ' exception: %s', str(e)) - return False - button = self._driver.find_element_by_xpath('id("divBT1")') - button.click() - # Wait for the continue button - continue_xpath = '//input[@value="Continue" and @type="button"]' - try: - self._wait.until(lambda _: - self._driver.find_element_by_xpath(continue_xpath)) - except selenium.common.exceptions.TimeoutException, e: - logging.exception('Unable to location the continue button, save probably' - ' failed.\nWebDriver exception: %s', str(e)) - return False - button = self._driver.find_element_by_xpath(continue_xpath) - button.click() - return True - - def SetMode(self, mode, band=None): - self.AddItemToCommandList(self._SetMode, (mode,), 1, 900) - - def _SetMode(self, mode): - # Different bands are not supported so we ignore. - # Create the mode to popup item mapping - mode_mapping = {self.mode_b: 'B-Only', self.mode_g: 'G-Only', - self.mode_b | self.mode_g: 'Mixed'} - mode_name = '' - if mode in mode_mapping.keys(): - mode_name = mode_mapping[mode] - else: - logging.exception('The mode selected %d is not supported by router %s.', - hex(mode), self.getRouterName()) - xpath = ('//select[@onchange="SelWL()" and @name="Mode"]') - self.SelectItemFromPopupByXPath(mode_name, xpath) - - def SetRadio(self, enabled=True): - # If we are enabling we are activating all other UI components, do it - # first. Otherwise we are turning everything off so do it last. - if enabled: - weight = 1 - else: - weight = 1000 - self.AddItemToCommandList(self._SetRadio, (enabled,), 1, weight) - - def _SetRadio(self, enabled=True): - xpath = ('//select[@onchange="SelWL()" and @name="Mode"]') - # To turn off we pick disabled, to turn on we set to G - if not enabled: - setting = 'Disabled' - else: - setting = 'G-Only' - self.SelectItemFromPopupByXPath(setting, xpath) - - def SetSSID(self, ssid): - self.AddItemToCommandList(self._SetSSID, (ssid,), 1, 900) - - def _SetSSID(self, ssid): - self._SetRadio(enabled=True) - xpath = ('//input[@maxlength="32" and @name="SSID"]') - self.SetConentsOfTextFieldByXPath(ssid, xpath) - - def SetChannel(self, channel): - self.AddItemToCommandList(self._SetChannel, (channel,), 1, 900) - - def _SetChannel(self, channel): - self._SetRadio(enabled=True) - channel_choices = ['1 - 2.412GHz', '2 - 2.417GHz', '3 - 2.422GHz', - '4 - 2.427GHz', '5 - 2.432GHz', '6 - 2.437GHz', - '7 - 2.442GHz', '8 - 2.447GHz', '9 - 2.452GHz', - '10 - 2.457GHz', '11 - 2.462GHz'] - xpath = ('//select[@onfocus="check_action(this,0)" and @name="Freq"]') - self.SelectItemFromPopupByXPath(channel_choices[channel - 1], xpath) - - def SetBand(self, band): - return None - - def SetSecurityDisabled(self): - self.AddItemToCommandList(self._SetSecurityDisabled, (), 2, 1000) - - def _SetSecurityDisabled(self): - xpath = ('//select[@name="SecurityMode"]') - self.SelectItemFromPopupByXPath(self.security_disabled, xpath) - - def SetSecurityWEP(self, key_value, authentication): - self.AddItemToCommandList(self._SetSecurityWEP, (key_value, authentication), - 2, 1000) - - def _SetSecurityWEP(self, key_value, authentication): - logging.info('This router %s does not support WEP authentication type: %s', - self.GetRouterName(), authentication) - popup = '//select[@name="SecurityMode"]' - try: - self._wait.until(lambda _: self._driver.find_element_by_xpath(popup)) - except selenium.common.exceptions.TimeoutException, e: - logging.exception('Unable to find the security mode pop up.\nWebDriver ' - ' exception: %s', str(e)) - text_field = ('//input[@name="wl_passphrase"]') - self.SelectItemFromPopupByXPath(self.security_wep, popup, - wait_for_xpath=text_field) - self.SetConentsOfTextFieldByXPath(key_value, text_field) - button = self._driver.find_element_by_xpath('//input[@value="Generate"]') - button.click() - - def SetSecurityWPAPSK(self, shared_key, update_interval=1800): - self.AddItemToCommandList(self._SetSecurityWPAPSK, - (shared_key, update_interval), 1, 900) - - def _SetSecurityWPAPSK(self, shared_key, update_interval=1800): - popup = '//select[@name="SecurityMode"]' - try: - self._wait.until(lambda _: self._driver.find_element_by_xpath(popup)) - except selenium.common.exceptions.TimeoutException, e: - logging.exception('Unable to find the security mode pop up. WebDriver ' - ' exception: %s', str(e)) - key_field = '//input[@name="PassPhrase"]' - self.SelectItemFromPopupByXPath(self.security_wpapsk, popup, - wait_for_xpath=key_field) - self.SetConentsOfTextFieldByXPath(shared_key, key_field) - interval_field = ('//input[@name="GkuInterval"]') - self.SetConentsOfTextFieldByXPath(str(update_interval), interval_field) - - def SetVisibility(self, visible=True): - self.AddItemToCommandList(self._SetVisibility, (visible,), 1, 900) - - def _SetVisibility(self, visible=True): - self._SetRadio(enabled=True) - # value=1 is visible; value=0 is invisible - int_value = 1 - if not visible: - int_value = 0 - xpath = ('//input[@value="%d" and @name="wl_closed"]' % int_value) - element = self._driver.find_element_by_xpath(xpath) - element.click() - diff --git a/chrome/test/functional/ap_lab/pyauto_ap_configurator.py b/chrome/test/functional/ap_lab/pyauto_ap_configurator.py deleted file mode 100644 index 3e99a5517..0000000 --- a/chrome/test/functional/ap_lab/pyauto_ap_configurator.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) 2012 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. - -"""Setup for AP Configurator Pyauto tests.""" - -import os -import sys - - -def _SetupPaths(): - """Setting path to find pyauto_functional.py.""" - ap_configurator_dir = os.path.abspath(os.path.dirname(__file__)) - sys.path.append(ap_configurator_dir) - sys.path.append(os.path.normpath(os.path.join(ap_configurator_dir, - os.pardir))) - -_SetupPaths() - - -from pyauto_functional import Main - - -if __name__ == '__main__': - Main() diff --git a/chrome/test/functional/apptest.py b/chrome/test/functional/apptest.py deleted file mode 100644 index 8a6ef83..0000000 --- a/chrome/test/functional/apptest.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import pyauto_functional # must be imported before pyauto -import pyauto - -class PyAutoEventsTest(pyauto.PyUITest): - """Tests using the event queue.""" - - def testBasicEvents(self): - """Basic test for the event queue.""" - url = self.GetHttpURLForDataPath('apptest', 'basic.html') - driver = self.NewWebDriver() - event_id = self.AddDomEventObserver(automation_id=4444, recurring=True) - success_id = self.AddDomEventObserver('test success', automation_id=4444) - self.NavigateToURL(url) - self._ExpectEvent(event_id, 'init') - self._ExpectEvent(event_id, 'login ready') - driver.find_element_by_id('login').click() - self._ExpectEvent(event_id, 'login start') - self._ExpectEvent(event_id, 'login done') - self.GetNextEvent(success_id) - - def testDomMutationEvents(self): - """Basic tests for WaitForDomNode.""" - url = self.GetHttpURLForDataPath('apptest', 'dom_mutations.html') - self.NavigateToURL(url) - self.WaitForDomNode('id("login")', expected_value='Log In') - self.NewWebDriver().find_element_by_id('login').click() - self.WaitForDomNode('id("console")', expected_value='.*succeeded.*') - - def testDomMutationGenericXPath(self): - """Test mutation observers with a generic xpath and regexp.""" - url = self.GetHttpURLForDataPath('apptest', 'dom_mutations.html') - self.NavigateToURL(url) - self.WaitForDomNode('//a', expected_value='Log In') - self.NewWebDriver().find_element_by_id('login').click() - self.WaitForDomNode('//div', expected_value='.*succeeded.*') - - def testDomMutationObservers(self): - """Tests for the various types of Dom Mutation observers.""" - url = self.GetHttpURLForDataPath('apptest', 'dom_mutations.html') - self.NavigateToURL(url) - self.GetNextEvent(self.AddDomMutationObserver('add', 'id("login")', - expected_value='Log In')) - success_id = self.AddDomMutationObserver('change', 'id("console")', - expected_value='.*succeeded.*') - self.NewWebDriver().find_element_by_id('login').click() - self.GetNextEvent(self.AddDomMutationObserver('remove', 'id("fail")/a')) - self.GetNextEvent(success_id) - - def testWaitUntilNavigationCompletes(self): - """Basic test for WaitUntilNavigationCompletes.""" - url = self.GetHttpURLForDataPath('apptest', 'dom_mutations.html') - js = """window.onunload = - function() { - window.domAutomationController.send("done"); - }; - window.location.href = "%s";""" % url - self.ExecuteJavascript(js) - self.WaitUntilNavigationCompletes() - self.WaitUntilNavigationCompletes() - self.WaitForDomNode('id("login")') - - def _ExpectEvent(self, event_id, expected_event_name): - """Checks that the next event is expected.""" - e = self.GetNextEvent(event_id) - self.assertEqual(e.get('name'), expected_event_name, - msg="unexpected event: %s" % e) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/autofill.py b/chrome/test/functional/autofill.py deleted file mode 100755 index a90b491..0000000 --- a/chrome/test/functional/autofill.py +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import os -import pickle -import re -import simplejson - -import pyauto_functional # Must be imported before pyauto -import pyauto -import test_utils -from selenium.webdriver.common.keys import Keys -from selenium.webdriver.common.action_chains import ActionChains -from webdriver_pages import settings - - -class AutofillTest(pyauto.PyUITest): - """Tests that autofill UI works correctly. Also contains a manual test for - the crowdsourcing server.""" - - def setUp(self): - pyauto.PyUITest.setUp(self) - self._driver = self.NewWebDriver() - - def AutofillCrowdsourcing(self): - """Test able to send POST request of web form to Autofill server. - - The Autofill server processes the data offline, so it can take a few days - for the result to be detectable. Manual verification is required. - """ - # HTML file needs to be run from a specific http:// url to be able to verify - # the results a few days later by visiting the same url. - url = 'http://www.corp.google.com/~dyu/autofill/crowdsourcing-test.html' - # Autofill server captures 2.5% of the data posted. - # Looping 1000 times is a safe minimum to exceed the server's threshold or - # noise. - for i in range(1000): - fname = 'David' - lname = 'Yu' - email = 'david.yu@gmail.com' - # Submit form to collect crowdsourcing data for Autofill. - self.NavigateToURL(url, 0, 0) - profile = {'fn': fname, 'ln': lname, 'em': email} - js = ''.join(['document.getElementById("%s").value = "%s";' % - (key, value) for key, value in profile.iteritems()]) - js += 'document.getElementById("testform").submit();' - self.ExecuteJavascript(js) - - def _SelectOptionXpath(self, value): - """Returns an xpath query used to select an item from a dropdown list. - Args: - value: Option selected for the drop-down list field. - - Returns: - The value of the xpath query. - """ - return '//option[@value="%s"]' % value - - def testPostalCodeAndStateLabelsBasedOnCountry(self): - """Verify postal code and state labels based on selected country.""" - data_file = os.path.join(self.DataDir(), 'autofill', 'functional', - 'state_zip_labels.txt') - test_data = simplejson.loads(open(data_file).read()) - - page = settings.AutofillEditAddressDialog.FromNavigation(self._driver) - # Initial check of State and ZIP labels. - self.assertEqual('State', page.GetStateLabel()) - self.assertEqual('ZIP code', page.GetPostalCodeLabel()) - - for country_code in test_data: - page.Fill(country_code=country_code) - - # Compare postal code labels. - actual_postal_label = page.GetPostalCodeLabel() - self.assertEqual( - test_data[country_code]['postalCodeLabel'], - actual_postal_label, - msg=('Postal code label "%s" does not match Country "%s"' % - (actual_postal_label, country_code))) - - # Compare state labels. - actual_state_label = page.GetStateLabel() - self.assertEqual( - test_data[country_code]['stateLabel'], - actual_state_label, - msg=('State label "%s" does not match Country "%s"' % - (actual_state_label, country_code))) - - def testNoDuplicatePhoneNumsInPrefs(self): - """Test duplicate phone numbers entered in prefs are removed.""" - page = settings.AutofillEditAddressDialog.FromNavigation(self._driver) - non_duplicates = ['111-1111', '222-2222'] - duplicates = ['111-1111'] - page.Fill(phones=non_duplicates + duplicates) - self.assertEqual(non_duplicates, page.GetPhones(), - msg='Duplicate phone number in prefs unexpectedly saved.') - - def testDisplayLineItemForEntriesWithNoCCNum(self): - """Verify Autofill creates a line item for CC entries with no CC number.""" - self.NavigateToURL('chrome://settings-frame/autofillEditCreditCard') - self._driver.find_element_by_id('name-on-card').send_keys('Jane Doe') - query_month = self._SelectOptionXpath('12') - query_year = self._SelectOptionXpath('2014') - self._driver.find_element_by_id('expiration-month').find_element_by_xpath( - query_month).click() - self._driver.find_element_by_id('expiration-year').find_element_by_xpath( - query_year).click() - self._driver.find_element_by_id( - 'autofill-edit-credit-card-apply-button').click() - # Refresh the page to ensure the UI is up-to-date. - self._driver.refresh() - list_entry = self._driver.find_element_by_class_name('autofill-list-item') - self.assertTrue(list_entry.is_displayed) - self.assertEqual('Jane Doe', list_entry.text, - msg='Saved CC line item not same as what was entered.') - - def _GetElementList(self, container_elem, fields_to_select): - """Returns all sub elements of specific characteristics. - - Args: - container_elem: An element that contains other elements. - fields_to_select: A list of fields to select with strings that - help create an xpath string, which in turn identifies - the elements needed. - For example: ['input', 'button'] - ['div[@id]', 'button[@disabled]'] - ['*[class="example"]'] - - Returns: - List of all subelements found in the container element. - """ - self.assertTrue(fields_to_select, msg='No fields specified for selection.') - fields_to_select = ['.//' + field for field in fields_to_select] - xpath_arg = ' | '.join(fields_to_select) - field_elems = container_elem.find_elements_by_xpath(xpath_arg) - return field_elems - - def _GetElementInfo(self, element): - """Returns visual comprehensive info about an element. - - This function identifies the text of the correspoinding label when tab - ordering fails. - This info consists of: - The labels, buttons, ids, placeholder attribute values, or the element id. - - Args: - element: The target element. - - Returns: - A string that identifies the element in the page. - """ - element_info = '' - if element.tag_name == 'button': - element_info = element.text - element_info = (element_info or element.get_attribute('id') or - element.get_attribute('placeholder') or - element.get_attribute('class') or element.id) - return '%s: %s' % (element.tag_name, element_info) - - def _LoadPageAndGetFieldList(self): - """Navigate to autofillEditAddress page and finds the elements with focus. - - These elements are of input, select, and button types. - - Returns: - A list with all elements that can receive focus. - """ - url = 'chrome://settings-frame/autofillEditAddress' - self._driver.get(url) - container_elem = self._driver.find_element_by_id( - 'autofill-edit-address-overlay') - # The container element contains input, select and button fields. Some of - # the buttons are disabled so they are ignored. - field_list = self._GetElementList(container_elem, - ['input', 'select', - 'button[not(@disabled)]']) - self.assertTrue(field_list, 'No fields found in "%s".' % url) - return field_list - - def testTabOrderForEditAddress(self): - """Verify the TAB ordering for Edit Address page is correct.""" - tab_press = ActionChains(self._driver).send_keys(Keys.TAB) - field_list = self._LoadPageAndGetFieldList() - - # Creates a dictionary where a field key returns the value of the next field - # in the field list. The last field of the field list is mapped to the first - # field of the field list. - field_nextfield_dict = dict( - zip(field_list, field_list[1:] + field_list[:1])) - - # Wait until a field of |field_list| has received the focus. - self.WaitUntil(lambda: - self._driver.switch_to_active_element().id in - [f.id for f in field_list]) - # The first field is expected to receive the focus. - self.assertEqual(self._driver.switch_to_active_element().id, - field_list[0].id, - msg='The first field did not receive tab focus.') - for field in field_list: - tab_press.perform() - # Wait until a field of |field_list|, other than the current field, has - # received the focus. - self.WaitUntil(lambda: - self._driver.switch_to_active_element().id != field.id and - self._driver.switch_to_active_element().id in - [f.id for f in field_list]) - - self.assertEqual(self._driver.switch_to_active_element().id, - field_nextfield_dict[field].id, - msg=('The TAB ordering is broken. Previous field: "%s"\n' - 'Field expected to receive focus: "%s"\n' - 'Field that received focus instead: "%s"') - % (self._GetElementInfo(field), - self._GetElementInfo(field_nextfield_dict[field]), - self._GetElementInfo( - self._driver.switch_to_active_element()))) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/chromeos_accessibility.py b/chrome/test/functional/chromeos_accessibility.py deleted file mode 100755 index 0dec404..0000000 --- a/chrome/test/functional/chromeos_accessibility.py +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import os -import subprocess -import sys -import time - -import pyauto_functional -import pyauto - -sys.path.append('/usr/local') # To make autotest libs importable. -from autotest.cros import cros_ui -from autotest.cros import cryptohome - - -class AccessibilityTest(pyauto.PyUITest): - """Tests for Accessibility. - - Test various chromeos functionalities while Accessibility is turned on. - """ - find_test_data_dir = 'find_in_page' - - def ShouldAutoLogin(self): - return False - - def setUp(self): - # We want a clean session_manager instance for every run, - # so restart ui now. - cros_ui.stop(allow_fail=True) - cryptohome.remove_all_vaults() - cros_ui.start(wait_for_login_prompt=False) - pyauto.PyUITest.setUp(self) - - def tearDown(self): - self._DisableSpokenFeedback() - pyauto.PyUITest.tearDown(self) - - def _Login(self): - """Perform login.""" - credentials = self.GetPrivateInfo()['test_google_account'] - self.Login(credentials['username'], credentials['password']) - logging.info('Logging in as %s' % credentials['username']) - login_info = self.GetLoginInfo() - self.assertTrue(login_info['is_logged_in'], msg='Login failed.') - - def _LoginWithSpokenFeedback(self): - self.EnableSpokenFeedback(True) - self._Login() - self.assertTrue(self.IsSpokenFeedbackEnabled(), - msg='Could not enable spoken feedback accessibility mode.') - - def _EnableSpokenFeedback(self): - self.EnableSpokenFeedback(True) - self.assertTrue(self.IsSpokenFeedbackEnabled(), - msg='Could not enable spoken feedback accessibility mode.') - - def _DisableSpokenFeedback(self): - self.EnableSpokenFeedback(False) - self.assertFalse(self.IsSpokenFeedbackEnabled(), - msg='Could not disable spoken feedback accessibility mode.') - - def testCanEnableSpokenFeedback(self): - """Tests that spoken feedback accessibility mode can be enabled.""" - self._EnableSpokenFeedback() - - def testLoginAsGuest(self): - """Test that Guest user login is possible when Accessibility is on.""" - self._EnableSpokenFeedback() - self.LoginAsGuest() - login_info = self.GetLoginInfo() - self.assertTrue(login_info['is_logged_in'], msg='Not logged in at all.') - self.assertTrue(login_info['is_guest'], msg='Not logged in as guest.') - url = self.GetFileURLForDataPath('title1.html') - self.NavigateToURL(url) - self.assertEqual(1, self.FindInPage('title')['match_count'], - msg='Failed to load the page or find the page contents.') - # crbug.com/129218: adding a volume change functionality to automate this - # issue. Please note that we don't verify any functionality here. - default_volume = self.GetVolumeInfo() - for test_volume in (50.00, 77.00, 85.00, 20.00): - self.SetVolume(test_volume) - time.sleep(1) - self.SetVolume(default_volume.get('volume')) - - def testAccessibilityBeforeLogin(self): - """Test Accessibility before login.""" - self._LoginWithSpokenFeedback() - self.Logout() - self.assertFalse(self.GetLoginInfo()['is_logged_in'], - msg='Still logged in when we should be logged out.') - self.assertTrue(self.IsSpokenFeedbackEnabled(), - msg='Spoken feedback accessibility mode disabled after loggin out.') - - def testAccessibilityAfterLogin(self): - """Test Accessibility after login.""" - self._Login() - self._EnableSpokenFeedback() - - def testPagePerformance(self): - """Test Chrome works fine when Accessibility is on.""" - self._LoginWithSpokenFeedback() - # Verify that opened tab page behaves normally when Spoken Feedback is - # enabled. crosbug.com/26731 - url = self.GetFileURLForDataPath(self.find_test_data_dir, 'largepage.html') - self.NavigateToURL(url) - self.assertEqual(373, self.FindInPage('daughter of Prince')['match_count'], - msg='Failed to load the page or find the page contents.') - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/chromeos_basic.py b/chrome/test/functional/chromeos_basic.py deleted file mode 100755 index 1071741..0000000 --- a/chrome/test/functional/chromeos_basic.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import pyauto_functional -import pyauto - - -class ChromeosBasic(pyauto.PyUITest): - """Basic tests for ChromeOS. - - Requires ChromeOS to be logged in. - """ - - def testAppendTabs(self): - """Basic test for primary chrome on ChromeOS (named testing interface).""" - self.AppendTab(pyauto.GURL('about:version')) - self.assertEqual(self.GetTabCount(), 2, msg='Expected 2 tabs') - - def testRestart(self): - """Basic test which involves restarting chrome on ChromeOS.""" - file_url = self.GetFileURLForDataPath('title2.html') - self.NavigateToURL(file_url) - self.assertEqual(1, len(self.GetHistoryInfo().History())) - self.RestartBrowser(clear_profile=False) - self.assertEqual(1, len(self.GetHistoryInfo().History())) - - def testSetDownloadShelfVisible(self): - self.assertFalse(self.IsDownloadShelfVisible()) - self.SetDownloadShelfVisible(True) - self.assertTrue(self.IsDownloadShelfVisible()) - self.SetDownloadShelfVisible(False) - self.assertFalse(self.IsDownloadShelfVisible()) - - def testSetVolume(self): - """Basic test for setting and getting the volume and mute state.""" - volume_info = self.GetVolumeInfo() - for mute_setting in (False, True, False): - self.SetMute(mute_setting) - self.assertEqual(mute_setting, self.GetVolumeInfo()['is_mute']) - for volume_setting in (40, 0, 100, 70): - self.SetVolume(volume_setting) - self.assertEqual(volume_setting, round(self.GetVolumeInfo()['volume'])) - - self.SetVolume(volume_info['volume']) - self.SetMute(volume_info['is_mute']) - self.assertEqual(volume_info, self.GetVolumeInfo()) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/chromeos_battery.py b/chrome/test/functional/chromeos_battery.py deleted file mode 100755 index 1fd613b..0000000 --- a/chrome/test/functional/chromeos_battery.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import os - -import pyauto_functional # Must be imported before pyauto -import pyauto - -from chromeos.power_strip import PowerStrip - -class ChromeosBattery(pyauto.PyUITest): - """Tests ChromeOS Battery Status. - - Preconditions: - 1) Device under test (DUT) is connected to the LAN via an - Ethernet-to-USB adapter plugged into one of its USB ports. - 2) AC power cable is connected to the DUT, and plugged into - the IP controlled Power Switch, outlet #4, located in the lab. - 3) Battery is installed in the DUT, and battery is not fully - discharged. - 4) Tester should have physical access to the power switch. - - Note about time calculation: - When AC power is turned off or on, the battery will take from 2 - to 60 seconds to calculate the time remaining to empty or full. - While calculating, the keys 'battery_time_to_full' and - 'battery_time_to_empty' are absent. - """ - - _BATTERY_CONFIG_FILE = os.path.join(pyauto.PyUITest.DataDir(), - 'pyauto_private', 'chromeos', 'power', - 'battery_testbed_config') - - def setUp(self): - pyauto.PyUITest.setUp(self) - self.InitPowerStrip() - - def tearDown(self): - # Leave AC power ON so battery does not discharge between tests - self._power_strip.PowerOn(self._outlet_battery_full) - pyauto.PyUITest.tearDown(self) - - def InitPowerStrip(self): - assert os.path.exists(ChromeosBattery._BATTERY_CONFIG_FILE), \ - 'Power Strip configuration file does not exist.' - power_config = pyauto.PyUITest.EvalDataFrom( - ChromeosBattery._BATTERY_CONFIG_FILE) - self._power_strip = PowerStrip(power_config['strip_ip']) - self._outlet_battery_full = (power_config['configs'] - ['battery_full'] - ['outlet_id']) - - def WaitUntilBatteryState(self, ac_power_on, time_key): - assert self.WaitUntil(lambda: self.BatteryPowerAndChargeStateAgree( - ac_power_on, time_key), timeout=60, retry_sleep=1), \ - 'Battery charge/discharge time was not calculated.' - return - - def BatteryPowerAndChargeStateAgree(self, ac_power_on, time_key): - battery_status = self.GetBatteryInfo() - return (battery_status.get('line_power_on') == ac_power_on and - time_key in battery_status) - - def testBatteryChargesWhenACisOn(self): - """Calculate battery time to full when AC is ON""" - # Apply AC power to chromebook with battery. - self._power_strip.PowerOn(self._outlet_battery_full) - - # Get info about charging battery - self.WaitUntilBatteryState(True,'battery_time_to_full') - battery_status = self.GetBatteryInfo() - self.assertTrue(battery_status.get('battery_is_present'), - msg='Battery is not present.') - self.assertTrue(battery_status.get('line_power_on'), - msg='Line power is off.') - self.assertTrue(battery_status.get('battery_time_to_full') >= 0, - msg='Battery charge time is negative.') - - def testBatteryDischargesWhenACisOff(self): - """Calculate battery time to empty when AC is OFF""" - self._power_strip.PowerOff(self._outlet_battery_full) - - # Get info about discharging battery - self.WaitUntilBatteryState(False,'battery_time_to_empty') - battery_status = self.GetBatteryInfo() - self.assertTrue(battery_status.get('battery_is_present'), - msg='Battery is not present.') - self.assertFalse(battery_status.get('line_power_on'), - msg='Line power is on.') - self.assertTrue(battery_status.get('battery_time_to_empty') >= 0, - msg='Battery discharge time is negative.') - - def testBatteryTimesAreDifferent(self): - """Time to full and time to empty should be different""" - # Turn AC Power ON - self._power_strip.PowerOn(self._outlet_battery_full) - - # Get charging battery time to full - self.WaitUntilBatteryState(True,'battery_time_to_full') - battery_status = self.GetBatteryInfo() - time_to_full = battery_status.get('battery_time_to_full') - - # Turn AC Power OFF - self._power_strip.PowerOff(self._outlet_battery_full) - - # Get discharging battery time to empty - self.WaitUntilBatteryState(False,'battery_time_to_empty') - battery_status = self.GetBatteryInfo() - time_to_empty = battery_status.get('battery_time_to_empty') - - # Compare charge to discharge time - """Confirm that time to full and time to empty are not - returning the same value, but that the values are - different (e.g., both are not zero).""" - self.assertNotEqual(time_to_full, time_to_empty, - msg='Battery time to full equals time to empty. ' - 'Though very unlikely, this is not impossible. ' - 'If test failed falsely, Kris owes Scott a beer.') - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/chromeos_browser.py b/chrome/test/functional/chromeos_browser.py deleted file mode 100755 index d079a83..0000000 --- a/chrome/test/functional/chromeos_browser.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import pyauto_functional # pyauto_functional must come before pyauto. -import pyauto -import test_utils - - -class ChromeosBrowserTest(pyauto.PyUITest): - - def testCannotCloseLastIncognito(self): - """Verify that last incognito window cannot be closed if it's the - last window""" - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - self.assertTrue(self.GetBrowserInfo()['windows'][1]['incognito'], - msg='Incognito window is not displayed') - - self.CloseBrowserWindow(0) - info = self.GetBrowserInfo()['windows'] - self.assertEqual(1, len(info)) - url = info[0]['tabs'][0]['url'] - self.assertEqual('chrome://newtab/', url, - msg='Unexpected URL: %s' % url) - self.assertTrue(info[0]['incognito'], - msg='Incognito window is not displayed.') - - def testCrashBrowser(self): - """Verify that after broswer crash is recovered, user can still navigate - to other URL.""" - test_utils.CrashBrowser(self) - self.RestartBrowser(clear_profile=False) - url = self.GetHttpURLForDataPath('english_page.html') - self.NavigateToURL(url) - self.assertEqual('This page is in English', self.GetActiveTabTitle()) - - def testFullScreen(self): - """Verify that a browser window can enter and exit full screen mode.""" - self.ApplyAccelerator(pyauto.IDC_FULLSCREEN) - self.assertTrue(self.WaitUntil(lambda: - self.GetBrowserInfo()['windows'][0]['fullscreen']), - msg='Full Screen is not displayed.') - - self.ApplyAccelerator(pyauto.IDC_FULLSCREEN) - self.assertTrue(self.WaitUntil(lambda: not - self.GetBrowserInfo()['windows'][0]['fullscreen']), - msg='Normal screen is not displayed.') - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/chromeos_crosh.py b/chrome/test/functional/chromeos_crosh.py deleted file mode 100755 index 690c1ef..0000000 --- a/chrome/test/functional/chromeos_crosh.py +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import os - -import pyauto_functional # must be imported before pyauto -import pyauto -import test_utils - - -class CroshTest(pyauto.PyUITest): - """Tests for crosh.""" - - def setUp(self): - """Close all windows at startup.""" - pyauto.PyUITest.setUp(self) - for _ in range(self.GetBrowserWindowCount()): - self.CloseBrowserWindow(0) - - def testBasic(self): - """Verify crosh basic flow.""" - test_utils.OpenCroshVerification(self) - - # Verify crosh prompt. - self.WaitForHtermText(text='crosh> ', - msg='Could not find "crosh> " prompt') - self.assertTrue( - self.GetHtermRowsText(start=0, end=2).endswith('crosh> '), - msg='Could not find "crosh> " prompt') - - # Run a crosh command. - self.SendKeysToHterm('help\\n') - self.WaitForHtermText(text='help_advanced', - msg='Could not find "help_advanced" in help output.') - - # Exit crosh and close tab. - self.SendKeysToHterm('exit\\n') - self.WaitForHtermText(text='command crosh completed with exit code 0', - msg='Could not exit crosh.') - - def testAddBookmark(self): - """Test crosh URL can be bookmarked""" - test_utils.OpenCroshVerification(self) - - # Add bookmark. - bookmarks = self.GetBookmarkModel() - bar_id = bookmarks.BookmarkBar()['id'] - name = 'crosh' - url = self.GetActiveTabURL() - count = bookmarks.NodeCount() - self.AddBookmarkURL(bar_id, 0, name, url.spec()) - bookmarks = self.GetBookmarkModel() - node = bookmarks.BookmarkBar()['children'][0] - self.assertEqual(count + 1, bookmarks.NodeCount()) - self.assertEqual(node['type'], 'url') - self.assertEqual(node['name'], name) - self.assertEqual(url.spec(), node['url']) - - def testMultipleWindowCrosh(self): - """Test that crosh can be opened in multiple windows.""" - test_utils.OpenCroshVerification(self) - - for windex in range (1, 4): # 3 new windows - self.OpenNewBrowserWindow(True) - self.OpenCrosh() - self.assertEqual('crosh', self.GetActiveTabTitle()) - - # Verify crosh prompt. - self.WaitForHtermText(text='crosh> ', tab_index=1, windex=windex, - msg='Could not find "crosh> " prompt') - self.assertTrue( - self.GetHtermRowsText(start=0, end=2, tab_index=1, - windex=windex).endswith('crosh> '), - msg='Could not find "crosh> " prompt') - - # Exit crosh. - self.SendKeysToHterm('exit\\n', tab_index=1, windex=windex) - self.WaitForHtermText(text='command crosh completed with exit code 0', - tab_index=1, windex=windex, - msg='Could not exit crosh.') - - def testShell(self): - """Test shell can be opened in crosh.""" - test_utils.OpenCroshVerification(self) - - # Verify crosh prompt. - self.WaitForHtermText(text='crosh> ', - msg='Could not find "crosh> " prompt') - self.assertTrue( - self.GetHtermRowsText(start=0, end=2).endswith('crosh> '), - msg='Could not find "crosh> " prompt') - - # Run a shell command. - self.SendKeysToHterm(r'shell\n') - self.WaitForHtermText(text='chronos@localhost', - msg='Could not find "chronos@localhost" in shell output.') - - def testConnectToAnotherhost(self): - """Test ssh to another host.""" - test_utils.OpenCroshVerification(self) - - # Verify crosh prompt. - self.WaitForHtermText(text='crosh> ', - msg='Could not find "crosh> " prompt') - self.assertTrue( - self.GetHtermRowsText(start=0, end=2).endswith('crosh> '), - msg='Could not find "crosh> " prompt') - - # Ssh to another host: chronos@localhost. - self.SendKeysToHterm(r'ssh chronos@localhost\n') - self.WaitForHtermText(text='Password', - msg='Could not find "Password" in shell output.') - self.SendKeysToHterm(r'test0000\n') - self.WaitForHtermText(text='chronos@localhost', - msg='Could not find "chronos@localhost" in shell output.') - - def testTabSwitching(self): - """Test tab can be switched in crosh.""" - test_utils.OpenCroshVerification(self) - - # Open 6 tabs - for x in xrange(3): - self.AppendTab(self.GetHttpURLForDataPath('title2.html')) - self.assertEqual('Title Of Awesomeness', self.GetActiveTabTitle(), - msg='Unable to navigate to title2.html and ' - 'verify tab title.') - self.OpenCrosh() - self.assertEqual(7, len(self.GetBrowserInfo()['windows'][0]['tabs'])) - - # Select tab 5 - self.ApplyAccelerator(pyauto.IDC_SELECT_TAB_4) - self.assertEqual('crosh', self.GetActiveTabTitle(), - msg='Unable to naviage to crosh.') - - # Run a crosh command. - self.SendKeysToHterm('help\\n', tab_index=4, windex=0) - self.WaitForHtermText(text='help_advanced', tab_index=4, windex=0, - msg='Could not find "help_advanced" in help output.') - - def testLargefileCrosh(self): - """Test large file is displayed in crosh.""" - test_utils.OpenCroshVerification(self) - - # Verify crosh prompt. - self.WaitForHtermText(text='crosh> ', - msg='Could not find "crosh> " prompt') - self.assertTrue( - self.GetHtermRowsText(start=0, end=2).endswith('crosh> '), - msg='Could not find "crosh> " prompt') - - # Login to localhost. - self.SendKeysToHterm(r'ssh chronos@localhost\n') - self.WaitForHtermText(text='Password', - msg='Could not find "Password" in shell output.') - self.SendKeysToHterm(r'test0000\n') - self.WaitForHtermText(text='chronos@localhost', - msg='Could not find "chronos@localhost" in shell output.') - - # Create a file with 140 characters per line, 50000 lines. - bigfn = '/tmp/bigfile.txt' - with open(bigfn, 'w') as file: - file.write(('0' * 140 + '\n') * 50000 + 'complete\n') - - # Cat a large file. - self.SendKeysToHterm(r'cat %s\n' % bigfn) - self.WaitForHtermText(text='complete', - msg='Could not find "complete" in shell output.') - os.remove(bigfn) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/chromeos_longterm_test.py b/chrome/test/functional/chromeos_longterm_test.py deleted file mode 100755 index b25a350..0000000 --- a/chrome/test/functional/chromeos_longterm_test.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import time - -import pyauto_functional -import pyauto -import pyauto_utils -import timer_queue - - -class ChromeOSLongTerm(pyauto.PyUITest): - """Set of long running tests for ChromeOS. - - This class is comprised of several tests that perform long term tests. - """ - - def _ActivateTabWithURL(self, url): - """Activates the window that has the given tab url. - - Args: - url: The url of the tab to find. - - Returns: - An array of the index values of the tab and window. Returns None if the - tab connot be found. - """ - info = self.GetBrowserInfo() - windows = info['windows'] - for window_index, window in enumerate(windows): - tabs = window['tabs'] - for tab_index, tab in enumerate(tabs): - tab['url'] = tab['url'].strip('/') - if tab['url'] == url: - self.ActivateTab(tab_index, window_index) - return [tab_index, window_index] - return None - - def _SetupLongTermWindow(self, long_term_pages): - """Appends a list of tab to the current active window. - - Args: - long_term_pages: The list of urls to open. - """ - for url in long_term_pages: - self.AppendTab(pyauto.GURL(url)) - - def _RefreshLongTermWindow(self, long_term_pages): - """ Refreshes all of the tabs from the given list. - - Args: - long_term_pages: The list of urls to refresh. - """ - for page in long_term_pages: - long_index = self._ActivateTabWithURL(page) - if not long_index: - logging.info('Unable to find page with url: %s.') - else: - self.ActivateTab(long_index[0], long_index[1]) - self.ReloadActiveTab(long_index[1]) - - def _ConfigureNewWindow(self, pages, incognito=False): - """Setups a windows with multiple tabs running. - - This method acts as a state machine. If a window containing a tab with the - url of the first item of pages it closes that window. If that window - cannot be found then a new window with the urls in pages is opened. - - Args: - pages: The list of urls to load. - """ - page_index = self._ActivateTabWithURL(pages[0]) - if not page_index: - # This means the pages do not exist, load them - if incognito: - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - else: - self.OpenNewBrowserWindow(True) - for url in pages: - self.AppendTab(pyauto.GURL(url), self.GetBrowserWindowCount() - 1) - # Cycle through the pages to make sure they render - win = self.GetBrowserInfo()['windows'][self.GetBrowserWindowCount() - 1] - for tab in win['tabs']: - self.ActivateTab(tab['index'], self.GetBrowserWindowCount() - 1) - # Give the plugin time to activate - time.sleep(1.5) - else: - self.CloseBrowserWindow(page_index[1]) - - def testLongTerm(self): - """Main entry point for the long term tests. - - This method will spin in a while loop forever until it encounters a keyboard - interrupt. Other worker methods will be managed by the TimerQueue. - """ - long_term_pages = ['http://news.google.com', 'http://www.engadget.com', - 'http://www.washingtonpost.com'] - - flash_pages = [ - 'http://www.craftymind.com/factory/guimark2/FlashChartingTest.swf', - 'http://www.craftymind.com/factory/guimark2/FlashGamingTest.swf', - 'http://www.craftymind.com/factory/guimark2/FlashTextTest.swf'] - - incognito_pages = ['http://www.msn.com', 'http://www.ebay.com', - 'http://www.bu.edu', 'http://www.youtube.com'] - - start_time = time.time() - self._SetupLongTermWindow(long_term_pages) - timers = timer_queue.TimerQueue() - timers.AddTimer(self._ConfigureNewWindow, 90, args=(flash_pages,)) - timers.AddTimer(self._RefreshLongTermWindow, 30, args=(long_term_pages,)) - timers.AddTimer(self._ConfigureNewWindow, 15, args=(incognito_pages, True)) - timers.start() - try: - while True: - if not timers.is_alive(): - logging.error('Timer queue died, shutting down.') - return - time.sleep(1) - - except KeyboardInterrupt: - # Kill the timers - timers.Stop() - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/chromeos_power.py b/chrome/test/functional/chromeos_power.py deleted file mode 100755 index b14886d..0000000 --- a/chrome/test/functional/chromeos_power.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging - -import pyauto_functional -import pyauto - - -class ChromeosPower(pyauto.PyUITest): - """Tests for ChromeOS power library and daemon.""" - - def testBatteryInfo(self): - """Print some information about the battery.""" - result = self.GetBatteryInfo() - self.assertTrue(result) - logging.debug(result) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/chromeos_prefs.py b/chrome/test/functional/chromeos_prefs.py deleted file mode 100755 index f6b8e64..0000000 --- a/chrome/test/functional/chromeos_prefs.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import os -import sys -import time - -import pyauto_functional # Must be imported before pyauto -import pyauto - -sys.path.append('/usr/local') # Required to import autotest libs -from autotest.cros import constants - - -class ChromeosPrefsTest(pyauto.PyUITest): - """TestCase for ChromeOS Preferences.""" - - # Defined in src/chrome/browser/chromeos/login/user_manager.cc - k_logged_in_users = 'LoggedInUsers' - k_user_images = 'UserImages' - k_image_path_node_name = 'path' - - def testAllUserImage(self): - """Verify changing all available default user images in Change picture.""" - - logged_in_user = constants.CREDENTIALS['$default'][0] - for i in range(19): - image = { - "index": i, - "path": "" - } - user_images = {} - user_images[logged_in_user] = image - self.SetLocalStatePrefs(ChromeosPrefsTest.k_user_images, user_images) - self.RestartBrowser(clear_profile=False) - current_user_images = self.GetLocalStatePrefsInfo().Prefs( - ChromeosPrefsTest.k_user_images) - current_image = current_user_images.get(logged_in_user) - self.assertEqual(image, current_image, - msg='Default user image was not set in preferences.') - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/chromeos_security.py b/chrome/test/functional/chromeos_security.py deleted file mode 100755 index b541451..0000000 --- a/chrome/test/functional/chromeos_security.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import os - -import pyauto_functional -import pyauto - - -class ChromeosSecurity(pyauto.PyUITest): - """Security tests for chrome on ChromeOS. - - Requires ChromeOS to be logged in. - """ - - def ExtraChromeFlags(self): - """Override default list of extra flags typically used with automation. - - See the default flags used with automation in pyauto.py. - Chrome flags for this test should be as close to reality as possible. - """ - return [ - '--homepage=about:blank', - ] - - def testCannotViewLocalFiles(self): - """Verify that local files cannot be accessed from the browser.""" - urls_and_titles = { - 'file:///': 'Index of /', - 'file:///etc/': 'Index of /etc/', - self.GetFileURLForDataPath('title2.html'): 'Title Of Awesomeness', - } - for url, title in urls_and_titles.iteritems(): - self.NavigateToURL(url) - self.assertNotEqual(title, self.GetActiveTabTitle(), - msg='Could access local file %s.' % url) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/chromeos_time.py b/chrome/test/functional/chromeos_time.py deleted file mode 100755 index 4cd42e4..0000000 --- a/chrome/test/functional/chromeos_time.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import sys - -import pyauto_functional # Must be imported before pyauto -import pyauto - -sys.path.append('/usr/local') # To make autotest libs importable. -from autotest.cros import cros_ui -from autotest.cros import ownership - - -class ChromeosTime(pyauto.PyUITest): - """Tests for the ChromeOS status area clock and timezone settings.""" - - def setUp(self): - cros_ui.fake_ownership() - pyauto.PyUITest.setUp(self) - self._initial_timezone = self.GetTimeInfo()['timezone'] - - def tearDown(self): - self.SetTimezone(self._initial_timezone) - pyauto.PyUITest.tearDown(self) - ownership.clear_ownership() - - def testTimeInfo(self): - """Print the the display time, date, and timezone.""" - logging.debug(self.GetTimeInfo()) - - def testSetTimezone(self): - """Sanity test to make sure setting the timezone works.""" - self.SetTimezone('America/Los_Angeles') - pacific_time = self.GetTimeInfo()['display_time'] - self.SetTimezone('America/New_York') - eastern_time = self.GetTimeInfo()['display_time'] - - self.assertNotEqual(pacific_time, eastern_time, - 'Time zone changed but display time did not.') - - def _IsTimezoneEditable(self): - """Check if the timezone is editable. - - It will navigate to the system settings page and verify that the - timezone settings drop down is not disabled. - - Returns: - True, if timezone dropdown is enabled - False, otherwise - """ - self.NavigateToURL('chrome://settings-frame') - ret = self.ExecuteJavascript(""" - var disabled = true; - var timezone = document.getElementById('timezone-select'); - if (timezone) - disabled = timezone.disabled; - domAutomationController.send(disabled.toString()); - """) - return ret == 'false' - - def testTimezoneIsEditable(self): - """Test that the timezone is always editable.""" - # This test only makes sense if we are not running as the owner. - self.assertFalse(self.GetLoginInfo()['is_owner']) - editable = self._IsTimezoneEditable() - self.assertTrue(editable, msg='Timezone is not editable when not owner.') - - def _SetTimezoneInUI(self, timezone): - self.NavigateToURL('chrome://settings-frame/settings') - self.ExecuteJavascript(""" - var selectElement = document.getElementById('timezone-select'); - selectElement.value = "%s"; - var event = document.createEvent("HTMLEvents"); - event.initEvent("change", true, true); - selectElement.dispatchEvent(event); - domAutomationController.send(""); - """ % timezone) - - def testSetTimezoneUI(self): - """Test that the timezone UI changes internal settings. - - Set the Timezone on the settings page. Check the internal timezone - afterwards. Timezones should be always editable.""" - - for timezone in ['America/Barbados', 'Europe/Helsinki']: - self._SetTimezoneInUI(timezone) - self.assertTrue(self.WaitUntil(lambda: self.GetTimeInfo()['timezone'], - expect_retval=timezone), - 'Timezone not changed as expected.'); - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/chromeos_volume.py b/chrome/test/functional/chromeos_volume.py deleted file mode 100755 index 52b8f0e..0000000 --- a/chrome/test/functional/chromeos_volume.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import os -import subprocess -import sys - -import pyauto_functional # Must be imported before pyauto -import pyauto - -sys.path.append('/usr/local') # To make autotest libs importable. -from autotest.cros import cros_ui -from autotest.cros import cryptohome - - -class ChromeosVolume(pyauto.PyUITest): - """Test case for volume levels. - - Test volume and mute changes with different state like, login, - lock, logout, etc... - """ - - def setUp(self): - # We want a clean session_manager instance for every run, - # so restart ui now. - cros_ui.stop(allow_fail=True) - cryptohome.remove_all_vaults() - cros_ui.start(wait_for_login_prompt=False) - pyauto.PyUITest.setUp(self) - self._initial_volume_info = self.GetVolumeInfo() - - def tearDown(self): - self.SetVolume(self._initial_volume_info['volume']) - self.SetMute(self._initial_volume_info['is_mute']) - pyauto.PyUITest.tearDown(self) - - def ShouldAutoLogin(self): - return False - - def _Login(self): - """Perform login""" - credentials = self.GetPrivateInfo()['test_google_account'] - self.Login(credentials['username'], credentials['password']) - logging.info('Logged in as %s' % credentials['username']) - login_info = self.GetLoginInfo() - self.assertTrue(login_info['is_logged_in'], msg='Login failed.') - - def testDefaultVolume(self): - """Test the default volume settings""" - self._Login() - board_name = self.ChromeOSBoard() - default_volume = self.GetPrivateInfo()['default_volume'] - assert default_volume.get(board_name), \ - 'No volume settings available for %s.' % board_name - expected = {u'volume': default_volume[board_name], - u'is_mute': default_volume['is_mute']} - volume = self.GetVolumeInfo() - self.assertEqual(volume.get('is_mute'), expected.get('is_mute')) - self.assertAlmostEqual(volume.get('volume'), expected.get('volume'), - msg='Volume settings are set to %s, not matching with default ' - 'volume settings %s.' % (volume, expected)) - - def testLoginLogoutVolume(self): - """Test that volume settings are preserved after login and logout""" - before_login = self.GetVolumeInfo() - self._Login() - after_login = self.GetVolumeInfo() - self.assertEqual(before_login, after_login, - msg='Before login : %s and after login : %s, volume states are not ' - 'matching' % (before_login, after_login)) - self.Logout() - after_logout = self.GetVolumeInfo() - self.assertEqual(after_login, after_logout, - msg='Before logout : %s and after logout : %s, volume states are not ' - 'matching' % (after_login, after_logout)) - - def testLoginLockoutVolume(self): - """Test that volume changes on the lock screen, are preserved""" - lock_volume = {u'volume': 50.000000000000014, u'is_mute': True} - self._Login() - login_vol = self.GetVolumeInfo() - self.LockScreen() - self.SetVolume(lock_volume['volume']) - self.SetMute(lock_volume['is_mute']) - self.UnlockScreen(self.GetPrivateInfo()['test_google_account']['password']) - after_login = self.GetVolumeInfo() - self.assertEqual(lock_volume, after_login, - msg='Locking screen volume changes are not preserved') - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/chromoting/__init__.py b/chrome/test/functional/chromoting/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/chrome/test/functional/chromoting/__init__.py +++ /dev/null diff --git a/chrome/test/functional/chromoting/auth.py b/chrome/test/functional/chromoting/auth.py deleted file mode 100755 index 283b339..0000000 --- a/chrome/test/functional/chromoting/auth.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Chromoting authentication related test cases.""" - -import chromoting_base -import pyauto - - -class ChromotingAuth(chromoting_base.ChromotingBase): - """Chromoting authentication related test cases.""" - - def setUp(self): - """Set up for auth test.""" - pyauto.PyUITest.setUp(self) - - webapp = self.InstallExtension(self.GetWebappPath()) - self.host.LaunchApp(webapp) - self.account = self.GetPrivateInfo()['test_chromoting_account'] - - def testDenyAllowAccess(self): - """Denies access and then allows access.""" - self.host.ContinueAuth() - self.host.SignIn(self.account['username'], self.account['password']) - self.host.DenyAccess() - self.host.ContinueAuth() - self.host.AllowAccess() - - def testSignOutAndSignBackIn(self): - """Signs out from chromoting and signs back in.""" - self.host.ContinueAuth() - self.host.SignIn(self.account['username'], self.account['password']) - self.host.AllowAccess() - self.host.SignOut() - self.host.ContinueAuth() - self.host.AllowAccess() - - -if __name__ == '__main__': - chromoting_base.Main() diff --git a/chrome/test/functional/chromoting/chromoting_base.py b/chrome/test/functional/chromoting/chromoting_base.py deleted file mode 100644 index b7dafb6..0000000 --- a/chrome/test/functional/chromoting/chromoting_base.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2012 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. - -"""Common imports, setup, etc for chromoting tests.""" - -import os - - -def _SetupPaths(): - """Add chrome/test/functional to sys.path for importing pyauto_functional""" - functional_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - os.sys.path.append(functional_dir) - -_SetupPaths() - - -import pyauto_functional # Must come before chromoting and pyauto. -from pyauto_functional import Main -import pyauto -import chromotinglib - - -class ChromotingBase(chromotinglib.ChromotingMixIn, pyauto.PyUITest): - """Chromoting pyauto test base class. - - The following member variables can be used in the child classes: - client_local: True if the client is on the same machines as host - host: The chromoting host side, instance of ChromotingBase - client: The chromoting client side, intance of ChromotingBase - client_tab_index: The tab index to the chromoting client tab - """ - def __init__(self, methodName): - pyauto.PyUITest.__init__(self, methodName) - - self.client_local = (self.remote == None) - self.host = self - self.client = self if self.client_local else self.remote - self.client_tab_index = 2 if self.client_local else 1 - - def ExtraChromeFlags(self): - """Add extra flags for chromoting testing. - - Add --allow-nacl-socket-api to connect chromoting successfully. - """ - extra_chrome_flags = ['--allow-nacl-socket-api=*'] - return pyauto.PyUITest.ExtraChromeFlags(self) + extra_chrome_flags diff --git a/chrome/test/functional/chromoting/it2me_basic.py b/chrome/test/functional/chromoting/it2me_basic.py deleted file mode 100755 index 2661e52..0000000 --- a/chrome/test/functional/chromoting/it2me_basic.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Basic tests for Chromoting it2me.""" - -import chromoting_base -import pyauto - - -class IT2MeBasic(chromoting_base.ChromotingBase): - """Drives it2me basic test cases.""" - - def setUp(self): - """Set up for it2me basic test.""" - # Disable test on vista and xp until the failure is figured - if self.IsWinVista() or self.IsWinXP(): - return - - pyauto.PyUITest.setUp(self) - - webapp = self.InstallExtension(self.GetWebappPath()) - self.LaunchApp(webapp) - self.Authenticate() - - if self.client_local: - self.client.LaunchApp(webapp) - - def testIT2MeBasic(self): - """Verify that we can start and disconnect a Chromoting it2me session.""" - # Disable test on vista and xp until the failure is figured - if self.IsWinVista() or self.IsWinXP(): - return - - access_code = self.host.Share() - self.assertTrue(access_code, - msg='Host attempted to share, but it failed. ' - 'No access code was found.') - - self.client.Connect(access_code, self.client_tab_index) - - self.host.CancelShare() - self.client.Disconnect(self.client_tab_index) - - -if __name__ == '__main__': - chromoting_base.Main() diff --git a/chrome/test/functional/chromoting/me2me_connect.py b/chrome/test/functional/chromoting/me2me_connect.py deleted file mode 100755 index 5f6b980..0000000 --- a/chrome/test/functional/chromoting/me2me_connect.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Chromoting me2me connect/disconnect related test cases.""" - -import chromoting_base -import pyauto - - -class Me2MeConnect(chromoting_base.ChromotingBase): - """Drives me2me connect test cases.""" - - def setUp(self): - """Set up for me2me connect test.""" - # Disable test on vista and xp until the failure is figured - if self.IsWinVista() or self.IsWinXP(): - return - - pyauto.PyUITest.setUp(self) - - self.InstallHostDaemon() - webapp = self.InstallExtension(self.GetWebappPath()) - self.host.LaunchApp(webapp) - self.host.Authenticate() - self.host.StartMe2Me() - self.host.CleanupHostList() - self.host.EnableConnectionsInstalled() - self.client.LaunchApp(webapp) - - def tearDown(self): - """Mainly uninstalls the host daemon.""" - # Disable test on vista and xp until the failure is figured - if self.IsWinVista() or self.IsWinXP(): - return - - self.host.DisableConnections() - self.UninstallHostDaemon() - - pyauto.PyUITest.tearDown(self) - - - def testMe2MeConnectDisconnectReconnectDisconnect(self): - """Connects, disconnects, reconnects and disconnects""" - # Disable test on vista and xp until the failure is figured - if self.IsWinVista() or self.IsWinXP(): - return - - self.client.ConnectMe2Me('111111', 'IN_SESSION', - self.client_tab_index) - self.client.DisconnectMe2Me(False, self.client_tab_index) - self.client.ReconnectMe2Me('111111', self.client_tab_index) - self.client.DisconnectMe2Me(True, self.client_tab_index) - - def testMe2MeConnectWithWrongPin(self): - """Connects and disconnects.""" - # Disable test on vista and xp until the failure is figured - if self.IsWinVista() or self.IsWinXP(): - return - - self.client.ConnectMe2Me('222222', 'CLIENT_CONNECT_FAILED_ME2ME', - self.client_tab_index) - self.client.ReconnectMe2Me('111111', self.client_tab_index) - self.client.DisconnectMe2Me(True, self.client_tab_index) - - def testMe2MeChangePin(self): - """Changes pin, connects with new pin and then disconnects.""" - # Disable test on vista and xp until the failure is figured - if self.IsWinVista() or self.IsWinXP(): - return - - self.host.ChangePin('222222') - self.client.ConnectMe2Me('222222', 'IN_SESSION', - self.client_tab_index) - self.client.DisconnectMe2Me(True, self.client_tab_index) - - def testMe2MeChangeName(self): - """Changes host name, connects and then disconnects.""" - # Disable test on vista and xp until the failure is figured - if self.IsWinVista() or self.IsWinXP(): - return - - self.client.ChangeName("Changed") - self.client.ConnectMe2Me('111111', 'IN_SESSION', - self.client_tab_index) - self.client.DisconnectMe2Me(True, self.client_tab_index) - - -if __name__ == '__main__': - chromoting_base.Main() diff --git a/chrome/test/functional/chromoting/me2me_enable.py b/chrome/test/functional/chromoting/me2me_enable.py deleted file mode 100755 index 283f8f6..0000000 --- a/chrome/test/functional/chromoting/me2me_enable.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Chromoting me2me enable/disable related test cases.""" - -import chromoting_base -import pyauto - - -class Me2MeEnable(chromoting_base.ChromotingBase): - """Drives the me2me enable test cases.""" - - def setUp(self): - """Set up for me2me enable test.""" - # Disable test on vista and xp until the failure is figured - if self.IsWinVista() or self.IsWinXP(): - return - - pyauto.PyUITest.setUp(self) - - self.InstallHostDaemon() - webapp = self.InstallExtension(self.GetWebappPath()) - self.host.LaunchApp(webapp) - self.host.Authenticate() - self.host.StartMe2Me() - - def tearDown(self): - """Mainly uninstalls the host daemon.""" - # Disable test on vista and xp until the failure is figured - if self.IsWinVista() or self.IsWinXP(): - return - - self.UninstallHostDaemon() - - pyauto.PyUITest.tearDown(self) - - def testMe2MeEnableDisable(self): - """Enables/disables remote connections. - - This test also exercises different pin conditions. - """ - # Disable test on vista and xp until the failure is figured - if self.IsWinVista() or self.IsWinXP(): - return - - self.host.EnableConnectionsInstalled(True) - self.host.DisableConnections() - - -if __name__ == '__main__': - chromoting_base.Main() diff --git a/chrome/test/functional/codesign.py b/chrome/test/functional/codesign.py deleted file mode 100755 index e942a4b..0000000 --- a/chrome/test/functional/codesign.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import commands -import glob -import logging -import os -import sys -import unittest - -import pyauto_functional # Must import before pyauto -import pyauto - -class CodesignTest(pyauto.PyUITest): - """Test if the build is code signed""" - - def testCodeSign(self): - """Check the app for codesign and bail out if it's non-branded.""" - browser_info = self.GetBrowserInfo() - - # bail out if not a branded build - if browser_info['properties']['branding'] != 'Google Chrome': - return - - # TODO: Add functionality for other operating systems (see crbug.com/47902) - if self.IsMac(): - self._MacCodeSign(browser_info) - - def _MacCodeSign(self, browser_info): - valid_text = 'valid on disk' - app_name = 'Google Chrome.app' - - # Codesign of the app @ xcodebuild/Release/Google Chrome.app/ - app_path = browser_info['child_process_path'] - app_path = app_path[:app_path.find(app_name)] - app_path = app_path + app_name - self.assertTrue(valid_text in self._checkCodeSign(app_path)) - - # Codesign of the frameWork - framework_path = glob.glob(os.path.join(app_path, 'Contents', 'Versions', - '*.*.*.*'))[0] - framework_path = os.path.join(framework_path, - 'Google Chrome Framework.framework') - self.assertTrue(valid_text in self._checkCodeSign(framework_path)) - - def _checkCodeSign(self, file_path): - """Return the output of the codesign""" - return commands.getoutput('codesign -vvv "%s"' % file_path) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/crash_reporter.py b/chrome/test/functional/crash_reporter.py deleted file mode 100755 index 0e58bd1..0000000 --- a/chrome/test/functional/crash_reporter.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import glob -import os - -import pyauto_functional # Must be imported before pyauto -import pyauto -import pyauto_utils - - -class CrashReporterTest(pyauto.PyUITest): - """TestCase for Crash Reporter.""" - - def testRendererCrash(self): - """Verify renderer's crash reporting. - - Attempts to crash, and then checks that crash dumps get generated. Does - not actually test crash reports on the server. - """ - # Bail out if not a branded build - properties = self.GetBrowserInfo()['properties'] - if properties['branding'] != 'Google Chrome': - return - - # Make sure Chrome minidumps are enabled on Chrome OS - if self.IsChromeOS(): - minidumps_file = '/mnt/stateful_partition/etc/enable_chromium_minidumps' - assert os.path.exists(minidumps_file), 'Chrome minidumps are not enabled.' - - breakpad_folder = properties['DIR_CRASH_DUMPS'] - self.assertTrue(breakpad_folder, 'Cannot figure crash dir') - - unused = pyauto_utils.ExistingPathReplacer(path=breakpad_folder) - # If the temp dir was created as root on chromeos, make sure chronos can - # write to it - if self.IsChromeOS() and os.geteuid() == 0: - os.chown(breakpad_folder, 1000, 1000) - self.NavigateToURL('about:crash') # Trigger renderer crash - dmp_files = glob.glob(os.path.join(breakpad_folder, '*.dmp')) - self.assertEqual(1, len(dmp_files)) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/execute_javascript.py b/chrome/test/functional/execute_javascript.py deleted file mode 100755 index 212ff44..0000000 --- a/chrome/test/functional/execute_javascript.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import os -import sys -import unittest - -import pyauto_functional -from pyauto import PyUITest - - -class ExecuteJavascriptTest(PyUITest): - def _GetExtensionInfoById(self, extensions, id): - for x in extensions: - if x['id'] == id: - return x - return None - - def testExecuteJavascript(self): - self.NavigateToURL(self.GetFileURLForDataPath( - 'frame_dom_access', 'frame_dom_access.html')) - - v = self.ExecuteJavascript('window.domAutomationController.send(' + - 'document.getElementById("myinput").nodeName)') - self.assertEqual(v, 'INPUT') - - def testGetDOMValue(self): - self.NavigateToURL(self.GetFileURLForDataPath( - 'frame_dom_access', 'frame_dom_access.html')) - - v = self.GetDOMValue('document.getElementById("myinput").nodeName') - self.assertEqual(v, 'INPUT') - - def testExecuteJavascriptInExtension(self): - """Test we can inject JavaScript into an extension.""" - dir_path = os.path.abspath( - os.path.join(self.DataDir(), 'extensions', 'js_injection_background')) - ext_id = self.InstallExtension(dir_path) - - # Verify extension is enabled. - extension = self._GetExtensionInfoById(self.GetExtensionsInfo(), ext_id) - self.assertTrue(extension['is_enabled'], - msg='Extension was disabled by default') - - # Get the background page's view. - background_view = self.WaitUntilExtensionViewLoaded( - view_type='EXTENSION_BACKGROUND_PAGE') - self.assertTrue(background_view, - msg='problematic background view: views = %s.' % - self.GetBrowserInfo()['extension_views']) - - # Get values from background page's DOM - v = self.ExecuteJavascriptInRenderView( - 'window.domAutomationController.send(' - 'document.getElementById("myinput").nodeName)', background_view) - self.assertEqual(v, 'INPUT', - msg='Incorrect value returned (v = %s).' % v) - v = self.ExecuteJavascriptInRenderView( - 'window.domAutomationController.send(bool_var)', background_view) - self.assertEqual(v, True, msg='Incorrect value returned (v = %s).' % v) - v = self.ExecuteJavascriptInRenderView( - 'window.domAutomationController.send(int_var)', background_view) - self.assertEqual(v, 42, msg='Incorrect value returned (v = %s).' % v) - v = self.ExecuteJavascriptInRenderView( - 'window.domAutomationController.send(str_var)', background_view) - self.assertEqual(v, 'foo', msg='Incorrect value returned (v = %s).' % v) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/extensions.py b/chrome/test/functional/extensions.py deleted file mode 100755 index 62f206f..0000000 --- a/chrome/test/functional/extensions.py +++ /dev/null @@ -1,346 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 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. - -""" -This module is a simple qa tool that installs extensions and tests whether the -browser crashes while visiting a list of urls. - -Usage: python extensions.py -v - -Note: This assumes that there is a directory of extensions called -'extensions-tool' and that there is a file of newline-separated urls to visit -called 'urls.txt' in the data directory. -""" - -import glob -import logging -import os -import sys - -import pyauto_functional # must be imported before pyauto -import pyauto - - -class ExtensionsPage(object): - """Access options in extensions page (chrome://extensions-frame).""" - - _URL = 'chrome://extensions-frame' - - def __init__(self, driver): - self._driver = driver - self._driver.get(ExtensionsPage._URL) - - def CheckExtensionVisible(self, ext_id): - """Returns True if |ext_id| exists on page.""" - return len(self._driver.find_elements_by_id(ext_id)) == 1 - - def SetEnabled(self, ext_id, enabled): - """Clicks on 'Enabled' checkbox for specified extension. - - Args: - ext_id: Extension ID to be enabled or disabled. - enabled: Boolean indicating whether |ext_id| is to be enabled or disabled. - """ - checkbox = self._driver.find_element_by_xpath( - '//*[@id="%s"]//*[@class="enable-controls"]//*[@type="checkbox"]' % - ext_id) - if checkbox != enabled: - checkbox.click() - # Reload page to ensure that the UI is recreated. - self._driver.get(ExtensionsPage._URL) - - def SetAllowInIncognito(self, ext_id, allowed): - """Clicks on 'Allow in incognito' checkbox for specified extension. - - Args: - ext_id: Extension ID to be enabled or disabled. - allowed: Boolean indicating whether |ext_id| is to be allowed or - disallowed in incognito. - """ - checkbox = self._driver.find_element_by_xpath( - '//*[@id="%s"]//*[@class="incognito-control"]//*[@type="checkbox"]' % - ext_id) - if checkbox.is_selected() != allowed: - checkbox.click() - # Reload page to ensure that the UI is recreated. - self._driver.get(ExtensionsPage._URL) - - def SetAllowAccessFileURLs(self, ext_id, allowed): - """Clicks on 'Allow access to file URLs' checkbox for specified extension. - - Args: - ext_id: Extension ID to be enabled or disabled. - allowed: Boolean indicating whether |ext_id| is to be allowed access to - file URLs. - """ - checkbox = self._driver.find_element_by_xpath( - '//*[@id="%s"]//*[@class="file-access-control"]//*[@type="checkbox"]' % - ext_id) - if checkbox.is_selected() != allowed: - checkbox.click() - - -class ExtensionsTest(pyauto.PyUITest): - """Test of extensions.""" - - def Debug(self): - """Test method for experimentation. - - This method is not run automatically. - """ - while True: - raw_input('Interact with the browser and hit <enter> to dump history.') - print '*' * 20 - self.pprint(self.GetExtensionsInfo()) - - def _GetInstalledExtensionIds(self): - return [extension['id'] for extension in self.GetExtensionsInfo()] - - def _ReturnCrashingExtensions(self, extensions, group_size, top_urls): - """Returns the group of extensions that crashes (if any). - - Install the given extensions in groups of group_size and return the - group of extensions that crashes (if any). - - Args: - extensions: A list of extensions to install. - group_size: The number of extensions to install at one time. - top_urls: The list of top urls to visit. - - Returns: - The extensions in the crashing group or None if there is no crash. - """ - curr_extension = 0 - num_extensions = len(extensions) - self.RestartBrowser() - orig_extension_ids = self._GetInstalledExtensionIds() - - while curr_extension < num_extensions: - logging.debug('New group of %d extensions.', group_size) - group_end = curr_extension + group_size - for extension in extensions[curr_extension:group_end]: - logging.debug('Installing extension: %s', extension) - self.InstallExtension(extension) - - for url in top_urls: - self.NavigateToURL(url) - - def _LogAndReturnCrashing(): - crashing_extensions = extensions[curr_extension:group_end] - logging.debug('Crashing extensions: %s', crashing_extensions) - return crashing_extensions - - # If the browser has crashed, return the extensions in the failing group. - try: - num_browser_windows = self.GetBrowserWindowCount() - except: - return _LogAndReturnCrashing() - else: - if not num_browser_windows: - return _LogAndReturnCrashing() - else: - # Uninstall all extensions that aren't installed by default. - new_extension_ids = [id for id in self._GetInstalledExtensionIds() - if id not in orig_extension_ids] - for extension_id in new_extension_ids: - self.UninstallExtensionById(extension_id) - - curr_extension = group_end - - # None of the extensions crashed. - return None - - def _GetExtensionInfoById(self, extensions, id): - for x in extensions: - if x['id'] == id: - return x - return None - - def ExtensionCrashes(self): - """Add top extensions; confirm browser stays up when visiting top urls.""" - # TODO: provide a way in pyauto to pass args to a test - take these as args - extensions_dir = os.path.join(self.DataDir(), 'extensions-tool') - urls_file = os.path.join(self.DataDir(), 'urls.txt') - - error_msg = 'The dir "%s" must exist' % os.path.abspath(extensions_dir) - assert os.path.exists(extensions_dir), error_msg - error_msg = 'The file "%s" must exist' % os.path.abspath(urls_file) - assert os.path.exists(urls_file), error_msg - - num_urls_to_visit = 100 - extensions_group_size = 20 - - top_urls = [l.rstrip() for l in - open(urls_file).readlines()[:num_urls_to_visit]] - - failed_extensions = glob.glob(os.path.join(extensions_dir, '*.crx')) - group_size = extensions_group_size - - while (group_size and failed_extensions): - failed_extensions = self._ReturnCrashingExtensions( - failed_extensions, group_size, top_urls) - group_size = group_size // 2 - - self.assertFalse(failed_extensions, - 'Extension(s) in failing group: %s' % failed_extensions) - - def _InstallExtensionCheckDefaults(self, crx_file): - """Installs extension at extensions/|crx_file| and checks default status. - - Checks that the installed extension is enabled and not allowed in incognito. - - Args: - crx_file: Relative path from self.DataDir()/extensions to .crx extension - to be installed. - - Returns: - The extension ID. - """ - crx_file_path = os.path.abspath( - os.path.join(self.DataDir(), 'extensions', crx_file)) - ext_id = self.InstallExtension(crx_file_path) - extension = self._GetExtensionInfoById(self.GetExtensionsInfo(), ext_id) - self.assertTrue(extension['is_enabled'], - msg='Extension was not enabled on installation') - self.assertFalse(extension['allowed_in_incognito'], - msg='Extension was allowed in incognito on installation.') - - return ext_id - - def _ExtensionValue(self, ext_id, key): - """Returns the value of |key| for |ext_id|. - - Args: - ext_id: The extension ID. - key: The key for which the extensions info value is required. - - Returns: - The value of extensions info |key| for |ext_id|. - """ - return self._GetExtensionInfoById(self.GetExtensionsInfo(), ext_id)[key] - - def _FileAccess(self, ext_id): - """Returns the value of newAllowFileAccess for |ext_id|. - - Args: - ext_id: The extension ID. - - Returns: - The value of extensions settings newAllowFileAccess for |ext_id|. - """ - extension_settings = self.GetPrefsInfo().Prefs()['extensions']['settings'] - return extension_settings[ext_id]['newAllowFileAccess'] - - def testGetExtensionPermissions(self): - """Ensures we can retrieve the host/api permissions for an extension. - - This test assumes that the 'Bookmark Manager' extension exists in a fresh - profile. - """ - extensions_info = self.GetExtensionsInfo() - bm_exts = [x for x in extensions_info if x['name'] == 'Bookmark Manager'] - self.assertTrue(bm_exts, - msg='Could not find info for the Bookmark Manager ' - 'extension.') - ext = bm_exts[0] - - permissions_host = ext['host_permissions'] - self.assertTrue(len(permissions_host) == 2 and - 'chrome://favicon/*' in permissions_host and - 'chrome://resources/*' in permissions_host, - msg='Unexpected host permissions information.') - - permissions_api = ext['api_permissions'] - print permissions_api - self.assertTrue(len(permissions_api) == 5 and - 'bookmarks' in permissions_api and - 'bookmarkManagerPrivate' in permissions_api and - 'metricsPrivate' in permissions_api and - 'systemPrivate' in permissions_api and - 'tabs' in permissions_api, - msg='Unexpected API permissions information.') - - def testDisableEnableExtension(self): - """Tests that an extension can be disabled and enabled with the UI.""" - ext_id = self._InstallExtensionCheckDefaults('good.crx') - - # Disable extension. - driver = self.NewWebDriver() - ext_page = ExtensionsPage(driver) - self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) - ext_page.SetEnabled(ext_id, False) - self.WaitUntil(self._ExtensionValue, args=[ext_id, 'is_enabled'], - expect_retval=False) - self.assertFalse(self._ExtensionValue(ext_id, 'is_enabled'), - msg='Extension did not get disabled.') - - # Enable extension. - self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) - ext_page.SetEnabled(ext_id, True) - self.WaitUntil(self._ExtensionValue, args=[ext_id, 'is_enabled'], - expect_retval=True) - self.assertTrue(self._ExtensionValue(ext_id, 'is_enabled'), - msg='Extension did not get enabled.') - - def testAllowIncognitoExtension(self): - """Tests allowing and disallowing an extension in incognito mode.""" - ext_id = self._InstallExtensionCheckDefaults('good.crx') - - # Allow in incognito. - driver = self.NewWebDriver() - ext_page = ExtensionsPage(driver) - self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) - ext_page.SetAllowInIncognito(ext_id, True) - - # Check extension now allowed in incognito. - self.WaitUntil(self._ExtensionValue, args=[ext_id, 'allowed_in_incognito'], - expect_retval=True) - self.assertTrue(self._ExtensionValue(ext_id, 'allowed_in_incognito'), - msg='Extension did not get allowed in incognito.') - - # Disallow in incognito. - self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) - ext_page.SetAllowInIncognito(ext_id, False) - - # Check extension now disallowed in incognito. - self.WaitUntil(self._ExtensionValue, args=[ext_id, 'allowed_in_incognito'], - expect_retval=False) - self.assertFalse(self._ExtensionValue(ext_id, 'allowed_in_incognito'), - msg='Extension did not get disallowed in incognito.') - - def testAllowAccessFileURLs(self): - """Tests disallowing and allowing and extension access to file URLs.""" - ext_id = self._InstallExtensionCheckDefaults(os.path.join('permissions', - 'files')) - - # Check extension allowed access to file URLs by default. - extension_settings = self.GetPrefsInfo().Prefs()['extensions']['settings'] - self.assertTrue(extension_settings[ext_id]['newAllowFileAccess'], - msg='Extension was not allowed access to file URLs on ' - 'installation') - - # Disallow access to file URLs. - driver = self.NewWebDriver() - ext_page = ExtensionsPage(driver) - self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) - ext_page.SetAllowAccessFileURLs(ext_id, False) - - # Check that extension does not have access to file URLs. - self.WaitUntil(self._FileAccess, args=[ext_id], expect_retval=False) - self.assertFalse(self._FileAccess(ext_id), - msg='Extension did not have access to file URLs denied.') - - # Allow access to file URLs. - self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) - ext_page.SetAllowAccessFileURLs(ext_id, True) - - # Check that extension now has access to file URLs. - self.WaitUntil(self._FileAccess, args=[ext_id], expect_retval=True) - self.assertTrue(self._FileAccess(ext_id), - msg='Extension did not have access to file URLs granted.') - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/fullscreen_mouselock.py b/chrome/test/functional/fullscreen_mouselock.py deleted file mode 100755 index 6cb5b97..0000000 --- a/chrome/test/functional/fullscreen_mouselock.py +++ /dev/null @@ -1,618 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import os -import re -import shutil -import time - -import pyauto_functional # Must be imported before pyauto -import pyauto -import test_utils -from selenium.webdriver.common.action_chains import ActionChains -from selenium.common.exceptions import WebDriverException -from selenium.webdriver.common.keys import Keys -from webdriver_pages import settings - - -class FullscreenMouselockTest(pyauto.PyUITest): - """TestCase for Fullscreen and Mouse Lock.""" - - def setUp(self): - pyauto.PyUITest.setUp(self) - self._driver = self.NewWebDriver() - # Get the hostname pattern (e.g. http://127.0.0.1:57622). - self._hostname_pattern = ( - re.sub('/files/$', '', self.GetHttpURLForDataPath(''))) - - def Debug(self): - """Test method for experimentation. - - This method will not run automatically. - """ - page = settings.ContentSettingsPage.FromNavigation(self._driver) - import pdb - pdb.set_trace() - - def ExtraChromeFlags(self): - """Ensures Chrome is launched with custom flags. - - Returns: - A list of extra flags to pass to Chrome when it is launched. - """ - # Extra flag needed by scroll performance tests. - return super(FullscreenMouselockTest, - self).ExtraChromeFlags() + ['--enable-pointer-lock'] - - def testFullScreenMouseLockHooks(self): - """Verify fullscreen and mouse lock automation hooks work.""" - self.NavigateToURL(self.GetHttpURLForDataPath( - 'fullscreen_mouselock', 'fullscreen_mouselock.html')) - - # Starting off we shouldn't be fullscreen - self.assertFalse(self.IsFullscreenForBrowser()) - self.assertFalse(self.IsFullscreenForTab()) - - # Go fullscreen - self._driver.find_element_by_id('enterFullscreen').click() - self.assertTrue(self.WaitUntil(self.IsFullscreenForTab)) - - # Bubble should be up prompting to allow fullscreen - self.assertTrue(self.IsFullscreenBubbleDisplayed()) - self.assertTrue(self.IsFullscreenBubbleDisplayingButtons()) - self.assertTrue(self.IsFullscreenPermissionRequested()) - - # Accept bubble, it should go away. - self.AcceptCurrentFullscreenOrMouseLockRequest() - self.assertTrue(self.WaitUntil( - lambda: not self.IsFullscreenBubbleDisplayingButtons())) - - # Try to lock mouse, it won't lock yet but permision will be requested. - self.assertFalse(self.IsMouseLocked()) - self._driver.find_element_by_id('lockMouse1').click() - self.assertTrue(self.WaitUntil(self.IsMouseLockPermissionRequested)) - self.assertFalse(self.IsMouseLocked()) - - # Deny mouse lock. - self.DenyCurrentFullscreenOrMouseLockRequest() - self.assertTrue(self.WaitUntil( - lambda: not self.IsFullscreenBubbleDisplayingButtons())) - self.assertFalse(self.IsMouseLocked()) - - # Try mouse lock again, and accept it. - self._driver.find_element_by_id('lockMouse1').click() - self.assertTrue(self.WaitUntil(self.IsMouseLockPermissionRequested)) - self.AcceptCurrentFullscreenOrMouseLockRequest() - self.assertTrue(self.WaitUntil(self.IsMouseLocked)) - - # The following doesn't work - as sending the key to the input field isn't - # picked up by the browser. :( Need an alternative way. - # - # # Ideally we wouldn't target a specific element, we'd just send keys to - # # whatever the current keyboard focus was. - # keys_target = driver.find_element_by_id('sendKeysTarget') - # - # # ESC key should exit fullscreen and mouse lock. - # - # print "# ESC key should exit fullscreen and mouse lock." - # keys_target.send_keys(Keys.ESCAPE) - # self.assertTrue(self.WaitUntil(lambda: not self.IsFullscreenForBrowser())) - # self.assertTrue(self.WaitUntil(lambda: not self.IsFullscreenForTab())) - # self.assertTrue(self.WaitUntil(lambda: not self.IsMouseLocked())) - # - # # Check we can go browser fullscreen - # print "# Check we can go browser fullscreen" - # keys_target.send_keys(Keys.F11) - # self.assertTrue(self.WaitUntil(self.IsFullscreenForBrowser)) - - def _LaunchFSAndExpectPrompt(self, button_action='enterFullscreen'): - """Helper function to launch fullscreen and expect a prompt. - - Fullscreen is initiated and a bubble prompt appears asking to allow or - cancel from fullscreen mode. The actual fullscreen mode doesn't take place - until after approving the prompt. - - If the helper is not successful then the test will fail. - - Args: - button_action: The button id to click to initiate an action. Default is to - click enterFullscreen. - """ - self.NavigateToURL(self.GetHttpURLForDataPath( - 'fullscreen_mouselock', 'fullscreen_mouselock.html')) - # Should not be in fullscreen mode during initial launch. - self.assertFalse(self.IsFullscreenForBrowser()) - self.assertFalse(self.IsFullscreenForTab()) - # Go into fullscreen mode. - self._driver.find_element_by_id(button_action).click() - self.assertTrue(self.WaitUntil(self.IsFullscreenForTab)) - # Bubble should display prompting to allow fullscreen. - self.assertTrue(self.IsFullscreenPermissionRequested()) - - def _InitiateBrowserFullscreen(self): - """Helper function that initiates browser fullscreen.""" - self.NavigateToURL(self.GetHttpURLForDataPath( - 'fullscreen_mouselock', 'fullscreen_mouselock.html')) - # Should not be in fullscreen mode during initial launch. - self.assertFalse(self.IsFullscreenForBrowser()) - self.assertFalse(self.IsFullscreenForTab()) - # Initiate browser fullscreen. - self.ApplyAccelerator(pyauto.IDC_FULLSCREEN) - self.assertTrue(self.WaitUntil(self.IsFullscreenForBrowser)) - self.assertTrue(self.WaitUntil(lambda: not self.IsFullscreenForTab())) - self.assertTrue(self.WaitUntil(lambda: not self.IsMouseLocked())) - - def _InitiateTabFullscreen(self): - """Helper function that initiates tab fullscreen.""" - self.NavigateToURL(self.GetHttpURLForDataPath( - 'fullscreen_mouselock', 'fullscreen_mouselock.html')) - # Initiate tab fullscreen. - self._driver.find_element_by_id('enterFullscreen').click() - self.assertTrue(self.WaitUntil(self.IsFullscreenForTab)) - - def _AcceptFullscreenOrMouseLockRequest(self): - """Helper function to accept fullscreen or mouse lock request.""" - self.AcceptCurrentFullscreenOrMouseLockRequest() - self.assertTrue(self.WaitUntil( - lambda: not self.IsFullscreenBubbleDisplayingButtons())) - - def _EnableFullscreenAndMouseLockMode(self): - """Helper function to enable fullscreen and mouse lock mode.""" - self._LaunchFSAndExpectPrompt(button_action='enterFullscreenAndLockMouse1') - # Allow fullscreen. - self.AcceptCurrentFullscreenOrMouseLockRequest() - # The wait is needed due to crbug.com/123396. Should be able to click the - # fullscreen and mouselock button and be both accepted in a single action. - self.assertTrue(self.WaitUntil(self.IsMouseLockPermissionRequested)) - # Allow mouse lock. - self.AcceptCurrentFullscreenOrMouseLockRequest() - self.assertTrue(self.WaitUntil(self.IsMouseLocked)) - - def _EnableMouseLockMode(self, button_action='lockMouse1'): - """Helper function to enable mouse lock mode. - - Args: - button_action: The button id to click to initiate an action. Default is to - click lockMouse1. - """ - self._driver.find_element_by_id(button_action).click() - self.assertTrue(self.WaitUntil(self.IsMouseLockPermissionRequested)) - self.AcceptCurrentFullscreenOrMouseLockRequest() - self.assertTrue(self.IsMouseLocked()) - - def _EnableAndReturnLockMouseResult(self): - """Helper function to enable and return mouse lock result.""" - self.NavigateToURL(self.GetHttpURLForDataPath( - 'fullscreen_mouselock', 'fullscreen_mouselock.html')) - self._driver.find_element_by_id('lockMouse2').click() - self.assertTrue( - self.WaitUntil(self.IsMouseLockPermissionRequested)) - self.AcceptCurrentFullscreenOrMouseLockRequest() - # Waits until lock_result gets 'success' or 'failure'. - return self._driver.execute_script('return lock_result') - - def _ClickAnchorLink(self): - """Clicks the anchor link until it's successfully clicked. - - Clicks on the anchor link and compares the js |clicked_elem_ID| variabled - with the anchor id. Returns True if the link is clicked. - """ - element_id = 'anchor' - # Catch WebDriverException: u'Element is not clickable at point (185.5, - # 669.5). Instead another element would receive the click. - try: - self._driver.find_element_by_id(element_id).click() - except WebDriverException: - return False - return self._driver.execute_script('return clicked_elem_ID') == element_id - - def testPrefsForFullscreenAllowed(self): - """Verify prefs when fullscreen is allowed.""" - self._LaunchFSAndExpectPrompt() - self._AcceptFullscreenOrMouseLockRequest() - content_settings = ( - self.GetPrefsInfo().Prefs()['profile']['content_settings']) - self.assertEqual( - {self._hostname_pattern + ',*': {'fullscreen': 1}}, # Allow hostname. - content_settings['pattern_pairs'], - msg='Saved hostname pattern does not match expected pattern.') - - def testPrefsForFullscreenExit(self): - """Verify prefs is empty when exit fullscreen mode before allowing.""" - self._LaunchFSAndExpectPrompt() - self._driver.find_element_by_id('exitFullscreen').click() - # Verify exit from fullscreen mode. - self.assertTrue(self.WaitUntil(lambda: not self.IsFullscreenForTab())) - content_settings = ( - self.GetPrefsInfo().Prefs()['profile']['content_settings']) - self.assertEqual( - {}, content_settings['pattern_pairs'], - msg='Patterns saved when there should be none.') - - def testPatternsForFSAndML(self): - """Verify hostname pattern and behavior for allowed mouse cursor lock. - - To lock the mouse, the browser needs to be in fullscreen mode. - """ - self._EnableFullscreenAndMouseLockMode() - self._EnableMouseLockMode() - expected_pattern = ( - {self._hostname_pattern + ',*': {'fullscreen': 1, 'mouselock': 1}}) - content_settings = ( - self.GetPrefsInfo().Prefs()['profile']['content_settings']) - self.assertEqual( - expected_pattern, content_settings['pattern_pairs'], - msg='Saved hostname and behavior patterns do not match expected.') - - def testPatternsForAllowMouseLock(self): - """Verify hostname pattern and behavior for allowed mouse cursor lock. - - Enable fullscreen mode and enable mouse lock separately. - """ - self._LaunchFSAndExpectPrompt() - self.AcceptCurrentFullscreenOrMouseLockRequest() - self._EnableMouseLockMode() - expected_pattern = ( - {self._hostname_pattern + ',*': {'fullscreen': 1, 'mouselock': 1}}) - content_settings = ( - self.GetPrefsInfo().Prefs()['profile']['content_settings']) - self.assertEqual( - expected_pattern, content_settings['pattern_pairs'], - msg='Saved hostname and behavior patterns do not match expected.') - - def testNoMouseLockRequest(self): - """Verify mouse lock request does not appear. - - When allowing all sites to disable the mouse cursor, the mouse lock request - bubble should not show. The mouse cursor should be automatically disabled - when clicking on a disable mouse button. - """ - # Allow all sites to disable mouse cursor. - self.SetPrefs(pyauto.kDefaultContentSettings, {u'mouselock': 1}) - self._LaunchFSAndExpectPrompt() - # Allow for fullscreen mode. - self._AcceptFullscreenOrMouseLockRequest() - self._driver.set_script_timeout(2) - # Receive callback status (success or failure) from javascript that the - # click has registered and the mouse lock status has changed. - lock_result = self._driver.execute_async_script( - 'lockMouse1(arguments[arguments.length - 1])') - self.assertEqual(lock_result, 'success', msg='Mouse lock unsuccessful.') - self.assertTrue(self.WaitUntil( - lambda: not self.IsMouseLockPermissionRequested())) - self.assertTrue(self.IsMouseLocked()) - - def testUnableToLockMouse(self): - """Verify mouse lock is disabled. - - When not allowing any site to disable the mouse cursor, the mouse lock - request bubble should not show and the mouse cursor should not be disabled. - """ - # Do not allow any site to disable mouse cursor. - self.SetPrefs(pyauto.kDefaultContentSettings, {u'mouselock': 2}) - self._LaunchFSAndExpectPrompt() - # Allow for fullscreen mode. - self._AcceptFullscreenOrMouseLockRequest() - self._driver.set_script_timeout(2) - # Receive callback status (success or failure) from javascript that the - # click has registered and the mouse lock status has changed. - lock_result = self._driver.execute_async_script( - 'lockMouse1(arguments[arguments.length - 1])') - self.assertEqual(lock_result, 'failure', msg='Mouse locked unexpectedly.') - self.assertTrue(self.WaitUntil( - lambda: not self.IsMouseLockPermissionRequested())) - self.assertTrue(self.WaitUntil(lambda: not self.IsMouseLocked())) - - def testEnterTabFSWhileInBrowserFS(self): - """Verify able to enter into tab fullscreen while in browser fullscreen.""" - self._InitiateBrowserFullscreen() - # Initiate tab fullscreen. - self._driver.find_element_by_id('enterFullscreen').click() - self.assertTrue(self.WaitUntil(lambda: self.IsFullscreenForTab())) - self.assertTrue(self.WaitUntil(lambda: not self.IsMouseLocked())) - - def testMouseLockInBrowserFS(self): - """Verify mouse lock in browser fullscreen requires allow prompt.""" - self._InitiateBrowserFullscreen() - self._driver.set_script_timeout(2) - self._driver.execute_script('lockMouse1AndSetLockResult()') - # Bubble should display prompting to allow mouselock. - self.assertTrue(self.WaitUntil(self.IsMouseLockPermissionRequested)) - self.AcceptCurrentFullscreenOrMouseLockRequest() - # Waits until lock_result gets 'success' or 'failure'. - lock_result = self._driver.execute_script('return lock_result') - self.assertEqual(lock_result, 'success', - msg='Mouse was not locked in browser fullscreen.') - - def testNoMouseLockWhenCancelFS(self): - """Verify mouse lock breaks when canceling tab fullscreen. - - This test uses javascript to initiate exit of tab fullscreen after mouse - lock success callback. - """ - self._LaunchFSAndExpectPrompt() - self._driver.set_script_timeout(2) - lock_result = self._driver.execute_script('lockMouse1AndSetLockResult()') - self.assertTrue( - self.WaitUntil(lambda: self.IsMouseLockPermissionRequested())) - self.AcceptCurrentFullscreenOrMouseLockRequest() - self.assertTrue(self.WaitUntil(self.IsMouseLocked)) - # Waits until lock_result gets 'success' or 'failure'. - lock_result = self._driver.execute_script('return lock_result') - self.assertEqual( - lock_result, 'success', msg='Mouse is not locked.') - self._driver.execute_script('document.webkitCancelFullScreen()') - self.assertTrue(self.WaitUntil(lambda: not self.IsFullscreenForTab()), - msg='Tab is still in fullscreen.') - self.assertTrue(self.WaitUntil(lambda: not self.IsMouseLocked()), - msg='Mouse is still locked after exiting fullscreen.') - - def testNoTabFSExitWhenJSExitMouseLock(self): - """Verify tab fullscreen does not exit when javascript init mouse lock exit. - - This test uses javascript to initiate exit of mouse lock after mouse - lock success callback. - """ - self._LaunchFSAndExpectPrompt() - self._EnableMouseLockMode() - self._driver.execute_script('navigator.webkitPointer.unlock()') - self.WaitUntil(lambda: not self.IsMouseLocked()) - self.assertTrue(self.IsFullscreenForTab(), msg='Tab fullscreen was lost.') - - def testMouseLockExitWhenAlertDialogShow(self): - """Verify mouse lock breaks when alert dialog appears.""" - self._LaunchFSAndExpectPrompt() - self._EnableMouseLockMode() - # Need to catch the exception here since the alert dialog raises - # a WebDriverException due to a modal dialog. - from selenium.common.exceptions import WebDriverException - try: - self._driver.execute_script('alert("A modal dialog")') - except WebDriverException: - pass - - self.assertTrue(self.WaitUntil(lambda: self.IsFullscreenForTab()), - msg='Tab fullscreen was lost.') - self.assertTrue(self.WaitUntil(lambda: not self.IsMouseLocked()), - msg='Mouse is still locked') - - def testMouseLockExitWhenBrowserLoseFocus(self): - """Verify mouse lock breaks when browser loses focus. - - Mouse lock breaks when the focus is placed on another new window. - """ - self._LaunchFSAndExpectPrompt() - self.AcceptCurrentFullscreenOrMouseLockRequest() - # Open a new window to shift focus away. - self.OpenNewBrowserWindow(True) - self.assertTrue(self.WaitUntil(lambda: self.IsFullscreenForTab())) - self.assertTrue(self.WaitUntil(lambda: not self.IsMouseLocked()), - msg='Mouse lock did not break when browser lost focus.') - - def testMouseLockLostOnReload(self): - """Verify mouse lock is lost on page reload.""" - self.NavigateToURL(self.GetHttpURLForDataPath( - 'fullscreen_mouselock', 'fullscreen_mouselock.html')) - self._EnableMouseLockMode() - self.ReloadActiveTab() - self.assertTrue(self.WaitUntil(lambda: not self.IsMouseLocked()), - msg='Mouse lock did not break when page is reloaded.') - - def testNoMLBubbleWhenTabLoseFocus(self): - """Verify mouse lock bubble goes away when tab loses focus.""" - self.NavigateToURL(self.GetHttpURLForDataPath( - 'fullscreen_mouselock', 'fullscreen_mouselock.html')) - self._driver.find_element_by_id('lockMouse1').click() - self.assertTrue(self.WaitUntil(self.IsMouseLockPermissionRequested)) - self.AppendTab(pyauto.GURL('chrome://newtab')) - self.assertTrue(self.WaitUntil( - lambda: not self.IsFullscreenBubbleDisplayingButtons()), - msg='Mouse lock bubble did not clear when tab lost focus.') - - def testTabFSExitWhenNavBackToPrevPage(self): - """Verify tab fullscreen exit when navigating back to previous page. - - This test navigates to a new page while in tab fullscreen mode by using - GoBack() to navigate to the previous google.html page. - """ - self.NavigateToURL(self.GetHttpURLForDataPath('google', 'google.html')) - self._InitiateTabFullscreen() - self.TabGoBack() - self.assertFalse( - self.IsFullscreenForTab(), - msg='Tab fullscreen did not exit when navigating to a new page.') - - def testTabFSExitWhenNavToNewPage(self): - """Verify tab fullscreen exit when navigating to a new website. - - This test navigates to a new website while in tab fullscreen. - """ - self._InitiateTabFullscreen() - self.NavigateToURL(self.GetHttpURLForDataPath('google', 'google.html')) - self.assertFalse( - self.IsFullscreenForTab(), - msg='Tab fullscreen did not exit when navigating to a new website.') - - def testTabFSDoesNotExitForAnchorLinks(self): - """Verify tab fullscreen does not exit for anchor links. - - Tab fullscreen should not exit when following a link to the same page such - as example.html#anchor. - """ - self._InitiateTabFullscreen() - self.assertTrue(self.WaitUntil(self._ClickAnchorLink)) - self.assertTrue( - self.WaitUntil(self.IsFullscreenForTab), - msg='Tab fullscreen should not exit when clicking on an anchor link.') - - def testMLExitWhenNavBackToPrevPage(self): - """Verify mouse lock exit when navigating back to previous page. - - This test navigates to a new page while mouse lock is activated by using - GoBack() to navigate to the previous google.html page. - """ - self.NavigateToURL(self.GetHttpURLForDataPath('google', 'google.html')) - lock_result = self._EnableAndReturnLockMouseResult() - self.assertEqual( - lock_result, 'success', msg='Mouse is not locked.') - self.TabGoBack() - self.assertFalse( - self.IsMouseLocked(), - msg='Mouse lock did not exit when navigating to the prev page.') - - def testMLExitWhenNavToNewPage(self): - """Verify mouse lock exit when navigating to a new website.""" - lock_result = self._EnableAndReturnLockMouseResult() - self.assertEqual( - lock_result, 'success', msg='Mouse is not locked.') - self.NavigateToURL(self.GetHttpURLForDataPath('google', 'google.html')) - self.assertFalse( - self.IsMouseLocked(), - msg='Mouse lock did not exit when navigating to a new website.') - - def testMLDoesNotExitForAnchorLinks(self): - """Verify mouse lock does not exit for anchor links. - - Mouse lock should not exist when following a link to the same page such as - example.html#anchor. - """ - lock_result = self._EnableAndReturnLockMouseResult() - self.assertEqual( - lock_result, 'success', msg='Mouse is not locked.') - ActionChains(self._driver).move_to_element( - self._driver.find_element_by_id('anchor')).click().perform() - self.assertTrue(self.WaitUntil(self.IsMouseLocked), - msg='Mouse lock broke when clicking on an anchor link.') - - def ExitTabFSToBrowserFS(self): - """Verify exiting tab fullscreen leaves browser in browser fullscreen. - - This test is semi-automated. - - The browser initiates browser fullscreen, then initiates tab fullscreen. The - test verifies that existing tab fullscreen by simulating ESC key press or - clicking the js function to exitFullscreen() will exit the tab fullscreen - leaving browser fullscreen intact. - """ - self._InitiateBrowserFullscreen() - # Initiate tab fullscreen. - self._driver.find_element_by_id('enterFullscreen').click() - self.assertTrue(self.WaitUntil(lambda: self.IsFullscreenForTab())) - # Require manual intervention to send ESC key due to crbug.com/123930. - # TODO(dyu): Update to a full test once associated bug is fixed. - logging.info('Press ESC key to exit tab fullscreen.') - time.sleep(5) - self.assertTrue(self.WaitUntil(lambda: not self.IsFullscreenForTab())) - self.assertTrue(self.WaitUntil(lambda: self.IsFullscreenForBrowser()), - msg='Not in browser fullscreen mode.') - - self._driver.find_element_by_id('enterFullscreen').click() - self.assertTrue(self.WaitUntil(lambda: self.IsFullscreenForTab())) - # Exit tab fullscreen by clicking button exitFullscreen(). - self._driver.find_element_by_id('exitFullscreen').click() - self.assertTrue(self.WaitUntil(lambda: not self.IsFullscreenForTab())) - self.assertTrue(self.WaitUntil(lambda: self.IsFullscreenForBrowser()), - msg='Not in browser fullscreen mode.') - - def F11KeyExitsTabAndBrowserFS(self): - """Verify existing tab fullscreen exits all fullscreen modes. - - This test is semi-automated. - - The browser initiates browser fullscreen, then initiates tab fullscreen. The - test verifies that existing tab fullscreen by simulating F11 key press or - CMD + SHIFT + F keys on the Mac will exit the tab fullscreen and the - browser fullscreen. - """ - self._InitiateBrowserFullscreen() - # Initiate tab fullscreen. - self._driver.find_element_by_id('enterFullscreen').click() - self.assertTrue(self.WaitUntil(lambda: self.IsFullscreenForTab())) - # Require manual intervention to send F11 key due to crbug.com/123930. - # TODO(dyu): Update to a full test once associated bug is fixed. - logging.info('Press F11 key to exit tab fullscreen.') - time.sleep(5) - self.assertTrue(self.WaitUntil(lambda: not self.IsFullscreenForTab())) - self.assertTrue(self.WaitUntil(lambda: not self.IsFullscreenForBrowser()), - msg='Browser is in fullscreen mode.') - - def SearchForTextOutsideOfContainer(self): - """Verify text outside of container is not visible when fullscreen. - - This test is semi-automated. - - Verify this test manually until there is a way to find text on screen - without using FindInPage(). - - The text that is outside of the fullscreen container should only be visible - when fullscreen is off. The text should not be visible while in fullscreen - mode. - """ - self.NavigateToURL(self.GetHttpURLForDataPath( - 'fullscreen_mouselock', 'fullscreen_mouselock.html')) - # Should not be in fullscreen mode during initial launch. - self.assertFalse(self.IsFullscreenForBrowser()) - self.assertFalse(self.IsFullscreenForTab()) - self.assertTrue( - self.WaitUntil(lambda: self.FindInPage( - 'This text is outside of the container')['match_count'], - expect_retval=1)) - # Go into fullscreen mode. - self._driver.find_element_by_id('enterFullscreen').click() - self.assertTrue(self.WaitUntil(self.IsFullscreenForTab)) - time.sleep(5) - # TODO(dyu): find a way to verify on screen text instead of using - # FindInPage() which searches for text in the HTML. - - def SameMouseLockMovement(self): - """Verify the correct feel of mouse movement data when mouse is locked. - - This test is semi-automated. - - This test loads the same web page in two different tabs while in mouse lock - mode. Each tab loads the web page from a different URL (e.g. by loading it - from a localhost server and a file url). The test verifies - that the mouse lock movements work the same in both - tabs. - """ - url1 = self.GetHttpURLForDataPath( - 'fullscreen_mouselock', 'fullscreen_mouselock.html') - url2 = self.GetFileURLForDataPath( - 'fullscreen_mouselock', 'fullscreen_mouselock.html') - tab2 = 'f1-4' - self.NavigateToURL(url1) - self.RunCommand(pyauto.IDC_NEW_TAB) # Open new tab. - self.NavigateToURL(url2, 0, 1) - self._driver.switch_to_window(tab2) - self._EnableMouseLockMode() # Lock mouse in tab 2. - raw_input('Manually move the mouse cursor on the page in tab 2. Shift+Tab \ - into tab 1, click on lockMouse1() button, and move the mouse \ - cursor on the page in tab 1. Verify mouse movement is smooth.') - - def MouseEventsIndependentOfExitBubble(self): - """Verify mouse events are independent of the exit FS exit bubble for ML. - - Mouse movement events should work immediately when mouse lock is activated. - The events should not be blocked waiting for the exit instruction bubble to - clear. - """ - self.NavigateToURL(self.GetHttpURLForDataPath( - 'fullscreen_mouselock', 'fullscreen_mouselock.html')) - # Should not be in fullscreen mode during initial launch. - self.assertFalse(self.IsFullscreenForBrowser()) - self.assertFalse(self.IsFullscreenForTab()) - # Go into fullscreen mode. - self._driver.find_element_by_id('enterFullscreen').click() - self.assertTrue(self.WaitUntil(self.IsFullscreenForTab)) - self._EnableMouseLockMode() - raw_input( - '1. Move the mouse, see movement data being received by the page.\ - 2. Press ESC key.\ - 3. Lock the mouse without going fullscreen. Click lockMouse1() button.\ - Verify: The mouse movement events should work immediately.') - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/gpu.py b/chrome/test/functional/gpu.py deleted file mode 100755 index 3f88ed2..0000000 --- a/chrome/test/functional/gpu.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import os - -import pyauto_functional # Must be imported before pyauto -import pyauto - - -class GpuTest(pyauto.PyUITest): - """GPU Tests Runner.""" - - def _GetGpuPID(self): - """Fetch the pid of the GPU process.""" - child_processes = self.GetBrowserInfo()['child_processes'] - for x in child_processes: - if x['type'] == 'GPU': - return x['pid'] - return None - - def _IsHardwareAccelerated(self, feature): - """Check if gpu is enabled in the machine before running any tests.""" - self.NavigateToURL('about:gpu') - def IsFeatureStatusLoaded(): - """Returns whether the feature status UI has been loaded. - - The about:gpu page fetches status for features asynchronously, so use - this to check if the fetch is done. - """ - js = """ - var list = document.querySelector(".feature-status-list"); - domAutomationController.send(list.hasChildNodes() ? "done" : ""); - """ - return self.ExecuteJavascript(js) - self.assertTrue(self.WaitUntil(IsFeatureStatusLoaded, 10)) - search = feature + ': Hardware accelerated' - find_result = self.FindInPage(search)['match_count'] - if find_result: - # about:gpu page starts a gpu process. Restart the browser to clear - # the state. We could kill the gpu process, but navigating to a page - # after killing the gpu can lead to flakiness. - # See crbug.com/93423. - self.RestartBrowser() - return True - else: - logging.warn('Hardware acceleration not available') - return False - - def _VerifyGPUProcessOnPage(self, url): - url = self.GetFileURLForDataPath('pyauto_private', 'gpu', url) - self.NavigateToURL(url) - self.assertTrue(self.WaitUntil( - lambda: self._GetGpuPID() is not None), msg='No process for GPU') - - def testSingleGpuProcess(self): - """Verify there's only one gpu process shared across all uses.""" - self.assertTrue(self._IsHardwareAccelerated('WebGL')) - url = self.GetFileURLForDataPath('pyauto_private', - 'gpu', 'WebGLField.html') - self.AppendTab(pyauto.GURL(url)) - # Open a new window. - self.OpenNewBrowserWindow(True) - self.NavigateToURL(url, 1, 0) - # Open a new incognito window. - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - self.NavigateToURL(url, 1, 0) - # Verify there's only 1 gpu process. - gpu_process_count = 0 - for x in self.GetBrowserInfo()['child_processes']: - if x['type'] == 'GPU': - gpu_process_count += 1 - self.assertEqual(1, gpu_process_count) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/gtalk/__init__.py b/chrome/test/functional/gtalk/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/chrome/test/functional/gtalk/__init__.py +++ /dev/null diff --git a/chrome/test/functional/gtalk/gtalk_base_test.py b/chrome/test/functional/gtalk/gtalk_base_test.py deleted file mode 100644 index b1dd2e5..0000000 --- a/chrome/test/functional/gtalk/gtalk_base_test.py +++ /dev/null @@ -1,348 +0,0 @@ -# Copyright (c) 2012 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. - -"""Base GTalk tests. - -This module contains a set of common utilities for querying -and manipulating the Google Talk Chrome Extension. -""" - -import logging -import re -import os - -import pyauto_gtalk -import pyauto -import pyauto_errors - - -class GTalkBaseTest(pyauto.PyUITest): - """Base test class for testing GTalk.""" - - _injected_js = None - - def Prompt(self, text): - """Pause execution with debug output. - - Args: - text: The debug output. - """ - text = str(text) - raw_input('--------------------> ' + text) - - def InstallGTalkExtension(self, gtalk_version): - """Download and install the GTalk extension.""" - extension_path = os.path.abspath( - os.path.join(self.DataDir(), 'extensions', 'gtalk', - gtalk_version + '.crx')) - self.assertTrue( - os.path.exists(extension_path), - msg='Failed to find GTalk extension: ' + extension_path) - - extension = self.GetGTalkExtensionInfo() - if extension: - logging.info('Extension already installed. Skipping install...\n') - return - - self.InstallExtension(extension_path, False) - extension = self.GetGTalkExtensionInfo() - self.assertTrue(extension, msg='Failed to install GTalk extension.') - self.assertTrue(extension['is_enabled'], msg='GTalk extension is disabled.') - - def UninstallGTalkExtension(self): - """Uninstall the GTalk extension (if present)""" - extension = self.GetGTalkExtensionInfo() - if extension: - self.UninstallExtensionById(extension['id']) - - def GetGTalkExtensionInfo(self): - """Get the data object about the GTalk extension.""" - extensions = [x for x in self.GetExtensionsInfo() - if x['name'] == 'Chat for Google'] - return extensions[0] if len(extensions) == 1 else None - - def RunInMole(self, js, mole_index=0): - """Execute javascript in a chat mole. - - Args: - js: The javascript to run. - mole_index: The index of the mole in which to run the JS. - - Returns: - The resulting value from executing the javascript. - """ - return self._RunInRenderView(self.GetMoleInfo(mole_index), js, - '//iframe[1]') - - def RunInAllMoles(self, js): - """Execute javascript in all chat moles. - - Args: - js: The javascript to run. - """ - moles = self.GetMolesInfo() - for mole in moles: - self._RunInRenderView(mole, js, '//iframe[1]') - - def RunInRoster(self, js): - """Execute javascript in the chat roster. - - Args: - js: The javascript to run. - - Returns: - The resulting value from executing the javascript. - """ - return self._RunInRenderView(self.GetViewerInfo(), js, - '//iframe[1]\n//iframe[1]') - - def RunInLoginPage(self, js, xpath=''): - """Execute javascript in the gaia login popup. - - Args: - js: The javascript to run. - xpath: The xpath to the frame in which to execute the javascript. - - Returns: - The resulting value from executing the javascript. - """ - return self._RunInTab(self.GetLoginPageInfo(), js, xpath) - - def RunInViewer(self, js, xpath=''): - """Execute javascript in the GTalk viewer window. - - Args: - js: The javascript to run. - xpath: The xpath to the frame in which to execute the javascript. - - Returns: - The resulting value from executing the javascript. - """ - return self._RunInRenderView(self.GetViewerInfo(), js, xpath) - - def RunInBackground(self, js, xpath=''): - """Execute javascript in the GTalk viewer window. - - Args: - js: The javascript to run. - xpath: The xpath to the frame in which to execute the javascript. - - Returns: - The resulting value from executing the javascript. - """ - background_view = self.GetBackgroundInfo() - return self._RunInRenderView(background_view['view'], js, xpath) - - def GetMoleInfo(self, mole_index=0): - """Get the data object about a given chat mole. - - Args: - mole_index: The index of the mole to retrieve. - - Returns: - Data object describing mole. - """ - extension = self.GetGTalkExtensionInfo() - return self._GetExtensionViewInfo( - 'chrome-extension://%s/panel.html' % extension['id'], - mole_index) - - def GetMolesInfo(self): - """Get the data objects for all of the chat moles. - - Returns: - Set of data objects describing moles. - """ - extension = self.GetGTalkExtensionInfo() - return self._GetMatchingExtensionViews( - 'chrome-extension://%s/panel.html' % extension['id']) - - def GetViewerInfo(self): - """Get the data object about the GTalk viewer dialog.""" - extension = self.GetGTalkExtensionInfo() - return self._GetExtensionViewInfo( - 'chrome-extension://%s/viewer.html' % extension['id']) - - def GetLoginPageInfo(self): - """Get the data object about the gaia login popup.""" - return self._GetTabInfo('https://accounts.google.com/ServiceLogin?') - - def GetBackgroundInfo(self): - """Get the data object about the GTalk background page.""" - extension_views = self.GetBrowserInfo()['extension_views'] - for extension_view in extension_views: - if 'Google Talk' in extension_view['name'] and \ - 'EXTENSION_BACKGROUND_PAGE' == extension_view['view_type']: - return extension_view - return None - - def WaitUntilResult(self, result, func, msg): - """Loop func until a condition matches is satified. - - Args: - result: Value of func() at which to stop. - func: Function to run at each iteration. - msg: Error to print upon timing out. - """ - assert callable(func) - self.assertTrue(self.WaitUntil( - lambda: func(), expect_retval=result), msg=msg) - - def WaitUntilCondition(self, func, matches, msg): - """Loop func until condition matches is satified. - - Args: - func: Function to run at each iteration. - matches: Funtion to evalute output and determine whether to stop. - msg: Error to print upon timing out. - """ - assert callable(func) - assert callable(matches) - self.assertTrue(self.WaitUntil( - lambda: matches(func())), msg=msg) - - def _WrapJs(self, statement): - """Wrap the javascript to be executed. - - Args: - statement: The piece of javascript to wrap. - - Returns: - The wrapped javascript. - """ - return """ - window.domAutomationController.send( - (function(){ - %s - try{return %s} - catch(e){return "JS_ERROR: " + e}})()) - """ % (self._GetInjectedJs(), statement) - - def _RunInTab(self, tab, js, xpath=''): - """Execute javascript in a given tab. - - Args: - tab: The data object for the Chrome window tab returned by - _GetTabInfo. - js: The javascript to run. - xpath: The xpath to the frame in which to execute the javascript. - - Returns: - The resulting value from executing the javascript. - """ - if not tab: - logging.debug('Tab not found: %s' % tab) - return False - logging.info('Run in tab: %s' % js) - - value = self.ExecuteJavascript( - self._WrapJs(js), - tab_index = tab['index'], - windex = tab['windex'], - frame_xpath = xpath) - self._LogRun(js, value) - return value - - def _RunInRenderView(self, view, js, xpath=''): - """Execute javascript in a given render view. - - Args: - view: The data object for the Chrome render view returned by - _GetExtensionViewInfo. - js: The javascript to run. - xpath: The xpath to the frame in which to execute the javascript. - - Returns: - The resulting value from executing the javascript. - """ - if not view: - logging.debug('View not found: %s' % view) - return False - logging.info('Run in view: %s' % js) - - value = self.ExecuteJavascriptInRenderView( - self._WrapJs(js), - view, - frame_xpath = xpath) - self._LogRun(js, value) - return value - - def _LogRun(self, js, value): - """Log a particular run. - - Args: - js: The javascript statement executed. - value: The return value for the execution. - """ - # works around UnicodeEncodeError: 'ascii' codec can't encode... - out = value - if not isinstance(value, basestring): - out = str(value) - out = re.sub('\s', ';', out[:300]) - logging.info(js + ' ===> ' + out.encode('utf-8')) - - def _GetTabInfo(self, url_query, index=0): - """Get the data object for a given tab. - - Args: - url_query: The substring of the URL to search for. - index: The index within the list of matches to return. - - Returns: - The data object for the tab. - """ - windows = self.GetBrowserInfo()['windows'] - i = 0 - for win in windows: - for tab in win['tabs']: - if tab['url'] and url_query in tab['url']: - # Store reference to windex used in _RunInTab. - tab['windex'] = win['index'] - if i == index: - return tab - i = i + 1 - return None - - def _GetExtensionViewInfo(self, url_query, index=0): - """Get the data object for a given extension view. - - Args: - url_query: The substring of the URL to search for. - index: The index within the list of matches to return. - - Returns: - The data object for the tab. - """ - - candidate_views = self._GetMatchingExtensionViews(url_query) - if len(candidate_views) > index: - return candidate_views[index] - return None - - def _GetMatchingExtensionViews(self, url_query): - """Gets the data objects for the extension views matching the url_query. - - Args: - url_query: The substring of the URL to search for. - - Returns: - An array of matching data objects. - """ - extension_views = self.GetBrowserInfo()['extension_views'] - candidate_views = list() - for extension_view in extension_views: - if extension_view['url'] and url_query in extension_view['url']: - candidate_views.append(extension_view['view']) - - # No guarantee on view order, so sort the views to get the correct one for - # a given index. - candidate_views.sort() - return candidate_views - - def _GetInjectedJs(self): - """Get the javascript to inject in the execution environment.""" - if self._injected_js is None: - self._injected_js = open( - os.path.join(os.path.dirname(__file__), 'jsutils.js')).read() - return self._injected_js diff --git a/chrome/test/functional/gtalk/jsutils.js b/chrome/test/functional/gtalk/jsutils.js deleted file mode 100644 index 721a2db..0000000 --- a/chrome/test/functional/gtalk/jsutils.js +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) 2012 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. - -/** - * @fileoverview JS utilities automatically injected by GTalk PyAuto tests. - */ - -/** - * Key codes to use with KeyboardEvent. - */ -$KEYS = { - ENTER: 13, - ESC: 27 -}; - -/** - * The first Chrome extension view with a URL containing the query. - */ -$VIEW = function(query) { - var views = chrome.extension.getViews(); - for (var i = 0; i < views.length; i++) { - var url = views[i].location.href; - if (url && url.indexOf(query) >= 0) { - return views[i]; - } - } - return null; -}; - -/** - * The body element of the given window. - */ -$BODY = function(opt_window) { - return (opt_window || window).document.body; -}; - -/** - * Find the ancestor of the given element with a particular tag name. - */ -$FindByTagName = function(element, tag, index) { - var tagElements = element.getElementsByTagName(tag); - if (index < tagElements.length) { - return tagElements[index]; - } - return null; -}; - -/** - * Find the first ancestor of the given element containing the given text. - */ -$FindByText = function(baseElement, text) { - var allElements = baseElement.getElementsByTagName('*'); - for (var i = 0; i < allElements.length; i++) { - var element = allElements[i]; - if (element.innerText && element.innerText.indexOf(text) >= 0) { - var child = $FindByText(element, text); - return child != null ? child : element; - } - } - return null; -}; - -/** - * Simulate a click on a given element. - */ -$Click = function(element) { - var mouseEvent = element.ownerDocument.createEvent('MouseEvent'); - mouseEvent.initMouseEvent('click', true, true, window, - 1, 0, 0, 0, 0, false, false, false, false, 0, null); - element.dispatchEvent(mouseEvent); - return true; -}; - -/** - * Simulate typing text on a given element. - */ -$Type = function(element, text) { - var keyEvent = element.ownerDocument.createEvent('TextEvent'); - keyEvent.initTextEvent('textInput', true, true, window, text); - element.dispatchEvent(keyEvent); - return true; -}; - -/** - * Simulate pressing a certain key on a given element. - */ -$Press = function(baseElement, keycode, opt_ctrlKey, opt_shiftKey, - opt_altKey, opt_metaKey) { - var sendKeyEvent = function(element, eventType) { - // Unfortuantely, using the typical KeyboardEvent and initKeyboardEvent - // fails in Chrome due to a webkit bug: - // https://bugs.webkit.org/show_bug.cgi?id=16735 - // We employ a workaround of synthesizing a raw 'Event' suggested here: - // http://code.google.com/p/selenium/issues/detail?id=567 - var keyEvent = element.ownerDocument.createEvent('Events'); - keyEvent.ctrlKey = Boolean(opt_ctrlKey); - keyEvent.shiftKey = Boolean(opt_shiftKey); - keyEvent.altKey = Boolean(opt_altKey); - keyEvent.metaKey = Boolean(opt_metaKey); - keyEvent.initEvent(eventType, true, true); - keyEvent.keyCode = keycode; - element.dispatchEvent(keyEvent); - } - sendKeyEvent(baseElement, 'keydown'); - sendKeyEvent(baseElement, 'keypress'); - sendKeyEvent(baseElement, 'keyup'); - return true; -}; diff --git a/chrome/test/functional/gtalk/pyauto_gtalk.py b/chrome/test/functional/gtalk/pyauto_gtalk.py deleted file mode 100755 index 1a54221..0000000 --- a/chrome/test/functional/gtalk/pyauto_gtalk.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Setup for GTalk Pyauto tests.""" - -import os -import sys - - -def _SetupPaths(): - """Setting path to find pyauto_functional.py.""" - gtalk_dir = os.path.abspath(os.path.dirname(__file__)) - sys.path.append(gtalk_dir) - sys.path.append(os.path.normpath(os.path.join(gtalk_dir, os.pardir))) - -_SetupPaths() - - -from pyauto_functional import Main - - -if __name__ == '__main__': - Main() diff --git a/chrome/test/functional/gtalk/test_basic.py b/chrome/test/functional/gtalk/test_basic.py deleted file mode 100755 index e9f96f3..0000000 --- a/chrome/test/functional/gtalk/test_basic.py +++ /dev/null @@ -1,312 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Basic sanity tests for the GTalk extension. - -This module contains the basic set of sanity tests run on the -GTalk extension. -""" - -import logging -import sys -import time -import traceback -import urllib2 -import os - -import gtalk_base_test -import pyauto_gtalk # must preceed pyauto -import pyauto - - -class BasicTest(gtalk_base_test.GTalkBaseTest): - """Test for Google Talk Chrome Extension.""" - - def _OpenRoster(self, gtalk_version): - """Download Talk extension and open the roster.""" - - self.InstallGTalkExtension(gtalk_version) - - # Wait for the background view to load. - extension = self.GetGTalkExtensionInfo() - background_view = self.WaitUntilExtensionViewLoaded( - extension_id=extension['id'], - view_type='EXTENSION_BACKGROUND_PAGE') - self.assertTrue(background_view, - msg='Failed to get background view: views = %s.' % - self.GetBrowserInfo()['extension_views']) - - # Click browser action icon - self.TriggerBrowserActionById(extension['id']) - - # Wait for viewer window to open. - self.assertTrue( - self.WaitUntil(self.GetViewerInfo), - msg='Timed out waiting for viewer.html to open.') - - # Wait for the sign-in iframe to load. - self.WaitUntilCondition( - lambda: self.RunInViewer( - 'window.document.getElementsByTagName("iframe") != null && ' - 'window.document.getElementsByTagName("iframe").length > 0') and - self.RunInViewer('window.location.href', - '//iframe[1]'), - lambda url: url and '/qsignin' in url, - msg='Timed out waiting for /qsignin page.') - - - def _SignIn(self, gtalk_version): - """Download the extension, open the roster, and sign in""" - # Open the roster. - self._OpenRoster(gtalk_version) - - # Wait for /qsignin's BODY. - self.WaitUntilResult(True, - lambda: self.RunInViewer( - 'Boolean($BODY())', '//iframe[1]'), - msg='Timed out waiting for document.body in /qsignin page.') - - # Wait for the "Sign In" link. - self.WaitUntilResult(True, - lambda: self.RunInViewer( - 'Boolean($FindByText($BODY(), "Sign In"))', '//iframe[1]'), - msg='Timed out waiting for "Sign In" link in DOM.') - - # Click the "Sign In" link. - self.assertTrue(self.RunInViewer( - '$Click($FindByText($BODY(), "Sign In"))', '//iframe[1]')) - - # Wait for the login page to open. - self.assertTrue(self.WaitUntil(self.GetLoginPageInfo), - msg='Timed out waiting for login page to open.') - - # Wait for the login page's form element. - self.WaitUntilResult(True, - lambda: self.RunInLoginPage('Boolean(document.forms[0])'), - msg='Timed out waiting for document.forms[0].') - - # Fill and submit the login form. - credentials = self.GetPrivateInfo()['test_google_account'] - - self.RunInLoginPage( - 'document.forms[0].Email.value="' + credentials['username'] + '"') - self.RunInLoginPage( - 'document.forms[0].Passwd.value="' + credentials['password'] + '"') - self.RunInLoginPage('document.forms[0].submit() || true') - - def RunBasicFunctionalityTest(self, gtalk_version): - """Run tests for basic functionality in GTalk.""" - - # Install the extension, open the viewer, and sign in. - self._SignIn(gtalk_version) - - # Wait for the roster container iframe. - self.WaitUntilResult(True, - lambda: self.RunInViewer( - 'window.document.getElementById("popoutRoster") != null'), - msg='Timed out waiting for roster container iframe.') - - self.WaitUntilResult(True, - lambda: self.RunInViewer('Boolean(window.frames[0])', '//iframe[1]'), - msg='Timed out waiting for roster iframe.') - - # Wait for the roster iframe to load. - self.WaitUntilCondition( - lambda: self.RunInRoster('window.location.href'), - lambda url: url and '/frame' in url, - msg='Timed out waiting for /frame in url.') - - self.WaitUntilResult(True, - lambda: self.RunInRoster( - 'Boolean($FindByText($BODY(), "Send a message to..."))'), - msg='Timed out waiting for "Send a message to..." label in roster DOM.') - - # Wait for "chatpinger@appspot.com" to appear in the roster. - self.WaitUntilResult(True, - lambda: self.RunInRoster( - 'Boolean($FindByText($BODY(), "chatpinger@appspot.com"))'), - msg='Timed out waiting for chatpinger@appspot.com in roster DOM.') - - # Works around for issue where mole doesn't open when clicked too quickly. - time.sleep(1) - - # Click "chatpinger@appspot.com" to open a chat mole. - self.RunInRoster('$Click($FindByText($BODY(), "chatpinger@appspot.com"))') - - # Wait until ready to check whether mole is open(temporary work around). - time.sleep(1) - - # Wait for chat mole to open. - self.assertTrue(self.WaitUntil(self.GetMoleInfo), - msg='Timed out waiting for mole window to open.') - - self.WaitUntilResult(True, - lambda: self.RunInViewer( - 'window.document.getElementsByTagName("iframe") != null'), - msg='Timed out waiting for iframes to load.') - - # Wait for chat mole to load. - self.WaitUntilResult(True, - lambda: self.RunInMole('Boolean(window.location.href)'), - msg='Timed out waiting for mole window location.') - - # Wait for the chat mole's input textarea to load. - self.WaitUntilResult(True, - lambda: self.RunInMole( - 'Boolean($FindByTagName($BODY(), "textarea", 0))'), - msg='Timed out waiting for mole textarea.') - - # Type /ping in the mole's input widget. - self.assertTrue(self.RunInMole( - '$Type($FindByTagName($BODY(), "textarea", 0), "/ping")'), - msg='Error typing in mole textarea.') - - # Type ENTER in the mole's input widget. - self.assertTrue(self.RunInMole( - '$Press($FindByTagName($BODY(),"textarea",0), $KEYS.ENTER)'), - msg='Error sending ENTER in mole textarea.') - - # Wait for chat input to clear. - self.WaitUntilResult(True, - lambda: self.RunInMole( - 'Boolean($FindByTagName($BODY(),"textarea",0).value=="")'), - msg='Timed out waiting for textarea to clear after ENTER.') - - # Wait for /ping to appear in the chat history. - self.WaitUntilCondition( - lambda: self.RunInMole('window.document.body.innerHTML'), - lambda html: html and '/ping' in html, - msg='Timed out waiting for /ping to appear in mole DOM.') - - # Wait for the echo "Ping!" to appear in the chat history. - self.WaitUntilCondition( - lambda: self.RunInMole('window.document.body.innerHTML'), - lambda html: html and 'Ping!' in html, - msg='Timed out waiting for "Ping!" reply to appear in mole DOM.') - - # Request a ping in 7 seconds. - self.assertTrue(self.RunInMole( - '$Type($FindByTagName($BODY(),"textarea",0), "/ping 7")'), - msg='Error typing "ping /7" in mole textarea.') - - # Press Enter in chat input. - self.assertTrue(self.RunInMole( - '$Press($FindByTagName($BODY(),"textarea",0), $KEYS.ENTER)'), - msg='Error sending ENTER after "ping /7" in mole textarea.') - - # Briefly show mole for visual examination. - # Also works around issue where extension may show the first - # Ping! notification before closing the mole. - time.sleep(2) - - # Press escape to close the mole. - self.assertTrue(self.RunInMole( - '$Press($FindByTagName($BODY(),"textarea",0), $KEYS.ESC)'), - msg='Error sending ESC after "ping /7" in mole textarea.') - - # Wait for the mole to close. - self.assertTrue(self.WaitUntil( - lambda: not(bool(self.GetMoleInfo()))), - msg='Timed out waiting for chatpinger mole to close.') - - # Ensure "chatpinger2@appspot.com" is in the roster. - self.WaitUntilResult(True, - lambda: self.RunInRoster( - 'Boolean($FindByText($BODY(), "chatpinger2@appspot.com"))'), - msg='Timed out waiting for chatpinger2@appspot.com in roster DOM.') - - # Click "chatpinger2@appspot.com" in the roster. - self.RunInRoster('$Click($FindByText($BODY(), "chatpinger2@appspot.com"))') - - self.WaitUntilResult(True, - lambda: self.RunInViewer( - 'window.document.getElementsByTagName("iframe") != null'), - msg='Timed out waiting for iframes to load.') - - # Wait for a second chat mole to open. - time.sleep(1) - self.assertTrue(self.WaitUntil(lambda: bool(self.GetMoleInfo(1))), - msg='Timed out waiting for second mole window to open.') - - # Wait for mole content to load - self.WaitUntilCondition( - lambda: self.RunInMole('window.document.body.innerHTML', 1), - lambda html: html and 'Ping!' in html, - msg='Timed out waiting for Ping! to appear in mole DOM.') - - # Disable the extension. - extension = self.GetGTalkExtensionInfo() - self.SetExtensionStateById(extension['id'], enable=False, - allow_in_incognito=False) - extension = self.GetGTalkExtensionInfo() - self.assertFalse(extension['is_enabled']) - - # Verify all moles + windows are closed. - self.assertTrue(self.WaitUntil(lambda: not(bool(self.GetViewerInfo()))), - msg='Timed out waiting for viewer.html to close after disabling.') - self.assertTrue(self.WaitUntil(lambda: not(bool(self.GetMoleInfo()))), - msg='Timed out waiting for first mole to close after disabling.') - self.assertTrue(self.WaitUntil(lambda: not(bool(self.GetMoleInfo(1)))), - msg='Timed out waiting for second mole to close after disabling.') - - def _GetCurrentGtalkVersion(self): - """Read current gtalk extension version from file.""" - return self._GetGtalkVersion('current_version') - - def _GetRCGtalkVersion(self): - """Read RC gtalk extension version from file""" - return self._GetGtalkVersion('rc_version') - - def _GetGtalkVersion(self, version_type): - """Read gtalk version from file""" - version_path = os.path.abspath( - os.path.join(self.DataDir(), 'extensions', - 'gtalk', version_type)) - self.assertTrue( - os.path.exists(version_path), - msg='Failed to find version ' + version_path) - with open(version_path) as version_file: - return version_file.read() - - def _TestBasicFunctionality(self, version): - """Run tests for basic functionality in GTalk with retries.""" - - # Since this test goes against prod servers, we'll retry to mitigate - # flakiness due to network issues. - RETRIES = 5 - for tries in range(RETRIES): - logging.info('Calling RunBasicFunctionalityTest on %s. Try #%s/%s' - % (version, tries + 1, RETRIES)) - try: - self.RunBasicFunctionalityTest(version) - logging.info('RunBasicFunctionalityTest on %s succeeded. Tries: %s' - % (version, tries + 1)) - break - except Exception as e: - logging.info("\n*** ERROR in RunBasicFunctionalityTest ***") - exc_type, exc_value, exc_traceback = sys.exc_info() - traceback.print_exception(exc_type, exc_value, exc_traceback) - logging.info("\n") - if tries < RETRIES - 1: - self.NavigateToURL('http://accounts.google.com/Logout') - logging.info('Closing all moles.') - self.RunInAllMoles( - '$Press($FindByTagName($BODY(),"textarea",0), $KEYS.ESC)') - logging.info('Retrying...') - else: - raise - - def testCurrentVersion(self): - """Run basic functionality test on current version of gtalk extension""" - version = self._GetCurrentGtalkVersion() - self._TestBasicFunctionality(version) - - def testRCVersion(self): - """Run basic functionality test on RC version of gtalk extension""" - version = self._GetRCGtalkVersion() - self._TestBasicFunctionality(version) - -if __name__ == '__main__': - pyauto_gtalk.Main() diff --git a/chrome/test/functional/infobars.py b/chrome/test/functional/infobars.py deleted file mode 100755 index 3d896a2..0000000 --- a/chrome/test/functional/infobars.py +++ /dev/null @@ -1,322 +0,0 @@ -#!/usr/bin/env python -# Copyright 2013 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import os -import re - -import pyauto_functional # Must be imported before pyauto -import pyauto -import test_utils - - -class InfobarTest(pyauto.PyUITest): - """TestCase for Infobars.""" - - def Debug(self): - """Test method for experimentation. - - This method will not run automatically. - To run: - python chrome/test/functional/infobars.py infobars.InfobarTest.Debug - """ - while True: - raw_input('Hit <enter> to dump info.. ') - info = self.GetBrowserInfo() - for window in info['windows']: - for tab in window['tabs']: - print 'Window', window['index'], 'tab', tab['index'] - self.pprint(tab['infobars']) - - def setUp(self): - pyauto.PyUITest.setUp(self) - self._flash_plugin_type = 'Plug-in' - if self.GetBrowserInfo()['properties']['branding'] == 'Google Chrome': - self._flash_plugin_type = 'Pepper Plugin' - # Forcibly trigger all plugins to get registered. crbug.com/94123 - # Sometimes flash files loaded too quickly after firing browser - # ends up getting downloaded, which seems to indicate that the plugin - # hasn't been registered yet. - self.GetPluginsInfo() - - def _GetTabInfo(self, windex=0, tab_index=0): - """Helper to return info for the given tab in the given window. - - Defaults to first tab in first window. - """ - return self.GetBrowserInfo()['windows'][windex]['tabs'][tab_index] - - def testPluginCrashInfobar(self): - """Verify the "plugin crashed" infobar.""" - flash_url = self.GetFileURLForContentDataPath('plugin', 'flash.swf') - # Trigger flash plugin - self.NavigateToURL(flash_url) - child_processes = self.GetBrowserInfo()['child_processes'] - flash = [x for x in child_processes if - x['type'] == self._flash_plugin_type and - x['name'] == 'Shockwave Flash'][0] - self.assertTrue(flash) - logging.info('Killing flash plugin. pid %d' % flash['pid']) - self.Kill(flash['pid']) - self.assertTrue(self.WaitForInfobarCount(1)) - crash_infobar = self._GetTabInfo()['infobars'] - self.assertTrue(crash_infobar) - self.assertEqual(1, len(crash_infobar)) - self.assertTrue('crashed' in crash_infobar[0]['text']) - self.assertEqual('confirm_infobar', crash_infobar[0]['type']) - # Dismiss the infobar - self.PerformActionOnInfobar('dismiss', infobar_index=0) - self.assertFalse(self._GetTabInfo()['infobars']) - - def _VerifyGeolocationInfobar(self, windex, tab_index): - """Verify geolocation infobar properties. - - Assumes that geolocation infobar is showing up in the given tab in the - given window. - """ - # TODO(dyu): Remove this helper function when a function to identify - # infobar_type and index of the type is implemented. - tab_info = self._GetTabInfo(windex, tab_index) - geolocation_infobar = tab_info['infobars'] - self.assertTrue(geolocation_infobar) - self.assertEqual(1, len(geolocation_infobar)) - self.assertEqual('Learn more', geolocation_infobar[0]['link_text']) - self.assertEqual(2, len(geolocation_infobar[0]['buttons'])) - self.assertEqual('Allow', geolocation_infobar[0]['buttons'][0]) - self.assertEqual('Deny', geolocation_infobar[0]['buttons'][1]) - - def testGeolocationInfobar(self): - """Verify geoLocation infobar.""" - url = self.GetHttpURLForDataPath('geolocation', 'geolocation_on_load.html') - self.NavigateToURL(url) - self.assertTrue(self.WaitForInfobarCount(1)) - self._VerifyGeolocationInfobar(windex=0, tab_index=0) - # Accept, and verify that the infobar went away - self.PerformActionOnInfobar('accept', infobar_index=0) - self.assertFalse(self._GetTabInfo()['infobars']) - - def testGeolocationInfobarInMultipleTabsAndWindows(self): - """Verify GeoLocation inforbar in multiple tabs.""" - url = self.GetFileURLForDataPath( # triggers geolocation - 'geolocation', 'geolocation_on_load.html') - for tab_index in range(1, 2): - self.AppendTab(pyauto.GURL(url)) - self.assertTrue( - self.WaitForInfobarCount(1, windex=0, tab_index=tab_index)) - self._VerifyGeolocationInfobar(windex=0, tab_index=tab_index) - # Try in a new window - self.OpenNewBrowserWindow(True) - self.NavigateToURL(url, 1, 0) - self.assertTrue(self.WaitForInfobarCount(1, windex=1, tab_index=0)) - self._VerifyGeolocationInfobar(windex=1, tab_index=0) - # Incognito window - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - self.NavigateToURL(url, 2, 0) - self.assertTrue(self.WaitForInfobarCount(1, windex=2, tab_index=0)) - self._VerifyGeolocationInfobar(windex=2, tab_index=0) - - def _GetFlashCrashInfobarCount(self, windex=0, tab_index=0): - """Returns the count of 'Shockwave Flash has crashed' infobars.""" - browser_window = self.GetBrowserInfo()['windows'][windex] - infobars = browser_window['tabs'][tab_index]['infobars'] - flash_crash_infobar_count = 0 - for infobar in infobars: - if (('text' in infobar) and - infobar['text'].startswith('Shockwave Flash has crashed')): - flash_crash_infobar_count += 1 - return flash_crash_infobar_count - - def testPluginCrashForMultiTabs(self): - """Verify plugin crash infobar shows up only on the tabs using plugin.""" - non_flash_url = self.GetFileURLForDataPath('english_page.html') - flash_url = self.GetFileURLForContentDataPath('plugin', 'FlashSpin.swf') - # False = Non flash url, True = Flash url - # We have set of these values to compare a flash page and a non-flash page - urls_type = [False, True, False, True, False] - for _ in range(2): - self.AppendTab(pyauto.GURL(flash_url)) - self.AppendTab(pyauto.GURL(non_flash_url)) - # Killing flash process - child_processes = self.GetBrowserInfo()['child_processes'] - flash = [x for x in child_processes if - x['type'] == self._flash_plugin_type and - x['name'] == 'Shockwave Flash'][0] - self.assertTrue(flash) - self.Kill(flash['pid']) - # Crash plugin infobar should show up in the second tab of this window - # so passing window and tab argument in the wait for an infobar. - self.assertTrue(self.WaitForInfobarCount(1, windex=0, tab_index=1)) - for i in range(len(urls_type)): - # Verify that if page doesn't have flash plugin, - # it should not have infobar popped-up - self.ActivateTab(i) - if not urls_type[i]: - self.assertEqual( - self._GetFlashCrashInfobarCount(0, i), 0, - msg='Did not expect crash infobar in tab at index %d' % i) - elif urls_type[i]: - self.assertEqual( - self._GetFlashCrashInfobarCount(0, i), 1, - msg='Expected crash infobar in tab at index %d' % i) - infobar = self.GetBrowserInfo()['windows'][0]['tabs'][i]['infobars'] - self.assertEqual(infobar[0]['type'], 'confirm_infobar') - self.assertEqual(len(infobar), 1) - - -class OneClickInfobarTest(pyauto.PyUITest): - """Tests for one-click sign in infobar.""" - - BLOCK_COOKIE_PATTERN = {'https://accounts.google.com/': {'cookies': 2}} - OC_INFOBAR_TYPE = 'oneclicklogin_infobar' - PW_INFOBAR_TYPE = 'password_infobar' - URL = 'https://www.google.com/accounts/ServiceLogin' - URL_LOGIN = 'https://www.google.com/accounts/Login' - URL_LOGOUT = 'https://www.google.com/accounts/Logout' - - def setUp(self): - pyauto.PyUITest.setUp(self) - self._driver = self.NewWebDriver() - - def _LogIntoGoogleAccount(self, tab_index=0, windex=0): - """Log into Google account. - - Args: - tab_index: The tab index, default is 0. - windex: The window index, default is 0. - """ - creds = self.GetPrivateInfo()['test_google_account'] - username = creds['username'] - password = creds['password'] - test_utils.GoogleAccountsLogin(self, username, password, tab_index, windex) - # TODO(dyu): Use WaitUntilNavigationCompletes after investigating - # crbug.com/124877 - self.WaitUntil( - lambda: self.GetDOMValue('document.readyState'), - expect_retval='complete') - - def _PerformActionOnInfobar(self, action): - """Perform an action on the infobar: accept, cancel, or dismiss. - - The one-click sign in infobar must show in the first tab of the first - window. If action is 'accept' then the account is synced. If the action is - 'cancel' then the infobar should be dismissed and never shown again. The - account will not be synced. If the action is 'dismiss' then the infobar will - shown again after the next login. - - Args: - action: The action to perform on the infobar. - """ - infobar_index = test_utils.WaitForInfobarTypeAndGetIndex( - self, self.OC_INFOBAR_TYPE) - self.PerformActionOnInfobar(action, infobar_index) - - def _DisplayOneClickInfobar(self, tab_index=0, windex=0): - """One-click sign in infobar appears after logging into google account. - - Args: - tab_index: The tab index, default is 0. - windex: The window index, default is 0. - """ - self._LogIntoGoogleAccount(tab_index=tab_index, windex=windex) - self.assertTrue(self.WaitUntil( - lambda: test_utils.GetInfobarIndexByType( - self, self.OC_INFOBAR_TYPE, - tab_index=tab_index, windex=windex) is not None), - msg='The one-click login infobar did not appear.') - - def testDisplayOneClickInfobar(self): - """Verify one-click infobar appears after login into google account. - - One-click infobar should appear after signing into a google account - for the first time using a clean profile. - """ - self._DisplayOneClickInfobar() - - def testNoOneClickInfobarAfterCancel(self): - """Verify one-click infobar does not appear again after clicking cancel. - - The one-click infobar should not display again after logging into an - account and selecting to reject sync the first time. The test covers - restarting the browser with the same profile and verifying the one-click - infobar does not show after login. - - This test also verifies that the password infobar displays. - """ - self._DisplayOneClickInfobar() - self._PerformActionOnInfobar(action='cancel') # Click 'No thanks' button. - self.NavigateToURL(self.URL_LOGOUT) - self._LogIntoGoogleAccount() - test_utils.WaitForInfobarTypeAndGetIndex(self, self.PW_INFOBAR_TYPE) - test_utils.AssertInfobarTypeDoesNotAppear(self, self.OC_INFOBAR_TYPE) - # Restart browser with the same profile. - self.RestartBrowser(clear_profile=False) - self.NavigateToURL(self.URL_LOGOUT) - self._LogIntoGoogleAccount() - test_utils.AssertInfobarTypeDoesNotAppear(self, self.OC_INFOBAR_TYPE) - - def testDisplayOneClickInfobarAfterDismiss(self): - """Verify one-click infobar appears again after clicking dismiss button. - - The one-click infobar should display again after logging into an - account and clicking to dismiss the infobar the first time. - - This test also verifies that the password infobar does not display. - The one-click infobar should supersede the password infobar. - """ - self._DisplayOneClickInfobar() - self._PerformActionOnInfobar(action='dismiss') # Click 'x' button. - self.NavigateToURL(self.URL_LOGOUT) - self._LogIntoGoogleAccount() - test_utils.WaitForInfobarTypeAndGetIndex(self, self.OC_INFOBAR_TYPE) - test_utils.AssertInfobarTypeDoesNotAppear(self, self.PW_INFOBAR_TYPE) - - def _OpenSecondProfile(self): - """Create a second profile.""" - self.OpenNewBrowserWindowWithNewProfile() - self.assertEqual(2, len(self.GetMultiProfileInfo()['profiles']), - msg='The second profile was not created.') - - def testDisplayOneClickInfobarPerProfile(self): - """Verify one-click infobar appears for each profile after sign-in.""" - # Default profile. - self._DisplayOneClickInfobar() - self._OpenSecondProfile() - self._DisplayOneClickInfobar(windex=1) - - def testNoOneClickInfobarWhenCookiesBlocked(self): - """Verify one-click infobar does not show when cookies are blocked. - - One-click sign in should not be enabled if cookies are blocked for Google - accounts domain. - - This test verifies the following bug: crbug.com/117841 - """ - # Block cookies for Google accounts domain. - self.SetPrefs(pyauto.kContentSettingsPatternPairs, - self.BLOCK_COOKIE_PATTERN) - self._LogIntoGoogleAccount() - test_utils.AssertInfobarTypeDoesNotAppear(self, self.OC_INFOBAR_TYPE) - - def testOneClickInfobarShownWhenWinLoseFocus(self): - """Verify one-click infobar still shows when window loses focus. - - This test verifies the following bug: crbug.com/121739 - """ - self._LogIntoGoogleAccount() - test_utils.WaitForInfobarTypeAndGetIndex(self, self.OC_INFOBAR_TYPE) - # Open new window to shift focus away. - self.OpenNewBrowserWindow(True) - test_utils.GetInfobarIndexByType(self, self.OC_INFOBAR_TYPE) - - def testNoOneClickInfobarInIncognito(self): - """Verify that one-click infobar does not show up in incognito mode.""" - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - self._LogIntoGoogleAccount(windex=1) - test_utils.AssertInfobarTypeDoesNotAppear( - self, self.OC_INFOBAR_TYPE, windex=1) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/ispy/OWNERS b/chrome/test/functional/ispy/OWNERS deleted file mode 100644 index 7fa81ec..0000000 --- a/chrome/test/functional/ispy/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -craigdh@chromium.org -frankf@chromium.org diff --git a/chrome/test/functional/ispy/__init__.py b/chrome/test/functional/ispy/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/chrome/test/functional/ispy/__init__.py +++ /dev/null diff --git a/chrome/test/functional/ispy/app.yaml b/chrome/test/functional/ispy/app.yaml deleted file mode 100644 index 869464f..0000000 --- a/chrome/test/functional/ispy/app.yaml +++ /dev/null @@ -1,17 +0,0 @@ -application: google.com:ispy -version: 1 -runtime: python27 -api_version: 1 -threadsafe: True - -handlers: -- url: /.* - script: server.app.application - -libraries: -- name: webapp2 - version: latest -- name: jinja2 - version: latest -- name: PIL - version: latest diff --git a/chrome/test/functional/ispy/client/__init__.py b/chrome/test/functional/ispy/client/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/chrome/test/functional/ispy/client/__init__.py +++ /dev/null diff --git a/chrome/test/functional/ispy/client/boto_bucket.py b/chrome/test/functional/ispy/client/boto_bucket.py deleted file mode 100644 index 5ea9c97..0000000 --- a/chrome/test/functional/ispy/client/boto_bucket.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright 2013 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. - -"""Implementation of CloudBucket using Google Cloud Storage as the backend.""" -import os -import sys - -# boto requires depot_tools/third_party be in the path. Use -# src/tools/find_depot_tools.py to add this directory. -sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, - os.pardir, os.pardir, os.pardir, 'tools')) -import find_depot_tools -DEPOT_TOOLS_PATH = find_depot_tools.add_depot_tools_to_path() -sys.path.append(os.path.join(os.path.abspath(DEPOT_TOOLS_PATH), 'third_party')) -import boto - -from ..common import cloud_bucket - - -class BotoCloudBucket(cloud_bucket.BaseCloudBucket): - """Interfaces with GS using the boto library.""" - - def __init__(self, key, secret, bucket_name): - """Initializes the bucket with a key, secret, and bucket_name. - - Args: - key: the API key to access GS. - secret: the API secret to access GS. - bucket_name: the name of the bucket to connect to. - """ - uri = boto.storage_uri('', 'gs') - conn = uri.connect(key, secret) - self.bucket = conn.get_bucket(bucket_name) - - def _GetKey(self, path): - key = boto.gs.key.Key(self.bucket) - key.key = path - return key - - # override - def UploadFile(self, path, contents, content_type): - key = self._GetKey(path) - key.set_metadata('Content-Type', content_type) - key.set_contents_from_string(contents) - # Open permissions for the appengine account to read/write. - key.add_email_grant('FULL_CONTROL', - 'ispy.google.com@appspot.gserviceaccount.com') - - # override - def DownloadFile(self, path): - key = self._GetKey(path) - if key.exists(): - return key.get_contents_as_string() - else: - raise cloud_bucket.FileNotFoundError - - # override - def UpdateFile(self, path, contents): - key = self._GetKey(path) - if key.exists(): - key.set_contents_from_string(contents) - else: - raise cloud_bucket.FileNotFoundError - - # override - def RemoveFile(self, path): - key = self._GetKey(path) - key.delete() - - # override - def FileExists(self, path): - key = self._GetKey(path) - return key.exists() - - # override - def GetImageURL(self, path): - key = self._GetKey(path) - if key.exists(): - # Corrects a bug in boto that incorrectly generates a url - # to a resource in Google Cloud Storage. - return key.generate_url(3600).replace('AWSAccessKeyId', 'GoogleAccessId') - else: - raise cloud_bucket.FileNotFoundError(path) - - # override - def GetAllPaths(self, prefix): - return (key.key for key in self.bucket.get_all_keys(prefix=prefix)) diff --git a/chrome/test/functional/ispy/client/dom.py b/chrome/test/functional/ispy/client/dom.py deleted file mode 100644 index facac13..0000000 --- a/chrome/test/functional/ispy/client/dom.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2014 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. - - -def GetScriptToWaitForUnchangingDOM(): - """Gets Javascript that waits until the DOM is stable for 5 seconds. - - Times out if the DOM is not stable within 30 seconds. - - Returns: - Javascript as a string. - """ - return """ - var target = document.body; - var callback = arguments[arguments.length - 1] - - var timeout_id = setTimeout(function() { - callback() - }, 5000); - - var observer = new MutationObserver(function(mutations) { - clearTimeout(timeout_id); - timeout_id = setTimeout(function() { - callback(); - }, 5000); - }).observe(target, {attributes: true, childList: true, - characterData: true, subtree: true}); - """ diff --git a/chrome/test/functional/ispy/client/wait_on_ajax.js b/chrome/test/functional/ispy/client/wait_on_ajax.js deleted file mode 100644 index da1fce9..0000000 --- a/chrome/test/functional/ispy/client/wait_on_ajax.js +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2013 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. - -var target = document.body; -var callback = arguments[arguments.length - 1] - -var timeout_id = setTimeout(function() { - callback() -}, 5000); - -var observer = new MutationObserver(function(mutations) { - clearTimeout(timeout_id); - timeout_id = setTimeout(function() { - callback(); - }, 5000); -}).observe(target, {attributes: true, childList: true, - characterData: true, subtree: true}); diff --git a/chrome/test/functional/ispy/common/__init__.py b/chrome/test/functional/ispy/common/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/chrome/test/functional/ispy/common/__init__.py +++ /dev/null diff --git a/chrome/test/functional/ispy/common/cloud_bucket.py b/chrome/test/functional/ispy/common/cloud_bucket.py deleted file mode 100644 index 39134c9..0000000 --- a/chrome/test/functional/ispy/common/cloud_bucket.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright 2013 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. - -"""Abstract injector class for GS requests.""" - - -class FileNotFoundError(Exception): - """Thrown by a subclass of CloudBucket when a file is not found.""" - pass - - -class BaseCloudBucket(object): - """An abstract base class for working with GS.""" - - def UploadFile(self, path, contents, content_type): - """Uploads a file to GS. - - Args: - path: where in GS to upload the file. - contents: the contents of the file to be uploaded. - content_type: the MIME Content-Type of the file. - """ - raise NotImplementedError - - def DownloadFile(self, path): - """Downsloads a file from GS. - - Args: - path: the location in GS to download the file from. - - Returns: - String contents of the file downloaded. - - Raises: - bucket_injector.NotFoundException: if the file is not found. - """ - raise NotImplementedError - - def UpdateFile(self, path, contents): - """Uploads a file to GS. - - Args: - path: location of the file in GS to update. - contents: the contents of the file to be updated. - """ - raise NotImplementedError - - def RemoveFile(self, path): - """Removes a file from GS. - - Args: - path: the location in GS to download the file from. - """ - raise NotImplementedError - - def FileExists(self, path): - """Checks if a file exists in GS. - - Args: - path: the location in GS of the file. - - Returns: - boolean representing whether the file exists in GS. - """ - raise NotImplementedError - - def GetImageURL(self, path): - """Gets a URL to an item in GS from its path. - - Args: - path: the location in GS of a file. - - Returns: - an url to a file in GS. - - Raises: - bucket_injector.NotFoundException: if the file is not found. - """ - raise NotImplementedError - - def GetAllPaths(self, prefix): - """Gets paths to files in GS that start with a prefix. - - Args: - prefix: the prefix to filter files in GS. - - Returns: - a generator of paths to files in GS. - """ - raise NotImplementedError diff --git a/chrome/test/functional/ispy/common/constants.py b/chrome/test/functional/ispy/common/constants.py deleted file mode 100644 index a8b1956..0000000 --- a/chrome/test/functional/ispy/common/constants.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright 2013 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. - -"""Constants for I-Spy.""" - -BUCKET = 'ispy-bucket' diff --git a/chrome/test/functional/ispy/common/image_tools.py b/chrome/test/functional/ispy/common/image_tools.py deleted file mode 100644 index aaa0748..0000000 --- a/chrome/test/functional/ispy/common/image_tools.py +++ /dev/null @@ -1,322 +0,0 @@ -# Copyright 2013 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. - -"""Utilities for performing pixel-by-pixel image comparision.""" - -import itertools -import StringIO -from PIL import Image - - -def _AreTheSameSize(images): - """Returns whether a set of images are the size size. - - Args: - images: a list of images to compare. - - Returns: - boolean. - - Raises: - Exception: One image or fewer is passed in. - """ - if len(images) > 1: - return all(images[0].size == img.size for img in images[1:]) - else: - raise Exception('No images passed in.') - - -def _GetDifferenceWithMask(image1, image2, mask=None, - masked_color=(225, 225, 225, 255), - same_color=(255, 255, 255, 255), - different_color=(210, 0, 0, 255)): - """Returns an image representing the difference between the two images. - - This function computes the difference between two images taking into - account a mask if it is provided. The final three arguments represent - the coloration of the generated image. - - Args: - image1: the first image to compare. - image2: the second image to compare. - mask: an optional mask image consisting of only black and white pixels - where white pixels indicate the portion of the image to be masked out. - masked_color: the color of a masked section in the resulting image. - same_color: the color of an unmasked section that is the same. - between images 1 and 2 in the resulting image. - different_color: the color of an unmasked section that is different - between images 1 and 2 in the resulting image. - - Returns: - A 2-tuple with an image representing the unmasked difference between the - two input images and the number of different pixels. - - Raises: - Exception: if image1, image2, and mask are not the same size. - """ - image_mask = mask - if not mask: - image_mask = Image.new('RGBA', image1.size, (0, 0, 0, 255)) - if not _AreTheSameSize([image1, image2, image_mask]): - raise Exception('images and mask must be the same size.') - image_diff = Image.new('RGBA', image1.size, (0, 0, 0, 255)) - data = [] - diff_pixels = 0 - for m, px1, px2 in itertools.izip(image_mask.getdata(), - image1.getdata(), - image2.getdata()): - if m == (255, 255, 255, 255): - data.append(masked_color) - elif px1 == px2: - data.append(same_color) - else: - data.append(different_color) - diff_pixels += 1 - - image_diff.putdata(data) - return (image_diff, diff_pixels) - - -def CreateMask(images): - """Computes a mask for a set of images. - - Returns a difference mask that is computed from the images - which are passed in. The mask will have a white pixel - anywhere that the input images differ and a black pixel - everywhere else. - - Args: - images: list of images to compute the mask from. - - Returns: - an image of only black and white pixels where white pixels represent - areas in the input images that have differences. - - Raises: - Exception: if the images passed in are not of the same size. - Exception: if fewer than one image is passed in. - """ - if not images: - raise Exception('mask must be created from one or more images.') - mask = Image.new('RGBA', images[0].size, (0, 0, 0, 255)) - image = images[0] - for other_image in images[1:]: - mask = _GetDifferenceWithMask( - image, - other_image, - mask, - masked_color=(255, 255, 255, 255), - same_color=(0, 0, 0, 255), - different_color=(255, 255, 255, 255))[0] - return mask - - -def AddMasks(masks): - """Combines a list of mask images into one mask image. - - Args: - masks: a list of mask-images. - - Returns: - a new mask that represents the sum of the masked - regions of the passed in list of mask-images. - - Raises: - Exception: if masks is an empty list, or if masks are not the same size. - """ - if not masks: - raise Exception('masks must be a list containing at least one image.') - if len(masks) > 1 and not _AreTheSameSize(masks): - raise Exception('masks in list must be of the same size.') - white = (255, 255, 255, 255) - black = (0, 0, 0, 255) - masks_data = [mask.getdata() for mask in masks] - image = Image.new('RGBA', masks[0].size, black) - image.putdata([white if white in px_set else black - for px_set in itertools.izip(*masks_data)]) - return image - - -def ConvertDiffToMask(diff): - """Converts a Diff image into a Mask image. - - Args: - diff: the diff image to convert. - - Returns: - a new mask image where everything that was masked or different in the diff - is now masked. - """ - white = (255, 255, 255, 255) - black = (0, 0, 0, 255) - diff_data = diff.getdata() - image = Image.new('RGBA', diff.size, black) - image.putdata([black if px == white else white for px in diff_data]) - return image - - -def VisualizeImageDifferences(image1, image2, mask=None): - """Returns an image repesenting the unmasked differences between two images. - - Iterates through the pixel values of two images and an optional - mask. If the pixel values are the same, or the pixel is masked, - (0,0,0) is stored for that pixel. Otherwise, (255,255,255) is stored. - This ultimately produces an image where unmasked differences between - the two images are white pixels, and everything else is black. - - Args: - image1: an RGB image - image2: another RGB image of the same size as image1. - mask: an optional RGB image consisting of only white and black pixels - where the white pixels represent the parts of the images to be masked - out. - - Returns: - A 2-tuple with an image representing the unmasked difference between the - two input images and the number of different pixels. - - Raises: - Exception: if the two images and optional mask are different sizes. - """ - return _GetDifferenceWithMask(image1, image2, mask) - - -def InflateMask(image, passes): - """A function that adds layers of pixels around the white edges of a mask. - - This function evaluates a 'frontier' of valid pixels indices. Initially, - this frontier contains all indices in the image. However, with each pass - only the pixels' indices which were added to the mask by inflation - are added to the next pass's frontier. This gives the algorithm a - large upfront cost that scales negligably when the number of passes - is increased. - - Args: - image: the RGBA PIL.Image mask to inflate. - passes: the number of passes to inflate the image by. - - Returns: - A RGBA PIL.Image. - """ - inflated = Image.new('RGBA', image.size) - new_dataset = list(image.getdata()) - old_dataset = list(image.getdata()) - - frontier = set(range(len(old_dataset))) - new_frontier = set() - - l = [-1, 1] - - def _ShadeHorizontal(index, px): - col = index % image.size[0] - if px == (255, 255, 255, 255): - for x in l: - if 0 <= col + x < image.size[0]: - if old_dataset[index + x] != (255, 255, 255, 255): - new_frontier.add(index + x) - new_dataset[index + x] = (255, 255, 255, 255) - - def _ShadeVertical(index, px): - row = index / image.size[0] - if px == (255, 255, 255, 255): - for x in l: - if 0 <= row + x < image.size[1]: - if old_dataset[index + image.size[0] * x] != (255, 255, 255, 255): - new_frontier.add(index + image.size[0] * x) - new_dataset[index + image.size[0] * x] = (255, 255, 255, 255) - - for _ in range(passes): - for index in frontier: - _ShadeHorizontal(index, old_dataset[index]) - _ShadeVertical(index, old_dataset[index]) - old_dataset, new_dataset = new_dataset, new_dataset - frontier, new_frontier = new_frontier, set() - inflated.putdata(new_dataset) - return inflated - - -def TotalDifferentPixels(image1, image2, mask=None): - """Computes the number of different pixels between two images. - - Args: - image1: the first RGB image to be compared. - image2: the second RGB image to be compared. - mask: an optional RGB image of only black and white pixels - where white pixels indicate the parts of the image to be masked out. - - Returns: - the number of differing pixels between the images. - - Raises: - Exception: if the images to be compared and the mask are not the same size. - """ - image_mask = mask - if not mask: - image_mask = Image.new('RGBA', image1.size, (0, 0, 0, 255)) - if _AreTheSameSize([image1, image2, image_mask]): - total_diff = 0 - for px1, px2, m in itertools.izip(image1.getdata(), - image2.getdata(), - image_mask.getdata()): - if m == (255, 255, 255, 255): - continue - elif px1 != px2: - total_diff += 1 - else: - continue - return total_diff - else: - raise Exception('images and mask must be the same size') - - -def SameImage(image1, image2, mask=None): - """Returns a boolean representing whether the images are the same. - - Returns a boolean indicating whether two images are similar - enough to be considered the same. Essentially wraps the - TotalDifferentPixels function. - - - Args: - image1: an RGB image to compare. - image2: an RGB image to compare. - mask: an optional image of only black and white pixels - where white pixels are masked out - - Returns: - True if the images are similar, False otherwise. - - Raises: - Exception: if the images (and mask) are different sizes. - """ - different_pixels = TotalDifferentPixels(image1, image2, mask) - return different_pixels == 0 - - -def EncodePNG(image): - """Returns the PNG file-contents of the image. - - Args: - image: an RGB image to be encoded. - - Returns: - a base64 encoded string representing the image. - """ - f = StringIO.StringIO() - image.save(f, 'PNG') - encoded_image = f.getvalue() - f.close() - return encoded_image - - -def DecodePNG(png): - """Returns a RGB image from PNG file-contents. - - Args: - encoded_image: PNG file-contents of an RGB image. - - Returns: - an RGB image - """ - return Image.open(StringIO.StringIO(png)) diff --git a/chrome/test/functional/ispy/common/image_tools_unittest.py b/chrome/test/functional/ispy/common/image_tools_unittest.py deleted file mode 100644 index 017c172..0000000 --- a/chrome/test/functional/ispy/common/image_tools_unittest.py +++ /dev/null @@ -1,183 +0,0 @@ -# Copyright 2013 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import unittest -import sys -import os -from PIL import Image - -import image_tools - - -def _GenImage(size, color): - return Image.new('RGBA', size, color) - - -def _AllPixelsOfColor(image, color): - return not any(px != color for px in image.getdata()) - - -class ImageToolsTest(unittest.TestCase): - - def setUp(self): - self.black25 = _GenImage((25, 25), (0, 0, 0, 255)) - self.black50 = _GenImage((50, 50), (0, 0, 0, 255)) - self.white25 = _GenImage((25, 25), (255, 255, 255, 255)) - self.white50 = _GenImage((50, 50), (255, 255, 255, 255)) - - def testAreTheSameSize(self): - self.assertTrue(image_tools._AreTheSameSize([self.black25, self.black25])) - self.assertTrue(image_tools._AreTheSameSize([self.white25, self.white25])) - self.assertTrue(image_tools._AreTheSameSize([self.black50, self.black50])) - self.assertTrue(image_tools._AreTheSameSize([self.white50, self.white50])) - self.assertTrue(image_tools._AreTheSameSize([self.black25, self.white25])) - self.assertTrue(image_tools._AreTheSameSize([self.black50, self.white50])) - - self.assertFalse(image_tools._AreTheSameSize([self.black50, self.black25])) - self.assertFalse(image_tools._AreTheSameSize([self.white50, self.white25])) - self.assertFalse(image_tools._AreTheSameSize([self.black25, self.white50])) - self.assertFalse(image_tools._AreTheSameSize([self.black50, self.white25])) - - self.assertRaises(Exception, image_tools._AreTheSameSize, []) - self.assertRaises(Exception, image_tools._AreTheSameSize, [self.black50]) - - def testGetDifferenceWithMask(self): - self.assertTrue(_AllPixelsOfColor(image_tools._GetDifferenceWithMask( - self.black25, self.black25)[0], (255, 255, 255, 255))) - self.assertTrue(_AllPixelsOfColor(image_tools._GetDifferenceWithMask( - self.white25, self.black25)[0], (210, 0, 0, 255))) - self.assertTrue(_AllPixelsOfColor(image_tools._GetDifferenceWithMask( - self.black25, self.black25, mask=self.black25)[0], - (255, 255, 255, 255))) - self.assertTrue(_AllPixelsOfColor(image_tools._GetDifferenceWithMask( - self.black25, self.black25, mask=self.white25)[0], - (225, 225, 225, 255))) - self.assertTrue(_AllPixelsOfColor(image_tools._GetDifferenceWithMask( - self.black25, self.white25, mask=self.black25)[0], - (210, 0, 0, 255))) - self.assertTrue(_AllPixelsOfColor(image_tools._GetDifferenceWithMask( - self.black25, self.white25, mask=self.white25)[0], - (225, 225, 225, 255))) - self.assertRaises(Exception, image_tools._GetDifferenceWithMask, - self.white25, - self.black50) - self.assertRaises(Exception, image_tools._GetDifferenceWithMask, - self.white25, - self.white25, - mask=self.black50) - - def testCreateMask(self): - m1 = image_tools.CreateMask([self.black25, self.white25]) - self.assertTrue(_AllPixelsOfColor(m1, (255, 255, 255, 255))) - m2 = image_tools.CreateMask([self.black25, self.black25]) - self.assertTrue(_AllPixelsOfColor(m2, (0, 0, 0, 255))) - m3 = image_tools.CreateMask([self.white25, self.white25]) - self.assertTrue(_AllPixelsOfColor(m3, (0, 0, 0, 255))) - - def testAddMasks(self): - m1 = image_tools.CreateMask([self.black25, self.white25]) - m2 = image_tools.CreateMask([self.black25, self.black25]) - m3 = image_tools.CreateMask([self.black50, self.black50]) - self.assertTrue(_AllPixelsOfColor(image_tools.AddMasks([m1]), - (255, 255, 255, 255))) - self.assertTrue(_AllPixelsOfColor(image_tools.AddMasks([m2]), - (0, 0, 0, 255))) - self.assertTrue(_AllPixelsOfColor(image_tools.AddMasks([m1, m2]), - (255, 255, 255, 255))) - self.assertTrue(_AllPixelsOfColor(image_tools.AddMasks([m1, m1]), - (255, 255, 255, 255))) - self.assertTrue(_AllPixelsOfColor(image_tools.AddMasks([m2, m2]), - (0, 0, 0, 255))) - self.assertTrue(_AllPixelsOfColor(image_tools.AddMasks([m3]), - (0, 0, 0, 255))) - self.assertRaises(Exception, image_tools.AddMasks, []) - self.assertRaises(Exception, image_tools.AddMasks, [m1, m3]) - - def testTotalDifferentPixels(self): - self.assertEquals(image_tools.TotalDifferentPixels(self.white25, - self.white25), - 0) - self.assertEquals(image_tools.TotalDifferentPixels(self.black25, - self.black25), - 0) - self.assertEquals(image_tools.TotalDifferentPixels(self.white25, - self.black25), - 25*25) - self.assertEquals(image_tools.TotalDifferentPixels(self.white25, - self.black25, - mask=self.white25), - 0) - self.assertEquals(image_tools.TotalDifferentPixels(self.white25, - self.white25, - mask=self.white25), - 0) - self.assertEquals(image_tools.TotalDifferentPixels(self.white25, - self.black25, - mask=self.black25), - 25*25) - self.assertEquals(image_tools.TotalDifferentPixels(self.white25, - self.white25, - mask=self.black25), - 0) - self.assertRaises(Exception, image_tools.TotalDifferentPixels, - self.white25, self.white50) - self.assertRaises(Exception, image_tools.TotalDifferentPixels, - self.white25, self.white25, mask=self.white50) - - def testSameImage(self): - self.assertTrue(image_tools.SameImage(self.white25, self.white25)) - self.assertFalse(image_tools.SameImage(self.white25, self.black25)) - - self.assertTrue(image_tools.SameImage(self.white25, self.black25, - mask=self.white25)) - self.assertFalse(image_tools.SameImage(self.white25, self.black25, - mask=self.black25)) - self.assertTrue(image_tools.SameImage(self.black25, self.black25)) - self.assertTrue(image_tools.SameImage(self.black25, self.black25, - mask=self.white25)) - self.assertTrue(image_tools.SameImage(self.white25, self.white25, - mask=self.white25)) - self.assertRaises(Exception, image_tools.SameImage, - self.white25, self.white50) - self.assertRaises(Exception, image_tools.SameImage, - self.white25, self.white25, - mask=self.white50) - - def testInflateMask(self): - cross_image = Image.new('RGBA', (3, 3)) - white_image = Image.new('RGBA', (3, 3)) - dot_image = Image.new('RGBA', (3, 3)) - b = (0, 0, 0, 255) - w = (255, 255, 255, 255) - dot_image.putdata([b, b, b, - b, w, b, - b, b, b]) - cross_image.putdata([b, w, b, - w, w, w, - b, w, b]) - white_image.putdata([w, w, w, - w, w, w, - w, w, w]) - self.assertEquals(list(image_tools.InflateMask(dot_image, 1).getdata()), - list(cross_image.getdata())) - self.assertEquals(list(image_tools.InflateMask(dot_image, 0).getdata()), - list(dot_image.getdata())) - self.assertEquals(list(image_tools.InflateMask(dot_image, 2).getdata()), - list(white_image.getdata())) - self.assertEquals(list(image_tools.InflateMask(dot_image, 3).getdata()), - list(white_image.getdata())) - self.assertEquals(list(image_tools.InflateMask(self.black25, 1).getdata()), - list(self.black25.getdata())) - - def testPNGEncodeDecode(self): - self.assertTrue(_AllPixelsOfColor( - image_tools.DecodePNG( - image_tools.EncodePNG(self.white25)), (255, 255, 255, 255))) - self.assertTrue(_AllPixelsOfColor( - image_tools.DecodePNG( - image_tools.EncodePNG(self.black25)), (0, 0, 0, 255))) - - -if __name__ == '__main__': - unittest.main() diff --git a/chrome/test/functional/ispy/common/ispy_utils.py b/chrome/test/functional/ispy/common/ispy_utils.py deleted file mode 100644 index a138f07..0000000 --- a/chrome/test/functional/ispy/common/ispy_utils.py +++ /dev/null @@ -1,304 +0,0 @@ -# Copyright 2013 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. - -"""Internal utilities for managing I-Spy test results in Google Cloud Storage. - -See the ispy.ispy_api module for the external API. -""" - -import collections -import itertools -import json -import os -import sys - -import image_tools - - -_INVALID_EXPECTATION_CHARS = ['/', '\\', ' ', '"', '\''] - - -def IsValidExpectationName(expectation_name): - return not any(c in _INVALID_EXPECTATION_CHARS for c in expectation_name) - - -def GetExpectationPath(expectation, file_name=''): - """Get the path to a test file in the given test run and expectation. - - Args: - expectation: name of the expectation. - file_name: name of the file. - - Returns: - the path as a string relative to the bucket. - """ - return 'expectations/%s/%s' % (expectation, file_name) - - -def GetFailurePath(test_run, expectation, file_name=''): - """Get the path to a failure file in the given test run and test. - - Args: - test_run: name of the test run. - expectation: name of the expectation. - file_name: name of the file. - - Returns: - the path as a string relative to the bucket. - """ - return GetTestRunPath(test_run, '%s/%s' % (expectation, file_name)) - - -def GetTestRunPath(test_run, file_name=''): - """Get the path to a the given test run. - - Args: - test_run: name of the test run. - file_name: name of the file. - - Returns: - the path as a string relative to the bucket. - """ - return 'failures/%s/%s' % (test_run, file_name) - - -class ISpyUtils(object): - """Utility functions for working with an I-Spy google storage bucket.""" - - def __init__(self, cloud_bucket): - """Initialize with a cloud bucket instance to supply GS functionality. - - Args: - cloud_bucket: An object implementing the cloud_bucket.BaseCloudBucket - interface. - """ - self.cloud_bucket = cloud_bucket - - def UploadImage(self, full_path, image): - """Uploads an image to a location in GS. - - Args: - full_path: the path to the file in GS including the file extension. - image: a RGB PIL.Image to be uploaded. - """ - self.cloud_bucket.UploadFile( - full_path, image_tools.EncodePNG(image), 'image/png') - - def DownloadImage(self, full_path): - """Downloads an image from a location in GS. - - Args: - full_path: the path to the file in GS including the file extension. - - Returns: - The downloaded RGB PIL.Image. - - Raises: - cloud_bucket.NotFoundError: if the path to the image is not valid. - """ - return image_tools.DecodePNG(self.cloud_bucket.DownloadFile(full_path)) - - def UpdateImage(self, full_path, image): - """Updates an existing image in GS, preserving permissions and metadata. - - Args: - full_path: the path to the file in GS including the file extension. - image: a RGB PIL.Image. - """ - self.cloud_bucket.UpdateFile(full_path, image_tools.EncodePNG(image)) - - def GenerateExpectation(self, expectation, images): - """Creates and uploads an expectation to GS from a set of images and name. - - This method generates a mask from the uploaded images, then - uploads the mask and first of the images to GS as a expectation. - - Args: - expectation: name for this expectation, any existing expectation with the - name will be replaced. - images: a list of RGB encoded PIL.Images - - Raises: - ValueError: if the expectation name is invalid. - """ - if not IsValidExpectationName(expectation): - raise ValueError("Expectation name contains an illegal character: %s." % - str(_INVALID_EXPECTATION_CHARS)) - - mask = image_tools.InflateMask(image_tools.CreateMask(images), 7) - self.UploadImage( - GetExpectationPath(expectation, 'expected.png'), images[0]) - self.UploadImage(GetExpectationPath(expectation, 'mask.png'), mask) - - def PerformComparison(self, test_run, expectation, actual): - """Runs an image comparison, and uploads discrepancies to GS. - - Args: - test_run: the name of the test_run. - expectation: the name of the expectation to use for comparison. - actual: an RGB-encoded PIL.Image that is the actual result. - - Raises: - cloud_bucket.NotFoundError: if the given expectation is not found. - ValueError: if the expectation name is invalid. - """ - if not IsValidExpectationName(expectation): - raise ValueError("Expectation name contains an illegal character: %s." % - str(_INVALID_EXPECTATION_CHARS)) - - expectation_tuple = self.GetExpectation(expectation) - if not image_tools.SameImage( - actual, expectation_tuple.expected, mask=expectation_tuple.mask): - self.UploadImage( - GetFailurePath(test_run, expectation, 'actual.png'), actual) - diff, diff_pxls = image_tools.VisualizeImageDifferences( - expectation_tuple.expected, actual, mask=expectation_tuple.mask) - self.UploadImage(GetFailurePath(test_run, expectation, 'diff.png'), diff) - self.cloud_bucket.UploadFile( - GetFailurePath(test_run, expectation, 'info.txt'), - json.dumps({ - 'different_pixels': diff_pxls, - 'fraction_different': - diff_pxls / float(actual.size[0] * actual.size[1])}), - 'application/json') - - def GetExpectation(self, expectation): - """Returns the given expectation from GS. - - Args: - expectation: the name of the expectation to get. - - Returns: - A named tuple: 'Expectation', containing two images: expected and mask. - - Raises: - cloud_bucket.NotFoundError: if the test is not found in GS. - """ - Expectation = collections.namedtuple('Expectation', ['expected', 'mask']) - return Expectation(self.DownloadImage(GetExpectationPath(expectation, - 'expected.png')), - self.DownloadImage(GetExpectationPath(expectation, - 'mask.png'))) - - def ExpectationExists(self, expectation): - """Returns whether the given expectation exists in GS. - - Args: - expectation: the name of the expectation to check. - - Returns: - A boolean indicating whether the test exists. - """ - expected_image_exists = self.cloud_bucket.FileExists( - GetExpectationPath(expectation, 'expected.png')) - mask_image_exists = self.cloud_bucket.FileExists( - GetExpectationPath(expectation, 'mask.png')) - return expected_image_exists and mask_image_exists - - def FailureExists(self, test_run, expectation): - """Returns whether a failure for the expectation exists for the given run. - - Args: - test_run: the name of the test_run. - expectation: the name of the expectation that failed. - - Returns: - A boolean indicating whether the failure exists. - """ - actual_image_exists = self.cloud_bucket.FileExists( - GetFailurePath(test_run, expectation, 'actual.png')) - test_exists = self.ExpectationExists(expectation) - info_exists = self.cloud_bucket.FileExists( - GetFailurePath(test_run, expectation, 'info.txt')) - return test_exists and actual_image_exists and info_exists - - def RemoveExpectation(self, expectation): - """Removes an expectation and all associated failures with that test. - - Args: - expectation: the name of the expectation to remove. - """ - test_paths = self.cloud_bucket.GetAllPaths( - GetExpectationPath(expectation)) - for path in test_paths: - self.cloud_bucket.RemoveFile(path) - - def GenerateExpectationPinkOut(self, expectation, images, pint_out, rgb): - """Uploads an ispy-test to GS with the pink_out workaround. - - Args: - expectation: the name of the expectation to be uploaded. - images: a json encoded list of base64 encoded png images. - pink_out: an image. - RGB: a json list representing the RGB values of a color to mask out. - - Raises: - ValueError: if expectation name is invalid. - """ - if not IsValidExpectationName(expectation): - raise ValueError("Expectation name contains an illegal character: %s." % - str(_INVALID_EXPECTATION_CHARS)) - - # convert the pink_out into a mask - black = (0, 0, 0, 255) - white = (255, 255, 255, 255) - pink_out.putdata( - [black if px == (rgb[0], rgb[1], rgb[2], 255) else white - for px in pink_out.getdata()]) - mask = image_tools.CreateMask(images) - mask = image_tools.InflateMask(image_tools.CreateMask(images), 7) - combined_mask = image_tools.AddMasks([mask, pink_out]) - self.UploadImage(GetExpectationPath(expectation, 'expected.png'), images[0]) - self.UploadImage(GetExpectationPath(expectation, 'mask.png'), combined_mask) - - def RemoveFailure(self, test_run, expectation): - """Removes a failure from GS. - - Args: - test_run: the name of the test_run. - expectation: the expectation on which the failure to be removed occured. - """ - failure_paths = self.cloud_bucket.GetAllPaths( - GetFailurePath(test_run, expectation)) - for path in failure_paths: - self.cloud_bucket.RemoveFile(path) - - def GetFailure(self, test_run, expectation): - """Returns a given test failure's expected, diff, and actual images. - - Args: - test_run: the name of the test_run. - expectation: the name of the expectation the result corresponds to. - - Returns: - A named tuple: Failure containing three images: expected, diff, and - actual. - - Raises: - cloud_bucket.NotFoundError: if the result is not found in GS. - """ - expected = self.DownloadImage( - GetExpectationPath(expectation, 'expected.png')) - actual = self.DownloadImage( - GetFailurePath(test_run, expectation, 'actual.png')) - diff = self.DownloadImage( - GetFailurePath(test_run, expectation, 'diff.png')) - info = json.loads(self.cloud_bucket.DownloadFile( - GetFailurePath(test_run, expectation, 'info.txt'))) - Failure = collections.namedtuple( - 'Failure', ['expected', 'diff', 'actual', 'info']) - return Failure(expected, diff, actual, info) - - def GetAllPaths(self, prefix): - """Gets urls to all files in GS whose path starts with a given prefix. - - Args: - prefix: the prefix to filter files in GS by. - - Returns: - a list containing urls to all objects that started with - the prefix. - """ - return self.cloud_bucket.GetAllPaths(prefix) - diff --git a/chrome/test/functional/ispy/common/ispy_utils_unittest.py b/chrome/test/functional/ispy/common/ispy_utils_unittest.py deleted file mode 100644 index 2b55c2c..0000000 --- a/chrome/test/functional/ispy/common/ispy_utils_unittest.py +++ /dev/null @@ -1,207 +0,0 @@ -# Copyright 2013 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import os -from PIL import Image -import sys -import unittest - -import cloud_bucket -import image_tools -import ispy_utils -import mock_cloud_bucket - - -class ISpyUtilsUnitTest(unittest.TestCase): - - def setUp(self): - # Set up structures that will be reused throughout testing. - self.bucket = mock_cloud_bucket.MockCloudBucket() - self.ispy_utils = ispy_utils.ISpyUtils(self.bucket) - self.white = Image.new('RGBA', (25, 25), (255, 255, 255, 255)) - self.red = Image.new('RGBA', (25, 25), (255, 0, 0, 255)) - self.black = Image.new('RGBA', (25, 25), (0, 0, 0, 255)) - self.masked = Image.new('RGBA', (25, 25), (210, 0, 0, 255)) - - def testUploadImage(self): - self.bucket.Reset() - # Upload some images to the datastore. - self.ispy_utils.UploadImage('path/to/white.png', self.white) - self.ispy_utils.UploadImage('path/to/black.png', self.black) - self.ispy_utils.UploadImage('path/to/red.png', self.red) - # Confirm that the images actually got uploaded. - self.assertEquals(self.bucket.datastore['path/to/white.png'], - image_tools.EncodePNG(self.white)) - self.assertEquals(self.bucket.datastore['path/to/black.png'], - image_tools.EncodePNG(self.black)) - self.assertEquals(self.bucket.datastore['path/to/red.png'], - image_tools.EncodePNG(self.red)) - - def testDownloadImage(self): - self.bucket.Reset() - # Upload some images to the datastore. - self.ispy_utils.UploadImage('path/to/white.png', self.white) - self.ispy_utils.UploadImage('path/to/black.png', self.black) - self.ispy_utils.UploadImage('path/to/red.png', self.red) - # Check that the DownloadImage function gets the correct images. - self.assertEquals( - image_tools.EncodePNG( - self.ispy_utils.DownloadImage('path/to/white.png')), - image_tools.EncodePNG(self.white)) - self.assertEquals( - image_tools.EncodePNG( - self.ispy_utils.DownloadImage('path/to/black.png')), - image_tools.EncodePNG(self.black)) - self.assertEquals( - image_tools.EncodePNG( - self.ispy_utils.DownloadImage('path/to/red.png')), - image_tools.EncodePNG(self.red)) - # Check that the DownloadImage function throws an error for a - # nonexistant image. - self.assertRaises(cloud_bucket.FileNotFoundError, - self.ispy_utils.DownloadImage, - 'path/to/yellow.png') - - def testUpdateImage(self): - self.bucket.Reset() - # Upload some images to the datastore. - self.ispy_utils.UploadImage('path/to/image.png', self.white) - self.assertEquals(self.bucket.datastore['path/to/image.png'], - image_tools.EncodePNG(self.white)) - self.ispy_utils.UpdateImage('path/to/image.png', self.black) - # Confirm that the image actually got updated. - self.assertEquals(self.bucket.datastore['path/to/image.png'], - image_tools.EncodePNG(self.black)) - - def testGenerateExpectation(self): - self.bucket.Reset() - # Upload some tests to the datastore. - self.ispy_utils.GenerateExpectation('test', [self.white, self.black]) - self.ispy_utils.GenerateExpectation('test1', [self.black, self.black]) - self.ispy_utils.GenerateExpectation('test2', [self.black]) - # Confirm that the tests were successfully uploaded. - self.assertEquals(self.bucket.datastore[ - ispy_utils.GetExpectationPath('test', 'expected.png')], - image_tools.EncodePNG(self.white)) - self.assertEquals(self.bucket.datastore[ - ispy_utils.GetExpectationPath('test', 'mask.png')], - image_tools.EncodePNG(self.white)) - self.assertEquals(self.bucket.datastore[ - ispy_utils.GetExpectationPath('test1', 'expected.png')], - image_tools.EncodePNG(self.black)) - self.assertEquals(self.bucket.datastore[ - ispy_utils.GetExpectationPath('test1', 'mask.png')], - image_tools.EncodePNG(self.black)) - self.assertEquals(self.bucket.datastore[ - ispy_utils.GetExpectationPath('test2', 'expected.png')], - image_tools.EncodePNG(self.black)) - self.assertEquals(self.bucket.datastore[ - ispy_utils.GetExpectationPath('test2', 'mask.png')], - image_tools.EncodePNG(self.black)) - - def testPerformComparison(self): - self.bucket.Reset() - self.ispy_utils.GenerateExpectation('test1', [self.red, self.red]) - self.ispy_utils.PerformComparison('test', 'test1', self.black) - self.assertEquals(self.bucket.datastore[ - ispy_utils.GetFailurePath('test', 'test1', 'actual.png')], - image_tools.EncodePNG(self.black)) - self.ispy_utils.PerformComparison('test', 'test1', self.red) - self.assertTrue(self.bucket.datastore.has_key( - ispy_utils.GetFailurePath('test', 'test1', 'actual.png'))) - - def testGetExpectation(self): - self.bucket.Reset() - # Upload some tests to the datastore - self.ispy_utils.GenerateExpectation('test1', [self.white, self.black]) - self.ispy_utils.GenerateExpectation('test2', [self.red, self.white]) - test1 = self.ispy_utils.GetExpectation('test1') - test2 = self.ispy_utils.GetExpectation('test2') - # Check that GetExpectation gets the appropriate tests. - self.assertEquals(image_tools.EncodePNG(test1.expected), - image_tools.EncodePNG(self.white)) - self.assertEquals(image_tools.EncodePNG(test1.mask), - image_tools.EncodePNG(self.white)) - self.assertEquals(image_tools.EncodePNG(test2.expected), - image_tools.EncodePNG(self.red)) - self.assertEquals(image_tools.EncodePNG(test2.mask), - image_tools.EncodePNG(self.white)) - # Check that GetExpectation throws an error for a nonexistant test. - self.assertRaises( - cloud_bucket.FileNotFoundError, self.ispy_utils.GetExpectation, 'test3') - - def testExpectationExists(self): - self.bucket.Reset() - self.ispy_utils.GenerateExpectation('test1', [self.white, self.black]) - self.ispy_utils.GenerateExpectation('test2', [self.white, self.black]) - self.assertTrue(self.ispy_utils.ExpectationExists('test1')) - self.assertTrue(self.ispy_utils.ExpectationExists('test2')) - self.assertFalse(self.ispy_utils.ExpectationExists('test3')) - - def testFailureExists(self): - self.bucket.Reset() - self.ispy_utils.GenerateExpectation('test1', [self.white, self.white]) - self.ispy_utils.PerformComparison('test', 'test1', self.black) - self.ispy_utils.PerformComparison('test', 'test1', self.white) - self.assertTrue(self.ispy_utils.FailureExists('test', 'test1')) - self.assertFalse(self.ispy_utils.FailureExists('test', 'test2')) - - def testRemoveExpectation(self): - self.bucket.Reset() - self.ispy_utils.GenerateExpectation('test1', [self.white, self.white]) - self.ispy_utils.GenerateExpectation('test2', [self.white, self.white]) - self.assertTrue(self.ispy_utils.ExpectationExists('test1')) - self.assertTrue(self.ispy_utils.ExpectationExists('test2')) - self.ispy_utils.RemoveExpectation('test1') - self.assertFalse(self.ispy_utils.ExpectationExists('test1')) - self.assertTrue(self.ispy_utils.ExpectationExists('test2')) - self.ispy_utils.RemoveExpectation('test2') - self.assertFalse(self.ispy_utils.ExpectationExists('test1')) - self.assertFalse(self.ispy_utils.ExpectationExists('test2')) - - def testRemoveFailure(self): - self.bucket.Reset() - self.ispy_utils.GenerateExpectation('test1', [self.white, self.white]) - self.ispy_utils.GenerateExpectation('test2', [self.white, self.white]) - self.ispy_utils.PerformComparison('test', 'test1', self.black) - self.ispy_utils.RemoveFailure('test', 'test1') - self.assertFalse(self.ispy_utils.FailureExists('test', 'test1')) - self.assertTrue(self.ispy_utils.ExpectationExists('test1')) - self.assertFalse(self.ispy_utils.FailureExists('test', 'test2')) - self.assertTrue(self.ispy_utils.ExpectationExists('test2')) - - def testGetFailure(self): - self.bucket.Reset() - # Upload a result - self.ispy_utils.GenerateExpectation('test1', [self.red, self.red]) - self.ispy_utils.PerformComparison('test', 'test1', self.black) - res = self.ispy_utils.GetFailure('test', 'test1') - # Check that the function correctly got the result. - self.assertEquals(image_tools.EncodePNG(res.expected), - image_tools.EncodePNG(self.red)) - self.assertEquals(image_tools.EncodePNG(res.diff), - image_tools.EncodePNG(self.masked)) - self.assertEquals(image_tools.EncodePNG(res.actual), - image_tools.EncodePNG(self.black)) - # Check that the function raises an error when given non-existant results. - self.assertRaises(cloud_bucket.FileNotFoundError, - self.ispy_utils.GetFailure, 'test', 'test2') - - def testGetAllPaths(self): - self.bucket.Reset() - # Upload some tests. - self.ispy_utils.GenerateExpectation('test1', [self.white, self.black]) - # Check that the function gets all urls matching the prefix. - self.assertEquals( - set(self.ispy_utils.GetAllPaths( - ispy_utils.GetExpectationPath('test1'))), - set([ispy_utils.GetExpectationPath('test1', 'expected.png'), - ispy_utils.GetExpectationPath('test1', 'mask.png')])) - self.assertEquals( - set(self.ispy_utils.GetAllPaths( - ispy_utils.GetExpectationPath('test3'))), set()) - - -if __name__ == '__main__': - unittest.main() diff --git a/chrome/test/functional/ispy/common/mock_cloud_bucket.py b/chrome/test/functional/ispy/common/mock_cloud_bucket.py deleted file mode 100644 index 803fd57..0000000 --- a/chrome/test/functional/ispy/common/mock_cloud_bucket.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright 2013 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. - -"""Subclass of CloudBucket used for testing.""" - -import os -import sys - -sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) -import cloud_bucket - - -class MockCloudBucket(cloud_bucket.BaseCloudBucket): - """Subclass of CloudBucket used for testing.""" - - def __init__(self): - """Initializes the MockCloudBucket with its datastore. - - Returns: - An instance of MockCloudBucket. - """ - self.datastore = {} - - def Reset(self): - """Clears the MockCloudBucket's datastore.""" - self.datastore = {} - - # override - def UploadFile(self, path, contents, content_type): - self.datastore[path] = contents - - # override - def DownloadFile(self, path): - if self.datastore.has_key(path): - return self.datastore[path] - else: - raise cloud_bucket.FileNotFoundError - - # override - def UpdateFile(self, path, contents): - if not self.FileExists(path): - raise cloud_bucket.FileNotFoundError - self.UploadFile(path, contents, '') - - # override - def RemoveFile(self, path): - if self.datastore.has_key(path): - self.datastore.pop(path) - - # override - def FileExists(self, path): - return self.datastore.has_key(path) - - # override - def GetImageURL(self, path): - if self.datastore.has_key(path): - return path - else: - raise cloud_bucket.FileNotFoundError - - # override - def GetAllPaths(self, prefix): - return (item[0] for item in self.datastore.items() - if item[0].startswith(prefix)) diff --git a/chrome/test/functional/ispy/ispy_api.py b/chrome/test/functional/ispy/ispy_api.py deleted file mode 100644 index e297368..0000000 --- a/chrome/test/functional/ispy/ispy_api.py +++ /dev/null @@ -1,229 +0,0 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import json -import logging -import os -from distutils.version import LooseVersion -from PIL import Image - -from common import cloud_bucket -from common import ispy_utils - - -class ISpyApi(object): - """The public API for interacting with ISpy.""" - - def __init__(self, cloud_bucket): - """Initializes the utility class. - - Args: - cloud_bucket: a BaseCloudBucket in which to the version file, - expectations and results are to be stored. - """ - self._cloud_bucket = cloud_bucket - self._ispy = ispy_utils.ISpyUtils(self._cloud_bucket) - self._rebaselineable_cache = {} - - def UpdateExpectationVersion(self, chrome_version, version_file): - """Updates the most recent expectation version to the Chrome version. - - Should be called after generating a new set of expectations. - - Args: - chrome_version: the chrome version as a string of the form "31.0.123.4". - version_file: path to the version file in the cloud bucket. The version - file contains a json list of ordered Chrome versions for which - expectations exist. - """ - insert_pos = 0 - expectation_versions = [] - try: - expectation_versions = self._GetExpectationVersionList(version_file) - if expectation_versions: - try: - version = self._GetExpectationVersion( - chrome_version, expectation_versions) - if version == chrome_version: - return - insert_pos = expectation_versions.index(version) - except: - insert_pos = len(expectation_versions) - except cloud_bucket.FileNotFoundError: - pass - expectation_versions.insert(insert_pos, chrome_version) - logging.info('Updating expectation version...') - self._cloud_bucket.UploadFile( - version_file, json.dumps(expectation_versions), - 'application/json') - - def _GetExpectationVersion(self, chrome_version, expectation_versions): - """Returns the expectation version for the given Chrome version. - - Args: - chrome_version: the chrome version as a string of the form "31.0.123.4". - expectation_versions: Ordered list of Chrome versions for which - expectations exist, as stored in the version file. - - Returns: - Expectation version string. - """ - # Find the closest version that is not greater than the chrome version. - for version in expectation_versions: - if LooseVersion(version) <= LooseVersion(chrome_version): - return version - raise Exception('No expectation exists for Chrome %s' % chrome_version) - - def _GetExpectationVersionList(self, version_file): - """Gets the list of expectation versions from google storage. - - Args: - version_file: path to the version file in the cloud bucket. The version - file contains a json list of ordered Chrome versions for which - expectations exist. - - Returns: - Ordered list of Chrome versions. - """ - try: - return json.loads(self._cloud_bucket.DownloadFile(version_file)) - except: - return [] - - def _GetExpectationNameWithVersion(self, device_type, expectation, - chrome_version, version_file): - """Get the expectation to be used with the current Chrome version. - - Args: - device_type: string identifier for the device type. - expectation: name for the expectation to generate. - chrome_version: the chrome version as a string of the form "31.0.123.4". - - Returns: - Version as an integer. - """ - version = self._GetExpectationVersion( - chrome_version, self._GetExpectationVersionList(version_file)) - return self._CreateExpectationName(device_type, expectation, version) - - def _CreateExpectationName(self, device_type, expectation, version): - """Create the full expectation name from the expectation and version. - - Args: - device_type: string identifier for the device type, example: mako - expectation: base name for the expectation, example: google.com - version: expectation version, example: 31.0.23.1 - - Returns: - Full expectation name as a string, example: mako:google.com(31.0.23.1) - """ - return '%s:%s(%s)' % (device_type, expectation, version) - - def GenerateExpectation(self, device_type, expectation, chrome_version, - version_file, screenshots): - """Create an expectation for I-Spy. - - Args: - device_type: string identifier for the device type. - expectation: name for the expectation to generate. - chrome_version: the chrome version as a string of the form "31.0.123.4". - screenshots: a list of similar PIL.Images. - """ - # https://code.google.com/p/chromedriver/issues/detail?id=463 - expectation_with_version = self._CreateExpectationName( - device_type, expectation, chrome_version) - if self._ispy.ExpectationExists(expectation_with_version): - logging.warning( - 'I-Spy expectation \'%s\' already exists, overwriting.', - expectation_with_version) - logging.info('Generating I-Spy expectation...') - self._ispy.GenerateExpectation(expectation_with_version, screenshots) - - def PerformComparison(self, test_run, device_type, expectation, - chrome_version, version_file, screenshot): - """Compare a screenshot with the given expectation in I-Spy. - - Args: - test_run: name for the test run. - device_type: string identifier for the device type. - expectation: name for the expectation to compare against. - chrome_version: the chrome version as a string of the form "31.0.123.4". - screenshot: a PIL.Image to compare. - """ - # https://code.google.com/p/chromedriver/issues/detail?id=463 - logging.info('Performing I-Spy comparison...') - self._ispy.PerformComparison( - test_run, - self._GetExpectationNameWithVersion( - device_type, expectation, chrome_version, version_file), - screenshot) - - def CanRebaselineToTestRun(self, test_run): - """Returns whether the test run has associated expectations. - - Returns: - True if RebaselineToTestRun() can be called for this test run. - """ - if test_run in self._rebaselineable_cache: - return True - return self._cloud_bucket.FileExists( - ispy_utils.GetTestRunPath(test_run, 'rebaseline.txt')) - - def RebaselineToTestRun(self, test_run): - """Update the version file to use expectations associated with |test_run|. - - Args: - test_run: The name of the test run to rebaseline. - """ - rebaseline_path = ispy_utils.GetTestRunPath(test_run, 'rebaseline.txt') - rebaseline_attrib = json.loads( - self._cloud_bucket.DownloadFile(rebaseline_path)) - self.UpdateExpectationVersion( - rebaseline_attrib['version'], rebaseline_attrib['version_file']) - self._cloud_bucket.RemoveFile(rebaseline_path) - - def _SetTestRunRebaselineable(self, test_run, chrome_version, version_file): - """Writes a JSON file containing the data needed to rebaseline. - - Args: - test_run: The name of the test run to add the rebaseline file to. - chrome_version: the chrome version that can be rebaselined to (must have - associated Expectations). - version_file: the path of the version file associated with the test run. - """ - self._rebaselineable_cache[test_run] = True - self._cloud_bucket.UploadFile( - ispy_utils.GetTestRunPath(test_run, 'rebaseline.txt'), - json.dumps({ - 'version': chrome_version, - 'version_file': version_file}), - 'application/json') - - def PerformComparisonAndPrepareExpectation(self, test_run, device_type, - expectation, chrome_version, - version_file, screenshots): - """Perform comparison and generate an expectation that can used later. - - The test run web UI will have a button to set the Expectations generated for - this version as the expectation for comparison with later versions. - - Args: - test_run: The name of the test run to add the rebaseline file to. - device_type: string identifier for the device type. - chrome_version: the chrome version that can be rebaselined to (must have - associated Expectations). - version_file: the path of the version file associated with the test run. - screenshot: a list of similar PIL.Images. - """ - if not self.CanRebaselineToTestRun(test_run): - self._SetTestRunRebaselineable(test_run, chrome_version, version_file) - expectation_with_version = self._CreateExpectationName( - device_type, expectation, chrome_version) - self._ispy.GenerateExpectation(expectation_with_version, screenshots) - self._ispy.PerformComparison( - test_run, - self._GetExpectationNameWithVersion( - device_type, expectation, chrome_version, version_file), - screenshots[-1]) - diff --git a/chrome/test/functional/ispy/ispy_api_unittest.py b/chrome/test/functional/ispy/ispy_api_unittest.py deleted file mode 100755 index 2e6a476..0000000 --- a/chrome/test/functional/ispy/ispy_api_unittest.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import json -import unittest -from PIL import Image - -import ispy_api -from common import cloud_bucket -from common import mock_cloud_bucket - - -class ISpyApiTest(unittest.TestCase): - """Unittest for the ISpy API.""" - - def setUp(self): - self.cloud_bucket = mock_cloud_bucket.MockCloudBucket() - self.ispy = ispy_api.ISpyApi(self.cloud_bucket) - self.white_img = Image.new('RGBA', (10, 10), (255, 255, 255, 255)) - self.black_img = Image.new('RGBA', (10, 10), (0, 0, 0, 255)) - - def testGenerateExpectationsRunComparison(self): - self.ispy.GenerateExpectation( - 'device', 'test', '1.1.1.1', 'versions.json', - [self.white_img, self.white_img]) - self.ispy.UpdateExpectationVersion('1.1.1.1', 'versions.json') - self.ispy.PerformComparison( - 'test1', 'device', 'test', '1.1.1.1', 'versions.json', self.white_img) - expect_name = self.ispy._CreateExpectationName( - 'device', 'test', '1.1.1.1') - self.assertFalse(self.ispy._ispy.FailureExists('test1', expect_name)) - self.ispy.PerformComparison( - 'test2', 'device', 'test', '1.1.1.1','versions.json', self.black_img) - self.assertTrue(self.ispy._ispy.FailureExists('test2', expect_name)) - - def testUpdateExpectationVersion(self): - self.ispy.UpdateExpectationVersion('1.0.0.0', 'versions.json') - self.ispy.UpdateExpectationVersion('1.0.4.0', 'versions.json') - self.ispy.UpdateExpectationVersion('2.1.5.0', 'versions.json') - self.ispy.UpdateExpectationVersion('1.1.5.0', 'versions.json') - self.ispy.UpdateExpectationVersion('0.0.0.0', 'versions.json') - self.ispy.UpdateExpectationVersion('1.1.5.0', 'versions.json') - self.ispy.UpdateExpectationVersion('0.0.0.1', 'versions.json') - versions = json.loads(self.cloud_bucket.DownloadFile('versions.json')) - self.assertEqual(versions, - ['2.1.5.0', '1.1.5.0', '1.0.4.0', '1.0.0.0', '0.0.0.1', '0.0.0.0']) - - def testPerformComparisonAndPrepareExpectation(self): - self.assertFalse(self.ispy.CanRebaselineToTestRun('test')) - self.assertRaises( - cloud_bucket.FileNotFoundError, - self.ispy.PerformComparisonAndPrepareExpectation, - 'test', 'device', 'expect', '1.0', 'versions.json', - [self.white_img, self.white_img]) - self.assertTrue(self.ispy.CanRebaselineToTestRun('test')) - self.ispy.RebaselineToTestRun('test') - versions = json.loads(self.cloud_bucket.DownloadFile('versions.json')) - self.assertEqual(versions, ['1.0']) - self.ispy.PerformComparisonAndPrepareExpectation( - 'test1', 'device', 'expect', '1.1', 'versions.json', - [self.white_img, self.white_img]) - - -if __name__ == '__main__': - unittest.main() diff --git a/chrome/test/functional/ispy/server/__init__.py b/chrome/test/functional/ispy/server/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/chrome/test/functional/ispy/server/__init__.py +++ /dev/null diff --git a/chrome/test/functional/ispy/server/app.py b/chrome/test/functional/ispy/server/app.py deleted file mode 100644 index 6e90804..0000000 --- a/chrome/test/functional/ispy/server/app.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2013 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import os -import sys -import webapp2 - -sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir)) -import debug_view_handler -import image_handler -import main_view_handler -import rebaseline_handler -import update_mask_handler - - -application = webapp2.WSGIApplication( - [('/update_mask', update_mask_handler.UpdateMaskHandler), - ('/rebaseline', rebaseline_handler.RebaselineHandler), - ('/debug_view', debug_view_handler.DebugViewHandler), - ('/image', image_handler.ImageHandler), - ('/', main_view_handler.MainViewHandler)], debug=True) diff --git a/chrome/test/functional/ispy/server/debug_view_handler.py b/chrome/test/functional/ispy/server/debug_view_handler.py deleted file mode 100644 index 96bfe3c..0000000 --- a/chrome/test/functional/ispy/server/debug_view_handler.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2013 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. - -"""Request handler to display the debug view for a Failure.""" - -import jinja2 -import os -import sys -import webapp2 - -from common import ispy_utils - -import views - -JINJA = jinja2.Environment( - loader=jinja2.FileSystemLoader(os.path.dirname(views.__file__)), - extensions=['jinja2.ext.autoescape']) - - -class DebugViewHandler(webapp2.RequestHandler): - """Request handler to display the debug view for a failure.""" - - def get(self): - """Handles get requests to the /debug_view page. - - GET Parameters: - test_run: The test run. - expectation: The expectation name. - """ - test_run = self.request.get('test_run') - expectation = self.request.get('expectation') - expected_path = ispy_utils.GetExpectationPath(expectation, 'expected.png') - actual_path = ispy_utils.GetFailurePath(test_run, expectation, 'actual.png') - data = {} - - def _ImagePath(url): - return '/image?file_path=%s' % url - - data['expected'] = _ImagePath(expected_path) - data['actual'] = _ImagePath(actual_path) - data['test_run'] = test_run - data['expectation'] = expectation - template = JINJA.get_template('debug_view.html') - self.response.write(template.render(data)) diff --git a/chrome/test/functional/ispy/server/gs_bucket.py b/chrome/test/functional/ispy/server/gs_bucket.py deleted file mode 100644 index a132f05..0000000 --- a/chrome/test/functional/ispy/server/gs_bucket.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright 2013 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. - -"""Implementation of CloudBucket using Google Cloud Storage as the backend.""" -import os -import sys - -import cloudstorage - -from common import cloud_bucket - - -class GoogleCloudStorageBucket(cloud_bucket.BaseCloudBucket): - """Subclass of cloud_bucket.CloudBucket with actual GS commands.""" - - def __init__(self, bucket): - """Initializes the bucket. - - Args: - bucket: the name of the bucket to connect to. - """ - self.bucket = '/' + bucket - - def _full_path(self, path): - return self.bucket + '/' + path.lstrip('/') - - # override - def UploadFile(self, path, contents, content_type): - gs_file = cloudstorage.open( - self._full_path(path), 'w', content_type=content_type) - gs_file.write(contents) - gs_file.close() - - # override - def DownloadFile(self, path): - try: - gs_file = cloudstorage.open(self._full_path(path), 'r') - r = gs_file.read() - gs_file.close() - except Exception as e: - raise Exception('%s: %s' % (self._full_path(path), str(e))) - return r - - # override - def UpdateFile(self, path, contents): - if not self.FileExists(path): - raise cloud_bucket.FileNotFoundError - gs_file = cloudstorage.open(self._full_path(path), 'w') - gs_file.write(contents) - gs_file.close() - - # override - def RemoveFile(self, path): - cloudstorage.delete(self._full_path(path)) - - # override - def FileExists(self, path): - try: - cloudstorage.stat(self._full_path(path)) - except cloudstorage.NotFoundError: - return False - return True - - # override - def GetImageURL(self, path): - return '/image?file_path=%s' % path - - # override - def GetAllPaths(self, prefix): - return (f.filename[len(self.bucket) + 1:] for f in - cloudstorage.listbucket(self.bucket, prefix=prefix)) diff --git a/chrome/test/functional/ispy/server/image_handler.py b/chrome/test/functional/ispy/server/image_handler.py deleted file mode 100644 index d1f11f2..0000000 --- a/chrome/test/functional/ispy/server/image_handler.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2013 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. - -"""Request handler to display an image from Google Cloud Storage.""" - -import json -import os -import sys -import webapp2 - -from common import cloud_bucket -from common import constants - -import gs_bucket - - -class ImageHandler(webapp2.RequestHandler): - """A request handler to avoid the Same-Origin problem in the debug view.""" - - def get(self): - """Handles get requests to the ImageHandler. - - GET Parameters: - file_path: A path to an image resource in Google Cloud Storage. - """ - file_path = self.request.get('file_path') - if not file_path: - self.error(404) - return - bucket = gs_bucket.GoogleCloudStorageBucket(constants.BUCKET) - try: - image = bucket.DownloadFile(file_path) - except cloud_bucket.FileNotFoundError: - self.error(404) - else: - self.response.headers['Content-Type'] = 'image/png' - self.response.out.write(image) diff --git a/chrome/test/functional/ispy/server/main_view_handler.py b/chrome/test/functional/ispy/server/main_view_handler.py deleted file mode 100644 index 0738a0c..0000000 --- a/chrome/test/functional/ispy/server/main_view_handler.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright 2013 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. - -"""Request handler to serve the main_view page.""" - -import jinja2 -import json -import os -import re -import sys -import webapp2 - -import ispy_api -from common import constants -from common import ispy_utils - -import gs_bucket -import views - -JINJA = jinja2.Environment( - loader=jinja2.FileSystemLoader(os.path.dirname(views.__file__)), - extensions=['jinja2.ext.autoescape']) - - -class MainViewHandler(webapp2.RequestHandler): - """Request handler to serve the main_view page.""" - - def get(self): - """Handles a get request to the main_view page. - - If the test_run parameter is specified, then a page displaying all of - the failed runs in the test_run will be shown. Otherwise a view listing - all of the test_runs available for viewing will be displayed. - """ - test_run = self.request.get('test_run') - bucket = gs_bucket.GoogleCloudStorageBucket(constants.BUCKET) - ispy = ispy_utils.ISpyUtils(bucket) - # Load the view. - if test_run: - self._GetForTestRun(test_run, ispy) - return - self._GetAllTestRuns(ispy) - - def _GetAllTestRuns(self, ispy): - """Renders a list view of all of the test_runs available in GS. - - Args: - ispy: An instance of ispy_api.ISpyApi. - """ - template = JINJA.get_template('list_view.html') - data = {} - test_runs = set([path.lstrip('/').split('/')[1] for path in - ispy.GetAllPaths('failures/')]) - base_url = '/?test_run=%s' - data['links'] = [(test_run, base_url % test_run) for test_run in test_runs] - self.response.write(template.render(data)) - - def _GetForTestRun(self, test_run, ispy): - """Renders a sorted list of failure-rows for a given test_run. - - This method will produce a list of failure-rows that are sorted - in descending order by number of different pixels. - - Args: - test_run: The name of the test_run to render failure rows from. - ispy: An instance of ispy_api.ISpyApi. - """ - paths = set([path for path in ispy.GetAllPaths('failures/' + test_run) - if path.endswith('actual.png')]) - can_rebaseline = ispy_api.ISpyApi( - ispy.cloud_bucket).CanRebaselineToTestRun(test_run) - rows = [self._CreateRow(test_run, path, ispy) for path in paths] - - # Function that sorts by the different_pixels field in the failure-info. - def _Sorter(a, b): - return cmp(b['percent_different'], - a['percent_different']) - template = JINJA.get_template('main_view.html') - self.response.write( - template.render({'comparisons': sorted(rows, _Sorter), - 'test_run': test_run, - 'can_rebaseline': can_rebaseline})) - - def _CreateRow(self, test_run, path, ispy): - """Creates one failure-row. - - This method builds a dictionary with the data necessary to display a - failure in the main_view html template. - - Args: - test_run: The name of the test_run the failure is in. - path: A path to the failure's actual.png file. - ispy: An instance of ispy_api.ISpyApi. - - Returns: - A dictionary with fields necessary to render a failure-row - in the main_view html template. - """ - res = {} - res['expectation'] = path.lstrip('/').split('/')[2] - res['test_run'] = test_run - res['info'] = json.loads(ispy.cloud_bucket.DownloadFile( - ispy_utils.GetFailurePath(res['test_run'], res['expectation'], - 'info.txt'))) - expected = ispy_utils.GetExpectationPath( - res['expectation'], 'expected.png') - diff = ispy_utils.GetFailurePath(test_run, res['expectation'], 'diff.png') - res['percent_different'] = res['info']['fraction_different'] * 100 - res['expected_path'] = expected - res['diff_path'] = diff - res['actual_path'] = path - res['expected'] = ispy.cloud_bucket.GetImageURL(expected) - res['diff'] = ispy.cloud_bucket.GetImageURL(diff) - res['actual'] = ispy.cloud_bucket.GetImageURL(path) - return res diff --git a/chrome/test/functional/ispy/server/rebaseline_handler.py b/chrome/test/functional/ispy/server/rebaseline_handler.py deleted file mode 100644 index 81bdb4b..0000000 --- a/chrome/test/functional/ispy/server/rebaseline_handler.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2014 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. - -"""Request Handler that updates the Expectation version.""" - -import webapp2 - -import ispy_api -from common import constants - -import gs_bucket - - -class RebaselineHandler(webapp2.RequestHandler): - """Request handler to allow test mask updates.""" - - def post(self): - """Accepts post requests. - - Expects a test_run as a parameter and updates the associated version file to - use the expectations associated with that test run. - """ - test_run = self.request.get('test_run') - - # Fail if test_run parameter is missing. - if not test_run: - self.response.headers['Content-Type'] = 'json/application' - self.response.write(json.dumps( - {'error': '\'test_run\' must be supplied to rebaseline.'})) - return - # Otherwise, set up the utilities. - bucket = gs_bucket.GoogleCloudStorageBucket(constants.BUCKET) - ispy = ispy_api.ISpyApi(bucket) - # Update versions file. - ispy.RebaselineToTestRun(test_run) - # Redirect back to the sites list for the test run. - self.redirect('/?test_run=%s' % test_run) diff --git a/chrome/test/functional/ispy/server/update_mask_handler.py b/chrome/test/functional/ispy/server/update_mask_handler.py deleted file mode 100644 index 10cb964..0000000 --- a/chrome/test/functional/ispy/server/update_mask_handler.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2013 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. - -"""Request Handler to allow test mask updates.""" - -import webapp2 -import re -import sys -import os - -from common import constants -from common import image_tools -from common import ispy_utils - -import gs_bucket - - -class UpdateMaskHandler(webapp2.RequestHandler): - """Request handler to allow test mask updates.""" - - def post(self): - """Accepts post requests. - - This method will accept a post request containing device, site and - device_id parameters. This method takes the diff of the run - indicated by it's parameters and adds it to the mask of the run's - test. It will then delete the run it is applied to and redirect - to the device list view. - """ - test_run = self.request.get('test_run') - expectation = self.request.get('expectation') - - # Short-circuit if a parameter is missing. - if not (test_run and expectation): - self.response.headers['Content-Type'] = 'json/application' - self.response.write(json.dumps( - {'error': '\'test_run\' and \'expectation\' must be ' - 'supplied to update a mask.'})) - return - # Otherwise, set up the utilities. - self.bucket = gs_bucket.GoogleCloudStorageBucket(constants.BUCKET) - self.ispy = ispy_utils.ISpyUtils(self.bucket) - # Short-circuit if the failure does not exist. - if not self.ispy.FailureExists(test_run, expectation): - self.response.headers['Content-Type'] = 'json/application' - self.response.write(json.dumps( - {'error': 'Could not update mask because failure does not exist.'})) - return - # Get the failure namedtuple (which also computes the diff). - failure = self.ispy.GetFailure(test_run, expectation) - # Upload the new mask in place of the original. - self.ispy.UpdateImage( - ispy_utils.GetExpectationPath(expectation, 'mask.png'), - image_tools.ConvertDiffToMask(failure.diff)) - # Now that there is no diff for the two images, remove the failure. - self.ispy.RemoveFailure(test_run, expectation) - # Redirect back to the sites list for the test run. - self.redirect('/?test_run=%s' % test_run) diff --git a/chrome/test/functional/ispy/server/views/__init__.py b/chrome/test/functional/ispy/server/views/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/chrome/test/functional/ispy/server/views/__init__.py +++ /dev/null diff --git a/chrome/test/functional/ispy/server/views/debug_view.html b/chrome/test/functional/ispy/server/views/debug_view.html deleted file mode 100644 index 8371280..0000000 --- a/chrome/test/functional/ispy/server/views/debug_view.html +++ /dev/null @@ -1,47 +0,0 @@ -<html> - <head> - <title>Debug {{ expectation }}</title> - <script language="javascript"> - var current = 0; - var toggle_interval = null; - - var toggle = function() { - current = (current + 1) % 2; - var image = document.getElementById("screenshot"); - image.src = (current ? "{{ actual }}" : "{{ expected }}"); - var title = document.getElementById("text"); - title.textContent = (current ? "Actual" : "Expected"); - } - - var setup = function() { - toggle(); - toggle_interval = window.setInterval(toggle, 1000); - } - - var manualToggle = function() { - if (toggle_interval != null) - window.clearInterval(toggle_interval); - toggle(); - } - - var confirmSubmit = function() { - return confirm("The area in this diff will be ignored in all future comparisions. Are you sure?"); - } - </script> - </head> - <body onload="setup();"> - <div> - <a href="javascript:void(0)" onclick="manualToggle();">Toggle</a> - → - <span id="text"></span> - </div> - <br> - <form action="/update_mask" method="post" onsubmit="return confirmSubmit();"> - <input type="hidden" name="test_run" value="{{ test_run }}"/> - <input type="hidden" name="expectation" value="{{ expectation }}"/> - <input type="submit" value="Ignore similar diffs in the future"/> - </form> - <br> - <img id="screenshot" src=""/> - </body> -</html> diff --git a/chrome/test/functional/ispy/server/views/list_view.html b/chrome/test/functional/ispy/server/views/list_view.html deleted file mode 100644 index f6b5dc6..0000000 --- a/chrome/test/functional/ispy/server/views/list_view.html +++ /dev/null @@ -1,28 +0,0 @@ -<!DOCTYPE html> -{% autoescape on %} -<html> - <head> - <title>I-Spy Test Runs</title> - <style> - #container { - display: table; - background-color:#DDD; - border: 1px solid #AAA; - width: 400px; - margin: 5px; - padding: 5px; - } - </style> - </head> - <body> - <h3>Test Runs</h3> - <div id="container"> - {% for link in links %} - <div> - <a href="{{ link[1] }}">{{ link[0] }}</a> - </div> - {% endfor %} - </div> - </body> -</html> -{% endautoescape %} diff --git a/chrome/test/functional/ispy/server/views/main_view.html b/chrome/test/functional/ispy/server/views/main_view.html deleted file mode 100644 index d722c6a..0000000 --- a/chrome/test/functional/ispy/server/views/main_view.html +++ /dev/null @@ -1,78 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <title>{{ test_run }} failures</title> - <style> - .image { - max-height: 325px; - max-width: 325px; - } - .cell { - padding-right: 25px; - padding-left: 25px; - float: left; - width: 20%; - } - .imagelink { - border-width: 0px; - } - .info { - padding-bottom: 25px; - } - .row { - padding-top: 10px; - padding-bottom: 10px; - border-bottom: 2px solid #888; - height: 350px; - } - </style> - - <script language="javascript"> - var confirmSubmit = function() { - return confirm("The screenshots generated with this version of chrome will be used as the expected images for future comparisions. Are you sure?"); - } - </script> - </head> - <body> - <h3>Test Run: {{ test_run }}</h3> - {% if can_rebaseline %} - <form action="/rebaseline" method="post" onsubmit="return confirmSubmit();"> - <input type="hidden" name="test_run" value="{{ test_run }}"/> - <input type="submit" value="Set as LKGR"/> - </form> - <br> - {% endif %} - {% if not comparisons %} - <h2>No failures.</h2> - {% endif %} - {% for comp in comparisons %} - <div class="row"> - <div class="cell"> - Diff ({{ "%.1f"|format(comp['percent_different']) }}%)<br> - <a class="imagelink" href="{{ comp['diff'] }}"> - <img class="image" src={{ comp['diff'] }}> - </a> - </div> - <div class="cell"> - Expected<br> - <a class="imagelink" href="{{ comp['expected'] }}"> - <img class="image" src={{ comp['expected'] }}> - </a> - </div> - <div class="cell"> - Actual<br> - <a class="imagelink" href="{{ comp['actual'] }}"> - <img class="image" src={{ comp['actual'] }}> - </a> - </div> - <div class="cell"> - <br> - <div class="info"> - {{ comp['expectation'] }}<br> - <a href='/debug_view?test_run={{ comp['test_run'] }}&expectation={{ comp['expectation'] }}'>Debug View</a> - </div> - </div> - </div> - {% endfor %} - </body> -</html> diff --git a/chrome/test/functional/media/OWNERS b/chrome/test/functional/media/OWNERS deleted file mode 100644 index aa43ab1..0000000 --- a/chrome/test/functional/media/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -dalecurtis@chromium.org -scherkus@chromium.org -shadi@chromium.org diff --git a/chrome/test/functional/media/README b/chrome/test/functional/media/README deleted file mode 100644 index 36a5c5a..0000000 --- a/chrome/test/functional/media/README +++ /dev/null @@ -1,49 +0,0 @@ -HTML5 Media Performance/Functional Test -======================================= - -Description ------------ -This directory contains suites for HTML5 media performance/functional tests. - -media_test_runner.py is the main module and it executes a media test -class (a subclass of MediaTastBase class) with different configuration -(parameters) which are passed in the form of environment variables -(e.g., the number of runs). The location of the subclass is passed as -one of the arguments. - -An example invocation is - python media_test_runner.py -p ./media_perf.py - -In this example, media_perf.py will be invoked using the default set -of parameters. If the test class is not specified in the argument, -whole AVPERF suite is executed, which is defined in -src/chrome/test/functional/PYAUTO_TESTS. - -To Run Tests ------------- -0) Build pyauto (http://www.chromium.org/developers/testing/pyauto) - -1) Add the following in the .gclient and execute "gclient sync". This step is -necessary to pull the test video/audio from deps. - -"custom_deps" : { - "src/chrome/test/data/media/avperf": - "http://src.chromium.org/svn/trunk/deps/avperf", -}, - -2) Execute "python media_test_runner.py" from "src/chrome/test/functional" -directory. Available options can be obtained by "media_test_runner.py -h" - -3) The results are reported to the standard output. An example output is - -RESULT time: t= [0.01182, 0.00995, 2.02328, 2.02021] sec -RESULT procutil: p= [14.10000, 15.20000, 9.10000, 9.00000] percent -RESULT procuser: l= [0.54000, 0.88000, 0.74000, 1.08000] load -RESULT procsystem: l= [0.06000, 0.11000, 0.09000, 0.13000] load -RESULT memoryrss: m= [34.74637, 35.27885, 34.46374, 35.60243] MB -RESULT memoryvms: m= [1001.08288, 1001.34502, 1001.08288, 1001.34502] MB -RESULT memoryutil: p= [0.87187, 0.88523, 0.86478, 0.89335] percent - -This data is read by the perfbot and used for displaying perf graphs and -regression monitoring. The perfbot link is -http://build.chromium.org/p/chromium.perf_av/console. diff --git a/chrome/test/functional/media/__init__.py b/chrome/test/functional/media/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/chrome/test/functional/media/__init__.py +++ /dev/null diff --git a/chrome/test/functional/media/audio_latency_perf.py b/chrome/test/functional/media/audio_latency_perf.py deleted file mode 100755 index 3775457..0000000 --- a/chrome/test/functional/media/audio_latency_perf.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Audio latency performance test. - -Benchmark measuring how fast we can continuously repeat a short sound clip. In -the ideal scenario we'd have zero latency processing script, seeking back to the -beginning of the clip, and resuming audio playback. - -Performance is recorded as the average latency of N playbacks. I.e., if we play -a clip 50 times and the ideal duration of all playbacks is 1000ms and the total -duration is 1500ms, the recorded result is (1500ms - 1000ms) / 50 == 10ms. -""" -import os - -import pyauto_media -import pyauto_utils -import pyauto - - -# HTML test path; relative to src/chrome/test/data. -_TEST_HTML_PATH = os.path.join('media', 'html', 'audio_latency_perf.html') - - -class AudioLatencyPerfTest(pyauto.PyUITest): - """PyAuto test container. See file doc string for more information.""" - - def testAudioLatency(self): - """Launches HTML test which runs the audio latency test.""" - self.NavigateToURL(self.GetFileURLForDataPath(_TEST_HTML_PATH)) - - # Block until the test finishes and notifies us. - self.assertTrue(self.ExecuteJavascript('startTest();')) - latency = float(self.GetDOMValue('averageLatency')) - pyauto_utils.PrintPerfResult('audio_latency', 'latency', latency, 'ms') - - -if __name__ == '__main__': - pyauto_media.Main() diff --git a/chrome/test/functional/media/audio_playback_perf.py b/chrome/test/functional/media/audio_playback_perf.py deleted file mode 100755 index 1b91351..0000000 --- a/chrome/test/functional/media/audio_playback_perf.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Audio basic playback test. Verifies basic audio output. - -The tests plays an audio file and records its output while playing. It uses -SOX tool to eliminate silence from begining and end of audio recorded. Then it -uses PESQ tool to verify the recorded audio against expected file. - -The output of PESQ is a pair of numbers between 0 and 5 describing how close the -recorded audio is to its reference file. - -The output of this test are the PESQ values that are graphed, example: -RESULT audio_pesq: ref= 4.498 score -RESULT audio_pesq: actual= 4.547 score -""" - -import logging -import os -import sys -import tempfile - -import audio_tools -import pyauto_media -import pyauto -import pyauto_utils - - -_TEST_HTML_PATH = pyauto.PyUITest.GetFileURLForDataPath('media', 'html', - 'audio_record.html') -_MEDIA_PATH = os.path.abspath(os.path.join(pyauto.PyUITest.DataDir(), - 'pyauto_private', 'media')) - -# The media file to play. -_TEST_AUDIO = pyauto.PyUITest.GetFileURLForPath(_MEDIA_PATH, 'dance2_10sec.wav') -# The recording duration should be at least as the media duration. We use 5 -# extra seconds of record in this test. -_RECORD_DURATION = 15 - -# Due to different noise introduced by audio recording tools on windows and -# linux, we require an expected audio file per platform. -if 'win32' in sys.platform: - _TEST_EXPECTED_AUDIO = os.path.join(_MEDIA_PATH, 'audio_record_win.wav') -else: - _TEST_EXPECTED_AUDIO = os.path.join(_MEDIA_PATH, 'audio_record_lin.wav') - - -def GetTempFilename(): - """Returns an absolute path to an empty temp file.""" - f, path = tempfile.mkstemp(suffix='_actual.wav') - os.close(f) - return path - - -class AudioRecordPerfTest(pyauto.PyUITest): - """PyAuto test container. See file doc string for more information.""" - - def testBasicAudioPlaybackRecord(self): - """Plays an audio file and verifies its output against expected.""" - # The 2 temp files that will be potentially used in the test. - temp_file = None - file_no_silence = None - # Rebase test expectation file is REBASE=1 env variable is set. - rebase = 'REBASE' in os.environ - - try: - temp_file = GetTempFilename() - record_thread = audio_tools.AudioRecorderThread(_RECORD_DURATION, - temp_file) - record_thread.start() - self.NavigateToURL(_TEST_HTML_PATH) - self.assertTrue(self.ExecuteJavascript("startTest('%s');" % _TEST_AUDIO)) - record_thread.join() - - if record_thread.error: - self.fail(record_thread.error) - file_no_silence = _TEST_EXPECTED_AUDIO if rebase else GetTempFilename() - audio_tools.RemoveSilence(temp_file, file_no_silence) - - # Exit if we just want to rebase expected output. - if rebase: - return - - pesq_values = audio_tools.RunPESQ(_TEST_EXPECTED_AUDIO, file_no_silence) - if not pesq_values: - self.fail('Test failed to get pesq results.') - pyauto_utils.PrintPerfResult('audio_pesq', 'ref', pesq_values[0], 'score') - pyauto_utils.PrintPerfResult('audio_pesq', 'actual', pesq_values[1], - 'score') - except: - logging.error('Test failed: %s', self.GetDOMValue('error')) - finally: - # Delete the temporary files used by the test. - if temp_file: - os.remove(temp_file) - if not rebase and file_no_silence: - os.remove(file_no_silence) - - -if __name__ == '__main__': - pyauto_media.Main() diff --git a/chrome/test/functional/media/audio_tools.py b/chrome/test/functional/media/audio_tools.py deleted file mode 100755 index 3b14a7c..0000000 --- a/chrome/test/functional/media/audio_tools.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Audio tools for recording and analyzing audio. - -The audio tools provided here are mainly to: -- record playing audio. -- remove silence from beginning and end of audio file. -- compare audio files using PESQ tool. - -The tools are supported on Windows and Linux. -""" - -import commands -import ctypes -import logging -import os -import re -import subprocess -import sys -import threading -import time - -import pyauto_media -import pyauto - - -_TOOLS_PATH = os.path.abspath(os.path.join(pyauto.PyUITest.DataDir(), - 'pyauto_private', 'media', 'tools')) - -WINDOWS = 'win32' in sys.platform -if WINDOWS: - _PESQ_PATH = os.path.join(_TOOLS_PATH, 'pesq.exe') - _SOX_PATH = os.path.join(_TOOLS_PATH, 'sox.exe') - _AUDIO_RECORDER = r'SoundRecorder.exe' - _FORCE_MIC_VOLUME_MAX_UTIL = os.path.join(_TOOLS_PATH, - r'force_mic_volume_max.exe') -else: - _PESQ_PATH = os.path.join(_TOOLS_PATH, 'pesq') - _SOX_PATH = commands.getoutput('which sox') - _AUDIO_RECORDER = commands.getoutput('which arecord') - _PACMD_PATH = commands.getoutput('which pacmd') - - -class AudioRecorderThread(threading.Thread): - """A thread that records audio out of the default audio output.""" - - def __init__(self, duration, output_file, record_mono=False): - threading.Thread.__init__(self) - self.error = '' - self._duration = duration - self._output_file = output_file - self._record_mono = record_mono - - def run(self): - """Starts audio recording.""" - if WINDOWS: - if self._record_mono: - logging.error("Mono recording not supported on Windows yet!") - - duration = time.strftime('%H:%M:%S', time.gmtime(self._duration)) - cmd = [_AUDIO_RECORDER, '/FILE', self._output_file, '/DURATION', - duration] - # This is needed to run SoundRecorder.exe on Win-64 using Python-32 bit. - ctypes.windll.kernel32.Wow64DisableWow64FsRedirection( - ctypes.byref(ctypes.c_long())) - else: - num_channels = 1 if self._record_mono else 2 - cmd = [_AUDIO_RECORDER, '-d', self._duration, '-f', 'dat', '-c', - str(num_channels), self._output_file] - - cmd = [str(s) for s in cmd] - logging.debug('Running command: %s', ' '.join(cmd)) - returncode = subprocess.call(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - if returncode != 0: - self.error = 'Failed to record audio.' - else: - logging.debug('Finished recording audio into %s.', self._output_file) - - -def RunPESQ(audio_file_ref, audio_file_test, sample_rate=16000): - """Runs PESQ to compare audio test file to a reference audio file. - - Args: - audio_file_ref: The reference audio file used by PESQ. - audio_file_test: The audio test file to compare. - sample_rate: Sample rate used by PESQ algorithm, possible values are only - 8000 or 16000. - - Returns: - A tuple of float values representing PESQ scores of the audio_file_ref and - audio_file_test consecutively. - """ - # Work around a bug in PESQ when the ref file path is > 128 chars. PESQ will - # compute an incorrect score then (!), and the relative path to the ref file - # should be a lot shorter than the absolute one. - audio_file_ref = os.path.relpath(audio_file_ref) - cmd = [_PESQ_PATH, '+%d' % sample_rate, audio_file_ref, audio_file_test] - logging.debug('Running command: %s', ' '.join(cmd)) - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - output, error = p.communicate() - if p.returncode != 0: - logging.error('Error running pesq: %s\n%s', output, error) - # Last line of PESQ output shows the results. Example: - # P.862 Prediction (Raw MOS, MOS-LQO): = 4.180 4.319 - result = re.search('Prediction.*= (\d{1}\.\d{3})\t(\d{1}\.\d{3})', - output) - if not result or len(result.groups()) != 2: - return None - return (float(result.group(1)), float(result.group(2))) - - -def RemoveSilence(input_audio_file, output_audio_file): - """Removes silence from beginning and end of the input_audio_file. - - Args: - input_audio_file: The audio file to remove silence from. - output_audio_file: The audio file to save the output audio. - """ - # SOX documentation for silence command: http://sox.sourceforge.net/sox.html - # To remove the silence from both beginning and end of the audio file, we call - # sox silence command twice: once on normal file and again on its reverse, - # then we reverse the final output. - # Silence parameters are (in sequence): - # ABOVE_PERIODS: The period for which silence occurs. Value 1 is used for - # silence at beginning of audio. - # DURATION: the amount of time in seconds that non-silence must be detected - # before sox stops trimming audio. - # THRESHOLD: value used to indicate what sample value is treates as silence. - ABOVE_PERIODS = '1' - DURATION = '2' - THRESHOLD = '5%' - - cmd = [_SOX_PATH, input_audio_file, output_audio_file, 'silence', - ABOVE_PERIODS, DURATION, THRESHOLD, 'reverse', 'silence', - ABOVE_PERIODS, DURATION, THRESHOLD, 'reverse'] - logging.debug('Running command: %s', ' '.join(cmd)) - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - output, error = p.communicate() - if p.returncode != 0: - logging.error('Error removing silence from audio: %s\n%s', output, error) - - -def ForceMicrophoneVolumeTo100Percent(): - if WINDOWS: - # The volume max util is implemented in WebRTC in - # webrtc/tools/force_mic_volume_max/force_mic_volume_max.cc. - if not os.path.exists(_FORCE_MIC_VOLUME_MAX_UTIL): - raise Exception('Missing required binary %s.' % - _FORCE_MIC_VOLUME_MAX_UTIL) - cmd = [_FORCE_MIC_VOLUME_MAX_UTIL] - else: - # The recording device id is machine-specific. We assume here it is called - # Monitor of render (which corresponds to the id render.monitor). You can - # list the available recording devices with pacmd list-sources. - RECORDING_DEVICE_ID = 'render.monitor' - HUNDRED_PERCENT_VOLUME = '65536' - cmd = [_PACMD_PATH, 'set-source-volume', RECORDING_DEVICE_ID, - HUNDRED_PERCENT_VOLUME] - - logging.debug('Running command: %s', ' '.join(cmd)) - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - output, error = p.communicate() - if p.returncode != 0: - logging.error('Error forcing mic volume to 100%%: %s\n%s', output, error) diff --git a/chrome/test/functional/media/cns_test_base.py b/chrome/test/functional/media/cns_test_base.py deleted file mode 100644 index 04271d5..0000000 --- a/chrome/test/functional/media/cns_test_base.py +++ /dev/null @@ -1,201 +0,0 @@ -# Copyright (c) 2012 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. - -"""Constrained network server (CNS) test base.""" - -import logging -import os -import Queue -import subprocess -import sys -import threading -import urllib2 - -import pyauto -import pyauto_paths - - -# List of commonly used network constraints settings. -# Each setting is a tuppe of the form: -# ('TEST_NAME', [BANDWIDTH_Kbps, LATENCY_ms, PACKET_LOSS_%]) -# -# Note: The test name should satisfy the regex [\w\.-]+ (check -# tools/perf_expectations/tests/perf_expectations_unittest.py for details). It -# is used to name the result graphs on the dashboards. -# -# The WiFi, DSL, and Cable settings were taken from webpagetest.org as -# approximations of their respective real world networks. The settings were -# based on 2011 FCC Broadband Data report (http://www.fcc.gov/document/ -# measuring-broadband-america-report-consumer-broadband-performance-us). -DialUp = ('DialUp', [56, 120, 5]) -Slow = ('Slow', [256, 105, 1]) -Wifi = ('Wifi', [1024, 60, 0]) -DSL = ('DSL', [1541, 50, 0]) -Cable = ('Cable', [5120, 28, 0]) -NoConstraints = ('NoConstraints', [0, 0, 0]) - -# Path to CNS executable relative to source root. -_CNS_PATH = os.path.join( - 'media', 'tools', 'constrained_network_server', 'cns.py') - -# Port to start the CNS on. -_CNS_PORT = 9000 - -# A flag to determine whether to launch a local CNS instance or to connect -# to the external CNS server. Default to False since all current bots use an -# external instance. -# If not on Windows, set USE_LOCAL_CNS=1 env variable to switch the flag. -USE_LOCAL_CNS = ('win' not in sys.platform and 'USE_LOCAL_CNS' in os.environ and - os.environ['USE_LOCAL_CNS'] == '1') - -# Base CNS URL, only requires & separated parameter names appended. -if USE_LOCAL_CNS: - CNS_BASE_URL = 'http://127.0.0.1:%d/ServeConstrained?' % _CNS_PORT -else: - CNS_BASE_URL = 'http://chromeperf34:%d/ServeConstrained?' % _CNS_PORT - CNS_CLEANUP_URL = 'http://chromeperf34:%d/Cleanup' % _CNS_PORT - -# Used for server sanity check. -_TEST_VIDEO = 'roller.webm' - -# Directory root to serve files from. -_ROOT_PATH = os.path.join(pyauto.PyUITest.DataDir(), 'pyauto_private', 'media') - - -class CNSTestBase(pyauto.PyUITest): - """CNS test base hadles startup and teardown of CNS server.""" - - def __init__(self, *args, **kwargs): - """Initialize CNSTestBase by setting the arguments for CNS server. - - Args: - Check cns.py command line argument list for details. - """ - self._port = kwargs.get('port', _CNS_PORT) - self._interface = kwargs.get('interface', 'lo') - self._www_root = kwargs.get('www_root', _ROOT_PATH) - self._verbose = kwargs.get('verbose', True) - self._expiry_time = kwargs.get('expiry_time', 0) - self._socket_timeout = kwargs.get('socket_timeout') - pyauto.PyUITest.__init__(self, *args, **kwargs) - - def setUp(self): - """Ensures the Constrained Network Server (CNS) server is up and running.""" - if USE_LOCAL_CNS: - self._SetUpLocal() - else: - self._SetUpExternal() - - def _SetUpExternal(self): - """Ensures the test can connect to the external CNS server.""" - if self.WaitUntil(self._CanAccessServer, retry_sleep=3, timeout=30, - debug=False): - pyauto.PyUITest.setUp(self) - else: - self.fail('Failed to connect to CNS.') - - def _SetUpLocal(self): - """Starts the CNS server locally.""" - cmd = [sys.executable, os.path.join(pyauto_paths.GetSourceDir(), _CNS_PATH), - '--port', str(self._port), - '--interface', self._interface, - '--www-root', self._www_root, - '--expiry-time', str(self._expiry_time)] - - if self._socket_timeout: - cmd.extend(['--socket-timeout', str(self._socket_timeout)]) - if self._verbose: - cmd.append('-v') - logging.debug('Starting CNS server: %s ', ' '.join(cmd)) - - self._cns_process = subprocess.Popen(cmd, stderr=subprocess.PIPE) - ProcessLogger(self._cns_process) - - if self.WaitUntil(self._CanAccessServer, retry_sleep=3, timeout=30, - debug=False): - pyauto.PyUITest.setUp(self) - else: - self.tearDown() - self.fail('Failed to start CNS.') - - def _CanAccessServer(self): - """Checks if the CNS server can serve a file with no network constraints.""" - test_url = ''.join([CNS_BASE_URL, 'f=', _TEST_VIDEO]) - try: - return urllib2.urlopen(test_url) is not None - except Exception: - return False - - def tearDown(self): - """Stops the Constrained Network Server (CNS).""" - if USE_LOCAL_CNS: - logging.debug('Stopping CNS server.') - # Do not use process.kill(), it will not clean up cns. - self.Kill(self._cns_process.pid) - # Need to wait since the process logger has a lock on the process stderr. - self._cns_process.wait() - self.assertFalse(self._cns_process.returncode is None) - logging.debug('CNS server stopped.') - else: - # Call CNS Cleanup to remove all ports created by this client. - self.NavigateToURL(CNS_CLEANUP_URL) - pyauto.PyUITest.tearDown(self) - - -class ProcessLogger(threading.Thread): - """A thread to log a process's stderr output.""" - - def __init__(self, process): - """Starts the process logger thread. - - Args: - process: The process to log. - """ - threading.Thread.__init__(self) - self._process = process - self.start() - - def run(self): - """Adds debug statements for the process's stderr output.""" - line = True - while line: - line = self._process.stderr.readline() - logging.debug(line.strip()) - - -def GetFileURL(file_name, bandwidth=0, latency=0, loss=0, new_port=False): - """Returns CNS URL for the file with specified constraints. - - Args: - Check cns.ServeConstrained() args for more details. - """ - video_url = [CNS_BASE_URL, 'f=' + file_name] - if bandwidth > 0: - video_url.append('bandwidth=%d' % bandwidth) - if latency > 0: - video_url.append('latency=%d' % latency) - if loss > 0: - video_url.append('loss=%d' % loss) - if new_port: - video_url.append('new_port=%s' % new_port) - return '&'.join(video_url) - - -def CreateCNSPerfTasks(network_constraints_settings, test_media_files): - """Returns a queue of tasks combinining network constrains with media files. - - Args: - network_constraints_settings: List of (setting_name, setting_values) - tupples. - test_media_files: List of media files to run the tests on. - """ - # Convert relative test path into an absolute path. - tasks = Queue.Queue() - for file_name in test_media_files: - for series_name, settings in network_constraints_settings: - logging.debug('Add test: %s\tSettings: %s\tMedia: %s', series_name, - settings, file_name) - tasks.put((series_name, settings, file_name)) - - return tasks diff --git a/chrome/test/functional/media/media_basic_playback.py b/chrome/test/functional/media/media_basic_playback.py deleted file mode 100755 index e70b262..0000000 --- a/chrome/test/functional/media/media_basic_playback.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Basic playback test. Checks playback, seek, and replay based on events. - -This test uses the bear videos from the test matrix in h264, vp8, and theora -formats. -""" -import logging -import os - -import pyauto_media -import pyauto - - -# HTML test path; relative to src/chrome/test/data. -_TEST_HTML_PATH = os.path.join('media', 'html', 'media_basic_playback.html') - -# Test videos to play. TODO(dalecurtis): Convert to text matrix parser when we -# have more test videos in the matrix. Code already written, see patch here: -# https://chromiumcodereview.appspot.com/9290008/#ps12 -_TEST_VIDEOS = [ - pyauto.PyUITest.GetFileURLForContentDataPath('media', name) - for name in ['bear.mp4', 'bear.ogv', 'bear.webm', 'bear_silent.mp4', - 'bear_silent.ogv', 'bear_silent.webm']] - -# Expected events for the first iteration and every iteration thereafter. -_EXPECTED_EVENTS_0 = [('ended', 2), ('playing', 2), ('seeked', 1), - ('suspend', 1)] -_EXPECTED_EVENTS_n = [('abort', 1), ('emptied', 1)] + _EXPECTED_EVENTS_0 - - -class MediaBasicPlaybackTest(pyauto.PyUITest): - """PyAuto test container. See file doc string for more information.""" - - def testBasicPlaybackMatrix(self): - """Launches HTML test which plays each video until end, seeks, and replays. - - Specifically ensures that after the above sequence of events, the following - are true: - - 1. The first video has only 2x playing, 2x ended, and 1x seeked events. - 2. Each subsequent video additionally has 1x abort and 1x emptied due to - switching of the src attribute. - 3. video.currentTime == video.duration for each video. - - See the HTML file at _TEST_HTML_PATH for more information. - """ - self.NavigateToURL(self.GetFileURLForDataPath(_TEST_HTML_PATH)) - - for i, media in enumerate(_TEST_VIDEOS): - logging.debug('Running basic playback test for %s', media) - - # Block until the test finishes and notifies us. Upon return the value of - # video.currentTime == video.duration is provided. - try: - self.assertTrue(self.ExecuteJavascript("startTest('%s');" % media)) - - # PyAuto has trouble with arrays, so convert to string prior to request. - events = self.GetDOMValue("events.join(',')").split(',') - counts = [(item, events.count(item)) for item in sorted(set(events))] - - # The first loop will not have the abort and emptied events triggered by - # changing the video src. - if (i == 0): - self.assertEqual(counts, _EXPECTED_EVENTS_0) - else: - self.assertEqual(counts, _EXPECTED_EVENTS_n) - except: - logging.debug( - 'Test failed with events: %s', self.GetDOMValue("events.join(',')")) - raise - - -if __name__ == '__main__': - pyauto_media.Main() diff --git a/chrome/test/functional/media/media_constrained_network_perf.py b/chrome/test/functional/media/media_constrained_network_perf.py deleted file mode 100755 index d06b82d..0000000 --- a/chrome/test/functional/media/media_constrained_network_perf.py +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Records metrics on playing media under constrained network conditions. - -Spins up a Constrained Network Server (CNS) and runs through a test matrix of -bandwidth, latency, and packet loss settings. Tests running media files defined -in _TEST_MEDIA_EPP record the extra-play-percentage (EPP) metric and the -time-to-playback (TTP) metric in a format consumable by the Chromium perf bots. -Other tests running media files defined in _TEST_MEDIA_NO_EPP record only the -TTP metric. - -Since even a small number of different settings yields a large test matrix, the -design is threaded... however PyAuto is not, so a global lock is used when calls -into PyAuto are necessary. The number of threads can be set by _TEST_THREADS. - -The CNS code is located under: <root>/src/media/tools/constrained_network_server -""" - -import logging -import os -import posixpath -import Queue - -import pyauto_media -import pyauto_utils - -import cns_test_base -import worker_thread - -# The network constraints used for measuring ttp and epp. -# Previous tests with 2% and 5% packet loss resulted in inconsistent data. Thus -# packet loss is not used often in perf tests. Tests with very low bandwidth, -# such as 56K Dial-up resulted in very slow tests (about 8 mins to run each -# test iteration). In addition, metrics for Dial-up would be out of range of the -# other tests metrics, making the graphs hard to read. -_TESTS_TO_RUN = [cns_test_base.Cable, - cns_test_base.Wifi, - cns_test_base.DSL, - cns_test_base.Slow, - cns_test_base.NoConstraints] - -# HTML test path; relative to src/chrome/test/data. Loads a test video and -# records metrics in JavaScript. -_TEST_HTML_PATH = os.path.join( - 'media', 'html', 'media_constrained_network.html') - -# Number of threads to use during testing. -_TEST_THREADS = 3 - -# Number of times we run the same test to eliminate outliers. -_TEST_ITERATIONS = 3 - -# Media file names used for measuring epp and tpp. -_TEST_MEDIA_EPP = ['roller.webm'] -_TEST_MEDIA_EPP.extend(posixpath.join('crowd', name) for name in - ['crowd360.ogv', 'crowd.wav', 'crowd.ogg']) - -# Media file names used for measuring tpp without epp. -_TEST_MEDIA_NO_EPP = [posixpath.join('dartmoor', name) for name in - ['dartmoor2.ogg', 'dartmoor2.m4a', 'dartmoor2.mp3', - 'dartmoor2.wav']] -_TEST_MEDIA_NO_EPP.extend(posixpath.join('crowd', name) for name in - ['crowd1080.webm', 'crowd1080.ogv', 'crowd1080.mp4', - 'crowd360.webm', 'crowd360.mp4']) - -# Timeout values for epp and ttp tests in seconds. -_TEST_EPP_TIMEOUT = 180 -_TEST_TTP_TIMEOUT = 20 - - -class CNSWorkerThread(worker_thread.WorkerThread): - """Worker thread. Runs a test for each task in the queue.""" - - def __init__(self, *args, **kwargs): - """Sets up CNSWorkerThread class variables.""" - # Allocate local vars before WorkerThread.__init__ runs the thread. - self._metrics = {} - self._test_iterations = _TEST_ITERATIONS - worker_thread.WorkerThread.__init__(self, *args, **kwargs) - - def _HaveMetricOrError(self, var_name, unique_url): - """Checks if the page has variable value ready or if an error has occured. - - The varaible value must be set to < 0 pre-run. - - Args: - var_name: The variable name to check the metric for. - unique_url: The url of the page to check for the variable's metric. - - Returns: - True is the var_name value is >=0 or if an error_msg exists. - """ - self._metrics[var_name] = int(self.GetDOMValue(var_name, url=unique_url)) - end_test = self.GetDOMValue('endTest', url=unique_url) - - return self._metrics[var_name] >= 0 or end_test - - def _GetEventsLog(self, unique_url): - """Returns the log of video events fired while running the test. - - Args: - unique_url: The url of the page identifying the test. - """ - return self.GetDOMValue('eventsMsg', url=unique_url) - - def _GetVideoProgress(self, unique_url): - """Gets the video's current play progress percentage. - - Args: - unique_url: The url of the page to check for video play progress. - """ - return int(self.CallJavascriptFunc('calculateProgress', url=unique_url)) - - def RunTask(self, unique_url, task): - """Runs the specific task on the url given. - - It is assumed that a tab with the unique_url is already loaded. - Args: - unique_url: A unique identifier of the test page. - task: A (series_name, settings, file_name, run_epp) tuple. - Returns: - True if at least one iteration of the tests run as expected. - """ - ttp_results = [] - epp_results = [] - # Build video source URL. Values <= 0 mean the setting is disabled. - series_name, settings, (file_name, run_epp) = task - video_url = cns_test_base.GetFileURL( - file_name, bandwidth=settings[0], latency=settings[1], - loss=settings[2], new_port=True) - - graph_name = series_name + '_' + os.path.basename(file_name) - for iter_num in xrange(self._test_iterations): - # Start the test! - self.CallJavascriptFunc('startTest', [video_url], url=unique_url) - - # Wait until the necessary metrics have been collected. - self._metrics['epp'] = self._metrics['ttp'] = -1 - self.WaitUntil(self._HaveMetricOrError, args=['ttp', unique_url], - retry_sleep=1, timeout=_TEST_EPP_TIMEOUT, debug=False) - # Do not wait for epp if ttp is not available. - if self._metrics['ttp'] >= 0: - ttp_results.append(self._metrics['ttp']) - if run_epp: - self.WaitUntil( - self._HaveMetricOrError, args=['epp', unique_url], retry_sleep=2, - timeout=_TEST_EPP_TIMEOUT, debug=False) - - if self._metrics['epp'] >= 0: - epp_results.append(self._metrics['epp']) - - logging.debug('Iteration:%d - Test %s ended with %d%% of the video ' - 'played.', iter_num, graph_name, - self._GetVideoProgress(unique_url),) - - if self._metrics['ttp'] < 0 or (run_epp and self._metrics['epp'] < 0): - logging.error('Iteration:%d - Test %s failed to end gracefully due ' - 'to time-out or error.\nVideo events fired:\n%s', - iter_num, graph_name, self._GetEventsLog(unique_url)) - - # End of iterations, print results, - pyauto_utils.PrintPerfResult('ttp', graph_name, ttp_results, 'ms') - - # Return true if we got at least one result to report. - if run_epp: - pyauto_utils.PrintPerfResult('epp', graph_name, epp_results, '%') - return len(epp_results) != 0 - return len(ttp_results) != 0 - - -class MediaConstrainedNetworkPerfTest(cns_test_base.CNSTestBase): - """PyAuto test container. See file doc string for more information.""" - - def _RunDummyTest(self, test_path): - """Runs a dummy test with high bandwidth and no latency or packet loss. - - Fails the unit test if the dummy test does not end. - - Args: - test_path: Path to HTML/JavaScript test code. - """ - tasks = Queue.Queue() - tasks.put(('Dummy Test', [5000, 0, 0], (_TEST_MEDIA_EPP[0], True))) - # Dummy test should successfully finish by passing all the tests. - if worker_thread.RunWorkerThreads(self, CNSWorkerThread, tasks, 1, - test_path): - self.fail('Failed to run dummy test.') - - def testConstrainedNetworkPerf(self): - - """Starts CNS, spins up worker threads to run through _TEST_CONSTRAINTS.""" - # Run a dummy test to avoid Chrome/CNS startup overhead. - logging.debug('Starting a dummy test to avoid Chrome/CNS startup overhead.') - self._RunDummyTest(_TEST_HTML_PATH) - logging.debug('Dummy test has finished. Starting real perf tests.') - - # Tests that wait for EPP metrics. - media_files = [(name, True) for name in _TEST_MEDIA_EPP] - media_files.extend((name, False) for name in _TEST_MEDIA_NO_EPP) - tasks = cns_test_base.CreateCNSPerfTasks(_TESTS_TO_RUN, media_files) - if worker_thread.RunWorkerThreads(self, CNSWorkerThread, tasks, - _TEST_THREADS, _TEST_HTML_PATH): - self.fail('Some tests failed to run as expected.') - - -if __name__ == '__main__': - pyauto_media.Main() diff --git a/chrome/test/functional/media/media_jerky.py b/chrome/test/functional/media/media_jerky.py deleted file mode 100755 index 6d1424b..0000000 --- a/chrome/test/functional/media/media_jerky.py +++ /dev/null @@ -1,224 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Jerkiness performance test for video playback. - -Uses jerky tool, (http://go/jerky), to record a jerkiness metric for videos -sensitive to jerkiness. - -Jerkiness is defined as a percentage of the average on screen frame time by the -formula below. Where smoothed_frame_time[i] represents a frame's on screen time -plus amortized measurement gap error (time taken to capture each frame). - -sqrt(average((avg_frame_time - smoothed_frame_time[i])^2, i=m..n)) ------------------------------------------------------------------- - avg_frame_time - -Currently, only the Linux binaries are checked in for this test since we only -have a Linux performance bot. The current binary is a custom build with some -fixes from veganjerky (http://go/veganjerky) for timing, waiting, and stdout -flushing. - -TODO(dalecurtis): Move Jerky tool sources into the Chromium tree. - -TODO(dalecurtis): Jerky tool uses a questionable method for determining torn -frames, determine if it is actually worth recording. -""" - -import glob -import logging -import os -import re -import subprocess -import tempfile - -import pyauto_media -import pyauto -import pyauto_utils - -# HTML test path; relative to src/chrome/test/data. -_TEST_HTML_PATH = os.path.join('media', 'html', 'media_jerky.html') - -# Path under data path for test files. -_TEST_MEDIA_PATH = os.path.join('pyauto_private', 'media', 'birds') - -# Path to Jerky tool executable. -_JERKY_PATH = os.path.join('pyauto_private', 'media', 'tools', 'jerky') - -# Regular expression for extracting jerkiness percentage. Sample line: -# using 1:9 title 'test.log (35.36% jerky, 0 teared frames)' lw 2 with lines -_JERKY_LOG_REGEX = re.compile( - r'\((\d{0,3}\.?\d{0,2})% jerky, (\d+) teared frames\)') - -# Regular expression for extracting computed fps. Sample line: -# INFO: 33797 us per frame => 29.6 fps. -_JERKY_LOG_FPS_REGEX = re.compile(r' => (\d+\.\d+) fps') - -# Minimum and maximum number of iterations for each test. Due to timing issues -# the jerky tool may not always calculate the fps correctly. When invalid -# results are detected, the test is rerun up to the maxium # of times set below. -_JERKY_ITERATIONS_MIN = 3 -_JERKY_ITERATIONS_MAX = 10 - -# The media files used for testing. Each entry represents a tuple of (filename, -# width, height, fps). The width and height are used to create a calibration -# pattern for jerky tool. The fps is used to ensure Jerky tool computed a valid -# result. -_TEST_VIDEOS = [('birds540.webm', 960, 540, 29.9)] - - -def GetTempFilename(): - """Returns an absolute path to an empty temp file.""" - f, path = tempfile.mkstemp(prefix='jerky_tmp') - os.close(f) - return path - - -class MediaJerkyPerfTest(pyauto.PyUITest): - """PyAuto test container. See file doc string for more information.""" - - def StartJerkyCapture(self): - """Starts jerky tool in capture mode and waits until its ready to capture. - - Returns: - A tuple of the jerky process and an absolute path to the capture log. - """ - jerky_log = GetTempFilename() - logging.debug('Logging data to %s', jerky_log) - process = subprocess.Popen( - [os.path.join(self.DataDir(), _JERKY_PATH), - 'capture', '--log', jerky_log], - stdout=subprocess.PIPE) - - # Set the jerky tool process to soft-realtime w/ round-robin scheduling. - subprocess.check_call(['sudo', 'chrt', '-r', '-p', str(process.pid)]) - - # Wait for server to start up. - line = True - while line: - line = process.stdout.readline() - if 'Waiting for calibration pattern to disappear' in line: - return process, jerky_log - self.fail('Failed to launch Jerky tool.') - - def AnalyzeJerkyCapture(self, jerky_log): - """Run jerky analyze on the specified log and return various metrics. - - Once analyze has completed, the jerky_log and associated outputs will be - removed. - - Args: - jerky_log: Absolute path to the capture log. - - Returns: - Tuple of fps, jerkiness, and torn frames. - """ - results_log_base = GetTempFilename() - process = subprocess.Popen( - [os.path.join(self.DataDir(), _JERKY_PATH), - 'analyze', '--ref', jerky_log, '--out', results_log_base], - stdout=subprocess.PIPE) - - # Wait for process to end w/o risking deadlock. - stdout = process.communicate()[0] - self.assertEquals(process.returncode, 0) - - # Scrape out the calculated FPS. - fps_match = None - for line in stdout.splitlines(): - fps_match = _JERKY_LOG_FPS_REGEX.search(line) - if fps_match: - break - - # Open *.error.gnuplot and scrape out jerkiness. - jerky_match = None - with open('%s.error.gnuplot' % results_log_base) as results: - for line in results: - jerky_match = _JERKY_LOG_REGEX.search(line) - if jerky_match: - break - - # Cleanup all the temp and results files jerky spits out. - for log in glob.glob('%s*' % results_log_base) + [jerky_log]: - os.unlink(log) - - if fps_match and jerky_match: - return (float(fps_match.group(1)), float(jerky_match.group(1)), - int(jerky_match.group(2))) - return None, None, None - - def testMediaJerkyPerformance(self): - """Launches Jerky tool and records jerkiness for HTML5 videos. - - For each video, the test starts up jerky tool then plays until the Jerky - tool collects enough information. Next the capture log is analyzed using - Jerky's analyze command. If the computed fps matches the expected fps the - jerkiness metric is recorded. - - The test will run up to _JERKY_ITERATIONS_MAX times in an attempt to get at - least _JERKY_ITERATIONS_MIN valid values. The results are recorded under - the 'jerkiness' variable for graphing on the bots. - """ - self.NavigateToURL(self.GetFileURLForDataPath(_TEST_HTML_PATH)) - - # Xvfb on the bots is restricted to 1024x768 at present. Ensure we're using - # all of the real estate we can. Jerky tool needs a clear picture of every - # frame, so we can't clip the video in any way. - self.SetWindowDimensions(0, 0, 1024, 768) - - for name, width, height, expected_fps in _TEST_VIDEOS: - jerkiness = [] - torn_frames = [] - file_url = self.GetFileURLForDataPath( - os.path.join(_TEST_MEDIA_PATH, name)) - - # Initialize the calibration area for Jerky tool. - self.assertTrue(self.ExecuteJavascript( - 'initializeTest(%d, %d);' % (width, height))) - - runs_left = _JERKY_ITERATIONS_MIN - runs_total = 0 - while runs_left > 0 and runs_total < _JERKY_ITERATIONS_MAX: - runs_total += 1 - logging.info('Running Jerky perf test #%d for %s.', runs_total, name) - - # Startup Jerky tool in capture mode. - jerky_process, jerky_log = self.StartJerkyCapture() - - # Start playback of the test video. - self.assertTrue(self.ExecuteJavascript("startTest('%s');" % file_url)) - - # Wait for jerky tool to finish if it hasn't already. - self.assertTrue(jerky_process.wait() == 0) - - # Stop playback of the test video so the next run can cleanly find the - # calibration zone. - self.assertTrue(self.ExecuteJavascript('stopTest();')) - - # Analyze the results. - jerky_fps, jerky_percent, jerky_torn_frames = self.AnalyzeJerkyCapture( - jerky_log) - if (jerky_fps is None or jerky_percent is None or - jerky_torn_frames is None): - logging.error('No metrics recorded for this run.') - continue - - # Ensure the results for this run are valid. - if jerky_fps != expected_fps: - logging.error( - 'Invalid fps detected (actual: %f, expected: %f, jerkiness: %f). ' - 'Discarding results for this run.', jerky_fps, expected_fps, - jerky_percent) - continue - - jerkiness.append(jerky_percent) - torn_frames.append(jerky_torn_frames) - runs_left -= 1 - - pyauto_utils.PrintPerfResult('jerkiness', name, jerkiness, '%') - - -if __name__ == '__main__': - pyauto_media.Main() diff --git a/chrome/test/functional/media/media_scrub_perf.py b/chrome/test/functional/media/media_scrub_perf.py deleted file mode 100755 index e8d605a..0000000 --- a/chrome/test/functional/media/media_scrub_perf.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Scrubbing performance test for <video>. - -Measures the times to scrub video and audio files. Scrubbing is simulated by -having consecutive small seeks performed. The test measures both forward and -backward scrubbing. -""" - -import os - -import pyauto_media -import pyauto_utils -import pyauto - -# HTML test path; relative to src/chrome/test/data. -_TEST_HTML_PATH = os.path.join('media', 'html', 'media_scrub.html') - -# Path under data path for test files. -_TEST_MEDIA_PATH = os.path.join('media', 'avperf') - -# The media files used for testing. -_TEST_MEDIA = [os.path.join('tulip', name) for name in - ['tulip2.webm', 'tulip2.wav', 'tulip2.ogv', 'tulip2.ogg', - 'tulip2.mp4', 'tulip2.mp3', 'tulip2.m4a']] - - -class MediaScrubPerfTest(pyauto.PyUITest): - """PyAuto test container. See file doc string for more information.""" - - def testMediaScrubPerformance(self): - """Launches HTML test which runs the scrub test and records performance.""" - self.NavigateToURL(self.GetFileURLForDataPath(_TEST_HTML_PATH)) - - for media in _TEST_MEDIA: - file_name = self.GetFileURLForDataPath( - os.path.join(_TEST_MEDIA_PATH, media)) - - # Some tests take more than the default PyAuto calls timeout, so we start - # each test and wait until 'testDone' flag is set by the test. - self.CallJavascriptFunc('startTest', [file_name]) - - if not self.WaitUntil(self.GetDOMValue, args=['testDone'], - retry_sleep=5, timeout=180, debug=False): - error_msg = 'Scrubbing tests timed out.' - else: - error_msg = self.GetDOMValue('errorMsg') - if error_msg: - self.fail('Error while running the test: %s' % error_msg) - - forward_scrub_time = float(self.GetDOMValue('forwardScrubTime')) - backward_scrub_time = float(self.GetDOMValue('backwardScrubTime')) - mixed_scrub_time = float(self.GetDOMValue('mixedScrubTime')) - pyauto_utils.PrintPerfResult('scrubbing', os.path.basename(file_name) + - '_forward', forward_scrub_time, 'ms') - pyauto_utils.PrintPerfResult('scrubbing', os.path.basename(file_name) + - '_backward', backward_scrub_time, 'ms') - pyauto_utils.PrintPerfResult('scrubbing', os.path.basename(file_name) + - '_mixed', mixed_scrub_time, 'ms') - - -if __name__ == '__main__': - pyauto_media.Main() diff --git a/chrome/test/functional/media/media_seek_perf.py b/chrome/test/functional/media/media_seek_perf.py deleted file mode 100755 index b0e86b5..0000000 --- a/chrome/test/functional/media/media_seek_perf.py +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Seek performance testing for <video>. - -Calculates the short and long seek times for different video formats on -different network constraints. -""" - -import logging -import os -import posixpath - -import pyauto_media -import pyauto_utils - -import cns_test_base -import worker_thread - -# Number of threads to use during testing. -_TEST_THREADS = 3 - -# HTML test path; relative to src/chrome/test/data. -_TEST_HTML_PATH = os.path.join('media', 'html', 'media_seek.html') - -# The media files used for testing. -# Path under CNS root folder (pyauto_private/media). -_TEST_VIDEOS = [posixpath.join('dartmoor', name) for name in - ['dartmoor2.mp3', 'dartmoor2.wav']] - -_TEST_VIDEOS.extend([posixpath.join('crowd', name) for name in - ['crowd1080.webm', 'crowd1080.ogv', 'crowd1080.mp4', - 'crowd360.webm', 'crowd360.ogv', 'crowd360.mp4']]) - -# Constraints to run tests on. -_TESTS_TO_RUN = [ - cns_test_base.Wifi, - cns_test_base.NoConstraints] - - -class SeekWorkerThread(worker_thread.WorkerThread): - """Worker thread. Runs a test for each task in the queue.""" - - def RunTask(self, unique_url, task): - """Runs the specific task on the url given. - - It is assumed that a tab with the unique_url is already loaded. - Args: - unique_url: A unique identifier of the test page. - task: A (series_name, settings, file_name) tuple to run the test on. - """ - series_name, settings, file_name = task - - video_url = cns_test_base.GetFileURL( - file_name, bandwidth=settings[0], latency=settings[1], - loss=settings[2]) - - # Start the test! - self.CallJavascriptFunc('startTest', [video_url], unique_url) - - logging.debug('Running perf test for %s.', video_url) - # Time out is dependent on (seeking time * iterations). For 3 iterations - # per seek we get total of 18 seeks per test. We expect buffered and - # cached seeks to be fast. Through experimentation an average of 10 secs - # per seek was found to be adequate. - if not self.WaitUntil(self.GetDOMValue, args=['endTest', unique_url], - retry_sleep=5, timeout=300, debug=False): - error_msg = 'Seek tests timed out.' - else: - error_msg = self.GetDOMValue('errorMsg', unique_url) - - cached_states = self.GetDOMValue( - "Object.keys(CachedState).join(',')", unique_url).split(',') - seek_test_cases = self.GetDOMValue( - "Object.keys(SeekTestCase).join(',')", unique_url).split(',') - - graph_name = series_name + '_' + os.path.basename(file_name) - for state in cached_states: - for seek_case in seek_test_cases: - values = self.GetDOMValue( - "seekRecords[CachedState.%s][SeekTestCase.%s].join(',')" % - (state, seek_case), unique_url) - if values: - results = [float(value) for value in values.split(',')] - else: - results = [] - pyauto_utils.PrintPerfResult('seek_%s_%s' % (state.lower(), - seek_case.lower()), graph_name, - results, 'ms') - - if error_msg: - logging.error('Error while running %s: %s.', graph_name, error_msg) - return False - else: - return True - - -class MediaSeekPerfTest(cns_test_base.CNSTestBase): - """PyAuto test container. See file doc string for more information.""" - - def __init__(self, *args, **kwargs): - """Initialize the CNSTestBase with socket_timeout = 60 secs.""" - cns_test_base.CNSTestBase.__init__(self, socket_timeout='60', - *args, **kwargs) - - def testMediaSeekPerformance(self): - """Launches HTML test which plays each video and records seek stats.""" - tasks = cns_test_base.CreateCNSPerfTasks(_TESTS_TO_RUN, _TEST_VIDEOS) - if worker_thread.RunWorkerThreads(self, SeekWorkerThread, tasks, - _TEST_THREADS, _TEST_HTML_PATH): - self.fail('Some tests failed to run as expected.') - - -if __name__ == '__main__': - pyauto_media.Main() diff --git a/chrome/test/functional/media/media_stat_perf.py b/chrome/test/functional/media/media_stat_perf.py deleted file mode 100755 index f212848..0000000 --- a/chrome/test/functional/media/media_stat_perf.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""CPU, Memory, and FPS performance test for <video>. - -Calculates decoded fps, dropped fps, CPU, and memory statistics while playing -HTML5 media element. The test compares results of playing a media file on -different video resolutions. -""" - -import logging -import os -import psutil - -import pyauto_media -import pyauto -import pyauto_utils - -# HTML test path; relative to src/chrome/test/data. -_TEST_HTML_PATH = os.path.join('media', 'html', 'media_stat_perf.html') - -# Path under data path for test files. -_TEST_MEDIA_PATH_CROWD = os.path.join('pyauto_private', 'media', 'crowd') - -# Path under data path for test files. -_TEST_MEDIA_PATH_TULIP = os.path.join('media', 'avperf', 'tulip') -# The media files used for testing. -_TEST_VIDEOS = [os.path.join(_TEST_MEDIA_PATH_CROWD, name) for name in [ - 'crowd2160.webm', 'crowd1080.webm']] -_TEST_VIDEOS.extend([os.path.join(_TEST_MEDIA_PATH_TULIP, name) for name in [ - 'tulip2.webm', 'tulip2.wav', 'tulip2.ogv', 'tulip2.ogg', 'tulip2.mp4', - 'tulip2.mp3', 'tulip2.m4a']]) - -class MediaStatsPerfTest(pyauto.PyUITest): - """PyAuto test container. See file doc string for more information.""" - - def _GetChromeRendererProcess(self): - """Returns the Chrome renderer process.""" - renderer_id = self.GetBrowserInfo()['windows'][0]['tabs'][1]['renderer_pid'] - if not renderer_id: - self.fail('Can not find the tab renderer process.') - return psutil.Process(renderer_id) - - def testMediaPerformance(self): - """Launches HTML test which plays each video and records statistics.""" - for file_name in _TEST_VIDEOS: - # Append a tab and delete it at the end of the test to free its memory. - self.AppendTab(pyauto.GURL(self.GetFileURLForDataPath(_TEST_HTML_PATH))) - - file_url = self.GetFileURLForDataPath(file_name) - logging.debug('Running perf test for %s.', file_url) - - renderer_process = self._GetChromeRendererProcess() - # Call to set a starting time to record CPU usage by the renderer. - renderer_process.get_cpu_percent() - - self.assertTrue( - self.CallJavascriptFunc('startTest', [file_url], tab_index=1)) - - cpu_usage = renderer_process.get_cpu_percent() - mem_usage_mb = renderer_process.get_memory_info()[0] / 1024 - file_name = os.path.basename(file_name) - pyauto_utils.PrintPerfResult('cpu', file_name, cpu_usage, '%') - pyauto_utils.PrintPerfResult('memory', file_name, mem_usage_mb, 'KB') - - decoded_fps = [ - float(value) for value in - self.GetDOMValue("decodedFPS.join(',')", tab_index=1).split(',')] - dropped_frames = self.GetDOMValue('droppedFrames', tab_index=1) - dropped_fps = [ - float(value) for value in - self.GetDOMValue("droppedFPS.join(',')", tab_index=1).split(',')] - - pyauto_utils.PrintPerfResult('fps', file_name, decoded_fps, 'fps') - pyauto_utils.PrintPerfResult('dropped_fps', file_name, dropped_fps, 'fps') - pyauto_utils.PrintPerfResult('dropped_frames', file_name, dropped_frames, - 'frames') - - self.CloseTab(tab_index=1) - - -if __name__ == '__main__': - pyauto_media.Main() diff --git a/chrome/test/functional/media/mixed_audio_latency_perf.py b/chrome/test/functional/media/mixed_audio_latency_perf.py deleted file mode 100755 index f3764d7..0000000 --- a/chrome/test/functional/media/mixed_audio_latency_perf.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Audio latency performance test. - -Benchmark measuring how fast we can continuously repeat a short sound -clip, while another clip is running in the background. In the ideal -scenario we'd have zero latency processing script, seeking back to the -beginning of the clip, and resuming audio playback. - -Performance is recorded as the average latency of N playbacks. I.e., if we play -a clip 50 times and the ideal duration of all playbacks is 1000ms and the total -duration is 1500ms, the recorded result is (1500ms - 1000ms) / 50 == 10ms. -""" -import os - -import pyauto_media -import pyauto_utils -import pyauto - - -# HTML test path; relative to src/chrome/test/data. -_TEST_HTML_PATH = os.path.join('media', 'html', 'mixed_audio_latency_perf.html') - - -class MixedAudioLatencyPerfTest(pyauto.PyUITest): - """PyAuto test container. See file doc string for more information.""" - - def testAudioLatency(self): - """Launches HTML test which runs the audio latency test.""" - self.NavigateToURL(self.GetFileURLForDataPath(_TEST_HTML_PATH)) - - # Block until the test finishes and notifies us. - self.assertTrue(self.ExecuteJavascript('startTest();')) - latency = float(self.GetDOMValue('averageLatency')) - pyauto_utils.PrintPerfResult('audio_latency', 'latency_bg_clip', latency, - 'ms') - - -if __name__ == '__main__': - pyauto_media.Main() diff --git a/chrome/test/functional/media/pyauto_media.py b/chrome/test/functional/media/pyauto_media.py deleted file mode 100644 index 952330b2..0000000 --- a/chrome/test/functional/media/pyauto_media.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2012 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. - -"""PyAuto media test base. Handles PyAuto initialization and path setup. - -Required to ensure each media test can load the appropriate libraries. Each -test must include this snippet: - - # This should be at the top - import pyauto_media - - <test code> - - # This should be at the bottom. - if __name__ == '__main__': - pyauto_media.Main() -""" - -import os -import sys - - -def _SetupPaths(): - """Add paths required for loading PyAuto and other utilities to sys.path.""" - media_dir = os.path.abspath(os.path.dirname(__file__)) - sys.path.append(media_dir) - sys.path.append(os.path.normpath(os.path.join(media_dir, os.pardir))) - - # Add psutil library path. - # TODO(dalecurtis): This should only be added for tests which use psutil. - sys.path.append(os.path.normpath(os.path.join( - media_dir, os.pardir, os.pardir, os.pardir, os.pardir, - 'third_party', 'psutil'))) - - -_SetupPaths() - - -import pyauto_functional -Main = pyauto_functional.Main diff --git a/chrome/test/functional/media/worker_thread.py b/chrome/test/functional/media/worker_thread.py deleted file mode 100644 index 394d051..0000000 --- a/chrome/test/functional/media/worker_thread.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) 2012 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. - -"""Worker thread base class. - -Worker threads are used to run multiple PyUITests simultaneously. They -synchronize calls to the browser.""" - -import itertools -import threading -import pyauto - - -# A static lock used to synchronize worker threads access to the browser. -__lock = threading.RLock() - -def synchronized(fn): - """A decorator to wrap a lock around function calls.""" - def syncFun(*args, **kwargs): - with __lock: - return fn(*args, **kwargs) - - return syncFun - - -def RunWorkerThreads(pyauto_test, test_worker_class, tasks, num_threads, - test_path): - """Creates a matrix of tasks and starts test worker threads to run them. - - Args: - pyauto_test: Reference to a pyauto.PyUITest instance. - test_worker_class: WorkerThread class reference. - tasks: Queue of tasks to run by the worker threads. - num_threads: Number of threads to run simultaneously. - test_path: Path to HTML/JavaScript test code. - """ - # Convert relative test path into an absolute path. - test_url = pyauto_test.GetFileURLForDataPath(test_path) - - # Add shutdown magic to end of queue. - for _ in xrange(num_threads): - tasks.put(None) - - threads = [] - for _ in xrange(num_threads): - threads.append(test_worker_class(pyauto_test, tasks, test_url)) - - # Wait for threads to exit, gracefully or otherwise. - for thread in threads: - thread.join() - - return sum(thread.failures for thread in threads) - - -class WorkerThread(threading.Thread): - """Thread which for each queue task: opens tab, runs task, closes tab.""" - - # Atomic, monotonically increasing task identifier. Used to ID tabs. - _task_id = itertools.count() - - def __init__(self, pyauto_test, tasks, url): - """Sets up WorkerThread class variables. - - Args: - pyauto_test: Reference to a pyauto.PyUITest instance. - tasks: Queue containing task tuples used by RunTest(). - url: File URL to HTML/JavaScript test code. - """ - threading.Thread.__init__(self) - self.__pyauto = pyauto_test - self.__tasks = tasks - self.__url = url - self.failures = 0 - self.start() - - def RunTask(self, unique_url, task): - """Runs the specific task on the url test page. - - This method should be overridden to start the test on the unique_url page. - - Args: - unique_url: A unique identifier of the test page. - task: A tuple with information needed to run the test. - Returns: - True if the task finished as expected. - """ - raise NotImplementedError('RunTask should be defined in a subclass.') - - def run(self): - """For each task in queue: opens new tab, calls RunTask(), then closes tab. - - No exception handling is done to make sure the main thread exits properly - during Chrome crashes or other failures. - - For a clean shutdown, put the magic exit value None in the queue. - """ - while True: - task = self.__tasks.get() - # Check for magic exit values. - if task is None: - break - # Make the test URL unique so we can figure out our tab index later. - unique_url = '%s?%d' % (self.__url, WorkerThread._task_id.next()) - self.AppendTab(unique_url) - if not self.RunTask(unique_url, task): - self.failures += 1 - self.CloseTabByURL(unique_url) - self.__tasks.task_done() - - def __FindTabLocked(self, url): - """Returns the tab index for the tab belonging to this url. - - __lock must be owned by caller. - """ - if url is None: - return 0 - for tab in self.__pyauto.GetBrowserInfo()['windows'][0]['tabs']: - if tab['url'] == url: - return tab['index'] - - # The following are wrappers to pyauto.PyUITest functions. They are wrapped - # with an internal lock to avoid problems when more than one thread edits the - # state of the browser. - # - # We limit access of subclasses to the following functions. If subclasses - # access other PyUITest functions, then there is no guarantee of thread - # safety. - # - # For details on the args check pyauto.PyUITest class. - @synchronized - def AppendTab(self, url): - self.__pyauto.AppendTab(pyauto.GURL(url)) - - @synchronized - def CallJavascriptFunc(self, fun_name, fun_args=[], url=None): - return self.__pyauto.CallJavascriptFunc(fun_name, fun_args, - tab_index=self.__FindTabLocked(url)) - - @synchronized - def CloseTabByURL(self, url): - """Closes the tab with the given url.""" - self.__pyauto.CloseTab(tab_index=self.__FindTabLocked(url)) - - @synchronized - def GetDOMValue(self, name, url=None): - return self.__pyauto.GetDOMValue(name, tab_index=self.__FindTabLocked(url)) - - def WaitUntil(self, *args, **kwargs): - """We do not need to lock WaitUntil since it does not call into Chrome. - - Ensure that the function passed in the args is thread safe. - """ - return self.__pyauto.WaitUntil(*args, **kwargs) diff --git a/chrome/test/functional/memory.py b/chrome/test/functional/memory.py deleted file mode 100755 index ee2ae4c..0000000 --- a/chrome/test/functional/memory.py +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import os -import sys -import time - -import pyauto_functional # Must be imported before pyauto -import pyauto -import test_utils - - -class MemoryTest(pyauto.PyUITest): - """Tests for memory usage of Chrome-related processes. - - These tests are meant to be used manually, not as part of the continuous - test cycle. This is because each test starts up and periodically - measures/records the memory usage of a relevant Chrome process, doing so - repeatedly until the test is manually killed. Currently, this script only - works in Linux and ChromeOS, as it uses a Linux shell command to query the - system for process memory usage info (test_utils.GetMemoryUsageOfProcess()). - - The tests in this suite produce the following output files (relative to the - current working directory): - - testTabRendererProcessMemoryUsage: 'renderer_process_mem.txt' - testExtensionProcessMemoryUsage: 'extension_process_mem.txt' - """ - - # Constants for all tests in this suite. - NUM_SECONDS_BETWEEN_MEASUREMENTS = 10 - MEASUREMENT_LOG_MESSAGE_TEMPLATE = '[%s] %.2f MB (pid: %d)' - LOG_TO_OUTPUT_FILE = True - - # Constants for testTabRendererProcessMemoryUsage. - RENDERER_PROCESS_URL = 'http://chrome.angrybirds.com' - RENDERER_PROCESS_OUTPUT_FILE = 'renderer_process_mem.txt' - - # Constants for testExtensionProcessMemoryUsage. - EXTENSION_LOCATION = os.path.abspath(os.path.join( - pyauto.PyUITest.DataDir(), 'extensions', 'google_talk.crx')) - EXTENSION_PROCESS_NAME = 'Google Talk' - EXTENSION_PROCESS_OUTPUT_FILE = 'extension_process_mem.txt' - - def _GetPidOfExtensionProcessByName(self, name): - """Identifies the process ID of an extension process, given its name. - - Args: - name: The string name of an extension process, as returned by the function - GetBrowserInfo(). - - Returns: - The integer process identifier (PID) for the specified process, or - None if the PID cannot be identified. - """ - info = self.GetBrowserInfo()['extension_views'] - pid = [x['pid'] for x in info if x['name'] == '%s' % name] - if pid: - return pid[0] - return None - - def _LogMessage(self, log_file, msg): - """Logs a message to the screen, and to a log file if necessary. - - Args: - log_file: The string name of a log file to which to write. - msg: The message to log. - """ - print msg - sys.stdout.flush() - if self.LOG_TO_OUTPUT_FILE: - print >>open(log_file, 'a'), msg - - def testTabRendererProcessMemoryUsage(self): - """Test the memory usage of the renderer process for a tab. - - This test periodically queries the system for the current memory usage - of a tab's renderer process. The test will take measurements forever; you - must manually kill the test to terminate it. - """ - if (self.LOG_TO_OUTPUT_FILE and - os.path.exists(self.RENDERER_PROCESS_OUTPUT_FILE)): - os.remove(self.RENDERER_PROCESS_OUTPUT_FILE) - self.NavigateToURL(self.RENDERER_PROCESS_URL) - self._LogMessage( - self.RENDERER_PROCESS_OUTPUT_FILE, - 'Memory usage for renderer process of a tab navigated to: "%s"' % ( - self.RENDERER_PROCESS_URL)) - - # A user must manually kill this test to terminate the following loop. - while True: - pid = self.GetBrowserInfo()['windows'][0]['tabs'][0]['renderer_pid'] - usage = test_utils.GetMemoryUsageOfProcess(pid) - current_time = time.asctime(time.localtime(time.time())) - self._LogMessage( - self.RENDERER_PROCESS_OUTPUT_FILE, - self.MEASUREMENT_LOG_MESSAGE_TEMPLATE % (current_time, usage, pid)) - time.sleep(self.NUM_SECONDS_BETWEEN_MEASUREMENTS) - - def testExtensionProcessMemoryUsage(self): - """Test the memory usage of an extension process. - - This test periodically queries the system for the current memory usage - of an extension process. The test will take measurements forever; you - must manually kill the test to terminate it. - """ - if (self.LOG_TO_OUTPUT_FILE and - os.path.exists(self.EXTENSION_PROCESS_OUTPUT_FILE)): - os.remove(self.EXTENSION_PROCESS_OUTPUT_FILE) - self.InstallExtension(self.EXTENSION_LOCATION) - # The PID is 0 until the extension has a chance to start up. - self.WaitUntil( - lambda: self._GetPidOfExtensionProcessByName( - self.EXTENSION_PROCESS_NAME) not in [0, None]) - self._LogMessage( - self.EXTENSION_PROCESS_OUTPUT_FILE, - 'Memory usage for extension process with name: "%s"' % ( - self.EXTENSION_PROCESS_NAME)) - - # A user must manually kill this test to terminate the following loop. - while True: - pid = self._GetPidOfExtensionProcessByName(self.EXTENSION_PROCESS_NAME) - usage = test_utils.GetMemoryUsageOfProcess(pid) - current_time = time.asctime(time.localtime(time.time())) - self._LogMessage( - self.EXTENSION_PROCESS_OUTPUT_FILE, - self.MEASUREMENT_LOG_MESSAGE_TEMPLATE % (current_time, usage, pid)) - time.sleep(self.NUM_SECONDS_BETWEEN_MEASUREMENTS) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/multiprofile.py b/chrome/test/functional/multiprofile.py deleted file mode 100755 index c715db2..0000000 --- a/chrome/test/functional/multiprofile.py +++ /dev/null @@ -1,314 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import re - -import pyauto_functional -import pyauto - - -class MultiprofileTest(pyauto.PyUITest): - """Tests for Multi-Profile / Multi-users""" - - _RESTORE_STARTUP_URL_VALUE = 4 - _RESTORE_LASTOPEN_URL_VALUE = 1 - _RESTORE_DEFAULT_URL_VALUE = 0 - - def Debug(self): - """Test method for experimentation. - - This method will not run automatically. - """ - while True: - raw_input('Hit <enter> to dump info.. ') - self.pprint(self.GetMultiProfileInfo()) - - def _GetSearchEngineWithKeyword(self, keyword, windex=0): - """Get search engine info and return an element that matches keyword. - - Args: - keyword: Search engine keyword field. - windex: The window index, default is 0. - - Returns: - A search engine info dict or None. - """ - match_list = ([x for x in self.GetSearchEngineInfo(windex=windex) - if x['keyword'] == keyword]) - if match_list: - return match_list[0] - return None - - def _SetPreferences(self, dict, windex=0): - """Sets preferences settings. - - Args: - _dict: Dictionary of key preferences and its value to be set. - windex: The window index, defaults to 0 (the first window). - """ - for key in dict.iterkeys(): - self.SetPrefs(key, dict[key], windex=windex) - - def _SetStartUpPage(self, url, windex=0): - """Set start up page. - - Args: - url: URL of the page to be set as start up page. - windex: The window index, default is 0. - """ - _dict = {pyauto.kURLsToRestoreOnStartup: [url], - pyauto.kRestoreOnStartup: self._RESTORE_STARTUP_URL_VALUE} - self._SetPreferences(_dict, windex=windex) - prefs_info = self.GetPrefsInfo(windex=windex).Prefs( - pyauto.kURLsToRestoreOnStartup) - self.assertTrue(url in prefs_info) - - def _SetHomePage(self, url, windex=0): - """Create new profile and set home page. - - Args: - url: URL of the page to be set as home page - windex: The window index, default is 0. - """ - _dict = {pyauto.kHomePage: url, - pyauto.kHomePageIsNewTabPage: False, pyauto.kShowHomeButton: True, - pyauto.kRestoreOnStartup: self._RESTORE_DEFAULT_URL_VALUE} - self._SetPreferences(_dict, windex=windex) - self.assertTrue(url in - self.GetPrefsInfo(windex=windex).Prefs(pyauto.kHomePage)) - - def _SetSessionRestoreURLs(self, set_restore, windex=0): - """Create new profile and set home page. - - Args: - set_restore: Value of action of start up. - windex: The window index, default is 0. - """ - self.NavigateToURL('http://www.google.com/', windex) - self.AppendTab(pyauto.GURL('http://news.google.com/'), windex) - num_tabs = self.GetTabCount(windex) - dict = {pyauto.kRestoreOnStartup: set_restore} - self._SetPreferences(dict, windex=windex) - - def _AddSearchEngine(self, title, keyword, url, windex=0): - """Add search engine. - - Args: - title: Name for search engine. - keyword: Keyword, used to initiate a custom search from omnibox. - url: URL template for this search engine's query. - windex: The window index, default is 0. - """ - self.AddSearchEngine(title, keyword, url, windex=windex) - name = self._GetSearchEngineWithKeyword(keyword, windex=windex) - self.assertTrue(name) - - def _AssertStartUpPage(self, url, profile='Default'): - """Asserts start up page for given profile. - - Args: - url: URL of the page to be set as start up page - profile: The profile name, defaults to 'Default'. - """ - self.AppendBrowserLaunchSwitch('--profile-directory=' + profile) - self.RestartBrowser(clear_profile=False) - info = self.GetBrowserInfo() - self.assertEqual(url, info['windows'][0]['tabs'][0]['url'].rstrip('/')) - self.assertTrue(url in - self.GetPrefsInfo().Prefs(pyauto.kURLsToRestoreOnStartup)) - - def _AssertHomePage(self, url, profile='Default'): - """Asserts home page for given profile. - - Args: - url: URL of the page to be set as home page - profile: The profile name, defaults to 'Dafault'. - """ - self.AppendBrowserLaunchSwitch('--profile-directory=' + profile) - self.RestartBrowser(clear_profile=False) - self.assertTrue(url in self.GetPrefsInfo().Prefs(pyauto.kHomePage)) - - def _AssertDefaultSearchEngine(self, search_engine, profile='Default'): - """Asserts default search engine for given profile. - - Args: - search_engine: Name of default search engine. - profile: The profile name, defaults to 'Default'. - """ - self.AppendBrowserLaunchSwitch('--profile-directory=' + profile) - self.RestartBrowser(clear_profile=False) - name = self._GetSearchEngineWithKeyword(search_engine) - self.assertTrue(name['is_default']) - self.SetOmniboxText('test search') - self.OmniboxAcceptInput() - self.assertTrue(re.search(search_engine, self.GetActiveTabURL().spec())) - - def _AssertSessionRestore(self, url_list, set_restore, num_tabs=1, - profile='Default'): - """Asserts urls when session is set to restored or set default. - - Args: - url_list: List of URL to be restored. - set_restore: Value of action of start up. - num_tabs: Number of tabs to be restored, default is 1. - profile: The profile name, defaults to 'Default'. - """ - self.AppendBrowserLaunchSwitch('--profile-directory=' + profile) - self.RestartBrowser(clear_profile=False) - self.assertEqual(num_tabs, self.GetTabCount()) - self.assertEqual(self.GetPrefsInfo().Prefs(pyauto.kRestoreOnStartup), - set_restore) - tab_index = 0 - while (tab_index < num_tabs): - self.ActivateTab(tab_index) - self.assertEqual(url_list[tab_index], self.GetActiveTabURL().spec()) - tab_index += 1 - - def testBasic(self): - """Multi-profile windows can open.""" - self.assertEqual(1, self.GetBrowserWindowCount()) - self.assertTrue(self.GetMultiProfileInfo()['enabled'], - msg='Multi-profile is not enabled') - self.OpenNewBrowserWindowWithNewProfile() - # Verify multi-profile info. - multi_profile = self.GetMultiProfileInfo() - self.assertEqual(2, len(multi_profile['profiles'])) - new_profile = multi_profile['profiles'][1] - self.assertTrue(new_profile['name']) - - # Verify browser windows. - self.assertEqual(2, self.GetBrowserWindowCount(), - msg='New browser window did not open') - info = self.GetBrowserInfo() - new_profile_window = info['windows'][1] - self.assertEqual('Profile 1', new_profile_window['profile_path']) - self.assertEqual(1, len(new_profile_window['tabs'])) - self.assertEqual('chrome://newtab/', new_profile_window['tabs'][0]['url']) - - def test20NewProfiles(self): - """Verify we can create 20 new profiles.""" - for index in range(1, 21): - self.OpenNewBrowserWindowWithNewProfile() - multi_profile = self.GetMultiProfileInfo() - self.assertEqual(index + 1, len(multi_profile['profiles']), - msg='Expected %d profiles after adding %d new users. Got %d' % ( - index + 1, index, len(multi_profile['profiles']))) - - def testStartUpPageOptionInMultiProfile(self): - """Test startup page for Multi-profile windows.""" - self.assertTrue(self.GetMultiProfileInfo()['enabled'], - msg='Multi-profile is not enabled') - # Launch browser with new Profile 1, set startup page to 'www.google.com'. - self.OpenNewBrowserWindowWithNewProfile() - self._SetStartUpPage('http://www.google.com', windex=1) - # Launch browser with new Profile 2, set startup page to 'www.yahoo.com'. - self.OpenNewBrowserWindowWithNewProfile() - # Verify start up page for Profile 2 is still newtab page. - info = self.GetBrowserInfo() - self.assertEqual('chrome://newtab/', info['windows'][2]['tabs'][0]['url']) - self._SetStartUpPage('http://www.yahoo.com', windex=2) - # Exit Profile 1 / Profile 2 - self.CloseBrowserWindow(2) - self.CloseBrowserWindow(1) - # Relaunch Browser with Profile 2, verify startup page. - self._AssertStartUpPage('http://www.yahoo.com', profile='Profile 2') - # Relaunch Browser with Profile 1, verify startup page. - self._AssertStartUpPage('http://www.google.com', profile='Profile 1') - - def testHomePageOptionMultiProfile(self): - """Test Home page for Multi-profile windows.""" - self.assertTrue(self.GetMultiProfileInfo()['enabled'], - msg='Multi-profile is not enabled') - # Launch browser with new Profile 1, set homepage to 'www.google.com'. - self.OpenNewBrowserWindowWithNewProfile() - self._SetHomePage('http://www.google.com', windex=1) - # Launch browser with new Profile 2, set homepage to 'www.yahoo.com'. - self.OpenNewBrowserWindowWithNewProfile() - self._SetHomePage('http://www.yahoo.com', windex=2) - # Exit Profile 1 / Profile 2 - self.CloseBrowserWindow(2) - self.CloseBrowserWindow(1) - # Relaunch Browser with Profile 2, verify startup page. - self._AssertHomePage('http://www.yahoo.com', profile='Profile 2') - # Relaunch Browser with Profile 1, verify startup page. - self._AssertHomePage('http://www.google.com', profile='Profile 1') - - def testSessionRestoreInMultiProfile(self): - """Test session restore preference for Multi-profile windows.""" - self.assertTrue(self.GetMultiProfileInfo()['enabled'], - msg='Multi-profile is not enabled') - # Launch browser with new Profile 1, set pref to restore session on - # startup. - self.OpenNewBrowserWindowWithNewProfile() - self._SetSessionRestoreURLs(self._RESTORE_LASTOPEN_URL_VALUE, windex=1) - # Launch browser with new Profile 2, do not set session restore pref. - self.OpenNewBrowserWindowWithNewProfile() - self._SetSessionRestoreURLs(self._RESTORE_DEFAULT_URL_VALUE, windex=2) - # Exit Profile 1 / Profile 2 - self.CloseBrowserWindow(2) - self.CloseBrowserWindow(1) - # Relaunch Browser with Profile 1, verify session restores on startup. - url_list = ['http://www.google.com/', 'http://news.google.com/'] - self._AssertSessionRestore(url_list, self._RESTORE_LASTOPEN_URL_VALUE, - num_tabs=2, profile='Profile 1') - # Relaunch Browser with Profile 2, verify session does not get restored. - url_list = ['chrome://newtab/'] - self._AssertSessionRestore(url_list, self._RESTORE_DEFAULT_URL_VALUE, - num_tabs=1, profile='Profile 2') - - def testMakeSearchEngineDefaultInMultiprofile(self): - """Test adding and making a search engine default for Multi-profiles.""" - self.assertTrue(self.GetMultiProfileInfo()['enabled'], - msg='Multi-profile is not enabled') - # Launch browser with new Profile 1, add search engine to 'Hulu'. - self.OpenNewBrowserWindowWithNewProfile() - self._AddSearchEngine('Hulu', 'hulu.com', - 'http://www.hulu.com/search?query=%s&ref=os&src={referrer:source?}', 1) - self.MakeSearchEngineDefault('hulu.com', windex=1) - # Launch browser with new Profile 2, add search engine to 'Youtube'. - self.OpenNewBrowserWindowWithNewProfile() - self._AddSearchEngine('YouTube Video Search', 'youtube.com', - 'http://www.youtube.com/results?search_query=%s&page={startPage?}'+ - '&utm_source=opensearch', 2) - self.MakeSearchEngineDefault('youtube.com', windex=2) - # Exit Profile 1 / Profile 2 - self.CloseBrowserWindow(2) - self.CloseBrowserWindow(1) - # Relaunch Browser with Profile 1, verify default search engine as 'Hulu'. - self._AssertDefaultSearchEngine('hulu.com', profile='Profile 1') - # Relaunch Browser with Profile 2, verify default search engine as - # 'Youtube'. - self._AssertDefaultSearchEngine('youtube.com', profile='Profile 2') - - def testDeleteSearchEngineInMultiprofile(self): - """Test adding then deleting a search engine for Multi-profiles.""" - self.assertTrue(self.GetMultiProfileInfo()['enabled'], - msg='Multi-profile is not enabled') - # Launch browser with new Profile 1, add 'foo.com' as new search engine. - self.OpenNewBrowserWindowWithNewProfile() - self._AddSearchEngine('foo', 'foo.com', 'http://foo/?q=%s', windex=1) - # Launch browser with new Profile 2, add 'foo.com' as new search engine. - self.OpenNewBrowserWindowWithNewProfile() - self._AddSearchEngine('foo', 'foo.com', 'http://foo/?q=%s', windex=2) - # Delete search engine 'foo.com' from Profile 1 and exit. - self.DeleteSearchEngine('foo.com', windex=1) - self.CloseBrowserWindow(2) - self.CloseBrowserWindow(1) - # Relaunch Browser with Profile 1, verify search engine 'foo.com' - # is deleted. - self.AppendBrowserLaunchSwitch('--profile-directory=Profile 1') - self.RestartBrowser(clear_profile=False) - foo = self._GetSearchEngineWithKeyword('foo.com') - self.assertFalse(foo) - # Relaunch Browser with Profile 2, verify search engine 'foo.com' - # is not deleted. - self.AppendBrowserLaunchSwitch('--profile-directory=Profile 2') - self.RestartBrowser(clear_profile=False) - foo = self._GetSearchEngineWithKeyword('foo.com') - self.assertTrue(foo) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/nacl_sdk.py b/chrome/test/functional/nacl_sdk.py deleted file mode 100755 index 41a95fa..0000000 --- a/chrome/test/functional/nacl_sdk.py +++ /dev/null @@ -1,796 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import copy -import ctypes -from distutils import version -import fnmatch -import glob -import hashlib -import logging -import os -import platform -import re -import shutil -import subprocess -import sys -import tempfile -import urllib2 -import xml.dom.minidom -import zipfile - -import pyauto_functional # Must be imported before pyauto. -import pyauto -import pyauto_utils -import test_utils - - -class NaClSDKTest(pyauto.PyUITest): - """Tests for the NaCl SDK.""" - _isExamplesTest = False - _extracted_sdk_path = None - _temp_dir = None - _updated_pepper_versions = [] - _latest_updated_pepper_versions = [] - _settings = { - 'post_sdk_download_url': 'http://code.google.com/chrome/nativeclient/' - 'docs/download.html', - 'post_sdk_zip': 'http://storage.googleapis.com/' - 'nativeclient-mirror/nacl/nacl_sdk/nacl_sdk.zip', - 'min_required_chrome_build': 14, - } - - def tearDown(self): - pyauto.PyUITest.tearDown(self) - if not self._isExamplesTest: - self._RemoveDownloadedTestFile() - - def testNaClSDK(self): - """Verify that NaCl SDK is working properly.""" - if not self._HasAllSystemRequirements(): - logging.info('System does not meet the requirements.') - return - self._extracted_sdk_path = tempfile.mkdtemp() - self._VerifyDownloadLinks() - self._VerifyNaClSDKInstaller() - self._VerifyInstall() - self._VerifyUpdate() - self._LaunchServerAndVerifyExamplesAllPepperVersions() - - def NaClSDKExamples(self): - """Verify if NaCl SDK examples are working.""" - self._isExamplesTest = True - nacl_sdk_root = os.environ.get('NACL_SDK_ROOT', None) - pepper_version = os.environ.get('PEPPER_VER', None) - if nacl_sdk_root and pepper_version: - self._LaunchServerAndVerifyExamples('pepper_' + pepper_version, - nacl_sdk_root) - else: - self.fail(msg='Missing pepper version to be checked or SDK path.') - - def _VerifyDownloadLinks(self): - """Verify the download links. - - Simply verify that NaCl download links exist in html page. - """ - html = None - for i in xrange(3): - try: - html = urllib2.urlopen(self._settings['post_sdk_download_url']).read() - break - except: - pass - self.assertTrue(html, - msg='Cannot open URL: %s' % - self._settings['post_sdk_download_url']) - sdk_url = self._settings['post_sdk_zip'] - self.assertTrue(sdk_url in html, - msg='Missing SDK download URL: %s' % sdk_url) - - def _VerifyNaClSDKInstaller(self): - """Verify NaCl SDK installer.""" - search_list = [ - 'sdk_cache/', - 'sdk_tools/', - ] - mac_lin_additional_search_items = [ - 'naclsdk', - ] - win_additional_search_items = [ - 'naclsdk.bat' - ] - self._DownloadNaClSDK() - self._ExtractNaClSDK() - if pyauto.PyUITest.IsWin(): - self._SearchNaClSDKFile( - search_list + win_additional_search_items) - elif pyauto.PyUITest.IsMac() or pyauto.PyUITest.IsLinux(): - self._SearchNaClSDKFile( - search_list + mac_lin_additional_search_items) - else: - self.fail(msg='NaCl SDK does not support this OS.') - - def _VerifyInstall(self): - """Install NACL sdk.""" - # Executing naclsdk(.bat) list - if pyauto.PyUITest.IsWin(): - source_file = os.path.join( - self._extracted_sdk_path, 'nacl_sdk', 'naclsdk.bat') - elif pyauto.PyUITest.IsMac() or pyauto.PyUITest.IsLinux(): - source_file = os.path.join( - self._extracted_sdk_path, 'nacl_sdk', 'naclsdk') - subprocess.call(['chmod', '-R', '755', self._extracted_sdk_path]) - else: - self.fail(msg='NaCl SDK does not support this OS.') - subprocess.Popen([source_file, 'list'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE).communicate() - - def _VerifyUpdate(self): - """Update NACL sdk""" - # Executing naclsdk(.bat) update - if pyauto.PyUITest.IsWin(): - source_file = os.path.join(self._extracted_sdk_path, 'nacl_sdk', - 'naclsdk.bat') - elif pyauto.PyUITest.IsMac() or pyauto.PyUITest.IsLinux(): - source_file = os.path.join(self._extracted_sdk_path, 'nacl_sdk', - 'naclsdk') - else: - self.fail(msg='NaCl SDK does not support this OS.') - # Executing nacl_sdk(.bat) update to get the latest version. - updated_output = subprocess.Popen([source_file, 'update'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] - self._updated_pepper_versions.extend( - re.findall('Updating bundle (pepper_[0-9]{2})', updated_output)) - self._updated_pepper_versions = list(set(self._updated_pepper_versions)) - self._updated_pepper_versions.sort(key=str.lower) - updated_pepper_versions_len = len(self._updated_pepper_versions) - self._latest_updated_pepper_versions = filter( - lambda x: x >= 'pepper_18', self._updated_pepper_versions) - - def _GetURLForExampleName(self, name, toolchain): - return 'http://localhost:5103/%s/index_%s.html' % (name, toolchain) - - def _GetExampleNamesAndURLs(self, examples_path): - """Get a list of all examples as (name, url) tuples. - - Args: - examples_path: The path to the examples directory in the NaCl SDK. - """ - toolchains = ['newlib', 'glibc', 'pnacl'] - - examples = [] - for toolchain in toolchains: - for example in os.listdir(examples_path): - html_path = os.path.join(examples_path, example, - 'index_%s.html' % (toolchain,)) - if os.path.exists(html_path): - example_url = self._GetURLForExampleName(example, toolchain) - examples.append((example, example_url)) - return examples - - def _LaunchServerAndVerifyExamplesAllPepperVersions(self): - for pepper_version in self._latest_updated_pepper_versions: - pepper_path = os.path.join(self._extracted_sdk_path, - 'nacl_sdk', 'pepper_' + str(pepper_version)) - self._LaunchServerAndVerifyExamples(pepper_version, pepper_path) - - def _LaunchServerAndVerifyExamples(self, pepper_version, pepper_path): - """Start local HTTP server and verify examples.""" - if self._ChromeAndPepperVersionMatch(pepper_version): - # Close server if it's already open. - if self._IsURLAlive('http://localhost:5103'): - self._CloseHTTPServer() - - examples_path = os.path.join(pepper_path, 'examples') - - # Launch local http server. - proc = subprocess.Popen(['make RUN'], shell=True, cwd=examples_path) - self.WaitUntil( - lambda: self._IsURLAlive('http://localhost:5103'), - timeout=150, retry_sleep=1) - - examples = self._GetExampleNamesAndURLs(examples_path) - try: - self._OpenExamplesAndStartTest(examples) - finally: - self._CloseHTTPServer(proc) - - else: - self.pprint('Pepper Version %s does not match the Chrome version %s.' - % (pepper_version, - self.GetBrowserInfo()['properties']['ChromeVersion'])) - - def _ChromeAndPepperVersionMatch(self, pepper_version='pepper_18'): - """Determine if chrome and pepper version match""" - version_number = re.findall('pepper_([0-9]{2})', pepper_version) - browser_info = self.GetBrowserInfo() - chrome_version = browser_info['properties']['ChromeVersion'] - chrome_build = int(chrome_version.split('.')[0]) - return int(chrome_build) == int(version_number[0]) - - def _RemoveDownloadedTestFile(self): - """Delete downloaded files and dirs from downloads directory.""" - if self._extracted_sdk_path and os.path.exists(self._extracted_sdk_path): - self._CloseHTTPServer() - - def _RemoveFile(): - shutil.rmtree(self._extracted_sdk_path, ignore_errors=True) - return os.path.exists(self._extracted_sdk_path) - - success = self.WaitUntil(_RemoveFile, retry_sleep=2, - expect_retval=False) - self.assertTrue(success, - msg='Cannot remove %s' % self._extracted_sdk_path) - - if self._temp_dir: - pyauto_utils.RemovePath(self._temp_dir) - - def _OpenExamplesAndStartTest(self, examples): - """Open each example and verify that it's working. - - Args: - examples: A list of example (name, url) tuples. - """ - example_verify_funcs = { - 'dlopen': self._VerifyDynamicLibraryOpen, - 'file_io': self._VerifyFileIoExample, - 'geturl': self._VerifyGetURLExample, - 'input_events': self._VerifyInputEventsExample, - 'load_progress': self._VerifyLoadProgressExample, - 'mt_input_events': self._VerifyMultithreadedInputEventsExample, - 'pi_generator': self._VerifyPiGeneratorExample, - 'sine_synth': self._VerifySineSynthExample, - 'websocket': self._VerifyWebSocketExample, - } - - # Remove examples that we don't yet verify - examples = [(name, url) for name, url in examples - if name in example_verify_funcs] - - # Open all examples. - for name, url in examples: - self.AppendTab(pyauto.GURL(url)) - self._CheckForCrashes() - - # Verify all examples are working. - for name, url in examples: - self._VerifyAnExample(name, url, example_verify_funcs[name]) - self._CheckForCrashes() - - # Close each tab and check for crashes. - tab_count = self.GetTabCount() - for index in xrange(tab_count - 1, 0, -1): - self.CloseTab(tab_index=index) - self._CheckForCrashes() - - def _VerifyAnExample(self, name, url, verify_func): - """Verify NaCl example is working. - - Args: - name: A string name of the example. - url: A string url of the example. - verify_func: The function to verify the example. - Takes (tab_index, name, url) as parameters. - """ - if not verify_func: - self.fail(msg='No test available for %s.' % name) - - info = self.GetBrowserInfo() - tabs = info['windows'][0]['tabs'] - tab_index = None - for tab in tabs: - if url == tab['url']: - self.ActivateTab(tab['index']) - tab_index = tab['index'] - break - - if tab_index: - verify_func(tab_index, name, url) - - def _VerifyElementPresent(self, element_id, expected_value, tab_index, msg, - attribute='innerHTML', timeout=150): - """Determine if dom element has the expected value. - - Args: - element_id: Dom element's id. - expected_value: String to be matched against the Dom element. - tab_index: Tab index to work on. - attribute: Attribute to match |expected_value| against, if - given. Defaults to 'innerHTML'. - timeout: The max timeout (in secs) for which to wait. - """ - js_code = """ - var output = document.getElementById('%s').%s; - var result; - if (output.indexOf('%s') != -1) - result = 'pass'; - else - result = 'fail'; - window.domAutomationController.send(result); - """ % (element_id, attribute, expected_value) - success = self.WaitUntil( - lambda: self.ExecuteJavascript(js_code, tab_index), - timeout=timeout, expect_retval='pass') - self.assertTrue(success, msg=msg) - - def _CreateJSToSimulateMouseclick(self): - """Create javascript to simulate mouse click event.""" - js_code = """ - var rightClick = document.createEvent('MouseEvents'); - rightClick.initMouseEvent( - 'mousedown', true, true, document, - 1, 32, 121, 10, 100, - false, false, false, false, - 2, common.naclModule - ); - common.naclModule.dispatchEvent(rightClick); - window.domAutomationController.send('done'); - """ - return js_code - - def _VerifyInputEventsExample(self, tab_index, name, url): - """Verify Input Events Example. - - Args: - tab_index: Tab index integer that the example is on. - name: A string name of the example. - url: A string url of the example. - """ - success = self._VerifyElementPresent('eventString', 'DidChangeView', - tab_index, msg='Example %s failed. URL: %s' % (name, url)) - - # Simulate mouse click on event module. - js_code = self._CreateJSToSimulateMouseclick() - self.ExecuteJavascript(js_code, tab_index) - - # Check if 'eventString' has handled above mouse click. - success = self.WaitUntil( - lambda: re.search('DidHandleInputEvent', self.GetDOMValue( - 'document.getElementById("eventString").innerHTML', - tab_index)).group(), expect_retval='DidHandleInputEvent') - self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) - - def _VerifyMultithreadedInputEventsExample(self, tab_index, name, url): - """Verify Input Events Example. - - Args: - tab_index: Tab index integer that the example is on. - name: A string name of the example. - url: A string url of the example. - """ - success = self.WaitUntil( - lambda: bool(self.GetDOMValue( - 'document.getElementById("eventString").innerHTML', - tab_index).find('DidChangeView') + 1)) - - self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) - - # Simulate mouse click on event module. - js_code = self._CreateJSToSimulateMouseclick() - self.ExecuteJavascript(js_code, tab_index) - - # Check if above mouse click is handled. - success = self._VerifyElementPresent('eventString', 'Mouse event', - tab_index, msg='Example %s failed. URL: %s' % (name, url)) - - # Kill worker thread and queue - js_code = """ - document.getElementsByTagName('button')[0].click(); - window.domAutomationController.send('done'); - """ - self.ExecuteJavascript(js_code, tab_index) - - # Check if main thread has cancelled queue. - success = self._VerifyElementPresent('eventString', 'Received cancel', - tab_index, msg='Example %s failed. URL: %s' % (name, url)) - - # Simulate mouse click on event module. - js_code = self._CreateJSToSimulateMouseclick() - self.ExecuteJavascript(js_code, tab_index) - - # Check if above mouse click is not handled after killing worker thread. - def _CheckMouseClickEventStatus(): - return self.GetDOMValue( - 'document.getElementById("eventString").innerHTML', - tab_index).find('Mouse event', self.GetDOMValue( - 'document.getElementById("eventString").innerHTML', tab_index).find( - 'Received cancel')) - - success = self.WaitUntil(_CheckMouseClickEventStatus, expect_retval=-1) - self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) - - def _VerifyFileIoExample(self, tab_index, name, url): - """Verify File IO Example. - - Args: - tab_index: Tab index integer that the example is on. - name: A string name of the example. - url: A string url of the example. - """ - def _CheckStatus(substring_expected, fail_msg): - self.assertTrue( - self.WaitUntil( - lambda: self.GetDOMValue( - 'document.getElementById("statusField").innerHTML', tab_index)\ - .find(substring_expected) != -1, expect_retval=True), - msg='Example %s failed. URL: %s. Reason: %s' % (name, url, fail_msg)) - - # Give permission to use file system by clicking infobar OK - infobar_index = test_utils.WaitForInfobarTypeAndGetIndex(self, - 'confirm_infobar', 0, tab_index) - self.PerformActionOnInfobar('accept', infobar_index, 0, tab_index) - _CheckStatus('Ready!', 'NaCl module load') - - # Check that deleting non-existing files gives file not found - js_code = """ - document.getElementById('file_name').value = '/abc'; - document.getElementById('file_editor').value = 'test'; - document.getElementById('delete_but').click(); - window.domAutomationController.send('done'); - """ - self.ExecuteJavascript(js_code, tab_index) - _CheckStatus('File not found', 'Delete non-existing') - - # Check that saving works - js_code = """ - document.getElementById('save_but').click(); - window.domAutomationController.send('done'); - """ - self.ExecuteJavascript(js_code, tab_index) - _CheckStatus('Save successful', 'Save test') - - # Check that we load what we saved - js_code = """ - document.getElementById('file_editor').value = 'different'; - document.getElementById('load_but').click(); - window.domAutomationController.send('done'); - """ - self.ExecuteJavascript(js_code, tab_index) - _CheckStatus('Load complete', 'Load test') - self.assertTrue( - self.GetDOMValue('document.getElementById("file_editor").value', - tab_index).find('test') != -1, msg='Loaded wrong text or failed') - - # Check that we delete files successfully - js_code = """ - document.getElementById('delete_but').click(); - window.domAutomationController.send('done'); - """ - self.ExecuteJavascript(js_code, tab_index) - _CheckStatus('File deleted', 'Delete test') - - # Check that file is deleted and load produces not found - js_code = """ - document.getElementById('load_but').click(); - window.domAutomationController.send('done'); - """ - self.ExecuteJavascript(js_code, tab_index) - _CheckStatus('File not found', 'Load deleted test') - - def _VerifyWebSocketExample(self, tab_index, name, url): - """Verify Web Socket Open Example. - - Args: - tab_index: Tab index integer that the example is on. - name: A string name of the example. - url: A string url of the example. - """ - # Check if example is loaded. - success = self.WaitUntil( - lambda: self.GetDOMValue( - 'document.getElementById("statusField").innerHTML', tab_index), - expect_retval='SUCCESS') - self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) - - # Simulate clicking on Connect button to establish a connection. - js_code = """ - document.getElementsByTagName('input')[1].click(); - window.domAutomationController.send('done'); - """ - self.ExecuteJavascript(js_code, tab_index) - - # Check if connected - success = self._VerifyElementPresent('log', 'connected', tab_index, - msg='Example %s failed. URL: %s' % (name, url)) - - # Simulate clicking on Send button to send text message in log. - js_code = """ - document.getElementsByTagName('input')[3].click(); - window.domAutomationController.send('done'); - """ - self.ExecuteJavascript(js_code, tab_index) - success = self.WaitUntil( - lambda: bool(re.search('send:', self.GetDOMValue( - 'document.getElementById("log").textContent', tab_index)))) - self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) - - def _VerifyDynamicLibraryOpen(self, tab_index, name, url): - """Verify Dynamic Library Open Example. - - Args: - tab_index: Tab index integer that the example is on. - name: A string name of the example. - url: A string url of the example. - """ - # Check if example is loaded. - success = self._VerifyElementPresent('log', 'Eightball loaded!', - tab_index, msg='Example %s failed. URL: %s' % (name, url)) - - # Simulate clicking on ASK button and check answer log for desired answer. - js_code = """ - document.getElementsByTagName('input')[1].click(); - window.domAutomationController.send('done'); - """ - self.ExecuteJavascript(js_code, tab_index) - def _CheckAnswerLog(): - return bool(re.search(r'NO|YES|42|MAYBE NOT|DEFINITELY|' - 'ASK ME TOMORROW|MAYBE|PARTLY CLOUDY', - self.GetDOMValue('document.getElementById("log").innerHTML', - tab_index))) - - success = self.WaitUntil(_CheckAnswerLog) - self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) - - def _VerifyLoadProgressExample(self, tab_index, name, url): - """Verify Dynamic Library Open Example. - - Args: - tab_index: Tab index integer that the example is on. - name: A string name of the example. - url: A string url of the example. - """ - # Check if example loads and displays loading progress. - success = self.WaitUntil( - lambda: self.GetDOMValue( - 'document.getElementById("statusField").innerHTML', tab_index), - timeout=150, expect_retval='SUCCESS') - self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) - - def _CheckLoadProgressStatus(): - return re.search( - r'(loadstart).+(progress:).+(load).+(loadend).+(lastError:)', - self.GetDOMValue( - 'document.getElementById("log").innerHTML', tab_index)) - success = self.WaitUntil(_CheckLoadProgressStatus) - self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) - - def _VerifyPiGeneratorExample(self, tab_index, name, url): - """Verify Pi Generator Example. - - Args: - tab_index: Tab index integer that the example is on. - name: A string name of the example. - url: A string url of the example. - """ - success = self.WaitUntil( - lambda: self.GetDOMValue('document.getElementById("pi").value', - tab_index)[0:3], - expect_retval='3.1') - self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) - - def _VerifySineSynthExample(self, tab_index, name, url): - """Verify Sine Wave Synthesizer Example. - - Args: - tab_index: Tab index integer that the example is on. - name: A string name of the example. - url: A string url of the example. - """ - success = self.WaitUntil( - lambda: self.GetDOMValue( - 'document.getElementById("frequency_field").value', - tab_index), timeout=150, expect_retval='440') - self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) - self.ExecuteJavascript( - 'document.body.getElementsByTagName("button")[0].click();' - 'window.domAutomationController.send("done")', - tab_index) - - def _VerifyGetURLExample(self, tab_index, name, url): - """Verify GetURL Example. - - Args: - tab_index: Tab index integer that the example is on. - name: A string name of the example. - url: A string url of the example. - """ - success = self.WaitUntil( - lambda: self.GetDOMValue( - 'document.getElementById("statusField").innerHTML', - tab_index), timeout=150, expect_retval='SUCCESS') - self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) - self.ExecuteJavascript( - 'document.getElementById("button").click();' - 'window.domAutomationController.send("done")', - tab_index) - success = self._VerifyElementPresent('general_output', 'test passed', - tab_index, msg='Example %s failed. URL: %s' % (name, url)) - - def _CheckForCrashes(self): - """Check for any browser/tab crashes and hangs.""" - self.assertTrue(self.GetBrowserWindowCount(), - msg='Browser crashed, no window is open.') - - info = self.GetBrowserInfo() - breakpad_folder = info['properties']['DIR_CRASH_DUMPS'] - old_dmp_files = glob.glob(os.path.join(breakpad_folder, '*.dmp')) - - # Verify there're no crash dump files. - for dmp_file in glob.glob(os.path.join(breakpad_folder, '*.dmp')): - self.assertTrue(dmp_file in old_dmp_files, - msg='Crash dump %s found' % dmp_file) - - # Check for any crashed tabs. - tabs = info['windows'][0]['tabs'] - for tab in tabs: - if tab['url'] != 'about:blank': - if not self.GetDOMValue('document.body.innerHTML', tab['index']): - self.fail(msg='Tab crashed on %s' % tab['url']) - - def _GetPlatformArchitecture(self): - """Get platform architecture. - - Returns: - A string representing the platform architecture. - """ - if pyauto.PyUITest.IsWin(): - if os.environ['PROGRAMFILES'] == 'C:\\Program Files (x86)': - return '64bit' - else: - return '32bit' - elif pyauto.PyUITest.IsMac() or pyauto.PyUITest.IsLinux(): - if platform.machine() == 'x86_64': - return '64bit' - else: - return '32bit' - return '32bit' - - def _HasPathInTree(self, pattern, is_file, root=os.curdir): - """Recursively checks if a file/directory matching a pattern exists. - - Args: - pattern: Pattern of file or directory name. - is_file: True if looking for file, or False if looking for directory. - root: Directory to start looking. - - Returns: - True, if root contains the directory name pattern, or - False otherwise. - """ - for path, dirs, files in os.walk(os.path.abspath(root)): - if is_file: - if len(fnmatch.filter(files, pattern)): - return True - else: - if len(fnmatch.filter(dirs, pattern)): - return True - return False - - def _HasAllSystemRequirements(self): - """Verify NaCl SDK installation system requirements. - - Returns: - True, if system passed requirements, or - False otherwise. - """ - # Check python version. - if sys.version_info[0:2] < (2, 6): - return False - - # Check OS requirements. - if pyauto.PyUITest.IsMac(): - mac_min_version = version.StrictVersion('10.6') - mac_version = version.StrictVersion(platform.mac_ver()[0]) - if mac_version < mac_min_version: - return False - elif pyauto.PyUITest.IsWin(): - if not (self.IsWin7() or self.IsWinVista() or self.IsWinXP()): - return False - elif pyauto.PyUITest.IsLinux(): - pass # TODO(chrisphan): Check Lin requirements. - else: - return False - - # Check for Chrome version compatibility. - # NaCl supports Chrome 10 and higher builds. - min_required_chrome_build = self._settings['min_required_chrome_build'] - browser_info = self.GetBrowserInfo() - chrome_version = browser_info['properties']['ChromeVersion'] - chrome_build = int(chrome_version.split('.')[0]) - return chrome_build >= min_required_chrome_build - - def _DownloadNaClSDK(self): - """Download NaCl SDK.""" - self._temp_dir = tempfile.mkdtemp() - dl_file = urllib2.urlopen(self._settings['post_sdk_zip']) - file_path = os.path.join(self._temp_dir, 'nacl_sdk.zip') - - try: - f = open(file_path, 'wb') - f.write(dl_file.read()) - except IOError: - self.fail(msg='Cannot open %s.' % file_path) - finally: - f.close() - - def _ExtractNaClSDK(self): - """Extract NaCl SDK.""" - source_file = os.path.join(self._temp_dir, 'nacl_sdk.zip') - if zipfile.is_zipfile(source_file): - zip = zipfile.ZipFile(source_file, 'r') - zip.extractall(self._extracted_sdk_path) - else: - self.fail(msg='%s is not a valid zip file' % source_file) - - def _IsURLAlive(self, url): - """Test if URL is alive.""" - try: - urllib2.urlopen(url) - except: - return False - return True - - def _CloseHTTPServer(self, proc=None): - """Close HTTP server. - - Args: - proc: Process that opened the HTTP server. - proc is None when there is no pointer to HTTP server process. - """ - if not self._IsURLAlive('http://localhost:5103'): - return - response = urllib2.urlopen('http://localhost:5103') - html = response.read() - if not 'Native Client' in html: - self.fail(msg='Port 5103 is in use.') - - urllib2.urlopen('http://localhost:5103?quit=1') - success = self.WaitUntil( - lambda: self._IsURLAlive('http://localhost:5103'), - retry_sleep=1, expect_retval=False) - if not success: - if not proc: - self.fail(msg='Failed to close HTTP server.') - else: - if proc.poll() == None: - try: - proc.kill() - except: - self.fail(msg='Failed to close HTTP server.') - - def _SearchNaClSDKFile(self, search_list): - """Search NaCl SDK file for example files and directories in Windows. - - Args: - search_list: A list of strings, representing file and - directory names for which to search. - """ - missing_items = [] - for name in search_list: - is_file = name.find('/') < 0 - if not is_file: - name = name.replace('/', '') - if not self._HasPathInTree(name, is_file, self._extracted_sdk_path): - missing_items.append(name) - self.assertEqual(len(missing_items), 0, - msg='Missing files or directories: %s' % - ', '.join(map(str, missing_items))) - - def ExtraChromeFlags(self): - """Ensures Nacl is enabled. - - Returns: - A list of extra flags to pass to Chrome when it is launched. - """ - extra_chrome_flags = [ - '--enable-nacl', - '--enable-nacl-exception-handling', - '--nacl-gdb', - ] - return pyauto.PyUITest.ExtraChromeFlags(self) + extra_chrome_flags - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/netflix.py b/chrome/test/functional/netflix.py deleted file mode 100755 index 4951b9b..0000000 --- a/chrome/test/functional/netflix.py +++ /dev/null @@ -1,256 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import os -import time - -import pyauto_functional -import pyauto -import test_utils - - -class NetflixTestHelper(): - """Helper functions for Netflix tests. - - For sample usage, look at class NetflixTest. - """ - - # Netflix player states. - IS_GUEST_MODE_ERROR = '0' - IS_PLAYING = '4' - - TITLE_HOMEPAGE = 'http://movies.netflix.com/WiHome' - SIGNOUT_PAGE = 'https://account.netflix.com/Logout' - # 30 Rock. - VIDEO_URL = 'https://movies.netflix.com/WiPlayer?movieid=70136124' - ALT_VIDEO_URL = 'https://movies.netflix.com/WiPlayer?movieid=70133713' - _pyauto = None - - def __init__(self, pyauto): - self._pyauto = pyauto - - def _IsNetflixPluginEnabled(self): - """Determine Netflix plugin availability and its state.""" - return [x for x in self._pyauto.GetPluginsInfo().Plugins() \ - if x['name'] == 'Netflix' and x['enabled']] - - def _LoginToNetflix(self): - """Login to Netflix.""" - credentials = self._pyauto.GetPrivateInfo()['test_netflix_acct'] - board_name = self._pyauto.ChromeOSBoard() - assert credentials.get(board_name), \ - 'No netflix credentials for %s.' % board_name - self._pyauto.NavigateToURL(credentials['login_url']) - login_js = """ - document.getElementById('email').value='%s'; - document.getElementById('password').value='%s'; - window.domAutomationController.send('ok'); - """ % (credentials[board_name], credentials['password']) - self._pyauto.assertEqual(self._pyauto.ExecuteJavascript(login_js), 'ok', - msg='Failed to set login credentials.') - self._pyauto.assertTrue(self._pyauto.SubmitForm('login-form'), - msg='Login to Netflix failed. We think this is an authetication ' - 'problem from the Netflix side. Sometimes we also see this while ' - 'login in manually.') - - def _GetVideoDroppedFrames(self, tab_index=0, windex=0): - """Returns total Netflix video dropped frames.""" - js = """ - var frames = nrdp.video.droppedFrames; - window.domAutomationController.send(frames + ''); - """ - return int(self._pyauto.ExecuteJavascript(js, tab_index=tab_index, - windex=windex)) - - def _GetVideoFrames(self, tab_index=0, windex=0): - """Returns Netflix video total frames.""" - js = """ - var frames = nrdp.video.totalFrames; - window.domAutomationController.send(frames + ''); - """ - return int(self._pyauto.ExecuteJavascript(js, tab_index=tab_index, - windex=windex)) - - def _HandleInfobars(self, err_msg): - """Manage infobars that come up during the test.""" - def _HandleNetflixInfobar(): - tab_info = self._pyauto.GetBrowserInfo()['windows'][0]['tabs'][0] - infobars = tab_info['infobars'] - index = 0 - for infobar in infobars: - if 'netflix' in infobar['text']: - # After storage infobar pops up, clicking the Ok button immediately - # returns the Storage error on faster machines like Stumpy/Lumpy so - # adding a delay of 1 second here. - time.sleep(1) - self._pyauto.PerformActionOnInfobar('accept', infobar_index=index) - return True - index = index + 1 - return False - self._pyauto.assertTrue(self._pyauto.WaitUntil(_HandleNetflixInfobar), - msg=err_msg) - - def CurrentPlaybackTime(self): - """Returns the current playback time in seconds.""" - time = self._pyauto.ExecuteJavascript(""" - time = nrdp.video.currentTime; - window.domAutomationController.send(time + ''); - """) - return int(float(time)) - - def SignOut(self): - """Sign out from Netflix Login.""" - self._pyauto.NavigateToURL(self.SIGNOUT_PAGE) - - def LoginAndStartPlaying(self): - """Login and start playing the video.""" - self._pyauto.assertTrue(self._pyauto._IsNetflixPluginEnabled(), - msg='Netflix plugin is disabled or not available.') - self._pyauto._LoginToNetflix() - self._pyauto.assertTrue(self._pyauto.WaitUntil( - lambda: self._pyauto.GetActiveTabURL().spec(), - expect_retval=self.TITLE_HOMEPAGE), - msg='Login to Netflix failed.') - self._pyauto.NavigateToURL(self.VIDEO_URL) - self._pyauto._HandleInfobars(err_msg='Netflix infobar did not show up') - - def CheckNetflixPlaying(self, expected_result, error_msg): - """Check if Netflix is playing the video or not. - - Args: - expected_result: expected return value from Netflix player. - error_msg: If expected value isn't matching, error message to throw. - """ - self._pyauto.assertTrue(self._pyauto.WaitUntil( - lambda: self._pyauto.ExecuteJavascript(""" - if (typeof nrdp == 'undefined') { - window.domAutomationController.send('not ready'); - } - player_status = nrdp.video.readyState; - window.domAutomationController.send(player_status + ''); - """), expect_retval=expected_result), - msg=error_msg) - - -class NetflixTest(pyauto.PyUITest, NetflixTestHelper): - """Test case for Netflix player.""" - - def __init__(self, methodName='runTest', **kwargs): - pyauto.PyUITest.__init__(self, methodName, **kwargs) - NetflixTestHelper.__init__(self, self) - - def ShouldAutoLogin(self): - return False - - def _Login(self): - """Perform login""" - credentials = self.GetPrivateInfo()['test_google_account'] - self.Login(credentials['username'], credentials['password']) - logging.info('Logged in as %s' % credentials['username']) - login_info = self.GetLoginInfo() - self.assertTrue(login_info['is_logged_in'], msg='Login failed.') - self.assertFalse(login_info['is_guest'], - msg='Should not be logged in as guest.') - - def setUp(self): - assert os.geteuid() == 0, 'Run test as root since we might need to logout' - pyauto.PyUITest.setUp(self) - if self.GetLoginInfo()['is_logged_in']: - self.Logout() - self._Login() - - def tearDown(self): - self.SignOut() - pyauto.PyUITest.tearDown(self) - - def testPlayerLoadsAndPlays(self): - """Test that Netflix player loads and plays the title.""" - self.LoginAndStartPlaying() - self._HandleInfobars(err_msg='Netflix plugin access infobar did not show up') - self.CheckNetflixPlaying(self.IS_PLAYING, - 'Player did not start playing the title.') - - def testMultiplePlayback(self): - """Test that playing two titles, Netflix returns multiple play error.""" - self.LoginAndStartPlaying() - self._HandleInfobars(err_msg='Netflix plugin access infobar did not show up') - self.CheckNetflixPlaying(self.IS_PLAYING, - 'Player did not start playing the title.') - self.AppendTab(self.ALT_VIDEO_URL) - self.assertTrue('Multiple Play Error' in self.GetTabContents(), - msg='Multiple Play Error is not found on the page.') - - def testPlaying(self): - """Test that title playing progresses.""" - self.LoginAndStartPlaying() - self._HandleInfobars(err_msg='Netflix plugin access infobar did not show up') - self.CheckNetflixPlaying(self.IS_PLAYING, - 'Player did not start playing the title.') - title_length = self.ExecuteJavascript(""" - time = nrdp.video.duration; - window.domAutomationController.send(time + ''); - """) - title_length = int(float(title_length)) - prev_time = 0 - current_time = 0 - count = 0 - while current_time < title_length: - # We want to test playing only for ten seconds. - count = count + 1 - if count == 10: - break - current_time = self.CurrentPlaybackTime() - self.assertTrue(prev_time <= current_time, - msg='Prev playing time %s is greater than current time %s.' - % (prev_time, current_time)) - prev_time = current_time - # play video for some time - time.sleep(1) - # In case player doesn't start playing at all, above while loop may - # still pass. So re-verifying and assuming that player did play something - # during last 10 seconds. - self.assertTrue(current_time > 0, - msg='Netflix player did not start playing.') - - -class NetflixGuestModeTest(pyauto.PyUITest, NetflixTestHelper): - """Netflix in guest mode.""" - - def __init__(self, methodName='runTest', **kwargs): - pyauto.PyUITest.__init__(self, methodName, **kwargs) - NetflixTestHelper.__init__(self, self) - - def setUp(self): - assert os.geteuid() == 0, 'Run test as root since we might need to logout' - pyauto.PyUITest.setUp(self) - if self.GetLoginInfo()['is_logged_in']: - self.Logout() - self.LoginAsGuest() - login_info = self.GetLoginInfo() - self.assertTrue(login_info['is_logged_in'], msg='Not logged in at all.') - self.assertTrue(login_info['is_guest'], msg='Not logged in as guest.') - - def ShouldAutoLogin(self): - return False - - def tearDown(self): - self.AppendTab(self.SIGNOUT_PAGE) - self.Logout() - pyauto.PyUITest.tearDown(self) - - def testGuestMode(self): - """Test that Netflix doesn't play in guest mode login.""" - self.LoginAndStartPlaying() - self.CheckNetflixPlaying( - self.IS_GUEST_MODE_ERROR, - 'Netflix player did not return a Guest mode error.') - # crosbug.com/p/14009 - self.assertTrue('Netflix Video Player Unavailable' in self.GetTabContents(), - msg='Guest Mode error is not found on the page.') - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/ntp.py b/chrome/test/functional/ntp.py deleted file mode 100755 index f176943..0000000 --- a/chrome/test/functional/ntp.py +++ /dev/null @@ -1,473 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import copy -import os - -import pyauto_functional # Must be imported before pyauto -import pyauto -import test_utils - - -class NTPTest(pyauto.PyUITest): - """Test of the NTP.""" - - # Default apps are registered in ProfileImpl::RegisterComponentExtensions(). - _EXPECTED_DEFAULT_APPS = [ - {u'title': u'Chrome Web Store'}, - ] - if pyauto.PyUITest.IsChromeOS(): - _EXPECTED_DEFAULT_APPS.append({u'title': u'Files'}) - _EXPECTED_DEFAULT_APPS.append({u'title': u'Chrome'}) - else: - _EXPECTED_DEFAULT_APPS.append({u'title': u'Cloud Print'}) - - # Default menu and thumbnail mode preferences are set in - # ShownSectionsHandler::RegisterUserPrefs. - if pyauto.PyUITest.IsChromeOS(): - _EXPECTED_DEFAULT_THUMB_INFO = { - u'apps': True, - u'most_visited': False - } - _EXPECTED_DEFAULT_MENU_INFO = { - u'apps': False, - u'most_visited': True, - u'recently_closed': True - } - else: - _EXPECTED_DEFAULT_THUMB_INFO = { - u'apps': False, - u'most_visited': True - } - _EXPECTED_DEFAULT_MENU_INFO = { - u'apps': False, - u'most_visited': False, - u'recently_closed': False - } - - def Debug(self): - """Test method for experimentation. - - This method is not run automatically. - """ - while True: - raw_input('Interact with the browser and hit <enter> to dump NTP info...') - print '*' * 20 - self.pprint(self._GetNTPInfo()) - - def __init__(self, methodName='runTest'): - super(NTPTest, self).__init__(methodName) - - # Create some dummy file urls we can use in the tests. - filenames = ['title1.html', 'title2.html'] - titles = [u'', u'Title Of Awesomeness'] - urls = map(lambda name: self.GetFileURLForDataPath(name), filenames) - self.PAGES = map(lambda url, title: {'url': url, 'title': title}, - urls, titles) - - def _NTPContainsThumbnail(self, check_thumbnail): - """Returns whether the NTP's Most Visited section contains the given - thumbnail.""" - for thumbnail in self.GetNTPThumbnails(): - if check_thumbnail['url'] == thumbnail['url']: - return True - return False - - def testFreshProfile(self): - """Tests that the NTP with a fresh profile is correct""" - thumbnails = self.GetNTPThumbnails() - default_sites = self.GetNTPDefaultSites() - self.assertEqual(len(default_sites), len(thumbnails)) - for thumbnail, default_site in zip(thumbnails, default_sites): - self.assertEqual(thumbnail['url'], default_site) - self.assertEqual(0, len(self.GetNTPRecentlyClosed())) - - def testRemoveDefaultThumbnails(self): - """Tests that the default thumbnails can be removed""" - self.RemoveNTPDefaultThumbnails() - self.assertFalse(self.GetNTPThumbnails()) - self.RestoreAllNTPThumbnails() - self.assertEqual(len(self.GetNTPDefaultSites()), - len(self.GetNTPThumbnails())) - self.RemoveNTPDefaultThumbnails() - self.assertFalse(self.GetNTPThumbnails()) - - def testOneMostVisitedSite(self): - """Tests that a site is added to the most visited sites""" - self.RemoveNTPDefaultThumbnails() - self.NavigateToURL(self.PAGES[1]['url']) - thumbnail = self.GetNTPThumbnails()[0] - self.assertEqual(self.PAGES[1]['url'], thumbnail['url']) - self.assertEqual(self.PAGES[1]['title'], thumbnail['title']) - - def testRemoveThumbnail(self): - """Tests removing a thumbnail works""" - self.RemoveNTPDefaultThumbnails() - for page in self.PAGES: - self.AppendTab(pyauto.GURL(page['url'])) - - thumbnails = self.GetNTPThumbnails() - for thumbnail in thumbnails: - self.assertEquals(thumbnail, self.GetNTPThumbnails()[0]) - self.RemoveNTPThumbnail(thumbnail) - self.assertFalse(self._NTPContainsThumbnail(thumbnail)) - self.assertFalse(self.GetNTPThumbnails()) - - def testIncognitoNotAppearInMostVisited(self): - """Tests that visiting a page in incognito mode does cause it to appear in - the Most Visited section""" - self.RemoveNTPDefaultThumbnails() - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - self.NavigateToURL(self.PAGES[0]['url'], 1, 0) - self.assertFalse(self.GetNTPThumbnails()) - - def testDifferentProfileNotAppearInMostVisited(self): - """Tests that visiting a page in one profile does not cause it to appear in - the Most Visited section of another.""" - self.RemoveNTPDefaultThumbnails() - self.OpenNewBrowserWindowWithNewProfile() - self.NavigateToURL(self.PAGES[0]['url'], 1, 0) - self.assertFalse(self.GetNTPThumbnails()) - - def testThumbnailPersistence(self): - """Tests that thumbnails persist across Chrome restarts""" - self.RemoveNTPDefaultThumbnails() - for page in self.PAGES: - self.AppendTab(pyauto.GURL(page['url'])) - thumbnails = self.GetNTPThumbnails() - self.RestartBrowser(clear_profile=False) - self.assertEqual(thumbnails, self.GetNTPThumbnails()) - - def testRestoreAllRemovedThumbnails(self): - """Tests restoring all removed thumbnails""" - for page in self.PAGES: - self.AppendTab(pyauto.GURL(page['url'])) - - thumbnails = self.GetNTPThumbnails() - for thumbnail in thumbnails: - self.RemoveNTPThumbnail(thumbnail) - - self.RestoreAllNTPThumbnails() - self.assertEquals(thumbnails, self.GetNTPThumbnails()) - - def testThumbnailRanking(self): - """Tests that the thumbnails are ordered according to visit count""" - self.RemoveNTPDefaultThumbnails() - for page in self.PAGES: - self.AppendTab(pyauto.GURL(page['url'])) - thumbnails = self.GetNTPThumbnails() - self.assertEqual(self.PAGES[0]['url'], self.GetNTPThumbnails()[0]['url']) - self.AppendTab(pyauto.GURL(self.PAGES[1]['url'])) - self.assertEqual(self.PAGES[1]['url'], self.GetNTPThumbnails()[0]['url']) - self.AppendTab(pyauto.GURL(self.PAGES[0]['url'])) - self.AppendTab(pyauto.GURL(self.PAGES[0]['url'])) - self.assertEqual(self.PAGES[0]['url'], self.GetNTPThumbnails()[0]['url']) - - def testThumbnailTitleChangeAfterPageTitleChange(self): - """Tests that once a page title changes, the thumbnail title changes too""" - self.RemoveNTPDefaultThumbnails() - self.NavigateToURL(self.PAGES[0]['url']) - self.assertEqual(self.PAGES[0]['title'], - self.GetNTPThumbnails()[0]['title']) - self.ExecuteJavascript('window.domAutomationController.send(' + - 'document.title = "new title")') - self.assertEqual('new title', self.GetNTPThumbnails()[0]['title']) - - def testCloseOneTab(self): - """Tests that closing a tab populates the recently closed list""" - self.RemoveNTPDefaultThumbnails() - self.AppendTab(pyauto.GURL(self.PAGES[1]['url'])) - self.CloseTab(tab_index=1) - self.assertEqual(self.PAGES[1]['url'], - self.GetNTPRecentlyClosed()[0]['url']) - self.assertEqual(self.PAGES[1]['title'], - self.GetNTPRecentlyClosed()[0]['title']) - - def testCloseOneWindow(self): - """Tests that closing a window populates the recently closed list""" - self.RemoveNTPDefaultThumbnails() - self.OpenNewBrowserWindow(True) - self.NavigateToURL(self.PAGES[0]['url'], 1, 0) - self.AppendTab(pyauto.GURL(self.PAGES[1]['url']), 1) - self.CloseBrowserWindow(1) - expected = [{ u'type': u'window', - u'tabs': [ - { u'type': u'tab', - u'url': self.PAGES[0]['url'], - u'direction': u'ltr' }, - { u'type': u'tab', - u'url': self.PAGES[1]['url']}] - }] - self.assertEquals(expected, test_utils.StripUnmatchedKeys( - self.GetNTPRecentlyClosed(), expected)) - - def testCloseMultipleTabs(self): - """Tests closing multiple tabs populates the Recently Closed section in - order""" - self.RemoveNTPDefaultThumbnails() - self.AppendTab(pyauto.GURL(self.PAGES[0]['url'])) - self.AppendTab(pyauto.GURL(self.PAGES[1]['url'])) - self.CloseTab(tab_index=2) - self.CloseTab(tab_index=1) - expected = [{ u'type': u'tab', - u'url': self.PAGES[0]['url'] - }, - { u'type': u'tab', - u'url': self.PAGES[1]['url'] - }] - self.assertEquals(expected, test_utils.StripUnmatchedKeys( - self.GetNTPRecentlyClosed(), expected)) - - def testCloseWindowWithOneTab(self): - """Tests that closing a window with only one tab only shows up as a tab in - the Recently Closed section""" - self.RemoveNTPDefaultThumbnails() - self.OpenNewBrowserWindow(True) - self.NavigateToURL(self.PAGES[0]['url'], 1, 0) - self.CloseBrowserWindow(1) - expected = [{ u'type': u'tab', - u'url': self.PAGES[0]['url'] - }] - self.assertEquals(expected, test_utils.StripUnmatchedKeys( - self.GetNTPRecentlyClosed(), expected)) - - def testCloseMultipleWindows(self): - """Tests closing multiple windows populates the Recently Closed list""" - self.RemoveNTPDefaultThumbnails() - self.OpenNewBrowserWindow(True) - self.NavigateToURL(self.PAGES[0]['url'], 1, 0) - self.AppendTab(pyauto.GURL(self.PAGES[1]['url']), 1) - self.OpenNewBrowserWindow(True) - self.NavigateToURL(self.PAGES[1]['url'], 2, 0) - self.AppendTab(pyauto.GURL(self.PAGES[0]['url']), 2) - self.CloseBrowserWindow(2) - self.CloseBrowserWindow(1) - expected = [{ u'type': u'window', - u'tabs': [ - { u'type': u'tab', - u'url': self.PAGES[0]['url'], - u'direction': u'ltr' }, - { u'type': u'tab', - u'url': self.PAGES[1]['url']}] - }, - { u'type': u'window', - u'tabs': [ - { u'type': u'tab', - u'url': self.PAGES[1]['url'], - u'direction': u'ltr' }, - { u'type': u'tab', - u'url': self.PAGES[0]['url']}] - }] - self.assertEquals(expected, test_utils.StripUnmatchedKeys( - self.GetNTPRecentlyClosed(), expected)) - - def testRecentlyClosedIncognito(self): - """Tests that we don't record closure of Incognito tabs or windows""" - #self.RemoveNTPDefaultThumbnails() - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - self.NavigateToURL(self.PAGES[0]['url'], 1, 0) - self.AppendTab(pyauto.GURL(self.PAGES[0]['url']), 1) - self.AppendTab(pyauto.GURL(self.PAGES[1]['url']), 1) - self.CloseTab(windex=1) - self.assertFalse(self.GetNTPRecentlyClosed()) - self.CloseBrowserWindow(1) - self.assertFalse(self.GetNTPRecentlyClosed()) - - def _VerifyAppInfo(self, actual_info, expected_info): - """Ensures that the actual app info contains the expected app info. - - This method assumes that both the actual and expected information for each - app contains at least the 'title' attribute. Both sets of info are - considered to match if the actual info contains at least the specified - expected info (if the actual info contains additional values that are not - specified in the expected info, that's ok). This function will fail the - current test if both sets of info don't match. - - Args: - actual_info: A list of dictionaries representing the information from - all apps that would currently be displayed on the NTP. - expected_info: A corrresponding list of dictionaries representing the - information that is expected. - """ - # Ensure all app info dictionaries contain at least the 'title' attribute. - self.assertTrue(all(map(lambda app: 'title' in app, actual_info)) and - all(map(lambda app: 'title' in app, expected_info)), - msg='At least one app is missing the "title" attribute.') - - # Sort both app lists by title to ensure they're in a known order. - actual_info = sorted(actual_info, key=lambda app: app['title']) - expected_info = sorted(expected_info, key=lambda app: app['title']) - - # Ensure the expected info matches the actual info. - self.assertTrue(len(actual_info) == len(expected_info), - msg='Expected %d app(s) on NTP, but got %d instead.' % ( - len(expected_info), len(actual_info))) - for i, expected_app in enumerate(expected_info): - for attribute in expected_app: - self.assertTrue(attribute in actual_info[i], - msg='Expected attribute "%s" not found in app info.' % ( - attribute)) - self.assertTrue(expected_app[attribute] == actual_info[i][attribute], - msg='For attribute "%s", expected value "%s", but got ' - '"%s".' % (attribute, expected_app[attribute], - actual_info[i][attribute])) - - def _InstallAndVerifySamplePackagedApp(self): - """Installs a sample packaged app and verifies the install is successful. - - Returns: - The string ID of the installed app. - """ - app_crx_file = os.path.abspath(os.path.join( - self.DataDir(), 'pyauto_private', 'apps', 'countdown.crx')) - return self.InstallExtension(app_crx_file) - - def testGetAppsInNewProfile(self): - """Ensures that the only app in a new profile is the Web Store app.""" - app_info = self.GetNTPApps() - self._VerifyAppInfo(app_info, self._EXPECTED_DEFAULT_APPS) - - def testGetAppsWhenInstallApp(self): - """Ensures that an installed app is reflected in the app info in the NTP.""" - self._InstallAndVerifySamplePackagedApp() - app_info = self.GetNTPApps() - expected_app_info = [ - { - u'title': u'Countdown' - } - ] - expected_app_info.extend(self._EXPECTED_DEFAULT_APPS) - self._VerifyAppInfo(app_info, expected_app_info) - - def testGetAppsWhenInstallNonApps(self): - """Ensures installed non-apps are not reflected in the NTP app info.""" - # Install a regular extension and a theme. - ext_crx_file = os.path.abspath(os.path.join(self.DataDir(), 'extensions', - 'page_action.crx')) - self.InstallExtension(ext_crx_file) - theme_crx_file = os.path.abspath(os.path.join(self.DataDir(), 'extensions', - 'theme.crx')) - self.SetTheme(theme_crx_file) - # Verify that no apps are listed on the NTP except for the Web Store. - app_info = self.GetNTPApps() - self._VerifyAppInfo(app_info, self._EXPECTED_DEFAULT_APPS) - - def testUninstallApp(self): - """Ensures that an uninstalled app is reflected in the NTP app info.""" - # First, install an app and verify that it exists in the NTP app info. - installed_app_id = self._InstallAndVerifySamplePackagedApp() - app_info = self.GetNTPApps() - expected_app_info = [ - { - u'title': u'Countdown' - } - ] - expected_app_info.extend(self._EXPECTED_DEFAULT_APPS) - self._VerifyAppInfo(app_info, expected_app_info) - - # Next, uninstall the app and verify that it is removed from the NTP. - self.assertTrue(self.UninstallExtensionById(installed_app_id), - msg='Call to UninstallExtensionById() returned False.') - app_info = self.GetNTPApps() - self._VerifyAppInfo(app_info, self._EXPECTED_DEFAULT_APPS) - - def testCannotUninstallWebStore(self): - """Ensures that the WebStore app cannot be uninstalled.""" - # Verify that the WebStore app is already installed in a fresh profile. - app_info = self.GetNTPApps() - self._VerifyAppInfo(app_info, self._EXPECTED_DEFAULT_APPS) - self.assertTrue(app_info and 'id' in app_info[0], - msg='Cannot identify ID of WebStore app.') - webstore_id = app_info[0]['id'] - - # Attempt to uninstall the WebStore app and verify that it still exists - # in the App info of the NTP even after we try to uninstall it. - self.assertFalse(self.UninstallExtensionById(webstore_id), - msg='Call to UninstallExtensionById() returned True.') - self._VerifyAppInfo(self.GetNTPApps(), self._EXPECTED_DEFAULT_APPS) - - def testLaunchAppWithDefaultSettings(self): - """Verifies that an app can be launched with the default settings.""" - # Install an app. - installed_app_id = self._InstallAndVerifySamplePackagedApp() - - # Launch the app from the NTP. - self.LaunchApp(installed_app_id) - - # Verify that the second tab in the first window is the app launch URL. - # It should be the second tab, not the first, since the call to LaunchApp - # should have first opened the NTP in a new tab, and then launched the app - # from there. - info = self.GetBrowserInfo() - actual_tab_url = info['windows'][0]['tabs'][1]['url'] - expected_app_url_start = 'chrome-extension://' + installed_app_id - self.assertTrue(actual_tab_url.startswith(expected_app_url_start), - msg='The app was not launched.') - - def testLaunchAppRegularTab(self): - """Verifies that an app can be launched in a regular tab.""" - installed_app_id = self._InstallAndVerifySamplePackagedApp() - - self.SetAppLaunchType(installed_app_id, 'regular', windex=0) - self.LaunchApp(installed_app_id) - - # Verify that the second tab in the first window is the app launch URL. - info = self.GetBrowserInfo() - actual_tab_url = info['windows'][0]['tabs'][1]['url'] - expected_app_url_start = 'chrome-extension://' + installed_app_id - self.assertTrue(actual_tab_url.startswith(expected_app_url_start), - msg='The app was not launched in a regular tab.') - - def testLaunchAppPinnedTab(self): - """Verifies that an app can be launched in a pinned tab.""" - installed_app_id = self._InstallAndVerifySamplePackagedApp() - - self.SetAppLaunchType(installed_app_id, 'pinned', windex=0) - self.LaunchApp(installed_app_id) - - # Verify that the first tab in the first window is the app launch URL, and - # that it is a pinned tab. - info = self.GetBrowserInfo() - actual_tab_url = info['windows'][0]['tabs'][0]['url'] - expected_app_url_start = 'chrome-extension://' + installed_app_id - self.assertTrue(actual_tab_url.startswith(expected_app_url_start) and - info['windows'][0]['tabs'][0]['pinned'], - msg='The app was not launched in a pinned tab.') - - def testLaunchAppFullScreen(self): - """Verifies that an app can be launched in fullscreen mode.""" - installed_app_id = self._InstallAndVerifySamplePackagedApp() - - self.SetAppLaunchType(installed_app_id, 'fullscreen', windex=0) - self.LaunchApp(installed_app_id) - - # Verify that the second tab in the first window is the app launch URL, and - # that the window is fullscreen. - info = self.GetBrowserInfo() - actual_tab_url = info['windows'][0]['tabs'][1]['url'] - expected_app_url_start = 'chrome-extension://' + installed_app_id - self.assertTrue(actual_tab_url.startswith(expected_app_url_start) and - info['windows'][0]['fullscreen'], - msg='The app was not launched in fullscreen mode.') - - def testLaunchAppNewWindow(self): - """Verifies that an app can be launched in a new window.""" - installed_app_id = self._InstallAndVerifySamplePackagedApp() - - self.SetAppLaunchType(installed_app_id, 'window', windex=0) - self.LaunchApp(installed_app_id) - - # Verify that a second window exists (at index 1), and that its first tab - # is the app launch URL. - info = self.GetBrowserInfo() - self.assertTrue(len(info['windows']) == 2, - msg='A second window does not exist.') - actual_tab_url = info['windows'][1]['tabs'][0]['url'] - expected_app_url_start = 'chrome-extension://' + installed_app_id - self.assertTrue(actual_tab_url.startswith(expected_app_url_start), - msg='The app was not launched in the new window.') - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/omnibox.py b/chrome/test/functional/omnibox.py deleted file mode 100755 index 75f6aac..0000000 --- a/chrome/test/functional/omnibox.py +++ /dev/null @@ -1,369 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import glob -import os -import re -import shutil -import tempfile -import urlparse - -import pyauto_functional # Must be imported before pyauto -import pyauto -import test_utils - - -class OmniboxTest(pyauto.PyUITest): - """Test cases for the omnibox.""" - - def Debug(self): - """Test method for experimentation. - - This method will not run automatically. - """ - import time - while True: - self.pprint(self.GetOmniboxInfo().omniboxdict) - time.sleep(1) - - def testFocusOnStartup(self): - """Verify that the omnibox has focus on startup.""" - self.WaitUntilOmniboxReadyHack() - self.assertTrue(self.GetOmniboxInfo().Properties('has_focus')) - - def testHistoryResult(self): - """Verify that the omnibox can fetch items from the history.""" - url = self.GetFileURLForDataPath('title2.html') - title = 'Title Of Awesomeness' - self.AppendTab(pyauto.GURL(url)) - def _VerifyHistoryResult(query_list, description, windex=0): - """Verify result matching given description for given list of queries.""" - for query_text in query_list: - matches = test_utils.GetOmniboxMatchesFor( - self, query_text, windex=windex, - attr_dict={'description': description}) - self.assertTrue(matches) - self.assertEqual(1, len(matches)) - item = matches[0] - self.assertEqual(url, item['destination_url']) - # Query using URL & title. - _VerifyHistoryResult([url, title], title) - # Verify results in another tab. - self.AppendTab(pyauto.GURL()) - _VerifyHistoryResult([url, title], title) - # Verify results in another window. - self.OpenNewBrowserWindow(True) - self.WaitUntilOmniboxReadyHack(windex=1) - _VerifyHistoryResult([url, title], title, windex=1) - # Verify results in an incognito window. - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - self.WaitUntilOmniboxReadyHack(windex=2) - _VerifyHistoryResult([url, title], title, windex=2) - - def _VerifyOmniboxURLMatches(self, url, description, windex=0): - """Verify URL match results from the omnibox. - - Args: - url: The URL to use. - description: The string description within the history page and Google - search to match against. - windex: The window index to work on. Defaults to 0 (first window). - """ - matches_description = test_utils.GetOmniboxMatchesFor( - self, url, windex=windex, attr_dict={'description': description}) - self.assertEqual(1, len(matches_description)) - if description == 'Google Search': - self.assertTrue(re.match('http://www.google.com/search.+', - matches_description[0]['destination_url'])) - else: - self.assertEqual(url, matches_description[0]['destination_url']) - - def testFetchHistoryResultItems(self): - """Verify omnibox fetches history items in 2nd tab, window and incognito.""" - url = self.GetFileURLForDataPath('title2.html') - title = 'Title Of Awesomeness' - desc = 'Google Search' - # Fetch history page item in the second tab. - self.AppendTab(pyauto.GURL(url)) - self._VerifyOmniboxURLMatches(url, title) - # Fetch history page items in the second window. - self.OpenNewBrowserWindow(True) - self.NavigateToURL(url, 1, 0) - self._VerifyOmniboxURLMatches(url, title, windex=1) - # Fetch google search items in incognito window. - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - self.NavigateToURL(url, 2, 0) - self._VerifyOmniboxURLMatches(url, desc, windex=2) - - def testSelect(self): - """Verify omnibox popup selection.""" - url1 = self.GetFileURLForDataPath('title2.html') - url2 = self.GetFileURLForDataPath('title1.html') - title1 = 'Title Of Awesomeness' - self.NavigateToURL(url1) - self.NavigateToURL(url2) - matches = test_utils.GetOmniboxMatchesFor(self, 'file://') - self.assertTrue(matches) - # Find the index of match for |url1|. - index = None - for i, match in enumerate(matches): - if match['description'] == title1: - index = i - self.assertTrue(index is not None) - self.OmniboxMovePopupSelection(index) # Select |url1| line in popup. - self.assertEqual(url1, self.GetOmniboxInfo().Text()) - self.OmniboxAcceptInput() - self.assertEqual(title1, self.GetActiveTabTitle()) - - def testInlineAutoComplete(self): - """Verify inline autocomplete for a pre-visited URL.""" - self.NavigateToURL('http://www.google.com') - matches = test_utils.GetOmniboxMatchesFor(self, 'goog') - self.assertTrue(matches) - # Omnibox should suggest auto completed URL as the first item. - matches_description = matches[0] - self.assertTrue('www.google.com' in matches_description['contents']) - self.assertEqual('history-url', matches_description['type']) - # The URL should be inline-autocompleted in the omnibox. - self.assertTrue('google.com' in self.GetOmniboxInfo().Text()) - - def testCrazyFilenames(self): - """Test omnibox query with filenames containing special chars. - - The files are created on the fly and cleaned after use. - """ - filename = os.path.join(self.DataDir(), 'downloads', 'crazy_filenames.txt') - zip_names = self.EvalDataFrom(filename) - # We got .zip filenames. Change them to .html. - crazy_filenames = [x.replace('.zip', '.html') for x in zip_names] - title = 'given title' - - def _CreateFile(name): - """Create the given html file.""" - fp = open(name, 'w') # |name| could be unicode. - print >>fp, '<html><title>%s</title><body>' % title - print >>fp, 'This is a junk file named <h2>%s</h2>' % repr(name) - print >>fp, '</body></html>' - fp.close() - - crazy_fileurls = [] - # Temp dir for hosting crazy filenames. - temp_dir = tempfile.mkdtemp(prefix='omnibox') - # Windows has a dual nature dealing with unicode filenames. - # While the files are internally saved as unicode, there's a non-unicode - # aware API that returns a locale-dependent coding on the true unicode - # filenames. This messes up things. - # Filesystem-interfacing functions like os.listdir() need to - # be given unicode strings to "do the right thing" on win. - # Ref: http://boodebr.org/main/python/all-about-python-and-unicode - try: - for filename in crazy_filenames: # |filename| is unicode. - file_path = os.path.join(temp_dir, filename.encode('utf-8')) - _CreateFile(os.path.join(temp_dir, filename)) - file_url = self.GetFileURLForPath(file_path) - crazy_fileurls.append(file_url) - self.NavigateToURL(file_url) - - # Verify omnibox queries. - for file_url in crazy_fileurls: - matches = test_utils.GetOmniboxMatchesFor(self, - file_url, attr_dict={'type': 'url-what-you-typed', - 'description': title}) - self.assertTrue(matches) - self.assertEqual(1, len(matches)) - self.assertTrue(os.path.basename(file_url) in - matches[0]['destination_url']) - finally: - shutil.rmtree(unicode(temp_dir)) # Unicode so that Win treats nicely. - - def testSuggest(self): - """Verify suggested results in omnibox.""" - matches = test_utils.GetOmniboxMatchesFor(self, 'apple') - self.assertTrue(matches) - self.assertTrue([x for x in matches if x['type'] == 'search-suggest']) - - def testDifferentTypesOfResults(self): - """Verify different types of results from omnibox. - - This includes history result, bookmark result, suggest results. - """ - url = 'http://www.google.com/' - title = 'Google' - search_string = 'google' - self.AddBookmarkURL( # Add a bookmark. - self.GetBookmarkModel().BookmarkBar()['id'], 0, title, url) - self.NavigateToURL(url) # Build up history. - matches = test_utils.GetOmniboxMatchesFor(self, search_string) - self.assertTrue(matches) - # Verify starred result (indicating bookmarked url). - self.assertTrue([x for x in matches if x['starred'] == True]) - for item_type in ('history-url', 'search-what-you-typed', - 'search-suggest',): - self.assertTrue([x for x in matches if x['type'] == item_type]) - - def testSuggestPref(self): - """Verify no suggests for omnibox when suggested-services disabled.""" - search_string = 'apple' - self.assertTrue(self.GetPrefsInfo().Prefs(pyauto.kSearchSuggestEnabled)) - matches = test_utils.GetOmniboxMatchesFor(self, search_string) - self.assertTrue(matches) - self.assertTrue([x for x in matches if x['type'] == 'search-suggest']) - # Disable suggest-service. - self.SetPrefs(pyauto.kSearchSuggestEnabled, False) - self.assertFalse(self.GetPrefsInfo().Prefs(pyauto.kSearchSuggestEnabled)) - matches = test_utils.GetOmniboxMatchesFor(self, search_string) - self.assertTrue(matches) - # Verify there are no suggest results. - self.assertFalse([x for x in matches if x['type'] == 'search-suggest']) - - def testAutoCompleteForSearch(self): - """Verify omnibox autocomplete for search.""" - search_string = 'youtu' - verify_string = 'youtube' - matches = test_utils.GetOmniboxMatchesFor(self, search_string) - # Retrieve last contents element. - matches_description = matches[-1]['contents'].split() - self.assertEqual(verify_string, matches_description[0]) - - def _GotContentHistory(self, search_text, url): - """Check if omnibox returns a previously-visited page for given search text. - - Args: - search_text: The string search text. - url: The string URL to look for in the omnibox matches. - - Returns: - True, if the omnibox returns the previously-visited page for the given - search text, or False otherwise. - """ - # Omnibox doesn't change results if searching the same text repeatedly. - # So setting '' in omnibox before the next repeated search. - self.SetOmniboxText('') - matches = test_utils.GetOmniboxMatchesFor(self, search_text) - matches_description = [x for x in matches if x['destination_url'] == url] - return 1 == len(matches_description) - - def testContentHistory(self): - """Verify omnibox results when entering page content. - - Test verifies that visited page shows up in omnibox on entering page - content. - """ - url = self.GetFileURLForPath( - os.path.join(self.DataDir(), 'find_in_page', 'largepage.html')) - self.NavigateToURL(url) - self.assertTrue(self.WaitUntil( - lambda: self._GotContentHistory('British throne', url))) - - def testOmniboxSearchHistory(self): - """Verify page navigation/search from omnibox are added to the history.""" - url = self.GetFileURLForDataPath('title2.html') - self.NavigateToURL(url) - self.AppendTab(pyauto.GURL('about:blank')) - self.SetOmniboxText('java') - self.WaitUntilOmniboxQueryDone() - self.OmniboxAcceptInput() - history = self.GetHistoryInfo().History() - self.assertEqual(2, len(history)) - self.assertEqual(url, history[1]['url']) - self.assertEqual('java - Google Search', history[0]['title']) - - def _VerifyHasBookmarkResult(self, matches): - """Verify that we have a bookmark result. - - Args: - matches: A list of match items, as returned by - test_utils.GetOmniboxMatchesFor(). - """ - matches_starred = [result for result in matches if result['starred']] - self.assertTrue(matches_starred) - self.assertEqual(1, len(matches_starred)) - - def _CheckBookmarkResultForVariousInputs(self, url, title, windex=0): - """Check if we get the bookmark for complete and partial inputs. - - Args: - url: A string URL. - title: A string title for the given URL. - windex: The window index to use. Defaults to 0 (first window). - """ - # Check if the complete URL would get the bookmark. - url_matches = test_utils.GetOmniboxMatchesFor(self, url, windex=windex) - self._VerifyHasBookmarkResult(url_matches) - # Check if the complete title would get the bookmark. - title_matches = test_utils.GetOmniboxMatchesFor(self, title, windex=windex) - self._VerifyHasBookmarkResult(title_matches) - # Check if the partial URL would get the bookmark. - split_url = urlparse.urlsplit(url) - partial_url = test_utils.GetOmniboxMatchesFor( - self, split_url.scheme, windex=windex) - self._VerifyHasBookmarkResult(partial_url) - # Check if the partial title would get the bookmark. - split_title = title.split() - search_term = split_title[len(split_title) - 1] - partial_title = test_utils.GetOmniboxMatchesFor( - self, search_term, windex=windex) - self._VerifyHasBookmarkResult(partial_title) - - def testBookmarkResultInNewTabAndWindow(self): - """Verify omnibox finds bookmarks in search options of new tabs/windows.""" - url = self.GetFileURLForDataPath('title2.html') - self.NavigateToURL(url) - title = 'This is Awesomeness' - bookmarks = self.GetBookmarkModel() - bar_id = bookmarks.BookmarkBar()['id'] - self.AddBookmarkURL(bar_id, 0, title, url) - bookmarks = self.GetBookmarkModel() - nodes = bookmarks.FindByTitle(title) - self.AppendTab(pyauto.GURL(url)) - self._CheckBookmarkResultForVariousInputs(url, title) - self.OpenNewBrowserWindow(True) - self.assertEqual(2, self.GetBrowserWindowCount()) - self.NavigateToURL(url, 1, 0) - self._CheckBookmarkResultForVariousInputs(url, title, windex=1) - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - self.assertEqual(3, self.GetBrowserWindowCount()) - self.NavigateToURL(url, 2, 0) - self._CheckBookmarkResultForVariousInputs(url, title, windex=2) - - def testAutoCompleteForNonAsciiSearch(self): - """Verify can search/autocomplete with non-ASCII incomplete keywords.""" - search_string = u'\u767e' - verify_string = u'\u767e\u5ea6\u4e00\u4e0b' - matches = test_utils.GetOmniboxMatchesFor(self, search_string) - self.assertTrue(verify_string in matches[-1]['contents']) - - -class OmniboxLiveTest(pyauto.PyUITest): - """Test cases for the omnibox that hit live servers (such as Google).""" - - def ExtraChromeFlags(self): - """Override default list of extra flags used in pyauto tests.""" - # Force the suggest field trial group. This doesn't guarantee that there - # will be no experimental behaviour, but there's no other way to disable - # all suggest field trials at the moment. TODO(mpearson): Consider allowing - # the suggest_url to be overridden using a flag (so that we can omit the - # "sugexp=chrome,mod=<n>" CGI param), or provide some other way to turn off - # all suggest field trials. - return ['--force-fieldtrials=OmniboxSearchSuggest/10/'] - - def testGoogleSearch(self): - """Verify Google search item in omnibox results.""" - search_text = 'hello world' - verify_str = 'Google Search' - url_re = 'http://www.google.com/search\?.*q=hello\+world.*' - matches_description = test_utils.GetOmniboxMatchesFor( - self, search_text, attr_dict={'description': verify_str}) - self.assertTrue(matches_description) - # There should be a least one entry with the description Google. Suggest - # results may end up having 'Google Search' in them, so use >=. - self.assertTrue(len(matches_description) >= 1) - item = matches_description[0] - self.assertTrue(re.search(url_re, item['destination_url'])) - self.assertEqual('search-what-you-typed', item['type']) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/perf.cfg b/chrome/test/functional/perf.cfg deleted file mode 100644 index ddb7306..0000000 --- a/chrome/test/functional/perf.cfg +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2012 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. - -# Configuration file for pyauto perf tests. -# This file is used to specify google account credentials and -# custom google account, gmail, docs, plus urls if you don't want to use -# the public urls of these services. It is formatted as a python dictionary. - -{ - # Set to custom username/password if desired. - # If you are external to Google, you must set username/password. - # If you are internal to Google, you can leave them as None - # and tests will try to use internal credential file. - 'username': None, - 'password': None, - - # Set to custom Google account url if desired. - 'google_account_url': None, # Defaults to 'https://accounts.google.com' - - # Set to custom Gmail/Plus/Docs urls if desired. - # Only effective for perf_endure.py - 'gmail_url': None, # Defaults to 'https://www.gmail.com' - 'plus_url': None, # Defaults to 'https://plus.google.com' - 'docs_url': None, # Defaults to 'https://docs.google.com' -} diff --git a/chrome/test/functional/perf.py b/chrome/test/functional/perf.py deleted file mode 100755 index 77b2eb5..0000000 --- a/chrome/test/functional/perf.py +++ /dev/null @@ -1,2426 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Basic pyauto performance tests. - -For tests that need to be run for multiple iterations (e.g., so that average -and standard deviation values can be reported), the default number of iterations -run for each of these tests is specified by |_DEFAULT_NUM_ITERATIONS|. -That value can optionally be tweaked by setting an environment variable -'NUM_ITERATIONS' to a positive integer, representing the number of iterations -to run. An additional, initial iteration will also be run to "warm up" the -environment, and the result from that initial iteration will be ignored. - -Some tests rely on repeatedly appending tabs to Chrome. Occasionally, these -automation calls time out, thereby affecting the timing measurements (see issue -crosbug.com/20503). To work around this, the tests discard timing measurements -that involve automation timeouts. The value |_DEFAULT_MAX_TIMEOUT_COUNT| -specifies the threshold number of timeouts that can be tolerated before the test -fails. To tweak this value, set environment variable 'MAX_TIMEOUT_COUNT' to the -desired threshold value. -""" - -import BaseHTTPServer -import commands -import errno -import itertools -import logging -import math -import os -import posixpath -import re -import SimpleHTTPServer -import SocketServer -import signal -import subprocess -import sys -import tempfile -import threading -import time -import timeit -import urllib -import urllib2 -import urlparse - -import pyauto_functional # Must be imported before pyauto. -import pyauto -import simplejson # Must be imported after pyauto; located in third_party. - -from netflix import NetflixTestHelper -import pyauto_utils -import test_utils -from youtube import YoutubeTestHelper - - -_CHROME_BASE_DIR = os.path.abspath(os.path.join( - os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, os.pardir)) - - -def FormatChromePath(posix_path, **kwargs): - """Convert a path relative to the Chromium root into an OS-specific path. - - Args: - posix_path: a path string that may be a format(). - Example: 'src/third_party/{module_name}/__init__.py' - kwargs: args for the format replacement. - Example: {'module_name': 'pylib'} - - Returns: - an absolute path in the current Chromium tree with formatting applied. - """ - formated_path = posix_path.format(**kwargs) - path_parts = formated_path.split('/') - return os.path.join(_CHROME_BASE_DIR, *path_parts) - - -def StandardDeviation(values): - """Returns the standard deviation of |values|.""" - avg = Mean(values) - if len(values) < 2 or not avg: - return 0.0 - temp_vals = [math.pow(x - avg, 2) for x in values] - return math.sqrt(sum(temp_vals) / (len(temp_vals) - 1)) - - -def Mean(values): - """Returns the arithmetic mean of |values|.""" - if not values or None in values: - return None - return sum(values) / float(len(values)) - - -def GeometricMean(values): - """Returns the geometric mean of |values|.""" - if not values or None in values or [x for x in values if x < 0.0]: - return None - if 0.0 in values: - return 0.0 - return math.exp(Mean([math.log(x) for x in values])) - - -class BasePerfTest(pyauto.PyUITest): - """Base class for performance tests.""" - - _DEFAULT_NUM_ITERATIONS = 10 # Keep synced with desktopui_PyAutoPerfTests.py. - _DEFAULT_MAX_TIMEOUT_COUNT = 10 - _PERF_OUTPUT_MARKER_PRE = '_PERF_PRE_' - _PERF_OUTPUT_MARKER_POST = '_PERF_POST_' - - def setUp(self): - """Performs necessary setup work before running each test.""" - self._num_iterations = self._DEFAULT_NUM_ITERATIONS - if 'NUM_ITERATIONS' in os.environ: - self._num_iterations = int(os.environ['NUM_ITERATIONS']) - self._max_timeout_count = self._DEFAULT_MAX_TIMEOUT_COUNT - if 'MAX_TIMEOUT_COUNT' in os.environ: - self._max_timeout_count = int(os.environ['MAX_TIMEOUT_COUNT']) - self._timeout_count = 0 - - # For users who want to see local perf graphs for Chrome when running the - # tests on their own machines. - self._local_perf_dir = None - if 'LOCAL_PERF_DIR' in os.environ: - self._local_perf_dir = os.environ['LOCAL_PERF_DIR'] - if not os.path.exists(self._local_perf_dir): - self.fail('LOCAL_PERF_DIR environment variable specified as %s, ' - 'but this directory does not exist.' % self._local_perf_dir) - # When outputting perf graph information on-the-fly for Chrome, this - # variable lets us know whether a perf measurement is for a new test - # execution, or the current test execution. - self._seen_graph_lines = {} - - pyauto.PyUITest.setUp(self) - - # Flush all buffers to disk and wait until system calms down. Must be done - # *after* calling pyauto.PyUITest.setUp, since that is where Chrome is - # killed and re-initialized for a new test. - # TODO(dennisjeffrey): Implement wait for idle CPU on Windows/Mac. - if self.IsLinux(): # IsLinux() also implies IsChromeOS(). - os.system('sync') - self._WaitForIdleCPU(60.0, 0.05) - - def _IsPIDRunning(self, pid): - """Checks if a given process id is running. - - Args: - pid: The process id of the process to check. - - Returns: - True if the process is running. False if not. - """ - try: - # Note that this sends the signal 0, which should not interfere with the - # process. - os.kill(pid, 0) - except OSError, err: - if err.errno == errno.ESRCH: - return False - - try: - with open('/proc/%s/status' % pid) as proc_file: - if 'zombie' in proc_file.read(): - return False - except IOError: - return False - return True - - def _GetAllDescendentProcesses(self, pid): - pstree_out = subprocess.check_output(['pstree', '-p', '%s' % pid]) - children = re.findall('\((\d+)\)', pstree_out) - return [int(pid) for pid in children] - - def _WaitForChromeExit(self, browser_info, timeout): - pid = browser_info['browser_pid'] - chrome_pids = self._GetAllDescendentProcesses(pid) - initial_time = time.time() - while time.time() - initial_time < timeout: - if any([self._IsPIDRunning(pid) for pid in chrome_pids]): - time.sleep(1) - else: - logging.info('_WaitForChromeExit() took: %s seconds', - time.time() - initial_time) - return - self.fail('_WaitForChromeExit() did not finish within %s seconds' % - timeout) - - def tearDown(self): - if self._IsPGOMode(): - browser_info = self.GetBrowserInfo() - pid = browser_info['browser_pid'] - # session_manager kills chrome without waiting for it to cleanly exit. - # Until that behavior is changed, we stop it and wait for Chrome to exit - # cleanly before restarting it. See: - # crbug.com/264717 - subprocess.call(['sudo', 'pkill', '-STOP', 'session_manager']) - os.kill(pid, signal.SIGINT) - self._WaitForChromeExit(browser_info, 120) - subprocess.call(['sudo', 'pkill', '-CONT', 'session_manager']) - - pyauto.PyUITest.tearDown(self) - - def _IsPGOMode(self): - return 'USE_PGO' in os.environ - - def _WaitForIdleCPU(self, timeout, utilization): - """Waits for the CPU to become idle (< utilization). - - Args: - timeout: The longest time in seconds to wait before throwing an error. - utilization: The CPU usage below which the system should be considered - idle (between 0 and 1.0 independent of cores/hyperthreads). - """ - time_passed = 0.0 - fraction_non_idle_time = 1.0 - logging.info('Starting to wait up to %fs for idle CPU...', timeout) - while fraction_non_idle_time >= utilization: - cpu_usage_start = self._GetCPUUsage() - time.sleep(2) - time_passed += 2.0 - cpu_usage_end = self._GetCPUUsage() - fraction_non_idle_time = \ - self._GetFractionNonIdleCPUTime(cpu_usage_start, cpu_usage_end) - logging.info('Current CPU utilization = %f.', fraction_non_idle_time) - if time_passed > timeout: - self._LogProcessActivity() - message = ('CPU did not idle after %fs wait (utilization = %f).' % ( - time_passed, fraction_non_idle_time)) - - # crosbug.com/37389 - if self._IsPGOMode(): - logging.info(message) - logging.info('Still continuing because we are in PGO mode.') - return - - self.fail(message) - logging.info('Wait for idle CPU took %fs (utilization = %f).', - time_passed, fraction_non_idle_time) - - def _LogProcessActivity(self): - """Logs the output of top on Linux/Mac/CrOS. - - TODO: use taskmgr or similar on Windows. - """ - if self.IsLinux() or self.IsMac(): # IsLinux() also implies IsChromeOS(). - logging.info('Logging current process activity using top.') - cmd = 'top -b -d1 -n1' - if self.IsMac(): - cmd = 'top -l1' - p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True) - output = p.stdout.read() - logging.info(output) - else: - logging.info('Process activity logging not implemented on this OS.') - - def _AppendTab(self, url): - """Appends a tab and increments a counter if the automation call times out. - - Args: - url: The string url to which the appended tab should be navigated. - """ - if not self.AppendTab(pyauto.GURL(url)): - self._timeout_count += 1 - - def _MeasureElapsedTime(self, python_command, num_invocations=1): - """Measures time (in msec) to execute a python command one or more times. - - Args: - python_command: A callable. - num_invocations: An integer number of times to invoke the given command. - - Returns: - The time required to execute the python command the specified number of - times, in milliseconds as a float. - """ - assert callable(python_command) - def RunCommand(): - for _ in range(num_invocations): - python_command() - timer = timeit.Timer(stmt=RunCommand) - return timer.timeit(number=1) * 1000 # Convert seconds to milliseconds. - - def _OutputPerfForStandaloneGraphing(self, graph_name, description, value, - units, units_x, is_stacked): - """Outputs perf measurement data to a local folder to be graphed. - - This function only applies to Chrome desktop, and assumes that environment - variable 'LOCAL_PERF_DIR' has been specified and refers to a valid directory - on the local machine. - - Args: - graph_name: A string name for the graph associated with this performance - value. - description: A string description of the performance value. Should not - include spaces. - value: Either a single numeric value representing a performance - measurement, or else a list of (x, y) tuples representing one or more - long-running performance measurements, where 'x' is an x-axis value - (such as an iteration number) and 'y' is the corresponding performance - measurement. If a list of tuples is given, then the |units_x| - argument must also be specified. - units: A string representing the units of the performance measurement(s). - Should not include spaces. - units_x: A string representing the units of the x-axis values associated - with the performance measurements, such as 'iteration' if the x values - are iteration numbers. If this argument is specified, then the - |value| argument must be a list of (x, y) tuples. - is_stacked: True to draw a "stacked" graph. First-come values are - stacked at bottom by default. - """ - revision_num_file = os.path.join(self._local_perf_dir, 'last_revision.dat') - if os.path.exists(revision_num_file): - with open(revision_num_file) as f: - revision = int(f.read()) - else: - revision = 0 - - if not self._seen_graph_lines: - # We're about to output data for a new test run. - revision += 1 - - # Update graphs.dat. - existing_graphs = [] - graphs_file = os.path.join(self._local_perf_dir, 'graphs.dat') - if os.path.exists(graphs_file): - with open(graphs_file) as f: - existing_graphs = simplejson.loads(f.read()) - is_new_graph = True - for graph in existing_graphs: - if graph['name'] == graph_name: - is_new_graph = False - break - if is_new_graph: - new_graph = { - 'name': graph_name, - 'units': units, - 'important': False, - } - if units_x: - new_graph['units_x'] = units_x - existing_graphs.append(new_graph) - with open(graphs_file, 'w') as f: - f.write(simplejson.dumps(existing_graphs)) - os.chmod(graphs_file, 0755) - - # Update data file for this particular graph. - existing_lines = [] - data_file = os.path.join(self._local_perf_dir, graph_name + '-summary.dat') - if os.path.exists(data_file): - with open(data_file) as f: - existing_lines = f.readlines() - existing_lines = map( - simplejson.loads, map(lambda x: x.strip(), existing_lines)) - - seen_key = graph_name - # We assume that the first line |existing_lines[0]| is the latest. - if units_x: - new_line = { - 'rev': revision, - 'traces': { description: [] } - } - if seen_key in self._seen_graph_lines: - # We've added points previously for this graph line in the current - # test execution, so retrieve the original set of points specified in - # the most recent revision in the data file. - new_line = existing_lines[0] - if not description in new_line['traces']: - new_line['traces'][description] = [] - for x_value, y_value in value: - new_line['traces'][description].append([str(x_value), str(y_value)]) - else: - new_line = { - 'rev': revision, - 'traces': { description: [str(value), str(0.0)] } - } - - if is_stacked: - new_line['stack'] = True - if 'stack_order' not in new_line: - new_line['stack_order'] = [] - if description not in new_line['stack_order']: - new_line['stack_order'].append(description) - - if seen_key in self._seen_graph_lines: - # Update results for the most recent revision. - existing_lines[0] = new_line - else: - # New results for a new revision. - existing_lines.insert(0, new_line) - self._seen_graph_lines[seen_key] = True - - existing_lines = map(simplejson.dumps, existing_lines) - with open(data_file, 'w') as f: - f.write('\n'.join(existing_lines)) - os.chmod(data_file, 0755) - - with open(revision_num_file, 'w') as f: - f.write(str(revision)) - - def _OutputPerfGraphValue(self, description, value, units, - graph_name, units_x=None, is_stacked=False): - """Outputs a performance value to have it graphed on the performance bots. - - The output format differs, depending on whether the current platform is - Chrome desktop or ChromeOS. - - For ChromeOS, the performance bots have a 30-character limit on the length - of the key associated with a performance value. A key on ChromeOS is - considered to be of the form "units_description" (for example, - "milliseconds_NewTabPage"), and is created from the |units| and - |description| passed as input to this function. Any characters beyond the - length 30 limit are truncated before results are stored in the autotest - database. - - Args: - description: A string description of the performance value. Should not - include spaces. - value: Either a numeric value representing a performance measurement, or - a list of values to be averaged. Lists may also contain (x, y) tuples - representing one or more performance measurements, where 'x' is an - x-axis value (such as an iteration number) and 'y' is the - corresponding performance measurement. If a list of tuples is given, - the |units_x| argument must also be specified. - units: A string representing the units of the performance measurement(s). - Should not include spaces. - graph_name: A string name for the graph associated with this performance - value. Only used on Chrome desktop. - units_x: A string representing the units of the x-axis values associated - with the performance measurements, such as 'iteration' if the x values - are iteration numbers. If this argument is specified, then the - |value| argument must be a list of (x, y) tuples. - is_stacked: True to draw a "stacked" graph. First-come values are - stacked at bottom by default. - """ - if (isinstance(value, list) and value[0] is not None and - isinstance(value[0], tuple)): - assert units_x - if units_x: - assert isinstance(value, list) - - if self.IsChromeOS(): - # Autotest doesn't support result lists. - autotest_value = value - if (isinstance(value, list) and value[0] is not None and - not isinstance(value[0], tuple)): - autotest_value = Mean(value) - - if units_x: - # TODO(dennisjeffrey): Support long-running performance measurements on - # ChromeOS in a way that can be graphed: crosbug.com/21881. - pyauto_utils.PrintPerfResult(graph_name, description, autotest_value, - units + ' ' + units_x) - else: - # Output short-running performance results in a format understood by - # autotest. - perf_key = '%s_%s' % (units, description) - if len(perf_key) > 30: - logging.warning('The description "%s" will be truncated to "%s" ' - '(length 30) when added to the autotest database.', - perf_key, perf_key[:30]) - print '\n%s(\'%s\', %f)%s' % (self._PERF_OUTPUT_MARKER_PRE, - perf_key, autotest_value, - self._PERF_OUTPUT_MARKER_POST) - - # Also output results in the format recognized by buildbot, for cases - # in which these tests are run on chromeOS through buildbot. Since - # buildbot supports result lists, it's ok for |value| to be a list here. - pyauto_utils.PrintPerfResult(graph_name, description, value, units) - - sys.stdout.flush() - else: - # TODO(dmikurube): Support stacked graphs in PrintPerfResult. - # See http://crbug.com/122119. - if units_x: - pyauto_utils.PrintPerfResult(graph_name, description, value, - units + ' ' + units_x) - else: - pyauto_utils.PrintPerfResult(graph_name, description, value, units) - - if self._local_perf_dir: - self._OutputPerfForStandaloneGraphing( - graph_name, description, value, units, units_x, is_stacked) - - def _OutputEventForStandaloneGraphing(self, description, event_list): - """Outputs event information to a local folder to be graphed. - - See function _OutputEventGraphValue below for a description of an event. - - This function only applies to Chrome Endure tests running on Chrome desktop, - and assumes that environment variable 'LOCAL_PERF_DIR' has been specified - and refers to a valid directory on the local machine. - - Args: - description: A string description of the event. Should not include - spaces. - event_list: A list of (x, y) tuples representing one or more events - occurring during an endurance test, where 'x' is the time of the event - (in seconds since the start of the test), and 'y' is a dictionary - representing relevant data associated with that event (as key/value - pairs). - """ - revision_num_file = os.path.join(self._local_perf_dir, 'last_revision.dat') - if os.path.exists(revision_num_file): - with open(revision_num_file) as f: - revision = int(f.read()) - else: - revision = 0 - - if not self._seen_graph_lines: - # We're about to output data for a new test run. - revision += 1 - - existing_lines = [] - data_file = os.path.join(self._local_perf_dir, '_EVENT_-summary.dat') - if os.path.exists(data_file): - with open(data_file) as f: - existing_lines = f.readlines() - existing_lines = map(eval, map(lambda x: x.strip(), existing_lines)) - - seen_event_type = description - value_list = [] - if seen_event_type in self._seen_graph_lines: - # We've added events previously for this event type in the current - # test execution, so retrieve the original set of values specified in - # the most recent revision in the data file. - value_list = existing_lines[0]['events'][description] - for event_time, event_data in event_list: - value_list.append([str(event_time), event_data]) - new_events = { - description: value_list - } - - new_line = { - 'rev': revision, - 'events': new_events - } - - if seen_event_type in self._seen_graph_lines: - # Update results for the most recent revision. - existing_lines[0] = new_line - else: - # New results for a new revision. - existing_lines.insert(0, new_line) - self._seen_graph_lines[seen_event_type] = True - - existing_lines = map(str, existing_lines) - with open(data_file, 'w') as f: - f.write('\n'.join(existing_lines)) - os.chmod(data_file, 0755) - - with open(revision_num_file, 'w') as f: - f.write(str(revision)) - - def _OutputEventGraphValue(self, description, event_list): - """Outputs a set of events to have them graphed on the Chrome Endure bots. - - An "event" can be anything recorded by a performance test that occurs at - particular times during a test execution. For example, a garbage collection - in the v8 heap can be considered an event. An event is distinguished from a - regular perf measurement in two ways: (1) an event is depicted differently - in the performance graphs than performance measurements; (2) an event can - be associated with zero or more data fields describing relevant information - associated with the event. For example, a garbage collection event will - occur at a particular time, and it may be associated with data such as - the number of collected bytes and/or the length of time it took to perform - the garbage collection. - - This function only applies to Chrome Endure tests running on Chrome desktop. - - Args: - description: A string description of the event. Should not include - spaces. - event_list: A list of (x, y) tuples representing one or more events - occurring during an endurance test, where 'x' is the time of the event - (in seconds since the start of the test), and 'y' is a dictionary - representing relevant data associated with that event (as key/value - pairs). - """ - pyauto_utils.PrintPerfResult('_EVENT_', description, event_list, '') - if self._local_perf_dir: - self._OutputEventForStandaloneGraphing(description, event_list) - - def _PrintSummaryResults(self, description, values, units, graph_name): - """Logs summary measurement information. - - This function computes and outputs the average and standard deviation of - the specified list of value measurements. It also invokes - _OutputPerfGraphValue() with the computed *average* value, to ensure the - average value can be plotted in a performance graph. - - Args: - description: A string description for the specified results. - values: A list of numeric value measurements. - units: A string specifying the units for the specified measurements. - graph_name: A string name for the graph associated with this performance - value. Only used on Chrome desktop. - """ - logging.info('Overall results for: %s', description) - if values: - logging.info(' Average: %f %s', Mean(values), units) - logging.info(' Std dev: %f %s', StandardDeviation(values), units) - self._OutputPerfGraphValue(description, values, units, graph_name) - else: - logging.info('No results to report.') - - def _RunNewTabTest(self, description, open_tab_command, graph_name, - num_tabs=1): - """Runs a perf test that involves opening new tab(s). - - This helper function can be called from different tests to do perf testing - with different types of tabs. It is assumed that the |open_tab_command| - will open up a single tab. - - Args: - description: A string description of the associated tab test. - open_tab_command: A callable that will open a single tab. - graph_name: A string name for the performance graph associated with this - test. Only used on Chrome desktop. - num_tabs: The number of tabs to open, i.e., the number of times to invoke - the |open_tab_command|. - """ - assert callable(open_tab_command) - - timings = [] - for iteration in range(self._num_iterations + 1): - orig_timeout_count = self._timeout_count - elapsed_time = self._MeasureElapsedTime(open_tab_command, - num_invocations=num_tabs) - # Only count the timing measurement if no automation call timed out. - if self._timeout_count == orig_timeout_count: - # Ignore the first iteration. - if iteration: - timings.append(elapsed_time) - logging.info('Iteration %d of %d: %f milliseconds', iteration, - self._num_iterations, elapsed_time) - self.assertTrue(self._timeout_count <= self._max_timeout_count, - msg='Test exceeded automation timeout threshold.') - self.assertEqual(1 + num_tabs, self.GetTabCount(), - msg='Did not open %d new tab(s).' % num_tabs) - for _ in range(num_tabs): - self.CloseTab(tab_index=1) - - self._PrintSummaryResults(description, timings, 'milliseconds', graph_name) - - def _GetConfig(self): - """Load perf test configuration file. - - Returns: - A dictionary that represents the config information. - """ - config_file = os.path.join(os.path.dirname(__file__), 'perf.cfg') - config = {'username': None, - 'password': None, - 'google_account_url': 'https://accounts.google.com/', - 'gmail_url': 'https://www.gmail.com', - 'plus_url': 'https://plus.google.com', - 'docs_url': 'https://docs.google.com'} - if os.path.exists(config_file): - try: - new_config = pyauto.PyUITest.EvalDataFrom(config_file) - for key in new_config: - if new_config.get(key) is not None: - config[key] = new_config.get(key) - except SyntaxError, e: - logging.info('Could not read %s: %s', config_file, str(e)) - return config - - def _LoginToGoogleAccount(self, account_key='test_google_account'): - """Logs in to a test Google account. - - Login with user-defined credentials if they exist. - Else login with private test credentials if they exist. - Else fail. - - Args: - account_key: The string key in private_tests_info.txt which is associated - with the test account login credentials to use. It will only - be used when fail to load user-defined credentials. - - Raises: - RuntimeError: if could not get credential information. - """ - private_file = os.path.join(pyauto.PyUITest.DataDir(), 'pyauto_private', - 'private_tests_info.txt') - config_file = os.path.join(os.path.dirname(__file__), 'perf.cfg') - config = self._GetConfig() - google_account_url = config.get('google_account_url') - username = config.get('username') - password = config.get('password') - if username and password: - logging.info( - 'Using google account credential from %s', - os.path.join(os.path.dirname(__file__), 'perf.cfg')) - elif os.path.exists(private_file): - creds = self.GetPrivateInfo()[account_key] - username = creds['username'] - password = creds['password'] - logging.info( - 'User-defined credentials not found,' + - ' using private test credentials instead.') - else: - message = 'No user-defined or private test ' \ - 'credentials could be found. ' \ - 'Please specify credential information in %s.' \ - % config_file - raise RuntimeError(message) - test_utils.GoogleAccountsLogin( - self, username, password, url=google_account_url) - self.NavigateToURL('about:blank') # Clear the existing tab. - - def _GetCPUUsage(self): - """Returns machine's CPU usage. - - This function uses /proc/stat to identify CPU usage, and therefore works - only on Linux/ChromeOS. - - Returns: - A dictionary with 'user', 'nice', 'system' and 'idle' values. - Sample dictionary: - { - 'user': 254544, - 'nice': 9, - 'system': 254768, - 'idle': 2859878, - } - """ - try: - f = open('/proc/stat') - cpu_usage_str = f.readline().split() - f.close() - except IOError, e: - self.fail('Could not retrieve CPU usage: ' + str(e)) - return { - 'user': int(cpu_usage_str[1]), - 'nice': int(cpu_usage_str[2]), - 'system': int(cpu_usage_str[3]), - 'idle': int(cpu_usage_str[4]) - } - - def _GetFractionNonIdleCPUTime(self, cpu_usage_start, cpu_usage_end): - """Computes the fraction of CPU time spent non-idling. - - This function should be invoked using before/after values from calls to - _GetCPUUsage(). - """ - time_non_idling_end = (cpu_usage_end['user'] + cpu_usage_end['nice'] + - cpu_usage_end['system']) - time_non_idling_start = (cpu_usage_start['user'] + cpu_usage_start['nice'] + - cpu_usage_start['system']) - total_time_end = (cpu_usage_end['user'] + cpu_usage_end['nice'] + - cpu_usage_end['system'] + cpu_usage_end['idle']) - total_time_start = (cpu_usage_start['user'] + cpu_usage_start['nice'] + - cpu_usage_start['system'] + cpu_usage_start['idle']) - return ((float(time_non_idling_end) - time_non_idling_start) / - (total_time_end - total_time_start)) - - def ExtraChromeFlags(self): - """Ensures Chrome is launched with custom flags. - - Returns: - A list of extra flags to pass to Chrome when it is launched. - """ - flags = super(BasePerfTest, self).ExtraChromeFlags() - # Window size impacts a variety of perf tests, ensure consistency. - flags.append('--window-size=1024,768') - if self._IsPGOMode(): - flags = flags + ['--no-sandbox'] - return flags - - -class TabPerfTest(BasePerfTest): - """Tests that involve opening tabs.""" - - def testNewTab(self): - """Measures time to open a new tab.""" - self._RunNewTabTest('NewTabPage', - lambda: self._AppendTab('chrome://newtab'), 'open_tab') - - def testNewTabFlash(self): - """Measures time to open a new tab navigated to a flash page.""" - self.assertTrue( - os.path.exists(os.path.join(self.ContentDataDir(), 'plugin', - 'flash.swf')), - msg='Missing required flash data file.') - url = self.GetFileURLForContentDataPath('plugin', 'flash.swf') - self._RunNewTabTest('NewTabFlashPage', lambda: self._AppendTab(url), - 'open_tab') - - def test20Tabs(self): - """Measures time to open 20 tabs.""" - self._RunNewTabTest('20TabsNewTabPage', - lambda: self._AppendTab('chrome://newtab'), - 'open_20_tabs', num_tabs=20) - - -class BenchmarkPerfTest(BasePerfTest): - """Benchmark performance tests.""" - - def testV8BenchmarkSuite(self): - """Measures score from v8 benchmark suite.""" - url = self.GetFileURLForDataPath('v8_benchmark_v6', 'run.html') - - def _RunBenchmarkOnce(url): - """Runs the v8 benchmark suite once and returns the results in a dict.""" - self.assertTrue(self.AppendTab(pyauto.GURL(url)), - msg='Failed to append tab for v8 benchmark suite.') - js_done = """ - var val = document.getElementById("status").innerHTML; - window.domAutomationController.send(val); - """ - self.assertTrue( - self.WaitUntil( - lambda: 'Score:' in self.ExecuteJavascript(js_done, tab_index=1), - timeout=300, expect_retval=True, retry_sleep=1), - msg='Timed out when waiting for v8 benchmark score.') - - js_get_results = """ - var result = {}; - result['final_score'] = document.getElementById("status").innerHTML; - result['all_results'] = document.getElementById("results").innerHTML; - window.domAutomationController.send(JSON.stringify(result)); - """ - results = eval(self.ExecuteJavascript(js_get_results, tab_index=1)) - score_pattern = '(\w+): (\d+)' - final_score = re.search(score_pattern, results['final_score']).group(2) - result_dict = {'final_score': int(final_score)} - for match in re.finditer(score_pattern, results['all_results']): - benchmark_name = match.group(1) - benchmark_score = match.group(2) - result_dict[benchmark_name] = int(benchmark_score) - self.CloseTab(tab_index=1) - return result_dict - - timings = {} - for iteration in xrange(self._num_iterations + 1): - result_dict = _RunBenchmarkOnce(url) - # Ignore the first iteration. - if iteration: - for key, val in result_dict.items(): - timings.setdefault(key, []).append(val) - logging.info('Iteration %d of %d:\n%s', iteration, - self._num_iterations, self.pformat(result_dict)) - - for key, val in timings.items(): - if key == 'final_score': - self._PrintSummaryResults('V8Benchmark', val, 'score', - 'v8_benchmark_final') - else: - self._PrintSummaryResults('V8Benchmark-%s' % key, val, 'score', - 'v8_benchmark_individual') - - def testSunSpider(self): - """Runs the SunSpider javascript benchmark suite.""" - url = self.GetFileURLForDataPath('sunspider', 'sunspider-driver.html') - self.assertTrue(self.AppendTab(pyauto.GURL(url)), - msg='Failed to append tab for SunSpider benchmark suite.') - - js_is_done = """ - var done = false; - if (document.getElementById("console")) - done = true; - window.domAutomationController.send(JSON.stringify(done)); - """ - self.assertTrue( - self.WaitUntil( - lambda: self.ExecuteJavascript(js_is_done, tab_index=1), - timeout=300, expect_retval='true', retry_sleep=1), - msg='Timed out when waiting for SunSpider benchmark score.') - - js_get_results = """ - window.domAutomationController.send( - document.getElementById("console").innerHTML); - """ - # Append '<br>' to the result to simplify regular expression matching. - results = self.ExecuteJavascript(js_get_results, tab_index=1) + '<br>' - total = re.search('Total:\s*([\d.]+)ms', results).group(1) - logging.info('Total: %f ms', float(total)) - self._OutputPerfGraphValue('SunSpider-total', float(total), 'ms', - 'sunspider_total') - - for match_category in re.finditer('\s\s(\w+):\s*([\d.]+)ms.+?<br><br>', - results): - category_name = match_category.group(1) - category_result = match_category.group(2) - logging.info('Benchmark "%s": %f ms', category_name, - float(category_result)) - self._OutputPerfGraphValue('SunSpider-' + category_name, - float(category_result), 'ms', - 'sunspider_individual') - - for match_result in re.finditer('<br>\s\s\s\s([\w-]+):\s*([\d.]+)ms', - match_category.group(0)): - result_name = match_result.group(1) - result_value = match_result.group(2) - logging.info(' Result "%s-%s": %f ms', category_name, result_name, - float(result_value)) - self._OutputPerfGraphValue( - 'SunSpider-%s-%s' % (category_name, result_name), - float(result_value), 'ms', 'sunspider_individual') - - def testDromaeoSuite(self): - """Measures results from Dromaeo benchmark suite.""" - url = self.GetFileURLForDataPath('dromaeo', 'index.html') - self.assertTrue(self.AppendTab(pyauto.GURL(url + '?dromaeo')), - msg='Failed to append tab for Dromaeo benchmark suite.') - - js_is_ready = """ - var val = document.getElementById('pause').value; - window.domAutomationController.send(val); - """ - self.assertTrue( - self.WaitUntil( - lambda: self.ExecuteJavascript(js_is_ready, tab_index=1), - timeout=30, expect_retval='Run', retry_sleep=1), - msg='Timed out when waiting for Dromaeo benchmark to load.') - - js_run = """ - $('#pause').val('Run').click(); - window.domAutomationController.send('done'); - """ - self.ExecuteJavascript(js_run, tab_index=1) - - js_is_done = """ - var val = document.getElementById('timebar').innerHTML; - window.domAutomationController.send(val); - """ - self.assertTrue( - self.WaitUntil( - lambda: 'Total' in self.ExecuteJavascript(js_is_done, tab_index=1), - timeout=900, expect_retval=True, retry_sleep=2), - msg='Timed out when waiting for Dromaeo benchmark to complete.') - - js_get_results = """ - var result = {}; - result['total_result'] = $('#timebar strong').html(); - result['all_results'] = {}; - $('.result-item.done').each(function (i) { - var group_name = $(this).find('.test b').html().replace(':', ''); - var group_results = {}; - group_results['result'] = - $(this).find('span').html().replace('runs/s', '') - - group_results['sub_groups'] = {} - $(this).find('li').each(function (i) { - var sub_name = $(this).find('b').html().replace(':', ''); - group_results['sub_groups'][sub_name] = - $(this).text().match(/: ([\d.]+)/)[1] - }); - result['all_results'][group_name] = group_results; - }); - window.domAutomationController.send(JSON.stringify(result)); - """ - results = eval(self.ExecuteJavascript(js_get_results, tab_index=1)) - total_result = results['total_result'] - logging.info('Total result: ' + total_result) - self._OutputPerfGraphValue('Dromaeo-total', float(total_result), - 'runsPerSec', 'dromaeo_total') - - for group_name, group in results['all_results'].iteritems(): - logging.info('Benchmark "%s": %s', group_name, group['result']) - self._OutputPerfGraphValue('Dromaeo-' + group_name.replace(' ', ''), - float(group['result']), 'runsPerSec', - 'dromaeo_individual') - for benchmark_name, benchmark_score in group['sub_groups'].iteritems(): - logging.info(' Result "%s": %s', benchmark_name, benchmark_score) - - def testSpaceport(self): - """Measures results from Spaceport benchmark suite.""" - # TODO(tonyg): Test is failing on bots. Diagnose and re-enable. - pass - -# url = self.GetFileURLForDataPath('third_party', 'spaceport', 'index.html') -# self.assertTrue(self.AppendTab(pyauto.GURL(url + '?auto')), -# msg='Failed to append tab for Spaceport benchmark suite.') -# -# # The test reports results to console.log in the format "name: value". -# # Inject a bit of JS to intercept those. -# js_collect_console_log = """ -# window.__pyautoresult = {}; -# window.console.log = function(str) { -# if (!str) return; -# var key_val = str.split(': '); -# if (!key_val.length == 2) return; -# __pyautoresult[key_val[0]] = key_val[1]; -# }; -# window.domAutomationController.send('done'); -# """ -# self.ExecuteJavascript(js_collect_console_log, tab_index=1) -# -# def _IsDone(): -# expected_num_results = 30 # The number of tests in benchmark. -# results = eval(self.ExecuteJavascript(js_get_results, tab_index=1)) -# return expected_num_results == len(results) -# -# js_get_results = """ -# window.domAutomationController.send( -# JSON.stringify(window.__pyautoresult)); -# """ -# self.assertTrue( -# self.WaitUntil(_IsDone, timeout=1200, expect_retval=True, -# retry_sleep=5), -# msg='Timed out when waiting for Spaceport benchmark to complete.') -# results = eval(self.ExecuteJavascript(js_get_results, tab_index=1)) -# -# for key in results: -# suite, test = key.split('.') -# value = float(results[key]) -# self._OutputPerfGraphValue(test, value, 'ObjectsAt30FPS', suite) -# self._PrintSummaryResults('Overall', [float(x) for x in results.values()], -# 'ObjectsAt30FPS', 'Overall') - - -class LiveWebappLoadTest(BasePerfTest): - """Tests that involve performance measurements of live webapps. - - These tests connect to live webpages (e.g., Gmail, Calendar, Docs) and are - therefore subject to network conditions. These tests are meant to generate - "ball-park" numbers only (to see roughly how long things take to occur from a - user's perspective), and are not expected to be precise. - """ - - def testNewTabGmail(self): - """Measures time to open a tab to a logged-in Gmail account. - - Timing starts right before the new tab is opened, and stops as soon as the - webpage displays the substring 'Last account activity:'. - """ - EXPECTED_SUBSTRING = 'Last account activity:' - - def _SubstringExistsOnPage(): - js = """ - var frame = document.getElementById("canvas_frame"); - var divs = frame.contentDocument.getElementsByTagName("div"); - for (var i = 0; i < divs.length; ++i) { - if (divs[i].innerHTML.indexOf("%s") >= 0) - window.domAutomationController.send("true"); - } - window.domAutomationController.send("false"); - """ % EXPECTED_SUBSTRING - return self.ExecuteJavascript(js, tab_index=1) - - def _RunSingleGmailTabOpen(): - self._AppendTab('http://www.gmail.com') - self.assertTrue(self.WaitUntil(_SubstringExistsOnPage, timeout=120, - expect_retval='true', retry_sleep=0.10), - msg='Timed out waiting for expected Gmail string.') - - self._LoginToGoogleAccount() - self._RunNewTabTest('NewTabGmail', _RunSingleGmailTabOpen, - 'open_tab_live_webapp') - - def testNewTabCalendar(self): - """Measures time to open a tab to a logged-in Calendar account. - - Timing starts right before the new tab is opened, and stops as soon as the - webpage displays the calendar print button (title 'Print my calendar'). - """ - EXPECTED_SUBSTRING = 'Month' - - def _DivTitleStartsWith(): - js = """ - var divs = document.getElementsByTagName("div"); - for (var i = 0; i < divs.length; ++i) { - if (divs[i].innerHTML == "%s") - window.domAutomationController.send("true"); - } - window.domAutomationController.send("false"); - """ % EXPECTED_SUBSTRING - return self.ExecuteJavascript(js, tab_index=1) - - def _RunSingleCalendarTabOpen(): - self._AppendTab('http://calendar.google.com') - self.assertTrue(self.WaitUntil(_DivTitleStartsWith, timeout=120, - expect_retval='true', retry_sleep=0.10), - msg='Timed out waiting for expected Calendar string.') - - self._LoginToGoogleAccount() - self._RunNewTabTest('NewTabCalendar', _RunSingleCalendarTabOpen, - 'open_tab_live_webapp') - - def testNewTabDocs(self): - """Measures time to open a tab to a logged-in Docs account. - - Timing starts right before the new tab is opened, and stops as soon as the - webpage displays the expected substring 'last modified' (case insensitive). - """ - EXPECTED_SUBSTRING = 'sort' - - def _SubstringExistsOnPage(): - js = """ - var divs = document.getElementsByTagName("div"); - for (var i = 0; i < divs.length; ++i) { - if (divs[i].innerHTML.toLowerCase().indexOf("%s") >= 0) - window.domAutomationController.send("true"); - } - window.domAutomationController.send("false"); - """ % EXPECTED_SUBSTRING - return self.ExecuteJavascript(js, tab_index=1) - - def _RunSingleDocsTabOpen(): - self._AppendTab('http://docs.google.com') - self.assertTrue(self.WaitUntil(_SubstringExistsOnPage, timeout=120, - expect_retval='true', retry_sleep=0.10), - msg='Timed out waiting for expected Docs string.') - - self._LoginToGoogleAccount() - self._RunNewTabTest('NewTabDocs', _RunSingleDocsTabOpen, - 'open_tab_live_webapp') - - -class NetflixPerfTest(BasePerfTest, NetflixTestHelper): - """Test Netflix video performance.""" - - def __init__(self, methodName='runTest', **kwargs): - pyauto.PyUITest.__init__(self, methodName, **kwargs) - NetflixTestHelper.__init__(self, self) - - def tearDown(self): - self.SignOut() - pyauto.PyUITest.tearDown(self) - - def testNetflixDroppedFrames(self): - """Measures the Netflix video dropped frames/second. Runs for 60 secs.""" - self.LoginAndStartPlaying() - self.CheckNetflixPlaying(self.IS_PLAYING, - 'Player did not start playing the title.') - # Ignore first 10 seconds of video playing so we get smooth videoplayback. - time.sleep(10) - init_dropped_frames = self._GetVideoDroppedFrames() - dropped_frames = [] - prev_dropped_frames = 0 - for iteration in xrange(60): - # Ignoring initial dropped frames of first 10 seconds. - total_dropped_frames = self._GetVideoDroppedFrames() - init_dropped_frames - dropped_frames_last_sec = total_dropped_frames - prev_dropped_frames - dropped_frames.append(dropped_frames_last_sec) - logging.info('Iteration %d of %d: %f dropped frames in the last second', - iteration + 1, 60, dropped_frames_last_sec) - prev_dropped_frames = total_dropped_frames - # Play the video for some time. - time.sleep(1) - self._PrintSummaryResults('NetflixDroppedFrames', dropped_frames, 'frames', - 'netflix_dropped_frames') - - def testNetflixCPU(self): - """Measures the Netflix video CPU usage. Runs for 60 seconds.""" - self.LoginAndStartPlaying() - self.CheckNetflixPlaying(self.IS_PLAYING, - 'Player did not start playing the title.') - # Ignore first 10 seconds of video playing so we get smooth videoplayback. - time.sleep(10) - init_dropped_frames = self._GetVideoDroppedFrames() - init_video_frames = self._GetVideoFrames() - cpu_usage_start = self._GetCPUUsage() - total_shown_frames = 0 - # Play the video for some time. - time.sleep(60) - total_video_frames = self._GetVideoFrames() - init_video_frames - total_dropped_frames = self._GetVideoDroppedFrames() - init_dropped_frames - cpu_usage_end = self._GetCPUUsage() - fraction_non_idle_time = \ - self._GetFractionNonIdleCPUTime(cpu_usage_start, cpu_usage_end) - # Counting extrapolation for utilization to play the video. - extrapolation_value = fraction_non_idle_time * \ - (float(total_video_frames) + total_dropped_frames) / total_video_frames - logging.info('Netflix CPU extrapolation: %f', extrapolation_value) - self._OutputPerfGraphValue('NetflixCPUExtrapolation', extrapolation_value, - 'extrapolation', 'netflix_cpu_extrapolation') - - -class YoutubePerfTest(BasePerfTest, YoutubeTestHelper): - """Test Youtube video performance.""" - - def __init__(self, methodName='runTest', **kwargs): - pyauto.PyUITest.__init__(self, methodName, **kwargs) - YoutubeTestHelper.__init__(self, self) - - def _VerifyVideoTotalBytes(self): - """Returns true if video total bytes information is available.""" - return self.GetVideoTotalBytes() > 0 - - def _VerifyVideoLoadedBytes(self): - """Returns true if video loaded bytes information is available.""" - return self.GetVideoLoadedBytes() > 0 - - def StartVideoForPerformance(self, video_id='zuzaxlddWbk'): - """Start the test video with all required buffering.""" - self.PlayVideoAndAssert(video_id) - self.ExecuteJavascript(""" - ytplayer.setPlaybackQuality('hd720'); - window.domAutomationController.send(''); - """) - self.AssertPlayerState(state=self.is_playing, - msg='Player did not enter the playing state') - self.assertTrue( - self.WaitUntil(self._VerifyVideoTotalBytes, expect_retval=True), - msg='Failed to get video total bytes information.') - self.assertTrue( - self.WaitUntil(self._VerifyVideoLoadedBytes, expect_retval=True), - msg='Failed to get video loaded bytes information') - loaded_video_bytes = self.GetVideoLoadedBytes() - total_video_bytes = self.GetVideoTotalBytes() - self.PauseVideo() - logging.info('total_video_bytes: %f', total_video_bytes) - # Wait for the video to finish loading. - while total_video_bytes > loaded_video_bytes: - loaded_video_bytes = self.GetVideoLoadedBytes() - logging.info('loaded_video_bytes: %f', loaded_video_bytes) - time.sleep(1) - self.PlayVideo() - # Ignore first 10 seconds of video playing so we get smooth videoplayback. - time.sleep(10) - - def testYoutubeDroppedFrames(self): - """Measures the Youtube video dropped frames/second. Runs for 60 secs. - - This test measures Youtube video dropped frames for three different types - of videos like slow, normal and fast motion. - """ - youtube_video = {'Slow': 'VT1-sitWRtY', - 'Normal': '2tqK_3mKQUw', - 'Fast': '8ETDE0VGJY4', - } - for video_type in youtube_video: - logging.info('Running %s video.', video_type) - self.StartVideoForPerformance(youtube_video[video_type]) - init_dropped_frames = self.GetVideoDroppedFrames() - total_dropped_frames = 0 - dropped_fps = [] - for iteration in xrange(60): - frames = self.GetVideoDroppedFrames() - init_dropped_frames - current_dropped_frames = frames - total_dropped_frames - dropped_fps.append(current_dropped_frames) - logging.info('Iteration %d of %d: %f dropped frames in the last ' - 'second', iteration + 1, 60, current_dropped_frames) - total_dropped_frames = frames - # Play the video for some time - time.sleep(1) - graph_description = 'YoutubeDroppedFrames' + video_type - self._PrintSummaryResults(graph_description, dropped_fps, 'frames', - 'youtube_dropped_frames') - - def testYoutubeCPU(self): - """Measures the Youtube video CPU usage. Runs for 60 seconds. - - Measures the Youtube video CPU usage (between 0 and 1), extrapolated to - totalframes in the video by taking dropped frames into account. For smooth - videoplayback this number should be < 0.5..1.0 on a hyperthreaded CPU. - """ - self.StartVideoForPerformance() - init_dropped_frames = self.GetVideoDroppedFrames() - logging.info('init_dropped_frames: %f', init_dropped_frames) - cpu_usage_start = self._GetCPUUsage() - total_shown_frames = 0 - for sec_num in xrange(60): - # Play the video for some time. - time.sleep(1) - total_shown_frames = total_shown_frames + self.GetVideoFrames() - logging.info('total_shown_frames: %f', total_shown_frames) - total_dropped_frames = self.GetVideoDroppedFrames() - init_dropped_frames - logging.info('total_dropped_frames: %f', total_dropped_frames) - cpu_usage_end = self._GetCPUUsage() - fraction_non_idle_time = self._GetFractionNonIdleCPUTime( - cpu_usage_start, cpu_usage_end) - logging.info('fraction_non_idle_time: %f', fraction_non_idle_time) - total_frames = total_shown_frames + total_dropped_frames - # Counting extrapolation for utilization to play the video. - extrapolation_value = (fraction_non_idle_time * - (float(total_frames) / total_shown_frames)) - logging.info('Youtube CPU extrapolation: %f', extrapolation_value) - # Video is still running so log some more detailed data. - self._LogProcessActivity() - self._OutputPerfGraphValue('YoutubeCPUExtrapolation', extrapolation_value, - 'extrapolation', 'youtube_cpu_extrapolation') - - -class FlashVideoPerfTest(BasePerfTest): - """General flash video performance tests.""" - - def FlashVideo1080P(self): - """Measures total dropped frames and average FPS for a 1080p flash video. - - This is a temporary test to be run manually for now, needed to collect some - performance statistics across different ChromeOS devices. - """ - # Open up the test webpage; it's assumed the test will start automatically. - webpage_url = 'http://www/~arscott/fl/FlashVideoTests.html' - self.assertTrue(self.AppendTab(pyauto.GURL(webpage_url)), - msg='Failed to append tab for webpage.') - - # Wait until the test is complete. - js_is_done = """ - window.domAutomationController.send(JSON.stringify(tests_done)); - """ - self.assertTrue( - self.WaitUntil( - lambda: self.ExecuteJavascript(js_is_done, tab_index=1) == 'true', - timeout=300, expect_retval=True, retry_sleep=1), - msg='Timed out when waiting for test result.') - - # Retrieve and output the test results. - js_results = """ - window.domAutomationController.send(JSON.stringify(tests_results)); - """ - test_result = eval(self.ExecuteJavascript(js_results, tab_index=1)) - test_result[0] = test_result[0].replace('true', 'True') - test_result = eval(test_result[0]) # Webpage only does 1 test right now. - - description = 'FlashVideo1080P' - result = test_result['averageFPS'] - logging.info('Result for %s: %f FPS (average)', description, result) - self._OutputPerfGraphValue(description, result, 'FPS', - 'flash_video_1080p_fps') - result = test_result['droppedFrames'] - logging.info('Result for %s: %f dropped frames', description, result) - self._OutputPerfGraphValue(description, result, 'DroppedFrames', - 'flash_video_1080p_dropped_frames') - - -class WebGLTest(BasePerfTest): - """Tests for WebGL performance.""" - - def _RunWebGLTest(self, url, description, graph_name): - """Measures FPS using a specified WebGL demo. - - Args: - url: The string URL that, once loaded, will run the WebGL demo (default - WebGL demo settings are used, since this test does not modify any - settings in the demo). - description: A string description for this demo, used as a performance - value description. Should not contain any spaces. - graph_name: A string name for the performance graph associated with this - test. Only used on Chrome desktop. - """ - self.assertTrue(self.AppendTab(pyauto.GURL(url)), - msg='Failed to append tab for %s.' % description) - - get_fps_js = """ - var fps_field = document.getElementById("fps"); - var result = -1; - if (fps_field) - result = fps_field.innerHTML; - window.domAutomationController.send(JSON.stringify(result)); - """ - - # Wait until we start getting FPS values. - self.assertTrue( - self.WaitUntil( - lambda: self.ExecuteJavascript(get_fps_js, tab_index=1) != '-1', - timeout=300, retry_sleep=1), - msg='Timed out when waiting for FPS values to be available.') - - # Let the experiment run for 5 seconds before we start collecting perf - # measurements. - time.sleep(5) - - # Collect the current FPS value each second for the next 30 seconds. The - # final result of this test will be the average of these FPS values. - fps_vals = [] - for iteration in xrange(30): - fps = self.ExecuteJavascript(get_fps_js, tab_index=1) - fps = float(fps.replace('"', '')) - fps_vals.append(fps) - logging.info('Iteration %d of %d: %f FPS', iteration + 1, 30, fps) - time.sleep(1) - self._PrintSummaryResults(description, fps_vals, 'fps', graph_name) - - def testWebGLAquarium(self): - """Measures performance using the WebGL Aquarium demo.""" - self._RunWebGLTest( - self.GetFileURLForDataPath('pyauto_private', 'webgl', 'aquarium', - 'aquarium.html'), - 'WebGLAquarium', 'webgl_demo') - - def testWebGLField(self): - """Measures performance using the WebGL Field demo.""" - self._RunWebGLTest( - self.GetFileURLForDataPath('pyauto_private', 'webgl', 'field', - 'field.html'), - 'WebGLField', 'webgl_demo') - - def testWebGLSpaceRocks(self): - """Measures performance using the WebGL SpaceRocks demo.""" - self._RunWebGLTest( - self.GetFileURLForDataPath('pyauto_private', 'webgl', 'spacerocks', - 'spacerocks.html'), - 'WebGLSpaceRocks', 'webgl_demo') - - -class GPUPerfTest(BasePerfTest): - """Tests for GPU performance.""" - - def setUp(self): - """Performs necessary setup work before running each test in this class.""" - self._gpu_info_dict = self.EvalDataFrom(os.path.join(self.DataDir(), - 'gpu', 'gpuperf.txt')) - self._demo_name_url_dict = self._gpu_info_dict['demo_info'] - pyauto.PyUITest.setUp(self) - - def _MeasureFpsOverTime(self, tab_index=0): - """Measures FPS using a specified demo. - - This function assumes that the demo is already loaded in the specified tab - index. - - Args: - tab_index: The tab index, default is 0. - """ - # Let the experiment run for 5 seconds before we start collecting FPS - # values. - time.sleep(5) - - # Collect the current FPS value each second for the next 10 seconds. - # Then return the average FPS value from among those collected. - fps_vals = [] - for iteration in xrange(10): - fps = self.GetFPS(tab_index=tab_index) - fps_vals.append(fps['fps']) - time.sleep(1) - return Mean(fps_vals) - - def _GetStdAvgAndCompare(self, avg_fps, description, ref_dict): - """Computes the average and compare set of values with reference data. - - Args: - avg_fps: Average fps value. - description: A string description for this demo, used as a performance - value description. - ref_dict: Dictionary which contains reference data for this test case. - - Returns: - True, if the actual FPS value is within 10% of the reference FPS value, - or False, otherwise. - """ - std_fps = 0 - status = True - # Load reference data according to platform. - platform_ref_dict = None - if self.IsWin(): - platform_ref_dict = ref_dict['win'] - elif self.IsMac(): - platform_ref_dict = ref_dict['mac'] - elif self.IsLinux(): - platform_ref_dict = ref_dict['linux'] - else: - self.assertFail(msg='This platform is unsupported.') - std_fps = platform_ref_dict[description] - # Compare reference data to average fps. - # We allow the average FPS value to be within 10% of the reference - # FPS value. - if avg_fps < (0.9 * std_fps): - logging.info('FPS difference exceeds threshold for: %s', description) - logging.info(' Average: %f fps', avg_fps) - logging.info('Reference Average: %f fps', std_fps) - status = False - else: - logging.info('Average FPS is actually greater than 10 percent ' - 'more than the reference FPS for: %s', description) - logging.info(' Average: %f fps', avg_fps) - logging.info(' Reference Average: %f fps', std_fps) - return status - - def testLaunchDemosParallelInSeparateTabs(self): - """Measures performance of demos in different tabs in same browser.""" - # Launch all the demos parallel in separate tabs - counter = 0 - all_demos_passed = True - ref_dict = self._gpu_info_dict['separate_tab_ref_data'] - # Iterate through dictionary and append all url to browser - for url in self._demo_name_url_dict.iterkeys(): - self.assertTrue( - self.AppendTab(pyauto.GURL(self._demo_name_url_dict[url])), - msg='Failed to append tab for %s.' % url) - counter += 1 - # Assert number of tab count is equal to number of tabs appended. - self.assertEqual(self.GetTabCount(), counter + 1) - # Measures performance using different demos and compare it golden - # reference. - for url in self._demo_name_url_dict.iterkeys(): - avg_fps = self._MeasureFpsOverTime(tab_index=counter) - # Get the reference value of fps and compare the results - if not self._GetStdAvgAndCompare(avg_fps, url, ref_dict): - all_demos_passed = False - counter -= 1 - self.assertTrue( - all_demos_passed, - msg='One or more demos failed to yield an acceptable FPS value') - - def testLaunchDemosInSeparateBrowser(self): - """Measures performance by launching each demo in a separate tab.""" - # Launch demos in the browser - ref_dict = self._gpu_info_dict['separate_browser_ref_data'] - all_demos_passed = True - for url in self._demo_name_url_dict.iterkeys(): - self.NavigateToURL(self._demo_name_url_dict[url]) - # Measures performance using different demos. - avg_fps = self._MeasureFpsOverTime() - self.RestartBrowser() - # Get the standard value of fps and compare the rseults - if not self._GetStdAvgAndCompare(avg_fps, url, ref_dict): - all_demos_passed = False - self.assertTrue( - all_demos_passed, - msg='One or more demos failed to yield an acceptable FPS value') - - def testLaunchDemosBrowseForwardBackward(self): - """Measures performance of various demos in browser going back and forth.""" - ref_dict = self._gpu_info_dict['browse_back_forward_ref_data'] - url_array = [] - desc_array = [] - all_demos_passed = True - # Get URL/Description from dictionary and put in individual array - for url in self._demo_name_url_dict.iterkeys(): - url_array.append(self._demo_name_url_dict[url]) - desc_array.append(url) - for index in range(len(url_array) - 1): - # Launch demo in the Browser - if index == 0: - self.NavigateToURL(url_array[index]) - # Measures performance using the first demo. - avg_fps = self._MeasureFpsOverTime() - status1 = self._GetStdAvgAndCompare(avg_fps, desc_array[index], - ref_dict) - # Measures performance using the second demo. - self.NavigateToURL(url_array[index + 1]) - avg_fps = self._MeasureFpsOverTime() - status2 = self._GetStdAvgAndCompare(avg_fps, desc_array[index + 1], - ref_dict) - # Go Back to previous demo - self.TabGoBack() - # Measures performance for first demo when moved back - avg_fps = self._MeasureFpsOverTime() - status3 = self._GetStdAvgAndCompare( - avg_fps, desc_array[index] + '_backward', - ref_dict) - # Go Forward to previous demo - self.TabGoForward() - # Measures performance for second demo when moved forward - avg_fps = self._MeasureFpsOverTime() - status4 = self._GetStdAvgAndCompare( - avg_fps, desc_array[index + 1] + '_forward', - ref_dict) - if not all([status1, status2, status3, status4]): - all_demos_passed = False - self.assertTrue( - all_demos_passed, - msg='One or more demos failed to yield an acceptable FPS value') - - -class HTML5BenchmarkTest(BasePerfTest): - """Tests for HTML5 performance.""" - - def testHTML5Benchmark(self): - """Measures performance using the benchmark at html5-benchmark.com.""" - self.NavigateToURL('http://html5-benchmark.com') - - start_benchmark_js = """ - benchmark(); - window.domAutomationController.send("done"); - """ - self.ExecuteJavascript(start_benchmark_js) - - js_final_score = """ - var score = "-1"; - var elem = document.getElementById("score"); - if (elem) - score = elem.innerHTML; - window.domAutomationController.send(score); - """ - # Wait for the benchmark to complete, which is assumed to be when the value - # of the 'score' DOM element changes to something other than '87485'. - self.assertTrue( - self.WaitUntil( - lambda: self.ExecuteJavascript(js_final_score) != '87485', - timeout=900, retry_sleep=1), - msg='Timed out when waiting for final score to be available.') - - score = self.ExecuteJavascript(js_final_score) - logging.info('HTML5 Benchmark final score: %f', float(score)) - self._OutputPerfGraphValue('HTML5Benchmark', float(score), 'score', - 'html5_benchmark') - - -class FileUploadDownloadTest(BasePerfTest): - """Tests that involve measuring performance of upload and download.""" - - def setUp(self): - """Performs necessary setup work before running each test in this class.""" - self._temp_dir = tempfile.mkdtemp() - self._test_server = PerfTestServer(self._temp_dir) - self._test_server_port = self._test_server.GetPort() - self._test_server.Run() - self.assertTrue(self.WaitUntil(self._IsTestServerRunning), - msg='Failed to start local performance test server.') - BasePerfTest.setUp(self) - - def tearDown(self): - """Performs necessary cleanup work after running each test in this class.""" - BasePerfTest.tearDown(self) - self._test_server.ShutDown() - pyauto_utils.RemovePath(self._temp_dir) - - def _IsTestServerRunning(self): - """Determines whether the local test server is ready to accept connections. - - Returns: - True, if a connection can be made to the local performance test server, or - False otherwise. - """ - conn = None - try: - conn = urllib2.urlopen('http://localhost:%d' % self._test_server_port) - return True - except IOError, e: - return False - finally: - if conn: - conn.close() - - def testDownload100MBFile(self): - """Measures the time to download a 100 MB file from a local server.""" - CREATE_100MB_URL = ( - 'http://localhost:%d/create_file_of_size?filename=data&mb=100' % - self._test_server_port) - DOWNLOAD_100MB_URL = 'http://localhost:%d/data' % self._test_server_port - DELETE_100MB_URL = ('http://localhost:%d/delete_file?filename=data' % - self._test_server_port) - - # Tell the local server to create a 100 MB file. - self.NavigateToURL(CREATE_100MB_URL) - - # Cleaning up downloaded files is done in the same way as in downloads.py. - # We first identify all existing downloaded files, then remove only those - # new downloaded files that appear during the course of this test. - download_dir = self.GetDownloadDirectory().value() - orig_downloads = [] - if os.path.isdir(download_dir): - orig_downloads = os.listdir(download_dir) - - def _CleanupAdditionalFilesInDir(directory, orig_files): - """Removes the additional files in the specified directory. - - This function will remove all files from |directory| that are not - specified in |orig_files|. - - Args: - directory: A string directory path. - orig_files: A list of strings representing the original set of files in - the specified directory. - """ - downloads_to_remove = [] - if os.path.isdir(directory): - downloads_to_remove = [os.path.join(directory, name) - for name in os.listdir(directory) - if name not in orig_files] - for file_name in downloads_to_remove: - pyauto_utils.RemovePath(file_name) - - def _DownloadFile(url): - self.DownloadAndWaitForStart(url) - self.WaitForAllDownloadsToComplete(timeout=2 * 60 * 1000) # 2 minutes. - - timings = [] - for iteration in range(self._num_iterations + 1): - elapsed_time = self._MeasureElapsedTime( - lambda: _DownloadFile(DOWNLOAD_100MB_URL), num_invocations=1) - # Ignore the first iteration. - if iteration: - timings.append(elapsed_time) - logging.info('Iteration %d of %d: %f milliseconds', iteration, - self._num_iterations, elapsed_time) - self.SetDownloadShelfVisible(False) - _CleanupAdditionalFilesInDir(download_dir, orig_downloads) - - self._PrintSummaryResults('Download100MBFile', timings, 'milliseconds', - 'download_file') - - # Tell the local server to delete the 100 MB file. - self.NavigateToURL(DELETE_100MB_URL) - - def testUpload50MBFile(self): - """Measures the time to upload a 50 MB file to a local server.""" - # TODO(dennisjeffrey): Replace the use of XMLHttpRequest in this test with - # FileManager automation to select the upload file when crosbug.com/17903 - # is complete. - START_UPLOAD_URL = ( - 'http://localhost:%d/start_upload?mb=50' % self._test_server_port) - - EXPECTED_SUBSTRING = 'Upload complete' - - def _IsUploadComplete(): - js = """ - result = ""; - var div = document.getElementById("upload_result"); - if (div) - result = div.innerHTML; - window.domAutomationController.send(result); - """ - return self.ExecuteJavascript(js).find(EXPECTED_SUBSTRING) >= 0 - - def _RunSingleUpload(): - self.NavigateToURL(START_UPLOAD_URL) - self.assertTrue( - self.WaitUntil(_IsUploadComplete, timeout=120, expect_retval=True, - retry_sleep=0.10), - msg='Upload failed to complete before the timeout was hit.') - - timings = [] - for iteration in range(self._num_iterations + 1): - elapsed_time = self._MeasureElapsedTime(_RunSingleUpload) - # Ignore the first iteration. - if iteration: - timings.append(elapsed_time) - logging.info('Iteration %d of %d: %f milliseconds', iteration, - self._num_iterations, elapsed_time) - - self._PrintSummaryResults('Upload50MBFile', timings, 'milliseconds', - 'upload_file') - - -class FlashTest(BasePerfTest): - """Tests to measure flash performance.""" - - def _RunFlashTestForAverageFPS(self, webpage_url, description, graph_name): - """Runs a single flash test that measures an average FPS value. - - Args: - webpage_url: The string URL to a webpage that will run the test. - description: A string description for this test. - graph_name: A string name for the performance graph associated with this - test. Only used on Chrome desktop. - """ - # Open up the test webpage; it's assumed the test will start automatically. - self.assertTrue(self.AppendTab(pyauto.GURL(webpage_url)), - msg='Failed to append tab for webpage.') - - # Wait until the final result is computed, then retrieve and output it. - js = """ - window.domAutomationController.send( - JSON.stringify(final_average_fps)); - """ - self.assertTrue( - self.WaitUntil( - lambda: self.ExecuteJavascript(js, tab_index=1) != '-1', - timeout=300, expect_retval=True, retry_sleep=1), - msg='Timed out when waiting for test result.') - result = float(self.ExecuteJavascript(js, tab_index=1)) - logging.info('Result for %s: %f FPS (average)', description, result) - self._OutputPerfGraphValue(description, result, 'FPS', graph_name) - - def testFlashGaming(self): - """Runs a simple flash gaming benchmark test.""" - webpage_url = self.GetHttpURLForDataPath('pyauto_private', 'flash', - 'FlashGamingTest2.html') - self._RunFlashTestForAverageFPS(webpage_url, 'FlashGaming', 'flash_fps') - - def testFlashText(self): - """Runs a simple flash text benchmark test.""" - webpage_url = self.GetHttpURLForDataPath('pyauto_private', 'flash', - 'FlashTextTest2.html') - self._RunFlashTestForAverageFPS(webpage_url, 'FlashText', 'flash_fps') - - def testScimarkGui(self): - """Runs the ScimarkGui benchmark tests.""" - webpage_url = self.GetHttpURLForDataPath('pyauto_private', 'flash', - 'scimarkGui.html') - self.assertTrue(self.AppendTab(pyauto.GURL(webpage_url)), - msg='Failed to append tab for webpage.') - - js = 'window.domAutomationController.send(JSON.stringify(tests_done));' - self.assertTrue( - self.WaitUntil( - lambda: self.ExecuteJavascript(js, tab_index=1), timeout=300, - expect_retval='true', retry_sleep=1), - msg='Timed out when waiting for tests to complete.') - - js_result = """ - var result = {}; - for (var i = 0; i < tests_results.length; ++i) { - var test_name = tests_results[i][0]; - var mflops = tests_results[i][1]; - var mem = tests_results[i][2]; - result[test_name] = [mflops, mem] - } - window.domAutomationController.send(JSON.stringify(result)); - """ - result = eval(self.ExecuteJavascript(js_result, tab_index=1)) - for benchmark in result: - mflops = float(result[benchmark][0]) - mem = float(result[benchmark][1]) - if benchmark.endswith('_mflops'): - benchmark = benchmark[:benchmark.find('_mflops')] - logging.info('Results for ScimarkGui_%s:', benchmark) - logging.info(' %f MFLOPS', mflops) - logging.info(' %f MB', mem) - self._OutputPerfGraphValue('ScimarkGui-%s-MFLOPS' % benchmark, mflops, - 'MFLOPS', 'scimark_gui_mflops') - self._OutputPerfGraphValue('ScimarkGui-%s-Mem' % benchmark, mem, 'MB', - 'scimark_gui_mem') - - -class LiveGamePerfTest(BasePerfTest): - """Tests to measure performance of live gaming webapps.""" - - def _RunLiveGamePerfTest(self, url, url_title_substring, - description, graph_name): - """Measures performance metrics for the specified live gaming webapp. - - This function connects to the specified URL to launch the gaming webapp, - waits for a period of time for the webapp to run, then collects some - performance metrics about the running webapp. - - Args: - url: The string URL of the gaming webapp to analyze. - url_title_substring: A string that is expected to be a substring of the - webpage title for the specified gaming webapp. Used to verify that - the webapp loads correctly. - description: A string description for this game, used in the performance - value description. Should not contain any spaces. - graph_name: A string name for the performance graph associated with this - test. Only used on Chrome desktop. - """ - self.NavigateToURL(url) - loaded_tab_title = self.GetActiveTabTitle() - self.assertTrue(url_title_substring in loaded_tab_title, - msg='Loaded tab title missing "%s": "%s"' % - (url_title_substring, loaded_tab_title)) - cpu_usage_start = self._GetCPUUsage() - - # Let the app run for 1 minute. - time.sleep(60) - - cpu_usage_end = self._GetCPUUsage() - fraction_non_idle_time = self._GetFractionNonIdleCPUTime( - cpu_usage_start, cpu_usage_end) - - logging.info('Fraction of CPU time spent non-idle: %f', - fraction_non_idle_time) - self._OutputPerfGraphValue(description + 'CpuBusy', fraction_non_idle_time, - 'Fraction', graph_name + '_cpu_busy') - v8_heap_stats = self.GetV8HeapStats() - v8_heap_size = v8_heap_stats['v8_memory_used'] / (1024.0 * 1024.0) - logging.info('Total v8 heap size: %f MB', v8_heap_size) - self._OutputPerfGraphValue(description + 'V8HeapSize', v8_heap_size, 'MB', - graph_name + '_v8_heap_size') - - def testAngryBirds(self): - """Measures performance for Angry Birds.""" - self._RunLiveGamePerfTest('http://chrome.angrybirds.com', 'Angry Birds', - 'AngryBirds', 'angry_birds') - - -class BasePageCyclerTest(BasePerfTest): - """Page class for page cycler tests. - - Derived classes must implement StartUrl(). - - Environment Variables: - PC_NO_AUTO: if set, avoids automatically loading pages. - """ - MAX_ITERATION_SECONDS = 60 - TRIM_PERCENT = 20 - DEFAULT_USE_AUTO = True - - # Page Cycler lives in src/data/page_cycler rather than src/chrome/test/data - DATA_PATH = os.path.abspath( - os.path.join(BasePerfTest.DataDir(), os.pardir, os.pardir, - os.pardir, 'data', 'page_cycler')) - - def setUp(self): - """Performs necessary setup work before running each test.""" - super(BasePageCyclerTest, self).setUp() - self.use_auto = 'PC_NO_AUTO' not in os.environ - - @classmethod - def DataPath(cls, subdir): - return os.path.join(cls.DATA_PATH, subdir) - - def ExtraChromeFlags(self): - """Ensures Chrome is launched with custom flags. - - Returns: - A list of extra flags to pass to Chrome when it is launched. - """ - # Extra flags required to run these tests. - # The first two are needed for the test. - # The plugins argument is to prevent bad scores due to pop-ups from - # running an old version of something (like Flash). - return (super(BasePageCyclerTest, self).ExtraChromeFlags() + - ['--js-flags="--expose_gc"', - '--enable-file-cookies', - '--allow-outdated-plugins']) - - def WaitUntilStarted(self, start_url): - """Check that the test navigates away from the start_url.""" - js_is_started = """ - var is_started = document.location.href !== "%s"; - window.domAutomationController.send(JSON.stringify(is_started)); - """ % start_url - self.assertTrue( - self.WaitUntil(lambda: self.ExecuteJavascript(js_is_started) == 'true', - timeout=10), - msg='Timed out when waiting to leave start page.') - - def WaitUntilDone(self, url, iterations): - """Check cookies for "__pc_done=1" to know the test is over.""" - def IsDone(): - cookies = self.GetCookie(pyauto.GURL(url)) # window 0, tab 0 - return '__pc_done=1' in cookies - self.assertTrue( - self.WaitUntil( - IsDone, - timeout=(self.MAX_ITERATION_SECONDS * iterations), - retry_sleep=1), - msg='Timed out waiting for page cycler test to complete.') - - def CollectPagesAndTimes(self, url): - """Collect the results from the cookies.""" - pages, times = None, None - cookies = self.GetCookie(pyauto.GURL(url)) # window 0, tab 0 - for cookie in cookies.split(';'): - if '__pc_pages' in cookie: - pages_str = cookie.split('=', 1)[1] - pages = pages_str.split(',') - elif '__pc_timings' in cookie: - times_str = cookie.split('=', 1)[1] - times = [float(t) for t in times_str.split(',')] - self.assertTrue(pages and times, - msg='Unable to find test results in cookies: %s' % cookies) - return pages, times - - def IteratePageTimes(self, pages, times, iterations): - """Regroup the times by the page. - - Args: - pages: the list of pages - times: e.g. [page1_iter1, page2_iter1, ..., page1_iter2, page2_iter2, ...] - iterations: the number of times for each page - Yields: - (pageN, [pageN_iter1, pageN_iter2, ...]) - """ - num_pages = len(pages) - num_times = len(times) - expected_num_times = num_pages * iterations - self.assertEqual( - expected_num_times, num_times, - msg=('num_times != num_pages * iterations: %s != %s * %s, times=%s' % - (num_times, num_pages, iterations, times))) - for i, page in enumerate(pages): - yield page, list(itertools.islice(times, i, None, num_pages)) - - def CheckPageTimes(self, pages, times, iterations): - """Assert that all the times are greater than zero.""" - failed_pages = [] - for page, times in self.IteratePageTimes(pages, times, iterations): - failed_times = [t for t in times if t <= 0.0] - if failed_times: - failed_pages.append((page, failed_times)) - if failed_pages: - self.fail('Pages with unexpected times: %s' % failed_pages) - - def TrimTimes(self, times, percent): - """Return a new list with |percent| number of times trimmed for each page. - - Removes the largest and smallest values. - """ - iterations = len(times) - times = sorted(times) - num_to_trim = int(iterations * float(percent) / 100.0) - logging.debug('Before trimming %d: %s' % (num_to_trim, times)) - a = num_to_trim / 2 - b = iterations - (num_to_trim / 2 + num_to_trim % 2) - trimmed_times = times[a:b] - logging.debug('After trimming: %s', trimmed_times) - return trimmed_times - - def ComputeFinalResult(self, pages, times, iterations): - """The final score that is calculated is a geometric mean of the - arithmetic means of each page's load time, and we drop the - upper/lower 20% of the times for each page so they don't skew the - mean. The geometric mean is used for the final score because the - time range for any given site may be very different, and we don't - want slower sites to weight more heavily than others. - """ - self.CheckPageTimes(pages, times, iterations) - page_means = [ - Mean(self.TrimTimes(times, percent=self.TRIM_PERCENT)) - for _, times in self.IteratePageTimes(pages, times, iterations)] - return GeometricMean(page_means) - - def StartUrl(self, test_name, iterations): - """Return the URL to used to start the test. - - Derived classes must implement this. - """ - raise NotImplemented - - def RunPageCyclerTest(self, name, description): - """Runs the specified PageCycler test. - - Args: - name: the page cycler test name (corresponds to a directory or test file) - description: a string description for the test - """ - iterations = self._num_iterations - start_url = self.StartUrl(name, iterations) - self.NavigateToURL(start_url) - if self.use_auto: - self.WaitUntilStarted(start_url) - self.WaitUntilDone(start_url, iterations) - pages, times = self.CollectPagesAndTimes(start_url) - final_result = self.ComputeFinalResult(pages, times, iterations) - logging.info('%s page cycler final result: %f' % - (description, final_result)) - self._OutputPerfGraphValue(description + '_PageCycler', final_result, - 'milliseconds', graph_name='PageCycler') - - -class PageCyclerTest(BasePageCyclerTest): - """Tests to run various page cyclers. - - Environment Variables: - PC_NO_AUTO: if set, avoids automatically loading pages. - """ - - def _PreReadDataDir(self, subdir): - """This recursively reads all of the files in a given url directory. - - The intent is to get them into memory before they are used by the benchmark. - - Args: - subdir: a subdirectory of the page cycler data directory. - """ - def _PreReadDir(dirname, names): - for rfile in names: - with open(os.path.join(dirname, rfile)) as fp: - fp.read() - for root, dirs, files in os.walk(self.DataPath(subdir)): - _PreReadDir(root, files) - - def StartUrl(self, test_name, iterations): - # Must invoke GetFileURLForPath before appending parameters to the URL, - # otherwise those parameters will get quoted. - start_url = self.GetFileURLForPath(self.DataPath(test_name), 'start.html') - start_url += '?iterations=%d' % iterations - if self.use_auto: - start_url += '&auto=1' - return start_url - - def RunPageCyclerTest(self, dirname, description): - """Runs the specified PageCycler test. - - Args: - dirname: directory containing the page cycler test - description: a string description for the test - """ - self._PreReadDataDir('common') - self._PreReadDataDir(dirname) - super(PageCyclerTest, self).RunPageCyclerTest(dirname, description) - - def testMoreJSFile(self): - self.RunPageCyclerTest('morejs', 'MoreJSFile') - - def testAlexaFile(self): - self.RunPageCyclerTest('alexa_us', 'Alexa_usFile') - - def testBloatFile(self): - self.RunPageCyclerTest('bloat', 'BloatFile') - - def testDHTMLFile(self): - self.RunPageCyclerTest('dhtml', 'DhtmlFile') - - def testIntl1File(self): - self.RunPageCyclerTest('intl1', 'Intl1File') - - def testIntl2File(self): - self.RunPageCyclerTest('intl2', 'Intl2File') - - def testMozFile(self): - self.RunPageCyclerTest('moz', 'MozFile') - - def testMoz2File(self): - self.RunPageCyclerTest('moz2', 'Moz2File') - - -class MemoryTest(BasePerfTest): - """Tests to measure memory consumption under different usage scenarios.""" - - def ExtraChromeFlags(self): - """Launches Chrome with custom flags. - - Returns: - A list of extra flags to pass to Chrome when it is launched. - """ - # Ensure Chrome assigns one renderer process to each tab. - return super(MemoryTest, self).ExtraChromeFlags() + ['--process-per-tab'] - - def _RecordMemoryStats(self, description, when, duration): - """Outputs memory statistics to be graphed. - - Args: - description: A string description for the test. Should not contain - spaces. For example, 'MemCtrl'. - when: A string description of when the memory stats are being recorded - during test execution (since memory stats may be recorded multiple - times during a test execution at certain "interesting" times). Should - not contain spaces. - duration: The number of seconds to sample data before outputting the - memory statistics. - """ - mem = self.GetMemoryStatsChromeOS(duration) - measurement_types = [ - ('gem_obj', 'GemObj'), - ('gtt', 'GTT'), - ('mem_free', 'MemFree'), - ('mem_available', 'MemAvail'), - ('mem_shared', 'MemShare'), - ('mem_cached', 'MemCache'), - ('mem_anon', 'MemAnon'), - ('mem_file', 'MemFile'), - ('mem_slab', 'MemSlab'), - ('browser_priv', 'BrowPriv'), - ('browser_shared', 'BrowShar'), - ('gpu_priv', 'GpuPriv'), - ('gpu_shared', 'GpuShar'), - ('renderer_priv', 'RendPriv'), - ('renderer_shared', 'RendShar'), - ] - for type_key, type_string in measurement_types: - if type_key not in mem: - continue - self._OutputPerfGraphValue( - '%s-Min%s-%s' % (description, type_string, when), - mem[type_key]['min'], 'KB', '%s-%s' % (description, type_string)) - self._OutputPerfGraphValue( - '%s-Max%s-%s' % (description, type_string, when), - mem[type_key]['max'], 'KB', '%s-%s' % (description, type_string)) - self._OutputPerfGraphValue( - '%s-End%s-%s' % (description, type_string, when), - mem[type_key]['end'], 'KB', '%s-%s' % (description, type_string)) - - def _RunTest(self, tabs, description, duration): - """Runs a general memory test. - - Args: - tabs: A list of strings representing the URLs of the websites to open - during this test. - description: A string description for the test. Should not contain - spaces. For example, 'MemCtrl'. - duration: The number of seconds to sample data before outputting memory - statistics. - """ - self._RecordMemoryStats(description, '0Tabs0', duration) - - for iteration_num in xrange(2): - for site in tabs: - self.AppendTab(pyauto.GURL(site)) - - self._RecordMemoryStats(description, - '%dTabs%d' % (len(tabs), iteration_num + 1), - duration) - - for _ in xrange(len(tabs)): - self.CloseTab(tab_index=1) - - self._RecordMemoryStats(description, '0Tabs%d' % (iteration_num + 1), - duration) - - def testOpenCloseTabsControl(self): - """Measures memory usage when opening/closing tabs to about:blank.""" - tabs = ['about:blank'] * 10 - self._RunTest(tabs, 'MemCtrl', 15) - - def testOpenCloseTabsLiveSites(self): - """Measures memory usage when opening/closing tabs to live sites.""" - tabs = [ - 'http://www.google.com/gmail', - 'http://www.google.com/calendar', - 'http://www.google.com/plus', - 'http://www.google.com/youtube', - 'http://www.nytimes.com', - 'http://www.cnn.com', - 'http://www.facebook.com/zuck', - 'http://www.techcrunch.com', - 'http://www.theverge.com', - 'http://www.yahoo.com', - ] - # Log in to a test Google account to make connections to the above Google - # websites more interesting. - self._LoginToGoogleAccount() - self._RunTest(tabs, 'MemLive', 20) - - -class PerfTestServerRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): - """Request handler for the local performance test server.""" - - def _IgnoreHandler(self, unused_args): - """A GET request handler that simply replies with status code 200. - - Args: - unused_args: A dictionary of arguments for the current GET request. - The arguments are ignored. - """ - self.send_response(200) - self.end_headers() - - def _CreateFileOfSizeHandler(self, args): - """A GET handler that creates a local file with the specified size. - - Args: - args: A dictionary of arguments for the current GET request. Must - contain 'filename' and 'mb' keys that refer to the name of the file - to create and its desired size, respectively. - """ - megabytes = None - filename = None - try: - megabytes = int(args['mb'][0]) - filename = args['filename'][0] - except (ValueError, KeyError, IndexError), e: - logging.exception('Server error creating file: %s', e) - assert megabytes and filename - with open(os.path.join(self.server.docroot, filename), 'wb') as f: - f.write('X' * 1024 * 1024 * megabytes) - self.send_response(200) - self.end_headers() - - def _DeleteFileHandler(self, args): - """A GET handler that deletes the specified local file. - - Args: - args: A dictionary of arguments for the current GET request. Must - contain a 'filename' key that refers to the name of the file to - delete, relative to the server's document root. - """ - filename = None - try: - filename = args['filename'][0] - except (KeyError, IndexError), e: - logging.exception('Server error deleting file: %s', e) - assert filename - try: - os.remove(os.path.join(self.server.docroot, filename)) - except OSError, e: - logging.warning('OS error removing file: %s', e) - self.send_response(200) - self.end_headers() - - def _StartUploadHandler(self, args): - """A GET handler to serve a page that uploads the given amount of data. - - When the page loads, the specified amount of data is automatically - uploaded to the same local server that is handling the current request. - - Args: - args: A dictionary of arguments for the current GET request. Must - contain an 'mb' key that refers to the size of the data to upload. - """ - megabytes = None - try: - megabytes = int(args['mb'][0]) - except (ValueError, KeyError, IndexError), e: - logging.exception('Server error starting upload: %s', e) - assert megabytes - script = """ - <html> - <head> - <script type='text/javascript'> - function startUpload() { - var megabytes = %s; - var data = Array((1024 * 1024 * megabytes) + 1).join('X'); - var boundary = '***BOUNDARY***'; - var xhr = new XMLHttpRequest(); - - xhr.open('POST', 'process_upload', true); - xhr.setRequestHeader( - 'Content-Type', - 'multipart/form-data; boundary="' + boundary + '"'); - xhr.setRequestHeader('Content-Length', data.length); - xhr.onreadystatechange = function() { - if (xhr.readyState == 4 && xhr.status == 200) { - document.getElementById('upload_result').innerHTML = - xhr.responseText; - } - }; - var body = '--' + boundary + '\\r\\n'; - body += 'Content-Disposition: form-data;' + - 'file_contents=' + data; - xhr.send(body); - } - </script> - </head> - - <body onload="startUpload();"> - <div id='upload_result'>Uploading...</div> - </body> - </html> - """ % megabytes - self.send_response(200) - self.end_headers() - self.wfile.write(script) - - def _ProcessUploadHandler(self, form): - """A POST handler that discards uploaded data and sends a response. - - Args: - form: A dictionary containing posted form data, as returned by - urlparse.parse_qs(). - """ - upload_processed = False - file_size = 0 - if 'file_contents' in form: - file_size = len(form['file_contents'][0]) - upload_processed = True - self.send_response(200) - self.end_headers() - if upload_processed: - self.wfile.write('Upload complete (%d bytes)' % file_size) - else: - self.wfile.write('No file contents uploaded') - - GET_REQUEST_HANDLERS = { - 'create_file_of_size': _CreateFileOfSizeHandler, - 'delete_file': _DeleteFileHandler, - 'start_upload': _StartUploadHandler, - 'favicon.ico': _IgnoreHandler, - } - - POST_REQUEST_HANDLERS = { - 'process_upload': _ProcessUploadHandler, - } - - def translate_path(self, path): - """Ensures files are served from the given document root. - - Overridden from SimpleHTTPServer.SimpleHTTPRequestHandler. - """ - path = urlparse.urlparse(path)[2] - path = posixpath.normpath(urllib.unquote(path)) - words = path.split('/') - words = filter(None, words) # Remove empty strings from |words|. - path = self.server.docroot - for word in words: - _, word = os.path.splitdrive(word) - _, word = os.path.split(word) - if word in (os.curdir, os.pardir): - continue - path = os.path.join(path, word) - return path - - def do_GET(self): - """Processes a GET request to the local server. - - Overridden from SimpleHTTPServer.SimpleHTTPRequestHandler. - """ - split_url = urlparse.urlsplit(self.path) - base_path = split_url[2] - if base_path.startswith('/'): - base_path = base_path[1:] - args = urlparse.parse_qs(split_url[3]) - if base_path in self.GET_REQUEST_HANDLERS: - self.GET_REQUEST_HANDLERS[base_path](self, args) - else: - SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) - - def do_POST(self): - """Processes a POST request to the local server. - - Overridden from SimpleHTTPServer.SimpleHTTPRequestHandler. - """ - form = urlparse.parse_qs( - self.rfile.read(int(self.headers.getheader('Content-Length')))) - path = urlparse.urlparse(self.path)[2] - if path.startswith('/'): - path = path[1:] - if path in self.POST_REQUEST_HANDLERS: - self.POST_REQUEST_HANDLERS[path](self, form) - else: - self.send_response(200) - self.send_header('Content-Type', 'text/plain') - self.end_headers() - self.wfile.write('No handler for POST request "%s".' % path) - - -class ThreadedHTTPServer(SocketServer.ThreadingMixIn, - BaseHTTPServer.HTTPServer): - def __init__(self, server_address, handler_class): - BaseHTTPServer.HTTPServer.__init__(self, server_address, handler_class) - - -class PerfTestServer(object): - """Local server for use by performance tests.""" - - def __init__(self, docroot): - """Initializes the performance test server. - - Args: - docroot: The directory from which to serve files. - """ - # The use of 0 means to start the server on an arbitrary available port. - self._server = ThreadedHTTPServer(('', 0), - PerfTestServerRequestHandler) - self._server.docroot = docroot - self._server_thread = threading.Thread(target=self._server.serve_forever) - - def Run(self): - """Starts the server thread.""" - self._server_thread.start() - - def ShutDown(self): - """Shuts down the server.""" - self._server.shutdown() - self._server_thread.join() - - def GetPort(self): - """Identifies the port number to which the server is currently bound. - - Returns: - The numeric port number to which the server is currently bound. - """ - return self._server.server_address[1] - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/perf/endure_graphs/config.js b/chrome/test/functional/perf/endure_graphs/config.js deleted file mode 100644 index cb9fa3d..0000000 --- a/chrome/test/functional/perf/endure_graphs/config.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2013 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. - -var Config = { - buildslave: 'Chrome Endure Bots', - title: 'Chrome Endure Tests', -}; diff --git a/chrome/test/functional/perf/endure_graphs/endure_plotter.html b/chrome/test/functional/perf/endure_graphs/endure_plotter.html deleted file mode 100644 index 42072e7..0000000 --- a/chrome/test/functional/perf/endure_graphs/endure_plotter.html +++ /dev/null @@ -1,114 +0,0 @@ -<!-- - Copyright (c) 2012 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. ---> - -<!-- - HTML for a general Chrome Endure graph. ---> - -<html> - <head> - <style> - body { - font-family: sans-serif; - } - div#output { - cursor: pointer; - } - div#switcher * { - border: 1px solid black; - border-radius: 4px 4px 0 0; - padding-left: 0.5em; - padding-right: 0.5em; - } - div#switcher a { - background: #ddd; - cursor: pointer; - } - canvas.plot { - border: 1px solid black; - } - div.plot-coordinates { - font-family: monospace; - } - iframe { - display: none; - width: 100%; - height: 100%; - border: none; - } - div.selected { - border-left: none; - } - #explain { - font-size: 0.75em; - font-style: italic; - color: rgb(100,100,100); - } - </style> - - <script src="js/common.js"></script> - <script src="js/coordinates.js"></script> - <script src="js/dom_utils.js"></script> - <script src="js/graph_utils.js"></script> - <script src="js/plotter.js"></script> - <script src="config.js"></script> - - <script src="endure_plotter.js"></script> - </head> - - <body> - <div id="header_lookout" align="center"> - <font style='color: #0066FF; font-family: Arial, serif; - font-size: 12pt; font-weight: bold;'> - <script> - document.write("<a target=\"_blank\" href=\""); - document.write(get_url()); - document.write("\">"); - if ('graph' in params && params.graph != '') - document.write(escape(params.graph)); - else - document.write(Config.title); - document.write("</a>"); - </script> - </font> - </div> - - <div id="header_text"> - Builds generated by the <i> - <script> - document.write(Config.buildslave); - </script> - </i> are run through <b> - <script> - document.write(Config.title); - </script> - </b> and the results of that test are charted here. - </div> - - <div id="explain"> - More information about Chrome Endure can be found here: - <a href="http://www.chromium.org/developers/testing/pyauto/perf/endure"> - http://www.chromium.org/developers/testing/pyauto/perf/endure</a> - </div> - - <p></p> - - <div id="switcher"></div> - <div id="output"></div> <br> - <div id="revisions"></div> <br> - <div id="comparisons"></div> <br> - <div id="events"></div> - <script> - if ('lookout' in params) { - document.getElementById("switcher").style.display = "none"; - document.getElementById("header_text").style.display = "none"; - document.getElementById("explain").style.display = "none"; - } else { - document.getElementById("header_lookout").style.display = "none"; - } - </script> - </body> -</html> diff --git a/chrome/test/functional/perf/endure_graphs/endure_plotter.js b/chrome/test/functional/perf/endure_graphs/endure_plotter.js deleted file mode 100644 index c7c90f7..0000000 --- a/chrome/test/functional/perf/endure_graphs/endure_plotter.js +++ /dev/null @@ -1,553 +0,0 @@ -/* - Copyright (c) 2012 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. -*/ - -/** - * @fileoverview Handles drawing a general Chrome Endure graph. - */ - -document.title = Config.title + ' - ' + Config.buildslave; - -var unitsX = 'unitsX'; -var unitsY = 'unitsY'; -var unitsYOther = null; -var graphList = []; -var revisionNumbers = []; -var graphDataOtherRows = null; - -var eventRows = null; -var eventTypes = []; -var eventInfo = null; - -var params = ParseParams(); - -/** - * Encapsulates a *-summary.dat file. - * @constructor - * - * @param {string} data Raw data from a *-summary.dat file. - */ -function Rows(data) { - this.rows = data.split('\n'); - this.length = this.rows.length; -} - -/** - * Returns the row at the given index. - * - * @param {number} i The index of a row of data from the *-summary.dat file. - * @return {Object} An object representing a row of data from the input file. - */ -Rows.prototype.get = function(i) { - if (!this.rows[i].length) return null; - var row = jsonToJs(this.rows[i]); - row.revision = isNaN(row['rev']) ? row['rev'] : parseInt(row['rev']); - return row; -}; - -/** - * Gets the current URL, but without the 'lookout' parameter. - * - * @return {string} The current URL, but without the 'lookout' parameter. - */ -function get_url() { - new_url = window.location.href; - new_url = new_url.replace(/\&lookout=1/, ''); - return new_url; -} - -/** - * Reports an error message on the webpage. - * - * @param {string} error An error message to display on the page. - */ -function reportError(error) { - document.getElementById('output').innerHTML = '<p>' + error + '</p>'; -} - -/** - * Converts a JSON string into a Javascript object. - * - * @param {string} data A string in JSON format. - * @return {Object} A Javascript object computed from the JSON string. - */ -function jsonToJs(data) { - return eval('(' + data + ')') -} - -/** - * Causes the page to navigate to another graph. - * - * @param {string} graph The name of the graph to which to navigate. - */ -function goTo(graph) { - params.graph = graph; - window.location.href = MakeURL(params); -} - -/** - * Returns a function that will navigate the page to another graph. - * - * @param {string} graph The name of the graph to which to navigate. - * @return {Function} A function that will navigate the page to another graph. - */ -function goToClosure(graph) { - return function(){goTo(graph)}; -} - -/** - * Changes the event being overlayed on the graph. - * - * @param {string} eventName The name of the event to overlay on the graph. - */ -function changeEventCompare(eventName) { - delete params.revisionOther; - delete params.graphOther; - if (eventName == 'None') { - delete params.event; - window.location.href = MakeURL(params); - } else { - params.event = eventName; - window.location.href = MakeURL(params); - } -} - -/** - * Changes the other measurement being overlayed on top of an original line on - * the graph. - * - * @param {string} graphName The name of the other graph to overlay on top of - * the existing graph. - */ -function changeMeasurementCompare(graphName) { - delete params.revisionOther; - delete params.event; - if (graphName == 'None') { - delete params.graphOther; - window.location.href = MakeURL(params); - } else { - params.graphOther = graphName; - window.location.href = MakeURL(params); - } -} - -/** - * Changes the number of the other revision to compare against on the graph. - * - * @param {string} revision The revision number of the other line to plot on - * the graph. - */ -function changeRevisionCompare(revision) { - delete params.graphOther; - delete params.event; - if (revision == 'None') { - delete params.revisionOther; - window.location.href = MakeURL(params); - } else { - params.revisionOther = revision; - window.location.href = MakeURL(params); - } -} - -/** - * Changes the displayed revision number of the graph line. - * - * @param {string} revision The revision number of the graph to display. - */ -function changeRevision(revision) { - delete params.revisionOther; - delete params.graphOther; - delete params.event; - params.revision = revision; - window.location.href = MakeURL(params); -} - -/** - * Initializes the UI for changing the revision number of the displayed graph. - */ -function initRevisionOptions() { - var html = '<table cellpadding=5><tr><td>'; - html += '<b>Chrome revision:</b> '; - html += '<select onchange=\"changeRevision(this.value)\">'; - for (var i = 0; i < revisionNumbers.length; ++i) { - html += '<option id=\"r' + revisionNumbers[i] + '\"'; - if (revisionNumbers[i] == params.revision) - html += 'selected=\"true\"'; - html += '>' + revisionNumbers[i] + '</option>'; - } - html += '</select></td></tr></table>'; - - document.getElementById('revisions').innerHTML = html; -} - -/** - * Initializes the UI for changing what is compared against the current line - * on the displayed graph. - */ -function initComparisonOptions() { - var html = '<table cellpadding=5>'; - html += '<tr><td><b>Compare with (select one):</b></td></tr>'; - - html += '<tr><td> Another run: '; - html += '<select onchange=\"changeRevisionCompare(this.value)\">'; - html += '<option selected=\"true\">None</option>'; - for (var i = 0; i < revisionNumbers.length; ++i) { - html += '<option id=\"r' + revisionNumbers[i] + '\"'; - if (revisionNumbers[i] == params.revisionOther) - html += 'selected=\"true\"'; - html += '>' + revisionNumbers[i] + '</option>'; - } - html += '</select></td></tr>' - - html += '<tr><td> Another measurement of same run: '; - html += '<select onchange=\"changeMeasurementCompare(this.value)\">'; - html += '<option selected=\"true\">None</option>'; - for (var i = 0; i < graphList.length; ++i) { - var graph = graphList[i]; - html += '<option id=\"r' + graph.name + '\"'; - if (graph.name == params.graphOther) - html += 'selected=\"true\"'; - html += '>' + graph.name + '</option>'; - } - html += '</select></td></tr>'; - - html += '<tr><td> Event overlay: '; - if (eventTypes.length >= 1) { - html += '<select onchange=\"changeEventCompare(this.value)\">'; - html += '<option selected=\"true\">None</option>'; - for (var i = 0; i < eventTypes.length; ++i) { - var eventType = eventTypes[i]; - html += '<option id=\"' + eventType + '\"'; - if (eventType == params.event) - html += 'selected=\"true\"'; - html += '>' + eventType + '</option>'; - } - html += '</select>'; - } else { - html += ' <i><font size=-1>No events for this revision</font></i>'; - } - html += '</td></tr></table>'; - - document.getElementById('comparisons').innerHTML = html; -} - -/** - * Initializes the UI for the tabs at the top of a graph to change the displayed - * line. - */ -function initPlotSwitcher(tabs) { - var switcher = document.getElementById('switcher'); - for (var i = 0; i < tabs.length; ++i) { - var is_selected = tabs[i] == params.graph; - var tab = document.createElement(is_selected ? 'span' : 'a'); - tab.appendChild(document.createTextNode(tabs[i] + ' ')); - if (!is_selected) - tab.addEventListener('click', goToClosure(tabs[i]), false); - switcher.appendChild(tab); - } -} - -/** - * Adds data to existing arrays indicating what data should be plotted. - * - * @param {number} revisionNum The revision number of the data to plot. - * @param {Rows} dataRows The |Rows| object containing the plot data. - * @param {Array} plotData A list of data lines to plot, to which new data will - * be appended. - * @param {Array} dataDescriptions A list of string descriptions corresponding - * to data lines in |plotData|, to which new data will be appended. - * @return {Object} A row object specified by {@code revisionNum} on success, - * otherwise returns null. - */ -function addToPlotData(revisionNum, dataRows, plotData, dataDescriptions) { - // Get data for the revision number(s) to plot. - var found = false; - for (var i = 0; i < dataRows.length; ++i) { - var row = dataRows.get(i); - if (row && row.revision == revisionNum) { - found = true; - break; - } - } - if (!found) { - return null; - } - - if (row.stack) { - if (!row.stack_order) { - reportError('No stack order was specified.'); - return null; - } - var traceList = row.stack_order; - } else { - // Identify the (single) trace name associated with this revision. - var traceName = null; - for (var t in row.traces) { - if (traceName) { - reportError('Only one trace per revision is supported for ' + - 'non-stacked graphs.'); - return null; - } - traceName = t; - } - var traceList = [traceName]; - } - - var lines = []; - for (var i = 0, traceName; traceName = traceList[i]; ++i) { - var trace = row.traces[traceName]; - if (!trace) { - reportError('No specified trace was found.'); - return null; - } - - var points = []; - for (var j = 0, point; point = trace[j]; ++j) { - points.push([parseFloat(point[0]), parseFloat(point[1])]); - } - lines.push(points); - dataDescriptions.push(traceName + ' [r' + row.revision + ']'); - } - - if (row.stack) { - lines = graphUtils.stackFrontToBack(graphUtils.interpolate(lines)); - } - - for (var i = 0, line; line = lines[i]; ++i) { - plotData.push(line); - } - - return row; -} - -/** - * Callback for when a *-summary.dat data file has been read. - * - * @param {string} data The string data from the inputted text file. - * @param {string} error A string error message, in case an error occurred - * during the file read. - */ -function receivedSummary(data, error) { - if (error) { - reportError(error); - return; - } - - var errorMessages = ''; - var rows = new Rows(data); - - // Build and order a list of revision numbers. - revisionNumbers = []; - for (var i = 0; i < rows.length; ++i) { - var row = rows.get(i); - if (!row) - continue; - revisionNumbers.push(row.revision); - } - revisionNumbers.sort( - function(a, b) { return parseInt(a, 10) - parseInt(b, 10) }); - - // Get the revision number to plot. - if (!('revision' in params) || params.revision == '') { - if (revisionNumbers.length >= 2 && 'lookout' in params) { - // Since the last graph (test run) might still be in progress, get the - // second-to-last graph to display on the summary page. That one - // is assumed to have finished running to completion. - params.revision = revisionNumbers[revisionNumbers.length-2]; - } else { - if (revisionNumbers.length >= 1) { - params.revision = revisionNumbers[revisionNumbers.length-1]; - } else { - reportError('No revision information to plot.'); - return; - } - } - } - - var plotData = []; // plotData is a list of graph lines; each graph line is - // a list of points; each point is a list of 2 values, - // representing the (x, y) pair. - var dataDescriptions = []; - - var row = addToPlotData(params.revision, rows, plotData, dataDescriptions); - if (!row) { - errorMessages += 'No data for the specified revision.<br>'; - } - // From index {@code plotData.length} onwards, any graph lines in - // {@code plotData} are considered to be part of a second set of graphs. - var graphsOtherStartIndex = plotData.length; - - var rowOther = null; - if ('revisionOther' in params) { - rowOther = addToPlotData(params.revisionOther, rows, plotData, - dataDescriptions); - if (!rowOther) - errorMessages += 'No data for the revision to compare against.<br>'; - } - - if ('graphOther' in params) { - rowOther = addToPlotData(params.revision, graphDataOtherRows, plotData, - dataDescriptions); - if (rowOther) { - for (var i = 0; i < graphList.length; ++i) { - if (graphList[i].name == params.graphOther) { - unitsYOther = graphList[i].units; - break; - } - } - } else { - errorMessages += 'No data for the measurement to compare against.<br>'; - } - } - - // Identify the events for the current revision. - if (eventRows) { - for (var index = 0; index < eventRows.length; ++index) { - var info = eventRows.get(index); - if (params.revision == info['rev']) { - eventInfo = info; - break; - } - } - if (eventInfo != null) { - for (var key in eventInfo['events']) { - eventTypes.push(key); - } - } - } - - // Get data for the events to display, if one was requested in the params. - var eventNameToPlot = null; - var eventInfoToPlot = null; - if ('event' in params && eventInfo != null) { - for (var key in eventInfo['events']) { - if (key == params['event']) { - eventInfoToPlot = eventInfo['events'][key]; - eventNameToPlot = key; - } - } - } - - // Draw everything. - if (errorMessages == '') { - var plotter = new Plotter( - plotData, - dataDescriptions, - eventNameToPlot, eventInfoToPlot, - unitsX, unitsY, unitsYOther, graphsOtherStartIndex, - document.getElementById('output'), - 'lookout' in params, - !!row.stack, - rowOther && !!rowOther.stack); - - plotter.plot(); - } else { - errorMessages = '<br><br><br><table border=2 cellpadding=5><tr><td>' + - errorMessages + '</td></tr></table><br><br>'; - document.getElementById('output').innerHTML = errorMessages; - } - - if (!('lookout' in params)) { - initRevisionOptions(); - initComparisonOptions(); - } -} - -/** - * Callback for when a second *-summary.dat data file has been read, in the - * event that a second graph line is being overlayed on top of an existing - * graph line. - * - * @param {string} data The string data from the inputted text file. - * @param {string} error A string error message, in case an error occurred - * during the file read. - */ -function receivedSummaryGraphOther(data, error) { - if (error) { - reportError(error); - return; - } - - graphDataOtherRows = new Rows(data); - Fetch(escape(params.graph) + '-summary.dat', receivedSummary); -} - -/** - * Callback for when an event info file has been read. - * - * @param {string} data The string data from the inputted text file. - * @param {string} error A string error message, in case an error occurred - * during the file read. - */ -function receivedEvents(data, error) { - if (!error) - eventRows = new Rows(data); - fetchSummary(); -} - -/** - * Callback for when a graphs.dat data file has been read. - * - * @param {string} data The string data from the inputted text file. - * @param {string} error A string error message, in case an error occurred - * during the file read. - */ -function receivedGraphList(data, error) { - if (error) { - reportError(error); - return; - } - graphList = jsonToJs(data); - - if (!('graph' in params) || params.graph == '') - if (graphList.length > 0) - params.graph = graphList[0].name - - // Add a selection tab for each graph, and find the units for the selected - // one while we're at it. - tabs = []; - for (var index = 0; index < graphList.length; ++index) { - var graph = graphList[index]; - tabs.push(graph.name); - if (graph.name == params.graph) { - unitsX = graph.units_x; - unitsY = graph.units; - } - } - initPlotSwitcher(tabs); - - fetchEvents(); -} - -/** - * Starts fetching a *-summary.dat file. - */ -function fetchSummary() { - if ('graphOther' in params) { - // We need to overlay a second graph over the first one, so we need to - // fetch that summary data too. Do it first. - Fetch(escape(params.graphOther) + '-summary.dat', - receivedSummaryGraphOther); - } else { - Fetch(escape(params.graph) + '-summary.dat', - receivedSummary); - } -} - -/** - * Starts fetching an event info file. - */ -function fetchEvents() { - Fetch('_EVENT_-summary.dat', receivedEvents); -} - -/** - * Starts fetching a graphs.dat file. - */ -function fetchGraphList() { - Fetch('graphs.dat', receivedGraphList); -} - -window.addEventListener('load', fetchGraphList, false); diff --git a/chrome/test/functional/perf/endure_graphs/js/common.js b/chrome/test/functional/perf/endure_graphs/js/common.js deleted file mode 100644 index 96399dc..0000000 --- a/chrome/test/functional/perf/endure_graphs/js/common.js +++ /dev/null @@ -1,89 +0,0 @@ -/* - Copyright (c) 2012 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. -*/ - -/** - * @fileoverview Common methods for performance-plotting Javascript. - */ - -/** - * Fetches a URL asynchronously and invokes a callback when complete. - * - * @param {string} url URL to fetch. - * @param {Function(string, string)} callback The function to invoke when the - * results of the URL fetch are complete. The function should accept two - * strings representing the URL data, and any errors, respectively. - */ -function Fetch(url, callback) { - var r = new XMLHttpRequest(); - r.open('GET', url, true); - r.setRequestHeader('pragma', 'no-cache'); - r.setRequestHeader('cache-control', 'no-cache'); - - r.onreadystatechange = function() { - if (r.readyState == 4) { - var text = r.responseText; - var error; - if (r.status != 200) - error = url + ': ' + r.status + ': ' + r.statusText; - else if (!text) - error = url + ': null response'; - callback(text, error); - } - } - - r.send(null); -} - -/** - * Parses the parameters of the current page's URL. - * - * @return {Object} An object with properties given by the parameters specified - * in the URL's query string. - */ -function ParseParams() { - var result = new Object(); - - var query = window.location.search.substring(1) - if (query.charAt(query.length - 1) == '/') - query = query.substring(0, query.length - 1) // Strip trailing slash. - var s = query.split('&'); - - for (i = 0; i < s.length; ++i) { - var v = s[i].split('='); - var key = v[0]; - var value = unescape(v[1]); - result[key] = value; - } - - if ('history' in result) { - result['history'] = parseInt(result['history']); - result['history'] = Math.max(result['history'], 2); - } - if ('rev' in result) { - result['rev'] = parseInt(result['rev']); - result['rev'] = Math.max(result['rev'], -1); - } - - return result; -} - -/** - * Creates the URL constructed from the current pathname and the given params. - * - * @param {Object} An object containing parameters for a URL query string. - * @return {string} The URL constructed from the given params. - */ -function MakeURL(params) { - var url = window.location.pathname; - var sep = '?'; - for (p in params) { - if (!p) - continue; - url += sep + p + '=' + params[p]; - sep = '&'; - } - return url; -} diff --git a/chrome/test/functional/perf/endure_graphs/js/coordinates.js b/chrome/test/functional/perf/endure_graphs/js/coordinates.js deleted file mode 100644 index a1e4219..0000000 --- a/chrome/test/functional/perf/endure_graphs/js/coordinates.js +++ /dev/null @@ -1,212 +0,0 @@ -/* - Copyright (c) 2012 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. -*/ - -/** - * @fileoverview Class and functions to handle positioning of plot data points. - */ - -/** - * Class that handles plot data positioning. - * @constructor - * - * @param {Array} plotData Data that will be plotted. It is an array of lines, - * where each line is an array of points, and each point is a length-2 array - * representing an (x, y) pair. - */ -function Coordinates(plotData) { - this.plotData = plotData; - - height = window.innerHeight - 16; - width = window.innerWidth - 16; - - this.widthMax = width; - this.heightMax = Math.min(400, height - 85); - - this.processValues_('x'); - this.processValues_('y'); -} - -/** - * Determines the min/max x or y values in the plot, accounting for some extra - * buffer space. - * - * @param {string} type The type of value to process, either 'x' or 'y'. - */ -Coordinates.prototype.processValues_ = function (type) { - var merged = []; - for (var i = 0; i < this.plotData.length; i++) - for (var j = 0; j < this.plotData[i].length; j++) { - if (type == 'x') - merged.push(parseFloat(this.plotData[i][j][0])); // Index 0 is x value. - else - merged.push(parseFloat(this.plotData[i][j][1])); // Index 1 is y value. - } - - min = merged[0]; - max = merged[0]; - for (var i = 1; i < merged.length; ++i) { - if (isNaN(min) || merged[i] < min) - min = merged[i]; - if (isNaN(max) || merged[i] > max) - max = merged[i]; - } - - var bufferSpace = 0.02 * (max - min); - - if (type == 'x') { - this.xBufferSpace_ = bufferSpace; - this.xMinValue_ = min; - this.xMaxValue_ = max; - } else { - this.yBufferSpace_ = bufferSpace; - this.yMinValue_ = min; - this.yMaxValue_ = max; - } -}; - -/** - * Difference between horizontal upper and lower limit values. - * - * @return {number} The x value range. - */ -Coordinates.prototype.xValueRange = function() { - return this.xUpperLimitValue() - this.xLowerLimitValue(); -}; - -/** - * Difference between vertical upper and lower limit values. - * - * @return {number} The y value range. - */ -Coordinates.prototype.yValueRange = function() { - return this.yUpperLimitValue() - this.yLowerLimitValue(); -}; - -/** - * Converts horizontal data value to pixel value on canvas. - * - * @param {number} value The x data value. - * @return {number} The corresponding x pixel value on the canvas. - */ -Coordinates.prototype.xPixel = function(value) { - return this.widthMax * - ((value - this.xLowerLimitValue()) / this.xValueRange()); -}; - -/** - * Converts vertical data value to pixel value on canvas. - * - * @param {number} value The y data value. - * @return {number} The corresponding y pixel value on the canvas. - */ -Coordinates.prototype.yPixel = function(value) { - if (this.yValueRange() == 0) { - // Completely horizontal lines should be centered horizontally. - return this.heightMax / 2; - } else { - return this.heightMax - - (this.heightMax * - (value - this.yLowerLimitValue()) / this.yValueRange()); - } -}; - -/** - * Converts x point on canvas to data value it represents. - * - * @param {number} position The x pixel value on the canvas. - * @return {number} The corresponding x data value. - */ -Coordinates.prototype.xValue = function(position) { - return this.xLowerLimitValue() + - (position / this.widthMax * this.xValueRange()); -}; - -/** - * Converts y point on canvas to data value it represents. - * - * @param {number} position The y pixel value on the canvas. - * @return {number} The corresponding y data value. - */ -Coordinates.prototype.yValue = function(position) { - var ratio = this.heightMax / (this.heightMax - position); - return this.yLowerLimitValue() + (this.yValueRange() / ratio); -}; - -/** - * Returns the minimum x value of all the data points. - * - * @return {number} The minimum x value of all the data points. - */ -Coordinates.prototype.xMinValue = function() { - return this.xMinValue_; -}; - -/** - * Returns the maximum x value of all the data points. - * - * @return {number} The maximum x value of all the data points. - */ -Coordinates.prototype.xMaxValue = function() { - return this.xMaxValue_; -}; - -/** - * Returns the minimum y value of all the data points. - * - * @return {number} The minimum y value of all the data points. - */ -Coordinates.prototype.yMinValue = function() { - return this.yMinValue_; -}; - -/** - * Returns the maximum y value of all the data points. - * - * @return {number} The maximum y value of all the data points. - */ -Coordinates.prototype.yMaxValue = function() { - return this.yMaxValue_; -}; - -/** - * Returns the x value at the lower limit of the bounding box of the canvas. - * - * @return {number} The x value at the lower limit of the bounding box of - * the canvas. - */ -Coordinates.prototype.xLowerLimitValue = function() { - return this.xMinValue_ - this.xBufferSpace_; -}; - -/** - * Returns the x value at the upper limit of the bounding box of the canvas. - * - * @return {number} The x value at the upper limit of the bounding box of - * the canvas. - */ -Coordinates.prototype.xUpperLimitValue = function() { - return this.xMaxValue_ + this.xBufferSpace_; -}; - -/** - * Returns the y value at the lower limit of the bounding box of the canvas. - * - * @return {number} The y value at the lower limit of the bounding box of - * the canvas. - */ -Coordinates.prototype.yLowerLimitValue = function() { - return this.yMinValue_ - this.yBufferSpace_; -}; - -/** - * Returns the y value at the upper limit of the bounding box of the canvas. - * - * @return {number} The y value at the upper limit of the bounding box of - * the canvas. - */ -Coordinates.prototype.yUpperLimitValue = function() { - return this.yMaxValue_ + this.yBufferSpace_; -}; diff --git a/chrome/test/functional/perf/endure_graphs/js/dom_utils.js b/chrome/test/functional/perf/endure_graphs/js/dom_utils.js deleted file mode 100644 index 09158c3..0000000 --- a/chrome/test/functional/perf/endure_graphs/js/dom_utils.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright (c) 2012 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. -*/ - -/** - * @fileoverview Collection of functions which operate on DOM. - */ - -var domUtils = window['domUtils'] || {}; - -/** - * Returns pageX and pageY of the given element. - * - * @param {Element} element An element of which the top-left position is to be - * returned in the coordinate system of the document page. - * @return {Object} A point object which has {@code x} and {@code y} fields. - */ -domUtils.pageXY = function(element) { - var x = 0, y = 0; - for (; element; element = element.offsetParent) { - x += element.offsetLeft; - y += element.offsetTop; - } - return {'x': x, 'y': y}; -}; - -/** - * Returns pageX and pageY of the given event. - * - * @param {Event} event An event of which the position is to be returned in - * the coordinate system of the document page. - * @return {Object} A point object which has {@code x} and {@code y} fields. - */ -domUtils.pageXYOfEvent = function(event) { - return (event.pageX != null && event.pageY != null) ? - {'x': event.pageX, 'y': event.pageY} : - {'x': event.clientX + document.body.scrollLeft + - document.documentElement.scrollLeft, - 'y': event.clientY + document.body.scrollTop + - document.documentElement.scrollTop}; -}; diff --git a/chrome/test/functional/perf/endure_graphs/js/graph_utils.js b/chrome/test/functional/perf/endure_graphs/js/graph_utils.js deleted file mode 100644 index ad5c83d..0000000 --- a/chrome/test/functional/perf/endure_graphs/js/graph_utils.js +++ /dev/null @@ -1,141 +0,0 @@ -/* - Copyright (c) 2012 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. -*/ - -/** - * @fileoverview Collection of functions which operate on graph data. - */ - -var graphUtils = window['graphUtils'] || {}; - -/** - * Interpolate given multiple lines of graphs, and returns the lines of - * the graphs where each line has the same number of points and x coordinates. - * - * For example, - * <pre> - * [[[0, 1], [2, 3]], // 1st line - * [[1, 3]]] // 2nd line - * </pre> - * will be converted to - * <pre> - * [[[0, 1], [1, 2], [2, 3]], // [1, 2] is interpolated. - * [[0, 0], [1, 3], [2, 0]]] // [0, 0] and [2, 0] are interpolated. - * </pre> - * where every line has points at x=0, 1 and 2. - * Interpolated data points are marked with a property - * {@code point.interpolated == true}. - * - * @param {Array.<Array.<Array.<number>>>} plotData List of arrays that - * represent individual lines. The line itself is an Array of points. - * @return {Array.<Array.<Array.<number>>>} An interpolated {@code plotData}. - * The original {@code plotData} is not affected. - */ -graphUtils.interpolate = function(plotData) { - var interpolated = []; // resulting interpolated {@code plotData} - var unconsumed = []; // indices to unconsumed points in {@code plotData} - for (var i = 0; i < plotData.length; ++i) { - interpolated.push([]); - unconsumed.push(0); - } - - // Returns the next x-coordinate to interpolate if any, or null. - function nextX() { - var index = null; - for (var i = 0; i < unconsumed.length; ++i) { - if (0 <= unconsumed[i] && unconsumed[i] < plotData[i].length && - (index == null || - plotData[i][unconsumed[i]][0] < - plotData[index][unconsumed[index]][0])) { - index = i; - } - } - return index == null ? null : plotData[index][unconsumed[index]][0]; - } - - for (var x = nextX(); x != null; x = nextX()) { // for all x - for (var i = 0; i < plotData.length; ++i) { // for all lines - var y = 0; - var hasPoint = false; - if (0 <= unconsumed[i] && unconsumed[i] < plotData[i].length) { - var p = plotData[i][unconsumed[i]]; - if (p[0] <= x) { - y = p[1]; // The original line has a point at x. - hasPoint = true; - } else if (unconsumed[i] == 0) { - y = 0; // y = 0 before the first point - } else { - // Interpolate a point. - var p0 = plotData[i][unconsumed[i] - 1]; - y = (x - p0[0]) / (p[0] - p0[0]) * (p[1] - p0[1]) + p0[1]; - } - } // else y = 0 because it's out of range. - - var point = [x, y]; - if (!hasPoint) { - point.interpolated = true; - } - interpolated[i].push(point); - } - - // Consume {@code plotData} by incrementing indices in {@code unconsumed}. - for (var i = 0; i < unconsumed.length; ++i) { - if (0 <= unconsumed[i] && unconsumed[i] < plotData[i].length && - plotData[i][unconsumed[i]][0] <= x) { - ++unconsumed[i]; - } - } - } - - return interpolated; -}; - -/** - * Creates and returns a set of stacked graphs, assuming the given - * {@code plotData} is interpolated by {@code graphUtils.interpolate}. - * - * For example, - * <pre> - * [[[0, 1], [1, 2]], // 1st line - * [[0, 1], [1, 3]], // 2nd line - * [[0, 2], [1, 1]]] // 3rd line - * </pre> - * will be converted to - * <pre> - * [[[0, 1], [1, 2]], // 1st - * [[0, 2], [1, 5]], // 1st + 2nd - * [[0, 4], [1, 6]]] // 1st + 2nd + 3rd - * </pre> - * - * @param {Array.<Array.<Array.<number>>>} plotData List of arrays that - * represent individual lines. The line itself is an Array of points. - * @return {Array.<Array.<Array.<number>>>} A stacked {@code plotData}. - * The original {@code plotData} is not affected. - */ -graphUtils.stackFrontToBack = function(plotData) { - if (!(plotData && plotData[0] && plotData[0].length > 0)) { - return []; - } - - var stacked = []; - for (var i = 0; i < plotData.length; ++i) { - stacked.push([]); - } - - for (var j = 0; j < plotData[0].length; ++j) { - for (var i = 0; i < plotData.length; ++i) { - var point = [ - plotData[i][j][0], - plotData[i][j][1] + - (i == 0 ? 0 : stacked[i - 1][j][1])]; - if (plotData[i][j].interpolated) { - point.interpolated = true; - } - stacked[i].push(point); - } - } - - return stacked; -}; diff --git a/chrome/test/functional/perf/endure_graphs/js/plotter.js b/chrome/test/functional/perf/endure_graphs/js/plotter.js deleted file mode 100644 index edbbf11..0000000 --- a/chrome/test/functional/perf/endure_graphs/js/plotter.js +++ /dev/null @@ -1,1199 +0,0 @@ -/* - Copyright (c) 2012 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. -*/ - -/** - * @fileoverview Collection of functions and classes used to plot data in a - * <canvas>. Create a Plotter() to generate a plot. - */ - -/** - * Adds commas to a given number. - * - * Examples: - * 1234.56 => "1,234.56" - * 99999 => "99,999" - * - * @param {string|number} number The number to format. - * @return {string} String representation of |number| with commas for every - * three digits to the left of a decimal point. - */ -function addCommas(number) { - number += ''; // Convert number to string if not already a string. - var numberParts = number.split('.'); - var integralPart = numberParts[0]; - var fractionalPart = numberParts.length > 1 ? '.' + numberParts[1] : ''; - var reThreeDigits = /(\d+)(\d{3})/; - while (reThreeDigits.test(integralPart)) - integralPart = integralPart.replace(reThreeDigits, '$1' + ',' + '$2'); - return integralPart + fractionalPart; -} - -/** - * Vertical marker to highlight data points that are being hovered over by the - * mouse. - * - * @param {string} color The color to make the marker, e.g., 'rgb(100,80,240)'. - * @return {Element} A div Element object representing the vertical marker. - */ -function VerticalMarker(color) { - var m = document.createElement('div'); - m.style.backgroundColor = color; - m.style.opacity = '0.3'; - m.style.position = 'absolute'; - m.style.left = '-2px'; - m.style.top = '-2px'; - m.style.width = '0px'; - m.style.height = '0px'; - return m; -} - -/** - * Class representing a horizontal marker at the indicated mouse location. - * @constructor - * - * @param {Element} canvasElement The canvas bounds. - * @param {Number} yValue The data value corresponding to the vertical click - * location. - * @param {Number} yOtherValue If the plot is overlaying two coordinate systems, - * this is the data value corresponding to the vertical click location in - * the second coordinate system. Can be null. - */ -function HorizontalMarker(canvasElement, yValue, yOtherValue) { - var m = document.createElement('div'); - m.style.backgroundColor = HorizontalMarker.COLOR; - m.style.opacity = '0.3'; - m.style.position = 'absolute'; - m.style.width = canvasElement.offsetWidth + 'px'; - m.style.height = HorizontalMarker.HEIGHT + 'px'; - - this.markerDiv = m; - this.value = yValue; - this.otherValue = yOtherValue; -} - -HorizontalMarker.HEIGHT = 5; -HorizontalMarker.COLOR = 'rgb(0,100,100)'; - -/** - * Locates this element at a specified position. - * - * @param {Element} canvasElement The canvas element at which this element is - * to be placed. - * @param {number} y Y position relative to the canvas element. - */ -HorizontalMarker.prototype.locateAt = function(canvasElement, y) { - var div = this.markerDiv; - div.style.left = domUtils.pageXY(canvasElement).x - - domUtils.pageXY(div.offsetParent) + 'px'; - div.style.top = (y + domUtils.pageXY(canvasElement).y - - domUtils.pageXY(div.offsetParent).y - - (HorizontalMarker.HEIGHT / 2)) + 'px'; -}; - -/** - * Removes the horizontal marker from the graph. - */ -HorizontalMarker.prototype.remove = function() { - this.markerDiv.parentNode.removeChild(this.markerDiv); -}; - -/** - * An information indicator hovering around the mouse cursor on the graph. - * This class is used to show a legend near the mouse cursor. - * - * A set of legends under the graph is managed separately in - * {@code Plotter.createLegendsSummaryElement_}. - * - * @constructor - */ -function HoveringInfo() { - this.containerDiv_ = document.createElement('div'); - this.containerDiv_.style.display = 'none'; - this.containerDiv_.style.position = 'absolute'; - this.containerDiv_.style.border = '1px solid #000'; - this.containerDiv_.style.padding = '0.12em'; - this.containerDiv_.style.backgroundColor = '#ddd'; - this.colorIndicator_ = document.createElement('div'); - this.colorIndicator_.style.display = 'inline-block'; - this.colorIndicator_.style.width = '1em'; - this.colorIndicator_.style.height = '1em'; - this.colorIndicator_.style.verticalAlign = 'text-bottom'; - this.colorIndicator_.style.margin = '0 0.24em 0 0'; - this.colorIndicator_.style.border = '1px solid #000'; - this.legendText_ = document.createElement('span'); - this.itemValueText_ = document.createElement('span'); - - this.containerDiv_.appendChild(this.colorIndicator_); - this.containerDiv_.appendChild(this.legendText_); - var div = document.createElement('div'); - div.appendChild(this.itemValueText_); - this.containerDiv_.appendChild(div); -} - -/** - * Returns the container element; - * - * @return {Element} The container element. - */ -HoveringInfo.prototype.getElement = function() { - return this.containerDiv_; -}; - -/** - * Shows or hides the element. - * - * @param {boolean} show Shows the element if true, or hides it. - */ -HoveringInfo.prototype.show = function(show) { - this.containerDiv_.style.display = show ? 'block' : 'none'; -}; - -/** - * Returns the position of the container element in the page coordinate. - * - * @return {Object} A point object which has {@code x} and {@code y} fields. - */ -HoveringInfo.prototype.pageXY = function() { - return domUtils.pageXY(this.containerDiv_); -}; - -/** - * Locates the element at the specified position. - * - * @param {number} x X position in the page coordinate. - * @param {number} y Y position in the page coordinate. - */ -HoveringInfo.prototype.locateAtPageXY = function(x, y) { - var parentXY = domUtils.pageXY(this.containerDiv_.offsetParent); - this.containerDiv_.style.left = x - parentXY.x + 'px'; - this.containerDiv_.style.top = y - parentXY.y + 'px'; -}; - -/** - * Returns the legend text. - * - * @return {?string} The legend text. - */ -HoveringInfo.prototype.getLegendText = function() { - return this.legendText_.textContent; -}; - -/** - * Changes the legend text. - * - * @param {string} text The new text to be set. - */ -HoveringInfo.prototype.setLegendText = function(text) { - this.legendText_.textContent = text; -}; - -/** - * Changes the item value. - * - * @param {number} value The new value to be shown. - */ -HoveringInfo.prototype.setItemValue = function(value) { - this.itemValueText_.textContent = 'Item value = ' + addCommas(value); -}; - -/** - * Changes the color of the color indicator. - * - * @param {string} color The new color to be set. - */ -HoveringInfo.prototype.setColorIndicator = function(color) { - this.colorIndicator_.style.backgroundColor = color; -}; - -/** - * Main class that does the actual plotting. - * - * Draws a chart using a canvas element. Takes an array of lines to draw. - * @constructor - * - * @param {Array} plotData list of arrays that represent individual lines. The - * line itself is an Array of points. - * @param {Array} dataDescriptions list of data descriptions for each line in - * |plotData|. - * @param {string} eventName The string name of an event to overlay on the - * graph. Should be 'null' if there are no events to overlay. - * @param {Object} eventInfo If |eventName| is specified, an array of event - * points to overlay on the graph. Each event point in the array is itself - * a 2-element array, where the first element is the x-axis value at which - * the event occurred during the test, and the second element is a - * dictionary of kay/value pairs representing metadata associated with the - * event. - * @param {string} unitsX The x-axis units of the data being plotted. - * @param {string} unitsY The y-axis units of the data being plotted. - * @param {string} unitsYOther If another graph (with different y-axis units) is - * being overlayed over the first graph, this represents the units of the - * other graph. Otherwise, this should be 'null'. - * @param {?number} graphsOtherStartIndex Specifies the starting index of - * the second set of lines. {@code plotData} in the range of - * [0, {@code graphsOtherStartIndex}) are treated as the first set of lines, - * and ones in the range of - * [{@code graphsOtherStartIndex}, {@code plotData.length}) are as - * the second set. 0, {@code plotData.length} and {@code null} mean - * no second set, i.e. all the data in {@code plotData} represent the single - * set of lines. - * @param {Element} resultNode A DOM Element object representing the DOM node to - * which the plot should be attached. - * @param {boolean} is_lookout Whether or not the graph should be drawn - * in 'lookout' mode, which is a summarized view that is made for overview - * pages when the graph is drawn in a more confined space. - * @param {boolean} stackedGraph Whether or not the first set of lines is - * a stacked graph. - * @param {boolean} stackedGraphOther Whether or not the second set of lines is - * a stacked graph. - * - * Example of the |plotData|: - * [ - * [line 1 data], - * [line 2 data] - * ]. - * Line data looks like [[point one], [point two]]. - * And individual points are [x value, y value] - */ -function Plotter(plotData, dataDescriptions, eventName, eventInfo, unitsX, - unitsY, unitsYOther, graphsOtherStartIndex, resultNode, - is_lookout, stackedGraph, stackedGraphOther) { - this.plotData_ = plotData; - this.dataDescriptions_ = dataDescriptions; - this.eventName_ = eventName; - this.eventInfo_ = eventInfo; - this.unitsX_ = unitsX; - this.unitsY_ = unitsY; - this.unitsYOther_ = unitsYOther; - this.graphsOtherStartIndex_ = - (0 < graphsOtherStartIndex && graphsOtherStartIndex < plotData.length) ? - graphsOtherStartIndex : null; - this.resultNode_ = resultNode; - this.is_lookout_ = is_lookout; - this.stackedGraph_ = stackedGraph; - this.stackedGraphOther_ = stackedGraphOther; - - this.dataColors_ = []; - - this.coordinates = null; - this.coordinatesOther = null; - if (this.unitsYOther_ && this.graphsOtherStartIndex_) { - // Need two different coordinate systems to overlay on the same graph. - this.coordinates = new Coordinates( - this.plotData_.slice(0, this.graphsOtherStartIndex_)); - this.coordinatesOther = new Coordinates( - this.plotData_.slice(this.graphsOtherStartIndex_)); - } else { - this.coordinates = new Coordinates(this.plotData_); - } - - // A color palette that's unambigous for normal and color-deficient viewers. - // Values are (red, green, blue) on a scale of 255. - // Taken from http://jfly.iam.u-tokyo.ac.jp/html/manuals/pdf/color_blind.pdf. - this.colors = [[0, 114, 178], // Blue. - [230, 159, 0], // Orange. - [0, 158, 115], // Green. - [204, 121, 167], // Purplish pink. - [86, 180, 233], // Sky blue. - [213, 94, 0], // Dark orange. - [0, 0, 0], // Black. - [240, 228, 66] // Yellow. - ]; - - for (var i = 0, colorIndex = 0; i < this.dataDescriptions_.length; ++i) - this.dataColors_[i] = this.makeColor(colorIndex++); -} - -/** - * Generates a string representing a color corresponding to the given index - * in a color array. Handles wrapping around the color array if necessary. - * - * @param {number} i An index into the |this.colors| array. - * @return {string} A string representing a color in 'rgb(X,Y,Z)' format. - */ -Plotter.prototype.makeColor = function(i) { - var index = i % this.colors.length; - return 'rgb(' + this.colors[index][0] + ',' + - this.colors[index][1] + ',' + - this.colors[index][2] + ')'; -}; - -/** - * Same as function makeColor above, but also takes a transparency value - * indicating how transparent to make the color appear. - * - * @param {number} i An index into the |this.colors| array. - * @param {number} transparencyPercent Percentage transparency to make the - * color, e.g., 0.75. - * @return {string} A string representing a color in 'rgb(X,Y,Z,A)' format, - * where A is the percentage transparency. - */ -Plotter.prototype.makeColorTransparent = function(i, transparencyPercent) { - var index = i % this.colors.length; - return 'rgba(' + this.colors[index][0] + ',' + - this.colors[index][1] + ',' + - this.colors[index][2] + ',' + transparencyPercent + ')'; -}; - -/** - * Gets the data color value associated with a specified color index. - * - * @param {number} i An index into the |this.colors| array. - * @return {string} A string representing a color in 'rgb(X,Y,Z,A)' format, - * where A is the percentage transparency. - */ -Plotter.prototype.getDataColor = function(i) { - if (this.dataColors_[i]) - return this.dataColors_[i]; - else - return this.makeColor(i); -}; - -/** - * Gets the fill color value associated with a specified color index. - * - * @param {number} i An index into the |this.colors| array. - * @return {string} A string representing a color in 'rgba(R,G,B,A)' format, - * where A is the percentage transparency. - */ -Plotter.prototype.getFillColor = function(i) { - return this.makeColorTransparent(i, 0.4); -}; - -/** - * Does the actual plotting. - */ -Plotter.prototype.plot = function() { - var self = this; - - this.canvasElement_ = this.canvas_(); - this.rulerDiv_ = this.ruler_(); - - // Markers for the result point(s)/events that the mouse is currently - // hovering over. - this.cursorDiv_ = new VerticalMarker('rgb(100,80,240)'); - this.cursorDivOther_ = new VerticalMarker('rgb(50,50,50)'); - this.eventDiv_ = new VerticalMarker('rgb(255, 0, 0)'); - this.hoveringInfo_ = new HoveringInfo(); - - this.resultNode_.appendChild(this.canvasElement_); - this.resultNode_.appendChild(this.coordinates_()); - this.resultNode_.appendChild(this.rulerDiv_); - this.resultNode_.appendChild(this.cursorDiv_); - this.resultNode_.appendChild(this.cursorDivOther_); - this.resultNode_.appendChild(this.eventDiv_); - this.resultNode_.appendChild(this.hoveringInfo_.getElement()); - this.attachEventListeners_(); - - // Now draw the canvas. - var ctx = this.canvasElement_.getContext('2d'); - - // Clear it with white: otherwise canvas will draw on top of existing data. - ctx.clearRect(0, 0, this.canvasElement_.width, this.canvasElement_.height); - - // Draw all data lines in the reverse order so the last graph appears on - // the backmost and the first graph appears on the frontmost. - function draw(plotData, coordinates, colorOffset, stack) { - for (var i = plotData.length - 1; i >= 0; --i) { - if (stack) { - self.plotAreaUnderLine_(ctx, self.getFillColor(colorOffset + i), - plotData[i], coordinates); - } - self.plotLine_(ctx, self.getDataColor(colorOffset + i), - plotData[i], coordinates); - } - } - draw(this.plotData_.slice(0, - this.graphsOtherStartIndex_ ? - this.graphsOtherStartIndex_ : - this.plotData_.length), - this.coordinates, 0, this.stackedGraph_); - if (this.graphsOtherStartIndex_) { - draw(this.plotData_.slice(this.graphsOtherStartIndex_), - this.unitsYOther_ ? this.coordinatesOther : this.coordinates, - this.graphsOtherStartIndex_, this.stackedGraphOther_); - } - - // Draw events overlayed on graph if needed. - if (this.eventName_ && this.eventInfo_) - this.plotEvents_(ctx, 'rgb(255, 150, 150)', this.coordinates); - - this.graduation_divs_ = this.graduations_(this.coordinates, 0, false); - if (this.unitsYOther_) { - this.graduation_divs_ = this.graduation_divs_.concat( - this.graduations_(this.coordinatesOther, 1, true)); - } - for (var i = 0; i < this.graduation_divs_.length; ++i) - this.resultNode_.appendChild(this.graduation_divs_[i]); -}; - -/** - * Draws events overlayed on top of an existing graph. - * - * @param {Object} ctx A canvas element object for drawing. - * @param {string} strokeStyles A string representing the drawing style. - * @param {Object} coordinateSystem A Coordinates object representing the - * coordinate system of the graph. - */ -Plotter.prototype.plotEvents_ = function(ctx, strokeStyles, coordinateSystem) { - ctx.strokeStyle = strokeStyles; - ctx.fillStyle = strokeStyles; - ctx.lineWidth = 1.0; - - ctx.beginPath(); - var data = this.eventInfo_; - for (var index = 0; index < data.length; ++index) { - var event_time = data[index][0]; - var x = coordinateSystem.xPixel(event_time); - ctx.moveTo(x, 0); - ctx.lineTo(x, this.canvasElement_.offsetHeight); - } - ctx.closePath(); - ctx.stroke(); -}; - -/** - * Draws a line on the graph. - * - * @param {Object} ctx A canvas element object for drawing. - * @param {string} strokeStyles A string representing the drawing style. - * @param {Array} data A list of [x, y] values representing the line to plot. - * @param {Object} coordinateSystem A Coordinates object representing the - * coordinate system of the graph. - */ -Plotter.prototype.plotLine_ = function(ctx, strokeStyles, data, - coordinateSystem) { - ctx.strokeStyle = strokeStyles; - ctx.fillStyle = strokeStyles; - ctx.lineWidth = 2.0; - - ctx.beginPath(); - var initial = true; - var allPoints = []; - for (var i = 0; i < data.length; ++i) { - var pointX = parseFloat(data[i][0]); - var pointY = parseFloat(data[i][1]); - var x = coordinateSystem.xPixel(pointX); - var y = coordinateSystem.yPixel(0); - if (isNaN(pointY)) { - // Re-set 'initial' if we're at a gap in the data. - initial = true; - } else { - y = coordinateSystem.yPixel(pointY); - if (initial) - initial = false; - else - ctx.lineTo(x, y); - } - - ctx.moveTo(x, y); - if (!data[i].interpolated) { - allPoints.push([x, y]); - } - } - ctx.closePath(); - ctx.stroke(); - - if (!this.is_lookout_) { - // Draw a small dot at each point. - for (var i = 0; i < allPoints.length; ++i) { - ctx.beginPath(); - ctx.arc(allPoints[i][0], allPoints[i][1], 3, 0, Math.PI*2, true); - ctx.fill(); - } - } -}; - -/** - * Fills an area under the given line on the graph. - * - * @param {Object} ctx A canvas element object for drawing. - * @param {string} fillStyle A string representing the drawing style. - * @param {Array} data A list of [x, y] values representing the line to plot. - * @param {Object} coordinateSystem A Coordinates object representing the - * coordinate system of the graph. - */ -Plotter.prototype.plotAreaUnderLine_ = function(ctx, fillStyle, data, - coordinateSystem) { - if (!data[0]) { - return; // nothing to draw - } - - ctx.beginPath(); - var x = coordinateSystem.xPixel(parseFloat(data[0][0]) || 0); - var y = coordinateSystem.yPixel(parseFloat(data[0][1]) || 0); - var y0 = coordinateSystem.yPixel(coordinateSystem.yMinValue()); - ctx.moveTo(x, y0); - for (var point, i = 0; point = data[i]; ++i) { - var pointX = parseFloat(point[0]); - var pointY = parseFloat(point[1]); - if (isNaN(pointX)) { continue; } // Skip an invalid point. - if (isNaN(pointY)) { - ctx.lineTo(x, y0); - var yWasNaN = true; - } else { - x = coordinateSystem.xPixel(pointX); - y = coordinateSystem.yPixel(pointY); - if (yWasNaN) { - ctx.lineTo(x, y0); - yWasNaN = false; - } - ctx.lineTo(x, y); - } - } - ctx.lineTo(x, y0); - - ctx.lineWidth = 0; - // Clear the area with white color first. - var COLOR_WHITE = 'rgb(255,255,255)'; - ctx.strokeStyle = COLOR_WHITE; - ctx.fillStyle = COLOR_WHITE; - ctx.fill(); - // Then, fill the area with the specified color. - ctx.strokeStyle = fillStyle; - ctx.fillStyle = fillStyle; - ctx.fill(); -}; - -/** - * Attaches event listeners to DOM nodes. - */ -Plotter.prototype.attachEventListeners_ = function() { - var self = this; - this.canvasElement_.parentNode.addEventListener( - 'mousemove', function(evt) { self.onMouseMove_(evt); }, false); - this.canvasElement_.parentNode.addEventListener( - 'mouseover', function(evt) { self.onMouseOver_(evt); }, false); - this.canvasElement_.parentNode.addEventListener( - 'mouseout', function(evt) { self.onMouseOut_(evt); }, false); - this.cursorDiv_.addEventListener( - 'click', function(evt) { self.onMouseClick_(evt); }, false); - this.cursorDivOther_.addEventListener( - 'click', function(evt) { self.onMouseClick_(evt); }, false); - this.eventDiv_.addEventListener( - 'click', function(evt) { self.onMouseClick_(evt); }, false); -}; - -/** - * Update the horizontal line that is following where the mouse is hovering. - * - * @param {Object} evt A mouse event object representing a mouse move event. - */ -Plotter.prototype.updateRuler_ = function(evt) { - var r = this.rulerDiv_; - r.style.left = this.canvasElement_.offsetLeft + 'px'; - r.style.top = this.canvasElement_.offsetTop + 'px'; - r.style.width = this.canvasElement_.offsetWidth + 'px'; - var h = domUtils.pageXYOfEvent(evt).y - - domUtils.pageXY(this.canvasElement_).y; - if (h > this.canvasElement_.offsetHeight) - h = this.canvasElement_.offsetHeight; - r.style.height = h + 'px'; -}; - -/** - * Update the highlighted data point at the x value that the mouse is hovering - * over. - * - * @param {Object} coordinateSystem A Coordinates object representing the - * coordinate system of the graph. - * @param {number} currentIndex The index into the |this.plotData| array of the - * data point being hovered over, for a given line. - * @param {Object} cursorDiv A DOM element div object representing the highlight - * itself. - * @param {number} dataIndex The index into the |this.plotData| array of the - * line being hovered over. - */ -Plotter.prototype.updateCursor_ = function(coordinateSystem, currentIndex, - cursorDiv, dataIndex) { - var c = cursorDiv; - c.style.top = this.canvasElement_.offsetTop + 'px'; - c.style.height = this.canvasElement_.offsetHeight + 'px'; - - // Left point is half-way to the previous x value, unless it's the first - // point, in which case it's the x value of the current point. - var leftPoint = null; - if (currentIndex == 0) { - leftPoint = this.canvasElement_.offsetLeft + - coordinateSystem.xPixel(this.plotData_[dataIndex][0][0]); - } - else { - var left_x = this.canvasElement_.offsetLeft + - coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex - 1][0]); - var curr_x = this.canvasElement_.offsetLeft + - coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex][0]); - leftPoint = (left_x + curr_x) / 2; - } - c.style.left = leftPoint; - - // Width is half-way to the next x value minus the left point, unless it's - // the last point, in which case it's the x value of the current point minus - // the left point. - if (currentIndex == this.plotData_[dataIndex].length - 1) { - var curr_x = this.canvasElement_.offsetLeft + - coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex][0]); - c.style.width = curr_x - leftPoint; - } - else { - var next_x = this.canvasElement_.offsetLeft + - coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex + 1][0]); - var curr_x = this.canvasElement_.offsetLeft + - coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex][0]); - c.style.width = ((next_x + curr_x) / 2) - leftPoint; - } -}; - -/** - * Update the highlighted event at the x value that the mouse is hovering over. - * - * @param {number} x The x-value (pixel) at which to draw the event highlight - * div. - * @param {boolean} show Whether or not to show the highlight div. - */ -Plotter.prototype.updateEventDiv_ = function(x, show) { - var c = this.eventDiv_; - c.style.top = this.canvasElement_.offsetTop + 'px'; - c.style.height = this.canvasElement_.offsetHeight + 'px'; - - if (show) { - c.style.left = this.canvasElement_.offsetLeft + (x - 2); - c.style.width = 8; - } else { - c.style.width = 0; - } -}; - -/** - * Updates the hovering information. - * - * @param {Event} evt An event object, which specifies the position of the mouse - * cursor. - * @param {boolean} show Whether or not to show the hovering info. Even if it's - * true, if the cursor position is out of the appropriate area, nothing will - * be shown. - */ -Plotter.prototype.updateHoveringInfo_ = function(evt, show) { - var evtPageXY = domUtils.pageXYOfEvent(evt); - var hoveringInfoPageXY = this.hoveringInfo_.pageXY(); - var canvasPageXY = domUtils.pageXY(this.canvasElement_); - - var coord = this.coordinates; - // p = the mouse cursor position in value coordinates. - var p = {'x': coord.xValue(evtPageXY.x - canvasPageXY.x), - 'y': coord.yValue(evtPageXY.y - canvasPageXY.y)}; - if (!show || - !(this.stackedGraph_ || this.stackedGraphOther_) || - p.x < coord.xMinValue() || coord.xMaxValue() < p.x || - p.y < coord.yMinValue() || coord.yMaxValue() < p.y) { - this.hoveringInfo_.show(false); - return; - } else { - this.hoveringInfo_.show(true); - } - - /** - * Finds the closest lines (upside and downside of the cursor position). - * Returns a set of upside/downside line indices and point index on success - * or null. - */ - function findClosestLines(lines, opt_startIndex, opt_endIndex) { - var offsetIndex = opt_startIndex || 0; - lines = - opt_endIndex != null ? lines.slice(offsetIndex, opt_endIndex) : - opt_startIndex != null ? lines.slice(offsetIndex) : - lines; - - var upsideClosestLineIndex = null; - var upsideClosestYDistance = coord.yValueRange(); - var downsideClosestLineIndex = null; - var downsideClosestYDistance = coord.yValueRange(); - var upsideClosestPointIndex = null; - - for (var lineIndex = 0, line; line = lines[lineIndex]; ++lineIndex) { - for (var i = 1; line[i]; ++i) { - var p0 = line[i - 1], p1 = line[i]; - if (p0[0] <= p.x && p.x < p1[0]) { - // Calculate y-value of the line at p.x, which is the cursor point. - var y = (p.x - p0[0]) / (p1[0] - p0[0]) * (p1[1] - p0[1]) + p0[1]; - if (p.y < y && y - p.y < upsideClosestYDistance) { - upsideClosestLineIndex = lineIndex; - upsideClosestYDistance = y - p.y; - - if (p.x - p0[0] < p1[0] - p.x) { - upsideClosestPointIndex = i - 1; - } else { - upsideClosestPointIndex = i; - } - } else if (y <= p.y && p.y - y < downsideClosestYDistance) { - downsideClosestLineIndex = lineIndex; - downsideClosestYDistance = p.y - y; - } - break; - } - } - } - - return (upsideClosestLineIndex != null && - upsideClosestPointIndex != null) ? - {'upsideLineIndex': offsetIndex + upsideClosestLineIndex, - 'downsideLineIndex': downsideClosestYDistance == null ? null : - offsetIndex + downsideClosestLineIndex, - 'upsidePointIndex': offsetIndex + upsideClosestPointIndex} : - null; - } - - // Find the closest lines above and below the mouse cursor. - var closest = null; - // Since the other set of graphs are drawn over the first set, try to find - // the closest lines from the other set of graphs first. - if (this.graphsOtherStartIndex_ && this.stackedGraphOther_) { - closest = findClosestLines(this.plotData_, this.graphsOtherStartIndex_); - } - if (!closest && this.stackedGraph_) { - closest = this.graphsOtherStartIndex_ ? - findClosestLines(this.plotData_, 0, this.graphsOtherStartIndex_) : - findClosestLines(this.plotData_); - } - if (!closest) { - this.hoveringInfo_.show(false); - return; - } - - // Update the contents of the hovering info box. - // Color indicator, description and the value of the item. - this.hoveringInfo_.setColorIndicator( - this.getDataColor(closest.upsideLineIndex)); - this.hoveringInfo_.setLegendText( - this.dataDescriptions_[closest.upsideLineIndex]); - var y1 = this.plotData_[closest.upsideLineIndex][closest.upsidePointIndex][1]; - var y0 = closest.downsideLineIndex == null ? - 0 : - this.plotData_[closest.downsideLineIndex][closest.upsidePointIndex][1]; - this.hoveringInfo_.setItemValue(y1 - y0); - - // Locate the hovering info box near the mouse cursor. - var DIV_X_OFFSET = 10, DIV_Y_OFFSET = -20; - if (evtPageXY.x + this.hoveringInfo_.getElement().offsetWidth < - canvasPageXY.x + this.canvasElement_.offsetWidth) { - this.hoveringInfo_.locateAtPageXY(evtPageXY.x + DIV_X_OFFSET, - evtPageXY.y + DIV_Y_OFFSET); - } else { // If lacking space at the right side, locate it at the left side. - this.hoveringInfo_.locateAtPageXY( - evtPageXY.x - this.hoveringInfo_.getElement().offsetWidth - DIV_X_OFFSET, - evtPageXY.y + DIV_Y_OFFSET); - } -}; - -/** - * Handle a mouse move event. - * - * @param {Object} evt A mouse event object representing a mouse move event. - */ -Plotter.prototype.onMouseMove_ = function(evt) { - var self = this; - - var canvas = evt.currentTarget.firstChild; - var evtPageXY = domUtils.pageXYOfEvent(evt); - var canvasPageXY = domUtils.pageXY(this.canvasElement_); - var positionX = evtPageXY.x - canvasPageXY.x; - var positionY = evtPageXY.y - canvasPageXY.y; - - // Identify the index of the x value that is closest to the mouse x value. - var xValue = this.coordinates.xValue(positionX); - var lineIndex = !this.stackedGraph_ ? 0 : - this.graphsOtherStartIndex_ ? this.graphsOtherStartIndex_ - 1 : - this.plotData_.length - 1; - var line = this.plotData_[lineIndex]; - var min_diff = Math.abs(line[0][0] - xValue); - indexValueX = 0; - for (var i = 1; i < line.length; ++i) { - var diff = Math.abs(line[i][0] - xValue); - if (diff < min_diff) { - min_diff = diff; - indexValueX = i; - } - } - - // Identify the index of the x value closest to the mouse x value for the - // other graph being overlayed on top of the original graph, if one exists. - if (this.unitsYOther_) { - var xValue = this.coordinatesOther.xValue(positionX); - var lineIndexOther = !this.stackedGraphOther_ ? - this.graphsOtherStartIndex_ : this.plotData_.length - 1; - var lineOther = this.plotData_[lineIndexOther]; - var min_diff = Math.abs(lineOther[0][0] - xValue); - var indexValueXOther = 0; - for (var i = 1; i < lineOther.length; ++i) { - var diff = Math.abs(lineOther[i][0] - xValue); - if (diff < min_diff) { - min_diff = diff; - indexValueXOther = i; - } - } - } - - // Update coordinate information displayed directly underneath the graph. - function legendLabel(lineIndex, opt_labelText) { - return '<span style="color:' + self.getDataColor(lineIndex) + '">' + - (opt_labelText || self.dataDescriptions_[lineIndex]) + - '</span>: '; - } - function valuesAtCursor(lineIndex, pointIndex, unitsY, yValue) { - return '<span style="color:' + self.getDataColor(lineIndex) + '">' + - self.plotData_[lineIndex][pointIndex][0] + ' ' + self.unitsX_ + ': ' + - addCommas(self.plotData_[lineIndex][pointIndex][1].toFixed(2)) + ' ' + - unitsY + '</span> [hovering at ' + addCommas(yValue.toFixed(2)) + - ' ' + unitsY + ']'; - } - - this.infoBox_.rows[0].label.innerHTML = legendLabel(lineIndex); - this.infoBox_.rows[0].content.innerHTML = valuesAtCursor( - lineIndex, indexValueX, this.unitsY_, this.coordinates.yValue(positionY)); - var row = this.infoBox_.rows[1]; - if (this.unitsYOther_) { - row.label.innerHTML = legendLabel(lineIndexOther); - row.content.innerHTML = valuesAtCursor( - lineIndexOther, indexValueXOther, this.unitsYOther_, - this.coordinatesOther.yValue(positionY)); - } else if (this.graphsOtherStartIndex_) { - row.label.innerHTML = legendLabel( - this.stackedGraphOther_ ? - this.plotData_.length - 1 : this.graphsOtherStartIndex_); - row.content.innerHTML = valuesAtCursor( - this.stackedGraphOther_ ? - this.plotData_.length - 1 : this.graphsOtherStartIndex_, - indexValueX, this.unitsY_, this.coordinates.yValue(positionY)); - } else if (!this.stackedGraph_ && this.dataDescriptions_.length > 1) { - row.label.innerHTML = legendLabel(1); - row.content.innerHTML = valuesAtCursor( - 1, indexValueX, this.unitsY_, this.coordinates.yValue(positionY)); - } else if (row) { - row.label.innerHTML = ''; - row.content.innerHTML = ''; - } - - // If there is a horizontal marker, also display deltas relative to it. - if (this.horizontal_marker_) { - var baseline = this.horizontal_marker_.value; - var delta = this.coordinates.yValue(positionY) - baseline; - var fraction = delta / baseline; // Allow division by 0. - - var deltaStr = (delta >= 0 ? '+' : '') + delta.toFixed(0) + ' ' + - this.unitsY_; - var percentStr = (fraction >= 0 ? '+' : '') + (fraction * 100).toFixed(3) + - '%'; - - this.baselineDeltasTd_.innerHTML = deltaStr + ': ' + percentStr; - - if (this.unitsYOther_) { - var baseline = this.horizontal_marker_.otherValue; - var yValue2 = this.coordinatesOther.yValue(positionY); - var delta = yValue2 - baseline; - var fraction = delta / baseline; // Allow division by 0. - - var deltaStr = (delta >= 0 ? '+' : '') + delta.toFixed(0) + ' ' + - this.unitsYOther_; - var percentStr = (fraction >= 0 ? '+' : '') + - (fraction * 100).toFixed(3) + '%'; - this.baselineDeltasTd_.innerHTML += '<br>' + deltaStr + ': ' + percentStr; - } - } - - this.updateRuler_(evt); - this.updateCursor_(this.coordinates, indexValueX, this.cursorDiv_, 0); - if (this.unitsYOther_ && this.graphsOtherStartIndex_) { - this.updateCursor_(this.coordinatesOther, indexValueXOther, - this.cursorDivOther_, this.graphsOtherStartIndex_); - } - - // If there are events displayed, see if we're hovering close to an existing - // event on the graph, and if so, display the metadata associated with it. - if (this.eventName_ != null && this.eventInfo_ != null) { - this.infoBox_.rows[1].label.innerHTML = 'Event "' + this.eventName_ + - '": '; - var data = this.eventInfo_; - var showed_event = false; - var x = 0; - for (var index = 0; index < data.length; ++index) { - var event_time = data[index][0]; - x = this.coordinates.xPixel(event_time); - if (positionX >= x - 10 && positionX <= x + 10) { - var metadata = data[index][1]; - var metadata_str = ""; - for (var meta_key in metadata) - metadata_str += meta_key + ': ' + metadata[meta_key] + ', '; - metadata_str = metadata_str.substring(0, metadata_str.length - 2); - this.infoBox_.rows[1].content.innerHTML = event_time + ' ' + - this.unitsX_ + ': {' + metadata_str + '}'; - showed_event = true; - this.updateEventDiv_(x, true); - break; - } - } - if (!showed_event) { - this.coordinatesTdOther_.innerHTML = - 'move mouse close to vertical event marker'; - this.updateEventDiv_(x, false); - } - } - - this.updateHoveringInfo_(evt, true); -}; - -/** - * Handle a mouse over event. - * - * @param {Object} evt A mouse event object representing a mouse move event. - */ -Plotter.prototype.onMouseOver_ = function(evt) { - this.updateHoveringInfo_(evt, true); -}; - -/** - * Handle a mouse out event. - * - * @param {Object} evt A mouse event object representing a mouse move event. - */ -Plotter.prototype.onMouseOut_ = function(evt) { - this.updateHoveringInfo_(evt, false); -}; - -/** - * Handle a mouse click event. - * - * @param {Object} evt A mouse event object representing a mouse click event. - */ -Plotter.prototype.onMouseClick_ = function(evt) { - // Shift-click controls the horizontal reference line. - if (evt.shiftKey) { - if (this.horizontal_marker_) - this.horizontal_marker_.remove(); - - var canvasY = domUtils.pageXYOfEvent(evt).y - - domUtils.pageXY(this.canvasElement_).y; - this.horizontal_marker_ = new HorizontalMarker( - this.canvasElement_, - this.coordinates.yValue(canvasY), - (this.coordinatesOther ? this.coordinatesOther.yValue(canvasY) : null)); - // Insert before cursor node, otherwise it catches clicks. - this.cursorDiv_.parentNode.insertBefore( - this.horizontal_marker_.markerDiv, this.cursorDiv_); - this.horizontal_marker_.locateAt(this.canvasElement_, canvasY); - } -}; - -/** - * Generates and returns a list of div objects representing horizontal lines in - * the graph that indicate y-axis values at a computed interval. - * - * @param {Object} coordinateSystem a Coordinates object representing the - * coordinate system for which the graduations should be created. - * @param {number} colorIndex An index into the |this.colors| array representing - * the color to make the graduations in the event that two graphs with - * different coordinate systems are being overlayed on the same plot. - * @param {boolean} isRightSide Whether or not the graduations should have - * right-aligned text (used when the graduations are for a second graph - * that is being overlayed on top of another graph). - * @return {Array} An array of DOM Element objects representing the divs. - */ -Plotter.prototype.graduations_ = function(coordinateSystem, colorIndex, - isRightSide) { - // Don't allow a graduation in the bottom 5% of the chart or the number label - // would overflow the chart bounds. - var yMin = coordinateSystem.yLowerLimitValue() + - .05 * coordinateSystem.yValueRange(); - var yRange = coordinateSystem.yUpperLimitValue() - yMin; - - // Use the largest scale that fits 3 or more graduations. - // We allow scales of [...,500, 250, 100, 50, 25, 10,...]. - var scale = 5000000000; - while (scale) { - if (Math.floor(yRange / scale) > 2) break; // 5s. - scale /= 2; - if (Math.floor(yRange / scale) > 2) break; // 2.5s. - scale /= 2.5; - if (Math.floor(yRange / scale) > 2) break; // 1s. - scale /= 2; - } - - var graduationPosition = yMin + (scale - yMin % scale); - var graduationDivs = []; - while (graduationPosition < coordinateSystem.yUpperLimitValue() || - yRange == 0) { - var graduation = document.createElement('div'); - var canvasPosition; - if (yRange == 0) { - // Center the graduation vertically. - canvasPosition = this.canvasElement_.offsetHeight / 2; - } else { - canvasPosition = coordinateSystem.yPixel(graduationPosition); - } - if (this.unitsYOther_) { - graduation.style.borderTop = '1px dashed ' + - this.makeColorTransparent(colorIndex, 0.4) - } else { - graduation.style.borderTop = '1px dashed rgba(0,0,0,.08)'; - } - graduation.style.position = 'absolute'; - graduation.style.left = this.canvasElement_.offsetLeft + 'px'; - graduation.style.top = canvasPosition + this.canvasElement_.offsetTop + - 'px'; - graduation.style.width = this.canvasElement_.offsetWidth - - this.canvasElement_.offsetLeft + 'px'; - graduation.style.paddingLeft = '4px'; - if (this.unitsYOther_) - graduation.style.color = this.makeColorTransparent(colorIndex, 0.9) - else - graduation.style.color = 'rgba(0,0,0,.4)'; - graduation.style.fontSize = '9px'; - graduation.style.paddingTop = '0'; - graduation.style.zIndex = '-1'; - if (isRightSide) - graduation.style.textAlign = 'right'; - if (yRange == 0) - graduation.innerHTML = addCommas(yMin); - else - graduation.innerHTML = addCommas(graduationPosition); - graduationDivs.push(graduation); - if (yRange == 0) - break; - graduationPosition += scale; - } - return graduationDivs; -}; - -/** - * Generates and returns a div object representing the horizontal line that - * follows the mouse pointer around the plot. - * - * @return {Object} A DOM Element object representing the div. - */ -Plotter.prototype.ruler_ = function() { - var ruler = document.createElement('div'); - ruler.setAttribute('class', 'plot-ruler'); - ruler.style.borderBottom = '1px dotted black'; - ruler.style.position = 'absolute'; - ruler.style.left = '-2px'; - ruler.style.top = '-2px'; - ruler.style.width = '0px'; - ruler.style.height = '0px'; - return ruler; -}; - -/** - * Generates and returns a canvas object representing the plot itself. - * - * @return {Object} A DOM Element object representing the canvas. - */ -Plotter.prototype.canvas_ = function() { - var canvas = document.createElement('canvas'); - canvas.setAttribute('id', '_canvas'); - canvas.setAttribute('class', 'plot'); - canvas.setAttribute('width', this.coordinates.widthMax); - canvas.setAttribute('height', this.coordinates.heightMax); - canvas.plotter = this; - return canvas; -}; - -/** - * Generates and returns a div object representing the coordinate information - * displayed directly underneath a graph. - * - * @return {Object} A DOM Element object representing the div. - */ -Plotter.prototype.coordinates_ = function() { - var coordinatesDiv = document.createElement('div'); - var table_html = '<table border=0 width="100%"'; - if (this.is_lookout_) { - table_html += ' style="font-size:0.8em"'; - } - table_html += '><tbody><tr>'; - table_html += '<td><span class="legend_item"></span>' + - '<span class="plot-coordinates"><i>move mouse over graph</i></span></td>'; - table_html += '<td align="right">x-axis is ' + this.unitsX_ + '</td>'; - table_html += '</tr><tr>'; - table_html += '<td><span class="legend_item"></span>' + - '<span class="plot-coordinates"></span></td>'; - - if (!this.is_lookout_) { - table_html += '<td align="right" style="color: ' + HorizontalMarker.COLOR + - '"><i>Shift-click to place baseline.</i></td>'; - } - table_html += '</tr></tbody></table>'; - coordinatesDiv.innerHTML = table_html; - - var trs = coordinatesDiv.querySelectorAll('tr'); - this.infoBox_ = {rows: []}; - this.infoBox_.rows.push({ - label: trs[0].querySelector('span.legend_item'), - content: trs[0].querySelector('span.plot-coordinates')}); - if (this.dataDescriptions_.length > 1 || this.eventName_) { - this.infoBox_.rows.push({ - label: trs[1].querySelector('span.legend_item'), - content: trs[1].querySelector('span.plot-coordinates')}); - } - - this.baselineDeltasTd_ = trs[1].childNodes[1]; - - // Add a summary of legends in case of stacked graphs. - if (this.stackedGraph_ || this.stackedGraphOther_) { - var legendPane = document.createElement('div'); - legendPane.style.fontSize = '80%'; - coordinatesDiv.appendChild(legendPane); - - if (this.graphsOtherStartIndex_) { - legendPane.appendChild( - this.createLegendsSummaryElement_( - this.dataDescriptions_.slice(0, this.graphsOtherStartIndex_), - 0)); - legendPane.appendChild( - this.createLegendsSummaryElement_( - this.dataDescriptions_.slice(this.graphsOtherStartIndex_), - this.graphsOtherStartIndex_)); - } else { - legendPane.appendChild( - this.createLegendsSummaryElement_(this.dataDescriptions_, 0)); - } - } - - return coordinatesDiv; -}; - -/** - * Creates and returns a DOM element which shows a summary of legends. - * - * @param {!Array.<string>} legendTexts An array of legend texts. - * @param {number} colorIndexOffset Offset index for color. i-th legend text - * has an indicator in {@code (colorIndexOffset + i)}-th color - * @return {!Element} An element which shows a summary of legends. - */ -Plotter.prototype.createLegendsSummaryElement_ = function(legendTexts, - colorIndexOffset) { - var containerElem = document.createElement('div'); - - for (var i = 0, text; text = legendTexts[i]; ++i) { - var colorIndicatorElem = document.createElement('div'); - colorIndicatorElem.style.display = 'inline-block'; - colorIndicatorElem.style.width = '1em'; - colorIndicatorElem.style.height = '1em'; - colorIndicatorElem.style.verticalAlign = 'text-bottom'; - colorIndicatorElem.style.margin = '0 0.24em 0 0'; - colorIndicatorElem.style.border = '1px solid #000'; - colorIndicatorElem.style.backgroundColor = - this.getDataColor(colorIndexOffset + i); - var legendTextElem = document.createElement('span'); - legendTextElem.textContent = text; - var legendElem = document.createElement('span'); - legendElem.style.whiteSpace = 'nowrap'; - legendElem.appendChild(colorIndicatorElem); - legendElem.appendChild(legendTextElem); - legendElem.style.margin = '0 0.8em 0 0'; - containerElem.appendChild(legendElem); - // Add a space to break lines if necessary. - containerElem.appendChild(document.createTextNode(' ')); - } - - return containerElem; -}; diff --git a/chrome/test/functional/perf/endure_result_parser.py b/chrome/test/functional/perf/endure_result_parser.py deleted file mode 100755 index f477270..0000000 --- a/chrome/test/functional/perf/endure_result_parser.py +++ /dev/null @@ -1,824 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Script to parse perf data from Chrome Endure test executions, to be graphed. - -This script connects via HTTP to a buildbot master in order to scrape and parse -perf data from Chrome Endure tests that have been run. The perf data is then -stored in local text files to be graphed by the Chrome Endure graphing code. - -It is assumed that any Chrome Endure tests that show up on the waterfall have -names that are of the following form: - -"endure_<webapp_name>-<test_name>" - -This script accepts either a URL or a local path as a buildbot location. -It switches its behavior if a URL is given, or a local path is given. - -When a URL is given, it gets buildbot logs from the buildbot builders URL -e.g. http://build.chromium.org/p/chromium.endure/builders/. - -When a local path is given, it gets buildbot logs from buildbot's internal -files in the directory e.g. /home/chrome-bot/buildbot. -""" - -import cPickle -import getpass -import logging -import optparse -import os -import re -import simplejson -import socket -import string -import sys -import time -import urllib -import urllib2 - - -CHROME_ENDURE_SLAVE_NAMES = [ - 'Linux QA Perf (0)', - 'Linux QA Perf (1)', - 'Linux QA Perf (2)', - 'Linux QA Perf (3)', - 'Linux QA Perf (4)', - 'Linux QA Perf (dbg)(0)', - 'Linux QA Perf (dbg)(1)', - 'Linux QA Perf (dbg)(2)', - 'Linux QA Perf (dbg)(3)', - 'Linux QA Perf (dbg)(4)', -] - -BUILDER_URL_BASE = 'http://build.chromium.org/p/chromium.endure/builders/' -LAST_BUILD_NUM_PROCESSED_FILE = os.path.join(os.path.dirname(__file__), - '_parser_last_processed.txt') -LOCAL_GRAPH_DIR = '/home/%s/www/chrome_endure_clean' % getpass.getuser() -MANGLE_TRANSLATION = string.maketrans(' ()', '___') - -def SetupBaseGraphDirIfNeeded(webapp_name, test_name, dest_dir): - """Sets up the directory containing results for a particular test, if needed. - - Args: - webapp_name: The string name of the webapp associated with the given test. - test_name: The string name of the test. - dest_dir: The name of the destination directory that needs to be set up. - """ - if not os.path.exists(dest_dir): - os.mkdir(dest_dir) # Test name directory. - os.chmod(dest_dir, 0755) - - # Create config file. - config_file = os.path.join(dest_dir, 'config.js') - if not os.path.exists(config_file): - with open(config_file, 'w') as f: - f.write('var Config = {\n') - f.write('buildslave: "Chrome Endure Bots",\n') - f.write('title: "Chrome Endure %s Test: %s",\n' % (webapp_name.upper(), - test_name)) - f.write('};\n') - os.chmod(config_file, 0755) - - # Set up symbolic links to the real graphing files. - link_file = os.path.join(dest_dir, 'index.html') - if not os.path.exists(link_file): - os.symlink('../../endure_plotter.html', link_file) - link_file = os.path.join(dest_dir, 'endure_plotter.js') - if not os.path.exists(link_file): - os.symlink('../../endure_plotter.js', link_file) - link_file = os.path.join(dest_dir, 'js') - if not os.path.exists(link_file): - os.symlink('../../js', link_file) - - -def WriteToDataFile(new_line, existing_lines, revision, data_file): - """Writes a new entry to an existing perf data file to be graphed. - - If there's an existing line with the same revision number, overwrite its data - with the new line. Else, prepend the info for the new revision. - - Args: - new_line: A dictionary representing perf information for the new entry. - existing_lines: A list of string lines from the existing perf data file. - revision: The string revision number associated with the new perf entry. - data_file: The string name of the perf data file to which to write. - """ - overwritten = False - for i, line in enumerate(existing_lines): - line_dict = simplejson.loads(line) - if line_dict['rev'] == revision: - existing_lines[i] = simplejson.dumps(new_line) - overwritten = True - break - elif int(line_dict['rev']) < int(revision): - break - if not overwritten: - existing_lines.insert(0, simplejson.dumps(new_line)) - - with open(data_file, 'w') as f: - f.write('\n'.join(existing_lines)) - os.chmod(data_file, 0755) - - -def OutputPerfData(revision, graph_name, values, units, units_x, dest_dir, - is_stacked=False, stack_order=[]): - """Outputs perf data to a local text file to be graphed. - - Args: - revision: The string revision number associated with the perf data. - graph_name: The string name of the graph on which to plot the data. - values: A dict which maps a description to a value. A value is either a - single data value to be graphed, or a list of 2-tuples - representing (x, y) points to be graphed for long-running tests. - units: The string description for the y-axis units on the graph. - units_x: The string description for the x-axis units on the graph. Should - be set to None if the results are not for long-running graphs. - dest_dir: The name of the destination directory to which to write. - is_stacked: True to draw a "stacked" graph. First-come values are - stacked at bottom by default. - stack_order: A list that contains key strings in the order to stack values - in the graph. - """ - # Update graphs.dat, which contains metadata associated with each graph. - existing_graphs = [] - graphs_file = os.path.join(dest_dir, 'graphs.dat') - if os.path.exists(graphs_file): - with open(graphs_file, 'r') as f: - existing_graphs = simplejson.loads(f.read()) - is_new_graph = True - for graph in existing_graphs: - if graph['name'] == graph_name: - is_new_graph = False - break - if is_new_graph: - new_graph = { - 'name': graph_name, - 'units': units, - 'important': False, - } - if units_x: - new_graph['units_x'] = units_x - existing_graphs.append(new_graph) - existing_graphs = sorted(existing_graphs, key=lambda x: x['name']) - with open(graphs_file, 'w') as f: - f.write(simplejson.dumps(existing_graphs, indent=2)) - os.chmod(graphs_file, 0755) - - # Update summary data file, containing the actual data to be graphed. - data_file_name = graph_name + '-summary.dat' - existing_lines = [] - data_file = os.path.join(dest_dir, data_file_name) - if os.path.exists(data_file): - with open(data_file, 'r') as f: - existing_lines = f.readlines() - existing_lines = map(lambda x: x.strip(), existing_lines) - new_traces = {} - for description in values: - value = values[description] - if units_x: - points = [] - for point in value: - points.append([str(point[0]), str(point[1])]) - new_traces[description] = points - else: - new_traces[description] = [str(value), str(0.0)] - new_line = { - 'traces': new_traces, - 'rev': revision - } - if is_stacked: - new_line['stack'] = True - new_line['stack_order'] = stack_order - - WriteToDataFile(new_line, existing_lines, revision, data_file) - - -def OutputEventData(revision, event_dict, dest_dir): - """Outputs event data to a local text file to be graphed. - - Args: - revision: The string revision number associated with the event data. - event_dict: A dict which maps a description to an array of tuples - representing event data to be graphed. - dest_dir: The name of the destination directory to which to write. - """ - data_file_name = '_EVENT_-summary.dat' - existing_lines = [] - data_file = os.path.join(dest_dir, data_file_name) - if os.path.exists(data_file): - with open(data_file, 'r') as f: - existing_lines = f.readlines() - existing_lines = map(lambda x: x.strip(), existing_lines) - - new_events = {} - for description in event_dict: - event_list = event_dict[description] - value_list = [] - for event_time, event_data in event_list: - value_list.append([str(event_time), event_data]) - new_events[description] = value_list - - new_line = { - 'rev': revision, - 'events': new_events - } - - WriteToDataFile(new_line, existing_lines, revision, data_file) - - -def UpdatePerfDataFromFetchedContent( - revision, content, webapp_name, test_name, graph_dir, only_dmp=False): - """Update perf data from fetched stdio data. - - Args: - revision: The string revision number associated with the new perf entry. - content: Fetched stdio data. - webapp_name: A name of the webapp. - test_name: A name of the test. - graph_dir: A path to the graph directory. - only_dmp: True if only Deep Memory Profiler results should be used. - """ - perf_data_raw = [] - - def AppendRawPerfData(graph_name, description, value, units, units_x, - webapp_name, test_name, is_stacked=False): - perf_data_raw.append({ - 'graph_name': graph_name, - 'description': description, - 'value': value, - 'units': units, - 'units_x': units_x, - 'webapp_name': webapp_name, - 'test_name': test_name, - 'stack': is_stacked, - }) - - # First scan for short-running perf test results. - for match in re.findall( - r'RESULT ([^:]+): ([^=]+)= ([-\d\.]+) (\S+)', content): - if (not only_dmp) or match[0].endswith('-DMP'): - try: - match2 = eval(match[2]) - except SyntaxError: - match2 = None - if match2: - AppendRawPerfData(match[0], match[1], match2, match[3], None, - webapp_name, webapp_name) - - # Next scan for long-running perf test results. - for match in re.findall( - r'RESULT ([^:]+): ([^=]+)= (\[[^\]]+\]) (\S+) (\S+)', content): - if (not only_dmp) or match[0].endswith('-DMP'): - try: - match2 = eval(match[2]) - except SyntaxError: - match2 = None - # TODO(dmikurube): Change the condition to use stacked graph when we - # determine how to specify it. - if match2: - AppendRawPerfData(match[0], match[1], match2, match[3], match[4], - webapp_name, test_name, match[0].endswith('-DMP')) - - # Next scan for events in the test results. - for match in re.findall( - r'RESULT _EVENT_: ([^=]+)= (\[[^\]]+\])', content): - try: - match1 = eval(match[1]) - except SyntaxError: - match1 = None - if match1: - AppendRawPerfData('_EVENT_', match[0], match1, None, None, - webapp_name, test_name) - - # For each graph_name/description pair that refers to a long-running test - # result or an event, concatenate all the results together (assume results - # in the input file are in the correct order). For short-running test - # results, keep just one if more than one is specified. - perf_data = {} # Maps a graph-line key to a perf data dictionary. - for data in perf_data_raw: - key_graph = data['graph_name'] - key_description = data['description'] - if not key_graph in perf_data: - perf_data[key_graph] = { - 'graph_name': data['graph_name'], - 'value': {}, - 'units': data['units'], - 'units_x': data['units_x'], - 'webapp_name': data['webapp_name'], - 'test_name': data['test_name'], - } - perf_data[key_graph]['stack'] = data['stack'] - if 'stack_order' not in perf_data[key_graph]: - perf_data[key_graph]['stack_order'] = [] - if (data['stack'] and - data['description'] not in perf_data[key_graph]['stack_order']): - perf_data[key_graph]['stack_order'].append(data['description']) - - if data['graph_name'] != '_EVENT_' and not data['units_x']: - # Short-running test result. - perf_data[key_graph]['value'][key_description] = data['value'] - else: - # Long-running test result or event. - if key_description in perf_data[key_graph]['value']: - perf_data[key_graph]['value'][key_description] += data['value'] - else: - perf_data[key_graph]['value'][key_description] = data['value'] - - # Finally, for each graph-line in |perf_data|, update the associated local - # graph data files if necessary. - for perf_data_key in perf_data: - perf_data_dict = perf_data[perf_data_key] - - dest_dir = os.path.join(graph_dir, perf_data_dict['webapp_name']) - if not os.path.exists(dest_dir): - os.mkdir(dest_dir) # Webapp name directory. - os.chmod(dest_dir, 0755) - dest_dir = os.path.join(dest_dir, perf_data_dict['test_name']) - - SetupBaseGraphDirIfNeeded(perf_data_dict['webapp_name'], - perf_data_dict['test_name'], dest_dir) - if perf_data_dict['graph_name'] == '_EVENT_': - OutputEventData(revision, perf_data_dict['value'], dest_dir) - else: - OutputPerfData(revision, perf_data_dict['graph_name'], - perf_data_dict['value'], - perf_data_dict['units'], perf_data_dict['units_x'], - dest_dir, - perf_data_dict['stack'], perf_data_dict['stack_order']) - - -def SlaveLocation(master_location, slave_info): - """Returns slave location for |master_location| and |slave_info|.""" - if master_location.startswith('http://'): - return master_location + urllib.quote(slave_info['slave_name']) - else: - return os.path.join(master_location, - slave_info['slave_name'].translate(MANGLE_TRANSLATION)) - - -def GetRevisionAndLogs(slave_location, build_num): - """Get a revision number and log locations. - - Args: - slave_location: A URL or a path to the build slave data. - build_num: A build number. - - Returns: - A pair of the revision number and a list of strings that contain locations - of logs. (False, []) in case of error. - """ - if slave_location.startswith('http://'): - location = slave_location + '/builds/' + str(build_num) - else: - location = os.path.join(slave_location, str(build_num)) - - revision = False - logs = [] - fp = None - try: - if location.startswith('http://'): - fp = urllib2.urlopen(location) - contents = fp.read() - revisions = re.findall(r'<td class="left">got_revision</td>\s+' - '<td>(\d+)</td>\s+<td>Source</td>', contents) - if revisions: - revision = revisions[0] - logs = [location + link + '/text' for link - in re.findall(r'(/steps/endure[^/]+/logs/stdio)', contents)] - else: - fp = open(location, 'rb') - build = cPickle.load(fp) - properties = build.getProperties() - if properties.has_key('got_revision'): - revision = build.getProperty('got_revision') - candidates = os.listdir(slave_location) - logs = [os.path.join(slave_location, filename) - for filename in candidates - if re.match(r'%d-log-endure[^/]+-stdio' % build_num, filename)] - - except urllib2.URLError, e: - logging.exception('Error reading build URL "%s": %s', location, str(e)) - return False, [] - except (IOError, OSError), e: - logging.exception('Error reading build file "%s": %s', location, str(e)) - return False, [] - finally: - if fp: - fp.close() - - return revision, logs - - -def ExtractTestNames(log_location, is_dbg): - """Extract test names from |log_location|. - - Returns: - A dict of a log location, webapp's name and test's name. False if error. - """ - if log_location.startswith('http://'): - location = urllib.unquote(log_location) - test_pattern = r'endure_([^_]+)(_test |-)([^/]+)/' - else: - location = log_location - test_pattern = r'endure_([^_]+)(_test_|-)([^/]+)-stdio' - - match = match[0] - webapp_name = match[0] - webapp_name = webapp_name + '_dbg' if is_dbg else webapp_name - test_name = match[2] - - return { - 'location': log_location, - 'webapp_name': webapp_name, - 'test_name': test_name, - } - - -def GetStdioContents(stdio_location): - """Gets appropriate stdio contents. - - Returns: - A content string of the stdio log. None in case of error. - """ - fp = None - contents = '' - try: - if stdio_location.startswith('http://'): - fp = urllib2.urlopen(stdio_location, timeout=60) - # Since in-progress test output is sent chunked, there's no EOF. We need - # to specially handle this case so we don't hang here waiting for the - # test to complete. - start_time = time.time() - while True: - data = fp.read(1024) - if not data: - break - contents += data - if time.time() - start_time >= 30: # Read for at most 30 seconds. - break - else: - fp = open(stdio_location) - data = fp.read() - contents = '' - index = 0 - - # Buildbot log files are stored in the netstring format. - # http://en.wikipedia.org/wiki/Netstring - while index < len(data): - index2 = index - while data[index2].isdigit(): - index2 += 1 - if data[index2] != ':': - logging.error('Log file is not in expected format: %s' % - stdio_location) - contents = None - break - length = int(data[index:index2]) - index = index2 + 1 - channel = int(data[index]) - index += 1 - if data[index+length-1] != ',': - logging.error('Log file is not in expected format: %s' % - stdio_location) - contents = None - break - if channel == 0: - contents += data[index:(index+length-1)] - index += length - - except (urllib2.URLError, socket.error, IOError, OSError), e: - # Issue warning but continue to the next stdio link. - logging.warning('Error reading test stdio data "%s": %s', - stdio_location, str(e)) - finally: - if fp: - fp.close() - - return contents - - -def UpdatePerfDataForSlaveAndBuild( - slave_info, build_num, graph_dir, master_location): - """Process updated perf data for a particular slave and build number. - - Args: - slave_info: A dictionary containing information about the slave to process. - build_num: The particular build number on the slave to process. - graph_dir: A path to the graph directory. - master_location: A URL or a path to the build master data. - - Returns: - True if the perf data for the given slave/build is updated properly, or - False if any critical error occurred. - """ - if not master_location.startswith('http://'): - # Source is a file. - from buildbot.status import builder - - slave_location = SlaveLocation(master_location, slave_info) - logging.debug(' %s, build %d.', slave_info['slave_name'], build_num) - is_dbg = '(dbg)' in slave_info['slave_name'] - - revision, logs = GetRevisionAndLogs(slave_location, build_num) - if not revision: - return False - - stdios = [] - for log_location in logs: - stdio = ExtractTestNames(log_location, is_dbg) - if not stdio: - return False - stdios.append(stdio) - - for stdio in stdios: - stdio_location = stdio['location'] - contents = GetStdioContents(stdio_location) - - if contents: - UpdatePerfDataFromFetchedContent(revision, contents, - stdio['webapp_name'], - stdio['test_name'], - graph_dir, is_dbg) - - return True - - -def GetMostRecentBuildNum(master_location, slave_name): - """Gets the most recent buld number for |slave_name| in |master_location|.""" - most_recent_build_num = None - - if master_location.startswith('http://'): - slave_url = master_location + urllib.quote(slave_name) - - url_contents = '' - fp = None - try: - fp = urllib2.urlopen(slave_url, timeout=60) - url_contents = fp.read() - except urllib2.URLError, e: - logging.exception('Error reading builder URL: %s', str(e)) - return None - finally: - if fp: - fp.close() - - matches = re.findall(r'/(\d+)/stop', url_contents) - if matches: - most_recent_build_num = int(matches[0]) - else: - matches = re.findall(r'#(\d+)</a></td>', url_contents) - if matches: - most_recent_build_num = sorted(map(int, matches), reverse=True)[0] - - else: - slave_path = os.path.join(master_location, - slave_name.translate(MANGLE_TRANSLATION)) - files = os.listdir(slave_path) - number_files = [int(filename) for filename in files if filename.isdigit()] - if number_files: - most_recent_build_num = sorted(number_files, reverse=True)[0] - - if most_recent_build_num: - logging.debug('%s most recent build number: %s', - slave_name, most_recent_build_num) - else: - logging.error('Could not identify latest build number for slave %s.', - slave_name) - - return most_recent_build_num - - -def UpdatePerfDataFiles(graph_dir, master_location): - """Updates the Chrome Endure graph data files with the latest test results. - - For each known Chrome Endure slave, we scan its latest test results looking - for any new test data. Any new data that is found is then appended to the - data files used to display the Chrome Endure graphs. - - Args: - graph_dir: A path to the graph directory. - master_location: A URL or a path to the build master data. - - Returns: - True if all graph data files are updated properly, or - False if any error occurred. - """ - slave_list = [] - for slave_name in CHROME_ENDURE_SLAVE_NAMES: - slave_info = {} - slave_info['slave_name'] = slave_name - slave_info['most_recent_build_num'] = None - slave_info['last_processed_build_num'] = None - slave_list.append(slave_info) - - # Identify the most recent build number for each slave. - logging.debug('Searching for latest build numbers for each slave...') - for slave in slave_list: - slave_name = slave['slave_name'] - slave['most_recent_build_num'] = GetMostRecentBuildNum( - master_location, slave_name) - - # Identify the last-processed build number for each slave. - logging.debug('Identifying last processed build numbers...') - if not os.path.exists(LAST_BUILD_NUM_PROCESSED_FILE): - for slave_info in slave_list: - slave_info['last_processed_build_num'] = 0 - else: - with open(LAST_BUILD_NUM_PROCESSED_FILE, 'r') as fp: - file_contents = fp.read() - for match in re.findall(r'([^:]+):(\d+)', file_contents): - slave_name = match[0].strip() - last_processed_build_num = match[1].strip() - for slave_info in slave_list: - if slave_info['slave_name'] == slave_name: - slave_info['last_processed_build_num'] = int( - last_processed_build_num) - for slave_info in slave_list: - if not slave_info['last_processed_build_num']: - slave_info['last_processed_build_num'] = 0 - logging.debug('Done identifying last processed build numbers.') - - # For each Chrome Endure slave, process each build in-between the last - # processed build num and the most recent build num, inclusive. To process - # each one, first get the revision number for that build, then scan the test - # result stdio for any performance data, and add any new performance data to - # local files to be graphed. - for slave_info in slave_list: - logging.debug('Processing %s, builds %d-%d...', - slave_info['slave_name'], - slave_info['last_processed_build_num'], - slave_info['most_recent_build_num']) - curr_build_num = slave_info['last_processed_build_num'] - while curr_build_num <= slave_info['most_recent_build_num']: - if not UpdatePerfDataForSlaveAndBuild(slave_info, curr_build_num, - graph_dir, master_location): - # Do not give up. The first files might be removed by buildbot. - logging.warning('Logs do not exist in buildbot for #%d of %s.' % - (curr_build_num, slave_info['slave_name'])) - curr_build_num += 1 - - # Log the newly-processed build numbers. - logging.debug('Logging the newly-processed build numbers...') - with open(LAST_BUILD_NUM_PROCESSED_FILE, 'w') as f: - for slave_info in slave_list: - f.write('%s:%s\n' % (slave_info['slave_name'], - slave_info['most_recent_build_num'])) - - return True - - -def GenerateIndexPage(graph_dir): - """Generates a summary (landing) page for the Chrome Endure graphs. - - Args: - graph_dir: A path to the graph directory. - """ - logging.debug('Generating new index.html page...') - - # Page header. - page = """ - <html> - - <head> - <title>Chrome Endure Overview</title> - <script language="javascript"> - function DisplayGraph(name, graph) { - document.write( - '<td><iframe scrolling="no" height="438" width="700" src="'); - document.write(name); - document.write('"></iframe></td>'); - } - </script> - </head> - - <body> - <center> - - <h1> - Chrome Endure - </h1> - """ - # Print current time. - page += '<p>Updated: %s</p>\n' % ( - time.strftime('%A, %B %d, %Y at %I:%M:%S %p %Z')) - - # Links for each webapp. - webapp_names = [x for x in os.listdir(graph_dir) if - x not in ['js', 'old_data', '.svn', '.git'] and - os.path.isdir(os.path.join(graph_dir, x))] - webapp_names = sorted(webapp_names) - - page += '<p> [' - for i, name in enumerate(webapp_names): - page += '<a href="#%s">%s</a>' % (name.upper(), name.upper()) - if i < len(webapp_names) - 1: - page += ' | ' - page += '] </p>\n' - - # Print out the data for each webapp. - for webapp_name in webapp_names: - page += '\n<h1 id="%s">%s</h1>\n' % (webapp_name.upper(), - webapp_name.upper()) - - # Links for each test for this webapp. - test_names = [x for x in - os.listdir(os.path.join(graph_dir, webapp_name))] - test_names = sorted(test_names) - - page += '<p> [' - for i, name in enumerate(test_names): - page += '<a href="#%s">%s</a>' % (name, name) - if i < len(test_names) - 1: - page += ' | ' - page += '] </p>\n' - - # Print out the data for each test for this webapp. - for test_name in test_names: - # Get the set of graph names for this test. - graph_names = [x[:x.find('-summary.dat')] for x in - os.listdir(os.path.join(graph_dir, - webapp_name, test_name)) - if '-summary.dat' in x and '_EVENT_' not in x] - graph_names = sorted(graph_names) - - page += '<h2 id="%s">%s</h2>\n' % (test_name, test_name) - page += '<table>\n' - - for i, graph_name in enumerate(graph_names): - if i % 2 == 0: - page += ' <tr>\n' - page += (' <script>DisplayGraph("%s/%s?graph=%s&lookout=1");' - '</script>\n' % (webapp_name, test_name, graph_name)) - if i % 2 == 1: - page += ' </tr>\n' - if len(graph_names) % 2 == 1: - page += ' </tr>\n' - page += '</table>\n' - - # Page footer. - page += """ - </center> - </body> - - </html> - """ - - index_file = os.path.join(graph_dir, 'index.html') - with open(index_file, 'w') as f: - f.write(page) - os.chmod(index_file, 0755) - - -def main(): - parser = optparse.OptionParser() - parser.add_option( - '-v', '--verbose', action='store_true', default=False, - help='Use verbose logging.') - parser.add_option( - '-s', '--stdin', action='store_true', default=False, - help='Input from stdin instead of slaves for testing this script.') - parser.add_option( - '-b', '--buildbot', dest='buildbot', metavar="BUILDBOT", - default=BUILDER_URL_BASE, - help='Use log files in a buildbot at BUILDBOT. BUILDBOT can be a ' - 'buildbot\'s builder URL or a local path to a buildbot directory. ' - 'Both an absolute path and a relative path are available, e.g. ' - '"/home/chrome-bot/buildbot" or "../buildbot". ' - '[default: %default]') - parser.add_option( - '-g', '--graph', dest='graph_dir', metavar="DIR", default=LOCAL_GRAPH_DIR, - help='Output graph data files to DIR. [default: %default]') - options, _ = parser.parse_args(sys.argv) - - logging_level = logging.DEBUG if options.verbose else logging.INFO - logging.basicConfig(level=logging_level, - format='[%(asctime)s] %(levelname)s: %(message)s') - - if options.stdin: - content = sys.stdin.read() - UpdatePerfDataFromFetchedContent( - '12345', content, 'webapp', 'test', options.graph_dir) - else: - if options.buildbot.startswith('http://'): - master_location = options.buildbot - else: - build_dir = os.path.join(options.buildbot, 'build') - third_party_dir = os.path.join(build_dir, 'third_party') - sys.path.append(third_party_dir) - sys.path.append(os.path.join(third_party_dir, 'buildbot_8_4p1')) - sys.path.append(os.path.join(third_party_dir, 'twisted_10_2')) - master_location = os.path.join(build_dir, 'masters', - 'master.chromium.endure') - success = UpdatePerfDataFiles(options.graph_dir, master_location) - if not success: - logging.error('Failed to update perf data files.') - sys.exit(0) - - GenerateIndexPage(options.graph_dir) - logging.debug('All done!') - - -if __name__ == '__main__': - main() diff --git a/chrome/test/functional/perf/endure_server.py b/chrome/test/functional/perf/endure_server.py deleted file mode 100755 index ae4cd0f..0000000 --- a/chrome/test/functional/perf/endure_server.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Start an HTTP server which serves Chrome Endure graphs. - -Usage: - python endure_server.py [options] - -To view Chrome Endure graphs from a browser, -run this script to start a local HTTP server that serves the directory -where graph code and test results are located. A port will be automatically -picked. You can then view the graphs via http://localhost:<GIVEN_PORT>. - -Examples: - >python endure_server.py - Start a server which serves the default location - <CURRENT_WORKING_DIR>/chrome_graph. - - >python endure_server.py --graph-dir=/home/user/Document/graph_dir - Start a server which serves /home/user/Document/graph_dir which - is where your graph code and test results are. -""" - -import BaseHTTPServer -import logging -import optparse -import os -import SimpleHTTPServer -import sys - - -class HelpFormatter(optparse.IndentedHelpFormatter): - """Format the help message of this script.""" - - def format_description(self, description): - """Override to keep original format of the description.""" - return description + '\n' if description else '' - - -def _ParseArgs(argv): - parser = optparse.OptionParser( - usage='%prog [options]', - formatter=HelpFormatter(), - description=__doc__) - parser.add_option( - '-g', '--graph-dir', type='string', - default=os.path.join(os.getcwd(), 'chrome_graph'), - help='The directory that contains graph code ' \ - 'and data files of test results. Default value is ' \ - '<CURRENT_WORKING_DIR>/chrome_graph') - return parser.parse_args(argv) - - -def Run(argv): - """Start an HTTP server which serves Chrome Endure graphs.""" - logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.DEBUG) - options, _ = _ParseArgs(argv) - graph_dir = os.path.abspath(options.graph_dir) - cur_dir = os.getcwd() - os.chdir(graph_dir) - httpd = BaseHTTPServer.HTTPServer( - ('', 0), SimpleHTTPServer.SimpleHTTPRequestHandler) - try: - logging.info('Serving %s at port %d', graph_dir, httpd.server_port) - logging.info('View graphs at http://localhost:%d', httpd.server_port) - logging.info('Press Ctrl-C to stop the server.') - httpd.serve_forever() - except KeyboardInterrupt: - logging.info('Shutting down ...') - httpd.shutdown() - finally: - os.chdir(cur_dir) - return 0 - - -if '__main__' == __name__: - sys.exit(Run(sys.argv[1:])) diff --git a/chrome/test/functional/perf/endure_setup.py b/chrome/test/functional/perf/endure_setup.py deleted file mode 100755 index dba1df4..0000000 --- a/chrome/test/functional/perf/endure_setup.py +++ /dev/null @@ -1,337 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Automate the setup process of Chrome Endure environment. - -Usage: - python endure_setup.py [option] - -We use <ENDURE_DIR> to refer to the root directory in which Chrome Endure -is set up. By default, <ENDURE_DIR> is the current working directory. - -First, run: - >python endure_setup.py -This command will automatically setup Chrome Endure in <ENDURE_DIR>. - -Next, run your first endure test by: - >TEST_LENGTH=30 LOCAL_PERF_DIR="<ENDURE_DIR>/chrome_graph" \\ - python <ENDURE_DIR>/src/chrome/test/functional/perf_endure.py \\ - perf_endure.ChromeEndureGmailTest.testGmailComposeDiscard \\ -The above commands runs a Chrome Endure test for 30 seconds and saves -the results to <ENDURE_DIR>/chrome_graph. - -Last, to view the graphs, run another script endure_server.py -within <ENDURE_DIR> to start a local HTTP server that serves -the graph directory, see endure_server.py for details. - -Use python endure_setup.py --help for more options. - -This script depends on the following modules -(which will be downloaded automatically): - depot_tools - src/chrome/test/pyautolib/fetch_prebuilt_pyauto.py - -Supported platforms: Linux and Linux_x64. -""" - -import logging -import optparse -import os -import platform -import shutil -import subprocess -import sys -import urllib -import urllib2 -import zipfile - -URLS = {'depot_tools': ('http://src.chromium.org' - '/chrome/trunk/tools/depot_tools'), - 'pyauto': ('https://src.chromium.org/' - 'chrome/trunk/src/chrome/test/functional.DEPS'), - 'binary': ('http://commondatastorage.googleapis.com/' - 'chromium-browser-continuous/{os_type}/{revision}'), - } - - -class SetupError(Exception): - """Catch errors in setting up Chrome Endure.""" - pass - - -class HelpFormatter(optparse.IndentedHelpFormatter): - """Format the help message of this script.""" - - def format_description(self, description): - """Override to keep the original format of the description.""" - return description + '\n' if description else '' - - -def Main(argv): - """Fetch Chrome Endure. - - Usage: - python endure_setup.py [options] - - Examples: - >python endure_setup.py - Fetch the latest version of Chrome Endure to the current - working directory. - - >python endure_setup.py --endure-dir=/home/user/endure_dir - Fetch the latest version of Chrome Endure to /home/user/endure_dir. - """ - parser = optparse.OptionParser( - formatter=HelpFormatter(), description=Main.__doc__) - parser.add_option( - '-d', '--endure-dir', type='string', default=os.getcwd(), - help='Directory in which to setup or update. ' \ - 'Default value is the current working directory.') - # TODO(fdeng): remove this option once the Chrome Endure - # graphing code is checked into chrome tree. - parser.add_option( - '-g', '--graph-zip-url', type='string', default=None, - help='URL to a zip file containing the chrome graphs.') - os_type = GetCurrentOSType() - if not os_type.startswith('Linux'): - raise SetupError('Only support Linux or Linux_x64, %s found' - % os_type) - options, _ = parser.parse_args(argv) - endure_dir = os.path.abspath(options.endure_dir) - depot_dir = os.path.join(endure_dir, 'depot_tools') - gclient = os.path.join(depot_dir, 'gclient') - fetch_py = os.path.join(endure_dir, 'src', 'chrome', - 'test', 'pyautolib', - 'fetch_prebuilt_pyauto.py') - binary_dir = os.path.join(endure_dir, 'src', 'out', 'Release') - graph_zip_url = options.graph_zip_url - graph_dir = os.path.join(endure_dir, 'chrome_graph') - - if not os.path.isdir(endure_dir): - os.makedirs(endure_dir) - - logging.info('Fetching depot tools...') - FetchDepot(depot_dir) - logging.info('Fetching PyAuto (python code)...') - FetchPyAuto(gclient, endure_dir) - logging.info('Fetching binaries(chrome, pyautolib, chrome driver)...') - FetchBinaries(fetch_py, binary_dir, os_type) - # TODO(fdeng): remove this after it is checked into the chrome tree. - logging.info('Fetching chrome graphing files...') - FetchGraph(graph_zip_url, graph_dir) - return 0 - - -def FetchDepot(depot_dir): - """Fetch depot_tools. - - Args: - depot_dir: The directory where depot_tools will be checked out. - - Raises: - SetupError: If fail. - """ - if subprocess.call(['svn', 'co', URLS['depot_tools'], depot_dir]) != 0: - raise SetupError('Error found when checking out depot_tools.') - if not CheckDepot(depot_dir): - raise SetupError('Could not get depot_tools.') - - -def CheckDepot(depot_dir): - """Check that some expected depot_tools files exist. - - Args: - depot_dir: The directory where depot_tools are checked out. - - Returns: - True if check passes otherwise False. - """ - gclient = os.path.join(depot_dir, 'gclient') - gclient_py = os.path.join(depot_dir, 'gclient.py') - files = [gclient, gclient_py] - for f in files: - if not os.path.exists(f): - return False - try: - subprocess.call([gclient, '--version']) - except OSError: - return False - return True - - -def FetchPyAuto(gclient, endure_dir): - """Use gclient to fetch python code. - - Args: - gclient: The path to the gclient executable. - endure_dir: Directory where Chrome Endure and - its dependencies will be checked out. - - Raises: - SetupError: if fails. - """ - cur_dir = os.getcwd() - os.chdir(endure_dir) - config_cmd = [gclient, 'config', URLS['pyauto']] - if subprocess.call(config_cmd) != 0: - raise SetupError('Running "%s" failed.' % ' '.join(config_cmd)) - sync_cmd = [gclient, 'sync'] - if subprocess.call(sync_cmd) != 0: - raise SetupError('Running "%s" failed.' % ' '.join(sync_cmd)) - CheckPyAuto(endure_dir) - logging.info('Sync PyAuto python code done.') - os.chdir(cur_dir) - - -def CheckPyAuto(endure_dir): - """Sanity check for Chrome Endure code. - - Args: - endure_dir: Directory of Chrome Endure and its dependencies. - - Raises: - SetupError: If fails. - """ - fetch_py = os.path.join(endure_dir, 'src', 'chrome', - 'test', 'pyautolib', - 'fetch_prebuilt_pyauto.py') - pyauto_py = os.path.join(endure_dir, 'src', - 'chrome', 'test', - 'pyautolib', 'pyauto.py') - files = [fetch_py, pyauto_py] - for f in files: - if not os.path.exists(f): - raise SetupError('Checking %s failed.' % f) - - -def FetchBinaries(fetch_py, binary_dir, os_type): - """Get the prebuilt binaries from continuous build archive. - - Args: - fetch_py: Path to the script which fetches pre-built binaries. - binary_dir: Directory of the pre-built binaries. - os_type: 'Mac', 'Win', 'Linux', 'Linux_x64'. - - Raises: - SetupError: If fails. - """ - revision = GetLatestRevision(os_type) - logging.info('Cleaning %s', binary_dir) - if os.path.exists(binary_dir): - shutil.rmtree(binary_dir) - logging.info('Downloading binaries...') - cmd = [fetch_py, '-d', binary_dir, - URLS['binary'].format( - os_type=os_type, revision=revision)] - if subprocess.call(cmd) == 0 and os.path.exists(binary_dir): - logging.info('Binaries at revision %s', revision) - else: - raise SetupError('Running "%s" failed.' % ' '.join(cmd)) - - -def FetchGraph(graph_zip_url, graph_dir): - """Fetch graph code. - - Args: - graph_zip_url: The url to a zip file containing the chrome graphs. - graph_dir: Directory of the chrome graphs. - - Raises: - SetupError: if unable to retrive the zip file. - """ - # TODO(fdeng): remove this function once chrome graph - # is checked into chrome tree. - if not graph_zip_url: - logging.info( - 'Skip fetching chrome graphs' + - ' since --graph-zip-url is not set.') - return - graph_zip = urllib.urlretrieve(graph_zip_url)[0] - if graph_zip is None or not os.path.exists(graph_zip): - raise SetupError('Unable to retrieve %s' % graph_zip_url) - if not os.path.exists(graph_dir): - os.mkdir(graph_dir) - UnzipFilenameToDir(graph_zip, graph_dir) - logging.info('Graph code is downloaded to %s', graph_dir) - - -def GetCurrentOSType(): - """Get a string representation for the current OS. - - Returns: - 'Mac', 'Win', 'Linux', or 'Linux_64'. - - Raises: - RuntimeError: if OS can't be identified. - """ - if sys.platform == 'darwin': - os_type = 'Mac' - if sys.platform == 'win32': - os_type = 'Win' - if sys.platform.startswith('linux'): - os_type = 'Linux' - if platform.architecture()[0] == '64bit': - os_type += '_x64' - else: - raise RuntimeError('Unknown platform') - return os_type - - -def GetLatestRevision(os_type): - """Figure out the latest revision number of the prebuilt binary archive. - - Args: - os_type: 'Mac', 'Win', 'Linux', or 'Linux_64'. - - Returns: - A string of latest revision number. - - Raises: - SetupError: If unable to get the latest revision number. - """ - last_change_url = ('http://commondatastorage.googleapis.com/' - 'chromium-browser-continuous/%s/LAST_CHANGE' % os_type) - response = urllib2.urlopen(last_change_url) - last_change = response.read() - if not last_change: - raise SetupError('Unable to get the latest revision number from %s' % - last_change_url) - return last_change - - -def UnzipFilenameToDir(filename, directory): - """Unzip |filename| to directory |directory|. - - This works with as low as python2.4 (used on win). - (Code is adapted from fetch_prebuilt_pyauto.py) - """ - # TODO(fdeng): remove this function as soon as the Chrome Endure - # graphing code is checked into the chrome tree. - zf = zipfile.ZipFile(filename) - pushd = os.getcwd() - if not os.path.isdir(directory): - os.mkdir(directory) - os.chdir(directory) - # Extract files. - for info in zf.infolist(): - name = info.filename - if name.endswith('/'): # dir - if not os.path.isdir(name): - os.makedirs(name) - else: # file - directory = os.path.dirname(name) - if directory and not os.path.isdir(directory): - os.makedirs(directory) - out = open(name, 'wb') - out.write(zf.read(name)) - out.close() - # Set permissions. Permission info in external_attr is shifted 16 bits. - os.chmod(name, info.external_attr >> 16L) - os.chdir(pushd) - - -if '__main__' == __name__: - logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.DEBUG) - sys.exit(Main(sys.argv[1:])) diff --git a/chrome/test/functional/perf_endure.py b/chrome/test/functional/perf_endure.py deleted file mode 100755 index e647435..0000000 --- a/chrome/test/functional/perf_endure.py +++ /dev/null @@ -1,776 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Performance tests for Chrome Endure (long-running perf tests on Chrome). - -This module accepts the following environment variable inputs: - TEST_LENGTH: The number of seconds in which to run each test. - PERF_STATS_INTERVAL: The number of seconds to wait in-between each sampling - of performance/memory statistics. - -The following variables are related to the Deep Memory Profiler. - DEEP_MEMORY_PROFILE: Enable the Deep Memory Profiler if it's set to 'True'. - DEEP_MEMORY_PROFILE_SAVE: Don't clean up dump files if it's set to 'True'. - DEEP_MEMORY_PROFILE_UPLOAD: Upload dumped files if the variable has a Google - Storage bucket like gs://chromium-endure/. The 'gsutil' script in $PATH - is used by default, or set a variable 'GSUTIL' to specify a path to the - 'gsutil' script. A variable 'REVISION' (or 'BUILDBOT_GOT_REVISION') is - used as a subdirectory in the destination if it is set. - GSUTIL: A path to the 'gsutil' script. Not mandatory. - REVISION: A string that represents the revision or some build configuration. - Not mandatory. - BUILDBOT_GOT_REVISION: Similar to 'REVISION', but checked only if 'REVISION' - is not specified. Not mandatory. -""" - -from datetime import datetime -import json -import logging -import os -import re -import subprocess -import tempfile -import time - -import perf -import pyauto_functional # Must be imported before pyauto. -import pyauto -import pyauto_errors -import pyauto_utils -import remote_inspector_client -import selenium.common.exceptions -from selenium.webdriver.support.ui import WebDriverWait - - -class NotSupportedEnvironmentError(RuntimeError): - """Represent an error raised since the environment (OS) is not supported.""" - pass - - -class DeepMemoryProfiler(object): - """Controls Deep Memory Profiler (dmprof) for endurance tests.""" - DEEP_MEMORY_PROFILE = False - DEEP_MEMORY_PROFILE_SAVE = False - DEEP_MEMORY_PROFILE_UPLOAD = '' - - _WORKDIR_PATTERN = re.compile('endure\.[0-9]+\.[0-9]+\.[A-Za-z0-9]+') - _SAVED_WORKDIRS = 8 - - _DMPROF_DIR_PATH = os.path.abspath(os.path.join( - os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, - 'tools', 'deep_memory_profiler')) - _DMPROF_SCRIPT_PATH = os.path.join(_DMPROF_DIR_PATH, 'dmprof') - _POLICIES = ['l0', 'l1', 'l2', 't0'] - - def __init__(self): - self._enabled = self.GetEnvironmentVariable( - 'DEEP_MEMORY_PROFILE', bool, self.DEEP_MEMORY_PROFILE) - self._save = self.GetEnvironmentVariable( - 'DEEP_MEMORY_PROFILE_SAVE', bool, self.DEEP_MEMORY_PROFILE_SAVE) - self._upload = self.GetEnvironmentVariable( - 'DEEP_MEMORY_PROFILE_UPLOAD', str, self.DEEP_MEMORY_PROFILE_UPLOAD) - if self._upload and not self._upload.endswith('/'): - self._upload += '/' - - self._revision = '' - self._gsutil = '' - self._json_file = None - self._last_json_filename = '' - self._proc = None - self._last_time = {} - for policy in self._POLICIES: - self._last_time[policy] = -1.0 - - def __nonzero__(self): - return self._enabled - - @staticmethod - def GetEnvironmentVariable(env_name, converter, default): - """Returns a converted environment variable for Deep Memory Profiler. - - Args: - env_name: A string name of an environment variable. - converter: A function taking a string to convert an environment variable. - default: A value used if the environment variable is not specified. - - Returns: - A value converted from the environment variable with 'converter'. - """ - return converter(os.environ.get(env_name, default)) - - def SetUp(self, is_linux, revision, gsutil): - """Sets up Deep Memory Profiler settings for a Chrome process. - - It sets environment variables and makes a working directory. - """ - if not self._enabled: - return - - if not is_linux: - raise NotSupportedEnvironmentError( - 'Deep Memory Profiler is not supported in this environment (OS).') - - self._revision = revision - self._gsutil = gsutil - - # Remove old dumped files with keeping latest _SAVED_WORKDIRS workdirs. - # It keeps the latest workdirs not to miss data by failure in uploading - # and other operations. Dumped files are no longer available if they are - # removed. Re-execution doesn't generate the same files. - tempdir = tempfile.gettempdir() - saved_workdirs = 0 - for filename in sorted(os.listdir(tempdir), reverse=True): - if self._WORKDIR_PATTERN.match(filename): - saved_workdirs += 1 - if saved_workdirs > self._SAVED_WORKDIRS: - fullpath = os.path.abspath(os.path.join(tempdir, filename)) - logging.info('Removing an old workdir: %s' % fullpath) - pyauto_utils.RemovePath(fullpath) - - dir_prefix = 'endure.%s.' % datetime.today().strftime('%Y%m%d.%H%M%S') - self._workdir = tempfile.mkdtemp(prefix=dir_prefix, dir=tempdir) - os.environ['HEAPPROFILE'] = os.path.join(self._workdir, 'endure') - os.environ['HEAP_PROFILE_MMAP'] = '1' - os.environ['DEEP_HEAP_PROFILE'] = '1' - - def TearDown(self): - """Tear down Deep Memory Profiler settings for the Chrome process. - - It removes the environment variables and the temporary directory. - Call it after Chrome finishes. Chrome may dump last files at the end. - """ - if not self._enabled: - return - - del os.environ['DEEP_HEAP_PROFILE'] - del os.environ['HEAP_PROFILE_MMAP'] - del os.environ['HEAPPROFILE'] - if not self._save and self._workdir: - pyauto_utils.RemovePath(self._workdir) - - def LogFirstMessage(self): - """Logs first messages.""" - if not self._enabled: - return - - logging.info('Running with the Deep Memory Profiler.') - if self._save: - logging.info(' Dumped files won\'t be cleaned.') - else: - logging.info(' Dumped files will be cleaned up after every test.') - - def StartProfiler(self, proc_info, is_last, webapp_name, test_description): - """Starts Deep Memory Profiler in background.""" - if not self._enabled: - return - - logging.info(' Profiling with the Deep Memory Profiler...') - - # Wait for a running dmprof process for last _GetPerformanceStat call to - # cover last dump files. - if is_last: - logging.info(' Waiting for the last dmprof.') - self._WaitForDeepMemoryProfiler() - - if self._proc and self._proc.poll() is None: - logging.info(' Last dmprof is still running.') - else: - if self._json_file: - self._last_json_filename = self._json_file.name - self._json_file.close() - self._json_file = None - first_dump = '' - last_dump = '' - for filename in sorted(os.listdir(self._workdir)): - if re.match('^endure.%05d.\d+.heap$' % proc_info['tab_pid'], - filename): - logging.info(' Profiled dump file: %s' % filename) - last_dump = filename - if not first_dump: - first_dump = filename - if first_dump: - logging.info(' First dump file: %s' % first_dump) - matched = re.match('^endure.\d+.(\d+).heap$', last_dump) - last_sequence_id = matched.group(1) - self._json_file = open( - os.path.join(self._workdir, - 'endure.%05d.%s.json' % (proc_info['tab_pid'], - last_sequence_id)), 'w+') - self._proc = subprocess.Popen( - '%s json %s' % (self._DMPROF_SCRIPT_PATH, - os.path.join(self._workdir, first_dump)), - shell=True, stdout=self._json_file) - if is_last: - # Wait only when it is the last profiling. dmprof may take long time. - self._WaitForDeepMemoryProfiler() - - # Upload the dumped files. - if first_dump and self._upload and self._gsutil: - if self._revision: - destination_path = '%s%s/' % (self._upload, self._revision) - else: - destination_path = self._upload - destination_path += '%s-%s-%s.zip' % ( - webapp_name, - test_description, - os.path.basename(self._workdir)) - gsutil_command = '%s upload --gsutil %s %s %s' % ( - self._DMPROF_SCRIPT_PATH, - self._gsutil, - os.path.join(self._workdir, first_dump), - destination_path) - logging.info('Uploading: %s' % gsutil_command) - try: - returncode = subprocess.call(gsutil_command, shell=True) - logging.info(' Return code: %d' % returncode) - except OSError, e: - logging.error(' Error while uploading: %s', e) - else: - logging.info('Note that the dumped files are not uploaded.') - else: - logging.info(' No dump files.') - - def ParseResultAndOutputPerfGraphValues( - self, webapp_name, test_description, output_perf_graph_value): - """Parses Deep Memory Profiler result, and outputs perf graph values.""" - if not self._enabled: - return - - results = {} - for policy in self._POLICIES: - if self._last_json_filename: - json_data = {} - with open(self._last_json_filename) as json_f: - json_data = json.load(json_f) - if json_data['version'] == 'JSON_DEEP_1': - results[policy] = json_data['snapshots'] - elif json_data['version'] == 'JSON_DEEP_2': - results[policy] = json_data['policies'][policy]['snapshots'] - for policy, result in results.iteritems(): - if result and result[-1]['second'] > self._last_time[policy]: - started = False - for legend in json_data['policies'][policy]['legends']: - if legend == 'FROM_HERE_FOR_TOTAL': - started = True - elif legend == 'UNTIL_HERE_FOR_TOTAL': - break - elif started: - output_perf_graph_value( - legend.encode('utf-8'), [ - (int(round(snapshot['second'])), snapshot[legend] / 1024) - for snapshot in result - if snapshot['second'] > self._last_time[policy]], - 'KB', - graph_name='%s%s-%s-DMP' % ( - webapp_name, test_description, policy), - units_x='seconds', is_stacked=True) - self._last_time[policy] = result[-1]['second'] - - def _WaitForDeepMemoryProfiler(self): - """Waits for the Deep Memory Profiler to finish if running.""" - if not self._enabled or not self._proc: - return - - self._proc.wait() - self._proc = None - if self._json_file: - self._last_json_filename = self._json_file.name - self._json_file.close() - self._json_file = None - - -class ChromeEndureBaseTest(perf.BasePerfTest): - """Implements common functionality for all Chrome Endure tests. - - All Chrome Endure test classes should inherit from this class. - """ - - _DEFAULT_TEST_LENGTH_SEC = 60 * 60 * 6 # Tests run for 6 hours. - _GET_PERF_STATS_INTERVAL = 60 * 5 # Measure perf stats every 5 minutes. - # TODO(dennisjeffrey): Do we still need to tolerate errors? - _ERROR_COUNT_THRESHOLD = 50 # Number of errors to tolerate. - _REVISION = '' - _GSUTIL = 'gsutil' - - def setUp(self): - # The environment variables for the Deep Memory Profiler must be set - # before perf.BasePerfTest.setUp() to inherit them to Chrome. - self._dmprof = DeepMemoryProfiler() - self._revision = str(os.environ.get('REVISION', self._REVISION)) - if not self._revision: - self._revision = str(os.environ.get('BUILDBOT_GOT_REVISION', - self._REVISION)) - self._gsutil = str(os.environ.get('GSUTIL', self._GSUTIL)) - if self._dmprof: - self._dmprof.SetUp(self.IsLinux(), self._revision, self._gsutil) - - perf.BasePerfTest.setUp(self) - - self._test_length_sec = int( - os.environ.get('TEST_LENGTH', self._DEFAULT_TEST_LENGTH_SEC)) - self._get_perf_stats_interval = int( - os.environ.get('PERF_STATS_INTERVAL', self._GET_PERF_STATS_INTERVAL)) - - logging.info('Running test for %d seconds.', self._test_length_sec) - logging.info('Gathering perf stats every %d seconds.', - self._get_perf_stats_interval) - - if self._dmprof: - self._dmprof.LogFirstMessage() - - # Set up a remote inspector client associated with tab 0. - logging.info('Setting up connection to remote inspector...') - self._remote_inspector_client = ( - remote_inspector_client.RemoteInspectorClient()) - logging.info('Connection to remote inspector set up successfully.') - - self._test_start_time = 0 - self._num_errors = 0 - self._events_to_output = [] - - def tearDown(self): - logging.info('Terminating connection to remote inspector...') - self._remote_inspector_client.Stop() - logging.info('Connection to remote inspector terminated.') - - # Must be done at end of this function except for post-cleaning after - # Chrome finishes. - perf.BasePerfTest.tearDown(self) - - # Must be done after perf.BasePerfTest.tearDown() - if self._dmprof: - self._dmprof.TearDown() - - def ExtraChromeFlags(self): - """Ensures Chrome is launched with custom flags. - - Returns: - A list of extra flags to pass to Chrome when it is launched. - """ - # The same with setUp, but need to fetch the environment variable since - # ExtraChromeFlags is called before setUp. - deep_memory_profile = DeepMemoryProfiler.GetEnvironmentVariable( - 'DEEP_MEMORY_PROFILE', bool, DeepMemoryProfiler.DEEP_MEMORY_PROFILE) - - # Ensure Chrome enables remote debugging on port 9222. This is required to - # interact with Chrome's remote inspector. - # Also, enable the memory benchmarking V8 extension for heap dumps. - extra_flags = ['--remote-debugging-port=9222', - '--enable-memory-benchmarking'] - if deep_memory_profile: - extra_flags.append('--no-sandbox') - return perf.BasePerfTest.ExtraChromeFlags(self) + extra_flags - - def _OnTimelineEvent(self, event_info): - """Invoked by the Remote Inspector Client when a timeline event occurs. - - Args: - event_info: A dictionary containing raw information associated with a - timeline event received from Chrome's remote inspector. Refer to - chrome/src/third_party/WebKit/Source/WebCore/inspector/Inspector.json - for the format of this dictionary. - """ - elapsed_time = int(round(time.time() - self._test_start_time)) - - if event_info['type'] == 'GCEvent': - self._events_to_output.append({ - 'type': 'GarbageCollection', - 'time': elapsed_time, - 'data': - {'collected_bytes': event_info['data']['usedHeapSizeDelta']}, - }) - - def _RunEndureTest(self, webapp_name, tab_title_substring, test_description, - do_scenario, frame_xpath=''): - """The main test harness function to run a general Chrome Endure test. - - After a test has performed any setup work and has navigated to the proper - starting webpage, this function should be invoked to run the endurance test. - - Args: - webapp_name: A string name for the webapp being tested. Should not - include spaces. For example, 'Gmail', 'Docs', or 'Plus'. - tab_title_substring: A unique substring contained within the title of - the tab to use, for identifying the appropriate tab. - test_description: A string description of what the test does, used for - outputting results to be graphed. Should not contain spaces. For - example, 'ComposeDiscard' for Gmail. - do_scenario: A callable to be invoked that implements the scenario to be - performed by this test. The callable is invoked iteratively for the - duration of the test. - frame_xpath: The string xpath of the frame in which to inject javascript - to clear chromedriver's cache (a temporary workaround until the - WebDriver team changes how they handle their DOM node cache). - """ - self._num_errors = 0 - self._test_start_time = time.time() - last_perf_stats_time = time.time() - if self._dmprof: - self.HeapProfilerDump('renderer', 'Chrome Endure (first)') - self._GetPerformanceStats( - webapp_name, test_description, tab_title_substring) - self._iteration_num = 0 # Available to |do_scenario| if needed. - - self._remote_inspector_client.StartTimelineEventMonitoring( - self._OnTimelineEvent) - - while time.time() - self._test_start_time < self._test_length_sec: - self._iteration_num += 1 - - if self._num_errors >= self._ERROR_COUNT_THRESHOLD: - logging.error('Error count threshold (%d) reached. Terminating test ' - 'early.' % self._ERROR_COUNT_THRESHOLD) - break - - if time.time() - last_perf_stats_time >= self._get_perf_stats_interval: - last_perf_stats_time = time.time() - if self._dmprof: - self.HeapProfilerDump('renderer', 'Chrome Endure') - self._GetPerformanceStats( - webapp_name, test_description, tab_title_substring) - - if self._iteration_num % 10 == 0: - remaining_time = self._test_length_sec - (time.time() - - self._test_start_time) - logging.info('Chrome interaction #%d. Time remaining in test: %d sec.' % - (self._iteration_num, remaining_time)) - - do_scenario() - # Clear ChromeDriver's DOM node cache so its growth doesn't affect the - # results of Chrome Endure. - # TODO(dennisjeffrey): Once the WebDriver team implements changes to - # handle their DOM node cache differently, we need to revisit this. It - # may no longer be necessary at that point to forcefully delete the cache. - # Additionally, the Javascript below relies on an internal property of - # WebDriver that may change at any time. This is only a temporary - # workaround to stabilize the Chrome Endure test results. - js = """ - (function() { - delete document.$wdc_; - window.domAutomationController.send('done'); - })(); - """ - try: - self.ExecuteJavascript(js, frame_xpath=frame_xpath) - except pyauto_errors.AutomationCommandTimeout: - self._num_errors += 1 - logging.warning('Logging an automation timeout: delete chromedriver ' - 'cache.') - - self._remote_inspector_client.StopTimelineEventMonitoring() - - if self._dmprof: - self.HeapProfilerDump('renderer', 'Chrome Endure (last)') - self._GetPerformanceStats( - webapp_name, test_description, tab_title_substring, is_last=True) - - def _GetProcessInfo(self, tab_title_substring): - """Gets process info associated with an open browser/tab. - - Args: - tab_title_substring: A unique substring contained within the title of - the tab to use; needed for locating the tab info. - - Returns: - A dictionary containing information about the browser and specified tab - process: - { - 'browser_private_mem': integer, # Private memory associated with the - # browser process, in KB. - 'tab_private_mem': integer, # Private memory associated with the tab - # process, in KB. - 'tab_pid': integer, # Process ID of the tab process. - } - """ - browser_process_name = ( - self.GetBrowserInfo()['properties']['BrowserProcessExecutableName']) - info = self.GetProcessInfo() - - # Get the information associated with the browser process. - browser_proc_info = [] - for browser_info in info['browsers']: - if browser_info['process_name'] == browser_process_name: - for proc_info in browser_info['processes']: - if proc_info['child_process_type'] == 'Browser': - browser_proc_info.append(proc_info) - self.assertEqual(len(browser_proc_info), 1, - msg='Expected to find 1 Chrome browser process, but found ' - '%d instead.\nCurrent process info:\n%s.' % ( - len(browser_proc_info), self.pformat(info))) - - # Get the process information associated with the specified tab. - tab_proc_info = [] - for browser_info in info['browsers']: - for proc_info in browser_info['processes']: - if (proc_info['child_process_type'] == 'Tab' and - [x for x in proc_info['titles'] if tab_title_substring in x]): - tab_proc_info.append(proc_info) - self.assertEqual(len(tab_proc_info), 1, - msg='Expected to find 1 %s tab process, but found %d ' - 'instead.\nCurrent process info:\n%s.' % ( - tab_title_substring, len(tab_proc_info), - self.pformat(info))) - - browser_proc_info = browser_proc_info[0] - tab_proc_info = tab_proc_info[0] - return { - 'browser_private_mem': browser_proc_info['working_set_mem']['priv'], - 'tab_private_mem': tab_proc_info['working_set_mem']['priv'], - 'tab_pid': tab_proc_info['pid'], - } - - def _GetPerformanceStats(self, webapp_name, test_description, - tab_title_substring, is_last=False): - """Gets performance statistics and outputs the results. - - Args: - webapp_name: A string name for the webapp being tested. Should not - include spaces. For example, 'Gmail', 'Docs', or 'Plus'. - test_description: A string description of what the test does, used for - outputting results to be graphed. Should not contain spaces. For - example, 'ComposeDiscard' for Gmail. - tab_title_substring: A unique substring contained within the title of - the tab to use, for identifying the appropriate tab. - is_last: A boolean value which should be True if it's the last call of - _GetPerformanceStats. The default is False. - """ - logging.info('Gathering performance stats...') - elapsed_time = int(round(time.time() - self._test_start_time)) - - memory_counts = self._remote_inspector_client.GetMemoryObjectCounts() - proc_info = self._GetProcessInfo(tab_title_substring) - - if self._dmprof: - self._dmprof.StartProfiler( - proc_info, is_last, webapp_name, test_description) - - # DOM node count. - dom_node_count = memory_counts['DOMNodeCount'] - self._OutputPerfGraphValue( - 'TotalDOMNodeCount', [(elapsed_time, dom_node_count)], 'nodes', - graph_name='%s%s-Nodes-DOM' % (webapp_name, test_description), - units_x='seconds') - - # Event listener count. - event_listener_count = memory_counts['EventListenerCount'] - self._OutputPerfGraphValue( - 'EventListenerCount', [(elapsed_time, event_listener_count)], - 'listeners', - graph_name='%s%s-EventListeners' % (webapp_name, test_description), - units_x='seconds') - - # Browser process private memory. - self._OutputPerfGraphValue( - 'BrowserPrivateMemory', - [(elapsed_time, proc_info['browser_private_mem'])], 'KB', - graph_name='%s%s-BrowserMem-Private' % (webapp_name, test_description), - units_x='seconds') - - # Tab process private memory. - self._OutputPerfGraphValue( - 'TabPrivateMemory', - [(elapsed_time, proc_info['tab_private_mem'])], 'KB', - graph_name='%s%s-TabMem-Private' % (webapp_name, test_description), - units_x='seconds') - - # V8 memory used. - v8_info = self.GetV8HeapStats() # First window, first tab. - v8_mem_used = v8_info['v8_memory_used'] / 1024.0 # Convert to KB. - self._OutputPerfGraphValue( - 'V8MemoryUsed', [(elapsed_time, v8_mem_used)], 'KB', - graph_name='%s%s-V8MemUsed' % (webapp_name, test_description), - units_x='seconds') - - # V8 memory allocated. - v8_mem_allocated = v8_info['v8_memory_allocated'] / 1024.0 # Convert to KB. - self._OutputPerfGraphValue( - 'V8MemoryAllocated', [(elapsed_time, v8_mem_allocated)], 'KB', - graph_name='%s%s-V8MemAllocated' % (webapp_name, test_description), - units_x='seconds') - - if self._dmprof: - self._dmprof.ParseResultAndOutputPerfGraphValues( - webapp_name, test_description, self._OutputPerfGraphValue) - - logging.info(' Total DOM node count: %d nodes' % dom_node_count) - logging.info(' Event listener count: %d listeners' % event_listener_count) - logging.info(' Browser process private memory: %d KB' % - proc_info['browser_private_mem']) - logging.info(' Tab process private memory: %d KB' % - proc_info['tab_private_mem']) - logging.info(' V8 memory used: %f KB' % v8_mem_used) - logging.info(' V8 memory allocated: %f KB' % v8_mem_allocated) - - # Output any new timeline events that have occurred. - if self._events_to_output: - logging.info('Logging timeline events...') - event_type_to_value_list = {} - for event_info in self._events_to_output: - if not event_info['type'] in event_type_to_value_list: - event_type_to_value_list[event_info['type']] = [] - event_type_to_value_list[event_info['type']].append( - (event_info['time'], event_info['data'])) - for event_type, value_list in event_type_to_value_list.iteritems(): - self._OutputEventGraphValue(event_type, value_list) - self._events_to_output = [] - else: - logging.info('No new timeline events to log.') - - def _GetElement(self, find_by, value): - """Gets a WebDriver element object from the webpage DOM. - - Args: - find_by: A callable that queries WebDriver for an element from the DOM. - value: A string value that can be passed to the |find_by| callable. - - Returns: - The identified WebDriver element object, if found in the DOM, or - None, otherwise. - """ - try: - return find_by(value) - except selenium.common.exceptions.NoSuchElementException: - return None - - def _ClickElementByXpath(self, driver, xpath): - """Given the xpath for a DOM element, clicks on it using WebDriver. - - Args: - driver: A WebDriver object, as returned by self.NewWebDriver(). - xpath: The string xpath associated with the DOM element to click. - - Returns: - True, if the DOM element was found and clicked successfully, or - False, otherwise. - """ - try: - self.WaitForDomNode(xpath) - except (pyauto_errors.JSONInterfaceError, - pyauto_errors.JavascriptRuntimeError) as e: - logging.exception('PyAuto exception: %s' % e) - return False - - try: - element = self._GetElement(driver.find_element_by_xpath, xpath) - element.click() - except (selenium.common.exceptions.StaleElementReferenceException, - selenium.common.exceptions.TimeoutException) as e: - logging.exception('WebDriver exception: %s' % e) - return False - - return True - - -class ChromeEndureControlTest(ChromeEndureBaseTest): - """Control tests for Chrome Endure.""" - - _WEBAPP_NAME = 'Control' - _TAB_TITLE_SUBSTRING = 'Chrome Endure Control Test' - - def testControlAttachDetachDOMTree(self): - """Continually attach and detach a DOM tree from a basic document.""" - test_description = 'AttachDetachDOMTree' - url = self.GetHttpURLForDataPath('chrome_endure', 'endurance_control.html') - self.NavigateToURL(url) - loaded_tab_title = self.GetActiveTabTitle() - self.assertTrue(self._TAB_TITLE_SUBSTRING in loaded_tab_title, - msg='Loaded tab title does not contain "%s": "%s"' % - (self._TAB_TITLE_SUBSTRING, loaded_tab_title)) - - def scenario(): - # Just sleep. Javascript in the webpage itself does the work. - time.sleep(5) - - self._RunEndureTest(self._WEBAPP_NAME, self._TAB_TITLE_SUBSTRING, - test_description, scenario) - - def testControlAttachDetachDOMTreeWebDriver(self): - """Use WebDriver to attach and detach a DOM tree from a basic document.""" - test_description = 'AttachDetachDOMTreeWebDriver' - url = self.GetHttpURLForDataPath('chrome_endure', - 'endurance_control_webdriver.html') - self.NavigateToURL(url) - loaded_tab_title = self.GetActiveTabTitle() - self.assertTrue(self._TAB_TITLE_SUBSTRING in loaded_tab_title, - msg='Loaded tab title does not contain "%s": "%s"' % - (self._TAB_TITLE_SUBSTRING, loaded_tab_title)) - - driver = self.NewWebDriver() - - def scenario(driver): - # Click the "attach" button to attach a large DOM tree (with event - # listeners) to the document, wait half a second, click "detach" to detach - # the DOM tree from the document, wait half a second. - self._ClickElementByXpath(driver, 'id("attach")') - time.sleep(0.5) - self._ClickElementByXpath(driver, 'id("detach")') - time.sleep(0.5) - - self._RunEndureTest(self._WEBAPP_NAME, self._TAB_TITLE_SUBSTRING, - test_description, lambda: scenario(driver)) - - -class IndexedDBOfflineTest(ChromeEndureBaseTest): - """Long-running performance tests for IndexedDB, modeling offline usage.""" - - _WEBAPP_NAME = 'IndexedDBOffline' - _TAB_TITLE_SUBSTRING = 'IndexedDB Offline' - - def setUp(self): - ChromeEndureBaseTest.setUp(self) - - url = self.GetHttpURLForDataPath('indexeddb', 'endure', 'app.html') - self.NavigateToURL(url) - loaded_tab_title = self.GetActiveTabTitle() - self.assertTrue(self._TAB_TITLE_SUBSTRING in loaded_tab_title, - msg='Loaded tab title does not contain "%s": "%s"' % - (self._TAB_TITLE_SUBSTRING, loaded_tab_title)) - - self._driver = self.NewWebDriver() - - def testOfflineOnline(self): - """Simulates user input while offline and sync while online. - - This test alternates between a simulated "Offline" state (where user - input events are queued) and an "Online" state (where user input events - are dequeued, sync data is staged, and sync data is unstaged). - """ - test_description = 'OnlineOfflineSync' - - def scenario(): - # Click the "Online" button and let simulated sync run for 1 second. - if not self._ClickElementByXpath(self._driver, 'id("online")'): - self._num_errors += 1 - logging.warning('Logging an automation error: click "online" button.') - - try: - self.WaitForDomNode('id("state")[text()="online"]') - except (pyauto_errors.JSONInterfaceError, - pyauto_errors.JavascriptRuntimeError): - self._num_errors += 1 - logging.warning('Logging an automation error: wait for "online".') - - time.sleep(1) - - # Click the "Offline" button and let user input occur for 1 second. - if not self._ClickElementByXpath(self._driver, 'id("offline")'): - self._num_errors += 1 - logging.warning('Logging an automation error: click "offline" button.') - - try: - self.WaitForDomNode('id("state")[text()="offline"]') - except (pyauto_errors.JSONInterfaceError, - pyauto_errors.JavascriptRuntimeError): - self._num_errors += 1 - logging.warning('Logging an automation error: wait for "offline".') - - time.sleep(1) - - self._RunEndureTest(self._WEBAPP_NAME, self._TAB_TITLE_SUBSTRING, - test_description, scenario) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/prefetch.py b/chrome/test/functional/prefetch.py deleted file mode 100755 index f53751d..0000000 --- a/chrome/test/functional/prefetch.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 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. - -"""This functional test spawns a web server, and runs chrome to point -at that web server. - -The content served contains prefetch requests, and the tests assert that the -webserver logs reflect that. - -Run like any functional test: -$ python chrome/test/functional/prefetch.py -in a repo with a built pyautolib - -The import of multiprocessing implies python 2.6 is required -""" - -import os -import time -import multiprocessing -import Queue -import string -from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer - -import pyauto_functional # Must be imported before pyauto -import pyauto - -# this class handles IPC retrieving server "logs" from our integral -# server. Each test should clear() the log, and then run asserts on -# the retrieval list. - -# at startup, the server puts an int in the queue which is its port, -# we store that for subsequent tests - -class ServerLog: - def clear(self): - self.log = {} - - def __init__(self,queue): - self.clear() - self.port = None - self.queue = queue - - def _readQueue(self): - try: - while True: - queueval = self.queue.get(False) - if isinstance(queueval,int): - self.port = queueval - else: - self.log[queueval] = True - except Queue.Empty: - return - - def getPort(self): - if not self.port: - self._readQueue() - return self.port - - def isRetrieved(self,path): - self._readQueue() - try: - return self.log[path] - except KeyError: - return None - -# -# The next few classes run a simple web server that returns log information -# via a multiprocessing.Queue. -# -class AbstractPrefetchServerHandler(BaseHTTPRequestHandler): - content = { - "prefetch-origin.html": - (200, """<html><head> -<link rel="prefetch" href="static-prefetch-target.html"> -<script type="text/javascript"> -function changeParagraph() -{ - var newPara = document.createElement("p"); - newPara.innerHTML = - "<link rel=\\"prefetch\\" href=\\"dynamic-prefetch-target.html\\">" + - "<p>This paragraph contains a dynamic link prefetch. " + - "The target of this prefetch is " + - "<a href=\\"dynamic-prefetch-target.html\\">this document.</a>"; - var para = document.getElementById("p1"); - document.body.insertBefore(newPara,para); -} -</script> -</head> -<body onload="changeParagraph()"> -<p id="p1">This is a document that contains a link prefetch. The target of -that prefetch is <a href="static-prefetch-target.html">this document.</a> -</body>"""), - "static-prefetch-target.html": - (200, "<html><head></head><body>empty</body>"), - "dynamic-prefetch-target.html": - (200, "<html><head></head><body>empty</body>")} - - def do_GET(self): - self.queue.put(self.path[1:]) - try: - response_code, response = self.content[self.path[1:]] - self.send_response(response_code) - self.end_headers() - self.wfile.write(response) - except KeyError: - self.send_response(404) - self.end_headers() - -def run_web_server(queue_arg): - class PrefetchServerHandler(AbstractPrefetchServerHandler): - queue = queue_arg - server = HTTPServer(('',0), PrefetchServerHandler) - queue.put(server.server_port) - server.serve_forever() - -# -# Here's the test itself -# -queue = multiprocessing.Queue() -server_log = ServerLog(queue) - -class PrefetchTest(pyauto.PyUITest): - """Testcase for Prefetching""" - def testBasic(self): - server_log.clear() - url = "http://localhost:%d/prefetch-origin.html" % server_log.getPort() - self.NavigateToURL(url) - self.assertEqual(True, server_log.isRetrieved("prefetch-origin.html")) - time.sleep(0.1) # required since prefetches occur after onload - self.assertEqual(True, server_log.isRetrieved( - "static-prefetch-target.html")) - self.assertEqual(True, server_log.isRetrieved( - "dynamic-prefetch-target.html")) - -if __name__ == '__main__': - web_server = multiprocessing.Process(target=run_web_server,args=(queue,)) - web_server.daemon = True - web_server.start() - pyauto_functional.Main() diff --git a/chrome/test/functional/prefs.py b/chrome/test/functional/prefs.py deleted file mode 100755 index 7dc95ae..0000000 --- a/chrome/test/functional/prefs.py +++ /dev/null @@ -1,238 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import os -import shutil - -import pyauto_functional # Must be imported before pyauto -import pyauto -import test_utils - -from webdriver_pages import settings -from webdriver_pages.settings import Behaviors, ContentTypes - - -class PrefsTest(pyauto.PyUITest): - """TestCase for Preferences.""" - - INFOBAR_TYPE = 'rph_infobar' - - def setUp(self): - pyauto.PyUITest.setUp(self) - self._driver = self.NewWebDriver() - - def Debug(self): - """Test method for experimentation. - - This method will not run automatically. - """ - while True: - raw_input('Interact with the browser and hit <enter> to dump prefs... ') - self.pprint(self.GetPrefsInfo().Prefs()) - - def testSessionRestore(self): - """Test session restore preference.""" - url1 = 'http://www.google.com/' - url2 = 'http://news.google.com/' - self.NavigateToURL(url1) - self.AppendTab(pyauto.GURL(url2)) - num_tabs = self.GetTabCount() - # Set pref to restore session on startup. - self.SetPrefs(pyauto.kRestoreOnStartup, 1) - logging.debug('Setting %s to 1' % pyauto.kRestoreOnStartup) - self.RestartBrowser(clear_profile=False) - self.assertEqual(self.GetPrefsInfo().Prefs(pyauto.kRestoreOnStartup), 1) - self.assertEqual(num_tabs, self.GetTabCount()) - self.ActivateTab(0) - self.assertEqual(url1, self.GetActiveTabURL().spec()) - self.ActivateTab(1) - self.assertEqual(url2, self.GetActiveTabURL().spec()) - - def testNavigationStateOnSessionRestore(self): - """Verify navigation state is preserved on session restore.""" - urls = ('http://www.google.com/', - 'http://news.google.com/', - 'http://dev.chromium.org/',) - for url in urls: - self.NavigateToURL(url) - self.TabGoBack() - self.assertEqual(self.GetActiveTabURL().spec(), urls[-2]) - self.SetPrefs(pyauto.kRestoreOnStartup, 1) # set pref to restore session - self.RestartBrowser(clear_profile=False) - # Verify that navigation state (forward/back state) is restored. - self.TabGoBack() - self.assertEqual(self.GetActiveTabURL().spec(), urls[0]) - for i in (-2, -1): - tab.GoForward() - self.assertEqual(self.GetActiveTabURL().spec(), urls[i]) - - def testSessionRestoreURLs(self): - """Verify restore URLs preference.""" - url1 = self.GetFileURLForPath(os.path.join(self.DataDir(), 'title1.html')) - url2 = self.GetFileURLForPath(os.path.join(self.DataDir(), 'title2.html')) - # Set pref to restore given URLs on startup - self.SetPrefs(pyauto.kRestoreOnStartup, 4) # 4 is for restoring URLs - self.SetPrefs(pyauto.kURLsToRestoreOnStartup, [url1, url2]) - self.RestartBrowser(clear_profile=False) - # Verify - self.assertEqual(self.GetPrefsInfo().Prefs(pyauto.kRestoreOnStartup), 4) - self.assertEqual(2, self.GetTabCount()) - self.ActivateTab(0) - self.assertEqual(url1, self.GetActiveTabURL().spec()) - self.ActivateTab(1) - self.assertEqual(url2, self.GetActiveTabURL().spec()) - - def testGeolocationPref(self): - """Verify geolocation pref. - - Checks for the geolocation infobar. - """ - # GetBrowserInfo() call seems to fail later on in this test. Call it early. - # crbug.com/89000 - branding = self.GetBrowserInfo()['properties']['branding'] - url = self.GetFileURLForPath(os.path.join( # triggers geolocation - self.DataDir(), 'geolocation', 'geolocation_on_load.html')) - self.assertEqual(3, # default state - self.GetPrefsInfo().Prefs(pyauto.kGeolocationDefaultContentSetting)) - self.NavigateToURL(url) - self.assertTrue(self.WaitForInfobarCount(1)) - self.assertTrue(self.GetBrowserInfo()['windows'][0]['tabs'][0]['infobars']) - # Disable geolocation - self.SetPrefs(pyauto.kGeolocationDefaultContentSetting, 2) - self.assertEqual(2, - self.GetPrefsInfo().Prefs(pyauto.kGeolocationDefaultContentSetting)) - self.ReloadTab() - # Fails on Win7/Vista Chromium bots. crbug.com/89000 - if (self.IsWin7() or self.IsWinVista()) and branding == 'Chromium': - return - behavior = self._driver.execute_async_script( - 'triggerGeoWithCallback(arguments[arguments.length - 1]);') - self.assertEqual( - behavior, Behaviors.BLOCK, - msg='Behavior is "%s" when it should be BLOCKED.' % behavior) - - def testAllowSelectedGeoTracking(self): - """Verify hostname pattern and behavior for allowed tracking.""" - # Default location tracking option "Ask me". - self.SetPrefs(pyauto.kGeolocationDefaultContentSetting, 3) - self.NavigateToURL( - self.GetHttpURLForDataPath('geolocation', 'geolocation_on_load.html')) - self.assertTrue(self.WaitForInfobarCount(1)) - self.PerformActionOnInfobar('accept', infobar_index=0) # Allow tracking. - # Get the hostname pattern (e.g. http://127.0.0.1:57622). - hostname_pattern = ( - '/'.join(self.GetHttpURLForDataPath('').split('/')[0:3])) - self.assertEqual( - # Allow the hostname. - {hostname_pattern+','+hostname_pattern: {'geolocation': 1}}, - self.GetPrefsInfo().Prefs(pyauto.kContentSettingsPatternPairs)) - - def testDismissedInfobarSavesNoEntry(self): - """Verify dismissing infobar does not save an exception entry.""" - # Default location tracking option "Ask me". - self.SetPrefs(pyauto.kGeolocationDefaultContentSetting, 3) - self.NavigateToURL( - self.GetFileURLForDataPath('geolocation', 'geolocation_on_load.html')) - self.assertTrue(self.WaitForInfobarCount(1)) - self.PerformActionOnInfobar('dismiss', infobar_index=0) - self.assertEqual( - {}, self.GetPrefsInfo().Prefs(pyauto.kContentSettingsPatternPairs)) - - def testGeolocationBlockedWhenTrackingDenied(self): - """Verify geolocations is blocked when tracking is denied. - - The test verifies the blocked hostname pattern entry on the Geolocations - exceptions page. - """ - # Ask for permission when site wants to track. - self.SetPrefs(pyauto.kGeolocationDefaultContentSetting, 3) - self.NavigateToURL( - self.GetHttpURLForDataPath('geolocation', 'geolocation_on_load.html')) - self.assertTrue(self.WaitForInfobarCount(1)) - self.PerformActionOnInfobar('cancel', infobar_index=0) # Deny tracking. - behavior = self._driver.execute_async_script( - 'triggerGeoWithCallback(arguments[arguments.length - 1]);') - self.assertEqual( - behavior, Behaviors.BLOCK, - msg='Behavior is "%s" when it should be BLOCKED.' % behavior) - # Get the hostname pattern (e.g. http://127.0.0.1:57622). - hostname_pattern = ( - '/'.join(self.GetHttpURLForDataPath('').split('/')[0:3])) - self.assertEqual( - # Block the hostname. - {hostname_pattern+','+hostname_pattern: {'geolocation': 2}}, - self.GetPrefsInfo().Prefs(pyauto.kContentSettingsPatternPairs)) - - def _CheckForVisibleImage(self, tab_index=0, windex=0): - """Checks whether or not an image is visible on the webpage. - - Args: - tab_index: Tab index. Defaults to 0 (first tab). - windex: Window index. Defaults to 0 (first window). - - Returns: - True if image is loaded, otherwise returns False if image is not loaded. - """ - # Checks whether an image is loaded by checking the area (width - # and height) of the image. If the area is non zero then the image is - # visible. If the area is zero then the image is not loaded. - # Chrome zeros the |naturalWidth| and |naturalHeight|. - script = """ - for (i=0; i < document.images.length; i++) { - if ((document.images[i].naturalWidth != 0) && - (document.images[i].naturalHeight != 0)) { - window.domAutomationController.send(true); - } - } - window.domAutomationController.send(false); - """ - return self.ExecuteJavascript(script, windex=windex, tab_index=tab_index) - - def testBlockImagesForHostname(self): - """Verify images blocked for defined hostname pattern.""" - url = 'http://www.google.com' - page = settings.ManageExceptionsPage.FromNavigation( - self._driver, ContentTypes.IMAGES) - pattern, behavior = (url, Behaviors.BLOCK) - # Add an exception BLOCK for hostname pattern 'www.google.com'. - page.AddNewException(pattern, behavior) - self.NavigateToURL(url) - self.assertFalse(self._CheckForVisibleImage(), - msg='At least one visible image found.') - - def testAllowImagesForHostname(self): - """Verify images allowed for defined hostname pattern.""" - url = 'http://www.google.com' - page = settings.ManageExceptionsPage.FromNavigation( - self._driver, ContentTypes.IMAGES) - pattern, behavior = (url, Behaviors.ALLOW) - # Add an exception ALLOW for hostname pattern 'www.google.com'. - page.AddNewException(pattern, behavior) - self.NavigateToURL(url) - self.assertTrue(self._CheckForVisibleImage(), - msg='No visible images found.') - - def testProtocolHandlerRegisteredCorrectly(self): - """Verify sites that ask to be default handlers registers correctly.""" - url = self.GetHttpURLForDataPath('settings', 'protocol_handler.html') - self.NavigateToURL(url) - # Returns a dictionary with the custom handler. - asked_handler_dict = self._driver.execute_script( - 'return registerCustomHandler()') - self.PerformActionOnInfobar( - 'accept', infobar_index=test_utils.WaitForInfobarTypeAndGetIndex( - self, self.INFOBAR_TYPE)) - self._driver.find_element_by_id('test_protocol').click() - self.assertTrue( - self._driver.execute_script( - 'return doesQueryConformsToProtocol("%s", "%s")' - % (asked_handler_dict['query_key'], - asked_handler_dict['query_value'])), - msg='Protocol did not register correctly.') - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/prefs_ui.py b/chrome/test/functional/prefs_ui.py deleted file mode 100755 index a4f0c03..0000000 --- a/chrome/test/functional/prefs_ui.py +++ /dev/null @@ -1,343 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import time - -import pyauto_functional # Must be imported before pyauto -import pyauto -import test_utils - -from webdriver_pages import settings -from webdriver_pages.settings import Behaviors, ContentTypes -from webdriver_pages.settings import RestoreOnStartupType - - -class PrefsUITest(pyauto.PyUITest): - """TestCase for Preferences UI.""" - - INFOBAR_TYPE = 'rph_infobar' - - def setUp(self): - pyauto.PyUITest.setUp(self) - self._driver = self.NewWebDriver() - - def Debug(self): - """Test method for experimentation. - - This method will not run automatically. - """ - driver = self.NewWebDriver() - page = settings.ContentSettingsPage.FromNavigation(driver) - import pdb - pdb.set_trace() - - def _GetGeolocationContentSettingsBehavior(self): - """Get the Content Settings behavior for Geolocation. - - Returns: - The exceptions behavior for the specified content type. - The content type is the available content setting available. - """ - behavior_key = self.GetPrefsInfo().Prefs( - pyauto.kGeolocationDefaultContentSetting) - behaviors_dict = {1: 'ALLOW', 2: 'BLOCK', 3: 'ASK'} - self.assertTrue( - behavior_key in behaviors_dict, - msg=('Invalid default behavior key "%s" for "geolocation" content' % - behavior_key)) - return behaviors_dict[behavior_key] - - def _VerifyContentExceptionUI(self, content_type, hostname_pattern, behavior, - incognito=False): - """Find hostname pattern and behavior within UI on content exceptions page. - - Args: - content_type: The string content settings type to manage. - hostname_pattern: The URL or pattern associated with the behavior. - behavior: The exception to allow or block the hostname. - incognito: Incognito list displayed on exceptions settings page. - Default to False. - """ - page = settings.ManageExceptionsPage.FromNavigation( - self._driver, content_type) - self.assertTrue(page.GetExceptions(incognito).has_key(hostname_pattern), - msg=('No displayed host name matches pattern "%s"' - % hostname_pattern)) - self.assertEqual(behavior, page.GetExceptions(incognito)[hostname_pattern], - msg=('Displayed behavior "%s" does not match behavior "%s"' - % (page.GetExceptions(incognito)[hostname_pattern], - behavior))) - - def testLocationSettingOptionsUI(self): - """Verify the location options setting UI. - - Set the options through the UI using webdriver and verify the settings in - pyAuto. - """ - page = settings.ContentSettingsPage.FromNavigation(self._driver) - page.SetContentTypeOption(ContentTypes.GEOLOCATION, Behaviors.ALLOW) - self.assertEqual( - 1, self.GetPrefsInfo().Prefs(pyauto.kGeolocationDefaultContentSetting)) - page.SetContentTypeOption(ContentTypes.GEOLOCATION, Behaviors.BLOCK) - self.assertEqual( - 2, self.GetPrefsInfo().Prefs(pyauto.kGeolocationDefaultContentSetting)) - page.SetContentTypeOption(ContentTypes.GEOLOCATION, Behaviors.ASK) - self.assertEqual( - 3, self.GetPrefsInfo().Prefs(pyauto.kGeolocationDefaultContentSetting)) - - def testBehaviorValueCorrectlyDisplayed(self): - """Verify the set behavior value is correctly displayed.""" - # Block all sites. - self.SetPrefs(pyauto.kGeolocationDefaultContentSetting, 2) - self.assertEqual( - self._GetGeolocationContentSettingsBehavior(), Behaviors.BLOCK.upper(), - msg='The behavior was incorrectly set.') - # Allow all sites. - self.SetPrefs(pyauto.kGeolocationDefaultContentSetting, 1) - self.assertEqual( - self._GetGeolocationContentSettingsBehavior(), Behaviors.ALLOW.upper(), - msg='The behavior was incorrectly set.') - # Ask for permission when site wants to track. - self.SetPrefs(pyauto.kGeolocationDefaultContentSetting, 3) - self.assertEqual( - self._GetGeolocationContentSettingsBehavior(), Behaviors.ASK.upper(), - msg='The behavior was incorrectly set.') - - def testExceptionsEntryCorrectlyDisplayed(self): - """Verify the exceptions line entry is correctly displayed in the UI.""" - geo_exception = ( - {'http://maps.google.com:80,http://maps.google.com:80': - {'geolocation': 2}}) - self.SetPrefs(pyauto.kContentSettingsPatternPairs, geo_exception) - self._VerifyContentExceptionUI( - ContentTypes.GEOLOCATION, 'http://maps.google.com:80', - Behaviors.BLOCK) - geo_exception = ( - {'http://maps.google.com:80,http://maps.google.com:80': - {'geolocation': 1}}) - self.SetPrefs(pyauto.kContentSettingsPatternPairs, geo_exception) - self._VerifyContentExceptionUI( - ContentTypes.GEOLOCATION, 'http://maps.google.com:80', - Behaviors.ALLOW) - geo_exception = ( - {'http://maps.google.com:80,http://maps.google.com:80': - {'geolocation': 3}}) - self.SetPrefs(pyauto.kContentSettingsPatternPairs, geo_exception) - self._VerifyContentExceptionUI( - ContentTypes.GEOLOCATION, 'http://maps.google.com:80', Behaviors.ASK) - - def testAddNewExceptionUI(self): - """Verify new exception added for hostname pattern and behavior in UI.""" - content_type = ContentTypes.PLUGINS - page = settings.ManageExceptionsPage.FromNavigation( - self._driver, content_type) - - pattern, behavior = ('bing.com', Behaviors.BLOCK) - page.AddNewException(pattern, behavior) - self.assertEqual(page.GetExceptions()[pattern], Behaviors.BLOCK, - msg='The behavior "%s" was not added for pattern "%s"' - % (behavior, pattern)) - - def testChangeExceptionBehaviorUI(self): - """Verify behavior for hostname pattern is changed in the UI.""" - content_type = ContentTypes.PLUGINS - page = settings.ManageExceptionsPage.FromNavigation( - self._driver, content_type) - - pattern, behavior = ('bing.com', Behaviors.BLOCK) - page.AddNewException(pattern, behavior) - new_behavior = Behaviors.ALLOW - page.SetBehaviorForPattern(pattern, new_behavior) - self.assertEqual(page.GetExceptions()[pattern], Behaviors.ALLOW, - msg='The behavior for "%s" did not change: "%s"' - % (pattern, behavior)) - - def testDeleteExceptionUI(self): - """Verify exception deleted for hostname pattern and behavior in the UI.""" - content_type = ContentTypes.PLUGINS - page = settings.ManageExceptionsPage.FromNavigation( - self._driver, content_type) - - pattern, behavior = ('bing.com', Behaviors.BLOCK) - page.AddNewException(pattern, behavior) - self.assertEqual(page.GetExceptions()[pattern], Behaviors.BLOCK, - msg='The behavior "%s" was not added for pattern "%s"' - % (behavior, pattern)) - page.DeleteException(pattern) - self.assertEqual(page.GetExceptions().get(pattern, KeyError), KeyError, - msg='Pattern "%s" was not deleted' % pattern) - - def testNoInitialLineEntryInUI(self): - """Verify no initial line entry is displayed in UI.""" - # Ask for permission when site wants to track. - self.SetPrefs(pyauto.kGeolocationDefaultContentSetting, 3) - self.assertEqual( - 3, self.GetPrefsInfo().Prefs(pyauto.kGeolocationDefaultContentSetting)) - page = settings.ManageExceptionsPage.FromNavigation( - self._driver, ContentTypes.GEOLOCATION) - self.assertEqual(0, len(page.GetExceptions())) - - def testCorrectCookiesSessionInUI(self): - """Verify exceptions for cookies in UI list entry.""" - # Block cookies for for a session for google.com. - self.SetPrefs(pyauto.kContentSettingsPatternPairs, - {'http://google.com:80': {'cookies': 2}}) - self._VerifyContentExceptionUI( - ContentTypes.COOKIES, 'http://google.com:80', Behaviors.BLOCK) - - def testInitialLineEntryInIncognitoUI(self): - """Verify initial line entry is displayed in Incognito UI.""" - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) # Display incognito list. - page = settings.ManageExceptionsPage.FromNavigation( - self._driver, ContentTypes.PLUGINS) - self.assertEqual(1, len(page.GetExceptions(incognito=True))) - - def testIncognitoExceptionsEntryCorrectlyDisplayed(self): - """Verify exceptions entry is correctly displayed in the incognito UI.""" - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) # Display incognito list. - page = settings.ManageExceptionsPage.FromNavigation( - self._driver, ContentTypes.PLUGINS) - pattern, behavior = ('http://maps.google.com:80', Behaviors.BLOCK) - page.AddNewException(pattern, behavior, incognito=True) - self._VerifyContentExceptionUI( - ContentTypes.PLUGINS, 'http://maps.google.com:80', - Behaviors.BLOCK, incognito=True) - - def testSetCookieAndDeleteInContentSettings(self): - """Verify a cookie can be deleted in the Content Settings UI.""" - # Create a cookie. - cookie_dict = { - 'name': 'test_cookie', - 'value': 'test_value', - 'expiry': time.time() + 30, - } - site = '127.0.0.1' - self.NavigateToURL(self.GetHttpURLForDataPath('google', 'google.html')) - self._driver.add_cookie(cookie_dict) - page = settings.CookiesAndSiteDataSettings.FromNavigation(self._driver) - page.DeleteSiteData(site) - self.assertTrue(site not in page.GetSiteNameList(), - 'Site "%s" was not deleted.' % site) - - def testRemoveMailProtocolHandler(self): - """Verify the mail protocol handler is added and removed successfully.""" - url = self.GetHttpURLForDataPath('settings', 'protocol_handler.html') - self.NavigateToURL(url) - # Returns a dictionary with the mail handler that was asked for - # registration. - asked_handler_dict = self._driver.execute_script( - 'return registerMailClient()') - self.PerformActionOnInfobar( - 'accept', infobar_index=test_utils.WaitForInfobarTypeAndGetIndex( - self, self.INFOBAR_TYPE)) - self._driver.find_element_by_id('test_mail_protocol').click() - - protocol_handlers_list = ( - self.GetPrefsInfo().Prefs(pyauto.kRegisteredProtocolHandlers)) - registered_mail_handler = {} - for handler_dict in protocol_handlers_list: - if (handler_dict['protocol'] == 'mailto' and - handler_dict['url'] == asked_handler_dict['url'] and - handler_dict['title'] == asked_handler_dict['title'] and - handler_dict.get('default')): - registered_mail_handler = handler_dict - break - # Verify the mail handler is registered as asked. - self.assertNotEqual( - registered_mail_handler, {}, - msg='Mail protocol handler was not registered correctly.') - # Verify the registered mail handler works as expected. - self.assertTrue( - self._driver.execute_script( - 'return doesQueryConformsToProtocol("%s", "%s")' - % (asked_handler_dict['query_key'], - asked_handler_dict['query_value'])), - msg='Mail protocol did not register correctly.') - - self._driver.get('chrome://settings-frame/handlers') - # There are 3 DIVs in a handler entry. The last one acts as a remove button. - # The remove button is also equivalent to setting the site to NONE. - self._driver.find_element_by_id('handlers-list').\ - find_element_by_xpath('.//div[@role="listitem"]').\ - find_element_by_xpath('.//div[@class="handlers-site-column"]').\ - find_element_by_xpath('.//option[@value="-1"]').click() - - self._driver.get(url) - self._driver.find_element_by_id('test_mail_protocol').click() - self.assertEqual(url, self._driver.current_url, - msg='Mail protocol still registered.') - -class BasicSettingsUITest(pyauto.PyUITest): - """Testcases for uber page basic settings UI.""" - - def setUp(self): - pyauto.PyUITest.setUp(self) - self._driver = self.NewWebDriver() - - def Debug(self): - """chrome://plugins test debug method. - - This method will not run automatically. - """ - driver = self.NewWebDriver() - page = settings.BasicSettingsPage.FromNavigation(driver) - import pdb - pdb.set_trace() - - def testOnStartupSettings(self): - """Verify user can set startup options.""" - page = settings.BasicSettingsPage.FromNavigation(self._driver) - page.SetOnStartupOptions(RestoreOnStartupType.NEW_TAB_PAGE) - self.assertEqual(RestoreOnStartupType.NEW_TAB_PAGE, - self.GetPrefsInfo().Prefs(pyauto.kRestoreOnStartup)) - page.SetOnStartupOptions(RestoreOnStartupType.RESTORE_SESSION) - self.assertEqual(RestoreOnStartupType.RESTORE_SESSION, - self.GetPrefsInfo().Prefs(pyauto.kRestoreOnStartup)) - page.SetOnStartupOptions(RestoreOnStartupType.RESTORE_URLS) - self.assertEqual(RestoreOnStartupType.RESTORE_URLS, - self.GetPrefsInfo().Prefs(pyauto.kRestoreOnStartup)) - - def testSetStartupPages(self): - """Verify user can add urls for startup pages.""" - page = settings.BasicSettingsPage.FromNavigation(self._driver) - for url in ['www.google.com', 'http://www.amazon.com', 'ebay.com']: - page.AddStartupPage(url) - self.assertEqual(RestoreOnStartupType.RESTORE_URLS, - self.GetPrefsInfo().Prefs(pyauto.kRestoreOnStartup)) - startup_urls = self.GetPrefsInfo().Prefs(pyauto.kURLsToRestoreOnStartup) - self.assertEqual(startup_urls[0], 'http://www.google.com/') - self.assertEqual(startup_urls[1], 'http://www.amazon.com/') - self.assertEqual(startup_urls[2], 'http://ebay.com/') - - def testUseCurrentPagesForStartup(self): - """Verify user can start up browser using current pages.""" - page = settings.BasicSettingsPage.FromNavigation(self._driver) - self.OpenNewBrowserWindow(True) - url1 = self.GetHttpURLForDataPath('title2.html') - url2 = self.GetHttpURLForDataPath('title3.html') - self.NavigateToURL(url1, 1, 0) - self.AppendTab(pyauto.GURL(url2), 1) - title_list = ['Title Of Awesomeness', - 'Title Of More Awesomeness'] - page.UseCurrentPageForStartup(title_list) - page.VerifyStartupURLs(title_list) - self.assertEqual(RestoreOnStartupType.RESTORE_URLS, - self.GetPrefsInfo().Prefs(pyauto.kRestoreOnStartup)) - startup_urls = self.GetPrefsInfo().Prefs(pyauto.kURLsToRestoreOnStartup) - self.assertEqual(len(startup_urls), 3) - self.assertEqual(startup_urls[1], url1) - self.assertEqual(startup_urls[2], url2) - - def testCancelStartupURLSetting(self): - """Verify canceled start up URLs settings are not saved.""" - page = settings.BasicSettingsPage.FromNavigation(self._driver) - for url in ['www.google.com', 'http://www.amazon.com']: - page.CancelStartupURLSetting(url) - startup_urls = self.GetPrefsInfo().Prefs(pyauto.kURLsToRestoreOnStartup) - self.assertEqual(len(startup_urls), 0) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/pyauto_functional.py b/chrome/test/functional/pyauto_functional.py deleted file mode 100755 index fa9b8a5..0000000 --- a/chrome/test/functional/pyauto_functional.py +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Setup for PyAuto functional tests. - -Use the following in your scripts to run them standalone: - -# This should be at the top -import pyauto_functional - -if __name__ == '__main__': - pyauto_functional.Main() - -This script can be used as an executable to fire off other scripts, similar -to unittest.py - python pyauto_functional.py test_script -""" - -import os -import subprocess -import sys - - -def _LocatePyAutoDir(): - sys.path.append(os.path.join(os.path.dirname(__file__), - os.pardir, 'pyautolib')) - - -_LocatePyAutoDir() -import pyauto_paths - - -def RunWithCorrectPythonIfNecessary(): - """Runs this script with the correct version of python if necessary. - - Different platforms and versions of pyautolib use different python versions. - Instead of requiring testers and infrastructure to handle choosing the right - version (and architecture), this will rerun the script with the correct - version of python. - - Note, this function will either return after doing nothing, or will exit with - the subprocess's return code. - """ - def RunAgain(): - """Run the script again with the correct version of python. - - Note, this function does not return, but exits with the return code of the - child. - """ - if sys.platform == 'cygwin' or sys.platform.startswith('win'): - cmd = [sys.executable] - elif sys.platform.startswith('darwin'): - # Arch runs the specified architecture of a universal binary. Run - # the 32 bit one. - cmd = ['arch', '-i386', 'python2.6'] - elif sys.platform.startswith('linux'): - cmd = ['python2.6'] - - cmd.extend(sys.argv) - print 'Running:', ' '.join(cmd) - proc = subprocess.Popen(cmd) - proc.wait() - sys.exit(proc.returncode) - - def IsChromeOS(): - lsb_release = '/etc/lsb-release' - if sys.platform.startswith('linux') and os.path.isfile(lsb_release): - with open(lsb_release) as fp: - contents = fp.read() - return 'CHROMEOS_RELEASE_NAME=' in contents - return False - - # Ensure this is the right python version (2.6 for chrome, 2.7 for chromeOS). - if IsChromeOS(): - if sys.version_info[0:2] != (2, 7): - cmd = ['python2.7'] + sys.argv - print 'Running: ', ' '.join(cmd) - proc = subprocess.Popen(cmd) - proc.wait() - else: - if sys.version_info[0:2] != (2, 6): - RunAgain() - - # Check this is the right bitness on mac. - # platform.architecture() will not help us on mac, since multiple binaries - # are stuffed inside the universal python binary. - if sys.platform.startswith('darwin') and sys.maxint > 2**32: - # User is running 64-bit python, but we should use 32-bit. - RunAgain() - - -# Do not attempt to figure out python versions if -# DO_NOT_RESTART_PYTHON_FOR_PYAUTO is set. -if os.getenv('DO_NOT_RESTART_PYTHON_FOR_PYAUTO') is None: - RunWithCorrectPythonIfNecessary() -else: - print 'Will not try to restart with the correct version of python '\ - 'as DO_NOT_RESTART_PYTHON_FOR_PYAUTO is set.' - - -try: - import pyauto -except ImportError: - print >>sys.stderr, 'Cannot import pyauto from %s' % sys.path - raise - - -class Main(pyauto.Main): - """Main program for running PyAuto functional tests.""" - - def __init__(self): - # Make scripts in this dir importable - sys.path.append(os.path.dirname(__file__)) - pyauto.Main.__init__(self) - - def TestsDir(self): - return os.path.dirname(__file__) - - -if __name__ == '__main__': - Main() diff --git a/chrome/test/functional/pyauto_webdriver.py b/chrome/test/functional/pyauto_webdriver.py deleted file mode 100755 index deb39c0..0000000 --- a/chrome/test/functional/pyauto_webdriver.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import pyauto_functional -import pyauto - - -class PyAutoWebDriverTest(pyauto.PyUITest): - """Tests PyAuto-WebDriver integration.""" - - def testTypeIntoTextBox(self): - """Type into a text input box and verify its value.""" - driver = self.NewWebDriver() - driver.get('about:blank') - driver.execute_script('document.body.innerHTML = "<input type=\'text\'>"') - input = driver.find_element_by_tag_name('input') - self.assertEquals('', input.get_attribute('value')) - input.send_keys('test') - self.assertEquals('test', input.get_attribute('value')) - - def testCanConnectToRestartedBrowser(self): - """Restart the browser and connect again with WebDriver.""" - driver = self.NewWebDriver() - self.RestartBrowser() - driver = self.NewWebDriver() - driver.get('about:blank') - self.assertEquals('about:blank', driver.title) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/remote_host_functional.py b/chrome/test/functional/remote_host_functional.py deleted file mode 100755 index 4d9209d..0000000 --- a/chrome/test/functional/remote_host_functional.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import sys - -import pyauto_functional -import pyauto -import remote_host - - -if __name__ == '__main__': - pyauto_suite = pyauto.PyUITestSuite(sys.argv) - remote_host.RemoteHost(('', 7410)) - del pyauto_suite diff --git a/chrome/test/functional/rlz/rlztest.py b/chrome/test/functional/rlz/rlztest.py deleted file mode 100755 index 3d89514..0000000 --- a/chrome/test/functional/rlz/rlztest.py +++ /dev/null @@ -1,270 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import chromium_proxy_server_ex -import commands -import logging -import optparse -import os -import re -import selenium.selenium -import shutil -import subprocess -import sys -import tempfile -import time -import unittest -import _winreg -import selenium.webdriver.common.keys - -from selenium import webdriver -from selenium.webdriver.common.keys import Keys as Keys -from selenium.webdriver.support.ui import WebDriverWait - -class RlzTest(unittest.TestCase): - - proxy_server_file = '' - chrome_driver_path = '' - - def setUp(self): - """Performs necessary setup work before running each test in this class.""" - # Delete RLZ key Folder - self.deleteRegistryKey() - # Launch Proxy Server - print ('Serving clients: 127.0.0.1') - self.proxy_server = subprocess.Popen([ - 'python', - RlzTest.proxy_server_file, - '--client=127.0.0.1', - '--port=8080', - '--urls=http://clients1.google.com/tools/pso/ping,www.google.com']) - print('\nLaunching Chrome...') - # Launch chrome and set proxy. - self.temp_data_dir = tempfile.mkdtemp() - self.launchChrome(self.temp_data_dir) - - def tearDown(self): - """Kills the chrome driver after after the test method has been called.""" - # Terminate the chrome driver. - print '\nTerminating Chrome Driver...' - self.driver.quit() - - # Kill proxy server. - print '\nKilling Proxy Server...' - subprocess.Popen.kill(self.proxy_server) - - # Delete temp profile directory - try: - shutil.rmtree(self.temp_data_dir) # delete directory - except OSError, e: - if e.errno != 2: # code 2 - no such file or directory - raise - - def launchChrome(self, data_directory): - """Launch chrome using chrome driver. - - Args: - data_directory: Temp directory to store preference data. - """ - service = webdriver.chrome.service.Service(RlzTest.chrome_driver_path) - service.start() - self.driver = webdriver.Remote( - service.service_url, { - 'proxy': {'proxyType': 'manual', 'httpProxy': 'localhost:8080'}, - 'chrome.nativeEvents': True, - 'chrome.switches': ['disable-extensions', - r'user-data-dir=' + data_directory]}) - - def deleteRegistryKey(self): - """Delete RLZ key Folder from win registry.""" - try: - hkey = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, - 'Software\\Google\\Common\\Rlz') - except _winreg.error, err: - return True - if(hkey): - _winreg.DeleteKey(_winreg.HKEY_CURRENT_USER, - 'Software\\Google\\Common\\Rlz\\Events\\C') - _winreg.DeleteKey(_winreg.HKEY_CURRENT_USER, - 'Software\\Google\\Common\\Rlz\\StatefulEvents\\C') - _winreg.DeleteKey(_winreg.HKEY_CURRENT_USER, - 'Software\\Google\\Common\\Rlz\\Events') - _winreg.DeleteKey(_winreg.HKEY_CURRENT_USER, - 'Software\\Google\\Common\\Rlz\\StatefulEvents') - _winreg.DeleteKey(_winreg.HKEY_CURRENT_USER, - 'Software\\Google\\Common\\Rlz\\PTimes') - _winreg.DeleteKey(_winreg.HKEY_CURRENT_USER, - 'Software\\Google\\Common\\Rlz\\RLZs') - _winreg.DeleteKey(_winreg.HKEY_CURRENT_USER, - 'Software\\Google\\Common\\Rlz') - - def GetKeyValueNames(self, key, subkey): - """Get the values for particular subkey - - Args: - key: Key is one of the predefined HKEY_* constants. - subkey: It is a string that identifies the sub_key to delete. - """ - list_names = [] - counter = 0 - try: - hkey = _winreg.OpenKey(key, subkey) - except _winreg.error, err: - return -1 - while True: - try: - value = _winreg.EnumValue(hkey, counter) - list_names.append(value[0]) - counter += 1 - except _winreg.error: - break - hkey.Close() - return list_names - - def _AssertEventsInPing(self, log_file, excepted_event_list, readFile=1): - """ Asserts events in ping appended. - - Args: - contents: String variable contains contents of log file. - excepted_event_list: List of expected events in ping. - readFile: Reading order for file. Default is 1 (Top to Bottom). - """ - for line in log_file[::readFile]: - if(re.search('events=', line)): - event_start = line.find('events=') - event_end = line.find('&rep', event_start) - events = line[event_start + 7 : event_end] - events_List = events.split(',') - print 'event_list',events_List - break - # Validate events in url ping. - for event in excepted_event_list: - self.assertTrue(event in events_List) - # Validate brand code in url ping. - self.assertTrue(re.search('CHMZ', line)) - # Print first chrome launch ping on Console. - start = line.find('http://clients1.google.com/tools/'+ - 'pso/ping?as=chrome&brand=CHMZ') - end = line.find('http://www', start) - print '\nChrome Launch ping sent :\n', line[start:end] - - - def _AssertEventsInRegistry(self, excepted_reg_keys): - """ Asserts events reported in win registry. - - Args: - excepted_reg_keys: List of expected events in win registry. - """ - list_key=self.GetKeyValueNames(_winreg.HKEY_CURRENT_USER, - 'Software\Google\Common\Rlz\StatefulEvents\C') - print ('\nList of event reported to registry-' - 'Software\Google\Common\Rlz\StatefulEvents:', list_key) - for key in excepted_reg_keys: - self.assertTrue(key in list_key) - - def _AssertRlzValues(self, log_file, readFile=1): - """ Asserts RLZ values. - - Args: - log_file: String variable contains contents of log file. - readFile: Reading order for file. Default is 1 (Top to Bottom). - """ - for line in log_file[::readFile]: - if(re.search('events=', line)): - event_start = line.find('rlz=') - event_end = line.find('&id', event_start) - events = line[event_start + 4 : event_end] - events_List = events.split(',') - self.assertTrue('C1:' in events_List) - self.assertTrue('C2:' in events_List) - - def _searchFromOmnibox(self, searchString): - """ Asserts RLZ values. - - Args: - searchString: Input string to be searched. - """ - self.driver.switch_to_active_element().send_keys(Keys.CONTROL + 'l') - self.driver.switch_to_active_element().send_keys(searchString) - self.driver.switch_to_active_element().send_keys(Keys.ENTER) - time.sleep(2) - - def testRlzPingAtFirstChromeLaunch(self): - """Test rlz ping when chrome is launched for first time.""" - # Wait for 100 sec till chrome sends ping to server. - time.sleep(100) - self.driver.get('http://www.google.com') - - # Open log file. - log_file = open( - os.getcwd() + '\chromium_proxy_server.log', 'r', 1).readlines() - - # Validate events first chrome launch(C1I,C2I,C1S) are appended in ping. - excepted_events = ['C1S', 'C1I', 'C2I'] - self._AssertEventsInPing(log_file, excepted_events) - - # Validate events in win registry. - excepted_reg_keys = ['C1I', 'C2I'] - self._AssertEventsInRegistry(excepted_reg_keys) - - def testRlzPingForFirstSearch(self): - """Test rlz ping when first search is performed in chrome.""" - # Type search string in omnibox. - self._searchFromOmnibox('java') - print '\nCurrent Url before chrome ping sent:\n', self.driver.current_url - - # Assert brand code 'CHMZ' is not appended in search string. - self.assertFalse(re.search('CHMZ', self.driver.current_url)) - - # Wait for 100 sec till chrome sends ping to server. - time.sleep(100) - - # Open log file. - log_file = open( - os.getcwd() + '\chromium_proxy_server.log', 'r', 1).readlines() - - # Validate events first chrome launch(C1I,C2I,C1S) and - # first search(C1F) are appended in ping. - excepted_events = ['C1S', 'C1I', 'C2I', 'C1F'] - self._AssertEventsInPing(log_file, excepted_events) - - # Assert C1, C2 rlz value appended in ping - self._AssertRlzValues(log_file) - - # Type search string in omnibox after ping is sent to server. - self._searchFromOmnibox('java') - print '\nCurrent Url after chrome ping sent:\n', self.driver.current_url - - # Assert brand code 'CHMZ' is appended in search string. - self.assertTrue(re.search('CHMZ', self.driver.current_url)) - - # Validate events in win registry. - excepted_reg_keys=['C1I', 'C2I', 'C1F'] - self._AssertEventsInRegistry(excepted_reg_keys) - - # Assert the log for search ping with query string/brand code appended. - log_file = open( - os.getcwd() + '\chromium_proxy_server.log', 'r', 1).readlines() - searchStringFound = False - for line in log_file[::-1]: - if(re.search('search?', line)): - self.assertTrue(re.search('java', line)) - self.assertTrue(re.search('CHMZ', line)) - print '\nChrome search ping send\n', line - searchStringFound = True - break - self.assertTrue(searchStringFound, 'Search Query String Not Found') - -def rlzInput(): - proxy_server_file = raw_input("Enter Proxy Server File Name: ") - chrome_driver_path = raw_input("Enter chrome driver path in"+ - "your system(c:\\chrome\\..):") - return (proxy_server_file, chrome_driver_path) - -if __name__ == '__main__': - server, driver = rlzInput() - RlzTest.proxy_server_file = server - RlzTest.chrome_driver_path = driver - unittest.main() diff --git a/chrome/test/functional/search_engines.py b/chrome/test/functional/search_engines.py deleted file mode 100755 index 2fcf9e8..0000000 --- a/chrome/test/functional/search_engines.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import re - -import pyauto_functional # Must be imported before pyauto -import pyauto -import test_utils - - -class SearchEnginesTest(pyauto.PyUITest): - """TestCase for Search Engines.""" - - _localhost_prefix = 'http://localhost:1000/' - - def _GetSearchEngineWithKeyword(self, keyword): - """Get search engine info and return an element that matches keyword. - - Args: - keyword: Search engine keyword field. - - Returns: - A search engine info dict or None. - """ - match_list = ([x for x in self.GetSearchEngineInfo() - if x['keyword'] == keyword]) - if match_list: - return match_list[0] - return None - - def Debug(self): - """Test method for experimentation. - - This method will not run automatically. - """ - while True: - raw_input('Interact with the browser and hit <enter>') - self.pprint(self.GetSearchEngineInfo()) - - def testDiscoverSearchEngine(self): - """Test that chrome discovers youtube search engine after searching.""" - # Take a snapshot of current search engine info. - info = self.GetSearchEngineInfo() - youtube = self._GetSearchEngineWithKeyword('youtube.com') - self.assertFalse(youtube) - # Use omnibox to invoke search engine discovery. - # Navigating using NavigateToURL does not currently invoke this logic. - self.SetOmniboxText('http://www.youtube.com') - self.OmniboxAcceptInput() - def InfoUpdated(old_info): - new_info = self.GetSearchEngineInfo() - if len(new_info) > len(old_info): - return True - return False - self.WaitUntil(lambda: InfoUpdated(info)) - youtube = self._GetSearchEngineWithKeyword('youtube.com') - self.assertTrue(youtube) - self.assertTrue(re.search('youtube', youtube['short_name'], - re.IGNORECASE)) - self.assertFalse(youtube['in_default_list']) - self.assertFalse(youtube['is_default']) - - def testDeleteSearchEngine(self): - """Test adding then deleting a search engine.""" - self.AddSearchEngine(title='foo', - keyword='foo.com', - url='http://foo/?q=%s') - foo = self._GetSearchEngineWithKeyword('foo.com') - self.assertTrue(foo) - self.DeleteSearchEngine('foo.com') - foo = self._GetSearchEngineWithKeyword('foo.com') - self.assertFalse(foo) - - def testMakeSearchEngineDefault(self): - """Test adding then making a search engine default.""" - self.AddSearchEngine( - title='foo', - keyword='foo.com', - url=self._localhost_prefix + '?q=%s') - foo = self._GetSearchEngineWithKeyword('foo.com') - self.assertTrue(foo) - self.assertFalse(foo['is_default']) - self.MakeSearchEngineDefault('foo.com') - foo = self._GetSearchEngineWithKeyword('foo.com') - self.assertTrue(foo) - self.assertTrue(foo['is_default']) - self.SetOmniboxText('foobar') - self.OmniboxAcceptInput() - self.assertEqual(self._localhost_prefix + '?q=foobar', - self.GetActiveTabURL().spec()) - - def testDefaultSearchEngines(self): - """Test that we have 3 default search options.""" - info = self.GetSearchEngineInfo() - self.assertEqual(len(info), 3) - # Verify that each can be used as the default search provider. - default_providers = ['google.com', 'yahoo.com', 'bing.com'] - for keyword in default_providers: - self.MakeSearchEngineDefault(keyword) - search_engine = self._GetSearchEngineWithKeyword(keyword) - self.assertTrue(search_engine['is_default']) - self.SetOmniboxText('test search') - self.OmniboxAcceptInput() - self.assertTrue(re.search(keyword, self.GetActiveTabURL().spec())) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/secure_shell.py b/chrome/test/functional/secure_shell.py deleted file mode 100755 index d9e59a6..0000000 --- a/chrome/test/functional/secure_shell.py +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import glob -import logging -import os -import time - -import pyauto_functional # must be imported before pyauto -import pyauto - - -class SecureShellTest(pyauto.PyUITest): - """Tests for Secure Shell app. - - Uses app from chrome/test/data/extensions/secure_shell/. - The test uses stable app by default. - Set the env var SECURE_SHELL_USE_DEV=1 to make it use the dev one. - """ - - assert pyauto.PyUITest.IsChromeOS(), 'Works on ChromeOS only' - - def setUp(self): - """Install secure shell app at startup.""" - pyauto.PyUITest.setUp(self) - - # Pick app from data dir. - app_dir = os.path.join(os.path.abspath( - self.DataDir()), 'extensions', 'secure_shell') - channel = 'dev' if os.getenv('SECURE_SHELL_USE_DEV') else 'stable' - files = glob.glob(os.path.join(app_dir, 'SecureShell-%s-*.crx' % channel)) - assert files, 'Secure Shell %s app missing in %s' % (channel, app_dir) - app_path = files[0] - - # Install app. - logging.debug('Using Secure shell app %s' % app_path) - self._app_id = self.InstallExtension(app_path, from_webstore=True) - - def testInstall(self): - """Install Secure Shell.""" - # Installation already done in setUp. Just verify. - self.assertTrue(self._app_id) - ssh_info = [x for x in self.GetExtensionsInfo() - if x['id'] == self._app_id][0] - self.assertTrue(ssh_info) - # Uninstall. - self.UninstallExtensionById(id=self._app_id) - self.assertFalse([x for x in self.GetExtensionsInfo() - if x['id'] == self._app_id], - msg='Could not uninstall.') - - def testLaunch(self): - """Launch Secure Shell and verify basic connect/exit flow. - - This basic flow also verifies that NaCl works since secure shell is based - on it. - """ - self.assertEqual(1, self.GetTabCount()) - then = time.time() - self.LaunchApp(self._app_id) - login_ui_frame = ( - '/descendant::iframe[contains(@src, "nassh_connect_dialog.html")]') - # Wait for connection dialog iframe to load. - self.WaitForDomNode(login_ui_frame, tab_index=1, - msg='Secure shell login dialog did not show up') - self.WaitForDomNode('id("field-description")', tab_index=1, - attribute='placeholder', - expected_value='username@hostname', # partial match - frame_xpath=login_ui_frame, - msg='Did not find secure shell username dialog') - now = time.time() - self.assertEqual(2, self.GetTabCount(), msg='Did not launch') - logging.info('Launched Secure Shell in %.2f secs' % (now - then)) - - # Fill in chronos@localhost using webdriver. - driver = self.NewWebDriver() - driver.switch_to_window(driver.window_handles[-1]) # last tab - driver.switch_to_frame(1) - user = 'chronos@localhost' - driver.find_element_by_id('field-description').send_keys(user + '\n') - - # Verify yes/no prompt - self.WaitForHtermText('continue connecting \(yes/no\)\?', tab_index=1, - msg='Did not get the yes/no prompt') - welcome_text = self.GetHtermRowsText(0, 8, tab_index=1) - self.assertTrue('Welcome to Secure Shell' in welcome_text, - msg='Did not get correct welcome message') - - # Type 'yes' and enter password - self.SendKeysToHterm('yes\\n', tab_index=1) - self.WaitForHtermText('Password:', tab_index=1, - msg='Did not get password prompt') - self.SendKeysToHterm('test0000\\n', tab_index=1) - self.WaitForHtermText('chronos@localhost $', tab_index=1, - msg='Did not get shell login prompt') - - # Type 'exit' and close the tab - self.SendKeysToHterm('exit\\n', tab_index=1) - # Check for only 'code 0' since that is what indicates that we exited - # successfully. Checking for more stringage causes flakes since the exit - # string does change at times. - self.WaitForHtermText('code 0', tab_index=1, - msg='Did not get correct exit message') - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/special_tabs.py b/chrome/test/functional/special_tabs.py deleted file mode 100755 index b53d110..0000000 --- a/chrome/test/functional/special_tabs.py +++ /dev/null @@ -1,328 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import os - -import pyauto_functional # Must be imported before pyauto -import pyauto -import test_utils - -class SpecialTabsTest(pyauto.PyUITest): - """TestCase for Special Tabs like about:version, chrome://history, etc.""" - - @staticmethod - def GetSpecialAcceleratorTabs(): - """Get a dict of accelerators and corresponding tab titles.""" - ret = { - pyauto.IDC_SHOW_HISTORY: 'History', - pyauto.IDC_MANAGE_EXTENSIONS: 'Extensions', - pyauto.IDC_SHOW_DOWNLOADS: 'Downloads', - } - return ret - - special_url_redirects = { - 'about:': 'chrome://version', - 'about:about': 'chrome://about', - 'about:appcache-internals': 'chrome://appcache-internals', - 'about:credits': 'chrome://credits', - 'about:dns': 'chrome://dns', - 'about:histograms': 'chrome://histograms', - 'about:plugins': 'chrome://plugins', - 'about:sync': 'chrome://sync-internals', - 'about:sync-internals': 'chrome://sync-internals', - 'about:version': 'chrome://version', - } - - special_url_tabs = { - 'chrome://about': { 'title': 'Chrome URLs' }, - 'chrome://appcache-internals': { 'title': 'AppCache Internals' }, - 'chrome://blob-internals': { 'title': 'Blob Storage Internals' }, - 'chrome://feedback': {}, - 'chrome://chrome-urls': { 'title': 'Chrome URLs' }, - 'chrome://crashes': { 'title': 'Crashes' }, - 'chrome://credits': { 'title': 'Credits' }, - 'chrome://downloads': { 'title': 'Downloads' }, - 'chrome://dns': { 'title': 'About DNS' }, - 'chrome://extensions': { 'title': 'Extensions' }, - 'chrome://flags': {}, - 'chrome://flash': {}, - 'chrome://gpu-internals': {}, - 'chrome://invalidations': { 'title': 'Invalidations' }, - 'chrome://histograms': { 'title': 'About Histograms' }, - 'chrome://history': { 'title': 'History' }, - 'chrome://inspect': { 'title': 'Inspect with Chrome Developer Tools' }, - 'chrome://media-internals': { 'title': 'Media Internals' }, - 'chrome://memory-redirect': { 'title': 'About Memory' }, - 'chrome://net-internals': {}, - 'chrome://net-internals/help.html': {}, - 'chrome://newtab': { 'title': 'New Tab', 'CSP': False }, - 'chrome://plugins': { 'title': 'Plug-ins' }, - 'chrome://settings': { 'title': 'Settings' }, - 'chrome://settings/autofill': { 'title': 'Settings - Autofill settings' }, - 'chrome://settings/clearBrowserData': - { 'title': 'Settings - Clear browsing data' }, - 'chrome://settings/content': { 'title': 'Settings - Content settings' }, - 'chrome://settings/languages': - { 'title': 'Settings - Languages' }, - 'chrome://settings/passwords': { 'title': 'Settings - Passwords' }, - 'chrome://stats': {}, - 'chrome://sync': { 'title': 'Sync Internals' }, - 'chrome://sync-internals': { 'title': 'Sync Internals' }, - 'chrome://terms': {}, - 'chrome://version': { 'title': 'About Version' }, - 'chrome://view-http-cache': {}, - 'chrome://webrtc-internals': { 'title': 'WebRTC Internals' }, - } - broken_special_url_tabs = { - # crashed under debug when invoked from location bar (bug 88223). - 'chrome://devtools': { 'CSP': False }, - - # returns "not available" despite having an URL constant. - 'chrome://dialog': { 'CSP': False }, - - # separate window on mac, PC untested, not implemented elsewhere. - 'chrome://ipc': { 'CSP': False }, - - # race against redirects via meta-refresh. - 'chrome://memory': { 'CSP': False }, - } - - chromeos_special_url_tabs = { - 'chrome://choose-mobile-network': { 'title': 'undefined', 'CSP': True }, - 'chrome://flags': { 'CSP': True }, - 'chrome://imageburner': { 'title':'Create a Recovery Media', 'CSP': True }, - 'chrome://keyboardoverlay': { 'title': 'Keyboard Overlay', 'CSP': True }, - 'chrome://network': { 'title': 'About Network' }, - 'chrome://os-credits': { 'title': 'Credits', 'CSP': False }, - 'chrome://proxy-settings': { 'CSP': False }, - 'chrome://register': { 'CSP': False }, - 'chrome://settings/languages': - { 'title': 'Settings - Languages and input' }, - 'chrome://sim-unlock': { 'title': 'Enter SIM card PIN', 'CSP': False }, - 'chrome://system': { 'title': 'About System', 'CSP': False }, - - # OVERRIDE - title and page different on CrOS - 'chrome://settings/accounts': { 'title': 'Settings - Users' }, - } - broken_chromeos_special_url_tabs = { - # returns "not available" page on chromeos=1 linux but has an URL constant. - 'chrome://activationmessage': { 'CSP': False }, - 'chrome://cloudprintresources': { 'CSP': False }, - 'chrome://cloudprintsetup': { 'CSP': False }, - 'chrome://collected-cookies': { 'CSP': False }, - 'chrome://constrained-test': { 'CSP': False }, - 'chrome://enterprise-enrollment': { 'CSP': False }, - 'chrome://http-auth': { 'CSP': False }, - 'chrome://login-container': { 'CSP': False }, - 'chrome://media-player': { 'CSP': False }, - 'chrome://screenshots': { 'CSP': False }, - 'chrome://slideshow': { 'CSP': False }, - 'chrome://syncresources': { 'CSP': False }, - 'chrome://theme': { 'CSP': False }, - 'chrome://view-http-cache': { 'CSP': False }, - - # crashes on chromeos=1 on linux, possibly missing real CrOS features. - 'chrome://cryptohome': { 'CSP': False}, - 'chrome://mobilesetup': { 'CSP': False }, - 'chrome://print': { 'CSP': False }, - } - - linux_special_url_tabs = { - 'chrome://linux-proxy-config': { 'title': 'Proxy Configuration Help' }, - 'chrome://tcmalloc': { 'title': 'tcmalloc stats' }, - 'chrome://sandbox': { 'title': 'Sandbox Status' }, - } - broken_linux_special_url_tabs = {} - - mac_special_url_tabs = { - 'chrome://settings/languages': { 'title': 'Settings - Languages' }, - } - broken_mac_special_url_tabs = {} - - win_special_url_tabs = { - 'chrome://conflicts': {}, - } - broken_win_special_url_tabs = { - # Sync on windows badly broken at the moment. - 'chrome://sync': {}, - } - - google_special_url_tabs = { - # OVERRIDE - different title for Google Chrome vs. Chromium. - 'chrome://terms': { - 'title': 'Google Chrome Terms of Service', - }, - } - broken_google_special_url_tabs = {} - - google_chromeos_special_url_tabs = { - # OVERRIDE - different title for Google Chrome OS vs. Chromium OS. - 'chrome://terms': { - 'title': 'Google Chrome OS Terms', - }, - } - broken_google_chromeos_special_url_tabs = {} - - google_win_special_url_tabs = {} - broken_google_win_special_url_tabs = {} - - google_mac_special_url_tabs = {} - broken_google_mac_special_url_tabs = {} - - google_linux_special_url_tabs = {} - broken_google_linux_special_url_tabs = {} - - def _VerifyAppCacheInternals(self): - """Confirm about:appcache-internals contains expected content for Caches. - Also confirms that the about page populates Application Caches.""" - # Navigate to html page to activate DNS prefetching. - self.NavigateToURL('http://futtta.be/html5/offline.php') - # Wait for page to load and display sucess or fail message. - self.WaitUntil( - lambda: self.GetDOMValue('document.getElementById("status").innerHTML'), - expect_retval='cached') - self.TabGoBack() - test_utils.StringContentCheck( - self, self.GetTabContents(), - ['Manifest', - 'http://futtta.be/html5/manifest.php'], - []) - - def _VerifyAboutDNS(self): - """Confirm about:dns contains expected content related to DNS info. - Also confirms that prefetching DNS records propogate.""" - # Navigate to a page to activate DNS prefetching. - self.NavigateToURL('http://www.google.com') - self.TabGoBack() - test_utils.StringContentCheck(self, self.GetTabContents(), - ['Host name', 'How long ago', 'Motivation'], - []) - - def _GetPlatformSpecialURLTabs(self): - tabs = self.special_url_tabs.copy() - broken_tabs = self.broken_special_url_tabs.copy() - if self.IsChromeOS(): - tabs.update(self.chromeos_special_url_tabs) - broken_tabs.update(self.broken_chromeos_special_url_tabs) - elif self.IsLinux(): - tabs.update(self.linux_special_url_tabs) - broken_tabs.update(self.broken_linux_special_url_tabs) - elif self.IsMac(): - tabs.update(self.mac_special_url_tabs) - broken_tabs.update(self.broken_mac_special_url_tabs) - elif self.IsWin(): - tabs.update(self.win_special_url_tabs) - broken_tabs.update(self.broken_win_special_url_tabs) - for key, value in broken_tabs.iteritems(): - if key in tabs: - del tabs[key] - broken_tabs = {} - if self.GetBrowserInfo()['properties']['branding'] == 'Google Chrome': - tabs.update(self.google_special_url_tabs) - broken_tabs.update(self.broken_google_special_url_tabs) - if self.IsChromeOS(): - tabs.update(self.google_chromeos_special_url_tabs) - broken_tabs.update(self.broken_google_chromeos_special_url_tabs) - elif self.IsLinux(): - tabs.update(self.google_linux_special_url_tabs) - broken_tabs.update(self.broken_google_linux_special_url_tabs) - elif self.IsMac(): - tabs.update(self.google_mac_special_url_tabs) - broken_tabs.update(self.broken_google_mac_special_url_tabs) - elif self.IsWin(): - tabs.update(self.google_win_special_url_tabs) - broken_tabs.update(self.broken_google_win_special_url_tabs) - for key, value in broken_tabs.iteritems(): - if key in tabs: - del tabs[key] - return tabs - - def testSpecialURLRedirects(self): - """Test that older about: URLs are implemented by newer chrome:// URLs. - The location bar may not get updated in all cases, so checking the - tab URL is misleading, instead check for the same contents as the - chrome:// page.""" - tabs = self._GetPlatformSpecialURLTabs() - for url, redirect in self.special_url_redirects.iteritems(): - if redirect in tabs: - logging.debug('Testing redirect from %s to %s.' % (url, redirect)) - self.NavigateToURL(url) - self.assertEqual(self.special_url_tabs[redirect]['title'], - self.GetActiveTabTitle()) - - def testSpecialURLTabs(self): - """Test special tabs created by URLs like chrome://downloads, - chrome://settings/extensionSettings, chrome://history etc. - Also ensures they specify content-security-policy and not inline - scripts for those pages that are expected to do so. Patches which - break this test by including new inline javascript are security - vulnerabilities and should be reverted.""" - tabs = self._GetPlatformSpecialURLTabs() - for url, properties in tabs.iteritems(): - logging.debug('Testing URL %s.' % url) - self.NavigateToURL(url) - expected_title = 'title' in properties and properties['title'] or url - actual_title = self.GetActiveTabTitle() - self.assertTrue(self.WaitUntil( - lambda: self.GetActiveTabTitle(), expect_retval=expected_title), - msg='Title did not match for %s. Expected: %s. Got %s' % ( - url, expected_title, self.GetActiveTabTitle())) - include_list = [] - exclude_list = [] - no_csp = 'CSP' in properties and not properties['CSP'] - if no_csp: - exclude_list.extend(['Content-Security-Policy']) - else: - exclude_list.extend(['<script>', 'onclick=', 'onload=', - 'onchange=', 'onsubmit=', 'javascript:']) - if 'includes' in properties: - include_list.extend(properties['includes']) - if 'excludes' in properties: - exclude_list.extend(properties['exlcudes']) - test_utils.StringContentCheck(self, self.GetTabContents(), - include_list, exclude_list) - result = self.ExecuteJavascript(""" - var r = 'blocked'; - var f = 'executed'; - var s = document.createElement('script'); - s.textContent = 'r = f'; - document.body.appendChild(s); - window.domAutomationController.send(r); - """) - logging.debug('has csp %s, result %s.' % (not no_csp, result)) - if no_csp: - self.assertEqual(result, 'executed', - msg='Got %s for %s' % (result, url)) - else: - self.assertEqual(result, 'blocked', - msg='Got %s for %s' % (result, url)) - - # Restart browser so that every URL gets a fresh instance. - self.RestartBrowser(clear_profile=True) - - def testAboutAppCacheTab(self): - """Test App Cache tab to confirm about page populates caches.""" - self.NavigateToURL('about:appcache-internals') - self._VerifyAppCacheInternals() - self.assertEqual('AppCache Internals', self.GetActiveTabTitle()) - - def testAboutDNSTab(self): - """Test DNS tab to confirm DNS about page propogates records.""" - self.NavigateToURL('about:dns') - self._VerifyAboutDNS() - self.assertEqual('About DNS', self.GetActiveTabTitle()) - - def testSpecialAcceratorTabs(self): - """Test special tabs created by accelerators.""" - for accel, title in self.GetSpecialAcceleratorTabs().iteritems(): - self.RunCommand(accel) - self.assertTrue(self.WaitUntil( - self.GetActiveTabTitle, expect_retval=title), - msg='Expected "%s", got "%s"' % (title, self.GetActiveTabTitle())) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/stress.py b/chrome/test/functional/stress.py deleted file mode 100755 index f9a4ffe..0000000 --- a/chrome/test/functional/stress.py +++ /dev/null @@ -1,806 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 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. - - -""" -Stess Tests for Google Chrome. - -This script runs 4 different stress tests: -1. Plugin stress. -2. Back and forward stress. -3. Download stress. -4. Preference stress. - -After every cycle (running all 4 stress tests) it checks for crashes. -If there are any crashes, the script generates a report, uploads it to -a server and mails about the crash and the link to the report on the server. -Apart from this whenever the test stops on mac it looks for and reports -zombies. - -Prerequisites: -Test needs the following files/folders in the Data dir. -1. A crash_report tool in "pyauto_private/stress/mac" folder for use on Mac. -2. A "downloads" folder containing stress_downloads and all the files - referenced in it. -3. A pref_dict file in "pyauto_private/stress/mac" folder. -4. A "plugin" folder containing doubleAnimation.xaml, flash.swf, FlashSpin.swf, - generic.html, get_flash_player.gif, js-invoker.swf, mediaplayer.wmv, - NavigatorTicker11.class, Plugins_page.html, sample5.mov, silverlight.xaml, - silverlight.js, embed.pdf, plugins_page.html and test6.swf. -5. A stress_pref file in "pyauto_private/stress". -""" - - -import commands -import glob -import logging -import os -import random -import re -import shutil -import sys -import time -import urllib -import test_utils -import subprocess - -import pyauto_functional -import pyauto -import pyauto_utils - - -CRASHES = 'crashes' # Name of the folder to store crashes - - -class StressTest(pyauto.PyUITest): - """Run all the stress tests.""" - - flash_url1 = pyauto.PyUITest.GetFileURLForPath( - os.path.join(pyauto.PyUITest.DataDir(), 'plugin', 'flash.swf')) - flash_url2 = pyauto.PyUITest.GetFileURLForPath( - os.path.join(pyauto.PyUITest.DataDir(),'plugin', 'js-invoker.swf')) - flash_url3 = pyauto.PyUITest.GetFileURLForPath( - os.path.join(pyauto.PyUITest.DataDir(), 'plugin', 'generic.html')) - plugin_url = pyauto.PyUITest.GetFileURLForPath( - os.path.join(pyauto.PyUITest.DataDir(), 'plugin', 'plugins_page.html')) - empty_url = pyauto.PyUITest.GetFileURLForPath( - os.path.join(pyauto.PyUITest.DataDir(), 'empty.html')) - download_url1 = pyauto.PyUITest.GetFileURLForPath( - os.path.join(pyauto.PyUITest.DataDir(), 'downloads', 'a_zip_file.zip')) - download_url2 = pyauto.PyUITest.GetFileURLForPath( - os.path.join(pyauto.PyUITest.DataDir(),'zip', 'test.zip')) - file_list = pyauto.PyUITest.EvalDataFrom( - os.path.join(pyauto.PyUITest.DataDir(), 'downloads', 'stress_downloads')) - symbols_dir = os.path.join(os.getcwd(), 'Build_Symbols') - stress_pref = pyauto.PyUITest.EvalDataFrom( - os.path.join(pyauto.PyUITest.DataDir(), 'pyauto_private', 'stress', - 'stress_pref')) - breakpad_dir = None - chrome_version = None - bookmarks_list = [] - - - def _FuncDir(self): - """Returns the path to the functional dir chrome/test/functional.""" - return os.path.dirname(__file__) - - def _DownloadSymbols(self): - """Downloads the symbols for the build being tested.""" - download_location = os.path.join(os.getcwd(), 'Build_Symbols') - if os.path.exists(download_location): - shutil.rmtree(download_location) - os.makedirs(download_location) - - url = self.stress_pref['symbols_dir'] + self.chrome_version - # TODO: Add linux symbol_files - if self.IsWin(): - url = url + '/win/' - symbol_files = ['chrome.dll.pdb', 'chrome.exe.pdb'] - elif self.IsMac(): - url = url + '/mac/' - symbol_files = map(urllib.quote, - ['Google Chrome Framework.framework', - 'Google Chrome Helper.app', - 'Google Chrome.app', - 'crash_inspector', - 'crash_report_sender', - 'ffmpegsumo.so', - 'libplugin_carbon_interpose.dylib']) - index = 0 - symbol_files = ['%s-%s-i386.breakpad' % (sym_file, self.chrome_version) \ - for sym_file in symbol_files] - logging.info(symbol_files) - - for sym_file in symbol_files: - sym_url = url + sym_file - logging.info(sym_url) - download_sym_file = os.path.join(download_location, sym_file) - logging.info(download_sym_file) - urllib.urlretrieve(sym_url, download_sym_file) - - def setUp(self): - pyauto.PyUITest.setUp(self) - self.breakpad_dir = self._CrashDumpFolder() - self.chrome_version = self.GetBrowserInfo()['properties']['ChromeVersion'] - - # Plugin stress functions - - def _CheckForPluginProcess(self, plugin_name): - """Checks if a particular plugin process exists. - - Args: - plugin_name : plugin process which should be running. - """ - process = self.GetBrowserInfo()['child_processes'] - self.assertTrue([x for x in process - if x['type'] == 'Plug-in' and - x['name'] == plugin_name]) - - def _GetPluginProcessId(self, plugin_name): - """Get Plugin process id. - - Args: - plugin_name: Plugin whose pid is expected. - Eg: "Shockwave Flash" - - Returns: - Process id if the plugin process is running. - None otherwise. - """ - for process in self.GetBrowserInfo()['child_processes']: - if process['type'] == 'Plug-in' and \ - re.search(plugin_name, process['name']): - return process['pid'] - return None - - def _CloseAllTabs(self): - """Close all but one tab in first window.""" - tab_count = self.GetTabCount(0) - for tab_index in xrange(tab_count - 1, 0, -1): - self.CloseTab(tab_index) - - def _CloseAllWindows(self): - """Close all windows except one.""" - win_count = self.GetBrowserWindowCount() - for windex in xrange(win_count - 1, 0, -1): - self.RunCommand(pyauto.IDC_CLOSE_WINDOW, windex) - - def _ReloadAllTabs(self): - """Reload all the tabs in first window.""" - for tab_index in range(self.GetTabCount()): - self.ReloadTab(tab_index) - - def _LoadFlashInMultipleTabs(self): - """Load Flash in multiple tabs in first window.""" - self.NavigateToURL(self.empty_url) - # Open 18 tabs with flash - for _ in range(9): - self.AppendTab(pyauto.GURL(self.flash_url1)) - self.AppendTab(pyauto.GURL(self.flash_url2)) - - def _OpenAndCloseMultipleTabsWithFlash(self): - """Stress test for flash in multiple tabs.""" - logging.info("In _OpenAndCloseMultipleWindowsWithFlash.") - self._LoadFlashInMultipleTabs() - self._CheckForPluginProcess('Shockwave Flash') - self._CloseAllTabs() - - def _OpenAndCloseMultipleWindowsWithFlash(self): - """Stress test for flash in multiple windows.""" - logging.info('In _OpenAndCloseMultipleWindowsWithFlash.') - # Open 5 Normal and 4 Incognito windows - for tab_index in range(1, 10): - if tab_index < 6: - self.OpenNewBrowserWindow(True) - else: - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - self.NavigateToURL(self.flash_url2, tab_index, 0) - self.AppendTab(pyauto.GURL(self.flash_url2), tab_index) - self._CloseAllWindows() - - def _OpenAndCloseMultipleTabsWithMultiplePlugins(self): - """Stress test using multiple plugins in multiple tabs.""" - logging.info('In _OpenAndCloseMultipleTabsWithMultiplePlugins.') - # Append 4 tabs with URL - for _ in range(5): - self.AppendTab(pyauto.GURL(self.plugin_url)) - self._CloseAllTabs() - - def _OpenAndCloseMultipleWindowsWithMultiplePlugins(self): - """Stress test using multiple plugins in multiple windows.""" - logging.info('In _OpenAndCloseMultipleWindowsWithMultiplePlugins.') - # Open 4 windows with URL - for tab_index in range(1, 5): - if tab_index < 6: - self.OpenNewBrowserWindow(True) - else: - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - self.NavigateToURL(self.plugin_url, tab_index, 0) - self._CloseAllWindows() - - def _KillAndReloadFlash(self): - """Stress test by killing flash process and reloading tabs.""" - self._LoadFlashInMultipleTabs() - flash_process_id1 = self._GetPluginProcessId('Shockwave Flash') - self.Kill(flash_process_id1) - self._ReloadAllTabs() - self._CloseAllTabs() - - def _KillAndReloadRenderersWithFlash(self): - """Stress test by killing renderer processes and reloading tabs.""" - logging.info('In _KillAndReloadRenderersWithFlash') - self._LoadFlashInMultipleTabs() - info = self.GetBrowserInfo() - # Kill all renderer processes - for tab_index in range(self.GetTabCount(0)): - self.KillRendererProcess( - info['windows'][0]['tabs'][tab_index]['renderer_pid']) - self._ReloadAllTabs() - self._CloseAllTabs() - - def _TogglePlugin(self, plugin_name): - """Toggle plugin status. - - Args: - plugin_name: Name of the plugin to toggle. - """ - plugins = self.GetPluginsInfo().Plugins() - for item in range(len(plugins)): - if re.search(plugin_name, plugins[item]['name']): - if plugins[item]['enabled']: - self.DisablePlugin(plugins[item]['path']) - else: - self.EnablePlugin(plugins[item]['path']) - - def _ToggleAndReloadFlashPlugin(self): - """Toggle flash and reload all tabs.""" - logging.info('In _ToggleAndReloadFlashPlugin') - for _ in range(10): - self.AppendTab(pyauto.GURL(self.flash_url3)) - # Disable Flash Plugin - self._TogglePlugin('Shockwave Flash') - self._ReloadAllTabs() - # Enable Flash Plugin - self._TogglePlugin('Shockwave Flash') - self._ReloadAllTabs() - self._CloseAllTabs() - - # Downloads stress functions - - def _LoadDownloadsInMultipleTabs(self): - """Load Downloads in multiple tabs in the same window.""" - # Open 15 tabs with downloads - logging.info('In _LoadDownloadsInMultipleTabs') - for tab_index in range(15): - # We open an empty tab and then downlad a file from it. - self.AppendTab(pyauto.GURL(self.empty_url)) - self.NavigateToURL(self.download_url1, 0, tab_index + 1) - self.AppendTab(pyauto.GURL(self.empty_url)) - self.NavigateToURL(self.download_url2, 0, tab_index + 2) - - def _OpenAndCloseMultipleTabsWithDownloads(self): - """Download items in multiple tabs.""" - logging.info('In _OpenAndCloseMultipleTabsWithDownloads') - self._LoadDownloadsInMultipleTabs() - self._CloseAllTabs() - - def _OpenAndCloseMultipleWindowsWithDownloads(self): - """Randomly have downloads in multiple windows.""" - logging.info('In _OpenAndCloseMultipleWindowsWithDownloads') - # Open 15 Windows randomly on both regular and incognito with downloads - for window_index in range(15): - tick = round(random.random() * 100) - if tick % 2 != 0: - self.NavigateToURL(self.download_url2, 0, 0) - else: - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - self.AppendTab(pyauto.GURL(self.empty_url), 1) - self.NavigateToURL(self.download_url2, 1, 1) - self._CloseAllWindows() - - def _OpenAndCloseMultipleTabsWithMultipleDownloads(self): - """Download multiple items in multiple tabs.""" - logging.info('In _OpenAndCloseMultipleTabsWithMultipleDownloads') - self.NavigateToURL(self.empty_url) - for _ in range(15): - for file in self.file_list: - count = 1 - url = self.GetFileURLForPath( - os.path.join(self.DataDir(), 'downloads', file)) - self.AppendTab(pyauto.GURL(self.empty_url)) - self.NavigateToURL(url, 0, count) - count = count + 1 - self._CloseAllTabs() - - def _OpenAndCloseMultipleWindowsWithMultipleDownloads(self): - """Randomly multiple downloads in multiple windows.""" - logging.info('In _OpenAndCloseMultipleWindowsWithMultipleDownloads') - for _ in range(15): - for file in self.file_list: - tick = round(random.random() * 100) - url = self.GetFileURLForPath( - os.path.join(self.DataDir(), 'downloads', file)) - if tick % 2!= 0: - self.NavigateToURL(url, 0, 0) - else: - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - self.AppendTab(pyauto.GURL(self.empty_url), 1) - self.NavigateToURL(url, 1, 1) - self._CloseAllWindows() - - # Back and Forward stress functions - - def _BrowserGoBack(self, window_index): - """Go back in the browser history. - - Chrome has limitation on going back and can only go back 49 pages. - - Args: - window_index: the index of the browser window to work on. - """ - for nback in range(48): # Go back 48 times. - if nback % 4 == 0: # Bookmark every 5th url when going back. - self._BookMarkEvery5thURL(window_index) - self.TabGoBack(tab_index=0, windex=window_index) - - def _BrowserGoForward(self, window_index): - """Go Forward in the browser history. - - Chrome has limitation on going back and can only go back 49 pages. - - Args: - window_index: the index of the browser window to work on. - """ - for nforward in range(48): # Go back 48 times. - if nforward % 4 == 0: # Bookmark every 5th url when going Forward - self._BookMarkEvery5thURL(window_index) - self.TabGoForward(tab_index=0, windex=window_index) - - def _AddToListAndBookmark(self, newname, url): - """Bookmark the url to bookmarkbar and to he list of bookmarks. - - Args: - newname: the name of the bookmark. - url: the url to bookmark. - """ - bookmarks = self.GetBookmarkModel() - bar_id = bookmarks.BookmarkBar()['id'] - self.AddBookmarkURL(bar_id, 0, newname, url) - self.bookmarks_list.append(newname) - - def _RemoveFromListAndBookmarkBar(self, name): - """Remove the bookmark bor and bookmarks list. - - Args: - name: the name of bookmark to remove. - """ - bookmarks = self.GetBookmarkModel() - node = bookmarks.FindByTitle(name) - self.RemoveBookmark(node[0]['id']) - self.bookmarks_list.remove(name) - - def _DuplicateBookmarks(self, name): - """Find duplicate bookmark in the bookmarks list. - - Args: - name: name of the bookmark. - - Returns: - True if it's a duplicate. - """ - for index in (self.bookmarks_list): - if index == name: - return True - return False - - def _BookMarkEvery5thURL(self, window_index): - """Check for duplicate in list and bookmark current url. - If its the first time and list is empty add the bookmark. - If its a duplicate remove the bookmark. - If its new tab page move over. - - Args: - window_index: the index of the browser window to work on. - """ - tab_title = self.GetActiveTabTitle(window_index) # get the page title - url = self.GetActiveTabURL(window_index).spec() # get the page url - if not self.bookmarks_list: - self._AddToListAndBookmark(tab_title, url) # first run bookmark the url - return - elif self._DuplicateBookmarks(tab_title): - self._RemoveFromListAndBookmarkBar(tab_title) - return - elif tab_title == 'New Tab': # new tab page pass over - return - else: - # new bookmark add it to bookmarkbar - self._AddToListAndBookmark(tab_title, url) - return - - def _ReadFileAndLoadInNormalAndIncognito(self): - """Read urls and load them in normal and incognito window. - We load 96 urls only as we can go back and forth 48 times. - Uses time to get different urls in normal and incognito window - The source file is taken from stress folder in /data folder. - """ - # URL source from stress folder in data folder - data_file = os.path.join(self.DataDir(), 'pyauto_private', 'stress', - 'urls_and_titles') - url_data = self.EvalDataFrom(data_file) - urls = url_data.keys() - i = 0 - ticks = int(time.time()) # get the latest time. - for url in urls: - if i <= 96 : # load only 96 urls. - if ticks % 2 == 0: # loading in Incognito and Normal window. - self.NavigateToURL(url) - else: - self.NavigateToURL(url, 1, 0) - else: - break - ticks = ticks - 1 - i += 1 - return - - def _StressTestNavigation(self): - """ This is the method from where various navigations are called. - First we load the urls then call navigete back and forth in - incognito window then in normal window. - """ - self._ReadFileAndLoadInNormalAndIncognito() # Load the urls. - self._BrowserGoBack(1) # Navigate back in incognito window. - self._BrowserGoForward(1) # Navigate forward in incognito window - self._BrowserGoBack(0) # Navigate back in normal window - self._BrowserGoForward(0) # Navigate forward in normal window - - # Preference stress functions - - def _RandomBool(self): - """For any preferences bool value, it takes True or False value. - We are generating random True or False value. - """ - return random.randint(0, 1) == 1 - - def _RandomURL(self): - """Some of preferences take string url, so generating random url here.""" - # Site list - site_list = ['test1.html', 'test2.html','test3.html','test4.html', - 'test5.html', 'test7.html', 'test6.html'] - random_site = random.choice(site_list) - # Returning a url of random site - return self.GetFileURLForPath(os.path.join(self.DataDir(), random_site)) - - def _RandomURLArray(self): - """Returns a list of 10 random URLs.""" - return [self._RandomURL() for _ in range(10)] - - def _RandomInt(self, max_number): - """Some of the preferences takes integer value. - Eg: If there are three options, we generate random - value for any option. - - Arg: - max_number: The number of options that a preference has. - """ - return random.randrange(1, max_number) - - def _RandomDownloadDir(self): - """Returns a random download directory.""" - return random.choice(['dl_dir1', 'dl_dir2', 'dl_dir3', - 'dl_dir4', 'dl_dir5']) - - def _SetPref(self): - """Reads the preferences from file and - sets the preferences to Chrome. - """ - raw_dictionary = self.EvalDataFrom(os.path.join(self.DataDir(), - 'pyauto_private', 'stress', 'pref_dict')) - value_dictionary = {} - - for key, value in raw_dictionary.iteritems(): - if value == 'BOOL': - value_dictionary[key] = self._RandomBool() - elif value == 'STRING_URL': - value_dictionary[key] = self._RandomURL() - elif value == 'ARRAY_URL': - value_dictionary[key] = self._RandomURLArray() - elif value == 'STRING_PATH': - value_dictionary[key] = self._RandomDownloadDir() - elif value[0:3] == 'INT': - # Normally we difine INT datatype with number of options, - # so parsing number of options and selecting any of them - # randomly. - value_dictionary[key] = 1 - max_number = raw_dictionary[key][3:4] - if not max_number == 1: - value_dictionary[key]= self._RandomInt(int(max_number)) - self.SetPrefs(getattr(pyauto,key), value_dictionary[key]) - - return value_dictionary - - # Crash reporting functions - - def _CrashDumpFolder(self): - """Get the breakpad folder. - - Returns: - The full path of the Crash Reports folder. - """ - breakpad_folder = self.GetBrowserInfo()['properties']['DIR_CRASH_DUMPS'] - self.assertTrue(breakpad_folder, 'Cannot figure crash dir') - return breakpad_folder - - def _DeleteDumps(self): - """Delete all the dump files in teh Crash Reports folder.""" - # should be called at the start of stress run - if os.path.exists(self.breakpad_dir): - logging.info('xxxxxxxxxxxxxxxINSIDE DELETE DUMPSxxxxxxxxxxxxxxxxx') - if self.IsMac(): - shutil.rmtree(self.breakpad_dir) - elif self.IsWin(): - files = os.listdir(self.breakpad_dir) - for file in files: - os.remove(file) - - first_crash = os.path.join(os.getcwd(), '1stcrash') - crashes_dir = os.path.join(os.getcwd(), 'crashes') - if (os.path.exists(crashes_dir)): - shutil.rmtree(crashes_dir) - shutil.rmtree(first_crash) - - def _SymbolicateCrashDmp(self, dmp_file, symbols_dir, output_file): - """Generate symbolicated crash report. - - Args: - dmp_file: the dmp file to symbolicate. - symbols_dir: the directory containing the symbols. - output_file: the output file. - - Returns: - Crash report text. - """ - report = '' - if self.IsWin(): - windbg_cmd = [ - os.path.join('C:', 'Program Files', 'Debugging Tools for Windows', - 'windbg.exe'), - '-Q', - '-y', - '\"', - symbols_dir, - '\"', - '-c', - '\".ecxr;k50;.logclose;q\"', - '-logo', - output_file, - '-z', - '\"', - dmp_file, - '\"'] - subprocess.call(windbg_cmd) - # Since we are directly writing the info into output_file, - # we just need to copy that in to report - report = open(output_file, 'r').read() - - elif self.IsMac(): - crash_report = os.path.join(self.DataDir(), 'pyauto_private', 'stress', - 'mac', 'crash_report') - for i in range(5): # crash_report doesn't work sometimes. So we retry - report = test_utils.Shell2( - '%s -S "%s" "%s"' % (crash_report, symbols_dir, dmp_file))[0] - if len(report) < 200: - try_again = 'Try %d. crash_report didn\'t work out. Trying again', i - logging.info(try_again) - else: - break - open(output_file, 'w').write(report) - return report - - def _SaveSymbols(self, symbols_dir, dump_dir=' ', multiple_dumps=True): - """Save the symbolicated files for all crash dumps. - - Args: - symbols_dir: the directory containing the symbols. - dump_dir: Path to the directory holding the crash dump files. - multiple_dumps: True if we are processing multiple dump files, - False if we are processing only the first crash. - """ - if multiple_dumps: - dump_dir = self.breakpad_dir - - if not os.path.isdir(CRASHES): - os.makedirs(CRASHES) - - # This will be sent to the method by the caller. - dmp_files = glob.glob(os.path.join(dump_dir, '*.dmp')) - for dmp_file in dmp_files: - dmp_id = os.path.splitext(os.path.basename(dmp_file))[0] - if multiple_dumps: - report_folder = CRASHES - else: - report_folder = dump_dir - report_fname = os.path.join(report_folder, - '%s.txt' % (dmp_id)) - report = self._SymbolicateCrashDmp(dmp_file, symbols_dir, - report_fname) - if report == '': - logging.info('Crash report is empty.') - # This is for copying the original dumps. - if multiple_dumps: - shutil.copy2(dmp_file, CRASHES) - - def _GetFirstCrashDir(self): - """Get first crash file in the crash folder. - Here we create the 1stcrash directory which holds the - first crash report, which will be attached to the mail. - """ - breakpad_folder = self.breakpad_dir - dump_list = glob.glob1(breakpad_folder,'*.dmp') - dump_list.sort(key=lambda s: os.path.getmtime(os.path.join( - breakpad_folder, s))) - first_crash_file = os.path.join(breakpad_folder, dump_list[0]) - - if not os.path.isdir('1stcrash'): - os.makedirs('1stcrash') - shutil.copy2(first_crash_file, '1stcrash') - first_crash_dir = os.path.join(os.getcwd(), '1stcrash') - return first_crash_dir - - def _GetFirstCrashFile(self): - """Get first crash file in the crash folder.""" - first_crash_dir = os.path.join(os.getcwd(), '1stcrash') - for each in os.listdir(first_crash_dir): - if each.endswith('.txt'): - first_crash_file = each - return os.path.join(first_crash_dir, first_crash_file) - - def _ProcessOnlyFirstCrash(self): - """ Process only the first crash report for email.""" - first_dir = self._GetFirstCrashDir() - self._SaveSymbols(self.symbols_dir, first_dir, False) - - def _GetOSName(self): - """Returns the OS type we are running this script on.""" - os_name = '' - if self.IsMac(): - os_number = commands.getoutput('sw_vers -productVersion | cut -c 1-4') - if os_number == '10.6': - os_name = 'Snow_Leopard' - elif os_number == '10.5': - os_name = 'Leopard' - elif self.IsWin(): - # TODO: Windows team need to find the way to get OS name - os_name = 'Windows' - if platform.version()[0] == '5': - os_name = os_name + '_XP' - else: - os_name = os_name + '_Vista/Win7' - return os_name - - def _ProcessUploadAndEmailCrashes(self): - """Upload the crashes found and email the team about this.""" - logging.info('#########INSIDE _ProcessUploadAndEmailCrashes#########') - try: - build_version = self.chrome_version - self._SaveSymbols(self.symbols_dir) - self._ProcessOnlyFirstCrash() - file_to_attach = self._GetFirstCrashFile() - # removing the crash_txt for now, - # since we are getting UnicodeDecodeError - # crash_txt = open(file_to_attach).read() - except ValueError: - test_utils.SendMail(self.stress_pref['mailing_address'], - self.stress_pref['mailing_address'], - "We don't have build version", - "BROWSER CRASHED, PLEASE CHECK", - self.stress_pref['smtp']) - # Move crash reports and dumps to server - os_name = self._GetOSName() - dest_dir = build_version + '_' + os_name - if (test_utils.Shell2(self.stress_pref['script'] % (CRASHES, dest_dir))): - logging.info('Copy Complete') - upload_dir= self.stress_pref['upload_dir'] + dest_dir - num_crashes = '\n \n Number of Crashes :' + \ - str(len(glob.glob1(self.breakpad_dir, '*.dmp'))) - mail_content = '\n\n Crash Report URL :' + upload_dir + '\n' + \ - num_crashes + '\n\n' # + crash_txt - mail_subject = 'Stress Results :' + os_name + '_' + build_version - # Sending mail with first crash report, # of crashes, location of upload - test_utils.SendMail(self.stress_pref['mailing_address'], - self.stress_pref['mailing_address'], - mail_subject, mail_content, - self.stress_pref['smtp'], file_to_attach) - - def _ReportCrashIfAny(self): - """Check for browser crashes and report.""" - if os.path.isdir(self.breakpad_dir): - listOfDumps = glob.glob(os.path.join(self.breakpad_dir, '*.dmp')) - if len(listOfDumps) > 0: - logging.info('========== INSIDE REPORT CRASH++++++++++++++') - # inform a method to process the dumps - self._ProcessUploadAndEmailCrashes() - - # Test functions - - def _PrefStress(self): - """Stress preferences.""" - default_prefs = self.GetPrefsInfo() - pref_dictionary = self._SetPref() - for key, value in pref_dictionary.iteritems(): - self.assertEqual(value, self.GetPrefsInfo().Prefs( - getattr(pyauto, key))) - - for key, value in pref_dictionary.iteritems(): - self.SetPrefs(getattr(pyauto, key), - default_prefs.Prefs(getattr(pyauto, key))) - - def _NavigationStress(self): - """Run back and forward stress in normal and incognito window.""" - self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) - self._StressTestNavigation() - - def _DownloadStress(self): - """Run all the Download stress test.""" - org_download_dir = self.GetDownloadDirectory().value() - new_dl_dir = os.path.join(org_download_dir, 'My+Downloads Folder') - os.path.exists(new_dl_dir) and shutil.rmtree(new_dl_dir) - os.makedirs(new_dl_dir) - self.SetPrefs(pyauto.kDownloadDefaultDirectory, new_dl_dir) - self._OpenAndCloseMultipleTabsWithDownloads() - self._OpenAndCloseMultipleWindowsWithDownloads() - self._OpenAndCloseMultipleTabsWithMultipleDownloads() - self._OpenAndCloseMultipleWindowsWithMultipleDownloads() - pyauto_utils.RemovePath(new_dl_dir) # cleanup - self.SetPrefs(pyauto.kDownloadDefaultDirectory, org_download_dir) - - def _PluginStress(self): - """Run all the plugin stress tests.""" - self._OpenAndCloseMultipleTabsWithFlash() - self._OpenAndCloseMultipleWindowsWithFlash() - self._OpenAndCloseMultipleTabsWithMultiplePlugins() - self._OpenAndCloseMultipleWindowsWithMultiplePlugins() - self._KillAndReloadRenderersWithFlash() - self._ToggleAndReloadFlashPlugin() - - def testStress(self): - """Run all the stress tests for 24 hrs.""" - if self.GetBrowserInfo()['properties']['branding'] != 'Google Chrome': - logging.info('This is not a branded build, so stopping the stress') - return 1 - self._DownloadSymbols() - run_number = 1 - start_time = time.time() - while True: - logging.info('run %d...' % run_number) - run_number = run_number + 1 - if (time.time() - start_time) >= 24*60*60: - logging.info('Its been 24hrs, so we break now.') - break - try: - methods = [self._NavigationStress, self._DownloadStress, - self._PluginStress, self._PrefStress] - random.shuffle(methods) - for method in methods: - method() - logging.info('Method %s done' % method) - except KeyboardInterrupt: - logging.info('----------We got a KeyboardInterrupt-----------') - except Exception, error: - logging.info('-------------There was an ERROR---------------') - logging.info(error) - - # Crash Reporting - self._ReportCrashIfAny() - self._DeleteDumps() - - if self.IsMac(): - zombie = 'ps -el | grep Chrom | grep -v grep | grep Z | wc -l' - zombie_count = int(commands.getoutput(zombie)) - if zombie_count > 0: - logging.info('WE HAVE ZOMBIES = %d' % zombie_count) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/test_pyauto.py b/chrome/test/functional/test_pyauto.py deleted file mode 100755 index adf1f27..0000000 --- a/chrome/test/functional/test_pyauto.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import time -import unittest - -import pyauto_functional # Must be imported before pyauto -import pyauto -import pyauto_errors - - -class PyAutoTest(pyauto.PyUITest): - """Test functionality of the PyAuto framework.""" - - _EXTRA_CHROME_FLAGS = [ - '--scooby-doo=123', - '--donald-duck=cool', - '--super-mario', - '--marvin-the-martian', - ] - - def ExtraChromeFlags(self): - """Ensures Chrome is launched with some custom flags. - - Overrides the default list of extra flags passed to Chrome. See - ExtraChromeFlags() in pyauto.py. - """ - return pyauto.PyUITest.ExtraChromeFlags(self) + self._EXTRA_CHROME_FLAGS - - def testSetCustomChromeFlags(self): - """Ensures that Chrome can be launched with custom flags.""" - self.NavigateToURL('about://version') - for flag in self._EXTRA_CHROME_FLAGS: - self.assertEqual(self.FindInPage(flag)['match_count'], 1, - msg='Missing expected Chrome flag "%s"' % flag) - - def testCallOnInvalidWindow(self): - """Verify that exception is raised when a browser is missing/invalid.""" - self.assertEqual(1, self.GetBrowserWindowCount()) - self.assertRaises( - pyauto_errors.JSONInterfaceError, - lambda: self.FindInPage('some text', windex=1)) # invalid window - - def testJSONInterfaceTimeout(self): - """Verify that an exception is raised when the JSON interface times out.""" - self.ClearEventQueue() - self.AddDomEventObserver('foo') - self.assertRaises( - pyauto_errors.AutomationCommandTimeout, - lambda: self.GetNextEvent(timeout=2000)) # event queue is empty - - def testActionTimeoutChanger(self): - """Verify that ActionTimeoutChanger works.""" - new_timeout = 1000 # 1 sec - changer = pyauto.PyUITest.ActionTimeoutChanger(self, new_timeout) - self.assertEqual(self._automation_timeout, new_timeout) - - # Verify the amount of time taken for automation timeout - then = time.time() - self.assertRaises( - pyauto_errors.AutomationCommandTimeout, - lambda: self.ExecuteJavascript('invalid js should timeout')) - elapsed = time.time() - then - self.assertTrue(elapsed < new_timeout / 1000.0 + 2, # margin of 2 secs - msg='ActionTimeoutChanger did not work. ' - 'Automation timeout took %f secs' % elapsed) - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/functional/test_utils.py b/chrome/test/functional/test_utils.py deleted file mode 100644 index 55d97c8..0000000 --- a/chrome/test/functional/test_utils.py +++ /dev/null @@ -1,401 +0,0 @@ -# Copyright 2013 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import copy -import ctypes -import email -import logging -import os -import shutil -import smtplib -import subprocess -import sys -import types - -import pyauto_functional -import pyauto -import pyauto_utils -import pyauto_errors - - -"""Commonly used functions for PyAuto tests.""" - -def CrashBrowser(test): - """Crashes the browser by navigating to special URL.""" - try: - test.NavigateToURL('chrome://inducebrowsercrashforrealz') - except pyauto_errors.JSONInterfaceError: - pass - else: - raise RuntimeError( - 'Browser did not crash at chrome://inducebrowsercrashforrealz') - - -def CopyFileFromDataDirToDownloadDir(test, file_path): - """Copy a file from data directory to downloads directory. - - Args: - test: derived from pyauto.PyUITest - base class for UI test cases. - path: path of the file relative to the data directory - """ - data_file = os.path.join(test.DataDir(), file_path) - download_dir = test.GetDownloadDirectory().value() - shutil.copy(data_file, download_dir) - - -def CopyFileFromContentDataDirToDownloadDir(test, file_path): - """Copy a file from content data directory to downloads directory. - - Args: - test: derived from pyauto.PyUITest - base class for UI test cases. - path: path of the file relative to the data directory - """ - data_file = os.path.join(test.ContentDataDir(), file_path) - download_dir = test.GetDownloadDirectory().value() - shutil.copy(data_file, download_dir) - - -def RemoveDownloadedTestFile(test, file_name): - """Delete a file from the downloads directory. - - Arg: - test: derived from pyauto.PyUITest - base class for UI test cases - file_name: name of file to remove - """ - downloaded_pkg = os.path.join(test.GetDownloadDirectory().value(), - file_name) - pyauto_utils.RemovePath(downloaded_pkg) - pyauto_utils.RemovePath(downloaded_pkg + '.crdownload') - - -def GoogleAccountsLogin(test, username, password, - tab_index=0, windex=0, url=None): - """Log into Google Accounts. - - Attempts to login to Google by entering the username/password into the google - login page and click submit button. - - Args: - test: derived from pyauto.PyUITest - base class for UI test cases. - username: users login input. - password: users login password input. - tab_index: The tab index, default is 0. - windex: The window index, default is 0. - url: an alternative url for login page, if None, original one will be used. - """ - url = url or 'https://accounts.google.com/' - test.NavigateToURL(url, windex, tab_index) - email_id = 'document.getElementById("Email").value = "%s"; ' \ - 'window.domAutomationController.send("done")' % username - password = 'document.getElementById("Passwd").value = "%s"; ' \ - 'window.domAutomationController.send("done")' % password - test.ExecuteJavascript(email_id, tab_index, windex) - test.ExecuteJavascript(password, tab_index, windex) - test.assertTrue(test.SubmitForm('gaia_loginform', tab_index, windex)) - - -def VerifyGoogleAccountCredsFilled(test, username, password, tab_index=0, - windex=0): - """Verify stored/saved user and password values to the values in the field. - - Args: - test: derived from pyauto.PyUITest - base class for UI test cases. - username: user log in input. - password: user log in password input. - tab_index: The tab index, default is 0. - windex: The window index, default is 0. - """ - email_value = test.GetDOMValue('document.getElementById("Email").value', - tab_index, windex) - passwd_value = test.GetDOMValue('document.getElementById("Passwd").value', - tab_index, windex) - test.assertEqual(email_value, username) - # Not using assertEqual because if it fails it would end up dumping the - # password (which is supposed to be private) - test.assertTrue(passwd_value == password) - - -def Shell2(cmd_string, bg=False): - """Run a shell command. - - Args: - cmd_string: command to run - bg: should the process be run in background? Default: False - - Returns: - Output, return code - """ - if not cmd_string: return ('', 0) - if bg: - cmd_string += ' 1>/dev/null 2>&1 &' - proc = os.popen(cmd_string) - if bg: return ('Background process: %s' % cmd_string, 0) - out = proc.read() - retcode = proc.close() - if not retcode: # Success - retcode = 0 - return (out, retcode) - - -def SendMail(send_from, send_to, subject, text, smtp, file_to_send=None): - """Send mail to all the group to notify about the crash and uploaded data. - - Args: - send_from: From mail id as a string. - send_to: To mail id. Can be a string representing a single address, or a - list of strings representing multiple addresses. - subject: Mail subject as a string. - text: Mail body as a string. - smtp: The smtp to use, as a string. - file_to_send: Attachments for the mail. - """ - msg = email.MIMEMultipart.MIMEMultipart() - msg['From'] = send_from - if isinstance(send_to, list): - msg['To'] = ','.join(send_to) - else: - msg['To'] = send_to - msg['Date'] = email.Utils.formatdate(localtime=True) - msg['Subject'] = subject - - # To send multiple files in one message, introduce for loop here for files. - msg.attach(email.MIMEText.MIMEText(text)) - part = email.MIMEBase.MIMEBase('application', 'octet-stream') - if file_to_send is not None: - part.set_payload(open(file_to_send,'rb').read()) - email.Encoders.encode_base64(part) - part.add_header('Content-Disposition', - 'attachment; filename="%s"' - % os.path.basename(file_to_send)) - msg.attach(part) - smtp_obj = smtplib.SMTP(smtp) - smtp_obj.sendmail(send_from, send_to, msg.as_string()) - smtp_obj.close() - - -def GetFreeSpace(path): - """Returns the free space (in bytes) on the drive containing |path|.""" - if sys.platform == 'win32': - free_bytes = ctypes.c_ulonglong(0) - ctypes.windll.kernel32.GetDiskFreeSpaceExW( - ctypes.c_wchar_p(os.path.dirname(path)), None, None, - ctypes.pointer(free_bytes)) - return free_bytes.value - fs_stat = os.statvfs(path) - return fs_stat.f_bsize * fs_stat.f_bavail - - -def StripUnmatchedKeys(item_to_strip, reference_item): - """Returns a copy of 'item_to_strip' where unmatched key-value pairs in - every dictionary are removed. - - This will examine each dictionary in 'item_to_strip' recursively, and will - remove keys that are not found in the corresponding dictionary in - 'reference_item'. This is useful for testing equality of a subset of data. - - Items may contain dictionaries, lists, or primitives, but only corresponding - dictionaries will be stripped. A corresponding entry is one which is found - in the same index in the corresponding parent array or at the same key in the - corresponding parent dictionary. - - Arg: - item_to_strip: item to copy and remove all unmatched key-value pairs - reference_item: item that serves as a reference for which keys-value pairs - to strip from 'item_to_strip' - - Returns: - a copy of 'item_to_strip' where all key-value pairs that do not have a - matching key in 'reference_item' are removed - - Example: - item_to_strip = {'tabs': 3, - 'time': 5908} - reference_item = {'tabs': 2} - StripUnmatchedKeys(item_to_strip, reference_item) will return {'tabs': 3} - """ - def StripList(list1, list2): - return_list = copy.deepcopy(list2) - for i in range(min(len(list1), len(list2))): - return_list[i] = StripUnmatchedKeys(list1[i], list2[i]) - return return_list - - def StripDict(dict1, dict2): - return_dict = {} - for key in dict1: - if key in dict2: - return_dict[key] = StripUnmatchedKeys(dict1[key], dict2[key]) - return return_dict - - item_to_strip_type = type(item_to_strip) - if item_to_strip_type is type(reference_item): - if item_to_strip_type is types.ListType: - return StripList(item_to_strip, reference_item) - elif item_to_strip_type is types.DictType: - return StripDict(item_to_strip, reference_item) - return copy.deepcopy(item_to_strip) - - -def StringContentCheck(test, content_string, have_list, nothave_list): - """Check for the presence or absence of strings within content. - - Confirm all strings in |have_list| are found in |content_string|. - Confirm all strings in |nothave_list| are not found in |content_string|. - - Args: - content_string: string containing the content to check. - have_list: list of strings expected to be found within the content. - nothave_list: list of strings expected to not be found within the content. - """ - for s in have_list: - test.assertTrue(s in content_string, - msg='"%s" missing from content.' % s) - for s in nothave_list: - test.assertTrue(s not in content_string, - msg='"%s" unexpectedly contained in content.' % s) - - -def CallFunctionWithNewTimeout(self, new_timeout, function): - """Sets the timeout to |new_timeout| and calls |function|. - - This method resets the timeout before returning. - """ - timeout_changer = pyauto.PyUITest.ActionTimeoutChanger( - self, new_timeout) - logging.info('Automation execution timeout has been changed to %d. ' - 'If the timeout is large the test might appear to hang.' - % new_timeout) - function() - del timeout_changer - - -def GetOmniboxMatchesFor(self, text, windex=0, attr_dict=None): - """Fetch omnibox matches with the given attributes for the given query. - - Args: - text: the query text to use - windex: the window index to work on. Defaults to 0 (first window) - attr_dict: the dictionary of properties to be satisfied - - Returns: - a list of match items - """ - self.SetOmniboxText(text, windex=windex) - self.WaitUntilOmniboxQueryDone(windex=windex) - if not attr_dict: - matches = self.GetOmniboxInfo(windex=windex).Matches() - else: - matches = self.GetOmniboxInfo(windex=windex).MatchesWithAttributes( - attr_dict=attr_dict) - return matches - - -def GetMemoryUsageOfProcess(pid): - """Queries the system for the current memory usage of a specified process. - - This function only works in Linux and ChromeOS. - - Args: - pid: The integer process identifier for the process to use. - - Returns: - The memory usage of the process in MB, given as a float. If the process - doesn't exist on the machine, then the value 0 is returned. - """ - assert pyauto.PyUITest.IsLinux() or pyauto.PyUITest.IsChromeOS() - process = subprocess.Popen('ps h -o rss -p %s' % pid, shell=True, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout = process.communicate()[0] - if stdout: - return float(stdout.strip()) / 1024 - else: - return 0 - - -def LoginToDevice(test, test_account='test_google_account'): - """Login to the Chromeos device using the given test account. - - If no test account is specified, we use test_google_account as the default. - You can choose test accounts from - - chrome/test/data/pyauto_private/private_tests_info.txt - - Args: - test_account: The account used to login to the Chromeos device. - """ - if not test.GetLoginInfo()['is_logged_in']: - credentials = test.GetPrivateInfo()[test_account] - test.Login(credentials['username'], credentials['password']) - login_info = test.GetLoginInfo() - test.assertTrue(login_info['is_logged_in'], msg='Login failed.') - else: - test.fail(msg='Another user is already logged in. Please logout first.') - -def GetInfobarIndexByType(test, infobar_type, windex=0, tab_index=0): - """Returns the index of the infobar of the given type. - - Args: - test: Derived from pyauto.PyUITest - base class for UI test cases. - infobar_type: The infobar type to look for. - windex: Window index. - tab_index: Tab index. - - Returns: - Index of infobar for infobar type, or None if not found. - """ - infobar_list = ( - test.GetBrowserInfo()['windows'][windex]['tabs'][tab_index] \ - ['infobars']) - for infobar in infobar_list: - if infobar_type == infobar['type']: - return infobar_list.index(infobar) - return None - -def WaitForInfobarTypeAndGetIndex(test, infobar_type, windex=0, tab_index=0): - """Wait for infobar type to appear and returns its index. - - If the infobar never appears, an exception will be raised. - - Args: - test: Derived from pyauto.PyUITest - base class for UI test cases. - infobar_type: The infobar type to look for. - windex: Window index. Defaults to 0 (first window). - tab_index: Tab index. Defaults to 0 (first tab). - - Returns: - Index of infobar for infobar type. - """ - test.assertTrue( - test.WaitUntil(lambda: GetInfobarIndexByType( - test, infobar_type, windex, tab_index) is not None), - msg='Infobar type for %s did not appear.' % infobar_type) - # Return the infobar index. - return GetInfobarIndexByType(test, infobar_type, windex, tab_index) - -def AssertInfobarTypeDoesNotAppear(test, infobar_type, windex=0, tab_index=0): - """Check that the infobar type does not appear. - - This function waits 20s to assert that the infobar does not appear. - - Args: - test: Derived from pyauto.PyUITest - base class for UI test cases. - infobar_type: The infobar type to look for. - windex: Window index. Defaults to 0 (first window). - tab_index: Tab index. Defaults to 0 (first tab). - """ - test.assertFalse( - test.WaitUntil(lambda: GetInfobarIndexByType( - test, infobar_type, windex, tab_index) is not None, timeout=20), - msg=('Infobar type for %s appeared when it should be hidden.' - % infobar_type)) - -def OpenCroshVerification(self): - """This test opens crosh. - - This function assumes that no browser windows are open. - """ - self.assertEqual(0, self.GetBrowserWindowCount()) - self.OpenCrosh() - self.assertEqual(1, self.GetBrowserWindowCount()) - self.assertEqual(1, self.GetTabCount(), - msg='Could not open crosh') - self.assertEqual('crosh', self.GetActiveTabTitle()) diff --git a/chrome/test/functional/tracing/pyauto_tracing.py b/chrome/test/functional/tracing/pyauto_tracing.py deleted file mode 100755 index a009657..0000000 --- a/chrome/test/functional/tracing/pyauto_tracing.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import os -import sys - -def _SetupPaths(): - """Setting path to find pyauto_functional.py.""" - tracing_dir = os.path.abspath(os.path.dirname(__file__)) - sys.path.append(tracing_dir) - sys.path.append(os.path.normpath(os.path.join(tracing_dir, os.pardir))) - -_SetupPaths() - - -from pyauto_functional import Main - - -if __name__ == '__main__': - Main() diff --git a/chrome/test/functional/tracing/tab_tracker.py b/chrome/test/functional/tracing/tab_tracker.py deleted file mode 100644 index 87e4886..0000000 --- a/chrome/test/functional/tracing/tab_tracker.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import uuid - - -class TabTracker(object): - """Uniquely track tabs within a window. - - This allows the creation of tabs whose indices can be - determined even after lower indexed tabs have been closed, therefore changing - that tab's index. - - This is accomplished via a containing window which is created and tracked via - the window's index. As a result of this, all calls to open and close tabs in - this TabTracker's window must go through the appropriate instance of the - TabTracker. Also note that if a lower indexed window is closed after this - TabTracker is instantiated, this TabTracker will lose track of its window - """ - - def __init__(self, browser, visible=False): - """ - Args: - browser: an instance of PyUITest - visible: whether or not this TabTracker's window will be visible - """ - # A binary search tree would be faster, but this is easier to write. - # If this needs to become faster, understand that the important operations - # here are append, arbitrary deletion and searching. - self._uuids = [None] - self._window_idx = browser.GetBrowserWindowCount() - self._browser = browser - browser.OpenNewBrowserWindow(visible) - # We leave the 0'th tab empty to have something to close on __del__ - - def __del__(self): - self._browser.CloseBrowserWindow(self._window_idx) - - def CreateTab(self, url='about:blank'): - """Create a tracked tab and return its uuid. - - Args: - url: a URL to navigate to - - Returns: - a uuid uniquely identifying that tab within this TabTracker - """ - self._browser.AppendTab(url, self._window_idx) - # We use uuids here rather than a monotonic integer to prevent confusion - # with the tab index. - tab_uuid = uuid.uuid4() - self._uuids.append(tab_uuid) - return tab_uuid - - def ReleaseTab(self, tab_uuid): - """Release and close a tab tracked by this TabTracker. - - Args: - tab_uuid: the uuid of the tab to close - """ - idx = self.GetTabIndex(tab_uuid) - self._browser.CloseTab(tab_index=idx, windex=self._window_idx) - del self._uuids[idx] - - def GetTabIndex(self, tab_uuid): - """Get the index of a tracked tab within this TabTracker's window. - - Args: - tab_uuid: the uuid of the tab to close - - Returns: - the index of the tab within this TabTracker's window - """ - return self._uuids.index(tab_uuid) - - def GetWindowIndex(self): - """Get the index of this TabTracker's window. - - Returns: - the index of this TabTracker's window - """ - return self._window_idx diff --git a/chrome/test/functional/tracing/timeline_model.py b/chrome/test/functional/tracing/timeline_model.py deleted file mode 100644 index 68573093..0000000 --- a/chrome/test/functional/tracing/timeline_model.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import json -import os - - -class TimelineModel(object): - """A proxy for about:tracing's TimelineModel class. - - Test authors should never need to know that this class is a proxy. - """ - @staticmethod - def _EscapeForQuotedJavascriptExecution(js): - # Poor man's string escape. - return js.replace('\'', '\\\''); - - def __init__(self, js_executor, shim_id): - self._js_executor = js_executor - self._shim_id = shim_id - - # Warning: The JSON serialization process removes cyclic references. - # TODO(eatnumber): regenerate these cyclic references on deserialization. - def _CallModelMethod(self, method_name, *args): - result = self._js_executor( - """window.timelineModelShims['%s'].invokeMethod('%s', '%s')""" % ( - self._shim_id, - self._EscapeForQuotedJavascriptExecution(method_name), - self._EscapeForQuotedJavascriptExecution(json.dumps(args)) - ) - ) - if result['success']: - return result['data'] - # TODO(eatnumber): Make these exceptions more reader friendly. - raise RuntimeError(result) - - def __del__(self): - self._js_executor(""" - window.timelineModelShims['%s'] = undefined; - window.domAutomationController.send(''); - """ % self._shim_id) - - def GetAllThreads(self): - return self._CallModelMethod('getAllThreads') - - def GetAllCpus(self): - return self._CallModelMethod('getAllCpus') - - def GetAllProcesses(self): - return self._CallModelMethod('getAllProcesses') - - def GetAllCounters(self): - return self._CallModelMethod('getAllCounters') - - def FindAllThreadsNamed(self, name): - return self._CallModelMethod('findAllThreadsNamed', name); diff --git a/chrome/test/functional/tracing/timeline_model_shim.js b/chrome/test/functional/tracing/timeline_model_shim.js deleted file mode 100644 index be4fcdb..0000000 --- a/chrome/test/functional/tracing/timeline_model_shim.js +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2012 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. - -function TimelineModelShim() { - tracing.TimelineModel.apply(this, arguments); -} - -TimelineModelShim.prototype = { - __proto__: tracing.TimelineModel.prototype, - - invokeMethod: function(methodName, args) { - var sendToPython = function(obj) { - // We use sendJSON here because domAutomationController's send() chokes on - // large amounts of data. Inside of send() it converts the arg to JSON and - // invokes sendJSON. The JSON conversion is what fails. This way works - // around the bad code, but note that the recieving python converts from - // JSON before passing it back to the pyauto test. - window.domAutomationController.sendJSON( - JSON.stringify(obj) - ); - }; - var result; - try { - result = this[methodName].apply(this, JSON.parse(args)); - } catch( e ) { - var ret = { - success: false, - message: 'Unspecified error', - }; - // We'll try sending the entire exception. If that doesn't work, it's ok. - try { - ret.exception = JSON.stringify(e); - } catch(e2) {} - if( typeof(e) == 'string' || e instanceof String ) { - ret.message = e; - } else { - if( e.stack != undefined ) ret.stack = e.stack; - if( e.message != undefined ) ret.message = e.message; - } - sendToPython(ret); - throw e; - } - sendToPython({ - success: true, - data: result - }); - } -}, - -// This causes the PyAuto ExecuteJavascript call which executed this file to -// return. -window.domAutomationController.send(''); diff --git a/chrome/test/functional/tracing/tracer.js b/chrome/test/functional/tracing/tracer.js deleted file mode 100644 index 75c43a7..0000000 --- a/chrome/test/functional/tracing/tracer.js +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2012 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. - -window.pyautoRecordTrace = function(systemTracing) { - 'use strict'; - if( window.timelineModelShimId == undefined ) - window.timelineModelShimId = 0; - if( window.timelineModelShims == undefined ) - window.timelineModelShims = {}; - var handler = function() { - tracingController.removeEventListener('traceEnded', handler); - var model = new TimelineModelShim( - Array.prototype.slice.call(arguments, 1) - ); - var events = [tracingController.traceEvents_]; - if (tracingController.supportsSystemTracing) - events.push(tracingController.systemTraceEvents_); - model.importTraces(events); - var shimId = window.timelineModelShimId; - window.timelineModelShims[shimId] = model; - window.domAutomationController.send(shimId); - window.timelineModelShimId++; - }; - tracingController.addEventListener('traceEnded', handler); - var willSystemTrace = - tracingController.supportsSystemTracing ? systemTracing : false; - tracingController.beginTracing(willSystemTrace); - return willSystemTrace; -}; - -// This causes the PyAuto ExecuteJavascript call which executed this file to -// return. -window.domAutomationController.send(''); diff --git a/chrome/test/functional/tracing/tracer.py b/chrome/test/functional/tracing/tracer.py deleted file mode 100644 index 0366e1d..0000000 --- a/chrome/test/functional/tracing/tracer.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import pyauto_tracing -import pyauto -import tab_tracker -from timeline_model import TimelineModel - -import os - - -class Tracer(object): - """Controlls chrome and system tracing, returning a TimelineModel.""" - - def __init__(self, browser, tab_tracker): - """ - Args: - browser: an instance of a PyUITest - tab_tracker: an instance of a TabTracker - """ - self._browser = browser - self._tab_tracker = tab_tracker - self._tab_uuid = tab_tracker.CreateTab('chrome://tracing') - - # TODO(eatnumber): Find a way to import timeline_model_shim.js from within - # TimelineModelProxy - # I'm not happy with importing timeline_model_shim.js - # here. I'd rather pull it in from within TimelineModelProxy. - # tracing_test.js depends on timeline_model_shim.js however. - files = ['timeline_model_shim.js', 'tracer.js'] - for fileName in files: - with open(os.path.join(os.path.dirname(__file__), fileName), 'r') as fd: - self._ExecuteJavascript(fd.read()) - - def __del__(self): - self._tab_tracker.ReleaseTab(self._tab_uuid) - - def _ExecuteJavascript(self, js): - return self._browser.ExecuteJavascript( - js = js, - windex = self._tab_tracker.GetWindowIndex(), - tab_index = self._tab_tracker.GetTabIndex(self._tab_uuid) - ) - - def BeginTracing(self, system_tracing=True): - """Start tracing. - - Args: - system_tracing: whether or not to gather system traces along with chrome - traces. - """ - self._ExecuteJavascript(""" - window.domAutomationController.send( - window.pyautoRecordTrace(%s) - ); - """ % ('true' if system_tracing else 'false')) - - def EndTracing(self): - """End tracing and return a TimelineModel. - - Returns: - an instance of a TimelineModel which contains the results of the trace - """ - return TimelineModel( - js_executor = self._ExecuteJavascript.__get__(self, Tracer), - shim_id = self._ExecuteJavascript('tracingController.endTracing();') - ) - - -class TracerFactory(object): - """A TracerFactory is used to produce a Tracer. - - It's recommended to use the same TracerFactory to produce all Tracers so that - the same TabTracker can be used throughout - - Args: - browser: an instance of a PyUITest - """ - def __init__(self, browser): - self._tab_tracker = tab_tracker.TabTracker(browser) - self._browser = browser - - def Produce(self): - """Produce an instance of a Tracer""" - return Tracer(self._browser, self._tab_tracker) diff --git a/chrome/test/functional/tracing/tracing_smoke_test.py b/chrome/test/functional/tracing/tracing_smoke_test.py deleted file mode 100755 index 4621421..0000000 --- a/chrome/test/functional/tracing/tracing_smoke_test.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import pyauto_tracing -import pyauto -import tracer - - -class TracingSmokeTest(pyauto.PyUITest): - """Test basic functionality of the tracing API.""" - def setUp(self): - super(TracingSmokeTest, self).setUp() - self._tracer_factory = tracer.TracerFactory(self) - - def testGetData(self): - """Check that we can find a CrBrowserMain thread.""" - tracer = self._tracer_factory.Produce() - tracer.BeginTracing() - model = tracer.EndTracing() - self.assertEqual(1, len(model.FindAllThreadsNamed('CrBrowserMain'))) - - def testMultipleTraces(self): - """Check that we can run multiple traces on the same tracer.""" - tracer = self._tracer_factory.Produce() - tracer.BeginTracing() - model1 = tracer.EndTracing() - tracer.BeginTracing() - model2 = tracer.EndTracing() - self.assertEqual(1, len(model1.FindAllThreadsNamed('CrBrowserMain'))) - self.assertEqual(1, len(model2.FindAllThreadsNamed('CrBrowserMain'))) - - def testMultipleTracers(self): - """Check that we can run multiple traces with multiple tracers.""" - tracer1 = self._tracer_factory.Produce() - tracer2 = self._tracer_factory.Produce() - # Nested calls to beginTracing is untested and probably won't work. - tracer1.BeginTracing() - model1 = tracer1.EndTracing() - tracer2.BeginTracing() - model2 = tracer2.EndTracing() - self.assertEqual(1, len(model1.FindAllThreadsNamed('CrBrowserMain'))) - self.assertEqual(1, len(model2.FindAllThreadsNamed('CrBrowserMain'))) - - def testModelValidAfterTracer(self): - """Check that a TimelineModel is valid after its Tracer is gone.""" - tracer = self._tracer_factory.Produce() - del self._tracer_factory - tracer.BeginTracing() - model = tracer.EndTracing() - del tracer - self.assertEqual(1, len(model.FindAllThreadsNamed('CrBrowserMain'))) - - -if __name__ == '__main__': - pyauto_tracing.Main() diff --git a/chrome/test/functional/webdriver_pages/__init__.py b/chrome/test/functional/webdriver_pages/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/chrome/test/functional/webdriver_pages/__init__.py +++ /dev/null diff --git a/chrome/test/functional/webdriver_pages/settings.py b/chrome/test/functional/webdriver_pages/settings.py deleted file mode 100644 index 2a916c0..0000000 --- a/chrome/test/functional/webdriver_pages/settings.py +++ /dev/null @@ -1,699 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import types - -import selenium.common.exceptions -from selenium.webdriver.common.action_chains import ActionChains -from selenium.webdriver.support.ui import WebDriverWait - - -def _FocusField(driver, list_elem, field_elem): - """Focuses a field in a dynamic list. - - Note, the item containing the field should not be focused already. - - Typing into a field is tricky because the js automatically focuses and - selects the text field after 50ms after it first receives focus. This - method focuses the field and waits for the timeout to occur. - For more info, see inline_editable_list.js and search for setTimeout. - See crbug.com/97369. - - Args: - list_elem: An element in the HTML list. - field_elem: An element in the HTML text field. - - Raises: - RuntimeError: If a timeout occurs when waiting for the focus event. - """ - # To wait properly for the focus, we focus the last text field, and then - # add a focus listener to it, so that we return when the element is focused - # again after the timeout. We have to focus a different element in between - # these steps, otherwise the focus event will not fire since the element - # already has focus. - # Ideally this should be fixed in the page. - - correct_focus_script = """ - (function(listElem, itemElem, callback) { - if (document.activeElement == itemElem) { - callback(); - return; - } - itemElem.focus(); - listElem.focus(); - itemElem.addEventListener("focus", callback); - }).apply(null, arguments); - """ - driver.set_script_timeout(5) - try: - driver.execute_async_script(correct_focus_script, list_elem, field_elem) - except selenium.common.exceptions.TimeoutException: - raise RuntimeError('Unable to focus list item ' + field_elem.tag_name) - - -class Item(object): - """A list item web element.""" - def __init__(self, elem): - self._elem = elem - - def Remove(self, driver): - button = self._elem.find_element_by_xpath('./button') - ActionChains(driver).move_to_element(button).click().perform() - - -class TextFieldsItem(Item): - """An item consisting only of text fields.""" - def _GetFields(self): - """Returns the text fields list.""" - return self._elem.find_elements_by_tag_name('input') - - def Set(self, values): - """Sets the value(s) of the item's text field(s). - - Args: - values: The new value or the list of the new values of the fields. - """ - field_list = self._GetFields() - if len(field_list) > 1: - assert type(values) == types.ListType, \ - """The values must be a list for a HTML list that has multi-field - items. '%s' should be in a list.""" % values - value_list = values - else: - value_list = [values] - - assert len(field_list) == len(value_list), \ - """The item to be added must have the same number of fields as an item - in the HTML list. Given item '%s' should have %s fields.""" % ( - value_list, len(field_list)) - for field, value in zip(field_list, value_list): - field.clear() - field.send_keys(value) - field_list[-1].send_keys('\n') # press enter on the last field. - - def Get(self): - """Returns the list of the text field values.""" - return map(lambda f: f.get_attribute('value'), self._GetFields()) - - -class TextField(object): - """A text field web element.""" - def __init__(self, elem): - self._elem = elem - - def Set(self, value): - """Sets the value of the text field. - - Args: - value: The new value of the field. - """ - self._elem.clear() - self._elem.send_keys(value) - - def Get(self): - """Returns the value of the text field.""" - return self._elem.get_attribute('value') - - -class List(object): - """A web element that holds a list of items.""" - - def __init__(self, driver, elem, item_class=Item): - """item element is an element in the HTML list. - item class is the class of item the list holds.""" - self._driver = driver - self._elem = elem - self._item_class = item_class - - def RemoveAll(self): - """Removes all items from the list. - - In the loop the removal of an elem renders the remaining elems of the list - invalid. After each item is removed, GetItems() is called. - """ - for i in range(len(self.GetItems())): - self.GetItems()[0].Remove(self._driver) - - def GetItems(self): - """Returns all the items that are in the list.""" - items = self._GetItemElems() - return map(lambda x: self._item_class(x), items) - - def GetSize(self): - """Returns the number of items in the list.""" - return len(self._GetItemElems()) - - def _GetItemElems(self): - return self._elem.find_elements_by_xpath('.//*[@role="listitem"]') - - -class DynamicList(List): - """A web element that holds a dynamic list of items of text fields. - - Terminology: - item element: an element in the HTML list item. - item_class: the class of item the list holds - placeholder: the last item element in the list, which is not committed yet - - The user can add new items to the list by typing in the placeholder item. - When a user presses enter or focuses something else, the placeholder item - is committed and a new placeholder is created. An item may contain 1 or - more text fields. - """ - - def __init__(self, driver, elem, item_class=TextFieldsItem): - return super(DynamicList, self).__init__( - driver, elem, item_class=item_class) - - def GetPlaceholderItem(self): - return self.GetItems()[-1] - - def GetCommittedItems(self): - """Returns all the items that are in the list, except the placeholder.""" - return map(lambda x: self._item_class(x), self._GetCommittedItemElems()) - - def GetSize(self): - """Returns the number of items in the list, excluding the placeholder.""" - return len(self._GetCommittedItemElems()) - - def _GetCommittedItemElems(self): - return self._GetItemElems()[:-1] - - def _GetPlaceholderElem(self): - return self._GetItemElems()[-1] - - -class AutofillEditAddressDialog(object): - """The overlay for editing an autofill address.""" - - _URL = 'chrome://settings-frame/autofillEditAddress' - - @staticmethod - def FromNavigation(driver): - """Creates an instance of the dialog by navigating directly to it.""" - driver.get(AutofillEditAddressDialog._URL) - return AutofillEditAddressDialog(driver) - - def __init__(self, driver): - self.driver = driver - assert self._URL == driver.current_url - self.dialog_elem = driver.find_element_by_id( - 'autofill-edit-address-overlay') - - def Fill(self, names=None, addr_line_1=None, city=None, state=None, - postal_code=None, country_code=None, phones=None): - """Fills in the given data into the appropriate fields. - - If filling into a text field, the given value will replace the current one. - If filling into a list, the values will be added after all items are - deleted. - - Note: 'names', in the new autofill UI, is an array of full names. A full - name is an array of first, middle, last names. Example: - names=[['Joe', '', 'King'], ['Fred', 'W', 'Michno']] - - Args: - names: List of names; each name should be [first, middle, last]. - addr_line_1: First line in the address. - city: City. - state: State. - postal_code: Postal code (zip code for US). - country_code: Country code (e.g., US or FR). - phones: List of phone numbers. - """ - id_dict = {'addr-line-1': addr_line_1, - 'city': city, - 'state': state, - 'postal-code': postal_code} - for id, value in id_dict.items(): - if value is not None: - TextField(self.dialog_elem.find_element_by_id(id)).Set(value) - - list_id_dict = {'full-name-list': names, - 'phone-list': phones} - for list_id, values in list_id_dict.items(): - if values is not None: - list = DynamicList(self.driver, - self.dialog_elem.find_element_by_id(list_id)) - list.RemoveAll() - for value in values: - list.GetPlaceholderItem().Set(value) - - if country_code is not None: - self.dialog_elem.find_element_by_xpath( - './/*[@id="country"]/*[@value="%s"]' % country_code).click() - - def GetStateLabel(self): - """Returns the label used for the state text field.""" - return self.dialog_elem.find_element_by_id('state-label').text - - def GetPostalCodeLabel(self): - """Returns the label used for the postal code text field.""" - return self.dialog_elem.find_element_by_id('postal-code-label').text - - def GetPhones(self): - """Returns a list of the phone numbers in the phones list.""" - list = DynamicList( - self.driver, self.dialog_elem.find_element_by_id('phone-list')) - return [item.Get()[0] for item in list.GetCommittedItems()] - - -class ContentTypes(object): - COOKIES = 'cookies' - IMAGES = 'images' - JAVASCRIPT = 'javascript' - HANDLERS = 'handlers' - PLUGINS = 'plugins' - POPUPS = 'popups' - GEOLOCATION = 'location' - NOTIFICATIONS = 'notifications' - PASSWORDS = 'passwords' - - -class Behaviors(object): - ALLOW = 'allow' - SESSION_ONLY = 'session_only' - ASK = 'ask' - BLOCK = 'block' - - -class ContentSettingsPage(object): - """The overlay for managing exceptions on the Content Settings page.""" - - _URL = 'chrome://settings-frame/content' - - @staticmethod - def FromNavigation(driver): - """Creates an instance of the dialog by navigating directly to it.""" - driver.get(ContentSettingsPage._URL) - return ContentSettingsPage(driver) - - def __init__(self, driver): - assert self._URL == driver.current_url - self.page_elem = driver.find_element_by_id( - 'content-settings-page') - - def SetContentTypeOption(self, content_type, option): - """Set the option for the specified content type. - - Args: - content_type: The content type to manage. - option: The option to allow, deny or ask. - """ - self.page_elem.find_element_by_xpath( - './/*[@name="%s"][@value="%s"]' % (content_type, option)).click() - - -class ManageExceptionsPage(object): - """The overlay for the content exceptions page.""" - - @staticmethod - def FromNavigation(driver, content_type): - """Creates an instance of the dialog by navigating directly to it. - - Args: - driver: The remote WebDriver instance to manage some content type. - content_type: The content type to manage. - """ - content_url = 'chrome://settings-frame/contentExceptions#%s' % content_type - driver.get(content_url) - return ManageExceptionsPage(driver, content_type) - - def __init__(self, driver, content_type): - self._list_elem = driver.find_element_by_xpath( - './/*[@id="content-settings-exceptions-area"]' - '//*[@contenttype="%s"]//list[@role="list"]' - '[@class="settings-list"]' % content_type) - self._driver = driver - self._content_type = content_type - try: - self._incognito_list_elem = driver.find_element_by_xpath( - './/*[@id="content-settings-exceptions-area"]' - '//*[@contenttype="%s"]//div[not(@hidden)]' - '//list[@mode="otr"][@role="list"]' - '[@class="settings-list"]' % content_type) - except selenium.common.exceptions.NoSuchElementException: - self._incognito_list_elem = None - - def _AssertIncognitoAvailable(self): - if not self._incognito_list_elem: - raise AssertionError( - 'Incognito settings in "%s" content page not available' - % self._content_type) - - def _GetExceptionList(self, incognito): - if not incognito: - list_elem = self._list_elem - else: - list_elem = self._incognito_list_elem - return DynamicList(self._driver, list_elem) - - def _GetPatternList(self, incognito): - if not incognito: - list_elem = self._list_elem - else: - list_elem = self._incognito_list_elem - pattern_list = [p.text for p in - list_elem.find_elements_by_xpath( - './/*[contains(@class, "exception-pattern")]' - '//*[@class="static-text"]')] - return pattern_list - - def AddNewException(self, pattern, behavior, incognito=False): - """Add a new pattern and behavior to the Exceptions page. - - Args: - pattern: Hostname pattern string. - behavior: Setting for the hostname pattern (Allow, Block, Session Only). - incognito: Incognito list box. Display to false. - - Raises: - AssertionError when an exception cannot be added on the content page. - """ - if incognito: - self._AssertIncognitoAvailable() - list_elem = self._incognito_list_elem - else: - list_elem = self._list_elem - # Select behavior first. - try: - list_elem.find_element_by_xpath( - './/*[@class="exception-setting"]' - '[not(@displaymode)]//option[@value="%s"]' - % behavior).click() - except selenium.common.exceptions.NoSuchElementException: - raise AssertionError( - 'Adding new exception not allowed in "%s" content page' - % self._content_type) - # Set pattern now. - self._GetExceptionList(incognito).GetPlaceholderItem().Set(pattern) - - def DeleteException(self, pattern, incognito=False): - """Delete the exception for the selected hostname pattern. - - Args: - pattern: Hostname pattern string. - incognito: Incognito list box. Default to false. - """ - if incognito: - self._AssertIncognitoAvailable() - list = self._GetExceptionList(incognito) - items = filter(lambda item: item.Get()[0] == pattern, - list.GetComittedItems()) - map(lambda item: item.Remove(self._driver), items) - - def GetExceptions(self, incognito=False): - """Returns a dictionary of {pattern: behavior}. - - Example: {'file:///*': 'block'} - - Args: - incognito: Incognito list box. Default to false. - """ - if incognito: - self._AssertIncognitoAvailable() - list_elem = self._incognito_list_elem - else: - list_elem = self._list_elem - pattern_list = self._GetPatternList(incognito) - behavior_list = list_elem.find_elements_by_xpath( - './/*[@role="listitem"][@class="deletable-item"]' - '//*[@class="exception-setting"][@displaymode="static"]') - assert len(pattern_list) == len(behavior_list), \ - 'Number of patterns does not match the behaviors.' - return dict(zip(pattern_list, [b.text.lower() for b in behavior_list])) - - def GetBehaviorForPattern(self, pattern, incognito=False): - """Returns the behavior for a given pattern on the Exceptions page. - - Args: - pattern: Hostname pattern string. - incognito: Incognito list box. Default to false. - """ - if incognito: - self._AssertIncognitoAvailable() - assert self.GetExceptions(incognito).has_key(pattern), \ - 'No displayed host name matches pattern "%s"' % pattern - return self.GetExceptions(incognito)[pattern] - - def SetBehaviorForPattern(self, pattern, behavior, incognito=False): - """Set the behavior for the selected pattern on the Exceptions page. - - Args: - pattern: Hostname pattern string. - behavior: Setting for the hostname pattern (Allow, Block, Session Only). - incognito: Incognito list box. Default to false. - - Raises: - AssertionError when the behavior cannot be changed on the content page. - """ - if incognito: - self._AssertIncognitoAvailable() - list_elem = self._incognito_list_elem - else: - list_elem = self._list_elem - pattern_list = self._GetPatternList(incognito) - listitem_list = list_elem.find_elements_by_xpath( - './/*[@role="listitem"][@class="deletable-item"]') - pattern_listitem_dict = dict(zip(pattern_list, listitem_list)) - # Set focus to appropriate listitem. - listitem_elem = pattern_listitem_dict[pattern] - listitem_elem.click() - # Set behavior. - try: - listitem_elem.find_element_by_xpath( - './/option[@value="%s"]' % behavior).click() - except selenium.common.exceptions.ElementNotVisibleException: - raise AssertionError( - 'Changing the behavior is invalid for pattern ' - '"%s" in "%s" content page' % (behavior, self._content_type)) - # Send enter key. - pattern_elem = listitem_elem.find_element_by_tag_name('input') - pattern_elem.send_keys('\n') - - -class RestoreOnStartupType(object): - NEW_TAB_PAGE = 5 - RESTORE_SESSION = 1 - RESTORE_URLS = 4 - - -class BasicSettingsPage(object): - """The basic settings page.""" - _URL = 'chrome://settings-frame/settings' - - @staticmethod - def FromNavigation(driver): - """Creates an instance of BasicSetting page by navigating to it.""" - driver.get(BasicSettingsPage._URL) - return BasicSettingsPage(driver) - - def __init__(self, driver): - self._driver = driver - assert self._URL == driver.current_url - - def SetOnStartupOptions(self, on_startup_option): - """Set on-startup options. - - Args: - on_startup_option: option types for on start up settings. - - Raises: - AssertionError when invalid startup option type is provided. - """ - if on_startup_option == RestoreOnStartupType.NEW_TAB_PAGE: - startup_option_elem = self._driver.find_element_by_id('startup-newtab') - elif on_startup_option == RestoreOnStartupType.RESTORE_SESSION: - startup_option_elem = self._driver.find_element_by_id( - 'startup-restore-session') - elif on_startup_option == RestoreOnStartupType.RESTORE_URLS: - startup_option_elem = self._driver.find_element_by_id( - 'startup-show-pages') - else: - raise AssertionError('Invalid value for restore start up option!') - startup_option_elem.click() - - def _GoToStartupSetPages(self): - self._driver.find_element_by_id('startup-set-pages').click() - - def _FillStartupURL(self, url): - list = DynamicList(self._driver, self._driver.find_element_by_id( - 'startupPagesList')) - list.GetPlaceholderItem().Set(url + '\n') - - def AddStartupPage(self, url): - """Add a startup URL. - - Args: - url: A startup url. - """ - self._GoToStartupSetPages() - self._FillStartupURL(url) - self._driver.find_element_by_id('startup-overlay-confirm').click() - self._driver.get(self._URL) - - def UseCurrentPageForStartup(self, title_list): - """Use current pages and verify page url show up in settings. - - Args: - title_list: startup web page title list. - """ - self._GoToStartupSetPages() - self._driver.find_element_by_id('startupUseCurrentButton').click() - self._driver.find_element_by_id('startup-overlay-confirm').click() - def is_current_page_visible(driver): - title_elem_list = driver.find_elements_by_xpath( - '//*[contains(@class, "title")][text()="%s"]' % title_list[0]) - if len(title_elem_list) == 0: - return False - return True - WebDriverWait(self._driver, 10).until(is_current_page_visible) - self._driver.get(self._URL) - - def VerifyStartupURLs(self, title_list): - """Verify saved startup URLs appear in set page UI. - - Args: - title_list: A list of startup page title. - - Raises: - AssertionError when start up URLs do not appear in set page UI. - """ - self._GoToStartupSetPages() - for i in range(len(title_list)): - try: - self._driver.find_element_by_xpath( - '//*[contains(@class, "title")][text()="%s"]' % title_list[i]) - except selenium.common.exceptions.NoSuchElementException: - raise AssertionError("Current page %s did not appear as startup page." - % title_list[i]) - self._driver.find_element_by_id('startup-overlay-cancel').click() - - def CancelStartupURLSetting(self, url): - """Cancel start up URL settings. - - Args: - url: A startup url. - """ - self._GoToStartupSetPages() - self._FillStartupURL(url) - self._driver.find_element_by_id('startup-overlay-cancel').click() - self._driver.get(self._URL) - - -class PasswordsSettings(object): - """The overlay for managing passwords on the Content Settings page.""" - - _URL = 'chrome://settings-frame/passwords' - - class PasswordsItem(Item): - """A list of passwords item web element.""" - def _GetFields(self): - """Returns the field list element.""" - return self._elem.find_elements_by_xpath('./div/*') - - def GetSite(self): - """Returns the site field value.""" - return self._GetFields()[0].text - - def GetUsername(self): - """Returns the username field value.""" - return self._GetFields()[1].text - - - @staticmethod - def FromNavigation(driver): - """Creates an instance of the dialog by navigating directly to it. - - Args: - driver: The remote WebDriver instance to manage some content type. - """ - driver.get(PasswordsSettings._URL) - return PasswordsSettings(driver) - - def __init__(self, driver): - self._driver = driver - assert self._URL == driver.current_url - list_elem = driver.find_element_by_id('saved-passwords-list') - self._items_list = List(self._driver, list_elem, self.PasswordsItem) - - def DeleteItem(self, url, username): - """Deletes a line entry in Passwords Content Settings. - - Args: - url: The URL string as it appears in the UI. - username: The username string as it appears in the second column. - """ - for password_item in self._items_list.GetItems(): - if (password_item.GetSite() == url and - password_item.GetUsername() == username): - password_item.Remove(self._driver) - - -class CookiesAndSiteDataSettings(object): - """The overlay for managing cookies on the Content Settings page.""" - - _URL = 'chrome://settings-frame/cookies' - - @staticmethod - def FromNavigation(driver): - """Creates an instance of the dialog by navigating directly to it. - - Args: - driver: The remote WebDriver instance for managing content type. - """ - driver.get(CookiesAndSiteDataSettings._URL) - return CookiesAndSiteDataSettings(driver) - - def __init__(self, driver): - self._driver = driver - assert self._URL == driver.current_url - self._list_elem = driver.find_element_by_id('cookies-list') - - def GetSiteNameList(self): - """Returns a list of the site names. - - This is a public function since the test needs to check if the site is - deleted. - """ - site_list = [p.text for p in - self._list_elem.find_elements_by_xpath( - './/*[contains(@class, "deletable-item")]' - '//div[@class="cookie-site"]')] - return site_list - - def _GetCookieNameList(self): - """Returns a list where each item is the list of cookie names of each site. - - Example: site1 | cookie1 cookie2 - site2 | cookieA - site3 | cookieA cookie1 cookieB - - Returns: - A cookie names list such as: - [ ['cookie1', 'cookie2'], ['cookieA'], ['cookieA', 'cookie1', 'cookieB'] ] - """ - cookie_name_list = [] - for elem in self._list_elem.find_elements_by_xpath( - './/*[@role="listitem"]'): - elem.click() - cookie_name_list.append([c.text for c in - elem.find_elements_by_xpath('.//div[@class="cookie-item"]')]) - return cookie_name_list - - def DeleteSiteData(self, site): - """Delete a site entry with its cookies in cookies content settings. - - Args: - site: The site string as it appears in the UI. - """ - delete_button_list = self._list_elem.find_elements_by_class_name( - 'row-delete-button') - site_list = self.GetSiteNameList() - for i in range(len(site_list)): - if site_list[i] == site: - # Highlight the item so the close button shows up, then delete button - # shows up, then click on the delete button. - ActionChains(self._driver).move_to_element( - delete_button_list[i]).click().perform() diff --git a/chrome/test/functional/webpagereplay.py b/chrome/test/functional/webpagereplay.py deleted file mode 100755 index 8d115ef..0000000 --- a/chrome/test/functional/webpagereplay.py +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Start and stop Web Page Replay. - -Of the public module names, the following one is key: - ReplayServer: a class to start/stop Web Page Replay. -""" - -import logging -import os -import re -import signal -import subprocess -import sys -import time -import urllib - - -_CHROME_SRC_DIR = os.path.abspath(os.path.join( - os.path.dirname(__file__), os.pardir, os.pardir, os.pardir)) -REPLAY_DIR = os.path.join( - _CHROME_SRC_DIR, 'third_party', 'webpagereplay') -LOG_PATH = os.path.join( - _CHROME_SRC_DIR, 'webpagereplay_logs', 'logs.txt') - - -# Chrome options to make it work with Web Page Replay. -def GetChromeFlags(replay_host, http_port, https_port): - assert replay_host and http_port and https_port, 'All arguments required' - return [ - '--host-resolver-rules=MAP * %s,EXCLUDE localhost' % replay_host, - '--testing-fixed-http-port=%s' % http_port, - '--testing-fixed-https-port=%s' % https_port, - '--ignore-certificate-errors', - ] - - -# Signal masks on Linux are inherited from parent processes. If anything -# invoking us accidentally masks SIGINT (e.g. by putting a process in the -# background from a shell script), sending a SIGINT to the child will fail -# to terminate it. Running this signal handler before execing should fix that -# problem. -def ResetInterruptHandler(): - signal.signal(signal.SIGINT, signal.SIG_DFL) - - -class ReplayError(Exception): - """Catch-all exception for the module.""" - pass - - -class ReplayNotFoundError(ReplayError): - def __init__(self, label, path): - self.args = (label, path) - - def __str__(self): - label, path = self.args - return 'Path does not exist for %s: %s' % (label, path) - - -class ReplayNotStartedError(ReplayError): - pass - - -class ReplayServer(object): - """Start and Stop Web Page Replay. - - Web Page Replay is a proxy that can record and "replay" web pages with - simulated network characteristics -- without having to edit the pages - by hand. With WPR, tests can use "real" web content, and catch - performance issues that may result from introducing network delays and - bandwidth throttling. - - Example: - with ReplayServer(archive_path): - self.NavigateToURL(start_url) - self.WaitUntil(...) - - Environment Variables (for development): - WPR_ARCHIVE_PATH: path to alternate archive file (e.g. '/tmp/foo.wpr'). - WPR_RECORD: if set, puts Web Page Replay in record mode instead of replay. - WPR_REPLAY_DIR: path to alternate Web Page Replay source. - """ - - def __init__(self, archive_path, replay_host, dns_port, http_port, https_port, - replay_options=None, replay_dir=None, - log_path=None): - """Initialize ReplayServer. - - Args: - archive_path: a path to a specific WPR archive (required). - replay_host: the hostname to serve traffic. - dns_port: an integer port on which to serve DNS traffic. May be zero - to let the OS choose an available port. If None DNS forwarding is - disabled. - http_port: an integer port on which to serve HTTP traffic. May be zero - to let the OS choose an available port. - https_port: an integer port on which to serve HTTPS traffic. May be zero - to let the OS choose an available port. - replay_options: an iterable of options strings to forward to replay.py. - replay_dir: directory that has replay.py and related modules. - log_path: a path to a log file. - """ - self.archive_path = os.environ.get('WPR_ARCHIVE_PATH', archive_path) - self.replay_options = list(replay_options or ()) - self.replay_dir = os.environ.get('WPR_REPLAY_DIR', replay_dir or REPLAY_DIR) - self.log_path = log_path or LOG_PATH - self.dns_port = dns_port - self.http_port = http_port - self.https_port = https_port - self._replay_host = replay_host - - if 'WPR_RECORD' in os.environ and '--record' not in self.replay_options: - self.replay_options.append('--record') - self.is_record_mode = '--record' in self.replay_options - self._AddDefaultReplayOptions() - - self.replay_py = os.path.join(self.replay_dir, 'replay.py') - - if self.is_record_mode: - self._CheckPath('archive directory', os.path.dirname(self.archive_path)) - elif not os.path.exists(self.archive_path): - self._CheckPath('archive file', self.archive_path) - self._CheckPath('replay script', self.replay_py) - - self.log_fh = None - self.replay_process = None - - def _AddDefaultReplayOptions(self): - """Set WPR command-line options. Can be overridden if needed.""" - self.replay_options = [ - '--host', str(self._replay_host), - '--port', str(self.http_port), - '--ssl_port', str(self.https_port), - '--use_closest_match', - '--no-dns_forwarding', - '--log_level', 'warning' - ] + self.replay_options - if self.dns_port is not None: - self.replay_options.extend(['--dns_port', str(self.dns_port)]) - - def _CheckPath(self, label, path): - if not os.path.exists(path): - raise ReplayNotFoundError(label, path) - - def _OpenLogFile(self): - log_dir = os.path.dirname(self.log_path) - if not os.path.exists(log_dir): - os.makedirs(log_dir) - return open(self.log_path, 'w') - - def WaitForStart(self, timeout): - """Checks to see if the server is up and running.""" - port_re = re.compile( - '.*?(?P<protocol>[A-Z]+) server started on (?P<host>.*):(?P<port>\d+)') - - start_time = time.time() - elapsed_time = 0 - while elapsed_time < timeout: - if self.replay_process.poll() is not None: - break # The process has exited. - - # Read the ports from the WPR log. - if not self.http_port or not self.https_port or not self.dns_port: - for line in open(self.log_path).readlines(): - m = port_re.match(line.strip()) - if m: - if not self.http_port and m.group('protocol') == 'HTTP': - self.http_port = int(m.group('port')) - elif not self.https_port and m.group('protocol') == 'HTTPS': - self.https_port = int(m.group('port')) - elif not self.dns_port and m.group('protocol') == 'DNS': - self.dns_port = int(m.group('port')) - - # Try to connect to the WPR ports. - if self.http_port and self.https_port: - try: - up_url = '%s://%s:%s/web-page-replay-generate-200' - http_up_url = up_url % ('http', self._replay_host, self.http_port) - https_up_url = up_url % ('https', self._replay_host, self.https_port) - if (200 == urllib.urlopen(http_up_url, None, {}).getcode() and - 200 == urllib.urlopen(https_up_url, None, {}).getcode()): - return True - except IOError: - pass - - poll_interval = min(max(elapsed_time / 10., .1), 5) - time.sleep(poll_interval) - elapsed_time = time.time() - start_time - - return False - - def StartServer(self): - """Start Web Page Replay and verify that it started. - - Raises: - ReplayNotStartedError: if Replay start-up fails. - """ - cmd_line = [sys.executable, self.replay_py] - cmd_line.extend(self.replay_options) - cmd_line.append(self.archive_path) - self.log_fh = self._OpenLogFile() - logging.debug('Starting Web-Page-Replay: %s', cmd_line) - kwargs = {'stdout': self.log_fh, 'stderr': subprocess.STDOUT} - if sys.platform.startswith('linux') or sys.platform == 'darwin': - kwargs['preexec_fn'] = ResetInterruptHandler - self.replay_process = subprocess.Popen(cmd_line, **kwargs) - if not self.WaitForStart(30): - log = open(self.log_path).read() - raise ReplayNotStartedError( - 'Web Page Replay failed to start. Log output:\n%s' % log) - - def StopServer(self): - """Stop Web Page Replay.""" - if self.replay_process: - logging.debug('Trying to stop Web-Page-Replay gracefully') - try: - url = 'http://localhost:%s/web-page-replay-command-exit' - urllib.urlopen(url % self.http_port, None, {}) - except IOError: - # IOError is possible because the server might exit without response. - pass - - start_time = time.time() - while time.time() - start_time < 10: # Timeout after 10 seconds. - if self.replay_process.poll() is not None: - break - time.sleep(1) - else: - try: - # Use a SIGINT so that it can do graceful cleanup. - self.replay_process.send_signal(signal.SIGINT) - except: # pylint: disable=W0702 - # On Windows, we are left with no other option than terminate(). - if 'no-dns_forwarding' not in self.replay_options: - logging.warning('DNS configuration might not be restored!') - try: - self.replay_process.terminate() - except: # pylint: disable=W0702 - pass - self.replay_process.wait() - if self.log_fh: - self.log_fh.close() - - def __enter__(self): - """Add support for with-statement.""" - self.StartServer() - return self - - def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb): - """Add support for with-statement.""" - self.StopServer() diff --git a/chrome/test/functional/webrtc_write_wsh.py b/chrome/test/functional/webrtc_write_wsh.py deleted file mode 100644 index 0cd13d3..0000000 --- a/chrome/test/functional/webrtc_write_wsh.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) 2012 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. -# -# This module is handler for incoming data to the pywebsocket standalone server -# (source is in http://code.google.com/p/pywebsocket/source/browse/trunk/src/). -# It follows the conventions of the pywebsocket server and in our case receives -# and stores incoming frames to disk. - -import Queue -import os -import sys -import threading - -_NUMBER_OF_WRITER_THREADS = 10 - -_HOME_ENV_NAME = 'HOMEPATH' if 'win32' == sys.platform else 'HOME' -_WORKING_DIR = os.path.join(os.environ[_HOME_ENV_NAME], 'webrtc_video_quality') - -# I couldn't think of other way to handle this but through a global variable -g_frame_number_counter = 0 -g_frames_queue = Queue.Queue() - - -def web_socket_do_extra_handshake(request): - pass # Always accept. - - -def web_socket_transfer_data(request): - while True: - data = request.ws_stream.receive_message() - if data is None: - return - - # We assume we will receive only frames, i.e. binary data - global g_frame_number_counter - frame_number = str(g_frame_number_counter) - g_frame_number_counter += 1 - g_frames_queue.put((frame_number, data)) - - -class FrameWriterThread(threading.Thread): - """Writes received frames to disk. - - The frames are named in the format frame_xxxx, where xxxx is the 0-padded - frame number. The frames and their numbers are obtained from a synchronized - queue. The frames are written in the directory specified by _WORKING_DIR. - """ - def __init__(self, queue): - threading.Thread.__init__(self) - self._queue = queue - - def run(self): - while True: - frame_number, frame_data = self._queue.get() - file_name = 'frame_' + frame_number.zfill(4) - file_name = os.path.join(_WORKING_DIR, file_name) - frame = open(file_name, "wb") - frame.write(frame_data) - frame.close() - self._queue.task_done() - - -def start_threads(): - for i in range(_NUMBER_OF_WRITER_THREADS): - t = FrameWriterThread(g_frames_queue) - t.setDaemon(True) - t.start() - g_frames_queue.join() - - -# This handler's entire code is imported as 'it is' and then incorporated in the -# code of the standalone pywebsocket server. If we put this start_threads() call -# inside a if __name__ == '__main__' clause it wouldn't run this code at all -# (tested). -start_threads() diff --git a/chrome/test/functional/youtube.py b/chrome/test/functional/youtube.py deleted file mode 100755 index 46a3d27..0000000 --- a/chrome/test/functional/youtube.py +++ /dev/null @@ -1,273 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import re -import time - -import pyauto_functional -import pyauto -import pyauto_errors -import test_utils - - -class YoutubeTestHelper(): - """Helper functions for Youtube tests. - - For sample usage, look at class YoutubeTest. - """ - - # YouTube player states - is_unstarted = '-1' - is_playing = '1' - is_paused = '2' - has_ended = '0' - _pyauto = None - - def __init__(self, pyauto): - self._pyauto = pyauto - - def IsFlashPluginEnabled(self): - """Verify flash plugin availability and its state.""" - return [x for x in self._pyauto.GetPluginsInfo().Plugins() \ - if x['name'] == 'Shockwave Flash' and x['enabled']] - - def AssertPlayerState(self, state, msg): - expected_regex = '^%s$' % state - self.WaitForDomNode('id("playerState")', expected_value=expected_regex, - msg=msg) - - def WaitUntilPlayerReady(self): - """Verify that player is ready.""" - self.AssertPlayerState(state=self.is_unstarted, - msg='Failed to load youtube player.') - - def GetPlayerState(self): - """Returns a player state.""" - js = """ - var val = ytplayer.getPlayerState(); - window.domAutomationController.send(val + ''); - """ - return self._pyauto.ExecuteJavascript(js) - - def GetVideoInfo(self): - """Returns Youtube video info.""" - youtube_apis = self._pyauto.GetPrivateInfo()['youtube_api'] - youtube_debug_text = youtube_apis['GetDebugText'] - return self._pyauto.ExecuteJavascript( - 'window.domAutomationController.send(%s);' % youtube_debug_text) - - def GetVideoDroppedFrames(self): - """Returns total Youtube video dropped frames. - - Returns: - -1 if failed to get video frames from the video data - """ - video_data = self._pyauto.GetVideoInfo() - matched = re.search('droppedFrames=([\d\.]+)', video_data) - if matched: - return int(matched.group(1)) - else: - return -1 - - def GetVideoFrames(self): - """Returns Youtube video frames/second. - - Returns: - -1 if failed to get droppd frames from the video data. - """ - video_data = self._pyauto.GetVideoInfo() - matched = re.search('videoFps=([\d\.]+)', video_data) - if matched: - return int(matched.group(1)) - else: - return -1 - - def GetVideoTotalBytes(self): - """Returns video total size in bytes. - - To call this function, video must be in the paying state, - or this returns 0. - """ - total_bytes = 0 - total_bytes = self._pyauto.ExecuteJavascript(""" - bytesTotal = document.getElementById("bytesTotal"); - window.domAutomationController.send(bytesTotal.innerHTML); - """) - return int(total_bytes) - - def GetVideoLoadedBytes(self): - """Returns video size in bytes.""" - loaded_bytes = 0 - loaded_bytes = self.ExecuteJavascript(""" - bytesLoaded = document.getElementById("bytesLoaded"); - window.domAutomationController.send(bytesLoaded.innerHTML); - """) - return int(loaded_bytes) - - def GetCurrentVideoTime(self): - """Returns the current time of the video in seconds.""" - current_time = 0 - current_time = self.ExecuteJavascript(""" - videoCurrentTime = document.getElementById("videoCurrentTime"); - window.domAutomationController.send(videoCurrentTime.innerHTML); - """) - return int(current_time) - - def PlayVideo(self): - """Plays the loaded video.""" - self._pyauto.ExecuteJavascript(""" - ytplayer.playVideo(); - window.domAutomationController.send(''); - """) - - def StopVideo(self): - """Stops the video and cancels loading.""" - self._pyauto.ExecuteJavascript(""" - ytplayer.stopVideo(); - window.domAutomationController.send(''); - """) - - def PauseVideo(self): - """Pause the video.""" - self.ExecuteJavascript(""" - ytplayer.pauseVideo(); - window.domAutomationController.send(''); - """) - - def PlayVideoAndAssert(self, youtube_video='zuzaxlddWbk', - ignore_assert=False): - """Start video and assert the playing state. - - By default test uses http://www.youtube.com/watch?v=zuzaxlddWbki. - - Args: - youtube_video: The string ID of the youtube video to play. - ignore_assert: flag to ignore the assertion and continue the test. - """ - self._pyauto.assertTrue(self._pyauto.IsFlashPluginEnabled(), - msg='From here Flash plugin is disabled or not available.') - url = self._pyauto.GetHttpURLForDataPath( - 'media', 'youtube.html?video=' + youtube_video) - self._pyauto.NavigateToURL(url) - self.WaitUntilPlayerReady() - i = 0 - # The YouTube player will get in a state where it does not return the - # number of loaded bytes. When this happens we need to reload the page - # before starting the test. - while self.GetVideoLoadedBytes() == 1 and i < 30: - self._pyauto.NavigateToURL(url) - self.WaitUntilPlayerReady() - i = i + 1 - self.PlayVideo() - if ignore_assert: - return self.is_playing - self.AssertPlayerState(state=self.is_playing, - msg='Player did not enter the playing state.') - - def VideoBytesLoadingAndAssert(self): - """Assert the video loading.""" - total_bytes = self.GetVideoTotalBytes() - prev_loaded_bytes = 0 - loaded_bytes = 0 - count = 0 - while loaded_bytes < total_bytes: - # We want to test bytes loading only twice - count = count + 1 - if count == 2: - break - loaded_bytes = self.GetVideoLoadedBytes() - self.assertTrue(prev_loaded_bytes <= loaded_bytes) - prev_loaded_bytes = loaded_bytes - # Give some time to load a video - time.sleep(1) - - def PlayFAVideo(self): - """Play and assert FA video playing. - - We are using multiple test videos in case any FA video playback fails - becuase other tests are palying the same video and the test gets the - simultaneous playback error. - """ - fa_videos = ('APRpcscmbY0', 'yQqvrED-np0', 'KJuFw6hQdNY', - 'BeFQbgxr_9g', 'L6JwlOudqA4') - credentials = self.GetPrivateInfo()['test_fa_account'] - test_utils.GoogleAccountsLogin(self, - credentials['username'], credentials['password']) - for video in fa_videos: - result = self.PlayVideoAndAssert(video, ignore_assert=True) - if result is self.is_playing: - return - self.assertTrue(False, msg='Player did not enter the playing state.') - - -class YoutubeTest(pyauto.PyUITest, YoutubeTestHelper): - """Test case for Youtube videos.""" - - def __init__(self, methodName='runTest', **kwargs): - pyauto.PyUITest.__init__(self, methodName, **kwargs) - YoutubeTestHelper.__init__(self, self) - - def testPlayerStatus(self): - """Test that YouTube loads a player and changes player states. - - Test verifies various player states like unstarted, playing, paused - and ended. - """ - # Navigating to Youtube video. This video is 122 seconds long. - # During tests, we are not goinig to play this video full. - self.PlayVideoAndAssert() - self.PauseVideo() - self.AssertPlayerState(state=self.is_paused, - msg='Player did not enter the paused state.') - # Seek to the end of video - self.ExecuteJavascript(""" - val = ytplayer.getDuration(); - ytplayer.seekTo(val, true); - window.domAutomationController.send(''); - """) - self.PlayVideo() - # We've seeked to almost the end of the video but not quite. - # Wait until the end. - self.AssertPlayerState(state=self.has_ended, - msg='Player did not reach the stopped state.') - - def testPlayerResolution(self): - """Test various video resolutions.""" - self.PlayVideoAndAssert() - resolutions = self.ExecuteJavascript(""" - res = ytplayer.getAvailableQualityLevels(); - window.domAutomationController.send(res.toString()); - """) - resolutions = resolutions.split(',') - for res in resolutions: - self.ExecuteJavascript(""" - ytplayer.setPlaybackQuality('%s'); - window.domAutomationController.send(''); - """ % res) - curr_res = self.ExecuteJavascript(""" - res = ytplayer.getPlaybackQuality(); - window.domAutomationController.send(res + ''); - """) - self.assertEqual(res, curr_res, msg='Resolution is not set to %s.' % res) - - def testPlayerBytes(self): - """Test that player downloads video bytes.""" - self.PlayVideoAndAssert() - self.VideoBytesLoadingAndAssert() - - def testFAVideo(self): - """Test that FlashAccess/DRM video plays.""" - self.PlayFAVideo() - self.StopVideo() - - def testFAVideoBytes(self): - """Test FlashAccess/DRM video bytes loading.""" - self.PlayFAVideo() - self.VideoBytesLoadingAndAssert() - self.StopVideo() - - -if __name__ == '__main__': - pyauto_functional.Main() diff --git a/chrome/test/pyautolib/OWNERS b/chrome/test/pyautolib/OWNERS deleted file mode 100644 index afe7a77..0000000 --- a/chrome/test/pyautolib/OWNERS +++ /dev/null @@ -1 +0,0 @@ -craigdh@chromium.org diff --git a/chrome/test/pyautolib/PYAUTO_TESTS b/chrome/test/pyautolib/PYAUTO_TESTS deleted file mode 100644 index 0b9cb3f..0000000 --- a/chrome/test/pyautolib/PYAUTO_TESTS +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2010 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'FULL': { - 'all': [ - ], - - 'win': [ - ], - - 'mac': [ - ], - - 'linux': [ - ], - - 'chromeos': [ - ], - }, -} diff --git a/chrome/test/pyautolib/argc_argv.i b/chrome/test/pyautolib/argc_argv.i deleted file mode 100644 index b705006..0000000 --- a/chrome/test/pyautolib/argc_argv.i +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// Make functions using (int argc, char** argv) usable as (sys.argv) from python - -%typemap(in) (int argc, char **argv) { - int i; - if (!PyList_Check($input)) { - PyErr_SetString(PyExc_ValueError, "Expecting a list"); - return NULL; - } - $1 = PyList_Size($input); - $2 = (char **) malloc(($1+1)*sizeof(char *)); - for (i = 0; i < $1; i++) { - PyObject *s = PyList_GetItem($input,i); - if (!PyString_Check(s)) { - free($2); - PyErr_SetString(PyExc_ValueError, "List items must be strings"); - return NULL; - } - $2[i] = PyString_AsString(s); - } - $2[i] = 0; -} - -%typemap(freearg) (int argc, char **argv) { - if ($2) free($2); -} - diff --git a/chrome/test/pyautolib/asan_stub.c b/chrome/test/pyautolib/asan_stub.c deleted file mode 100644 index ae8c215..0000000 --- a/chrome/test/pyautolib/asan_stub.c +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2012 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. -// -// Fake Address Sanitizer run-time support. -// Enough for programs to link and run, but will not find any errors. -// Also, linking this to shared libraries voids the warranty. -// -// We need this fake thunks if we build Chrome with ASAN support because -// pyautolib DSO is instrumented with ASAN and is loaded to regular python -// process that has no ASAN runtime. -// -// We have three options here: -// 1) use our custom build of Python that have ASAN runtime linked in, -// 2) do not instrument pyautolib with ASAN support, -// 3) use this fake asan thunks linked to pyautolib so that instrumented code -// does not complain. -// -// Note that we should not use real ASAN runtime linked with DSO because it will -// not have information about memory allocations made prior to DSO load. -// -// Option (2) is not easy because pyautolib uses code from Chrome -// (see chrome_tests.gypi, dependencies for target_name: pyautolib) that -// has been instrumented with ASAN. So even if we disable -sanitize=address -// for pyautolib own sources, ASAN instrumentation will creep in from there. -// To avoid ASAN instrumentation, we might force Chrome build to compile all our -// dependencies one more time without -fsanitize=address. -// -// Note also that using these empty stubs prevents ASAN from catching bugs in -// Python-pyautolib process. But we do not need it, we are interested in Chrome -// bugs. - - -#include <stdio.h> -#include <stdlib.h> -#include <sys/mman.h> - -void __asan_init() { - static int inited = 0; - if (inited) return; - inited = 1; -#if __WORDSIZE == 64 - unsigned long start = 0x100000000000; - unsigned long size = 0x100000000000; -#else - unsigned long start = 0x20000000; - unsigned long size = 0x20000000; -#endif - void *res = mmap((void*)start, size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, - 0, 0); - if (res == (void*)start) { - fprintf(stderr, "Fake AddressSanitizer run-time initialized ok at %p\n", - res); - } else { - fprintf(stderr, "Fake AddressSanitizer run-time failed to initialize.\n" - "You have been warned. Aborting."); - abort(); - } -} - -// Update the name when asan api updates. -void __asan_init_v1() { - __asan_init(); -} - -void __asan_init_v3() { - static int inited = 0; - if (inited) return; - inited = 1; -#if __WORDSIZE == 64 - unsigned long start = 0x00007fff8000; - unsigned long size = 0x100000000000; -#else - unsigned long start = 0x20000000; - unsigned long size = 0x20000000; -#endif - void *res = mmap((void*)start, size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, - 0, 0); - if (res == (void*)start) { - fprintf(stderr, "Fake AddressSanitizer run-time initialized ok at %p\n", - res); - } else { - fprintf(stderr, "Fake AddressSanitizer run-time failed to initialize.\n" - "You have been warned. Aborting."); - abort(); - } -} - -void __asan_handle_no_return() { } -void __asan_register_globals() { } -void __asan_report_load1() { } -void __asan_report_load16() { } -void __asan_report_load2() { } -void __asan_report_load4() { } -void __asan_report_load8() { } -void __asan_report_load_n() { } -void __asan_report_store1() { } -void __asan_report_store16() { } -void __asan_report_store2() { } -void __asan_report_store4() { } -void __asan_report_store8() { } -void __asan_report_store_n() { } -void __asan_set_error_report_callback() { } -void __asan_unregister_globals() { } -void __sanitizer_sandbox_on_notify() { } -void __asan_before_dynamic_init(const char *module_name) { } -void __asan_after_dynamic_init() { } -int __asan_option_detect_stack_use_after_return; -typedef unsigned long long uptr; -void __asan_poison_memory_region(void const volatile *addr, uptr size) { } -void __asan_unpoison_memory_region(void const volatile *addr, uptr size) { } -uptr __asan_stack_malloc_0(uptr size, uptr real_stack) {} -uptr __asan_stack_malloc_1(uptr size, uptr real_stack) {} -uptr __asan_stack_malloc_2(uptr size, uptr real_stack) {} -uptr __asan_stack_malloc_3(uptr size, uptr real_stack) {} -uptr __asan_stack_malloc_4(uptr size, uptr real_stack) {} -uptr __asan_stack_malloc_5(uptr size, uptr real_stack) {} -uptr __asan_stack_malloc_6(uptr size, uptr real_stack) {} -uptr __asan_stack_malloc_7(uptr size, uptr real_stack) {} -uptr __asan_stack_malloc_8(uptr size, uptr real_stack) {} -uptr __asan_stack_malloc_9(uptr size, uptr real_stack) {} -uptr __asan_stack_malloc_10(uptr size, uptr real_stack) {} -void __asan_stack_free_0(uptr ptr, uptr size, uptr real_stack) {} -void __asan_stack_free_1(uptr ptr, uptr size, uptr real_stack) {} -void __asan_stack_free_2(uptr ptr, uptr size, uptr real_stack) {} -void __asan_stack_free_3(uptr ptr, uptr size, uptr real_stack) {} -void __asan_stack_free_4(uptr ptr, uptr size, uptr real_stack) {} -void __asan_stack_free_5(uptr ptr, uptr size, uptr real_stack) {} -void __asan_stack_free_6(uptr ptr, uptr size, uptr real_stack) {} -void __asan_stack_free_7(uptr ptr, uptr size, uptr real_stack) {} -void __asan_stack_free_8(uptr ptr, uptr size, uptr real_stack) {} -void __asan_stack_free_9(uptr ptr, uptr size, uptr real_stack) {} -void __asan_stack_free_10(uptr ptr, uptr size, uptr real_stack) {} diff --git a/chrome/test/pyautolib/bookmark_model.py b/chrome/test/pyautolib/bookmark_model.py deleted file mode 100644 index b70fa4a..0000000 --- a/chrome/test/pyautolib/bookmark_model.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright (c) 2011 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. - -"""BookmarkModel: python representation of the bookmark model. - -Obtain one of these from PyUITestSuite::GetBookmarkModel() call. -""" - -import os -import simplejson as json -import sys - -class BookmarkModel(object): - - def __init__(self, json_string): - """Initialize a BookmarkModel from a string of json. - - The JSON representation is the same as used by the bookmark model - to save to disk. - - Args: - json_string: a string of JSON. - """ - self.bookdict = json.loads(json_string) - - def BookmarkBar(self): - """Return the bookmark bar node as a dict.""" - return self.bookdict['roots']['bookmark_bar'] - - def Other(self): - """Return the 'other' node (e.g. parent of "Other Bookmarks")""" - return self.bookdict['roots']['other'] - - def NodeCount(self, node=None): - """Return a count of bookmark nodes, including folders. - - The root node itself is included in the count. - - Args: - node: the root to start with. If not specified, count all.""" - if node == None: - return reduce(lambda x, y: x + y, - [self.NodeCount(x) - for x in self.bookdict['roots'].values()]) - total = 1 - children = node.get('children', None) - if children: - total = total + reduce(lambda x,y: x + y, - [self.NodeCount(x) for x in children]) - return total - - def FindByID(self, id, nodes=None): - """Find the bookmark by id. Return the dict or None. - - Args: - id: the id to look for. - nodes: an iterable of nodes to start with. If not specified, search all. - 'Not specified' means None, not []. - """ - # Careful; we may get an empty list which is different than not - # having specified a list. - if nodes == None: - nodes = self.bookdict['roots'].values() - # Check each item. If it matches, return. If not, check each of - # their kids. - for node in nodes: - if node['id'] == id: - return node - for child in node.get('children', []): - found_node = self.FindByID(id, [child]) - if found_node: - return found_node - # Not found at all. - return None - - def FindByTitle(self, title, nodes=None): - """Return a tuple of all nodes which have |title| in their title. - - Args: - title: the title to look for. - node: an iterable of nodes to start with. If not specified, search all. - 'Not specified' means None, not []. - """ - # Careful; we may get an empty list which is different than not - # having specified a list. - if nodes == None: - nodes = self.bookdict['roots'].values() - # Check each item. If it matches, return. If not, check each of - # their kids. - results = [] - for node in nodes: - node_title = node.get('title', None) or node.get('name', None) - if title == node_title: - results.append(node) - # Note we check everything; unlike the FindByID, we do not stop early. - for child in node.get('children', []): - results += self.FindByTitle(title, [child]) - return results diff --git a/chrome/test/pyautolib/chrome_driver_factory.py b/chrome/test/pyautolib/chrome_driver_factory.py deleted file mode 100644 index a5ef54bb..0000000 --- a/chrome/test/pyautolib/chrome_driver_factory.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) 2012 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. - -"""Factory that creates ChromeDriver instances for pyauto.""" - -import os -import random -import tempfile - -import pyauto_paths -from selenium import webdriver -from selenium.webdriver.chrome import service - - -class ChromeDriverFactory(object): - """"Factory that creates ChromeDriver instances for pyauto. - - Starts a single ChromeDriver server when necessary. Users should call 'Stop' - when no longer using the factory. - """ - - def __init__(self, port=0): - """Initialize ChromeDriverFactory. - - Args: - port: The port for WebDriver to use; by default the service will select a - free port. - """ - self._chromedriver_port = port - self._chromedriver_server = None - - def NewChromeDriver(self, pyauto): - """Creates a new remote WebDriver instance. - - This instance will connect to a new automation provider of an already - running Chrome. - - Args: - pyauto: pyauto.PyUITest instance - - Returns: - selenium.webdriver.remote.webdriver.WebDriver instance. - """ - if pyauto.IsChromeOS(): - os.putenv('DISPLAY', ':0.0') - os.putenv('XAUTHORITY', '/home/chronos/.Xauthority') - self._StartServerIfNecessary() - channel_id = 'testing' + hex(random.getrandbits(20 * 4))[2:-1] - if not pyauto.IsWin(): - channel_id = os.path.join(tempfile.gettempdir(), channel_id) - pyauto.CreateNewAutomationProvider(channel_id) - return webdriver.Remote(self._chromedriver_server.service_url, - {'chrome.channel': channel_id, - 'chrome.noWebsiteTestingDefaults': True}) - - def _StartServerIfNecessary(self): - """Starts the ChromeDriver server, if not already started.""" - if self._chromedriver_server is None: - exe = pyauto_paths.GetChromeDriverExe() - assert exe, 'Cannot find chromedriver exe. Did you build it?' - self._chromedriver_server = service.Service(exe, self._chromedriver_port) - self._chromedriver_server.start() - - def Stop(self): - """Stops the ChromeDriver server, if running.""" - if self._chromedriver_server is not None: - self._chromedriver_server.stop() - self._chromedriver_server = None - - def GetPort(self): - """Gets the port ChromeDriver is set to use. - - Returns: - The port all ChromeDriver instances returned from NewChromeDriver() will - be listening on. A return value of 0 indicates the ChromeDriver service - will select a free port. - """ - return self._chromedriver_port - - def __del__(self): - self.Stop() diff --git a/chrome/test/pyautolib/chromeos/__init__.py b/chrome/test/pyautolib/chromeos/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/chrome/test/pyautolib/chromeos/__init__.py +++ /dev/null diff --git a/chrome/test/pyautolib/chromeos/chromeos_utils.py b/chrome/test/pyautolib/chromeos/chromeos_utils.py deleted file mode 100755 index eb26a6f..0000000 --- a/chrome/test/pyautolib/chromeos/chromeos_utils.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import os -import sys - - -def _SetupPaths(): - sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir)) - sys.path.append('/usr/local') # to import autotest libs - -_SetupPaths() - -from autotest.cros import constants -import pyauto - - -class ChromeosUtils(pyauto.PyUITest): - """Utils for ChromeOS.""" - - def LoginToDefaultAccount(self): - """Login to ChromeOS using default testing account. - - Usage: - python chromeos_utils.py \ - chromeos_utils.ChromeosUtils.LoginToDefaultAccount - """ - # Should auto-login. Nothing to do here. - # TODO(nirnimesh): Remove this when auto-login feature - # reaches chromeos such that this helper is not necessary. - pass - - -if __name__ == '__main__': - pyauto.Main() diff --git a/chrome/test/pyautolib/chromeos/enable_testing.py b/chrome/test/pyautolib/chromeos/enable_testing.py deleted file mode 100755 index 373c764..0000000 --- a/chrome/test/pyautolib/chromeos/enable_testing.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 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. - -"""Enable chrome testing interface on ChromeOS. - -Enables chrome automation over a named automation channel on ChromeOS. -Also, allows passing extra flags to chrome (--extra-chrome-flags). -The path to named testing interface automation socket is printed out. - -Needs to be run with superuser privileges. - -Usage: - sudo python enable_testing.py --extra-chrome-flags="--homepage=about:blank" -""" - -import dbus -import optparse -import os -import sys - - -class EnableChromeTestingOnChromeOS(object): - """Helper to enable chrome testing interface on ChromeOS. - - Also, can add additional flags to chrome to be used for testing. - """ - - SESSION_MANAGER_INTERFACE = 'org.chromium.SessionManagerInterface' - SESSION_MANAGER_PATH = '/org/chromium/SessionManager' - SESSION_MANAGER_SERVICE = 'org.chromium.SessionManager' - - def _ParseArgs(self): - parser = optparse.OptionParser() - parser.add_option( - '', '--extra-chrome-flags', action='append', default=[], - help='Pass extra flags to chrome.') - self._options, self._args = parser.parse_args() - - def Run(self): - self._ParseArgs() - assert os.geteuid() == 0, 'Needs superuser privileges.' - system_bus = dbus.SystemBus() - manager = dbus.Interface(system_bus.get_object(self.SESSION_MANAGER_SERVICE, - self.SESSION_MANAGER_PATH), - self.SESSION_MANAGER_INTERFACE) - print manager.EnableChromeTesting(True, self._options.extra_chrome_flags) - return 0 - - -if __name__ == '__main__': - sys.exit(EnableChromeTestingOnChromeOS().Run()) diff --git a/chrome/test/pyautolib/chromeos/power_strip.py b/chrome/test/pyautolib/chromeos/power_strip.py deleted file mode 100644 index b614b67..0000000 --- a/chrome/test/pyautolib/chromeos/power_strip.py +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import logging -import re -import telnetlib - - -class PowerStrip(object): - """Controls Server Technology CW-16V1-C20M switched CDUs. - (Cabinet Power Distribution Unit) - - This class is used to control the CW-16V1-C20M unit which - is a 16 port remote power strip. The strip supports AC devices - using 100-120V 50/60Hz input voltages. The commands in this - class are supported by switches that use Sentry Switched CDU Version 6.0g. - - Opens a new connection for every command. - """ - - TIMEOUT = 10 - - def __init__(self, host, user='admn', password='admn'): - self._host = host - self._user = user - self._password = password - - def PowerOff(self, outlet): - """Powers off the device that is plugged into the specified outlet. - - Args: - outlet: The outlet ID defined on the switch (eg. .a14). - """ - self._DoCommand('off', outlet) - - def PowerOn(self, outlet): - """Powers on the device that is plugged into the specified outlet. - - Args: - outlet: The outlet ID defined on the switch (eg. .a14). - """ - self._DoCommand('on', outlet) - - def _DoCommand(self, command, outlet): - """Performs power strip commands on the specified outlet. - - Sample telnet interaction: - Escape character is '^]'. - - Sentry Switched CDU Version 6.0g - - Username: admn - Password: < password hidden from view > - - Location: - - Switched CDU: on .a1 - - Outlet Outlet Outlet Control - ID Name Status State - - .A1 TowerA_Outlet1 On On - - Command successful - - Switched CDU: < cdu cmd > - - Args: - command: A valid CW-16V1-C20M command that follows the format - <command> <outlet>. - outlet: The outlet ID defined on the switch (eg. .a14). - """ - tn = telnetlib.Telnet() - # To avoid 'Connection Reset by Peer: 104' exceptions when rapid calls - # are made to the telnet server on the power strip, we retry executing - # a command. - retry = range(5) - for attempt in retry: - try: - tn.open(self._host, timeout=PowerStrip.TIMEOUT) - resp = tn.read_until('Username:', timeout=PowerStrip.TIMEOUT) - assert 'Username' in resp, 'Username not found in response. (%s)' % resp - tn.write(self._user + '\n') - - resp = tn.read_until('Password:', timeout=PowerStrip.TIMEOUT) - assert 'Password' in resp, 'Password not found in response. (%s)' % resp - tn.write(self._password + '\n') - - resp = tn.read_until('Switched CDU:', timeout=PowerStrip.TIMEOUT) - assert 'Switched CDU' in resp, 'Standard prompt not found in ' \ - 'response. (%s)' % resp - tn.write('%s %s\n' % (command, outlet)) - - # Obtain the output of command and make sure it matches with the action - # we performed. - # Sample valid output: - # .A1 TowerA_Outlet1 On On - resp = tn.read_until('Switched CDU:', timeout=PowerStrip.TIMEOUT) - if not re.search('%s\s+\S+\s+%s\s+%s' % (outlet, command, command), - resp, re.I): - raise Exception('Command \'%s\' execution failed. (%s)' % - (command, resp)) - - # Exiting the telnet session cleanly significantly reduces the chance of - # connection error on initiating the following telnet session. - tn.write('exit\n') - tn.read_all() - - # If we've gotten this far, there is no need to retry. - break - except Exception as e: - logging.debug('Power strip retry on cmd "%s". Reason: %s' - % (command, str(e))) - if attempt == retry[-1]: - raise Exception('Sentry Command "%s" failed. ' - 'Reason: %s' % (command, str(e))) - finally: - tn.close() diff --git a/chrome/test/pyautolib/chromeos/suid_actions.py b/chrome/test/pyautolib/chromeos/suid_actions.py deleted file mode 100755 index b0ba529..0000000 --- a/chrome/test/pyautolib/chromeos/suid_actions.py +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Helper script to perform actions as a super-user on ChromeOS. - -Needs to be run with superuser privileges, typically using the -suid_python binary. - -Usage: - sudo python suid_actions.py --action=CleanFlimflamDirs -""" - -import optparse -import os -import shutil -import subprocess -import sys -import time - -sys.path.append('/usr/local') # to import autotest libs. -from autotest.cros import constants -from autotest.cros import cryptohome - -TEMP_BACKCHANNEL_FILE = '/tmp/pyauto_network_backchannel_file' - - -class SuidAction(object): - """Helper to perform some super-user actions on ChromeOS.""" - - def _ParseArgs(self): - parser = optparse.OptionParser() - parser.add_option( - '-a', '--action', help='Action to perform.') - self._options = parser.parse_args()[0] - if not self._options.action: - raise RuntimeError('No action specified.') - - def Run(self): - self._ParseArgs() - assert os.geteuid() == 0, 'Needs superuser privileges.' - handler = getattr(self, self._options.action) - assert handler and callable(handler), \ - 'No handler for %s' % self._options.action - handler() - return 0 - - ## Actions ## - def CleanFlimflamDirs(self): - """Clean the contents of all connection manager (shill/flimflam) profiles. - """ - flimflam_dirs = ['/home/chronos/user/flimflam', - '/home/chronos/user/shill', - '/var/cache/flimflam', - '/var/cache/shill'] - - # The stop/start flimflam command should stop/start shill respectivly if - # enabled. - os.system('stop flimflam') - try: - for flimflam_dir in flimflam_dirs: - if not os.path.exists(flimflam_dir): - continue - for item in os.listdir(flimflam_dir): - path = os.path.join(flimflam_dir, item) - if os.path.isdir(path): - shutil.rmtree(path) - else: - os.remove(path) - finally: - os.system('start flimflam') - # TODO(stanleyw): crosbug.com/29421 This method should wait until - # flimflam/shill is fully initialized and accessible via DBus again. - # Otherwise, there is a race conditions and subsequent accesses to - # flimflam/shill may fail. Until this is fixed, waiting for the - # resolv.conf file to be created is better than nothing. - begin = time.time() - while not os.path.exists(constants.RESOLV_CONF_FILE): - if time.time() - begin > 10: - raise RuntimeError('Timeout while waiting for flimflam/shill start.') - time.sleep(.25) - - def RemoveAllCryptohomeVaults(self): - """Remove any existing cryptohome vaults.""" - cryptohome.remove_all_vaults() - - def _GetEthInterfaces(self): - """Returns a list of the eth* interfaces detected by the device.""" - # Assumes ethernet interfaces all have "eth" in the name. - import pyudev - return sorted([iface.sys_name for iface in - pyudev.Context().list_devices(subsystem='net') - if 'eth' in iface.sys_name]) - - def _Renameif(self, old_iface, new_iface, mac_address): - """Renames the interface with mac_address from old_iface to new_iface. - - Args: - old_iface: The name of the interface you want to change. - new_iface: The name of the interface you want to change to. - mac_address: The mac address of the interface being changed. - """ - subprocess.call(['stop', 'flimflam']) - subprocess.call(['ifconfig', old_iface, 'down']) - subprocess.call(['nameif', new_iface, mac_address]) - subprocess.call(['ifconfig', new_iface, 'up']) - subprocess.call(['start', 'flimflam']) - - # Check and make sure interfaces have been renamed - eth_ifaces = self._GetEthInterfaces() - if new_iface not in eth_ifaces: - raise RuntimeError('Interface %s was not renamed to %s' % - (old_iface, new_iface)) - elif old_iface in eth_ifaces: - raise RuntimeError('Old iface %s is still present' % old_iface) - - def SetupBackchannel(self): - """Renames the connected ethernet interface to eth_test for offline mode - testing. Does nothing if no connected interface is found. - """ - # Return the interface with ethernet connected or returns if none found. - for iface in self._GetEthInterfaces(): - with open('/sys/class/net/%s/operstate' % iface, 'r') as fp: - if 'up' in fp.read(): - eth_iface = iface - break - else: - return - - # Write backup file to be used by TeardownBackchannel to restore the - # interface names. - with open(TEMP_BACKCHANNEL_FILE, 'w') as fpw: - with open('/sys/class/net/%s/address' % eth_iface) as fp: - mac_address = fp.read().strip() - fpw.write('%s, %s' % (eth_iface, mac_address)) - - self._Renameif(eth_iface, 'eth_test', mac_address) - - def TeardownBackchannel(self): - """Restores the eth interface names if SetupBackchannel was called.""" - if not os.path.isfile(TEMP_BACKCHANNEL_FILE): - return - - with open(TEMP_BACKCHANNEL_FILE, 'r') as fp: - eth_iface, mac_address = fp.read().split(',') - - self._Renameif('eth_test', eth_iface, mac_address) - os.remove(TEMP_BACKCHANNEL_FILE) - - -if __name__ == '__main__': - sys.exit(SuidAction().Run()) diff --git a/chrome/test/pyautolib/chromeos_network.py b/chrome/test/pyautolib/chromeos_network.py deleted file mode 100644 index 0b42fa0..0000000 --- a/chrome/test/pyautolib/chromeos_network.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import copy -import dbus -import logging -import os -import time - -from chromeos.power_strip import PowerStrip -import pyauto -import pyauto_errors - -class PyNetworkUITest(pyauto.PyUITest): - """A subclass of PyUITest for Chrome OS network tests. - - A subclass of PyUITest that automatically sets the flimflam - priorities to put wifi connections first before starting tests. - This is for convenience when writing wifi tests. - """ - _ROUTER_CONFIG_FILE = os.path.join(pyauto.PyUITest.DataDir(), - 'pyauto_private', 'chromeos', 'network', - 'wifi_testbed_config') - _FLIMFLAM_PATH = 'org.chromium.flimflam' - - def setUp(self): - self.CleanupFlimflamDirsOnChromeOS() - # Move ethernet to the end of the flimflam priority list, - # effectively hiding any ssh connections that the - # test harness might be using and putting wifi ahead. - self._PushServiceOrder('wifi,ethernet') - self._ParseDefaultRoutingTable() - pyauto.PyUITest.setUp(self) - - def tearDown(self): - pyauto.PyUITest.tearDown(self) - self._PopServiceOrder() - # Remove the route entry for the power strip. - if hasattr(self, 'ps_route_entry'): - os.system('route del -net %(ipaddress)s gateway %(gateway)s netmask ' - '%(netmask)s dev %(iface)s' % self.ps_route_entry) - - def _GetFlimflamManager(self): - _proxy = dbus.SystemBus().get_object(self._FLIMFLAM_PATH, '/') - return dbus.Interface(_proxy, self._FLIMFLAM_PATH + '.Manager') - - def _ParseDefaultRoutingTable(self): - """Creates and stores a dictionary of the default routing paths.""" - route_table_headers = ['destination', 'gateway', 'genmask', 'flags', - 'metric', 'ref', 'use', 'iface'] - routes = os.popen('route -n | egrep "^0.0.0.0"').read() - routes = [interface.split() for interface in routes.split('\n')][:-1] - self.default_routes = {} - for iface in routes: - self.default_routes[iface[-1]] = dict(zip(route_table_headers, iface)) - - def _SetServiceOrder(self, service_order): - self._GetFlimflamManager().SetServiceOrder(service_order) - # Flimflam throws a dbus exception if device is already disabled. This - # is not an error. - try: - self._GetFlimflamManager().DisableTechnology('wifi') - except dbus.DBusException as e: - if 'org.chromium.flimflam.Error.AlreadyDisabled' not in str(e): - raise e - self._GetFlimflamManager().EnableTechnology('wifi') - - def _PushServiceOrder(self, service_order): - self._old_service_order = self._GetFlimflamManager().GetServiceOrder() - self._SetServiceOrder(service_order) - service_order = service_order.split(',') - - # Verify services that are present in both the service_order - # we set and the one retrieved from device are in the correct order. - set_service_order = self._GetFlimflamManager().GetServiceOrder().split(',') - common_service = set(service_order) & set(set_service_order) - - service_order = [s for s in service_order if s in common_service] - set_service_order = [s for s in set_service_order if s in common_service] - - assert service_order == set_service_order, \ - 'Flimflam service order not set properly. %s != %s' % \ - (service_order, set_service_order) - - def _PopServiceOrder(self): - self._SetServiceOrder(self._old_service_order) - - # Verify services that are present in both the service_order - # we set and the one retrieved from device are in the correct order. - old_service_order = self._old_service_order.split(',') - set_service_order = self._GetFlimflamManager().GetServiceOrder().split(',') - common_service = set(old_service_order) & set(set_service_order) - - old_service_order = [s for s in old_service_order if s in common_service] - set_service_order = [s for s in set_service_order if s in common_service] - - assert old_service_order == set_service_order, \ - 'Flimflam service order not set properly. %s != %s' % \ - (old_service_order, set_service_order) diff --git a/chrome/test/pyautolib/chromoting_cert.p12 b/chrome/test/pyautolib/chromoting_cert.p12 Binary files differdeleted file mode 100644 index 4751cce..0000000 --- a/chrome/test/pyautolib/chromoting_cert.p12 +++ /dev/null diff --git a/chrome/test/pyautolib/chromoting_helper.py b/chrome/test/pyautolib/chromoting_helper.py deleted file mode 100644 index 1c14bb5..0000000 --- a/chrome/test/pyautolib/chromoting_helper.py +++ /dev/null @@ -1,200 +0,0 @@ -# Copyright (c) 2012 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. - -"""Chromoting helper to install/uninstall host and replace pref pane.""" - -import abc -import os -import shutil -import sys -import subprocess - - -class ChromotingHelper(object): - """Chromoting helper base class.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def InstallHost(self, bin_dir): - """Installs the chromoting host""" - return - - @abc.abstractmethod - def UninstallHost(self, bin_dir): - """Uninstalls the chromoting host""" - return - - -class ChromotingHelperMac(ChromotingHelper): - """Chromoting Helper class for Mac. - - Installs/uninstalls host and replace the pref pane for testing purpose. - """ - - def InstallHost(self, bin_dir): - """Installs host on Mac.""" - assert os.geteuid() == 0, 'Need superuser privileges' - - # Run most of the steps here with login user - login_uid = os.getuid() - os.seteuid(login_uid) - - # Change the working dir to the dir that has the host zip file - current_dir = os.getcwd() - pyautolib_dir = os.path.dirname(os.path.abspath(__file__)) - os.chdir(bin_dir) - host_dir = 'remoting-me2me-host-mac' - output_dir = os.path.join(host_dir, 'output') - - # Remove remoting-me2me-host-mac dir just in case - shutil.rmtree(host_dir, True) - - # Unzip the host archive and prepare the files/dirs - subprocess.call('unzip remoting-me2me-host-mac.zip', shell=True) - subprocess.call('mkdir ' + output_dir, shell=True) - - # Prepare security identity for code signing purpose - os.seteuid(0) - key_chain = '/Library/Keychains/ChromotingTest' - password = '1111' - key = os.path.join(pyautolib_dir, 'chromoting_key.p12') - cert = os.path.join(pyautolib_dir, 'chromoting_cert.p12') - subprocess.call(['security', 'delete-keychain', key_chain]) - subprocess.call(['security', 'create-keychain', '-p', - password, key_chain]) - subprocess.call(['security', 'import', key, - '-k', key_chain, '-P', password, '-A']) - subprocess.call(['security', 'import', cert, - '-k', key_chain, '-P', password]) - os.seteuid(login_uid) - - # Sign the host - do_signing = os.path.join(host_dir, 'do_signing.sh') - subprocess.call(do_signing + ' ' + output_dir + ' ' + host_dir + ' ' + - key_chain + ' "Chromoting Test"', shell=True) - - # Remove security identify - os.seteuid(0) - subprocess.call(['security', 'delete-keychain', key_chain]) - os.seteuid(login_uid) - - # Figure out the dmg name - version = "" - for output_file in os.listdir(output_dir): - if output_file.endswith('.dmg'): - version = os.path.basename(output_file)[len('ChromotingHost-'):-4] - - # Mount before installation - dmg = os.path.join(output_dir, 'ChromotingHost-' + version + '.dmg') - subprocess.call('hdiutil' + ' mount ' + dmg, shell=True) - - # Install host - os.seteuid(0) - mpkg = os.path.join('/Volumes', 'Chromoting Host ' + version, - 'Chromoting Host.pkg') - subprocess.call(['/usr/sbin/installer', '-pkg', - mpkg, '-target', '/']) - os.seteuid(login_uid) - - # Unmount after installation - mounted = os.path.join('/Volumes', 'Chromoting Host ' + version) - subprocess.call('hdiutil detach "' + mounted + '"', shell=True) - - # Clean up remoting-me2me-host-mac dir - shutil.rmtree(host_dir, True) - - # Resume the original working dir - os.chdir(current_dir) - - def UninstallHost(self, bin_dir): - """Uninstalls host on Mac.""" - assert os.geteuid() == 0, 'Need superuser privileges' - uninstall_app = os.path.join('/', 'Applications', - 'Chromoting Host Uninstaller.app', - 'Contents', 'MacOS', - 'remoting_host_uninstaller') - subprocess.call([uninstall_app, '--no-ui']) - - def ReplacePrefPaneMac(self, operation): - """Constructs mock pref pane to replace the actual pref pane on Mac.""" - assert os.geteuid() == 0, 'Need superuser privileges' - - pref_pane_dir = os.path.join('/Library', 'PreferencePanes') - - mock_pref_pane = os.path.join(pref_pane_dir, 'mock_pref_pane') - pref_pane = os.path.join(pref_pane_dir, - 'ChromeRemoteDesktop.prefPane') - mock_pref_pane_python = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - 'mock_pref_pane.py') - - # When the symlink from real pref pane to mock pref pane exists, - # mock pref pane will be modified to be a dir when the host is installed. - # After the host is installed and mock pref pane is modified to be a file, - # it will be a file until next host installation. - if os.path.isdir(mock_pref_pane): - shutil.rmtree(mock_pref_pane, True) - elif os.path.isfile(mock_pref_pane): - os.remove(mock_pref_pane) - - mock_pref_pane_file = open(mock_pref_pane, 'w') - mock_pref_pane_file.write('#!/bin/bash\n') - mock_pref_pane_file.write('\n') - mock_pref_pane_file.write('suid-python' + - ' ' + mock_pref_pane_python + ' ' + operation) - mock_pref_pane_file.close() - - subprocess.call(['chmod', 'a+x', mock_pref_pane]) - - # The real pref pane is a dir if the host is installed on a clean machine. - # Once the test is run on the machine, real pref pane will be replaced to - # a symlink. - if os.path.isdir(pref_pane): - shutil.rmtree(pref_pane, True) - elif os.path.isfile(pref_pane): - os.remove(pref_pane) - - subprocess.call(['ln', '-s', mock_pref_pane, pref_pane]) - - -class ChromotingHelperWindows(ChromotingHelper): - """Chromoting Helper class for Windows for installing/uninstalling host.""" - - def InstallHost(self, bin_dir): - """Installs host on Windows.""" - host_msi = os.path.join(bin_dir, 'chromoting.msi') - subprocess.Popen(['msiexec', '/i', host_msi, '/passive']).wait() - - def UninstallHost(self, bin_dir): - """Uninstalls host on Windows.""" - host_msi = os.path.join(bin_dir, 'chromoting.msi') - subprocess.Popen(['msiexec', '/x', host_msi, '/passive']).wait() - - -def Main(): - """Main function to dispatch operations.""" - assert sys.platform.startswith('win') or \ - sys.platform.startswith('darwin'), \ - 'Only support Windows and Mac' - - if sys.platform.startswith('win'): - helper = ChromotingHelperWindows() - elif sys.platform.startswith('darwin'): - helper = ChromotingHelperMac() - - if sys.argv[1] == 'install': - helper.InstallHost(sys.argv[2]) - elif sys.argv[1] == 'uninstall': - helper.UninstallHost(sys.argv[2]) - elif sys.argv[1] in ['enable', 'disable', 'changepin']: - assert sys.platform.startswith('darwin'), \ - 'Replacing pref pane is Mac specific' - helper.ReplacePrefPaneMac(sys.argv[1]) - else: - print >>sys.stderr, 'Invalid syntax' - return 1 - - -if __name__ == '__main__': - Main() diff --git a/chrome/test/pyautolib/chromoting_key.p12 b/chrome/test/pyautolib/chromoting_key.p12 Binary files differdeleted file mode 100644 index 55c75f1..0000000 --- a/chrome/test/pyautolib/chromoting_key.p12 +++ /dev/null diff --git a/chrome/test/pyautolib/chromotinglib.py b/chrome/test/pyautolib/chromotinglib.py deleted file mode 100644 index 86b9176..0000000 --- a/chrome/test/pyautolib/chromotinglib.py +++ /dev/null @@ -1,619 +0,0 @@ -# Copyright (c) 2012 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. - -"""Includes different methods to drive chromoting UI.""" - -import os -import subprocess -import sys -import time - -from pyauto_errors import JSONInterfaceError - - -class ChromotingMixIn(object): - """MixIn for PyUITest that adds Chromoting-specific methods. - - Prepend it as a base class of a test to enable Chromoting functionality. - This is a separate class from PyUITest to avoid namespace collisions. - - Example usage: - class ChromotingExample(chromoting.ChromotingMixIn, pyauto.PyUITest): - def testShare(self): - app = self.InstallApp(self.GetWebappPath()) - self.LaunchApp(app) - self.Authenticate() - self.assertTrue(self.Share()) - """ - - def _ExecuteJavascript(self, command, tab_index, windex): - """Helper that returns immediately after running a Javascript command. - """ - try: - self.ExecuteJavascript( - '%s; window.domAutomationController.send("done");' % command, - tab_index, windex) - return True - except JSONInterfaceError: - print '_ExecuteJavascript threw JSONInterfaceError' - return False - - def _WaitForJavascriptCondition(self, condition, tab_index, windex, - timeout=-1): - """Waits until the Javascript condition is true. - - This is different from a naive self.WaitUntil(lambda: self.GetDOMValue()) - because it uses Javascript to check the condition instead of Python. - - Returns: True if condition is satisfied or otherwise False. - """ - try: - return self.WaitUntil(lambda: self.GetDOMValue( - '(%s) ? "1" : ""' % condition, tab_index, windex), timeout) - except JSONInterfaceError: - print '_WaitForJavascriptCondition threw JSONInterfaceError' - return False - - def _ExecuteAndWaitForMode(self, command, mode, tab_index, windex): - """ Executes JavaScript and wait for remoting app mode equal to - the given mode. - - Returns: True if condition is satisfied or otherwise False. - """ - if not self._ExecuteJavascript(command, tab_index, windex): - return False - return self._WaitForJavascriptCondition( - 'remoting.currentMode == remoting.AppMode.%s' % mode, - tab_index, windex) - - def _ExecuteAndWaitForMajorMode(self, command, mode, tab_index, windex): - """ Executes JavaScript and wait for remoting app major mode equal to - the given mode. - - Returns: True if condition is satisfied or otherwise False. - """ - if not self._ExecuteJavascript(command, tab_index, windex): - return False - return self._WaitForJavascriptCondition( - 'remoting.getMajorMode() == remoting.AppMode.%s' % mode, - tab_index, windex) - - def GetWebappPath(self): - """Returns the path to the webapp. - - Expects the webapp to be in the same place as the pyautolib binaries. - """ - return os.path.join(self.BrowserPath(), 'remoting', 'remoting.webapp') - - def _GetHelperRunner(self): - """Returns the python binary name that runs chromoting_helper.py.""" - if sys.platform.startswith('win'): - return 'python' - else: - return 'suid-python' - - def _GetHelper(self): - """Get chromoting_helper.py.""" - return os.path.join(os.path.dirname(__file__), 'chromoting_helper.py') - - def InstallHostDaemon(self): - """Installs the host daemon.""" - subprocess.call([self._GetHelperRunner(), self._GetHelper(), - 'install', self.BrowserPath()]) - - def UninstallHostDaemon(self): - """Uninstalls the host daemon.""" - subprocess.call([self._GetHelperRunner(), self._GetHelper(), - 'uninstall', self.BrowserPath()]) - - def ContinueAuth(self, tab_index=1, windex=0): - """Starts authentication.""" - self.assertTrue( - self._WaitForJavascriptCondition('window.remoting && remoting.oauth2', - tab_index, windex), - msg='Timed out while waiting for remoting app to finish loading.') - self._ExecuteJavascript('remoting.oauth2.doAuthRedirect();', - tab_index, windex) - - def SignIn(self, email=None, password=None, otp=None, - tab_index=1, windex=0): - """Logs a user in. - - PyAuto tests start with a clean profile, so Chromoting tests should call - this for every run after launching the app. If email or password is - omitted, the user can type it into the browser window manually. - """ - self.assertTrue( - self._WaitForJavascriptCondition('document.getElementById("signIn")', - tab_index, windex), - msg='Unable to redirect for authentication.') - - if email: - self._ExecuteJavascript('document.getElementById("Email").value = "%s";' - 'document.getElementById("Passwd").focus();' - % email, tab_index, windex) - - if password: - self._ExecuteJavascript('document.getElementById("Passwd").value = "%s";' - 'document.getElementById("signIn").click();' - % password, tab_index, windex) - - if otp: - self.assertTrue( - self._WaitForJavascriptCondition( - 'document.getElementById("smsVerifyPin")', - tab_index, windex), - msg='Invalid username or password.') - self._ExecuteJavascript( - 'document.getElementById("smsUserPin").value = "%s";' - 'document.getElementById("smsVerifyPin").click();' % otp, - tab_index, windex) - - # If the account adder screen appears, then skip it. - self.assertTrue( - self._WaitForJavascriptCondition( - 'document.getElementById("skip") || ' - 'document.getElementById("submit_approve_access")', - tab_index, windex), - msg='No "skip adding account" or "approve access" link.') - self._ExecuteJavascript( - 'if (document.getElementById("skip")) ' - '{ document.getElementById("skip").click(); }', - tab_index, windex) - - def AllowAccess(self, tab_index=1, windex=0): - """Allows access to chromoting webapp.""" - # Approve access. - self.assertTrue( - self._WaitForJavascriptCondition( - 'document.getElementById("submit_approve_access")', - tab_index, windex), - msg='Did not go to permission page.') - self._WaitForJavascriptCondition( - '!document.getElementById("submit_approve_access").disabled', - tab_index, windex) - self._ExecuteJavascript( - 'document.getElementById("submit_approve_access").click();', - tab_index, windex) - - # Wait for some things to be ready. - self.assertTrue( - self._WaitForJavascriptCondition( - 'window.remoting && remoting.oauth2 && ' \ - 'remoting.oauth2.isAuthenticated()', - tab_index, windex), - msg='OAuth2 authentication failed.') - self.assertTrue( - self._WaitForJavascriptCondition( - 'window.localStorage.getItem("remoting-email")', - tab_index, windex), - msg='Chromoting app did not reload after authentication.') - - def DenyAccess(self, tab_index=1, windex=0): - """Deny and then allow access to chromoting webapp.""" - self.assertTrue( - self._WaitForJavascriptCondition( - 'document.getElementById("submit_deny_access")', - tab_index, windex), - msg='Did not go to permission page.') - self._WaitForJavascriptCondition( - '!document.getElementById("submit_deny_access").disabled', - tab_index, windex) - self._ExecuteJavascript( - 'document.getElementById("submit_deny_access").click();', - tab_index, windex) - - def SignOut(self, tab_index=1, windex=0): - """Signs out from chromoting and signs back in.""" - self._ExecuteAndWaitForMode( - 'document.getElementById("sign-out").click();', - 'UNAUTHENTICATED', tab_index, windex) - - def Authenticate(self, tab_index=1, windex=0): - """Finishes authentication flow for user.""" - self.ContinueAuth(tab_index, windex) - account = self.GetPrivateInfo()['test_chromoting_account'] - self.host.SignIn(account['username'], account['password'], None, - tab_index, windex) - self.host.AllowAccess(tab_index, windex) - - def StartMe2Me(self, tab_index=1, windex=0): - """Starts Me2Me. """ - self._ExecuteJavascript( - 'document.getElementById("get-started-me2me").click();', - tab_index, windex) - self.assertTrue( - self._WaitForJavascriptCondition( - 'document.getElementById("me2me-content").hidden == false', - tab_index, windex), - msg='No me2me content') - - def Share(self, tab_index=1, windex=0): - """Generates an access code and waits for incoming connections. - - Returns: - The access code on success; None otherwise. - """ - self._ExecuteAndWaitForMode( - 'remoting.tryShare();', - 'HOST_WAITING_FOR_CONNECTION', tab_index, windex) - return self.GetDOMValue( - 'document.getElementById("access-code-display").innerText', - tab_index, windex) - - def CancelShare(self, tab_index=1, windex=0): - """Stops sharing the desktop on the host side.""" - self.assertTrue( - self._ExecuteAndWaitForMode( - 'remoting.cancelShare();', - 'HOST_SHARE_FINISHED', tab_index, windex), - msg='Stopping sharing from the host side failed') - - def CleanupHostList(self, tab_index=1, windex=0): - """Removes hosts due to failure on previous stop-daemon""" - self.EnableConnectionsInstalled() - this_host_name = self.GetDOMValue( - 'document.getElementById("this-host-name").textContent', - tab_index, windex) - if this_host_name.endswith(' (offline)'): - this_host_name = this_host_name[:-10] - self.DisableConnections() - - total_hosts = self.GetDOMValue( - 'document.getElementById("host-list").childNodes.length', - tab_index, windex) - - # Start from the end while deleting bogus hosts - index = total_hosts - while index > 0: - index -= 1 - try: - hostname = self.GetDOMValue( - 'document.getElementById("host-list")' - '.childNodes[%s].textContent' % index, - tab_index, windex) - if hostname == this_host_name or \ - hostname == this_host_name + ' (offline)': - self._ExecuteJavascript( - 'document.getElementById("host-list")' - '.childNodes[%s].childNodes[3].click()' % index, - tab_index, windex) - self._ExecuteJavascript( - 'document.getElementById("confirm-host-delete").click()', - tab_index, windex) - except JSONInterfaceError: - print 'Ignore the error on deleting host' - - if self._WaitForJavascriptCondition( - 'document.getElementById("this-host-connect")' - '.getAttribute("data-daemon-state") == "enabled"', - tab_index, windex, 1): - self.DisableConnections() - - def EnableConnectionsInstalled(self, pin_exercise=False, - tab_index=1, windex=0): - """Enables the remote connections on the host side.""" - if sys.platform.startswith('darwin'): - subprocess.call([self._GetHelperRunner(), self._GetHelper(), 'enable']) - - self.assertTrue( - self._ExecuteAndWaitForMode( - 'document.getElementById("start-daemon").click();', - 'HOST_SETUP_ASK_PIN', tab_index, windex), - msg='Cannot start host setup') - self.assertTrue( - self._WaitForJavascriptCondition( - 'document.getElementById("ask-pin-form").hidden == false', - tab_index, windex), - msg='No ask pin dialog') - - if pin_exercise: - # Cancels the pin prompt - self._ExecuteJavascript( - 'document.getElementById("daemon-pin-cancel").click();', - tab_index, windex) - - # Enables again - self.assertTrue( - self._ExecuteAndWaitForMode( - 'document.getElementById("start-daemon").click();', - 'HOST_SETUP_ASK_PIN', tab_index, windex), - msg='Cannot start host setup') - - # Click ok without typing in pins - self._ExecuteJavascript( - 'document.getElementById("daemon-pin-ok").click();', - tab_index, windex) - self.assertTrue( - self._WaitForJavascriptCondition( - 'document.getElementById("daemon-pin-error-message")', - tab_index, windex), - msg='No pin error message') - - # Mis-matching pins - self._ExecuteJavascript( - 'document.getElementById("daemon-pin-entry").value = "111111";', - tab_index, windex) - self._ExecuteJavascript( - 'document.getElementById("daemon-pin-confirm").value = "123456";', - tab_index, windex) - self.assertTrue( - self._WaitForJavascriptCondition( - 'document.getElementById("daemon-pin-error-message")', - tab_index, windex), - msg='No pin error message') - - # Types in correct pins - self._ExecuteJavascript( - 'document.getElementById("daemon-pin-entry").value = "111111";', - tab_index, windex) - self._ExecuteJavascript( - 'document.getElementById("daemon-pin-confirm").value = "111111";', - tab_index, windex) - self.assertTrue( - self._ExecuteAndWaitForMode( - 'document.getElementById("daemon-pin-ok").click();', - 'HOST_SETUP_PROCESSING', tab_index, windex), - msg='Host setup was not started') - - # Handles preference panes - self.assertTrue( - self._WaitForJavascriptCondition( - 'remoting.currentMode == remoting.AppMode.HOST_SETUP_DONE', - tab_index, windex), - msg='Host setup was not done') - - # Dismisses the host config done dialog - self.assertTrue( - self._WaitForJavascriptCondition( - 'document.getElementById("host-setup-dialog")' - '.childNodes[5].hidden == false', - tab_index, windex), - msg='No host setup done dialog') - self.assertTrue( - self._ExecuteAndWaitForMode( - 'document.getElementById("host-config-done-dismiss").click();', - 'HOME', tab_index, windex), - msg='Failed to dismiss host setup confirmation dialog') - - def EnableConnectionsUninstalledAndCancel(self, tab_index=1, windex=0): - """Enables remote connections while host is not installed yet.""" - self.assertTrue( - self._ExecuteAndWaitForMode( - 'document.getElementById("start-daemon").click();', - 'HOST_SETUP_INSTALL', tab_index, windex), - msg='Cannot start host install') - self.assertTrue( - self._ExecuteAndWaitForMode( - 'document.getElementById("host-config-install-dismiss").click();', - 'HOME', tab_index, windex), - msg='Failed to dismiss host install dialog') - - def DisableConnections(self, tab_index=1, windex=0): - """Disables the remote connections on the host side.""" - if sys.platform.startswith('darwin'): - subprocess.call([self._GetHelperRunner(), self._GetHelper(), 'disable']) - - # Re-try to make disabling connection more stable - for _ in range (1, 4): - self._ExecuteJavascript( - 'document.getElementById("stop-daemon").click();', - tab_index, windex) - - # Immediately waiting for host-setup-dialog hidden sometimes times out - # even though visually it is hidden. Add some sleep here - time.sleep(2) - - if self._WaitForJavascriptCondition( - 'document.getElementById("host-setup-dialog")' - '.childNodes[3].hidden == true', - tab_index, windex, 1): - break; - - self.assertTrue( - self._ExecuteAndWaitForMode( - 'document.getElementById("host-config-done-dismiss").click();', - 'HOME', tab_index, windex), - msg='Failed to dismiss host setup confirmation dialog') - - def Connect(self, access_code, tab_index=1, windex=0): - """Connects to a Chromoting host and starts the session.""" - self.assertTrue( - self._ExecuteAndWaitForMode( - 'document.getElementById("access-code-entry").value = "%s";' - 'remoting.connectIt2Me();' % access_code, - 'IN_SESSION', tab_index, windex), - msg='Cannot connect it2me session') - - def ChangePin(self, pin='222222', tab_index=1, windex=0): - """Changes pin for enabled host.""" - if sys.platform.startswith('darwin'): - subprocess.call([self._GetHelperRunner(), self._GetHelper(), 'changepin']) - - self.assertTrue( - self._ExecuteAndWaitForMode( - 'document.getElementById("change-daemon-pin").click();', - 'HOST_SETUP_ASK_PIN', tab_index, windex), - msg='Cannot change daemon pin') - self.assertTrue( - self._WaitForJavascriptCondition( - 'document.getElementById("ask-pin-form").hidden == false', - tab_index, windex), - msg='No ask pin dialog') - - self._ExecuteJavascript( - 'document.getElementById("daemon-pin-entry").value = "' + pin + '";', - tab_index, windex) - self._ExecuteJavascript( - 'document.getElementById("daemon-pin-confirm").value = "' + - pin + '";', tab_index, windex) - self.assertTrue( - self._ExecuteAndWaitForMode( - 'document.getElementById("daemon-pin-ok").click();', - 'HOST_SETUP_PROCESSING', tab_index, windex), - msg='Host setup was not started') - - # Handles preference panes - self.assertTrue( - self._WaitForJavascriptCondition( - 'remoting.currentMode == remoting.AppMode.HOST_SETUP_DONE', - tab_index, windex), - msg='Host setup was not done') - - # Dismisses the host config done dialog - self.assertTrue( - self._WaitForJavascriptCondition( - 'document.getElementById("host-setup-dialog")' - '.childNodes[5].hidden == false', - tab_index, windex), - msg='No host setup done dialog') - self.assertTrue( - self._ExecuteAndWaitForMode( - 'document.getElementById("host-config-done-dismiss").click();', - 'HOME', tab_index, windex), - msg='Failed to dismiss host setup confirmation dialog') - - def ChangeName(self, new_name='Changed', tab_index=1, windex=0): - """Changes the host name.""" - self._ExecuteJavascript( - 'document.getElementById("this-host-rename").click();', - tab_index, windex) - self._ExecuteJavascript( - 'document.getElementById("this-host-name").childNodes[0].value = "' + - new_name + '";', tab_index, windex) - self._ExecuteJavascript( - 'document.getElementById("this-host-rename").click();', - tab_index, windex) - - def ConnectMe2Me(self, pin='111111', mode='IN_SESSION', - tab_index=1, windex=0): - """Connects to a Chromoting host and starts the session.""" - - # There is delay from the enabling remote connections to the host - # showing up in the host list. We need to reload the web app to get - # the host to show up. We will repeat this a few times to make sure - # eventually host appears. - for _ in range(1, 13): - self._ExecuteJavascript( - 'window.location.reload();', - tab_index, windex) - - # pyauto _GetResultFromJSONRequest throws JSONInterfaceError after - # 45 seconds if ExecuteJavascript is called right after reload. - # Waiting 2s here can avoid this. So instead of getting the error and - # wait 45s, we wait 2s here. If the error still happens, the following - # retry will handle that. - time.sleep(2) - - # If this-host-connect is still not enabled, let's retry one more time. - this_host_connect_enabled = False - for _ in range(1, 3): - daemon_state_enabled = self._WaitForJavascriptCondition( - 'document.getElementById("this-host-connect")' - '.getAttribute("data-daemon-state") == "enabled"', - tab_index, windex, 1) - host_online = self._WaitForJavascriptCondition( - 'document.getElementById("this-host-name")' - '.textContent.toString().indexOf("offline") == -1', - tab_index, windex, 1) - this_host_connect_enabled = daemon_state_enabled and host_online - if this_host_connect_enabled: - break - if this_host_connect_enabled: - break; - - # Clicking this-host-connect does work right after this-host-connect - # is enabled. Need to retry. - for _ in range(1, 4): - self._ExecuteJavascript( - 'document.getElementById("this-host-connect").click();', - tab_index, windex) - - # pyauto _GetResultFromJSONRequest throws JSONInterfaceError after - # a long time out if WaitUntil is called right after click. - # Waiting 2s here can avoid this. - time.sleep(2) - - # If cannot detect that pin-form appears, retry one more time. - pin_form_exposed = False - for _ in range(1, 3): - pin_form_exposed = self._WaitForJavascriptCondition( - 'document.getElementById("client-dialog")' - '.childNodes[9].hidden == false', - tab_index, windex, 1) - if pin_form_exposed: - break - - if pin_form_exposed: - break - - # Dismiss connect failure dialog before retry - if self._WaitForJavascriptCondition( - 'document.getElementById("client-dialog")' - '.childNodes[25].hidden == false', - tab_index, windex, 1): - self._ExecuteJavascript( - 'document.getElementById("client-finished-me2me-button")' - '.click();', - tab_index, windex) - - self._ExecuteJavascript( - 'document.getElementById("pin-entry").value = "' + pin + '";', - tab_index, windex) - self.assertTrue( - self._ExecuteAndWaitForMode( - 'document.getElementById("pin-form").childNodes[5].click();', - mode, tab_index, windex), - msg='Session was not started') - - def Disconnect(self, tab_index=1, windex=0): - """Disconnects from the Chromoting it2me session on the client side.""" - self.assertTrue( - self._ExecuteAndWaitForMode( - 'remoting.disconnect();', - 'CLIENT_SESSION_FINISHED_IT2ME', tab_index, windex), - msg='Disconnecting it2me session from the client side failed') - - def DisconnectMe2Me(self, confirmation=True, tab_index=1, windex=0): - """Disconnects from the Chromoting me2me session on the client side.""" - self.assertTrue( - self._ExecuteAndWaitForMode( - 'remoting.disconnect();', - 'CLIENT_SESSION_FINISHED_ME2ME', tab_index, windex), - msg='Disconnecting me2me session from the client side failed') - - if confirmation: - self.assertTrue( - self._ExecuteAndWaitForMode( - 'document.getElementById("client-finished-me2me-button")' - '.click();', 'HOME', tab_index, windex), - msg='Failed to dismiss session finished dialog') - - def ReconnectMe2Me(self, pin='111111', tab_index=1, windex=0): - """Reconnects the me2me session.""" - self._ExecuteJavascript( - 'document.getElementById("client-reconnect-button").click();', - tab_index, windex) - - # pyauto _GetResultFromJSONRequest throws JSONInterfaceError after - # a long time out if WaitUntil is called right after click. - time.sleep(2) - - # If cannot detect that pin-form appears, retry one more time. - for _ in range(1, 3): - pin_form_exposed = self._WaitForJavascriptCondition( - 'document.getElementById("client-dialog")' - '.childNodes[9].hidden == false', - tab_index, windex, 1) - if pin_form_exposed: - break - - self._ExecuteJavascript( - 'document.getElementById("pin-entry").value = "' + pin + '";', - tab_index, windex) - self.assertTrue( - self._ExecuteAndWaitForMode( - 'document.getElementById("pin-form").childNodes[5].click();', - 'IN_SESSION', tab_index, windex), - msg='Session was not started when reconnecting') diff --git a/chrome/test/pyautolib/dom_mutation_observer.js b/chrome/test/pyautolib/dom_mutation_observer.js deleted file mode 100644 index ced5ec3..0000000 --- a/chrome/test/pyautolib/dom_mutation_observer.js +++ /dev/null @@ -1,276 +0,0 @@ -/* Copyright (c) 2012 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. - * - * Helper javascript injected whenever a DomMutationEventObserver is created. - * - * This script uses MutationObservers to watch for changes to the DOM, then - * reports the event to the observer using the DomAutomationController. An - * anonymous namespace is used to prevent conflict with other Javascript. - * - * Args: - * automation_id: Automation id used to route DomAutomationController messages. - * observer_id: Id of the observer who will be receiving the messages. - * observer_type: One of 'add', 'remove', 'change', or 'exists'. - * xpath: XPath used to specify the DOM node of interest. - * attribute: If |expected_value| is provided, check if this attribute of the - * DOM node matches |expected value|. - * expected_value: If not null, regular expression to match with the value of - * |attribute| after the mutation. - */ -function(automation_id, observer_id, observer_type, xpath, attribute, - expected_value) { - - /* Raise an event for the DomMutationEventObserver. */ - function raiseEvent() { - if (window.domAutomationController) { - console.log("Event sent to DomEventObserver with id=" + - observer_id + "."); - window.domAutomationController.sendWithId( - automation_id, "__dom_mutation_observer__:" + observer_id); - } - } - - /* Calls raiseEvent if the expected node has been added to the DOM. - * - * Args: - * mutations: A list of mutation objects. - * observer: The mutation observer object associated with this callback. - */ - function addNodeCallback(mutations, observer) { - for (var j=0; j<mutations.length; j++) { - for (var i=0; i<mutations[j].addedNodes.length; i++) { - var node = mutations[j].addedNodes[i]; - if (xpathMatchesNode(node, xpath) && - nodeAttributeValueEquals(node, attribute, expected_value)) { - raiseEvent(); - observer.disconnect(); - delete observer; - return; - } - } - } - } - - /* Calls raiseEvent if the expected node has been removed from the DOM. - * - * Args: - * mutations: A list of mutation objects. - * observer: The mutation observer object associated with this callback. - */ - function removeNodeCallback(mutations, observer) { - var node = firstXPathNode(xpath); - if (!node) { - raiseEvent(); - observer.disconnect(); - delete observer; - } - } - - /* Calls raiseEvent if the given node has been changed to expected_value. - * - * Args: - * mutations: A list of mutation objects. - * observer: The mutation observer object associated with this callback. - */ - function changeNodeCallback(mutations, observer) { - for (var j=0; j<mutations.length; j++) { - if (nodeAttributeValueEquals(mutations[j].target, attribute, - expected_value)) { - raiseEvent(); - observer.disconnect(); - delete observer; - return; - } - } - } - - /* Calls raiseEvent if the expected node exists in the DOM. - * - * Args: - * mutations: A list of mutation objects. - * observer: The mutation observer object associated with this callback. - */ - function existsNodeCallback(mutations, observer) { - if (findNodeMatchingXPathAndValue(xpath, attribute, expected_value)) { - raiseEvent(); - observer.disconnect(); - delete observer; - return; - } - } - - /* Return true if the xpath matches the given node. - * - * Args: - * node: A node object from the DOM. - * xpath: An XPath used to compare with the DOM node. - */ - function xpathMatchesNode(node, xpath) { - var con = document.evaluate(xpath, document, null, - XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null); - var thisNode = con.iterateNext(); - while (thisNode) { - if (node == thisNode) { - return true; - } - thisNode = con.iterateNext(); - } - return false; - } - - /* Returns the first node in the DOM that matches the xpath. - * - * Args: - * xpath: XPath used to specify the DOM node of interest. - */ - function firstXPathNode(xpath) { - return document.evaluate(xpath, document, null, - XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; - } - - /* Returns the first node in the DOM that matches the xpath. - * - * Args: - * xpath: XPath used to specify the DOM node of interest. - * attribute: The attribute to match |expected_value| against. - * expected_value: A regular expression to match with the node's - * |attribute|. If null the match always succeeds. - */ - function findNodeMatchingXPathAndValue(xpath, attribute, expected_value) { - var nodes = document.evaluate(xpath, document, null, - XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); - var node; - while ( (node = nodes.iterateNext()) ) { - if (nodeAttributeValueEquals(node, attribute, expected_value)) - return node; - } - return null; - } - - /* Returns true if the node's |attribute| value is matched by the regular - * expression |expected_value|, false otherwise. - * - * Args: - * node: A node object from the DOM. - * attribute: The attribute to match |expected_value| against. - * expected_value: A regular expression to match with the node's - * |attribute|. If null the test always passes. - */ - function nodeAttributeValueEquals(node, attribute, expected_value) { - return expected_value == null || - (node[attribute] && RegExp(expected_value, "").test(node[attribute])); - } - - /* Watch for a node matching xpath to be added to the DOM. - * - * Args: - * xpath: XPath used to specify the DOM node of interest. - */ - function observeAdd(xpath) { - window.domAutomationController.send("success"); - if (findNodeMatchingXPathAndValue(xpath, attribute, expected_value)) { - raiseEvent(); - console.log("Matching node in DOM, assuming it was previously added."); - return; - } - - var obs = new MutationObserver(addNodeCallback); - obs.observe(document, - { childList: true, - attributes: true, - characterData: true, - subtree: true}); - } - - /* Watch for a node matching xpath to be removed from the DOM. - * - * Args: - * xpath: XPath used to specify the DOM node of interest. - */ - function observeRemove(xpath) { - window.domAutomationController.send("success"); - if (!firstXPathNode(xpath)) { - raiseEvent(); - console.log("No matching node in DOM, assuming it was already removed."); - return; - } - - var obs = new MutationObserver(removeNodeCallback); - obs.observe(document, - { childList: true, - attributes: true, - subtree: true}); - } - - /* Watch for the textContent of a node matching xpath to change to - * expected_value. - * - * Args: - * xpath: XPath used to specify the DOM node of interest. - */ - function observeChange(xpath) { - var node = firstXPathNode(xpath); - if (!node) { - console.log("No matching node in DOM."); - window.domAutomationController.send( - "No DOM node matching xpath exists."); - return; - } - window.domAutomationController.send("success"); - - var obs = new MutationObserver(changeNodeCallback); - obs.observe(node, - { childList: true, - attributes: true, - characterData: true, - subtree: true}); - } - - /* Watch for a node matching xpath to exist in the DOM. - * - * Args: - * xpath: XPath used to specify the DOM node of interest. - */ - function observeExists(xpath) { - window.domAutomationController.send("success"); - if (findNodeMatchingXPathAndValue(xpath, attribute, expected_value)) { - raiseEvent(); - console.log("Node already exists in DOM."); - return; - } - - var obs = new MutationObserver(existsNodeCallback); - obs.observe(document, - { childList: true, - attributes: true, - characterData: true, - subtree: true}); - } - - /* Interpret arguments and launch the requested observer function. */ - function installMutationObserver() { - switch (observer_type) { - case "add": - observeAdd(xpath); - break; - case "remove": - observeRemove(xpath); - break; - case "change": - observeChange(xpath); - break; - case "exists": - observeExists(xpath); - break; - } - console.log("MutationObserver javscript injection completed."); - } - - /* Ensure the DOM is loaded before attempting to create MutationObservers. */ - if (document.body) { - installMutationObserver(); - } else { - window.addEventListener("DOMContentLoaded", installMutationObserver, true); - } -} diff --git a/chrome/test/pyautolib/download_info.py b/chrome/test/pyautolib/download_info.py deleted file mode 100644 index 44d5705..0000000 --- a/chrome/test/pyautolib/download_info.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) 2011 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. - -"""DownloadInfo: python representation for downloads visible to Chrome. - -Obtain one of these from PyUITestSuite::GetDownloadsInfo() call. - -class MyDownloadsTest(pyauto.PyUITest): - def testDownload(self): - self.DownloadAndWaitForStart('http://my.url/package.zip') - self.WaitForAllDownloadsToComplete() - info = self.GetDownloadsInfo() - print info.Downloads() - self.assertEqual(info.Downloads()[0]['file_name'], 'packge.zip') - -See more tests in chrome/test/functional/downloads.py. -""" - -import os -import simplejson as json -import sys - -from pyauto_errors import JSONInterfaceError - - -class DownloadInfo(object): - """Represent info about Downloads. - - The info is represented as a list of DownloadItems. Each DownloadItem is a - dictionary with various attributes about a download, like id, file_name, - path, state, and so on. - """ - def __init__(self, downloads_dict): - """Initialize a DownloadInfo from a string of json. - - Args: - downloads_dict: a dict returned by the IPC command 'GetDownloadsInfo'. - A typical dict representing one download looks like: - {'downloads': [{'url': 'http://blah/a_file.zip', - 'file_name': 'a_file.zip', - 'state': 'COMPLETED', - ..., - ..., } ] } - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - # JSON string prepared in GetDownloadsInfo() in automation_provider.cc - self.downloadsdict = downloads_dict - if self.downloadsdict.has_key('error'): - raise JSONInterfaceError(self.downloadsdict['error']) - - def Downloads(self): - """Info about all downloads. - - This includes downloads in all states (COMPLETE, IN_PROGRESS, ...). - - Returns: - [downloaditem1, downloaditem2, ...] - """ - return self.downloadsdict.get('downloads', []) - - def DownloadsInProgress(self): - """Info about all downloads in progress. - - Returns: - [downloaditem1, downloaditem2, ...] - """ - return [x for x in self.Downloads() if x['state'] == 'IN_PROGRESS'] - - def DownloadsComplete(self): - """Info about all downloads that have completed. - - Returns: - [downloaditem1, downloaditem2, ...] - """ - return [x for x in self.Downloads() if x['state'] == 'COMPLETE'] diff --git a/chrome/test/pyautolib/fetch_prebuilt_pyauto.py b/chrome/test/pyautolib/fetch_prebuilt_pyauto.py deleted file mode 100755 index 963b98f..0000000 --- a/chrome/test/pyautolib/fetch_prebuilt_pyauto.py +++ /dev/null @@ -1,214 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Fetch prebuilt binaries to run PyAuto. - -Sets up Chrome and PyAuto binaries using prebuilt binaries from the -continuous build archives. Works on mac, win, linux (32 & 64 bit). - -Examples: - On Mac: - $ python fetch_prebuilt_pyauto.py -d xcodebuild/Release - http://build.chromium.org/f/chromium/continuous/mac/LATEST - - On Win: - $ python fetch_prebuilt_pyauto.py -d chrome\Release - http://build.chromium.org/f/chromium/continuous/win/LATEST -""" - -import glob -import httplib -import optparse -import os -import platform -import shutil -import sys -import urllib -import urllib2 -import urlparse - -import pyauto_utils - - -class FetchPrebuilt(object): - """Util class to fetch prebuilt binaries to run PyAuto.""" - - def _ParseArgs(self): - parser = optparse.OptionParser() - parser.add_option( - '-d', '--outdir', type='string', default=None, - help='Directory in which to setup. This is typically the directory ' - 'where the binaries would go when compiled from source.') - parser.add_option( - '-p', '--platform', type='string', - default=pyauto_utils.GetCurrentPlatform(), - help='Platform. Valid options: win, mac, linux32, linux64. ' - 'Default: current platform (%s)' % pyauto_utils.GetCurrentPlatform()) - parser.add_option( - '-l', '--latest', action='store_true', default=False, - help='Download the latest chromium build from commondatastorage. ' - '[default=False]') - self._options, self._args = parser.parse_args() - if self._options.latest: - self._url = self._GetLastestDownloadURL(self._options.platform) - elif not self._args: - print >>sys.stderr, 'Need download url' - sys.exit(2) - else: - self._url = self._args[0] - if not self._options.outdir: - print >>sys.stderr, 'Need output directory: -d/--outdir' - sys.exit(1) - self._outdir = self._options.outdir - # Chromium continuous build archive has a non-standard format. - if 'index.html?path=' in self._url: - self._url = self._url.replace('index.html?path=', '') - self._url = self._url.rstrip('/') - # Determine name of zip. - if not self._options.platform.startswith('linux'): - self._chrome_zip_name = 'chrome-%s' % {'mac': 'mac', - 'win': 'win32' - }[self._options.platform] - else: - linux_32_names = ['linux', 'lucid32bit'] - linux_64_names = ['linux64', 'lucid64bit'] - linux_names = {'linux': linux_32_names + linux_64_names, - 'linux32': linux_32_names, - 'linux64': linux_64_names - }[self._options.platform] - for name in linux_names: - zip_name = 'chrome-' + name - if pyauto_utils.DoesUrlExist('%s/%s.zip' % (self._url, zip_name)): - self._chrome_zip_name = zip_name - break - else: - raise RuntimeError('Could not find chrome zip at ' + self._url) - - # Setup urls to download. - self._chrome_zip_url = '%s/%s.zip' % (self._url, self._chrome_zip_name) - self._remoting_zip_url = self._url + '/' + 'remoting-webapp.zip' - chrome_test_url = '%s/%s.test' % (self._url, self._chrome_zip_name) - self._pyautolib_py_url = '%s/pyautolib.py' % chrome_test_url - if self._options.platform == 'win': - self._pyautolib_so_name = '_pyautolib.pyd' - self._chromedriver_name = 'chromedriver.exe' - else: - self._pyautolib_so_name = '_pyautolib.so' - self._chromedriver_name = 'chromedriver' - if self._options.platform == 'mac': - self._ffmpegsumo_so_name = 'ffmpegsumo.so' - self._ffmpegsumo_so_url = chrome_test_url + '/' + self._ffmpegsumo_so_name - self._pyautolib_so_url = chrome_test_url + '/' + self._pyautolib_so_name - self._chromedriver_url = chrome_test_url + '/' + self._chromedriver_name - - def _GetLastestDownloadURL(self, os_platform): - os_type = {'win': 'Win', - 'mac': 'Mac', - 'linux': 'Linux', - 'linux32': 'Linux', - 'linux64': 'Linux_x64'}[os_platform] - if os_type == 'Linux' and platform.architecture()[0] == '64bit': - os_type = 'Linux_x64' - last_change_url = ('http://commondatastorage.googleapis.com/' - 'chromium-browser-continuous/%s/LAST_CHANGE' % os_type) - response = urllib2.urlopen(last_change_url) - last_change = response.read() - if not last_change: - print >>sys.stderr, ('Unable to get latest from %s' % last_change_url) - sys.exit(2) - last_change_url = ('http://commondatastorage.googleapis.com/' - 'chromium-browser-continuous/%s/%s' % (os_type, - last_change)) - return last_change_url - - def Cleanup(self): - """Remove old binaries, if any.""" - pass - - def Run(self): - self._ParseArgs() - if not os.path.isdir(self._outdir): - os.makedirs(self._outdir) - get_remoting = pyauto_utils.DoesUrlExist(self._remoting_zip_url) - - # Fetch chrome & pyauto binaries - print 'Fetching', self._chrome_zip_url - chrome_zip = urllib.urlretrieve(self._chrome_zip_url)[0] - - if get_remoting: - print 'Fetching', self._remoting_zip_url - remoting_zip = urllib.urlretrieve(self._remoting_zip_url)[0] - else: - print 'Warning: %s does not exist.' % self._remoting_zip_url - - print 'Fetching', self._pyautolib_py_url - pyautolib_py = urllib.urlretrieve(self._pyautolib_py_url)[0] - - print 'Fetching', self._pyautolib_so_url - pyautolib_so = urllib.urlretrieve(self._pyautolib_so_url)[0] - - if self._options.platform == 'mac': - print 'Fetching', self._ffmpegsumo_so_url - ffmpegsumo_so = urllib.urlretrieve(self._ffmpegsumo_so_url)[0] - - print 'Fetching', self._chromedriver_url - chromedriver = urllib.urlretrieve(self._chromedriver_url)[0] - - chrome_unzip_dir = os.path.join(self._outdir, self._chrome_zip_name) - if os.path.exists(chrome_unzip_dir): - print 'Cleaning', chrome_unzip_dir - pyauto_utils.RemovePath(chrome_unzip_dir) - print 'Unzipping' - pyauto_utils.UnzipFilenameToDir(chrome_zip, self._outdir) - if get_remoting: - pyauto_utils.UnzipFilenameToDir(remoting_zip, self._outdir) - shutil.move(self._outdir + '/remoting-webapp', - self._outdir + '/remoting/remoting.webapp') - - # Copy over the binaries to outdir - items_to_copy = { - pyautolib_py: os.path.join(self._outdir, 'pyautolib.py'), - pyautolib_so: os.path.join(self._outdir, self._pyautolib_so_name), - chromedriver: os.path.join(self._outdir, self._chromedriver_name) - } - if self._options.platform == 'mac': - items_to_copy[ffmpegsumo_so] = \ - os.path.join(self._outdir, self._ffmpegsumo_so_name) - - unzip_dir_contents = glob.glob(os.path.join(chrome_unzip_dir, '*')) - for item in unzip_dir_contents: - name = os.path.basename(item) - items_to_copy[item] = os.path.join(self._outdir, name) - - for src, dest in items_to_copy.iteritems(): - pyauto_utils.RemovePath(dest) - print '%s ==> %s' % (os.path.basename(src), dest) - shutil.move(src, dest) - pyauto_utils.RemovePath(chrome_unzip_dir) - - # Final setup (if any) - # Set executable bit on chromedriver binary. - if not self._options.platform == 'win': - os.chmod(items_to_copy[chromedriver], 0700) - - # Create symlink to .framework on Mac - if self._options.platform == 'mac': - mac_app_name = os.path.basename([x for x in unzip_dir_contents - if x.endswith('.app')][0]) - os.chdir(self._outdir) - framework = glob.glob(os.path.join( - mac_app_name, 'Contents', 'Versions', '*', '*.framework'))[0] - print framework - dest = os.path.basename(framework) - os.path.lexists(dest) and os.remove(dest) - print 'Creating symlink "%s"' % dest - os.symlink(framework, dest) - - print 'Prepared binaries in "%s"' % self._outdir - return 0 - - -if __name__ == '__main__': - sys.exit(FetchPrebuilt().Run()) diff --git a/chrome/test/pyautolib/generate_docs.py b/chrome/test/pyautolib/generate_docs.py deleted file mode 100755 index a603ad8..0000000 --- a/chrome/test/pyautolib/generate_docs.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import optparse -import os -import pydoc -import shutil -import sys - - -def main(): - parser = optparse.OptionParser() - parser.add_option('-w', '--write', dest='dir', metavar='FILE', - default=os.path.join(os.getcwd(), 'pyauto_docs'), - help=('Directory path to write all of the documentation. ' - 'Defaults to "pyauto_docs" in current directory.')) - parser.add_option('-p', '--pyautolib', dest='pyautolib', metavar='FILE', - default=os.getcwd(), - help='Location of pyautolib directory') - (options, args) = parser.parse_args() - - if not os.path.isdir(options.dir): - os.makedirs(options.dir) - - # Add these paths so pydoc can find everything - sys.path.append(os.path.join(options.pyautolib, - '../../../third_party/')) - sys.path.append(options.pyautolib) - - # Get a snapshot of the current directory where pydoc will export the files - previous_contents = set(os.listdir(os.getcwd())) - pydoc.writedocs(options.pyautolib) - current_contents = set(os.listdir(os.getcwd())) - - if options.dir == os.getcwd(): - print 'Export complete, files are located in %s' % options.dir - return 1 - - new_files = current_contents.difference(previous_contents) - for file_name in new_files: - basename, extension = os.path.splitext(file_name) - if extension == '.html': - # Build the complete path - full_path = os.path.join(os.getcwd(), file_name) - existing_file_path = os.path.join(options.dir, file_name) - if os.path.isfile(existing_file_path): - os.remove(existing_file_path) - shutil.move(full_path, options.dir) - - print 'Export complete, files are located in %s' % options.dir - return 0 - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/chrome/test/pyautolib/history_info.py b/chrome/test/pyautolib/history_info.py deleted file mode 100644 index f8e05b3..0000000 --- a/chrome/test/pyautolib/history_info.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (c) 2011 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. - -"""History: python representation for history. - -Obtain one of these from PyUITestSuite::GetHistoryInfo() call. - -Example: -class MyTest(pyauto.PyUITest): - def testBasic(self): - url = 'http://www.google.com/' - self.NavigateToURL(url) - history = self.GetHistoryInfo() - self.assertEqual(1, len(history)) - self.assertEqual(url, history[0]['url']) - -See more tests in chrome/test/functional/history.py. -""" - -import simplejson as json - -from pyauto_errors import JSONInterfaceError - - -class HistoryInfo(object): - """Represent info about browsing history. - - The info is represented as a list of history items containing url, title, - time, etc. - """ - def __init__(self, history_dict): - """Initialize a HistoryInfo from a string of json. - - Args: - json_string: a dictionary as returned by the IPC command 'GetHistoryInfo'. - A typical dict representing history info looks like: - {'history': [ - {'url': 'http://www.google.com/', - 'title': 'Google', - ..., - ..., - }, ] } - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - # JSON string prepared in GetHistoryInfo() in automation_provider.cc - self.historydict = history_dict - - def History(self): - """Get history list. - - History is ordered latest first, that is in the same order as - chrome://history/ would list. - - Example: - [ { u'snippet': u'', - u'starred': False, - u'time': 1271781612, - u'title': u'Google News', - u'url': u'http://news.google.com/'}, - { u'snippet': u'', - u'starred': True, - u'time': 1271781602, - u'title': u'Google', - u'url': u'http://www.google.com/'}] - - The snippet attribute will be empty in most cases. If GetHistoryInfo() is - provided a non-empty search_text arg, the snippet attribute will contain the - snippet as it would be visible when searching for that text in the - chrome://history/ UI. - - Returns: - [item1, item2, ...] - """ - return self.historydict.get('history', []) diff --git a/chrome/test/pyautolib/mock_pref_pane.py b/chrome/test/pyautolib/mock_pref_pane.py deleted file mode 100644 index cbb3a48..0000000 --- a/chrome/test/pyautolib/mock_pref_pane.py +++ /dev/null @@ -1,124 +0,0 @@ -# Copyright (c) 2012 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. - -"""Mock pref pane for testing purpose on Mac.""" - -import Foundation -import os -import signal -import subprocess -import sys -import tempfile -import time - - -class MockPrefPane(object): - """Mock Pref Pane to enable/disable/changepin without system prompt. - - This only applies to Mac. - """ - - def __init__(self): - self._service_name = 'org.chromium.chromoting' - self._real_user_id = os.getuid() - self._config_file = os.path.join(tempfile.gettempdir(), - '%s.json' % self._service_name) - self._tool_script = '/Library/PrivilegedHelperTools/%s.me2me.sh' % \ - self._service_name - - def _GetJobPid(self): - """Gets the org.chromium.chromoting job id.""" - process = subprocess.Popen(['launchctl', 'list'], stdout=subprocess.PIPE) - pid = None - for line in process.stdout: - # Format is: - # 12345 - my.job (if my.job is running, number is job's PID) - # - 0 my.other.job (if my.other.job is not running) - fields = line.strip().split('\t') - if fields[2] == self._service_name and fields[0] != "-": - pid = fields[0] - break - process.wait() - return pid - - def Enable(self): - """Handles what pref pane does for enabling connection.""" - # Elevate privileges, otherwise tool_script executes with EUID != 0. - os.setuid(0) - subprocess.call([self._tool_script, '--enable'], - stdin=open(self._config_file)) - - # Drop privileges, start the launchd job as the logged-in user. - os.setuid(self._real_user_id) - subprocess.call(['launchctl', 'start', self._service_name]) - - # Starting a launchd job is an asynchronous operation that typically takes - # a couple of seconds, so poll until the job has started. - for _ in range(1, 10): - if self._GetJobPid(): - print '*** org.chromium.chromoting is running ***' - break - time.sleep(2) - - def Disable(self): - """Handles what pref pane does for disabling connection.""" - # Elevate privileges, otherwise tool_script executes with EUID != 0. - os.setuid(0) - subprocess.call([self._tool_script, '--disable'], - stdin=open(self._config_file)) - - # Drop privileges, stop the launchd job as the logged-in user. - os.setuid(self._real_user_id) - subprocess.call(['launchctl', 'stop', self._service_name]) - - # Stopping a launchd job is an asynchronous operation that typically takes - # a couple of seconds, so poll until the job has stopped. - for _ in range(1, 10): - if not self._GetJobPid(): - print '*** org.chromium.chromoting is not running ***' - break - time.sleep(2) - - def ChangePin(self): - """Handles what pref pane does for changing pin.""" - # Elevate privileges, otherwise tool_script executes with EUID != 0. - os.setuid(0) - subprocess.call([self._tool_script, '--save-config'], - stdin=open(self._config_file)) - - # Drop privileges and send SIGHUP to org.chromium.chromoting - os.setuid(self._real_user_id) - os.kill(int(self._GetJobPid()), signal.SIGHUP) - - def NotifyWebapp(self): - """Notifies the web app that pref pane operation is done.""" - notif_center = Foundation.NSDistributedNotificationCenter.defaultCenter() - notif_center.postNotificationName_object_userInfo_( - self._service_name + '.update_succeeded', None, None) - - -def Main(): - """Handles the mock pref pane actions.""" - assert sys.platform.startswith('darwin') - - print '*** Started mock pref pane ***' - print '*** EUID=%d, UID=%d ***' % (os.geteuid(), os.getuid()) - - pref_pane = MockPrefPane() - - if sys.argv[1] == 'enable': - pref_pane.Enable() - elif sys.argv[1] == 'disable': - pref_pane.Disable() - elif sys.argv[1] == 'changepin': - pref_pane.ChangePin() - else: - print >>sys.stderr, 'Invalid syntax' - return - - pref_pane.NotifyWebapp() - - -if __name__ == '__main__': - Main()
\ No newline at end of file diff --git a/chrome/test/pyautolib/omnibox_info.py b/chrome/test/pyautolib/omnibox_info.py deleted file mode 100644 index 5f11255..0000000 --- a/chrome/test/pyautolib/omnibox_info.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) 2011 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. - -"""Python representation for Chromium Omnibox. - -Obtain one of these from PyUITestSuite::GetOmniboxInfo() call. - -Example: -class MyTest(pyauto.PyUITest): - def testBasic(self): - info = self.OmniboxInfo() # fetch omnibox snapshot - print info.Matches() - -See more tests in chrome/test/functional/omnibox.py. -""" - -import simplejson as json - -from pyauto_errors import JSONInterfaceError - - -class OmniboxInfo(object): - """Represent info for Chromium Omnibox. - - Info contains: - - a list of matches in the same order as you'd see in the omnibox, - - a dictionary of properties related to the omnibox. - - Sample info text: - - { u'matches': [ - { - u'contents': u'google', - u'description': u'Google Search', - u'destination_url': u'http://www.google.com/search?aq=f&' - 'sourceid=chrome&ie=UTF-8&q=google', - u'starred': False, - u'type': u'search-what-you-typed'}, - { - u'contents': u'maps.google.com/', - u'description': u'Google Maps', - u'destination_url': u'http://maps.google.com/', - u'starred': False, - u'type': u'navsuggest'}, - { u'contents': u'google maps', - u'description': u'', - u'destination_url': u'http://www.google.com/search?aq=0&oq=google&' - 'sourceid=chrome&ie=UTF-8&q=google+maps', - u'starred': False, - u'type': u'search-suggest'}, - { u'contents': u'google earth', - u'description': u'', - u'destination_url': u'http://www.google.com/search?aq=1&oq=google&' - 'sourceid=chrome&ie=UTF-8&q=google+earth', - u'starred': False, - u'type': u'search-suggest'}, - { u'contents': u'Search Google for <enter query>', - u'description': u'(Keyword: google.com)', - u'destination_url': u'', - u'starred': False, - u'type': u'search-other-engine'}], - - u'properties': { u'has_focus': True, - u'keyword': u'', - u'query_in_progress': False, - u'text': u'google'}} - """ - def __init__(self, omnibox_dict): - """Initialize a OmniboxInfo from a json string. - - Args: - omnibox_dict: returned by an IPC call for the command 'GetOmniboxInfo'. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - # JSON string prepared in GetOmniboxInfo() in automation_provider.cc - self.omniboxdict = omnibox_dict - if self.omniboxdict.has_key('error'): - raise JSONInterfaceError(self.omniboxdict['error']) - - def Matches(self): - """Get omnibox matches. - - Returns: - a list of omnibox match items. - """ - return self.omniboxdict.get('matches', []) - - def MatchesWithAttributes(self, attr_dict): - """Find all omnibox matches which match the attributes in |attr_dict|. - - Args: - attr_dict: a dictionary of attributes to be satisfied. - All attributes in the given dictionary should be satisfied. - example: - { 'destiantion_url': 'http://www.google.com/', - 'description': 'Google' } - - Returns: - a list of omnibox match items. - """ - out = [] - for item in self.Matches(): - matched = True - for key, val in attr_dict.iteritems(): - if not item.has_key(key) or item[key] != val: - matched = False - if matched: - out.append(item) - return out - - def Properties(self, key=None): - """Get the properties - - Args: - key: if specified, value for the given property is returned. - - Returns: - a dictionary of properties if no key is given, OR - value corresponding to a particular property if key is given - """ - all = self.omniboxdict.get('properties') - if not key: - return all - return all.get(key) - - def Text(self): - """Get the text in the omnibox. - - This need not be the same as the user-inputted text, since omnibox may - autocomplete some URLs, or the user may move omnibox popup selection - up/down. - """ - return self.Properties('text') - - def IsQueryInProgress(self): - """Determine if a query is in progress.""" - return self.Properties('query_in_progress') diff --git a/chrome/test/pyautolib/plugins_info.py b/chrome/test/pyautolib/plugins_info.py deleted file mode 100644 index 6f55472..0000000 --- a/chrome/test/pyautolib/plugins_info.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) 2011 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. - -"""Python representation for Chromium Plugins info. - -This is the info available at about:plugins. -Obtain one of these from PyUITestSuite::GetPluginsInfo() call. - -Example: -class MyTest(pyauto.PyUITest): - def testBasic(self): - info = self.GetPluginsInfo() # fetch plugins snapshot - print info.Plugins() - -See more examples in chrome/test/functional/plugins.py. -""" - -import simplejson as json - -from pyauto_errors import JSONInterfaceError - - -class PluginsInfo(object): - """Represent info for Chromium plugins. - - The info is represented as a list of dictionaries, one for each plugin. - """ - def __init__(self, plugins_dict): - """Initialize a PluginsInfo from a json string. - - Args: - plugins_dict: a dictionary returned by the automation command - 'GetPluginsInfo'. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - # JSON string prepared in GetPluginsInfo() in automation_provider.cc - self.pluginsdict = plugins_dict - if self.pluginsdict.has_key('error'): - raise JSONInterfaceError(self.pluginsdict['error']) - - def Plugins(self): - """Get plugins. - - Returns: - a list of plugins info - Sample: - [ { u'desc': u'Shockwave Flash 10.0 r45', - u'enabled': True, - u'mimeTypes': [ { u'description': u'Shockwave Flash', - u'fileExtensions': [u'swf'], - u'mimeType': u'application/x-shockwave-flash'}, - { u'description': u'FutureSplash Player', - u'fileExtensions': [u'spl'], - u'mimeType': u'application/futuresplash'}], - u'name': u'Shockwave Flash', - u'path': u'/Library/Internet Plug-Ins/Flash Player.plugin', - u'version': u'10.0.45.2'}, - { u'desc': u'Version 1.1.2.9282', - u'enabled': True, - u'mimeTypes': [ { u'description': u'Google voice and video chat', - u'fileExtensions': [u'googletalk'], - u'mimeType': u'application/googletalk'}], - u'name': u'Google Talk NPAPI Plugin', - u'path': u'/Library/Internet Plug-Ins/googletalkbrowserplugin.plugin', - u'version': u'1.1.2.9282'}, - ..., - ..., - ] - """ - return self.pluginsdict.get('plugins', []) - - def PluginForPath(self, path): - """Get plugin info for the given plugin path. - - Returns: - a dictionary of info for the plugin. - """ - got = filter(lambda x: x['path'] == path, self.Plugins()) - if not got: return None - return got[0] - - def PluginForName(self, name): - """Get plugin info for the given name. - - There might be several plugins with the same name. - - Args: - name: the name for which to look for. - - Returns: - a list of info dictionaries for each plugin found with the given name. - """ - return filter(lambda x: x['name'] == name, self.Plugins()) - - def FirstPluginForName(self, name): - """Get plugin info for the first plugin with the given name. - - This is useful in case there are multiple plugins for a name. - - Args: - name: the name for which to look for. - - Returns: - a plugin info dictionary - None, if not found - """ - all = self.PluginForName(name) - if not all: return None - return all[0] diff --git a/chrome/test/pyautolib/policy_base.py b/chrome/test/pyautolib/policy_base.py deleted file mode 100644 index 023dfca..0000000 --- a/chrome/test/pyautolib/policy_base.py +++ /dev/null @@ -1,586 +0,0 @@ -# Copyright (c) 2012 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. - -"""Base class for tests that need to update the policies enforced by Chrome. - -Subclasses can call SetUserPolicy (ChromeOS, Linux, Windows) and -SetDevicePolicy (ChromeOS only) with a dictionary of the policies to install. - -The current implementation depends on the platform. The implementations might -change in the future, but tests relying on the above calls will keep working. -""" - -# On ChromeOS, a mock DMServer is started and enterprise enrollment faked -# against it. The mock DMServer then serves user and device policy to Chrome. -# -# For this setup to work, the DNS, GAIA and TPM (if present) are mocked as well: -# -# * The mock DNS resolves all addresses to 127.0.0.1. This allows the mock GAIA -# to handle all login attempts. It also eliminates the impact of flaky network -# connections on tests. Beware though that no cloud services can be accessed -# due to this DNS redirect. -# -# * The mock GAIA permits login with arbitrary credentials and accepts any OAuth -# tokens sent to it for verification as valid. -# -# * When running on a real device, its TPM is disabled. If the TPM were enabled, -# enrollment could not be undone without a reboot. Disabling the TPM makes -# cryptohomed behave as if no TPM was present, allowing enrollment to be -# undone by removing the install attributes. -# -# To disable the TPM, 0 must be written to /sys/class/misc/tpm0/device/enabled. -# Since this file is not writeable, a tpmfs is mounted that shadows the file -# with a writeable copy. - -import json -import logging -import os -import subprocess - -import pyauto - -if pyauto.PyUITest.IsChromeOS(): - import sys - import warnings - - import pyauto_paths - - # Ignore deprecation warnings, they make our output more cluttered. - warnings.filterwarnings('ignore', category=DeprecationWarning) - - # Find the path to the pyproto and add it to sys.path. - # Prepend it so that google.protobuf is loaded from here. - for path in pyauto_paths.GetBuildDirs(): - p = os.path.join(path, 'pyproto') - if os.path.isdir(p): - sys.path = [p, os.path.join(p, 'chrome', 'browser', 'policy', - 'proto')] + sys.path - break - sys.path.append('/usr/local') # to import autotest libs. - - import dbus - import device_management_backend_pb2 as dm - import pyauto_utils - import string - import tempfile - import urllib - import urllib2 - import uuid - from autotest.cros import auth_server - from autotest.cros import constants - from autotest.cros import cros_ui - from autotest.cros import dns_server -elif pyauto.PyUITest.IsWin(): - import _winreg as winreg -elif pyauto.PyUITest.IsMac(): - import getpass - import plistlib - -# ASN.1 object identifier for PKCS#1/RSA. -PKCS1_RSA_OID = '\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01' - -TPM_SYSFS_PATH = '/sys/class/misc/tpm0' -TPM_SYSFS_ENABLED_FILE = os.path.join(TPM_SYSFS_PATH, 'device/enabled') - - -class PolicyTestBase(pyauto.PyUITest): - """A base class for tests that need to set up and modify policies. - - Subclasses can use the methods SetUserPolicy (ChromeOS, Linux, Windows) and - SetDevicePolicy (ChromeOS only) to set the policies seen by Chrome. - """ - - if pyauto.PyUITest.IsChromeOS(): - # TODO(bartfab): Extend the C++ wrapper that starts the mock DMServer so - # that an owner can be passed in. Without this, the server will assume that - # the owner is user@example.com and for consistency, so must we. - owner = 'user@example.com' - # Subclasses may override these credentials to fake enrollment into another - # mode or use different device and machine IDs. - mode = 'enterprise' - device_id = string.upper(str(uuid.uuid4())) - machine_id = 'CROSTEST' - - _auth_server = None - _dns_server = None - - def ShouldAutoLogin(self): - return False - - @staticmethod - def _Call(command, check=False): - """Invokes a subprocess and optionally asserts the return value is zero.""" - with open(os.devnull, 'w') as devnull: - if check: - return subprocess.check_call(command.split(' '), stdout=devnull) - else: - return subprocess.call(command.split(' '), stdout=devnull) - - def _WriteFile(self, path, content): - """Writes content to path, creating any intermediary directories.""" - if not os.path.exists(os.path.dirname(path)): - os.makedirs(os.path.dirname(path)) - f = open(path, 'w') - f.write(content) - f.close() - - def _GetTestServerPoliciesFilePath(self): - """Returns the path of the cloud policy configuration file.""" - assert self.IsChromeOS() - return os.path.join(self._temp_data_dir, 'device_management') - - def _GetHttpURLForDeviceManagement(self): - """Returns the URL at which the TestServer is serving user policy.""" - assert self.IsChromeOS() - return self._http_server.GetURL('device_management').spec() - - def _RemoveIfExists(self, filename): - """Removes a file if it exists.""" - if os.path.exists(filename): - os.remove(filename) - - def _StartSessionManagerAndChrome(self): - """Starts the session manager and Chrome. - - Requires that the session manager be stopped already. - """ - # Ugly hack: session manager will not spawn Chrome if this file exists. That - # is usually a good thing (to keep the automation channel open), but in this - # case we really want to restart chrome. PyUITest.setUp() will be called - # after session manager and chrome have restarted, and will setup the - # automation channel. - restore_magic_file = False - if os.path.exists(constants.DISABLE_BROWSER_RESTART_MAGIC_FILE): - logging.debug('DISABLE_BROWSER_RESTART_MAGIC_FILE found. ' - 'Removing temporarily for the next restart.') - restore_magic_file = True - os.remove(constants.DISABLE_BROWSER_RESTART_MAGIC_FILE) - assert not os.path.exists(constants.DISABLE_BROWSER_RESTART_MAGIC_FILE) - - logging.debug('Starting session manager again') - cros_ui.start() - - # cros_ui.start() waits for the login prompt to be visible, so Chrome has - # already started once it returns. - if restore_magic_file: - open(constants.DISABLE_BROWSER_RESTART_MAGIC_FILE, 'w').close() - assert os.path.exists(constants.DISABLE_BROWSER_RESTART_MAGIC_FILE) - - def _WritePolicyOnChromeOS(self): - """Updates the mock DMServer's input file with current policy.""" - assert self.IsChromeOS() - policy_dict = { - 'google/chromeos/device': self._device_policy, - 'google/chromeos/user': { - 'mandatory': self._user_policy, - 'recommended': {}, - }, - 'managed_users': ['*'], - } - self._WriteFile(self._GetTestServerPoliciesFilePath(), - json.dumps(policy_dict, sort_keys=True, indent=2) + '\n') - - @staticmethod - def _IsCryptohomedReadyOnChromeOS(): - """Checks whether cryptohomed is running and ready to accept DBus calls.""" - assert pyauto.PyUITest.IsChromeOS() - try: - bus = dbus.SystemBus() - proxy = bus.get_object('org.chromium.Cryptohome', - '/org/chromium/Cryptohome') - dbus.Interface(proxy, 'org.chromium.CryptohomeInterface') - except dbus.DBusException: - return False - return True - - def _ClearInstallAttributesOnChromeOS(self): - """Resets the install attributes.""" - assert self.IsChromeOS() - self._RemoveIfExists('/home/.shadow/install_attributes.pb') - self._Call('restart cryptohomed', check=True) - assert self.WaitUntil(self._IsCryptohomedReadyOnChromeOS) - - def _DMPostRequest(self, request_type, request, headers): - """Posts a request to the mock DMServer.""" - assert self.IsChromeOS() - url = self._GetHttpURLForDeviceManagement() - url += '?' + urllib.urlencode({ - 'deviceid': self.device_id, - 'oauth_token': 'dummy_oauth_token_that_is_not_checked_anyway', - 'request': request_type, - 'devicetype': 2, - 'apptype': 'Chrome', - 'agent': 'Chrome', - }) - response = dm.DeviceManagementResponse() - response.ParseFromString(urllib2.urlopen(urllib2.Request( - url, request.SerializeToString(), headers)).read()) - return response - - def _DMRegisterDevice(self): - """Registers with the mock DMServer and returns the DMToken.""" - assert self.IsChromeOS() - dm_request = dm.DeviceManagementRequest() - request = dm_request.register_request - request.type = dm.DeviceRegisterRequest.DEVICE - request.machine_id = self.machine_id - dm_response = self._DMPostRequest('register', dm_request, {}) - return dm_response.register_response.device_management_token - - def _DMFetchPolicy(self, dm_token): - """Fetches device policy from the mock DMServer.""" - assert self.IsChromeOS() - dm_request = dm.DeviceManagementRequest() - policy_request = dm_request.policy_request - request = policy_request.request.add() - request.policy_type = 'google/chromeos/device' - request.signature_type = dm.PolicyFetchRequest.SHA1_RSA - headers = {'Authorization': 'GoogleDMToken token=' + dm_token} - dm_response = self._DMPostRequest('policy', dm_request, headers) - response = dm_response.policy_response.response[0] - assert response.policy_data - assert response.policy_data_signature - assert response.new_public_key - return response - - def ExtraChromeFlags(self): - """Sets up Chrome to use cloud policies on ChromeOS.""" - flags = pyauto.PyUITest.ExtraChromeFlags(self) - if self.IsChromeOS(): - while '--skip-oauth-login' in flags: - flags.remove('--skip-oauth-login') - url = self._GetHttpURLForDeviceManagement() - flags.append('--device-management-url=' + url) - flags.append('--disable-sync') - return flags - - def _SetUpWithSessionManagerStopped(self): - """Sets up the test environment after stopping the session manager.""" - assert self.IsChromeOS() - logging.debug('Stopping session manager') - cros_ui.stop(allow_fail=True) - - # Start mock GAIA server. - self._auth_server = auth_server.GoogleAuthServer() - self._auth_server.run() - - # Disable TPM if present. - if os.path.exists(TPM_SYSFS_PATH): - self._Call('mount -t tmpfs -o size=1k tmpfs %s' - % os.path.realpath(TPM_SYSFS_PATH), check=True) - self._WriteFile(TPM_SYSFS_ENABLED_FILE, '0') - - # Clear install attributes and restart cryptohomed to pick up the change. - self._ClearInstallAttributesOnChromeOS() - - # Set install attributes to mock enterprise enrollment. - bus = dbus.SystemBus() - proxy = bus.get_object('org.chromium.Cryptohome', - '/org/chromium/Cryptohome') - install_attributes = { - 'enterprise.device_id': self.device_id, - 'enterprise.domain': string.split(self.owner, '@')[-1], - 'enterprise.mode': self.mode, - 'enterprise.owned': 'true', - 'enterprise.user': self.owner - } - interface = dbus.Interface(proxy, 'org.chromium.CryptohomeInterface') - for name, value in install_attributes.iteritems(): - interface.InstallAttributesSet(name, '%s\0' % value) - interface.InstallAttributesFinalize() - - # Start mock DNS server that redirects all traffic to 127.0.0.1. - self._dns_server = dns_server.LocalDns() - self._dns_server.run() - - # Start mock DMServer. - source_dir = os.path.normpath(pyauto_paths.GetSourceDir()) - self._temp_data_dir = tempfile.mkdtemp(dir=source_dir) - logging.debug('TestServer input path: %s' % self._temp_data_dir) - relative_temp_data_dir = os.path.basename(self._temp_data_dir) - self._http_server = self.StartHTTPServer(relative_temp_data_dir) - - # Initialize the policy served. - self._device_policy = {} - self._user_policy = {} - self._WritePolicyOnChromeOS() - - # Register with mock DMServer and retrieve initial device policy blob. - dm_token = self._DMRegisterDevice() - policy = self._DMFetchPolicy(dm_token) - - # Write the initial device policy blob. - self._WriteFile(constants.OWNER_KEY_FILE, policy.new_public_key) - self._WriteFile(constants.SIGNED_POLICY_FILE, policy.SerializeToString()) - - # Remove any existing vaults. - self.RemoveAllCryptohomeVaultsOnChromeOS() - - # Restart session manager and Chrome. - self._StartSessionManagerAndChrome() - - def _tearDownWithSessionManagerStopped(self): - """Resets the test environment after stopping the session manager.""" - assert self.IsChromeOS() - logging.debug('Stopping session manager') - cros_ui.stop(allow_fail=True) - - # Stop mock GAIA server. - if self._auth_server: - self._auth_server.stop() - - # Reenable TPM if present. - if os.path.exists(TPM_SYSFS_PATH): - self._Call('umount %s' % os.path.realpath(TPM_SYSFS_PATH)) - - # Clear install attributes and restart cryptohomed to pick up the change. - self._ClearInstallAttributesOnChromeOS() - - # Stop mock DNS server. - if self._dns_server: - self._dns_server.stop() - - # Stop mock DMServer. - self.StopHTTPServer(self._http_server) - - # Clear the policy served. - pyauto_utils.RemovePath(self._temp_data_dir) - - # Remove the device policy blob. - self._RemoveIfExists(constants.OWNER_KEY_FILE) - self._RemoveIfExists(constants.SIGNED_POLICY_FILE) - - # Remove any existing vaults. - self.RemoveAllCryptohomeVaultsOnChromeOS() - - # Restart session manager and Chrome. - self._StartSessionManagerAndChrome() - - def setUp(self): - """Sets up the platform for policy testing. - - On ChromeOS, part of the setup involves restarting the session manager to - inject an initial device policy blob. - """ - if self.IsChromeOS(): - # Perform the remainder of the setup with the device manager stopped. - try: - self.WaitForSessionManagerRestart( - self._SetUpWithSessionManagerStopped) - except: - # Destroy the non re-entrant services. - if self._auth_server: - self._auth_server.stop() - if self._dns_server: - self._dns_server.stop() - raise - - pyauto.PyUITest.setUp(self) - self._branding = self.GetBrowserInfo()['properties']['branding'] - - def tearDown(self): - """Cleans up the policies and related files created in tests.""" - if self.IsChromeOS(): - # Perform the cleanup with the device manager stopped. - self.WaitForSessionManagerRestart(self._tearDownWithSessionManagerStopped) - else: - # On other platforms, there is only user policy to clear. - self.SetUserPolicy(refresh=False) - - pyauto.PyUITest.tearDown(self) - - def LoginWithTestAccount(self, account='prod_enterprise_test_user'): - """Convenience method for logging in with one of the test accounts.""" - assert self.IsChromeOS() - credentials = self.GetPrivateInfo()[account] - self.Login(credentials['username'], credentials['password']) - assert self.GetLoginInfo()['is_logged_in'] - - def _GetCurrentLoginScreenId(self): - return self.ExecuteJavascriptInOOBEWebUI( - """window.domAutomationController.send( - String(cr.ui.Oobe.getInstance().currentScreen.id)); - """) - - def _WaitForLoginScreenId(self, id): - self.assertTrue( - self.WaitUntil(function=self._GetCurrentLoginScreenId, - expect_retval=id), - msg='Expected login screen "%s" to be visible.' % id) - - def _CheckLoginFormLoading(self): - return self.ExecuteJavascriptInOOBEWebUI( - """window.domAutomationController.send( - cr.ui.Oobe.getInstance().currentScreen.loading); - """) - - def PrepareToWaitForLoginFormReload(self): - self.assertEqual('gaia-signin', - self._GetCurrentLoginScreenId(), - msg='Expected the login form to be visible.') - self.assertTrue( - self.WaitUntil(function=self._CheckLoginFormLoading, - expect_retval=False), - msg='Expected the login form to finish loading.') - # Set up a sentinel variable that is false now and will toggle to true when - # the login form starts reloading. - self.ExecuteJavascriptInOOBEWebUI( - """var screen = cr.ui.Oobe.getInstance().currentScreen; - if (!('reload_started' in screen)) { - screen.orig_loadAuthExtension_ = screen.loadAuthExtension_; - screen.loadAuthExtension_ = function(data) { - this.orig_loadAuthExtension_(data); - if (this.loading) - this.reload_started = true; - } - } - screen.reload_started = false; - window.domAutomationController.send(true);""") - - def _CheckLoginFormReloaded(self): - return self.ExecuteJavascriptInOOBEWebUI( - """window.domAutomationController.send( - cr.ui.Oobe.getInstance().currentScreen.reload_started && - !cr.ui.Oobe.getInstance().currentScreen.loading); - """) - - def WaitForLoginFormReload(self): - self.assertEqual('gaia-signin', - self._GetCurrentLoginScreenId(), - msg='Expected the login form to be visible.') - self.assertTrue( - self.WaitUntil(function=self._CheckLoginFormReloaded), - msg='Expected the login form to finish reloading.') - - def _SetUserPolicyChromeOS(self, user_policy=None): - """Writes the given user policy to the mock DMServer's input file.""" - self._user_policy = user_policy or {} - self._WritePolicyOnChromeOS() - - def _SetUserPolicyWin(self, user_policy=None): - """Writes the given user policy to the Windows registry.""" - def SetValueEx(key, sub_key, value): - if isinstance(value, int): - winreg.SetValueEx(key, sub_key, 0, winreg.REG_DWORD, int(value)) - elif isinstance(value, basestring): - winreg.SetValueEx(key, sub_key, 0, winreg.REG_SZ, value.encode('ascii')) - elif isinstance(value, list): - k = winreg.CreateKey(key, sub_key) - for index, v in list(enumerate(value)): - SetValueEx(k, str(index + 1), v) - winreg.CloseKey(k) - else: - raise TypeError('Unsupported data type: "%s"' % value) - - assert self.IsWin() - if self._branding == 'Google Chrome': - reg_base = r'SOFTWARE\Policies\Google\Chrome' - else: - reg_base = r'SOFTWARE\Policies\Chromium' - - if subprocess.call( - r'reg query HKEY_LOCAL_MACHINE\%s' % reg_base) == 0: - logging.debug(r'Removing %s' % reg_base) - subprocess.call(r'reg delete HKLM\%s /f' % reg_base) - - if user_policy is not None: - root_key = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, reg_base) - for k, v in user_policy.iteritems(): - SetValueEx(root_key, k, v) - winreg.CloseKey(root_key) - - def _SetUserPolicyLinux(self, user_policy=None): - """Writes the given user policy to the JSON policy file read by Chrome.""" - assert self.IsLinux() - sudo_cmd_file = os.path.join(os.path.dirname(__file__), - 'policy_posix_util.py') - - if self._branding == 'Google Chrome': - policies_location_base = '/etc/opt/chrome' - else: - policies_location_base = '/etc/chromium' - - if os.path.exists(policies_location_base): - logging.debug('Removing directory %s' % policies_location_base) - subprocess.call(['suid-python', sudo_cmd_file, - 'remove_dir', policies_location_base]) - - if user_policy is not None: - self._WriteFile('/tmp/chrome.json', - json.dumps(user_policy, sort_keys=True, indent=2) + '\n') - - policies_location = '%s/policies/managed' % policies_location_base - subprocess.call(['suid-python', sudo_cmd_file, - 'setup_dir', policies_location]) - subprocess.call(['suid-python', sudo_cmd_file, - 'perm_dir', policies_location]) - # Copy chrome.json file to the managed directory - subprocess.call(['suid-python', sudo_cmd_file, - 'copy', '/tmp/chrome.json', policies_location]) - os.remove('/tmp/chrome.json') - - def _SetUserPolicyMac(self, user_policy=None): - """Writes the given user policy to the plist policy file read by Chrome.""" - assert self.IsMac() - sudo_cmd_file = os.path.join(os.path.dirname(__file__), - 'policy_posix_util.py') - - if self._branding == 'Google Chrome': - policies_file_base = 'com.google.Chrome.plist' - else: - policies_file_base = 'org.chromium.Chromium.plist' - - policies_location = os.path.join('/Library', 'Managed Preferences', - getpass.getuser()) - - if os.path.exists(policies_location): - logging.debug('Removing directory %s' % policies_location) - subprocess.call(['suid-python', sudo_cmd_file, - 'remove_dir', policies_location]) - - if user_policy is not None: - policies_tmp_file = os.path.join('/tmp', policies_file_base) - plistlib.writePlist(user_policy, policies_tmp_file) - subprocess.call(['suid-python', sudo_cmd_file, - 'setup_dir', policies_location]) - # Copy policy file to the managed directory - subprocess.call(['suid-python', sudo_cmd_file, - 'copy', policies_tmp_file, policies_location]) - os.remove(policies_tmp_file) - - def SetUserPolicy(self, user_policy=None, refresh=True): - """Sets the user policy provided as a dict. - - Args: - user_policy: The user policy to set. None clears it. - refresh: If True, Chrome will refresh and apply the new policy. - Requires Chrome to be alive for it. - """ - if self.IsChromeOS(): - self._SetUserPolicyChromeOS(user_policy=user_policy) - elif self.IsWin(): - self._SetUserPolicyWin(user_policy=user_policy) - elif self.IsLinux(): - self._SetUserPolicyLinux(user_policy=user_policy) - elif self.IsMac(): - self._SetUserPolicyMac(user_policy=user_policy) - else: - raise NotImplementedError('Not available on this platform.') - - if refresh: - self.RefreshPolicies() - - def SetDevicePolicy(self, device_policy=None, refresh=True): - """Sets the device policy provided as a dict. - - Args: - device_policy: The device policy to set. None clears it. - refresh: If True, Chrome will refresh and apply the new policy. - Requires Chrome to be alive for it. - """ - assert self.IsChromeOS() - self._device_policy = device_policy or {} - self._WritePolicyOnChromeOS() - if refresh: - self.RefreshPolicies() diff --git a/chrome/test/pyautolib/policy_posix_util.py b/chrome/test/pyautolib/policy_posix_util.py deleted file mode 100755 index 420131b..0000000 --- a/chrome/test/pyautolib/policy_posix_util.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Helper script to copy policy files into the correct location the machine.""" - -import os -import shutil -import sys - - -def main(): - assert os.geteuid() == 0, 'Need superuser privileges' - if sys.argv[1] == 'copy': - assert os.path.isdir(sys.argv[3]) - shutil.copy(sys.argv[2], sys.argv[3]) - dirList = os.listdir(sys.argv[3]) - for fname in dirList: - filename = os.path.join(sys.argv[3], fname) - os.chmod(filename, 0755) - elif sys.argv[1] == 'setup_dir': - os.makedirs(sys.argv[2]) - elif sys.argv[1] == 'perm_dir': - os.system('chmod -R 755 "%s/../.."' % sys.argv[2]) - elif sys.argv[1] == 'remove_dir': - os.system('rm -rf "%s"' % sys.argv[2]) - else: - print >>sys.stderr, ( - 'Invalid syntax. Possible values are [copy], [setup_dir], ' - '[perm_dir], [remove_dir]') - return 1 - return 0 - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/chrome/test/pyautolib/prefs_info.py b/chrome/test/pyautolib/prefs_info.py deleted file mode 100644 index c84998f..0000000 --- a/chrome/test/pyautolib/prefs_info.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright (c) 2011 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. - -"""Python representation for Chromium Preferences. - -Obtain one of these from a call to PyUITest::GetPrefsInfo() or -PyUITest::GetLocalStatePrefsInfo(). - -Example: -class MyTest(pyauto.PyUITest): - def testBasic(self): - info = self.GetPrefsInfo() # fetch prefs snapshot - print info.Prefs() # all prefs - print info.Prefs('session.restore_on_startup') # a single pref - -See more tests in chrome/test/functional/prefs.py. -""" - -import simplejson as json - -from pyauto_errors import JSONInterfaceError - - -class PrefsInfo(object): - """Represent info for Chromium preferences. - - The info is represented as a hierarchy of prefs values. - The values could be plain (integer, bool, float) or complex (like - dictionary, list). - """ - def __init__(self, prefs_dict): - """Initialize a PrefsInfo from a json string. - - Args: - prefs_dict: a dictionary as returned by the IPC command 'GetPrefsInfo'. - A typical dict representing prefs snapshot looks like: - { u'prefs': - { u'alternate_error_pages': {u'enabled': True}, - u'autofill': { u'auxiliary_profiles_enabled': False, - u'default_creditcard': u'', - u'default_profile': u'', - u'enabled': True, - u'infobar_shown': False, - u'negative_upload_rate': 0.01, - u'positive_upload_rate': 0.01}, - u'bookmark_bar': {u'show_on_all_tabs': False}, - ... - ... - } - } - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - # JSON string prepared in PrefsInfo() in automation_provider.cc - self.prefsdict = prefs_dict - if self.prefsdict.has_key('error'): - raise JSONInterfaceError(self.prefsdict['error']) - - def Prefs(self, path=None): - """Get preferences. - - The preference dictionary (when using path=None) looks like: - - { u'alternate_error_pages': {u'enabled': True}, - u'autofill': { u'auxiliary_profiles_enabled': False, - u'default_creditcard': u'', - u'default_profile': u'', - u'enabled': True, - u'infobar_shown': False, - u'negative_upload_rate': 0.01, - u'positive_upload_rate': 0.01}, - u'bookmark_bar': {u'show_on_all_tabs': False}, - ... - ... - } - - In this case, to fetch the preference value for autofill enabled, use - 'autofill.enabled' as the path. - - Args: - path: If specified, return the preference item for the given path. - path is a dot-separated string like "session.restore_on_startup". - One of the equivalent names in chrome/common/pref_names.h could - also be used. - - Returns: - preference value. It could be a dictionary (like the example above), a - list or a plain value. - None, if prefernece for path not found (if path is given). - """ - all = self.prefsdict.get('prefs', {}) - if not path: # No path given. Return all prefs. - return all - for part in path.split('.'): # Narrow down to the requested prefs path. - all = all.get(part) - if all is None: return None - return all diff --git a/chrome/test/pyautolib/pyauto.py b/chrome/test/pyautolib/pyauto.py deleted file mode 100755 index 4f255b1c..0000000 --- a/chrome/test/pyautolib/pyauto.py +++ /dev/null @@ -1,5426 +0,0 @@ -#!/usr/bin/env python -# Copyright 2013 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. - -"""PyAuto: Python Interface to Chromium's Automation Proxy. - -PyAuto uses swig to expose Automation Proxy interfaces to Python. -For complete documentation on the functionality available, -run pydoc on this file. - -Ref: http://dev.chromium.org/developers/testing/pyauto - - -Include the following in your PyAuto test script to make it run standalone. - -from pyauto import Main - -if __name__ == '__main__': - Main() - -This script can be used as an executable to fire off other scripts, similar -to unittest.py - python pyauto.py test_script -""" - -import cStringIO -import copy -import functools -import hashlib -import inspect -import logging -import optparse -import os -import pickle -import pprint -import re -import shutil -import signal -import socket -import stat -import string -import subprocess -import sys -import tempfile -import time -import types -import unittest -import urllib - -import pyauto_paths - - -def _LocateBinDirs(): - """Setup a few dirs where we expect to find dependency libraries.""" - deps_dirs = [ - os.path.dirname(__file__), - pyauto_paths.GetThirdPartyDir(), - os.path.join(pyauto_paths.GetThirdPartyDir(), 'webdriver', 'pylib'), - ] - sys.path += map(os.path.normpath, pyauto_paths.GetBuildDirs() + deps_dirs) - -_LocateBinDirs() - -_PYAUTO_DOC_URL = 'http://dev.chromium.org/developers/testing/pyauto' - -try: - import pyautolib - # Needed so that all additional classes (like: FilePath, GURL) exposed by - # swig interface get available in this module. - from pyautolib import * -except ImportError: - print >>sys.stderr, 'Could not locate pyautolib shared libraries. ' \ - 'Did you build?\n Documentation: %s' % _PYAUTO_DOC_URL - # Mac requires python2.5 even when not the default 'python' (e.g. 10.6) - if 'darwin' == sys.platform and sys.version_info[:2] != (2,5): - print >>sys.stderr, '*\n* Perhaps use "python2.5", not "python" ?\n*' - raise - -# Should go after sys.path is set appropriately -import bookmark_model -import download_info -import history_info -import omnibox_info -import plugins_info -import prefs_info -from pyauto_errors import AutomationCommandFail -from pyauto_errors import AutomationCommandTimeout -from pyauto_errors import JavascriptRuntimeError -from pyauto_errors import JSONInterfaceError -from pyauto_errors import NTPThumbnailNotShownError -import pyauto_utils -import simplejson as json # found in third_party - -_CHROME_DRIVER_FACTORY = None -_DEFAULT_AUTOMATION_TIMEOUT = 45 -_HTTP_SERVER = None -_REMOTE_PROXY = None -_OPTIONS = None -_BROWSER_PID = None - -class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): - """Base class for UI Test Cases in Python. - - A browser is created before executing each test, and is destroyed after - each test irrespective of whether the test passed or failed. - - You should derive from this class and create methods with 'test' prefix, - and use methods inherited from PyUITestBase (the C++ side). - - Example: - - class MyTest(PyUITest): - - def testNavigation(self): - self.NavigateToURL("http://www.google.com") - self.assertEqual("Google", self.GetActiveTabTitle()) - """ - - def __init__(self, methodName='runTest', **kwargs): - """Initialize PyUITest. - - When redefining __init__ in a derived class, make sure that: - o you make a call this __init__ - o __init__ takes methodName as an arg. this is mandated by unittest module - - Args: - methodName: the default method name. Internal use by unittest module - - (The rest of the args can be in any order. They can even be skipped in - which case the defaults will be used.) - - clear_profile: If True, clean the profile dir before use. Defaults to True - homepage: the home page. Defaults to "about:blank" - """ - # Fetch provided keyword args, or fill in defaults. - clear_profile = kwargs.get('clear_profile', True) - homepage = kwargs.get('homepage', 'about:blank') - self._automation_timeout = _DEFAULT_AUTOMATION_TIMEOUT * 1000 - - pyautolib.PyUITestBase.__init__(self, clear_profile, homepage) - self.Initialize(pyautolib.FilePath(self.BrowserPath())) - unittest.TestCase.__init__(self, methodName) - - # Give all pyauto tests easy access to pprint.PrettyPrinter functions. - self.pprint = pprint.pprint - self.pformat = pprint.pformat - - # Set up remote proxies, if they were requested. - self.remotes = [] - self.remote = None - global _REMOTE_PROXY - if _REMOTE_PROXY: - self.remotes = _REMOTE_PROXY - self.remote = _REMOTE_PROXY[0] - - def __del__(self): - pyautolib.PyUITestBase.__del__(self) - - def _SetExtraChromeFlags(self): - """Prepares the browser to launch with the specified extra Chrome flags. - - This function is called right before the browser is launched for the first - time. - """ - for flag in self.ExtraChromeFlags(): - if flag.startswith('--'): - flag = flag[2:] - split_pos = flag.find('=') - if split_pos >= 0: - flag_name = flag[:split_pos] - flag_val = flag[split_pos + 1:] - self.AppendBrowserLaunchSwitch(flag_name, flag_val) - else: - self.AppendBrowserLaunchSwitch(flag) - - def __SetUp(self): - named_channel_id = None - if _OPTIONS: - named_channel_id = _OPTIONS.channel_id - if self.IsChromeOS(): # Enable testing interface on ChromeOS. - if self.get_clear_profile(): - self.CleanupBrowserProfileOnChromeOS() - self.EnableCrashReportingOnChromeOS() - if not named_channel_id: - named_channel_id = self.EnableChromeTestingOnChromeOS() - else: - self._SetExtraChromeFlags() # Flags already previously set for ChromeOS. - if named_channel_id: - self._named_channel_id = named_channel_id - self.UseNamedChannelID(named_channel_id) - # Initialize automation and fire the browser (does not fire the browser - # on ChromeOS). - self.SetUp() - - global _BROWSER_PID - try: - _BROWSER_PID = self.GetBrowserInfo()['browser_pid'] - except JSONInterfaceError: - raise JSONInterfaceError('Unable to get browser_pid over automation ' - 'channel on first attempt. Something went very ' - 'wrong. Chrome probably did not launch.') - - # Forcibly trigger all plugins to get registered. crbug.com/94123 - # Sometimes flash files loaded too quickly after firing browser - # ends up getting downloaded, which seems to indicate that the plugin - # hasn't been registered yet. - if not self.IsChromeOS(): - self.GetPluginsInfo() - - if (self.IsChromeOS() and not self.GetLoginInfo()['is_logged_in'] and - self.ShouldOOBESkipToLogin()): - if self.GetOOBEScreenInfo()['screen_name'] != 'login': - self.SkipToLogin() - if self.ShouldAutoLogin(): - # Login with default creds. - sys.path.append('/usr/local') # to import autotest libs - from autotest.cros import constants - creds = constants.CREDENTIALS['$default'] - self.Login(creds[0], creds[1]) - assert self.GetLoginInfo()['is_logged_in'] - logging.info('Logged in as %s.' % creds[0]) - - # If we are connected to any RemoteHosts, create PyAuto - # instances on the remote sides and set them up too. - for remote in self.remotes: - remote.CreateTarget(self) - remote.setUp() - - def setUp(self): - """Override this method to launch browser differently. - - Can be used to prevent launching the browser window by default in case a - test wants to do some additional setup before firing browser. - - When using the named interface, it connects to an existing browser - instance. - - On ChromeOS, a browser showing the login window is started. Tests can - initiate a user session by calling Login() or LoginAsGuest(). Cryptohome - vaults or flimflam profiles left over by previous tests can be cleared by - calling RemoveAllCryptohomeVaults() respectively CleanFlimflamDirs() before - logging in to improve isolation. Note that clearing flimflam profiles - requires a flimflam restart, briefly taking down network connectivity and - slowing down the test. This should be done for tests that use flimflam only. - """ - self.__SetUp() - - def tearDown(self): - for remote in self.remotes: - remote.tearDown() - - self.TearDown() # Destroy browser - - # Method required by the Python standard library unittest.TestCase. - def runTest(self): - pass - - @staticmethod - def BrowserPath(): - """Returns the path to Chromium binaries. - - Expects the browser binaries to be in the - same location as the pyautolib binaries. - """ - return os.path.normpath(os.path.dirname(pyautolib.__file__)) - - def ExtraChromeFlags(self): - """Return a list of extra chrome flags to use with Chrome for testing. - - These are flags needed to facilitate testing. Override this function to - use a custom set of Chrome flags. - """ - auth_ext_path = ('/usr/local/autotest/deps/pyauto_dep/' + - 'test_src/chrome/browser/resources/gaia_auth') - if self.IsChromeOS(): - return [ - '--homepage=about:blank', - '--allow-file-access', - '--allow-file-access-from-files', - '--enable-file-cookies', - '--disable-default-apps', - '--dom-automation', - '--skip-oauth-login', - # Enables injection of test content script for webui login automation - '--auth-ext-path=%s' % auth_ext_path, - # Enable automation provider, chromeos net and chromeos login logs - '--vmodule=*/browser/automation/*=2,*/chromeos/net/*=2,' + - '*/chromeos/login/*=2', - ] - else: - return [] - - def ShouldOOBESkipToLogin(self): - """Determine if we should skip the OOBE flow on ChromeOS. - - This makes automation skip the OOBE flow during setUp() and land directly - to the login screen. Applies only if not logged in already. - - Override and return False if OOBE flow is required, for OOBE tests, for - example. Calling this function directly will have no effect. - - Returns: - True, if the OOBE should be skipped and automation should - go to the 'Add user' login screen directly - False, if the OOBE should not be skipped. - """ - assert self.IsChromeOS() - return True - - def ShouldAutoLogin(self): - """Determine if we should auto-login on ChromeOS at browser startup. - - To be used for tests that expect user to be logged in before running test, - without caring which user. ShouldOOBESkipToLogin() should return True - for this to take effect. - - Override and return False to not auto login, for tests where login is part - of the use case. - - Returns: - True, if chrome should auto login after startup. - False, otherwise. - """ - assert self.IsChromeOS() - return True - - def CloseChromeOnChromeOS(self): - """Gracefully exit chrome on ChromeOS.""" - - def _GetListOfChromePids(): - """Retrieves the list of currently-running Chrome process IDs. - - Returns: - A list of strings, where each string represents a currently-running - 'chrome' process ID. - """ - proc = subprocess.Popen(['pgrep', '^chrome$'], stdout=subprocess.PIPE) - proc.wait() - return [x.strip() for x in proc.stdout.readlines()] - - orig_pids = _GetListOfChromePids() - subprocess.call(['pkill', '^chrome$']) - - def _AreOrigPidsDead(orig_pids): - """Determines whether all originally-running 'chrome' processes are dead. - - Args: - orig_pids: A list of strings, where each string represents the PID for - an originally-running 'chrome' process. - - Returns: - True, if all originally-running 'chrome' processes have been killed, or - False otherwise. - """ - for new_pid in _GetListOfChromePids(): - if new_pid in orig_pids: - return False - return True - - self.WaitUntil(lambda: _AreOrigPidsDead(orig_pids)) - - @staticmethod - def _IsRootSuid(path): - """Determine if |path| is a suid-root file.""" - return os.path.isfile(path) and (os.stat(path).st_mode & stat.S_ISUID) - - @staticmethod - def SuidPythonPath(): - """Path to suid_python binary on ChromeOS. - - This is typically in the same directory as pyautolib.py - """ - return os.path.join(PyUITest.BrowserPath(), 'suid-python') - - @staticmethod - def RunSuperuserActionOnChromeOS(action): - """Run the given action with superuser privs (on ChromeOS). - - Uses the suid_actions.py script. - - Args: - action: An action to perform. - See suid_actions.py for available options. - - Returns: - (stdout, stderr) - """ - assert PyUITest._IsRootSuid(PyUITest.SuidPythonPath()), \ - 'Did not find suid-root python at %s' % PyUITest.SuidPythonPath() - file_path = os.path.join(os.path.dirname(__file__), 'chromeos', - 'suid_actions.py') - args = [PyUITest.SuidPythonPath(), file_path, '--action=%s' % action] - proc = subprocess.Popen( - args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = proc.communicate() - return (stdout, stderr) - - def EnableChromeTestingOnChromeOS(self): - """Enables the named automation interface on chromeos. - - Restarts chrome so that you get a fresh instance. - Also sets some testing-friendly flags for chrome. - - Expects suid python to be present in the same dir as pyautolib.py - """ - assert PyUITest._IsRootSuid(self.SuidPythonPath()), \ - 'Did not find suid-root python at %s' % self.SuidPythonPath() - file_path = os.path.join(os.path.dirname(__file__), 'chromeos', - 'enable_testing.py') - args = [self.SuidPythonPath(), file_path] - # Pass extra chrome flags for testing - for flag in self.ExtraChromeFlags(): - args.append('--extra-chrome-flags=%s' % flag) - assert self.WaitUntil(lambda: self._IsSessionManagerReady(0)) - proc = subprocess.Popen(args, stdout=subprocess.PIPE) - automation_channel_path = proc.communicate()[0].strip() - assert len(automation_channel_path), 'Could not enable testing interface' - return automation_channel_path - - @staticmethod - def EnableCrashReportingOnChromeOS(): - """Enables crash reporting on ChromeOS. - - Writes the "/home/chronos/Consent To Send Stats" file with a 32-char - readable string. See comment in session_manager_setup.sh which does this - too. - - Note that crash reporting will work only if breakpad is built in, ie in a - 'Google Chrome' build (not Chromium). - """ - consent_file = '/home/chronos/Consent To Send Stats' - def _HasValidConsentFile(): - if not os.path.isfile(consent_file): - return False - stat = os.stat(consent_file) - return (len(open(consent_file).read()) and - (1000, 1000) == (stat.st_uid, stat.st_gid)) - if not _HasValidConsentFile(): - client_id = hashlib.md5('abcdefgh').hexdigest() - # Consent file creation and chown to chronos needs to be atomic - # to avoid races with the session_manager. crosbug.com/18413 - # Therefore, create a temp file, chown, then rename it as consent file. - temp_file = consent_file + '.tmp' - open(temp_file, 'w').write(client_id) - # This file must be owned by chronos:chronos! - os.chown(temp_file, 1000, 1000); - shutil.move(temp_file, consent_file) - assert _HasValidConsentFile(), 'Could not create %s' % consent_file - - @staticmethod - def _IsSessionManagerReady(old_pid): - """Is the ChromeOS session_manager running and ready to accept DBus calls? - - Called after session_manager is killed to know when it has restarted. - - Args: - old_pid: The pid that session_manager had before it was killed, - to ensure that we don't look at the DBus interface - of an old session_manager process. - """ - pgrep_process = subprocess.Popen(['pgrep', 'session_manager'], - stdout=subprocess.PIPE) - new_pid = pgrep_process.communicate()[0].strip() - if not new_pid or old_pid == new_pid: - return False - - import dbus - try: - bus = dbus.SystemBus() - proxy = bus.get_object('org.chromium.SessionManager', - '/org/chromium/SessionManager') - dbus.Interface(proxy, 'org.chromium.SessionManagerInterface') - except dbus.DBusException: - return False - return True - - @staticmethod - def CleanupBrowserProfileOnChromeOS(): - """Cleanup browser profile dir on ChromeOS. - - This does not clear cryptohome. - - Browser should not be running, or else there will be locked files. - """ - profile_dir = '/home/chronos/user' - for item in os.listdir(profile_dir): - # Deleting .pki causes stateful partition to get erased. - if item not in ['log', 'flimflam'] and not item.startswith('.'): - pyauto_utils.RemovePath(os.path.join(profile_dir, item)) - - chronos_dir = '/home/chronos' - for item in os.listdir(chronos_dir): - if item != 'user' and not item.startswith('.'): - pyauto_utils.RemovePath(os.path.join(chronos_dir, item)) - - @staticmethod - def CleanupFlimflamDirsOnChromeOS(): - """Clean the contents of flimflam profiles and restart flimflam.""" - PyUITest.RunSuperuserActionOnChromeOS('CleanFlimflamDirs') - - @staticmethod - def RemoveAllCryptohomeVaultsOnChromeOS(): - """Remove any existing cryptohome vaults.""" - PyUITest.RunSuperuserActionOnChromeOS('RemoveAllCryptohomeVaults') - - @staticmethod - def _IsInodeNew(path, old_inode): - """Determine whether an inode has changed. POSIX only. - - Args: - path: The file path to check for changes. - old_inode: The old inode number. - - Returns: - True if the path exists and its inode number is different from old_inode. - False otherwise. - """ - try: - stat_result = os.stat(path) - except OSError: - return False - if not stat_result: - return False - return stat_result.st_ino != old_inode - - def RestartBrowser(self, clear_profile=True, pre_launch_hook=None): - """Restart the browser. - - For use with tests that require to restart the browser. - - Args: - clear_profile: If True, the browser profile is cleared before restart. - Defaults to True, that is restarts browser with a clean - profile. - pre_launch_hook: If specified, must be a callable that is invoked before - the browser is started again. Not supported in ChromeOS. - """ - if self.IsChromeOS(): - assert pre_launch_hook is None, 'Not supported in ChromeOS' - self.TearDown() - if clear_profile: - self.CleanupBrowserProfileOnChromeOS() - self.CloseChromeOnChromeOS() - self.EnableChromeTestingOnChromeOS() - self.SetUp() - return - # Not chromeos - orig_clear_state = self.get_clear_profile() - self.CloseBrowserAndServer() - self.set_clear_profile(clear_profile) - if pre_launch_hook: - pre_launch_hook() - logging.debug('Restarting browser with clear_profile=%s', - self.get_clear_profile()) - self.LaunchBrowserAndServer() - self.set_clear_profile(orig_clear_state) # Reset to original state. - - @staticmethod - def DataDir(): - """Returns the path to the data dir chrome/test/data.""" - return os.path.normpath( - os.path.join(os.path.dirname(__file__), os.pardir, "data")) - - @staticmethod - def ChromeOSDataDir(): - """Returns the path to the data dir chromeos/test/data.""" - return os.path.normpath( - os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, - "chromeos", "test", "data")) - - @staticmethod - def GetFileURLForPath(*path): - """Get file:// url for the given path. - - Also quotes the url using urllib.quote(). - - Args: - path: Variable number of strings that can be joined. - """ - path_str = os.path.join(*path) - abs_path = os.path.abspath(path_str) - if sys.platform == 'win32': - # Don't quote the ':' in drive letter ( say, C: ) on win. - # Also, replace '\' with '/' as expected in a file:/// url. - drive, rest = os.path.splitdrive(abs_path) - quoted_path = drive.upper() + urllib.quote((rest.replace('\\', '/'))) - return 'file:///' + quoted_path - else: - quoted_path = urllib.quote(abs_path) - return 'file://' + quoted_path - - @staticmethod - def GetFileURLForDataPath(*relative_path): - """Get file:// url for the given path relative to the chrome test data dir. - - Also quotes the url using urllib.quote(). - - Args: - relative_path: Variable number of strings that can be joined. - """ - return PyUITest.GetFileURLForPath(PyUITest.DataDir(), *relative_path) - - @staticmethod - def GetHttpURLForDataPath(*relative_path): - """Get http:// url for the given path in the data dir. - - The URL will be usable only after starting the http server. - """ - global _HTTP_SERVER - assert _HTTP_SERVER, 'HTTP Server not yet started' - return _HTTP_SERVER.GetURL(os.path.join('files', *relative_path)).spec() - - @staticmethod - def ContentDataDir(): - """Get path to content/test/data.""" - return os.path.join(PyUITest.DataDir(), os.pardir, os.pardir, os.pardir, - 'content', 'test', 'data') - - @staticmethod - def GetFileURLForContentDataPath(*relative_path): - """Get file:// url for the given path relative to content test data dir. - - Also quotes the url using urllib.quote(). - - Args: - relative_path: Variable number of strings that can be joined. - """ - return PyUITest.GetFileURLForPath(PyUITest.ContentDataDir(), *relative_path) - - @staticmethod - def GetFtpURLForDataPath(ftp_server, *relative_path): - """Get ftp:// url for the given path in the data dir. - - Args: - ftp_server: handle to ftp server, an instance of SpawnedTestServer - relative_path: any number of path elements - - The URL will be usable only after starting the ftp server. - """ - assert ftp_server, 'FTP Server not yet started' - return ftp_server.GetURL(os.path.join(*relative_path)).spec() - - @staticmethod - def IsMac(): - """Are we on Mac?""" - return 'darwin' == sys.platform - - @staticmethod - def IsLinux(): - """Are we on Linux? ChromeOS is linux too.""" - return sys.platform.startswith('linux') - - @staticmethod - def IsWin(): - """Are we on Win?""" - return 'win32' == sys.platform - - @staticmethod - def IsWin7(): - """Are we on Windows 7?""" - if not PyUITest.IsWin(): - return False - ver = sys.getwindowsversion() - return (ver[3], ver[0], ver[1]) == (2, 6, 1) - - @staticmethod - def IsWinVista(): - """Are we on Windows Vista?""" - if not PyUITest.IsWin(): - return False - ver = sys.getwindowsversion() - return (ver[3], ver[0], ver[1]) == (2, 6, 0) - - @staticmethod - def IsWinXP(): - """Are we on Windows XP?""" - if not PyUITest.IsWin(): - return False - ver = sys.getwindowsversion() - return (ver[3], ver[0], ver[1]) == (2, 5, 1) - - @staticmethod - def IsChromeOS(): - """Are we on ChromeOS (or Chromium OS)? - - Checks for "CHROMEOS_RELEASE_NAME=" in /etc/lsb-release. - """ - lsb_release = '/etc/lsb-release' - if not PyUITest.IsLinux() or not os.path.isfile(lsb_release): - return False - for line in open(lsb_release).readlines(): - if line.startswith('CHROMEOS_RELEASE_NAME='): - return True - return False - - @staticmethod - def IsPosix(): - """Are we on Mac/Linux?""" - return PyUITest.IsMac() or PyUITest.IsLinux() - - @staticmethod - def IsEnUS(): - """Are we en-US?""" - # TODO: figure out the machine's langugage. - return True - - @staticmethod - def GetPlatform(): - """Return the platform name.""" - # Since ChromeOS is also Linux, we check for it first. - if PyUITest.IsChromeOS(): - return 'chromeos' - elif PyUITest.IsLinux(): - return 'linux' - elif PyUITest.IsMac(): - return 'mac' - elif PyUITest.IsWin(): - return 'win' - else: - return 'unknown' - - @staticmethod - def EvalDataFrom(filename): - """Return eval of python code from given file. - - The datastructure used in the file will be preserved. - """ - data_file = os.path.join(filename) - contents = open(data_file).read() - try: - ret = eval(contents) - except: - print >>sys.stderr, '%s is an invalid data file.' % data_file - raise - return ret - - @staticmethod - def ChromeOSBoard(): - """What is the ChromeOS board name""" - if PyUITest.IsChromeOS(): - for line in open('/etc/lsb-release'): - line = line.strip() - if line.startswith('CHROMEOS_RELEASE_BOARD='): - return line.split('=')[1] - return None - - @staticmethod - def Kill(pid): - """Terminate the given pid. - - If the pid refers to a renderer, use KillRendererProcess instead. - """ - if PyUITest.IsWin(): - subprocess.call(['taskkill.exe', '/T', '/F', '/PID', str(pid)]) - else: - os.kill(pid, signal.SIGTERM) - - @staticmethod - def GetPrivateInfo(): - """Fetch info from private_tests_info.txt in private dir. - - Returns: - a dictionary of items from private_tests_info.txt - """ - private_file = os.path.join( - PyUITest.DataDir(), 'pyauto_private', 'private_tests_info.txt') - assert os.path.exists(private_file), '%s missing' % private_file - return PyUITest.EvalDataFrom(private_file) - - def WaitUntil(self, function, timeout=-1, retry_sleep=0.25, args=[], - expect_retval=None, return_retval=False, debug=True): - """Poll on a condition until timeout. - - Waits until the |function| evalues to |expect_retval| or until |timeout| - secs, whichever occurs earlier. - - This is better than using a sleep, since it waits (almost) only as much - as needed. - - WARNING: This method call should be avoided as far as possible in favor - of a real wait from chromium (like wait-until-page-loaded). - Only use in case there's really no better option. - - EXAMPLES:- - Wait for "file.txt" to get created: - WaitUntil(os.path.exists, args=["file.txt"]) - - Same as above, but using lambda: - WaitUntil(lambda: os.path.exists("file.txt")) - - Args: - function: the function whose truth value is to be evaluated - timeout: the max timeout (in secs) for which to wait. The default - action is to wait for kWaitForActionMaxMsec, as set in - ui_test.cc - Use None to wait indefinitely. - retry_sleep: the sleep interval (in secs) before retrying |function|. - Defaults to 0.25 secs. - args: the args to pass to |function| - expect_retval: the expected return value for |function|. This forms the - exit criteria. In case this is None (the default), - |function|'s return value is checked for truth, - so 'non-empty-string' should match with True - return_retval: If True, return the value returned by the last call to - |function()| - debug: if True, displays debug info at each retry. - - Returns: - The return value of the |function| (when return_retval == True) - True, if returning when |function| evaluated to True (when - return_retval == False) - False, when returning due to timeout - """ - if timeout == -1: # Default - timeout = self._automation_timeout / 1000.0 - assert callable(function), "function should be a callable" - begin = time.time() - debug_begin = begin - retval = None - while timeout is None or time.time() - begin <= timeout: - retval = function(*args) - if (expect_retval is None and retval) or \ - (expect_retval is not None and expect_retval == retval): - return retval if return_retval else True - if debug and time.time() - debug_begin > 5: - debug_begin += 5 - if function.func_name == (lambda: True).func_name: - function_info = inspect.getsource(function).strip() - else: - function_info = '%s()' % function.func_name - logging.debug('WaitUntil(%s:%d %s) still waiting. ' - 'Expecting %s. Last returned %s.', - os.path.basename(inspect.getsourcefile(function)), - inspect.getsourcelines(function)[1], - function_info, - True if expect_retval is None else expect_retval, - retval) - time.sleep(retry_sleep) - return retval if return_retval else False - - def StartFTPServer(self, data_dir): - """Start a local file server hosting data files over ftp:// - - Args: - data_dir: path where ftp files should be served - - Returns: - handle to FTP Server, an instance of SpawnedTestServer - """ - ftp_server = pyautolib.SpawnedTestServer( - pyautolib.SpawnedTestServer.TYPE_FTP, - '127.0.0.1', - pyautolib.FilePath(data_dir)) - assert ftp_server.Start(), 'Could not start ftp server' - logging.debug('Started ftp server at "%s".', data_dir) - return ftp_server - - def StopFTPServer(self, ftp_server): - """Stop the local ftp server.""" - assert ftp_server, 'FTP Server not yet started' - assert ftp_server.Stop(), 'Could not stop ftp server' - logging.debug('Stopped ftp server.') - - def StartHTTPServer(self, data_dir): - """Starts a local HTTP SpawnedTestServer serving files from |data_dir|. - - Args: - data_dir: path where the SpawnedTestServer should serve files from. - This will be appended to the source dir to get the final document root. - - Returns: - handle to the HTTP SpawnedTestServer - """ - http_server = pyautolib.SpawnedTestServer( - pyautolib.SpawnedTestServer.TYPE_HTTP, - '127.0.0.1', - pyautolib.FilePath(data_dir)) - assert http_server.Start(), 'Could not start HTTP server' - logging.debug('Started HTTP server at "%s".', data_dir) - return http_server - - def StopHTTPServer(self, http_server): - assert http_server, 'HTTP server not yet started' - assert http_server.Stop(), 'Cloud not stop the HTTP server' - logging.debug('Stopped HTTP server.') - - def StartHttpsServer(self, cert_type, data_dir): - """Starts a local HTTPS SpawnedTestServer serving files from |data_dir|. - - Args: - cert_type: An instance of SSLOptions.ServerCertificate for three - certificate types: ok, expired, or mismatch. - data_dir: The path where SpawnedTestServer should serve files from. - This is appended to the source dir to get the final - document root. - - Returns: - Handle to the HTTPS SpawnedTestServer - """ - https_server = pyautolib.SpawnedTestServer( - pyautolib.SpawnedTestServer.TYPE_HTTPS, - pyautolib.SSLOptions(cert_type), - pyautolib.FilePath(data_dir)) - assert https_server.Start(), 'Could not start HTTPS server.' - logging.debug('Start HTTPS server at "%s".' % data_dir) - return https_server - - def StopHttpsServer(self, https_server): - assert https_server, 'HTTPS server not yet started.' - assert https_server.Stop(), 'Could not stop the HTTPS server.' - logging.debug('Stopped HTTPS server.') - - class ActionTimeoutChanger(object): - """Facilitate temporary changes to PyAuto command timeout. - - Automatically resets to original timeout when object is destroyed. - """ - _saved_timeout = -1 # Saved timeout value - - def __init__(self, ui_test, new_timeout): - """Initialize. - - Args: - ui_test: a PyUITest object - new_timeout: new timeout to use (in milli secs) - """ - self._saved_timeout = ui_test._automation_timeout - ui_test._automation_timeout = new_timeout - self._ui_test = ui_test - - def __del__(self): - """Reset command_execution_timeout_ms to original value.""" - self._ui_test._automation_timeout = self._saved_timeout - - class JavascriptExecutor(object): - """Abstract base class for JavaScript injection. - - Derived classes should override Execute method.""" - def Execute(self, script): - pass - - class JavascriptExecutorInTab(JavascriptExecutor): - """Wrapper for injecting JavaScript in a tab.""" - def __init__(self, ui_test, tab_index=0, windex=0, frame_xpath=''): - """Initialize. - - Refer to ExecuteJavascript() for the complete argument list - description. - - Args: - ui_test: a PyUITest object - """ - self._ui_test = ui_test - self.windex = windex - self.tab_index = tab_index - self.frame_xpath = frame_xpath - - def Execute(self, script): - """Execute script in the tab.""" - return self._ui_test.ExecuteJavascript(script, - self.tab_index, - self.windex, - self.frame_xpath) - - class JavascriptExecutorInRenderView(JavascriptExecutor): - """Wrapper for injecting JavaScript in an extension view.""" - def __init__(self, ui_test, view, frame_xpath=''): - """Initialize. - - Refer to ExecuteJavascriptInRenderView() for the complete argument list - description. - - Args: - ui_test: a PyUITest object - """ - self._ui_test = ui_test - self.view = view - self.frame_xpath = frame_xpath - - def Execute(self, script): - """Execute script in the render view.""" - return self._ui_test.ExecuteJavascriptInRenderView(script, - self.view, - self.frame_xpath) - - def _GetResultFromJSONRequestDiagnostics(self): - """Same as _GetResultFromJSONRequest without throwing a timeout exception. - - This method is used to diagnose if a command returns without causing a - timout exception to be thrown. This should be used for debugging purposes - only. - - Returns: - True if the request returned; False if it timed out. - """ - result = self._SendJSONRequest(-1, - json.dumps({'command': 'GetBrowserInfo',}), - self._automation_timeout) - if not result: - # The diagnostic command did not complete, Chrome is probably in a bad - # state - return False - return True - - def _GetResultFromJSONRequest(self, cmd_dict, windex=0, timeout=-1): - """Issue call over the JSON automation channel and fetch output. - - This method packages the given dictionary into a json string, sends it - over the JSON automation channel, loads the json output string returned, - and returns it back as a dictionary. - - Args: - cmd_dict: the command dictionary. It must have a 'command' key - Sample: - { - 'command': 'SetOmniboxText', - 'text': text, - } - windex: 0-based window index on which to work. Default: 0 (first window) - Use -ve windex or None if the automation command does not apply - to a browser window. Example: for chromeos login - - timeout: request timeout (in milliseconds) - - Returns: - a dictionary for the output returned by the automation channel. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - if timeout == -1: # Default - timeout = self._automation_timeout - if windex is None: # Do not target any window - windex = -1 - result = self._SendJSONRequest(windex, json.dumps(cmd_dict), timeout) - if not result: - additional_info = 'No information available.' - # Windows does not support os.kill until Python 2.7. - if not self.IsWin() and _BROWSER_PID: - browser_pid_exists = True - # Does the browser PID exist? - try: - # Does not actually kill the process - os.kill(int(_BROWSER_PID), 0) - except OSError: - browser_pid_exists = False - if browser_pid_exists: - if self._GetResultFromJSONRequestDiagnostics(): - # Browser info, worked, that means this hook had a problem - additional_info = ('The browser process ID %d still exists. ' - 'PyAuto was able to obtain browser info. It ' - 'is possible this hook is broken.' - % _BROWSER_PID) - else: - additional_info = ('The browser process ID %d still exists. ' - 'PyAuto was not able to obtain browser info. ' - 'It is possible the browser is hung.' - % _BROWSER_PID) - else: - additional_info = ('The browser process ID %d no longer exists. ' - 'Perhaps the browser crashed.' % _BROWSER_PID) - elif not _BROWSER_PID: - additional_info = ('The browser PID was not obtained. Does this test ' - 'have a unique startup configuration?') - # Mask private data if it is in the JSON dictionary - cmd_dict_copy = copy.copy(cmd_dict) - if 'password' in cmd_dict_copy.keys(): - cmd_dict_copy['password'] = '**********' - if 'username' in cmd_dict_copy.keys(): - cmd_dict_copy['username'] = 'removed_username' - raise JSONInterfaceError('Automation call %s received empty response. ' - 'Additional information:\n%s' % (cmd_dict_copy, - additional_info)) - ret_dict = json.loads(result) - if ret_dict.has_key('error'): - if ret_dict.get('is_interface_timeout'): - raise AutomationCommandTimeout(ret_dict['error']) - elif ret_dict.get('is_interface_error'): - raise JSONInterfaceError(ret_dict['error']) - else: - raise AutomationCommandFail(ret_dict['error']) - return ret_dict - - def NavigateToURL(self, url, windex=0, tab_index=None, navigation_count=1): - """Navigate the given tab to the given URL. - - Note that this method also activates the corresponding tab/window if it's - not active already. Blocks until |navigation_count| navigations have - completed. - - Args: - url: The URL to which to navigate, can be a string or GURL object. - windex: The index of the browser window to work on. Defaults to the first - window. - tab_index: The index of the tab to work on. Defaults to the active tab. - navigation_count: the number of navigations to wait for. Defaults to 1. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - if isinstance(url, GURL): - url = url.spec() - if tab_index is None: - tab_index = self.GetActiveTabIndex(windex) - cmd_dict = { - 'command': 'NavigateToURL', - 'url': url, - 'windex': windex, - 'tab_index': tab_index, - 'navigation_count': navigation_count, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def NavigateToURLAsync(self, url, windex=0, tab_index=None): - """Initiate a URL navigation. - - A wrapper for NavigateToURL with navigation_count set to 0. - """ - self.NavigateToURL(url, windex, tab_index, 0) - - def ApplyAccelerator(self, accelerator, windex=0): - """Apply the accelerator with the given id. - - Note that this method schedules the accelerator, but does not wait for it to - actually finish doing anything. - - Args: - accelerator: The accelerator id, IDC_BACK, IDC_NEWTAB, etc. The list of - ids can be found at chrome/app/chrome_command_ids.h. - windex: The index of the browser window to work on. Defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - - cmd_dict = { - 'command': 'ApplyAccelerator', - 'accelerator': accelerator, - 'windex': windex, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def RunCommand(self, accelerator, windex=0): - """Apply the accelerator with the given id and wait for it to finish. - - This is like ApplyAccelerator except that it waits for the command to finish - executing. - - Args: - accelerator: The accelerator id. The list of ids can be found at - chrome/app/chrome_command_ids.h. - windex: The index of the browser window to work on. Defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'RunCommand', - 'accelerator': accelerator, - 'windex': windex, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def IsMenuCommandEnabled(self, accelerator, windex=0): - """Check if a command is enabled for a window. - - Returns true if the command with the given accelerator id is enabled on the - given window. - - Args: - accelerator: The accelerator id. The list of ids can be found at - chrome/app/chrome_command_ids.h. - windex: The index of the browser window to work on. Defaults to the first - window. - - Returns: - True if the command is enabled for the given window. - """ - cmd_dict = { - 'command': 'IsMenuCommandEnabled', - 'accelerator': accelerator, - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None).get('enabled') - - def TabGoForward(self, tab_index=0, windex=0): - """Navigate a tab forward in history. - - Equivalent to clicking the Forward button in the UI. Activates the tab as a - side effect. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - self.ActivateTab(tab_index, windex) - self.RunCommand(IDC_FORWARD, windex) - - def TabGoBack(self, tab_index=0, windex=0): - """Navigate a tab backwards in history. - - Equivalent to clicking the Back button in the UI. Activates the tab as a - side effect. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - self.ActivateTab(tab_index, windex) - self.RunCommand(IDC_BACK, windex) - - def ReloadTab(self, tab_index=0, windex=0): - """Reload the given tab. - - Blocks until the page has reloaded. - - Args: - tab_index: The index of the tab to reload. Defaults to 0. - windex: The index of the browser window to work on. Defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - self.ActivateTab(tab_index, windex) - self.RunCommand(IDC_RELOAD, windex) - - def CloseTab(self, tab_index=0, windex=0, wait_until_closed=True): - """Close the given tab. - - Note: Be careful closing the last tab in a window as it may close the - browser. - - Args: - tab_index: The index of the tab to reload. Defaults to 0. - windex: The index of the browser window to work on. Defaults to the first - window. - wait_until_closed: Whether to block until the tab finishes closing. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'CloseTab', - 'tab_index': tab_index, - 'windex': windex, - 'wait_until_closed': wait_until_closed, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def WaitForTabToBeRestored(self, tab_index=0, windex=0, timeout=-1): - """Wait for the given tab to be restored. - - Args: - tab_index: The index of the tab to reload. Defaults to 0. - windex: The index of the browser window to work on. Defaults to the first - window. - timeout: Timeout in milliseconds. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'CloseTab', - 'tab_index': tab_index, - 'windex': windex, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None, timeout=timeout) - - def ReloadActiveTab(self, windex=0): - """Reload an active tab. - - Warning: Depending on the concept of an active tab is dangerous as it can - change during the test. Use ReloadTab and supply a tab_index explicitly. - - Args: - windex: The index of the browser window to work on. Defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - self.ReloadTab(self.GetActiveTabIndex(windex), windex) - - def GetActiveTabIndex(self, windex=0): - """Get the index of the currently active tab in the given browser window. - - Warning: Depending on the concept of an active tab is dangerous as it can - change during the test. Supply the tab_index explicitly, if possible. - - Args: - windex: The index of the browser window to work on. Defaults to the first - window. - - Returns: - An integer index for the currently active tab. - """ - cmd_dict = { - 'command': 'GetActiveTabIndex', - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict, - windex=None).get('tab_index') - - def ActivateTab(self, tab_index=0, windex=0): - """Activates the given tab in the specified window. - - Warning: Depending on the concept of an active tab is dangerous as it can - change during the test. Instead use functions that accept a tab_index - explicitly. - - Args: - tab_index: Integer index of the tab to activate; defaults to 0. - windex: Integer index of the browser window to use; defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'ActivateTab', - 'tab_index': tab_index, - 'windex': windex, - } - self.BringBrowserToFront(windex) - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def BringBrowserToFront(self, windex=0): - """Activate the browser's window and bring it to front. - - Args: - windex: Integer index of the browser window to use; defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'BringBrowserToFront', - 'windex': windex, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetBrowserWindowCount(self): - """Get the browser window count. - - Args: - None. - - Returns: - Integer count of the number of browser windows. Includes popups. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = {'command': 'GetBrowserWindowCount'} - return self._GetResultFromJSONRequest(cmd_dict, windex=None)['count'] - - def OpenNewBrowserWindow(self, show): - """Create a new browser window. - - Args: - show: Boolean indicating whether to show the window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'OpenNewBrowserWindow', - 'show': show, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def CloseBrowserWindow(self, windex=0): - """Create a new browser window. - - Args: - windex: Index of the browser window to close; defaults to 0. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'CloseBrowserWindow', - 'windex': windex, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def AppendTab(self, url, windex=0): - """Append a new tab. - - Creates a new tab at the end of given browser window and activates - it. Blocks until the specified |url| is loaded. - - Args: - url: The url to load, can be string or a GURL object. - windex: The index of the browser window to work on. Defaults to the first - window. - - Returns: - True if the url loads successfully in the new tab. False otherwise. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - if isinstance(url, GURL): - url = url.spec() - cmd_dict = { - 'command': 'AppendTab', - 'url': url, - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None).get('result') - - def GetTabCount(self, windex=0): - """Gets the number of tab in the given browser window. - - Args: - windex: Integer index of the browser window to use; defaults to the first - window. - - Returns: - The tab count. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'GetTabCount', - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None)['tab_count'] - - def GetTabInfo(self, tab_index=0, windex=0): - """Gets information about the specified tab. - - Args: - tab_index: Integer index of the tab to activate; defaults to 0. - windex: Integer index of the browser window to use; defaults to the first - window. - - Returns: - A dictionary containing information about the tab. - Example: - { u'title': "Hello World", - u'url': "http://foo.bar", } - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'GetTabInfo', - 'tab_index': tab_index, - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetActiveTabTitle(self, windex=0): - """Gets the title of the active tab. - - Warning: Depending on the concept of an active tab is dangerous as it can - change during the test. Use GetTabInfo and supply a tab_index explicitly. - - Args: - windex: Integer index of the browser window to use; defaults to the first - window. - - Returns: - The tab title as a string. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - return self.GetTabInfo(self.GetActiveTabIndex(windex), windex)['title'] - - def GetActiveTabURL(self, windex=0): - """Gets the URL of the active tab. - - Warning: Depending on the concept of an active tab is dangerous as it can - change during the test. Use GetTabInfo and supply a tab_index explicitly. - - Args: - windex: Integer index of the browser window to use; defaults to the first - window. - - Returns: - The tab URL as a GURL object. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - return GURL(str(self.GetTabInfo(self.GetActiveTabIndex(windex), - windex)['url'])) - - def ActionOnSSLBlockingPage(self, tab_index=0, windex=0, proceed=True): - """Take action on an interstitial page. - - Calling this when an interstitial page is not showing is an error. - - Args: - tab_index: Integer index of the tab to activate; defaults to 0. - windex: Integer index of the browser window to use; defaults to the first - window. - proceed: Whether to proceed to the URL or not. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'ActionOnSSLBlockingPage', - 'tab_index': tab_index, - 'windex': windex, - 'proceed': proceed, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetBookmarkModel(self, windex=0): - """Return the bookmark model as a BookmarkModel object. - - This is a snapshot of the bookmark model; it is not a proxy and - does not get updated as the bookmark model changes. - """ - bookmarks_as_json = self._GetBookmarksAsJSON(windex) - if not bookmarks_as_json: - raise JSONInterfaceError('Could not resolve browser proxy.') - return bookmark_model.BookmarkModel(bookmarks_as_json) - - def _GetBookmarksAsJSON(self, windex=0): - """Get bookmarks as a JSON dictionary; used by GetBookmarkModel().""" - cmd_dict = { - 'command': 'GetBookmarksAsJSON', - 'windex': windex, - } - self.WaitForBookmarkModelToLoad(windex) - return self._GetResultFromJSONRequest(cmd_dict, - windex=None)['bookmarks_as_json'] - - def WaitForBookmarkModelToLoad(self, windex=0): - """Gets the status of the bookmark bar as a dictionary. - - Args: - windex: Integer index of the browser window to use; defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'WaitForBookmarkModelToLoad', - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetBookmarkBarStatus(self, windex=0): - """Gets the status of the bookmark bar as a dictionary. - - Args: - windex: Integer index of the browser window to use; defaults to the first - window. - - Returns: - A dictionary. - Example: - { u'visible': True, - u'animating': False, - u'detached': False, } - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'GetBookmarkBarStatus', - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetBookmarkBarStatus(self, windex=0): - """Gets the status of the bookmark bar as a dictionary. - - Args: - windex: Integer index of the browser window to use; defaults to the first - window. - - Returns: - A dictionary. - Example: - { u'visible': True, - u'animating': False, - u'detached': False, } - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'GetBookmarkBarStatus', - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetBookmarkBarStatus(self, windex=0): - """Gets the status of the bookmark bar as a dictionary. - - Args: - windex: Integer index of the browser window to use; defaults to the first - window. - - Returns: - A dictionary. - Example: - { u'visible': True, - u'animating': False, - u'detached': False, } - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'GetBookmarkBarStatus', - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetBookmarkBarVisibility(self, windex=0): - """Returns the visibility of the bookmark bar. - - Args: - windex: Integer index of the browser window to use; defaults to the first - window. - - Returns: - True if the bookmark bar is visible, false otherwise. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - return self.GetBookmarkBarStatus(windex)['visible'] - - def AddBookmarkGroup(self, parent_id, index, title, windex=0): - """Adds a bookmark folder. - - Args: - parent_id: The parent bookmark folder. - index: The location in the parent's list to insert this bookmark folder. - title: The name of the bookmark folder. - windex: Integer index of the browser window to use; defaults to the first - window. - - Returns: - True if the bookmark bar is detached, false otherwise. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - if isinstance(parent_id, basestring): - parent_id = int(parent_id) - cmd_dict = { - 'command': 'AddBookmark', - 'parent_id': parent_id, - 'index': index, - 'title': title, - 'is_folder': True, - 'windex': windex, - } - self.WaitForBookmarkModelToLoad(windex) - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def AddBookmarkURL(self, parent_id, index, title, url, windex=0): - """Add a bookmark URL. - - Args: - parent_id: The parent bookmark folder. - index: The location in the parent's list to insert this bookmark. - title: The name of the bookmark. - url: The url of the bookmark. - windex: Integer index of the browser window to use; defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - if isinstance(parent_id, basestring): - parent_id = int(parent_id) - cmd_dict = { - 'command': 'AddBookmark', - 'parent_id': parent_id, - 'index': index, - 'title': title, - 'url': url, - 'is_folder': False, - 'windex': windex, - } - self.WaitForBookmarkModelToLoad(windex) - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def ReparentBookmark(self, id, new_parent_id, index, windex=0): - """Move a bookmark. - - Args: - id: The bookmark to move. - new_parent_id: The new parent bookmark folder. - index: The location in the parent's list to insert this bookmark. - windex: Integer index of the browser window to use; defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - if isinstance(id, basestring): - id = int(id) - if isinstance(new_parent_id, basestring): - new_parent_id = int(new_parent_id) - cmd_dict = { - 'command': 'ReparentBookmark', - 'id': id, - 'new_parent_id': new_parent_id, - 'index': index, - 'windex': windex, - } - self.WaitForBookmarkModelToLoad(windex) - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def SetBookmarkTitle(self, id, title, windex=0): - """Change the title of a bookmark. - - Args: - id: The bookmark to rename. - title: The new title for the bookmark. - windex: Integer index of the browser window to use; defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - if isinstance(id, basestring): - id = int(id) - cmd_dict = { - 'command': 'SetBookmarkTitle', - 'id': id, - 'title': title, - 'windex': windex, - } - self.WaitForBookmarkModelToLoad(windex) - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def SetBookmarkURL(self, id, url, windex=0): - """Change the URL of a bookmark. - - Args: - id: The bookmark to change. - url: The new url for the bookmark. - windex: Integer index of the browser window to use; defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - if isinstance(id, basestring): - id = int(id) - cmd_dict = { - 'command': 'SetBookmarkURL', - 'id': id, - 'url': url, - 'windex': windex, - } - self.WaitForBookmarkModelToLoad(windex) - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def RemoveBookmark(self, id, windex=0): - """Remove a bookmark. - - Args: - id: The bookmark to remove. - windex: Integer index of the browser window to use; defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - if isinstance(id, basestring): - id = int(id) - cmd_dict = { - 'command': 'RemoveBookmark', - 'id': id, - 'windex': windex, - } - self.WaitForBookmarkModelToLoad(windex) - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetDownloadsInfo(self, windex=0): - """Return info about downloads. - - This includes all the downloads recognized by the history system. - - Returns: - an instance of downloads_info.DownloadInfo - """ - return download_info.DownloadInfo( - self._GetResultFromJSONRequest({'command': 'GetDownloadsInfo'}, - windex=windex)) - - def GetOmniboxInfo(self, windex=0): - """Return info about Omnibox. - - This represents a snapshot of the omnibox. If you expect changes - you need to call this method again to get a fresh snapshot. - Note that this DOES NOT shift focus to the omnibox; you've to ensure that - the omnibox is in focus or else you won't get any interesting info. - - It's OK to call this even when the omnibox popup is not showing. In this - case however, there won't be any matches, but other properties (like the - current text in the omnibox) will still be fetched. - - Due to the nature of the omnibox, this function is sensitive to mouse - focus. DO NOT HOVER MOUSE OVER OMNIBOX OR CHANGE WINDOW FOCUS WHEN USING - THIS METHOD. - - Args: - windex: the index of the browser window to work on. - Default: 0 (first window) - - Returns: - an instance of omnibox_info.OmniboxInfo - """ - return omnibox_info.OmniboxInfo( - self._GetResultFromJSONRequest({'command': 'GetOmniboxInfo'}, - windex=windex)) - - def SetOmniboxText(self, text, windex=0): - """Enter text into the omnibox. This shifts focus to the omnibox. - - Args: - text: the text to be set. - windex: the index of the browser window to work on. - Default: 0 (first window) - """ - # Ensure that keyword data is loaded from the profile. - # This would normally be triggered by the user inputting this text. - self._GetResultFromJSONRequest({'command': 'LoadSearchEngineInfo'}) - cmd_dict = { - 'command': 'SetOmniboxText', - 'text': text, - } - self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - # TODO(ace): Remove this hack, update bug 62783. - def WaitUntilOmniboxReadyHack(self, windex=0): - """Wait until the omnibox is ready for input. - - This is a hack workaround for linux platform, which returns from - synchronous window creation methods before the omnibox is fully functional. - - No-op on non-linux platforms. - - Args: - windex: the index of the browser to work on. - """ - if self.IsLinux(): - return self.WaitUntil( - lambda : self.GetOmniboxInfo(windex).Properties('has_focus')) - - def WaitUntilOmniboxQueryDone(self, windex=0): - """Wait until omnibox has finished populating results. - - Uses WaitUntil() so the wait duration is capped by the timeout values - used by automation, which WaitUntil() uses. - - Args: - windex: the index of the browser window to work on. - Default: 0 (first window) - """ - return self.WaitUntil( - lambda : not self.GetOmniboxInfo(windex).IsQueryInProgress()) - - def OmniboxMovePopupSelection(self, count, windex=0): - """Move omnibox popup selection up or down. - - Args: - count: number of rows by which to move. - -ve implies down, +ve implies up - windex: the index of the browser window to work on. - Default: 0 (first window) - """ - cmd_dict = { - 'command': 'OmniboxMovePopupSelection', - 'count': count, - } - self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - def OmniboxAcceptInput(self, windex=0): - """Accepts the current string of text in the omnibox. - - This is equivalent to clicking or hiting enter on a popup selection. - Blocks until the page loads. - - Args: - windex: the index of the browser window to work on. - Default: 0 (first window) - """ - cmd_dict = { - 'command': 'OmniboxAcceptInput', - } - self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - def GetCookie(self, url, windex=0): - """Get the value of the cookie at url in context of the specified browser. - - Args: - url: Either a GURL object or url string specifing the cookie url. - windex: The index of the browser window to work on. Defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - if isinstance(url, GURL): - url = url.spec() - cmd_dict = { - 'command': 'GetCookiesInBrowserContext', - 'url': url, - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None)['cookies'] - - def DeleteCookie(self, url, cookie_name, windex=0): - """Delete the cookie at url with name cookie_name. - - Args: - url: Either a GURL object or url string specifing the cookie url. - cookie_name: The name of the cookie to delete as a string. - windex: The index of the browser window to work on. Defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - if isinstance(url, GURL): - url = url.spec() - cmd_dict = { - 'command': 'DeleteCookieInBrowserContext', - 'url': url, - 'cookie_name': cookie_name, - 'windex': windex, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def SetCookie(self, url, value, windex=0): - """Set the value of the cookie at url to value in the context of a browser. - - Args: - url: Either a GURL object or url string specifing the cookie url. - value: A string to set as the cookie's value. - windex: The index of the browser window to work on. Defaults to the first - window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - if isinstance(url, GURL): - url = url.spec() - cmd_dict = { - 'command': 'SetCookieInBrowserContext', - 'url': url, - 'value': value, - 'windex': windex, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetSearchEngineInfo(self, windex=0): - """Return info about search engines. - - Args: - windex: The window index, default is 0. - - Returns: - An ordered list of dictionaries describing info about each search engine. - - Example: - [ { u'display_url': u'{google:baseURL}search?q=%s', - u'host': u'www.google.com', - u'in_default_list': True, - u'is_default': True, - u'is_valid': True, - u'keyword': u'google.com', - u'path': u'/search', - u'short_name': u'Google', - u'supports_replacement': True, - u'url': u'{google:baseURL}search?q={searchTerms}'}, - { u'display_url': u'http://search.yahoo.com/search?p=%s', - u'host': u'search.yahoo.com', - u'in_default_list': True, - u'is_default': False, - u'is_valid': True, - u'keyword': u'yahoo.com', - u'path': u'/search', - u'short_name': u'Yahoo!', - u'supports_replacement': True, - u'url': u'http://search.yahoo.com/search?p={searchTerms}'}, - """ - # Ensure that the search engine profile is loaded into data model. - self._GetResultFromJSONRequest({'command': 'LoadSearchEngineInfo'}, - windex=windex) - cmd_dict = {'command': 'GetSearchEngineInfo'} - return self._GetResultFromJSONRequest( - cmd_dict, windex=windex)['search_engines'] - - def AddSearchEngine(self, title, keyword, url, windex=0): - """Add a search engine, as done through the search engines UI. - - Args: - title: name for search engine. - keyword: keyword, used to initiate a custom search from omnibox. - url: url template for this search engine's query. - '%s' is replaced by search query string when used to search. - windex: The window index, default is 0. - """ - # Ensure that the search engine profile is loaded into data model. - self._GetResultFromJSONRequest({'command': 'LoadSearchEngineInfo'}, - windex=windex) - cmd_dict = {'command': 'AddOrEditSearchEngine', - 'new_title': title, - 'new_keyword': keyword, - 'new_url': url} - self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - def EditSearchEngine(self, keyword, new_title, new_keyword, new_url, - windex=0): - """Edit info for existing search engine. - - Args: - keyword: existing search engine keyword. - new_title: new name for this search engine. - new_keyword: new keyword for this search engine. - new_url: new url for this search engine. - windex: The window index, default is 0. - """ - # Ensure that the search engine profile is loaded into data model. - self._GetResultFromJSONRequest({'command': 'LoadSearchEngineInfo'}, - windex=windex) - cmd_dict = {'command': 'AddOrEditSearchEngine', - 'keyword': keyword, - 'new_title': new_title, - 'new_keyword': new_keyword, - 'new_url': new_url} - self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - def DeleteSearchEngine(self, keyword, windex=0): - """Delete search engine with given keyword. - - Args: - keyword: the keyword string of the search engine to delete. - windex: The window index, default is 0. - """ - # Ensure that the search engine profile is loaded into data model. - self._GetResultFromJSONRequest({'command': 'LoadSearchEngineInfo'}, - windex=windex) - cmd_dict = {'command': 'PerformActionOnSearchEngine', 'keyword': keyword, - 'action': 'delete'} - self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - def MakeSearchEngineDefault(self, keyword, windex=0): - """Make search engine with given keyword the default search. - - Args: - keyword: the keyword string of the search engine to make default. - windex: The window index, default is 0. - """ - # Ensure that the search engine profile is loaded into data model. - self._GetResultFromJSONRequest({'command': 'LoadSearchEngineInfo'}, - windex=windex) - cmd_dict = {'command': 'PerformActionOnSearchEngine', 'keyword': keyword, - 'action': 'default'} - self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - def GetLocalStatePrefsInfo(self): - """Return info about preferences. - - This represents a snapshot of the local state preferences. If you expect - local state preferences to have changed, you need to call this method again - to get a fresh snapshot. - - Returns: - an instance of prefs_info.PrefsInfo - """ - return prefs_info.PrefsInfo( - self._GetResultFromJSONRequest({'command': 'GetLocalStatePrefsInfo'}, - windex=None)) - - def SetLocalStatePrefs(self, path, value): - """Set local state preference for the given path. - - Preferences are stored by Chromium as a hierarchical dictionary. - dot-separated paths can be used to refer to a particular preference. - example: "session.restore_on_startup" - - Some preferences are managed, that is, they cannot be changed by the - user. It's up to the user to know which ones can be changed. Typically, - the options available via Chromium preferences can be changed. - - Args: - path: the path the preference key that needs to be changed - example: "session.restore_on_startup" - One of the equivalent names in chrome/common/pref_names.h could - also be used. - value: the value to be set. It could be plain values like int, bool, - string or complex ones like list. - The user has to ensure that the right value is specified for the - right key. It's useful to dump the preferences first to determine - what type is expected for a particular preference path. - """ - cmd_dict = { - 'command': 'SetLocalStatePrefs', - 'windex': 0, - 'path': path, - 'value': value, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetPrefsInfo(self, windex=0): - """Return info about preferences. - - This represents a snapshot of the preferences. If you expect preferences - to have changed, you need to call this method again to get a fresh - snapshot. - - Args: - windex: The window index, default is 0. - Returns: - an instance of prefs_info.PrefsInfo - """ - cmd_dict = { - 'command': 'GetPrefsInfo', - 'windex': windex, - } - return prefs_info.PrefsInfo( - self._GetResultFromJSONRequest(cmd_dict, windex=None)) - - def SetPrefs(self, path, value, windex=0): - """Set preference for the given path. - - Preferences are stored by Chromium as a hierarchical dictionary. - dot-separated paths can be used to refer to a particular preference. - example: "session.restore_on_startup" - - Some preferences are managed, that is, they cannot be changed by the - user. It's up to the user to know which ones can be changed. Typically, - the options available via Chromium preferences can be changed. - - Args: - path: the path the preference key that needs to be changed - example: "session.restore_on_startup" - One of the equivalent names in chrome/common/pref_names.h could - also be used. - value: the value to be set. It could be plain values like int, bool, - string or complex ones like list. - The user has to ensure that the right value is specified for the - right key. It's useful to dump the preferences first to determine - what type is expected for a particular preference path. - windex: window index to work on. Defaults to 0 (first window). - """ - cmd_dict = { - 'command': 'SetPrefs', - 'windex': windex, - 'path': path, - 'value': value, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def SendWebkitKeyEvent(self, key_type, key_code, tab_index=0, windex=0): - """Send a webkit key event to the browser. - - Args: - key_type: the raw key type such as 0 for up and 3 for down. - key_code: the hex value associated with the keypress (virtual key code). - tab_index: tab index to work on. Defaults to 0 (first tab). - windex: window index to work on. Defaults to 0 (first window). - """ - cmd_dict = { - 'command': 'SendWebkitKeyEvent', - 'type': key_type, - 'text': '', - 'isSystemKey': False, - 'unmodifiedText': '', - 'nativeKeyCode': 0, - 'windowsKeyCode': key_code, - 'modifiers': 0, - 'windex': windex, - 'tab_index': tab_index, - } - # Sending request for key event. - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def SendWebkitCharEvent(self, char, tab_index=0, windex=0): - """Send a webkit char to the browser. - - Args: - char: the char value to be sent to the browser. - tab_index: tab index to work on. Defaults to 0 (first tab). - windex: window index to work on. Defaults to 0 (first window). - """ - cmd_dict = { - 'command': 'SendWebkitKeyEvent', - 'type': 2, # kCharType - 'text': char, - 'isSystemKey': False, - 'unmodifiedText': char, - 'nativeKeyCode': 0, - 'windowsKeyCode': ord((char).upper()), - 'modifiers': 0, - 'windex': windex, - 'tab_index': tab_index, - } - # Sending request for a char. - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def SetDownloadShelfVisible(self, is_visible, windex=0): - """Set download shelf visibility for the specified browser window. - - Args: - is_visible: A boolean indicating the desired shelf visibility. - windex: The window index, defaults to 0 (the first window). - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'SetDownloadShelfVisible', - 'is_visible': is_visible, - 'windex': windex, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def IsDownloadShelfVisible(self, windex=0): - """Determine whether the download shelf is visible in the given window. - - Args: - windex: The window index, defaults to 0 (the first window). - - Returns: - A boolean indicating the shelf visibility. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'IsDownloadShelfVisible', - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None)['is_visible'] - - def GetDownloadDirectory(self, tab_index=None, windex=0): - """Get the path to the download directory. - - Warning: Depending on the concept of an active tab is dangerous as it can - change during the test. Always supply a tab_index explicitly. - - Args: - tab_index: The index of the tab to work on. Defaults to the active tab. - windex: The index of the browser window to work on. Defaults to 0. - - Returns: - The path to the download directory as a FilePath object. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - if tab_index is None: - tab_index = self.GetActiveTabIndex(windex) - cmd_dict = { - 'command': 'GetDownloadDirectory', - 'tab_index': tab_index, - 'windex': windex, - } - return FilePath(str(self._GetResultFromJSONRequest(cmd_dict, - windex=None)['path'])) - - def WaitForAllDownloadsToComplete(self, pre_download_ids=[], windex=0, - timeout=-1): - """Wait for all pending downloads to complete. - - This function assumes that any downloads to wait for have already been - triggered and have started (it is ok if those downloads complete before this - function is called). - - Args: - pre_download_ids: A list of numbers representing the IDs of downloads that - exist *before* downloads to wait for have been - triggered. Defaults to []; use GetDownloadsInfo() to get - these IDs (only necessary if a test previously - downloaded files). - windex: The window index, defaults to 0 (the first window). - timeout: The maximum amount of time (in milliseconds) to wait for - downloads to complete. - """ - cmd_dict = { - 'command': 'WaitForAllDownloadsToComplete', - 'pre_download_ids': pre_download_ids, - } - self._GetResultFromJSONRequest(cmd_dict, windex=windex, timeout=timeout) - - def PerformActionOnDownload(self, id, action, window_index=0): - """Perform the given action on the download with the given id. - - Args: - id: The id of the download. - action: The action to perform on the download. - Possible actions: - 'open': Opens the download (waits until it has completed first). - 'toggle_open_files_like_this': Toggles the 'Always Open Files - Of This Type' option. - 'remove': Removes the file from downloads (not from disk). - 'decline_dangerous_download': Equivalent to 'Discard' option - after downloading a dangerous download (ex. an executable). - 'save_dangerous_download': Equivalent to 'Save' option after - downloading a dangerous file. - 'pause': Pause the download. If the download completed before - this call or is already paused, it's a no-op. - 'resume': Resume the download. If the download completed before - this call or was not paused, it's a no-op. - 'cancel': Cancel the download. - window_index: The window index, default is 0. - - Returns: - A dictionary representing the updated download item (except in the case - of 'decline_dangerous_download', 'toggle_open_files_like_this', and - 'remove', which return an empty dict). - Example dictionary: - { u'PercentComplete': 100, - u'file_name': u'file.txt', - u'full_path': u'/path/to/file.txt', - u'id': 0, - u'is_otr': False, - u'is_paused': False, - u'is_temporary': False, - u'open_when_complete': False, - u'referrer_url': u'', - u'state': u'COMPLETE', - u'danger_type': u'DANGEROUS_FILE', - u'url': u'file://url/to/file.txt' - } - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'PerformActionOnDownload', - 'id': id, - 'action': action - } - return self._GetResultFromJSONRequest(cmd_dict, windex=window_index) - - def DownloadAndWaitForStart(self, file_url, windex=0): - """Trigger download for the given url and wait for downloads to start. - - It waits for download by looking at the download info from Chrome, so - anything which isn't registered by the history service won't be noticed. - This is not thread-safe, but it's fine to call this method to start - downloading multiple files in parallel. That is after starting a - download, it's fine to start another one even if the first one hasn't - completed. - """ - try: - num_downloads = len(self.GetDownloadsInfo(windex).Downloads()) - except JSONInterfaceError: - num_downloads = 0 - - self.NavigateToURL(file_url, windex) # Trigger download. - # It might take a while for the download to kick in, hold on until then. - self.assertTrue(self.WaitUntil( - lambda: len(self.GetDownloadsInfo(windex).Downloads()) > - num_downloads)) - - def SetWindowDimensions( - self, x=None, y=None, width=None, height=None, windex=0): - """Set window dimensions. - - All args are optional and current values will be preserved. - Arbitrarily large values will be handled gracefully by the browser. - - Args: - x: window origin x - y: window origin y - width: window width - height: window height - windex: window index to work on. Defaults to 0 (first window) - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'SetWindowDimensions', - } - if x: - cmd_dict['x'] = x - if y: - cmd_dict['y'] = y - if width: - cmd_dict['width'] = width - if height: - cmd_dict['height'] = height - self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - def WaitForInfobarCount(self, count, windex=0, tab_index=0): - """Wait until infobar count becomes |count|. - - Note: Wait duration is capped by the automation timeout. - - Args: - count: requested number of infobars - windex: window index. Defaults to 0 (first window) - tab_index: tab index Defaults to 0 (first tab) - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - # TODO(phajdan.jr): We need a solid automation infrastructure to handle - # these cases. See crbug.com/53647. - def _InfobarCount(): - windows = self.GetBrowserInfo()['windows'] - if windex >= len(windows): # not enough windows - return -1 - tabs = windows[windex]['tabs'] - if tab_index >= len(tabs): # not enough tabs - return -1 - return len(tabs[tab_index]['infobars']) - - return self.WaitUntil(_InfobarCount, expect_retval=count) - - def PerformActionOnInfobar( - self, action, infobar_index, windex=0, tab_index=0): - """Perform actions on an infobar. - - Args: - action: the action to be performed. - Actions depend on the type of the infobar. The user needs to - call the right action for the right infobar. - Valid inputs are: - - "dismiss": closes the infobar (for all infobars) - - "accept", "cancel": click accept / cancel (for confirm infobars) - - "allow", "deny": click allow / deny (for media stream infobars) - infobar_index: 0-based index of the infobar on which to perform the action - windex: 0-based window index Defaults to 0 (first window) - tab_index: 0-based tab index. Defaults to 0 (first tab) - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'PerformActionOnInfobar', - 'action': action, - 'infobar_index': infobar_index, - 'tab_index': tab_index, - } - if action not in ('dismiss', 'accept', 'allow', 'deny', 'cancel'): - raise JSONInterfaceError('Invalid action %s' % action) - self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - def GetBrowserInfo(self): - """Return info about the browser. - - This includes things like the version number, the executable name, - executable path, pid info about the renderer/plugin/extension processes, - window dimensions. (See sample below) - - For notification pid info, see 'GetActiveNotifications'. - - Returns: - a dictionary - - Sample: - { u'browser_pid': 93737, - # Child processes are the processes for plugins and other workers. - u'child_process_path': u'.../Chromium.app/Contents/' - 'Versions/6.0.412.0/Chromium Helper.app/' - 'Contents/MacOS/Chromium Helper', - u'child_processes': [ { u'name': u'Shockwave Flash', - u'pid': 93766, - u'type': u'Plug-in'}], - u'extension_views': [ { - u'name': u'Webpage Screenshot', - u'pid': 93938, - u'extension_id': u'dgcoklnmbeljaehamekjpeidmbicddfj', - u'url': u'chrome-extension://dgcoklnmbeljaehamekjpeidmbicddfj/' - 'bg.html', - u'loaded': True, - u'view': { - u'render_process_id': 2, - u'render_view_id': 1}, - u'view_type': u'EXTENSION_BACKGROUND_PAGE'}] - u'properties': { - u'BrowserProcessExecutableName': u'Chromium', - u'BrowserProcessExecutablePath': u'Chromium.app/Contents/MacOS/' - 'Chromium', - u'ChromeVersion': u'6.0.412.0', - u'HelperProcessExecutableName': u'Chromium Helper', - u'HelperProcessExecutablePath': u'Chromium Helper.app/Contents/' - 'MacOS/Chromium Helper', - u'command_line_string': "COMMAND_LINE_STRING --WITH-FLAGS", - u'branding': 'Chromium', - u'is_official': False,} - # The order of the windows and tabs listed here will be the same as - # what shows up on screen. - u'windows': [ { u'index': 0, - u'height': 1134, - u'incognito': False, - u'profile_path': u'Default', - u'fullscreen': False, - u'visible_page_actions': - [u'dgcoklnmbeljaehamekjpeidmbicddfj', - u'osfcklnfasdofpcldmalwpicslasdfgd'] - u'selected_tab': 0, - u'tabs': [ { - u'index': 0, - u'infobars': [], - u'pinned': True, - u'renderer_pid': 93747, - u'url': u'http://www.google.com/' }, { - u'index': 1, - u'infobars': [], - u'pinned': False, - u'renderer_pid': 93919, - u'url': u'https://chrome.google.com/'}, { - u'index': 2, - u'infobars': [ { - u'buttons': [u'Allow', u'Deny'], - u'link_text': u'Learn more', - u'text': u'slides.html5rocks.com wants to track ' - 'your physical location', - u'type': u'confirm_infobar'}], - u'pinned': False, - u'renderer_pid': 93929, - u'url': u'http://slides.html5rocks.com/#slide14'}, - ], - u'type': u'tabbed', - u'width': 925, - u'x': 26, - u'y': 44}]} - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'GetBrowserInfo', - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def IsAura(self): - """Is this Aura?""" - return self.GetBrowserInfo()['properties']['aura'] - - def GetProcessInfo(self): - """Returns information about browser-related processes that currently exist. - - This will also return information about other currently-running browsers - besides just Chrome. - - Returns: - A dictionary containing browser-related process information as identified - by class MemoryDetails in src/chrome/browser/memory_details.h. The - dictionary contains a single key 'browsers', mapped to a list of - dictionaries containing information about each browser process name. - Each of those dictionaries contains a key 'processes', mapped to a list - of dictionaries containing the specific information for each process - with the given process name. - - The memory values given in |committed_mem| and |working_set_mem| are in - KBytes. - - Sample: - { 'browsers': [ { 'name': 'Chromium', - 'process_name': 'chrome', - 'processes': [ { 'child_process_type': 'Browser', - 'committed_mem': { 'image': 0, - 'mapped': 0, - 'priv': 0}, - 'is_diagnostics': False, - 'num_processes': 1, - 'pid': 7770, - 'product_name': '', - 'renderer_type': 'Unknown', - 'titles': [], - 'version': '', - 'working_set_mem': { 'priv': 43672, - 'shareable': 0, - 'shared': 59251}}, - { 'child_process_type': 'Tab', - 'committed_mem': { 'image': 0, - 'mapped': 0, - 'priv': 0}, - 'is_diagnostics': False, - 'num_processes': 1, - 'pid': 7791, - 'product_name': '', - 'renderer_type': 'Tab', - 'titles': ['about:blank'], - 'version': '', - 'working_set_mem': { 'priv': 16768, - 'shareable': 0, - 'shared': 26256}}, - ...<more processes>...]}]} - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { # Prepare command for the json interface. - 'command': 'GetProcessInfo', - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetNavigationInfo(self, tab_index=0, windex=0): - """Get info about the navigation state of a given tab. - - Args: - tab_index: The tab index, default is 0. - window_index: The window index, default is 0. - - Returns: - a dictionary. - Sample: - - { u'favicon_url': u'https://www.google.com/favicon.ico', - u'page_type': u'NORMAL_PAGE', - u'ssl': { u'displayed_insecure_content': False, - u'ran_insecure_content': False, - u'security_style': u'SECURITY_STYLE_AUTHENTICATED'}} - - Values for security_style can be: - SECURITY_STYLE_UNKNOWN - SECURITY_STYLE_UNAUTHENTICATED - SECURITY_STYLE_AUTHENTICATION_BROKEN - SECURITY_STYLE_AUTHENTICATED - - Values for page_type can be: - NORMAL_PAGE - ERROR_PAGE - INTERSTITIAL_PAGE - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'GetNavigationInfo', - 'tab_index': tab_index, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - def GetSecurityState(self, tab_index=0, windex=0): - """Get security details for a given tab. - - Args: - tab_index: The tab index, default is 0. - window_index: The window index, default is 0. - - Returns: - a dictionary. - Sample: - { "security_style": SECURITY_STYLE_AUTHENTICATED, - "ssl_cert_status": 3, // bitmask of status flags - "insecure_content_status": 1, // bitmask of status flags - } - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'GetSecurityState', - 'tab_index': tab_index, - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetHistoryInfo(self, search_text='', windex=0): - """Return info about browsing history. - - Args: - search_text: the string to search in history. Defaults to empty string - which means that all history would be returned. This is - functionally equivalent to searching for a text in the - chrome://history UI. So partial matches work too. - When non-empty, the history items returned will contain a - "snippet" field corresponding to the snippet visible in - the chrome://history/ UI. - windex: index of the browser window, defaults to 0. - - Returns: - an instance of history_info.HistoryInfo - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'GetHistoryInfo', - 'search_text': search_text, - } - return history_info.HistoryInfo( - self._GetResultFromJSONRequest(cmd_dict, windex=windex)) - - def InstallExtension(self, extension_path, with_ui=False, from_webstore=None, - windex=0, tab_index=0): - """Installs an extension from the given path. - - The path must be absolute and may be a crx file or an unpacked extension - directory. Returns the extension ID if successfully installed and loaded. - Otherwise, throws an exception. The extension must not already be installed. - - Args: - extension_path: The absolute path to the extension to install. If the - extension is packed, it must have a .crx extension. - with_ui: Whether the extension install confirmation UI should be shown. - from_webstore: If True, forces a .crx extension to be recognized as one - from the webstore. Can be used to force install an extension with - 'experimental' permissions. - windex: Integer index of the browser window to use; defaults to 0 - (first window). - - Returns: - The ID of the installed extension. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'InstallExtension', - 'path': extension_path, - 'with_ui': with_ui, - 'windex': windex, - 'tab_index': tab_index, - } - - if from_webstore: - cmd_dict['from_webstore'] = True - return self._GetResultFromJSONRequest(cmd_dict, windex=None)['id'] - - def GetExtensionsInfo(self, windex=0): - """Returns information about all installed extensions. - - Args: - windex: Integer index of the browser window to use; defaults to 0 - (first window). - - Returns: - A list of dictionaries representing each of the installed extensions. - Example: - [ { u'api_permissions': [u'bookmarks', u'experimental', u'tabs'], - u'background_url': u'', - u'description': u'Bookmark Manager', - u'effective_host_permissions': [u'chrome://favicon/*', - u'chrome://resources/*'], - u'host_permissions': [u'chrome://favicon/*', u'chrome://resources/*'], - u'id': u'eemcgdkfndhakfknompkggombfjjjeno', - u'is_component': True, - u'is_internal': False, - u'name': u'Bookmark Manager', - u'options_url': u'', - u'public_key': u'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQcByy+eN9jza\ - zWF/DPn7NW47sW7lgmpk6eKc0BQM18q8hvEM3zNm2n7HkJv/R6f\ - U+X5mtqkDuKvq5skF6qqUF4oEyaleWDFhd1xFwV7JV+/DU7bZ00\ - w2+6gzqsabkerFpoP33ZRIw7OviJenP0c0uWqDWF8EGSyMhB3tx\ - qhOtiQIDAQAB', - u'version': u'0.1' }, - { u'api_permissions': [...], - u'background_url': u'chrome-extension://\ - lkdedmbpkaiahjjibfdmpoefffnbdkli/\ - background.html', - u'description': u'Extension which lets you read your Facebook news \ - feed and wall. You can also post status updates.', - u'effective_host_permissions': [...], - u'host_permissions': [...], - u'id': u'lkdedmbpkaiahjjibfdmpoefffnbdkli', - u'name': u'Facebook for Google Chrome', - u'options_url': u'', - u'public_key': u'...', - u'version': u'2.0.9' - u'is_enabled': True, - u'allowed_in_incognito': True} ] - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'GetExtensionsInfo', - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None)['extensions'] - - def UninstallExtensionById(self, id, windex=0): - """Uninstall the extension with the given id. - - Args: - id: The string id of the extension. - windex: Integer index of the browser window to use; defaults to 0 - (first window). - - Returns: - True, if the extension was successfully uninstalled, or - False, otherwise. - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'UninstallExtensionById', - 'id': id, - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None)['success'] - - def SetExtensionStateById(self, id, enable, allow_in_incognito, windex=0): - """Set extension state: enable/disable, allow/disallow in incognito mode. - - Args: - id: The string id of the extension. - enable: A boolean, enable extension. - allow_in_incognito: A boolean, allow extension in incognito. - windex: Integer index of the browser window to use; defaults to 0 - (first window). - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'SetExtensionStateById', - 'id': id, - 'enable': enable, - 'allow_in_incognito': allow_in_incognito, - 'windex': windex, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def TriggerPageActionById(self, id, tab_index=0, windex=0): - """Trigger page action asynchronously in the active tab. - - The page action icon must be displayed before invoking this function. - - Args: - id: The string id of the extension. - tab_index: Integer index of the tab to use; defaults to 0 (first tab). - windex: Integer index of the browser window to use; defaults to 0 - (first window). - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'TriggerPageActionById', - 'id': id, - 'windex': windex, - 'tab_index': tab_index, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def TriggerBrowserActionById(self, id, tab_index=0, windex=0): - """Trigger browser action asynchronously in the active tab. - - Args: - id: The string id of the extension. - tab_index: Integer index of the tab to use; defaults to 0 (first tab). - windex: Integer index of the browser window to use; defaults to 0 - (first window). - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'TriggerBrowserActionById', - 'id': id, - 'windex': windex, - 'tab_index': tab_index, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def UpdateExtensionsNow(self, windex=0): - """Auto-updates installed extensions. - - Waits until all extensions are updated, loaded, and ready for use. - This is equivalent to clicking the "Update extensions now" button on the - chrome://extensions page. - - Args: - windex: Integer index of the browser window to use; defaults to 0 - (first window). - - Raises: - pyauto_errors.JSONInterfaceError if the automation returns an error. - """ - cmd_dict = { # Prepare command for the json interface. - 'command': 'UpdateExtensionsNow', - 'windex': windex, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def WaitUntilExtensionViewLoaded(self, name=None, extension_id=None, - url=None, view_type=None): - """Wait for a loaded extension view matching all the given properties. - - If no matching extension views are found, wait for one to be loaded. - If there are more than one matching extension view, return one at random. - Uses WaitUntil so timeout is capped by automation timeout. - Refer to extension_view dictionary returned in GetBrowserInfo() - for sample input/output values. - - Args: - name: (optional) Name of the extension. - extension_id: (optional) ID of the extension. - url: (optional) URL of the extension view. - view_type: (optional) Type of the extension view. - ['EXTENSION_BACKGROUND_PAGE'|'EXTENSION_POPUP'|'EXTENSION_INFOBAR'| - 'EXTENSION_DIALOG'] - - Returns: - The 'view' property of the extension view. - None, if no view loaded. - - Raises: - pyauto_errors.JSONInterfaceError if the automation returns an error. - """ - def _GetExtensionViewLoaded(): - extension_views = self.GetBrowserInfo()['extension_views'] - for extension_view in extension_views: - if ((name and name != extension_view['name']) or - (extension_id and extension_id != extension_view['extension_id']) or - (url and url != extension_view['url']) or - (view_type and view_type != extension_view['view_type'])): - continue - if extension_view['loaded']: - return extension_view['view'] - return False - - if self.WaitUntil(lambda: _GetExtensionViewLoaded()): - return _GetExtensionViewLoaded() - return None - - def WaitUntilExtensionViewClosed(self, view): - """Wait for the given extension view to to be closed. - - Uses WaitUntil so timeout is capped by automation timeout. - Refer to extension_view dictionary returned by GetBrowserInfo() - for sample input value. - - Args: - view: 'view' property of extension view. - - Raises: - pyauto_errors.JSONInterfaceError if the automation returns an error. - """ - def _IsExtensionViewClosed(): - extension_views = self.GetBrowserInfo()['extension_views'] - for extension_view in extension_views: - if view == extension_view['view']: - return False - return True - - return self.WaitUntil(lambda: _IsExtensionViewClosed()) - - def GetPluginsInfo(self, windex=0): - """Return info about plugins. - - This is the info available from about:plugins - - Returns: - an instance of plugins_info.PluginsInfo - """ - return plugins_info.PluginsInfo( - self._GetResultFromJSONRequest({'command': 'GetPluginsInfo'}, - windex=windex)) - - def EnablePlugin(self, path): - """Enable the plugin at the given path. - - Use GetPluginsInfo() to fetch path info about a plugin. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'EnablePlugin', - 'path': path, - } - self._GetResultFromJSONRequest(cmd_dict) - - def DisablePlugin(self, path): - """Disable the plugin at the given path. - - Use GetPluginsInfo() to fetch path info about a plugin. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'DisablePlugin', - 'path': path, - } - self._GetResultFromJSONRequest(cmd_dict) - - def GetTabContents(self, tab_index=0, window_index=0): - """Get the html contents of a tab (a la "view source"). - - As an implementation detail, this saves the html in a file, reads - the file into a buffer, then deletes it. - - Args: - tab_index: tab index, defaults to 0. - window_index: window index, defaults to 0. - Returns: - html content of a page as a string. - """ - tempdir = tempfile.mkdtemp() - # Make it writable by chronos on chromeos - os.chmod(tempdir, 0777) - filename = os.path.join(tempdir, 'content.html') - cmd_dict = { # Prepare command for the json interface - 'command': 'SaveTabContents', - 'tab_index': tab_index, - 'filename': filename - } - self._GetResultFromJSONRequest(cmd_dict, windex=window_index) - try: - f = open(filename) - all_data = f.read() - f.close() - return all_data - finally: - shutil.rmtree(tempdir, ignore_errors=True) - - def AddSavedPassword(self, password_dict, windex=0): - """Adds the given username-password combination to the saved passwords. - - Args: - password_dict: a dictionary that represents a password. Example: - { 'username_value': 'user@example.com', # Required - 'password_value': 'test.password', # Required - 'signon_realm': 'https://www.example.com/', # Required - 'time': 1279317810.0, # Can get from time.time() - 'origin_url': 'https://www.example.com/login', - 'username_element': 'username', # The HTML element - 'password_element': 'password', # The HTML element - 'submit_element': 'submit', # The HTML element - 'action_target': 'https://www.example.com/login/', - 'blacklist': False } - windex: window index; defaults to 0 (first window). - - *Blacklist notes* To blacklist a site, add a blacklist password with the - following dictionary items: origin_url, signon_realm, username_element, - password_element, action_target, and 'blacklist': True. Then all sites that - have password forms matching those are blacklisted. - - Returns: - True if adding the password succeeded, false otherwise. In incognito - mode, adding the password should fail. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'AddSavedPassword', - 'password': password_dict - } - return self._GetResultFromJSONRequest( - cmd_dict, windex=windex)['password_added'] - - def RemoveSavedPassword(self, password_dict, windex=0): - """Removes the password matching the provided password dictionary. - - Args: - password_dict: A dictionary that represents a password. - For an example, see the dictionary in AddSavedPassword. - windex: The window index, default is 0 (first window). - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'RemoveSavedPassword', - 'password': password_dict - } - self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - def GetSavedPasswords(self): - """Return the passwords currently saved. - - Returns: - A list of dictionaries representing each password. For an example - dictionary see AddSavedPassword documentation. The overall structure will - be: - [ {password1 dictionary}, {password2 dictionary} ] - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'GetSavedPasswords' - } - return self._GetResultFromJSONRequest(cmd_dict)['passwords'] - - def SetTheme(self, crx_file_path, windex=0): - """Installs the given theme synchronously. - - A theme file is a file with a .crx suffix, like an extension. The theme - file must be specified with an absolute path. This method call waits until - the theme is installed and will trigger the "theme installed" infobar. - If the install is unsuccessful, will throw an exception. - - Uses InstallExtension(). - - Returns: - The ID of the installed theme. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - return self.InstallExtension(crx_file_path, True, windex) - - def GetActiveNotifications(self): - """Gets a list of the currently active/shown HTML5 notifications. - - Returns: - a list containing info about each active notification, with the - first item in the list being the notification on the bottom of the - notification stack. The 'content_url' key can refer to a URL or a data - URI. The 'pid' key-value pair may be invalid if the notification is - closing. - - SAMPLE: - [ { u'content_url': u'data:text/html;charset=utf-8,%3C!DOCTYPE%l%3E%0Atm...' - u'display_source': 'www.corp.google.com', - u'origin_url': 'http://www.corp.google.com/', - u'pid': 8505}, - { u'content_url': 'http://www.gmail.com/special_notification.html', - u'display_source': 'www.gmail.com', - u'origin_url': 'http://www.gmail.com/', - u'pid': 9291}] - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - return [x for x in self.GetAllNotifications() if 'pid' in x] - - def GetAllNotifications(self): - """Gets a list of all active and queued HTML5 notifications. - - An active notification is one that is currently shown to the user. Chrome's - notification system will limit the number of notifications shown (currently - by only allowing a certain percentage of the screen to be taken up by them). - A notification will be queued if there are too many active notifications. - Once other notifications are closed, another will be shown from the queue. - - Returns: - a list containing info about each notification, with the first - item in the list being the notification on the bottom of the - notification stack. The 'content_url' key can refer to a URL or a data - URI. The 'pid' key-value pair will only be present for active - notifications. - - SAMPLE: - [ { u'content_url': u'data:text/html;charset=utf-8,%3C!DOCTYPE%l%3E%0Atm...' - u'display_source': 'www.corp.google.com', - u'origin_url': 'http://www.corp.google.com/', - u'pid': 8505}, - { u'content_url': 'http://www.gmail.com/special_notification.html', - u'display_source': 'www.gmail.com', - u'origin_url': 'http://www.gmail.com/'}] - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'GetAllNotifications', - } - return self._GetResultFromJSONRequest(cmd_dict)['notifications'] - - def CloseNotification(self, index): - """Closes the active HTML5 notification at the given index. - - Args: - index: the index of the notification to close. 0 refers to the - notification on the bottom of the notification stack. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'CloseNotification', - 'index': index, - } - return self._GetResultFromJSONRequest(cmd_dict) - - def WaitForNotificationCount(self, count): - """Waits for the number of active HTML5 notifications to reach the given - count. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'WaitForNotificationCount', - 'count': count, - } - self._GetResultFromJSONRequest(cmd_dict) - - def FindInPage(self, search_string, forward=True, - match_case=False, find_next=False, - tab_index=0, windex=0, timeout=-1): - """Find the match count for the given search string and search parameters. - This is equivalent to using the find box. - - Args: - search_string: The string to find on the page. - forward: Boolean to set if the search direction is forward or backwards - match_case: Boolean to set for case sensitive search. - find_next: Boolean to set to continue the search or start from beginning. - tab_index: The tab index, default is 0. - windex: The window index, default is 0. - timeout: request timeout (in milliseconds), default is -1. - - Returns: - number of matches found for the given search string and parameters - SAMPLE: - { u'match_count': 10, - u'match_left': 100, - u'match_top': 100, - u'match_right': 200, - u'match_bottom': 200} - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'FindInPage', - 'tab_index' : tab_index, - 'search_string' : search_string, - 'forward' : forward, - 'match_case' : match_case, - 'find_next' : find_next, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=windex, - timeout=timeout) - - def OpenFindInPage(self, windex=0): - """Opens the "Find in Page" box. - - Args: - windex: Index of the window; defaults to 0. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'OpenFindInPage', - 'windex' : windex, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def IsFindInPageVisible(self, windex=0): - """Returns the visibility of the "Find in Page" box. - - Args: - windex: Index of the window; defaults to 0. - - Returns: - A boolean indicating the visibility state of the "Find in Page" box. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'IsFindInPageVisible', - 'windex' : windex, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None)['is_visible'] - - - def AddDomEventObserver(self, event_name='', automation_id=-1, - recurring=False): - """Adds a DomEventObserver associated with the AutomationEventQueue. - - An app raises a matching event in Javascript by calling: - window.domAutomationController.sendWithId(automation_id, event_name) - - Args: - event_name: The event name to watch for. By default an event is raised - for any message. - automation_id: The Automation Id of the sent message. By default all - messages sent from the window.domAutomationController are - observed. Note that other PyAuto functions also send - messages through window.domAutomationController with - arbirary Automation Ids and they will be observed. - recurring: If False the observer will be removed after it generates one - event, otherwise it will continue observing and generating - events until explicity removed with RemoveEventObserver(id). - - Returns: - The id of the created observer, which can be used with GetNextEvent(id) - and RemoveEventObserver(id). - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'AddDomEventObserver', - 'event_name': event_name, - 'automation_id': automation_id, - 'recurring': recurring, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None)['observer_id'] - - def AddDomMutationObserver(self, mutation_type, xpath, - attribute='textContent', expected_value=None, - automation_id=44444, - exec_js=None, **kwargs): - """Sets up an event observer watching for a specific DOM mutation. - - Creates an observer that raises an event when a mutation of the given type - occurs on a DOM node specified by |selector|. - - Args: - mutation_type: One of 'add', 'remove', 'change', or 'exists'. - xpath: An xpath specifying the DOM node to watch. The node must already - exist if |mutation_type| is 'change'. - attribute: Attribute to match |expected_value| against, if given. Defaults - to 'textContent'. - expected_value: Optional regular expression to match against the node's - textContent attribute after the mutation. Defaults to None. - automation_id: The automation_id used to route the observer javascript - messages. Defaults to 44444. - exec_js: A callable of the form f(self, js, **kwargs) used to inject the - MutationObserver javascript. Defaults to None, which uses - PyUITest.ExecuteJavascript. - - Any additional keyword arguments are passed on to ExecuteJavascript and - can be used to select the tab where the DOM MutationObserver is created. - - Returns: - The id of the created observer, which can be used with GetNextEvent(id) - and RemoveEventObserver(id). - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - pyauto_errors.JavascriptRuntimeError if the injected javascript - MutationObserver returns an error. - """ - assert mutation_type in ('add', 'remove', 'change', 'exists'), \ - 'Unexpected value "%s" for mutation_type.' % mutation_type - cmd_dict = { - 'command': 'AddDomEventObserver', - 'event_name': '__dom_mutation_observer__:$(id)', - 'automation_id': automation_id, - 'recurring': False, - } - observer_id = ( - self._GetResultFromJSONRequest(cmd_dict, windex=None)['observer_id']) - expected_string = ('null' if expected_value is None else '"%s"' % - expected_value.replace('"', r'\"')) - jsfile = os.path.join(os.path.abspath(os.path.dirname(__file__)), - 'dom_mutation_observer.js') - with open(jsfile, 'r') as f: - js = ('(' + f.read() + ')(%d, %d, "%s", "%s", "%s", %s);' % - (automation_id, observer_id, mutation_type, - xpath.replace('"', r'\"'), attribute, expected_string)) - exec_js = exec_js or PyUITest.ExecuteJavascript - try: - jsreturn = exec_js(self, js, **kwargs) - except JSONInterfaceError: - raise JSONInterfaceError('Failed to inject DOM mutation observer.') - if jsreturn != 'success': - self.RemoveEventObserver(observer_id) - raise JavascriptRuntimeError(jsreturn) - return observer_id - - def WaitForDomNode(self, xpath, attribute='textContent', - expected_value=None, exec_js=None, timeout=-1, - msg='Expected DOM node failed to appear.', **kwargs): - """Waits until a node specified by an xpath exists in the DOM. - - NOTE: This does NOT poll. It returns as soon as the node appears, or - immediately if the node already exists. - - Args: - xpath: An xpath specifying the DOM node to watch. - attribute: Attribute to match |expected_value| against, if given. Defaults - to 'textContent'. - expected_value: Optional regular expression to match against the node's - textContent attribute. Defaults to None. - exec_js: A callable of the form f(self, js, **kwargs) used to inject the - MutationObserver javascript. Defaults to None, which uses - PyUITest.ExecuteJavascript. - msg: An optional error message used if a JSONInterfaceError is caught - while waiting for the DOM node to appear. - timeout: Time to wait for the node to exist before raising an exception, - defaults to the default automation timeout. - - Any additional keyword arguments are passed on to ExecuteJavascript and - can be used to select the tab where the DOM MutationObserver is created. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - pyauto_errors.JavascriptRuntimeError if the injected javascript - MutationObserver returns an error. - """ - observer_id = self.AddDomMutationObserver('exists', xpath, attribute, - expected_value, exec_js=exec_js, - **kwargs) - try: - self.GetNextEvent(observer_id, timeout=timeout) - except JSONInterfaceError: - raise JSONInterfaceError(msg) - - def GetNextEvent(self, observer_id=-1, blocking=True, timeout=-1): - """Waits for an observed event to occur. - - The returned event is removed from the Event Queue. If there is already a - matching event in the queue it is returned immediately, otherwise the call - blocks until a matching event occurs. If blocking is disabled and no - matching event is in the queue this function will immediately return None. - - Args: - observer_id: The id of the observer to wait for, matches any event by - default. - blocking: If True waits until there is a matching event in the queue, - if False and there is no event waiting in the queue returns None - immediately. - timeout: Time to wait for a matching event, defaults to the default - automation timeout. - - Returns: - Event response dictionary, or None if blocking is disabled and there is no - matching event in the queue. - SAMPLE: - { 'observer_id': 1, - 'name': 'login completed', - 'type': 'raised_event'} - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'GetNextEvent', - 'observer_id' : observer_id, - 'blocking' : blocking, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None, - timeout=timeout) - - def RemoveEventObserver(self, observer_id): - """Removes an Event Observer from the AutomationEventQueue. - - Expects a valid observer_id. - - Args: - observer_id: The id of the observer to remove. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'RemoveEventObserver', - 'observer_id' : observer_id, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def ClearEventQueue(self): - """Removes all events currently in the AutomationEventQueue. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'ClearEventQueue', - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def WaitUntilNavigationCompletes(self, tab_index=0, windex=0): - """Wait until the specified tab is done navigating. - - It is safe to call ExecuteJavascript() as soon as the call returns. If - there is no outstanding navigation the call will return immediately. - - Args: - tab_index: index of the tab. - windex: index of the window. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'WaitUntilNavigationCompletes', - 'tab_index': tab_index, - 'windex': windex, - } - return self._GetResultFromJSONRequest(cmd_dict) - - def ExecuteJavascript(self, js, tab_index=0, windex=0, frame_xpath=''): - """Executes a script in the specified frame of a tab. - - By default, execute the script in the top frame of the first tab in the - first window. The invoked javascript function must send a result back via - the domAutomationController.send function, or this function will never - return. - - Args: - js: script to be executed. - windex: index of the window. - tab_index: index of the tab. - frame_xpath: XPath of the frame to execute the script. Default is no - frame. Example: '//frames[1]'. - - Returns: - a value that was sent back via the domAutomationController.send method - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'ExecuteJavascript', - 'javascript' : js, - 'windex' : windex, - 'tab_index' : tab_index, - 'frame_xpath' : frame_xpath, - } - result = self._GetResultFromJSONRequest(cmd_dict)['result'] - # Wrap result in an array before deserializing because valid JSON has an - # array or an object as the root. - json_string = '[' + result + ']' - return json.loads(json_string)[0] - - def ExecuteJavascriptInRenderView(self, js, view, frame_xpath=''): - """Executes a script in the specified frame of an render view. - - The invoked javascript function must send a result back via the - domAutomationController.send function, or this function will never return. - - Args: - js: script to be executed. - view: A dictionary representing a unique id for the render view as - returned for example by. - self.GetBrowserInfo()['extension_views'][]['view']. - Example: - { 'render_process_id': 1, - 'render_view_id' : 2} - - frame_xpath: XPath of the frame to execute the script. Default is no - frame. Example: - '//frames[1]' - - Returns: - a value that was sent back via the domAutomationController.send method - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'ExecuteJavascriptInRenderView', - 'javascript' : js, - 'view' : view, - 'frame_xpath' : frame_xpath, - } - result = self._GetResultFromJSONRequest(cmd_dict, windex=None)['result'] - # Wrap result in an array before deserializing because valid JSON has an - # array or an object as the root. - json_string = '[' + result + ']' - return json.loads(json_string)[0] - - def ExecuteJavascriptInOOBEWebUI(self, js, frame_xpath=''): - """Executes a script in the specified frame of the OOBE WebUI. - - By default, execute the script in the top frame of the OOBE window. This - also works for all OOBE pages, including the enterprise enrollment - screen and login page. The invoked javascript function must send a result - back via the domAutomationController.send function, or this function will - never return. - - Args: - js: Script to be executed. - frame_xpath: XPath of the frame to execute the script. Default is no - frame. Example: '//frames[1]' - - Returns: - A value that was sent back via the domAutomationController.send method. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'ExecuteJavascriptInOOBEWebUI', - - 'javascript': js, - 'frame_xpath': frame_xpath, - } - result = self._GetResultFromJSONRequest(cmd_dict, windex=None)['result'] - # Wrap result in an array before deserializing because valid JSON has an - # array or an object as the root. - return json.loads('[' + result + ']')[0] - - - def GetDOMValue(self, expr, tab_index=0, windex=0, frame_xpath=''): - """Executes a Javascript expression and returns the value. - - This is a wrapper for ExecuteJavascript, eliminating the need to - explicitly call domAutomationController.send function. - - Args: - expr: expression value to be returned. - tab_index: index of the tab. - windex: index of the window. - frame_xpath: XPath of the frame to execute the script. Default is no - frame. Example: '//frames[1]'. - - Returns: - a string that was sent back via the domAutomationController.send method. - """ - js = 'window.domAutomationController.send(%s);' % expr - return self.ExecuteJavascript(js, tab_index, windex, frame_xpath) - - def CallJavascriptFunc(self, function, args=[], tab_index=0, windex=0): - """Executes a script which calls a given javascript function. - - The invoked javascript function must send a result back via the - domAutomationController.send function, or this function will never return. - - Defaults to first tab in first window. - - Args: - function: name of the function. - args: list of all the arguments to pass into the called function. These - should be able to be converted to a string using the |str| function. - tab_index: index of the tab within the given window. - windex: index of the window. - - Returns: - a string that was sent back via the domAutomationController.send method - """ - converted_args = map(lambda arg: json.dumps(arg), args) - js = '%s(%s)' % (function, ', '.join(converted_args)) - logging.debug('Executing javascript: %s', js) - return self.ExecuteJavascript(js, tab_index, windex) - - def HeapProfilerDump(self, process_type, reason, tab_index=0, windex=0): - """Dumps a heap profile. It works only on Linux and ChromeOS. - - We need an environment variable "HEAPPROFILE" set to a directory and a - filename prefix, for example, "/tmp/prof". In a case of this example, - heap profiles will be dumped into "/tmp/prof.(pid).0002.heap", - "/tmp/prof.(pid).0003.heap", and so on. Nothing happens when this - function is called without the env. - - Also, this requires the --enable-memory-benchmarking command line flag. - - Args: - process_type: A string which is one of 'browser' or 'renderer'. - reason: A string which describes the reason for dumping a heap profile. - The reason will be included in the logged message. - Examples: - 'To check memory leaking' - 'For PyAuto tests' - tab_index: tab index to work on if 'process_type' == 'renderer'. - Defaults to 0 (first tab). - windex: window index to work on if 'process_type' == 'renderer'. - Defaults to 0 (first window). - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - assert process_type in ('browser', 'renderer') - if self.IsLinux(): # IsLinux() also implies IsChromeOS(). - js = """ - if (!chrome.memoryBenchmarking || - !chrome.memoryBenchmarking.isHeapProfilerRunning()) { - domAutomationController.send('memory benchmarking disabled'); - } else { - chrome.memoryBenchmarking.heapProfilerDump("%s", "%s"); - domAutomationController.send('success'); - } - """ % (process_type, reason.replace('"', '\\"')) - result = self.ExecuteJavascript(js, tab_index, windex) - if result != 'success': - raise JSONInterfaceError('Heap profiler dump failed: ' + result) - else: - logging.warn('Heap-profiling is not supported in this OS.') - - def GetNTPThumbnails(self): - """Return a list of info about the sites in the NTP most visited section. - SAMPLE: - [{ u'title': u'Google', - u'url': u'http://www.google.com'}, - { - u'title': u'Yahoo', - u'url': u'http://www.yahoo.com'}] - """ - return self._GetNTPInfo()['most_visited'] - - def GetNTPThumbnailIndex(self, thumbnail): - """Returns the index of the given NTP thumbnail, or -1 if it is not shown. - - Args: - thumbnail: a thumbnail dict received from |GetNTPThumbnails| - """ - thumbnails = self.GetNTPThumbnails() - for i in range(len(thumbnails)): - if thumbnails[i]['url'] == thumbnail['url']: - return i - return -1 - - def RemoveNTPThumbnail(self, thumbnail): - """Removes the NTP thumbnail and returns true on success. - - Args: - thumbnail: a thumbnail dict received from |GetNTPThumbnails| - """ - self._CheckNTPThumbnailShown(thumbnail) - cmd_dict = { - 'command': 'RemoveNTPMostVisitedThumbnail', - 'url': thumbnail['url'] - } - self._GetResultFromJSONRequest(cmd_dict) - - def RestoreAllNTPThumbnails(self): - """Restores all the removed NTP thumbnails. - Note: - the default thumbnails may come back into the Most Visited sites - section after doing this - """ - cmd_dict = { - 'command': 'RestoreAllNTPMostVisitedThumbnails' - } - self._GetResultFromJSONRequest(cmd_dict) - - def GetNTPDefaultSites(self): - """Returns a list of URLs for all the default NTP sites, regardless of - whether they are showing or not. - - These sites are the ones present in the NTP on a fresh install of Chrome. - """ - return self._GetNTPInfo()['default_sites'] - - def RemoveNTPDefaultThumbnails(self): - """Removes all thumbnails for default NTP sites, regardless of whether they - are showing or not.""" - cmd_dict = { 'command': 'RemoveNTPMostVisitedThumbnail' } - for site in self.GetNTPDefaultSites(): - cmd_dict['url'] = site - self._GetResultFromJSONRequest(cmd_dict) - - def GetNTPRecentlyClosed(self): - """Return a list of info about the items in the NTP recently closed section. - SAMPLE: - [{ - u'type': u'tab', - u'url': u'http://www.bing.com', - u'title': u'Bing', - u'timestamp': 2139082.03912, # Seconds since epoch (Jan 1, 1970) - u'direction': u'ltr'}, - { - u'type': u'window', - u'timestamp': 2130821.90812, - u'tabs': [ - { - u'type': u'tab', - u'url': u'http://www.cnn.com', - u'title': u'CNN', - u'timestamp': 2129082.12098, - u'direction': u'ltr'}]}, - { - u'type': u'tab', - u'url': u'http://www.altavista.com', - u'title': u'Altavista', - u'timestamp': 21390820.12903, - u'direction': u'rtl'}] - """ - return self._GetNTPInfo()['recently_closed'] - - def GetNTPApps(self): - """Retrieves information about the apps listed on the NTP. - - In the sample data below, the "launch_type" will be one of the following - strings: "pinned", "regular", "fullscreen", "window", or "unknown". - - SAMPLE: - [ - { - u'app_launch_index': 2, - u'description': u'Web Store', - u'icon_big': u'chrome://theme/IDR_APP_DEFAULT_ICON', - u'icon_small': u'chrome://favicon/https://chrome.google.com/webstore', - u'id': u'ahfgeienlihckogmohjhadlkjgocpleb', - u'is_component_extension': True, - u'is_disabled': False, - u'launch_container': 2, - u'launch_type': u'regular', - u'launch_url': u'https://chrome.google.com/webstore', - u'name': u'Chrome Web Store', - u'options_url': u'', - }, - { - u'app_launch_index': 1, - u'description': u'A countdown app', - u'icon_big': (u'chrome-extension://aeabikdlfbfeihglecobdkdflahfgcpd/' - u'countdown128.png'), - u'icon_small': (u'chrome://favicon/chrome-extension://' - u'aeabikdlfbfeihglecobdkdflahfgcpd/' - u'launchLocalPath.html'), - u'id': u'aeabikdlfbfeihglecobdkdflahfgcpd', - u'is_component_extension': False, - u'is_disabled': False, - u'launch_container': 2, - u'launch_type': u'regular', - u'launch_url': (u'chrome-extension://aeabikdlfbfeihglecobdkdflahfgcpd/' - u'launchLocalPath.html'), - u'name': u'Countdown', - u'options_url': u'', - } - ] - - Returns: - A list of dictionaries in which each dictionary contains the information - for a single app that appears in the "Apps" section of the NTP. - """ - return self._GetNTPInfo()['apps'] - - def _GetNTPInfo(self): - """Get info about the New Tab Page (NTP). - - This does not retrieve the actual info displayed in a particular NTP; it - retrieves the current state of internal data that would be used to display - an NTP. This includes info about the apps, the most visited sites, - the recently closed tabs and windows, and the default NTP sites. - - SAMPLE: - { - u'apps': [ ... ], - u'most_visited': [ ... ], - u'recently_closed': [ ... ], - u'default_sites': [ ... ] - } - - Returns: - A dictionary containing all the NTP info. See details about the different - sections in their respective methods: GetNTPApps(), GetNTPThumbnails(), - GetNTPRecentlyClosed(), and GetNTPDefaultSites(). - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'GetNTPInfo', - } - return self._GetResultFromJSONRequest(cmd_dict) - - def _CheckNTPThumbnailShown(self, thumbnail): - if self.GetNTPThumbnailIndex(thumbnail) == -1: - raise NTPThumbnailNotShownError() - - def LaunchApp(self, app_id, windex=0): - """Opens the New Tab Page and launches the specified app from it. - - This method will not return until after the contents of a new tab for the - launched app have stopped loading. - - Args: - app_id: The string ID of the app to launch. - windex: The index of the browser window to work on. Defaults to 0 (the - first window). - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - self.AppendTab(GURL('chrome://newtab'), windex) # Also activates this tab. - cmd_dict = { - 'command': 'LaunchApp', - 'id': app_id, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - def SetAppLaunchType(self, app_id, launch_type, windex=0): - """Sets the launch type for the specified app. - - Args: - app_id: The string ID of the app whose launch type should be set. - launch_type: The string launch type, which must be one of the following: - 'pinned': Launch in a pinned tab. - 'regular': Launch in a regular tab. - 'fullscreen': Launch in a fullscreen tab. - 'window': Launch in a new browser window. - windex: The index of the browser window to work on. Defaults to 0 (the - first window). - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - self.assertTrue(launch_type in ('pinned', 'regular', 'fullscreen', - 'window'), - msg='Unexpected launch type value: "%s"' % launch_type) - cmd_dict = { - 'command': 'SetAppLaunchType', - 'id': app_id, - 'launch_type': launch_type, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - def GetV8HeapStats(self, tab_index=0, windex=0): - """Returns statistics about the v8 heap in the renderer process for a tab. - - Args: - tab_index: The tab index, default is 0. - window_index: The window index, default is 0. - - Returns: - A dictionary containing v8 heap statistics. Memory values are in bytes. - Example: - { 'renderer_id': 6223, - 'v8_memory_allocated': 21803776, - 'v8_memory_used': 10565392 } - """ - cmd_dict = { # Prepare command for the json interface. - 'command': 'GetV8HeapStats', - 'tab_index': tab_index, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - def GetFPS(self, tab_index=0, windex=0): - """Returns the current FPS associated with the renderer process for a tab. - - FPS is the rendered frames per second. - - Args: - tab_index: The tab index, default is 0. - window_index: The window index, default is 0. - - Returns: - A dictionary containing FPS info. - Example: - { 'renderer_id': 23567, - 'routing_id': 1, - 'fps': 29.404298782348633 } - """ - cmd_dict = { # Prepare command for the json interface. - 'command': 'GetFPS', - 'tab_index': tab_index, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=windex) - - def IsFullscreenForBrowser(self, windex=0): - """Returns true if the window is currently fullscreen and was initially - transitioned to fullscreen by a browser (vs tab) mode transition.""" - return self._GetResultFromJSONRequest( - { 'command': 'IsFullscreenForBrowser' }, - windex=windex).get('result') - - def IsFullscreenForTab(self, windex=0): - """Returns true if fullscreen has been caused by a tab.""" - return self._GetResultFromJSONRequest( - { 'command': 'IsFullscreenForTab' }, - windex=windex).get('result') - - def IsMouseLocked(self, windex=0): - """Returns true if the mouse is currently locked.""" - return self._GetResultFromJSONRequest( - { 'command': 'IsMouseLocked' }, - windex=windex).get('result') - - def IsMouseLockPermissionRequested(self, windex=0): - """Returns true if the user is currently prompted to give permision for - mouse lock.""" - return self._GetResultFromJSONRequest( - { 'command': 'IsMouseLockPermissionRequested' }, - windex=windex).get('result') - - def IsFullscreenPermissionRequested(self, windex=0): - """Returns true if the user is currently prompted to give permision for - fullscreen.""" - return self._GetResultFromJSONRequest( - { 'command': 'IsFullscreenPermissionRequested' }, - windex=windex).get('result') - - def IsFullscreenBubbleDisplayed(self, windex=0): - """Returns true if the fullscreen and mouse lock bubble is currently - displayed.""" - return self._GetResultFromJSONRequest( - { 'command': 'IsFullscreenBubbleDisplayed' }, - windex=windex).get('result') - - def IsFullscreenBubbleDisplayingButtons(self, windex=0): - """Returns true if the fullscreen and mouse lock bubble is currently - displayed and presenting buttons.""" - return self._GetResultFromJSONRequest( - { 'command': 'IsFullscreenBubbleDisplayingButtons' }, - windex=windex).get('result') - - def AcceptCurrentFullscreenOrMouseLockRequest(self, windex=0): - """Activate the accept button on the fullscreen and mouse lock bubble.""" - return self._GetResultFromJSONRequest( - { 'command': 'AcceptCurrentFullscreenOrMouseLockRequest' }, - windex=windex) - - def DenyCurrentFullscreenOrMouseLockRequest(self, windex=0): - """Activate the deny button on the fullscreen and mouse lock bubble.""" - return self._GetResultFromJSONRequest( - { 'command': 'DenyCurrentFullscreenOrMouseLockRequest' }, - windex=windex) - - def KillRendererProcess(self, pid): - """Kills the given renderer process. - - This will return only after the browser has received notice of the renderer - close. - - Args: - pid: the process id of the renderer to kill - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'KillRendererProcess', - 'pid': pid - } - return self._GetResultFromJSONRequest(cmd_dict) - - def NewWebDriver(self, port=0): - """Returns a new remote WebDriver instance. - - Args: - port: The port to start WebDriver on; by default the service selects an - open port. It is an error to request a port number and request a - different port later. - - Returns: - selenium.webdriver.remote.webdriver.WebDriver instance - """ - from chrome_driver_factory import ChromeDriverFactory - global _CHROME_DRIVER_FACTORY - if _CHROME_DRIVER_FACTORY is None: - _CHROME_DRIVER_FACTORY = ChromeDriverFactory(port=port) - self.assertTrue(_CHROME_DRIVER_FACTORY.GetPort() == port or port == 0, - msg='Requested a WebDriver on a specific port while already' - ' running on a different port.') - return _CHROME_DRIVER_FACTORY.NewChromeDriver(self) - - def CreateNewAutomationProvider(self, channel_id): - """Creates a new automation provider. - - The provider will open a named channel in server mode. - Args: - channel_id: the channel_id to open the server channel with - """ - cmd_dict = { - 'command': 'CreateNewAutomationProvider', - 'channel_id': channel_id - } - self._GetResultFromJSONRequest(cmd_dict) - - def OpenNewBrowserWindowWithNewProfile(self): - """Creates a new multi-profiles user, and then opens and shows a new - tabbed browser window with the new profile. - - This is equivalent to 'Add new user' action with multi-profiles. - - To account for crbug.com/108761 on Win XP, this call polls until the - profile count increments by 1. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - num_profiles = len(self.GetMultiProfileInfo()['profiles']) - cmd_dict = { # Prepare command for the json interface - 'command': 'OpenNewBrowserWindowWithNewProfile' - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - # TODO(nirnimesh): Remove when crbug.com/108761 is fixed - self.WaitUntil( - lambda: len(self.GetMultiProfileInfo()['profiles']), - expect_retval=(num_profiles + 1)) - - def OpenProfileWindow(self, path, num_loads=1): - """Open browser window for an existing profile. - - This is equivalent to picking a profile from the multi-profile menu. - - Multi-profile should be enabled and the requested profile should already - exist. Creates a new window for the given profile. Use - OpenNewBrowserWindowWithNewProfile() to create a new profile. - - Args: - path: profile path of the profile to be opened. - num_loads: the number of loads to wait for, when a new browser window - is created. Useful when restoring a window with many tabs. - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'OpenProfileWindow', - 'path': path, - 'num_loads': num_loads, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetMultiProfileInfo(self): - """Fetch info about all multi-profile users. - - Returns: - A dictionary. - Sample: - { - 'enabled': True, - 'profiles': [{'name': 'First user', - 'path': '/tmp/.org.chromium.Chromium.Tyx17X/Default'}, - {'name': 'User 1', - 'path': '/tmp/.org.chromium.Chromium.Tyx17X/profile_1'}], - } - - Profiles will be listed in the same order as visible in preferences. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { # Prepare command for the json interface - 'command': 'GetMultiProfileInfo' - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def RefreshPolicies(self): - """Refreshes all the available policy providers. - - Each policy provider will reload its policy source and push the updated - policies. This call waits for the new policies to be applied; any policies - installed before this call is issued are guaranteed to be ready after it - returns. - """ - # TODO(craigdh): Determine the root cause of RefreshPolicies' flakiness. - # See crosbug.com/30221 - timeout = PyUITest.ActionTimeoutChanger(self, 3 * 60 * 1000) - cmd_dict = { 'command': 'RefreshPolicies' } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def SubmitForm(self, form_id, tab_index=0, windex=0, frame_xpath=''): - """Submits the given form ID, and returns after it has been submitted. - - Args: - form_id: the id attribute of the form to submit. - - Returns: true on success. - """ - js = """ - document.getElementById("%s").submit(); - window.addEventListener("unload", function() { - window.domAutomationController.send("done"); - }); - """ % form_id - if self.ExecuteJavascript(js, tab_index, windex, frame_xpath) != 'done': - return False - # Wait until the form is submitted and the page completes loading. - return self.WaitUntil( - lambda: self.GetDOMValue('document.readyState', - tab_index, windex, frame_xpath), - expect_retval='complete') - - def SimulateAsanMemoryBug(self): - """Simulates a memory bug for Address Sanitizer to catch. - - Address Sanitizer (if it was built it) will catch the bug and abort - the process. - This method returns immediately before it actually causes a crash. - """ - cmd_dict = { 'command': 'SimulateAsanMemoryBug' } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - ## ChromeOS section - - def GetLoginInfo(self): - """Returns information about login and screen locker state. - - This includes things like whether a user is logged in, the username - of the logged in user, and whether the screen is locked. - - Returns: - A dictionary. - Sample: - { u'is_guest': False, - u'is_owner': True, - u'email': u'example@gmail.com', - u'user_image': 2, # non-negative int, 'profile', 'file' - u'is_screen_locked': False, - u'login_ui_type': 'nativeui', # or 'webui' - u'is_logged_in': True} - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'GetLoginInfo' } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def WaitForSessionManagerRestart(self, function): - """Call a function and wait for the ChromeOS session_manager to restart. - - Args: - function: The function to call. - """ - assert callable(function) - pgrep_process = subprocess.Popen(['pgrep', 'session_manager'], - stdout=subprocess.PIPE) - old_pid = pgrep_process.communicate()[0].strip() - function() - return self.WaitUntil(lambda: self._IsSessionManagerReady(old_pid)) - - def _WaitForInodeChange(self, path, function): - """Call a function and wait for the specified file path to change. - - Args: - path: The file path to check for changes. - function: The function to call. - """ - assert callable(function) - old_inode = os.stat(path).st_ino - function() - return self.WaitUntil(lambda: self._IsInodeNew(path, old_inode)) - - def ShowCreateAccountUI(self): - """Go to the account creation page. - - This is the same as clicking the "Create Account" link on the - ChromeOS login screen. Does not actually create a new account. - Should be displaying the login screen to work. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'ShowCreateAccountUI' } - # See note below under LoginAsGuest(). ShowCreateAccountUI() logs - # the user in as guest in order to access the account creation page. - assert self._WaitForInodeChange( - self._named_channel_id, - lambda: self._GetResultFromJSONRequest(cmd_dict, windex=None)), \ - 'Chrome did not reopen the testing channel after login as guest.' - self.SetUp() - - def SkipToLogin(self, skip_image_selection=True): - """Skips OOBE to the login screen. - - Assumes that we're at the beginning of OOBE. - - Args: - skip_image_selection: Boolean indicating whether the user image selection - screen should also be skipped. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'SkipToLogin', - 'skip_image_selection': skip_image_selection } - result = self._GetResultFromJSONRequest(cmd_dict, windex=None) - assert result['next_screen'] == 'login', 'Unexpected wizard transition' - - def GetOOBEScreenInfo(self): - """Queries info about the current OOBE screen. - - Returns: - A dictionary with the following keys: - - 'screen_name': The title of the current OOBE screen as a string. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'GetOOBEScreenInfo' } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def AcceptOOBENetworkScreen(self): - """Accepts OOBE network screen and advances to the next one. - - Assumes that we're already at the OOBE network screen. - - Returns: - A dictionary with the following keys: - - 'next_screen': The title of the next OOBE screen as a string. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'AcceptOOBENetworkScreen' } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def AcceptOOBEEula(self, accepted, usage_stats_reporting=False): - """Accepts OOBE EULA and advances to the next screen. - - Assumes that we're already at the OOBE EULA screen. - - Args: - accepted: Boolean indicating whether the EULA should be accepted. - usage_stats_reporting: Boolean indicating whether UMA should be enabled. - - Returns: - A dictionary with the following keys: - - 'next_screen': The title of the next OOBE screen as a string. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'AcceptOOBEEula', - 'accepted': accepted, - 'usage_stats_reporting': usage_stats_reporting } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def CancelOOBEUpdate(self): - """Skips update on OOBE and advances to the next screen. - - Returns: - A dictionary with the following keys: - - 'next_screen': The title of the next OOBE screen as a string. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'CancelOOBEUpdate' } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def PickUserImage(self, image): - """Chooses image for the newly created user. - - Should be called immediately after login. - - Args: - image_type: type of user image to choose. Possible values: - - "profile": Google profile image - - non-negative int: one of the default images - - Returns: - A dictionary with the following keys: - - 'next_screen': The title of the next OOBE screen as a string. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'PickUserImage', - 'image': image } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def LoginAsGuest(self): - """Login to chromeos as a guest user. - - Waits until logged in. - Should be displaying the login screen to work. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'LoginAsGuest' } - # Currently, logging in as guest causes session_manager to - # restart Chrome, which will close the testing channel. - # We need to call SetUp() again to reconnect to the new channel. - assert self._WaitForInodeChange( - self._named_channel_id, - lambda: self._GetResultFromJSONRequest(cmd_dict, windex=None)), \ - 'Chrome did not reopen the testing channel after login as guest.' - self.SetUp() - - def Login(self, username, password, timeout=120 * 1000): - """Login to chromeos. - - Waits until logged in and browser is ready. - Should be displaying the login screen to work. - - Note that in case of webui auth-extension-based login, gaia auth errors - will not be noticed here, because the browser has no knowledge of it. In - this case the GetNextEvent automation command will always time out. - - Args: - username: the username to log in as. - password: the user's password. - timeout: timeout in ms; defaults to two minutes. - - Returns: - An error string if an error occured. - None otherwise. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - self._GetResultFromJSONRequest({'command': 'AddLoginEventObserver'}, - windex=None) - cmd_dict = { - 'command': 'SubmitLoginForm', - 'username': username, - 'password': password, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - self.AddDomEventObserver('loginfail', automation_id=4444) - try: - if self.GetNextEvent(timeout=timeout).get('name') == 'loginfail': - raise JSONInterfaceError('Login denied by auth server.') - except JSONInterfaceError as e: - raise JSONInterfaceError('Login failed. Perhaps Chrome crashed, ' - 'failed to start, or the login flow is ' - 'broken? Error message: %s' % str(e)) - - def Logout(self): - """Log out from ChromeOS and wait for session_manager to come up. - - This is equivalent to pressing the 'Sign out' button from the - aura shell tray when logged in. - - Should be logged in to work. Re-initializes the automation channel - after logout. - """ - clear_profile_orig = self.get_clear_profile() - self.set_clear_profile(False) - assert self.GetLoginInfo()['is_logged_in'], \ - 'Trying to log out when already logged out.' - def _SignOut(): - cmd_dict = { 'command': 'SignOut' } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - assert self.WaitForSessionManagerRestart(_SignOut), \ - 'Session manager did not restart after logout.' - self.__SetUp() - self.set_clear_profile(clear_profile_orig) - - def LockScreen(self): - """Locks the screen on chromeos. - - Waits until screen is locked. - Should be logged in and screen should not be locked to work. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'LockScreen' } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def UnlockScreen(self, password): - """Unlocks the screen on chromeos, authenticating the user's password first. - - Waits until screen is unlocked. - Screen locker should be active for this to work. - - Returns: - An error string if an error occured. - None otherwise. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'UnlockScreen', - 'password': password, - } - result = self._GetResultFromJSONRequest(cmd_dict, windex=None) - return result.get('error_string') - - def SignoutInScreenLocker(self): - """Signs out of chromeos using the screen locker's "Sign out" feature. - - Effectively the same as clicking the "Sign out" link on the screen locker. - Screen should be locked for this to work. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'SignoutInScreenLocker' } - assert self.WaitForSessionManagerRestart( - lambda: self._GetResultFromJSONRequest(cmd_dict, windex=None)), \ - 'Session manager did not restart after logout.' - self.__SetUp() - - def GetBatteryInfo(self): - """Get details about battery state. - - Returns: - A dictionary with the following keys: - - 'battery_is_present': bool - 'line_power_on': bool - if 'battery_is_present': - 'battery_percentage': float (0 ~ 100) - 'battery_fully_charged': bool - if 'line_power_on': - 'battery_time_to_full': int (seconds) - else: - 'battery_time_to_empty': int (seconds) - - If it is still calculating the time left, 'battery_time_to_full' - and 'battery_time_to_empty' will be absent. - - Use 'battery_fully_charged' instead of 'battery_percentage' - or 'battery_time_to_full' to determine whether the battery - is fully charged, since the percentage is only approximate. - - Sample: - { u'battery_is_present': True, - u'line_power_on': False, - u'battery_time_to_empty': 29617, - u'battery_percentage': 100.0, - u'battery_fully_charged': False } - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'GetBatteryInfo' } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetPanelInfo(self): - """Get details about open ChromeOS panels. - - A panel is actually a type of browser window, so all of - this information is also available using GetBrowserInfo(). - - Returns: - A dictionary. - Sample: - [{ 'incognito': False, - 'renderer_pid': 4820, - 'title': u'Downloads', - 'url': u'chrome://active-downloads/'}] - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - panels = [] - for browser in self.GetBrowserInfo()['windows']: - if browser['type'] != 'panel': - continue - - panel = {} - panels.append(panel) - tab = browser['tabs'][0] - panel['incognito'] = browser['incognito'] - panel['renderer_pid'] = tab['renderer_pid'] - panel['title'] = self.GetActiveTabTitle(browser['index']) - panel['url'] = tab['url'] - - return panels - - def EnableSpokenFeedback(self, enabled): - """Enables or disables spoken feedback accessibility mode. - - Args: - enabled: Boolean value indicating the desired state of spoken feedback. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'EnableSpokenFeedback', - 'enabled': enabled, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def IsSpokenFeedbackEnabled(self): - """Check whether spoken feedback accessibility mode is enabled. - - Returns: - True if spoken feedback is enabled, False otherwise. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'IsSpokenFeedbackEnabled', } - result = self._GetResultFromJSONRequest(cmd_dict, windex=None) - return result.get('spoken_feedback') - - def GetTimeInfo(self, windex=0): - """Gets info about the ChromeOS status bar clock. - - Set the 24-hour clock by using: - self.SetPrefs('settings.clock.use_24hour_clock', True) - - Returns: - a dictionary. - Sample: - {u'display_date': u'Tuesday, July 26, 2011', - u'display_time': u'4:30', - u'timezone': u'America/Los_Angeles'} - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'GetTimeInfo' } - if self.GetLoginInfo()['is_logged_in']: - return self._GetResultFromJSONRequest(cmd_dict, windex=windex) - else: - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def SetTimezone(self, timezone): - """Sets the timezone on ChromeOS. A user must be logged in. - - The timezone is the relative path to the timezone file in - /usr/share/zoneinfo. For example, /usr/share/zoneinfo/America/Los_Angeles is - 'America/Los_Angeles'. For a list of valid timezones see - 'chromeos/settings/timezone_settings.cc'. - - This method does not return indication of success or failure. - If the timezone is it falls back to a valid timezone. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { - 'command': 'SetTimezone', - 'timezone': timezone, - } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def UpdateCheck(self): - """Checks for a ChromeOS update. Blocks until finished updating. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'UpdateCheck' } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def GetVolumeInfo(self): - """Gets the volume and whether the device is muted. - - Returns: - a tuple. - Sample: - (47.763456790123456, False) - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'GetVolumeInfo' } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def SetVolume(self, volume): - """Sets the volume on ChromeOS. Only valid if not muted. - - Args: - volume: The desired volume level as a percent from 0 to 100. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - assert volume >= 0 and volume <= 100 - cmd_dict = { - 'command': 'SetVolume', - 'volume': float(volume), - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def SetMute(self, mute): - """Sets whether ChromeOS is muted or not. - - Args: - mute: True to mute, False to unmute. - - Raises: - pyauto_errors.JSONInterfaceError if the automation call returns an error. - """ - cmd_dict = { 'command': 'SetMute' } - cmd_dict = { - 'command': 'SetMute', - 'mute': mute, - } - return self._GetResultFromJSONRequest(cmd_dict, windex=None) - - # HTML Terminal - - def OpenCrosh(self): - """Open crosh. - - Equivalent to pressing Ctrl-Alt-t. - Opens in the last active (non-incognito) window. - - Waits long enough for crosh to load, but does not wait for the crosh - prompt. Use WaitForHtermText() for that. - """ - cmd_dict = { 'command': 'OpenCrosh' } - self._GetResultFromJSONRequest(cmd_dict, windex=None) - - def WaitForHtermText(self, text, msg=None, tab_index=0, windex=0): - """Waits for the given text in a hterm tab. - - Can be used to wait for the crosh> prompt or ssh prompt. - - This does not poll. It uses dom mutation observers to wait - for the given text to show up. - - Args: - text: the text to wait for. Can be a regex. - msg: the failure message to emit if the text could not be found. - tab_index: the tab for the hterm tab. Default: 0. - windex: the window index for the hterm tab. Default: 0. - """ - self.WaitForDomNode( - xpath='//*[contains(text(), "%s")]' % text, frame_xpath='//iframe', - msg=msg, tab_index=tab_index, windex=windex) - - def GetHtermRowsText(self, start, end, tab_index=0, windex=0): - """Fetch rows from a html terminal tab. - - Works for both crosh and ssh tab. - Uses term_.getRowsText(start, end) javascript call. - - Args: - start: start line number (0-based). - end: the end line (one beyond the line of interest). - tab_index: the tab for the hterm tab. Default: 0. - windex: the window index for the hterm tab. Default: 0. - """ - return self.ExecuteJavascript( - 'domAutomationController.send(term_.getRowsText(%d, %d))' % ( - start, end), - tab_index=tab_index, windex=windex) - - def SendKeysToHterm(self, text, tab_index=0, windex=0): - """Send keys to a html terminal tab. - - Works for both crosh and ssh tab. - Uses term_.onVTKeystroke(str) javascript call. - - Args: - text: the text to send. - tab_index: the tab for the hterm tab. Default: 0. - windex: the window index for the hterm tab. Default: 0. - """ - return self.ExecuteJavascript( - 'term_.onVTKeystroke("%s");' - 'domAutomationController.send("done")' % text, - tab_index=tab_index, windex=windex) - - - def GetMemoryStatsChromeOS(self, duration): - """Identifies and returns different kinds of current memory usage stats. - - This function samples values each second for |duration| seconds, then - outputs the min, max, and ending values for each measurement type. - - Args: - duration: The number of seconds to sample data before outputting the - minimum, maximum, and ending values for each measurement type. - - Returns: - A dictionary containing memory usage information. Each measurement type - is associated with the min, max, and ending values from among all - sampled values. Values are specified in KB. - { - 'gem_obj': { # GPU memory usage. - 'min': ..., - 'max': ..., - 'end': ..., - }, - 'gtt': { ... }, # GPU memory usage (graphics translation table). - 'mem_free': { ... }, # CPU free memory. - 'mem_available': { ... }, # CPU available memory. - 'mem_shared': { ... }, # CPU shared memory. - 'mem_cached': { ... }, # CPU cached memory. - 'mem_anon': { ... }, # CPU anon memory (active + inactive). - 'mem_file': { ... }, # CPU file memory (active + inactive). - 'mem_slab': { ... }, # CPU slab memory. - 'browser_priv': { ... }, # Chrome browser private memory. - 'browser_shared': { ... }, # Chrome browser shared memory. - 'gpu_priv': { ... }, # Chrome GPU private memory. - 'gpu_shared': { ... }, # Chrome GPU shared memory. - 'renderer_priv': { ... }, # Total private memory of all renderers. - 'renderer_shared': { ... }, # Total shared memory of all renderers. - } - """ - logging.debug('Sampling memory information for %d seconds...' % duration) - stats = {} - - for _ in xrange(duration): - # GPU memory. - gem_obj_path = '/sys/kernel/debug/dri/0/i915_gem_objects' - if os.path.exists(gem_obj_path): - p = subprocess.Popen('grep bytes %s' % gem_obj_path, - stdout=subprocess.PIPE, shell=True) - stdout = p.communicate()[0] - - gem_obj = re.search( - '\d+ objects, (\d+) bytes\n', stdout).group(1) - if 'gem_obj' not in stats: - stats['gem_obj'] = [] - stats['gem_obj'].append(int(gem_obj) / 1024.0) - - gtt_path = '/sys/kernel/debug/dri/0/i915_gem_gtt' - if os.path.exists(gtt_path): - p = subprocess.Popen('grep bytes %s' % gtt_path, - stdout=subprocess.PIPE, shell=True) - stdout = p.communicate()[0] - - gtt = re.search( - 'Total [\d]+ objects, ([\d]+) bytes', stdout).group(1) - if 'gtt' not in stats: - stats['gtt'] = [] - stats['gtt'].append(int(gtt) / 1024.0) - - # CPU memory. - stdout = '' - with open('/proc/meminfo') as f: - stdout = f.read() - mem_free = re.search('MemFree:\s*([\d]+) kB', stdout).group(1) - - if 'mem_free' not in stats: - stats['mem_free'] = [] - stats['mem_free'].append(int(mem_free)) - - mem_dirty = re.search('Dirty:\s*([\d]+) kB', stdout).group(1) - mem_active_file = re.search( - 'Active\(file\):\s*([\d]+) kB', stdout).group(1) - mem_inactive_file = re.search( - 'Inactive\(file\):\s*([\d]+) kB', stdout).group(1) - - with open('/proc/sys/vm/min_filelist_kbytes') as f: - mem_min_file = f.read() - - # Available memory = - # MemFree + ActiveFile + InactiveFile - DirtyMem - MinFileMem - if 'mem_available' not in stats: - stats['mem_available'] = [] - stats['mem_available'].append( - int(mem_free) + int(mem_active_file) + int(mem_inactive_file) - - int(mem_dirty) - int(mem_min_file)) - - mem_shared = re.search('Shmem:\s*([\d]+) kB', stdout).group(1) - if 'mem_shared' not in stats: - stats['mem_shared'] = [] - stats['mem_shared'].append(int(mem_shared)) - - mem_cached = re.search('Cached:\s*([\d]+) kB', stdout).group(1) - if 'mem_cached' not in stats: - stats['mem_cached'] = [] - stats['mem_cached'].append(int(mem_cached)) - - mem_anon_active = re.search('Active\(anon\):\s*([\d]+) kB', - stdout).group(1) - mem_anon_inactive = re.search('Inactive\(anon\):\s*([\d]+) kB', - stdout).group(1) - if 'mem_anon' not in stats: - stats['mem_anon'] = [] - stats['mem_anon'].append(int(mem_anon_active) + int(mem_anon_inactive)) - - mem_file_active = re.search('Active\(file\):\s*([\d]+) kB', - stdout).group(1) - mem_file_inactive = re.search('Inactive\(file\):\s*([\d]+) kB', - stdout).group(1) - if 'mem_file' not in stats: - stats['mem_file'] = [] - stats['mem_file'].append(int(mem_file_active) + int(mem_file_inactive)) - - mem_slab = re.search('Slab:\s*([\d]+) kB', stdout).group(1) - if 'mem_slab' not in stats: - stats['mem_slab'] = [] - stats['mem_slab'].append(int(mem_slab)) - - # Chrome process memory. - pinfo = self.GetProcessInfo()['browsers'][0]['processes'] - total_renderer_priv = 0 - total_renderer_shared = 0 - for process in pinfo: - mem_priv = process['working_set_mem']['priv'] - mem_shared = process['working_set_mem']['shared'] - if process['child_process_type'] == 'Browser': - if 'browser_priv' not in stats: - stats['browser_priv'] = [] - stats['browser_priv'].append(int(mem_priv)) - if 'browser_shared' not in stats: - stats['browser_shared'] = [] - stats['browser_shared'].append(int(mem_shared)) - elif process['child_process_type'] == 'GPU': - if 'gpu_priv' not in stats: - stats['gpu_priv'] = [] - stats['gpu_priv'].append(int(mem_priv)) - if 'gpu_shared' not in stats: - stats['gpu_shared'] = [] - stats['gpu_shared'].append(int(mem_shared)) - elif process['child_process_type'] == 'Tab': - # Sum the memory of all renderer processes. - total_renderer_priv += int(mem_priv) - total_renderer_shared += int(mem_shared) - if 'renderer_priv' not in stats: - stats['renderer_priv'] = [] - stats['renderer_priv'].append(int(total_renderer_priv)) - if 'renderer_shared' not in stats: - stats['renderer_shared'] = [] - stats['renderer_shared'].append(int(total_renderer_shared)) - - time.sleep(1) - - # Compute min, max, and ending values to return. - result = {} - for measurement_type in stats: - values = stats[measurement_type] - result[measurement_type] = { - 'min': min(values), - 'max': max(values), - 'end': values[-1], - } - - return result - - ## ChromeOS section -- end - - -class ExtraBrowser(PyUITest): - """Launches a new browser with some extra flags. - - The new browser is launched with its own fresh profile. - This class does not apply to ChromeOS. - """ - def __init__(self, chrome_flags=[], methodName='runTest', **kwargs): - """Accepts extra chrome flags for launching a new browser instance. - - Args: - chrome_flags: list of extra flags when launching a new browser. - """ - assert not PyUITest.IsChromeOS(), \ - 'This function cannot be used to launch a new browser in ChromeOS.' - PyUITest.__init__(self, methodName=methodName, **kwargs) - self._chrome_flags = chrome_flags - PyUITest.setUp(self) - - def __del__(self): - """Tears down the browser and then calls super class's destructor""" - PyUITest.tearDown(self) - PyUITest.__del__(self) - - def ExtraChromeFlags(self): - """Prepares the browser to launch with specified Chrome flags.""" - return PyUITest.ExtraChromeFlags(self) + self._chrome_flags - - -class _RemoteProxy(): - """Class for PyAuto remote method calls. - - Use this class along with RemoteHost.testRemoteHost to establish a PyAuto - connection with another machine and make remote PyAuto calls. The RemoteProxy - mimics a PyAuto object, so all json-style PyAuto calls can be made on it. - - The remote host acts as a dumb executor that receives method call requests, - executes them, and sends all of the results back to the RemoteProxy, including - the return value, thrown exceptions, and console output. - - The remote host should be running the same version of PyAuto as the proxy. - A mismatch could lead to undefined behavior. - - Example usage: - class MyTest(pyauto.PyUITest): - def testRemoteExample(self): - remote = pyauto._RemoteProxy(('127.0.0.1', 7410)) - remote.NavigateToURL('http://www.google.com') - title = remote.GetActiveTabTitle() - self.assertEqual(title, 'Google') - """ - class RemoteException(Exception): - pass - - def __init__(self, host): - self.RemoteConnect(host) - - def RemoteConnect(self, host): - begin = time.time() - while time.time() - begin < 50: - self._socket = socket.socket() - if not self._socket.connect_ex(host): - break - time.sleep(0.25) - else: - # Make one last attempt, but raise a socket error on failure. - self._socket = socket.socket() - self._socket.connect(host) - - def RemoteDisconnect(self): - if self._socket: - self._socket.shutdown(socket.SHUT_RDWR) - self._socket.close() - self._socket = None - - def CreateTarget(self, target): - """Registers the methods and creates a remote instance of a target. - - Any RPC calls will then be made on the remote target instance. Note that the - remote instance will be a brand new instance and will have none of the state - of the local instance. The target's class should have a constructor that - takes no arguments. - """ - self._Call('CreateTarget', target.__class__) - self._RegisterClassMethods(target) - - def _RegisterClassMethods(self, remote_class): - # Make remote-call versions of all remote_class methods. - for method_name, _ in inspect.getmembers(remote_class, inspect.ismethod): - # Ignore private methods and duplicates. - if method_name[0] in string.letters and \ - getattr(self, method_name, None) is None: - setattr(self, method_name, functools.partial(self._Call, method_name)) - - def _Call(self, method_name, *args, **kwargs): - # Send request. - request = pickle.dumps((method_name, args, kwargs)) - if self._socket.send(request) != len(request): - raise self.RemoteException('Error sending remote method call request.') - - # Receive response. - response = self._socket.recv(4096) - if not response: - raise self.RemoteException('Client disconnected during method call.') - result, stdout, stderr, exception = pickle.loads(response) - - # Print any output the client captured, throw any exceptions, and return. - sys.stdout.write(stdout) - sys.stderr.write(stderr) - if exception: - raise self.RemoteException('%s raised by remote client: %s' % - (exception[0], exception[1])) - return result - - -class PyUITestSuite(pyautolib.PyUITestSuiteBase, unittest.TestSuite): - """Base TestSuite for PyAuto UI tests.""" - - def __init__(self, args): - pyautolib.PyUITestSuiteBase.__init__(self, args) - - # Figure out path to chromium binaries - browser_dir = os.path.normpath(os.path.dirname(pyautolib.__file__)) - logging.debug('Loading pyauto libs from %s', browser_dir) - self.InitializeWithPath(pyautolib.FilePath(browser_dir)) - os.environ['PATH'] = browser_dir + os.pathsep + os.environ['PATH'] - - unittest.TestSuite.__init__(self) - cr_source_root = os.path.normpath(os.path.join( - os.path.dirname(__file__), os.pardir, os.pardir, os.pardir)) - self.SetCrSourceRoot(pyautolib.FilePath(cr_source_root)) - - # Start http server, if needed. - global _OPTIONS - if _OPTIONS and not _OPTIONS.no_http_server: - self._StartHTTPServer() - if _OPTIONS and _OPTIONS.remote_host: - self._ConnectToRemoteHosts(_OPTIONS.remote_host.split(',')) - - def __del__(self): - # python unittest module is setup such that the suite gets deleted before - # the test cases, which is odd because our test cases depend on - # initializtions like exitmanager, autorelease pool provided by the - # suite. Forcibly delete the test cases before the suite. - del self._tests - pyautolib.PyUITestSuiteBase.__del__(self) - - global _HTTP_SERVER - if _HTTP_SERVER: - self._StopHTTPServer() - - global _CHROME_DRIVER_FACTORY - if _CHROME_DRIVER_FACTORY is not None: - _CHROME_DRIVER_FACTORY.Stop() - - def _StartHTTPServer(self): - """Start a local file server hosting data files over http://""" - global _HTTP_SERVER - assert not _HTTP_SERVER, 'HTTP Server already started' - http_data_dir = _OPTIONS.http_data_dir - http_server = pyautolib.SpawnedTestServer( - pyautolib.SpawnedTestServer.TYPE_HTTP, - '127.0.0.1', - pyautolib.FilePath(http_data_dir)) - assert http_server.Start(), 'Could not start http server' - _HTTP_SERVER = http_server - logging.debug('Started http server at "%s".', http_data_dir) - - def _StopHTTPServer(self): - """Stop the local http server.""" - global _HTTP_SERVER - assert _HTTP_SERVER, 'HTTP Server not yet started' - assert _HTTP_SERVER.Stop(), 'Could not stop http server' - _HTTP_SERVER = None - logging.debug('Stopped http server.') - - def _ConnectToRemoteHosts(self, addresses): - """Connect to remote PyAuto instances using a RemoteProxy. - - The RemoteHost instances must already be running.""" - global _REMOTE_PROXY - assert not _REMOTE_PROXY, 'Already connected to a remote host.' - _REMOTE_PROXY = [] - for address in addresses: - if address == 'localhost' or address == '127.0.0.1': - self._StartLocalRemoteHost() - _REMOTE_PROXY.append(_RemoteProxy((address, 7410))) - - def _StartLocalRemoteHost(self): - """Start a remote PyAuto instance on the local machine.""" - # Add the path to our main class to the RemoteHost's - # environment, so it can load that class at runtime. - import __main__ - main_path = os.path.dirname(__main__.__file__) - env = os.environ - if env.get('PYTHONPATH', None): - env['PYTHONPATH'] += ':' + main_path - else: - env['PYTHONPATH'] = main_path - - # Run it! - subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), - 'remote_host.py')], env=env) - - -class _GTestTextTestResult(unittest._TextTestResult): - """A test result class that can print formatted text results to a stream. - - Results printed in conformance with gtest output format, like: - [ RUN ] autofill.AutofillTest.testAutofillInvalid: "test desc." - [ OK ] autofill.AutofillTest.testAutofillInvalid - [ RUN ] autofill.AutofillTest.testFillProfile: "test desc." - [ OK ] autofill.AutofillTest.testFillProfile - [ RUN ] autofill.AutofillTest.testFillProfileCrazyCharacters: "Test." - [ OK ] autofill.AutofillTest.testFillProfileCrazyCharacters - """ - def __init__(self, stream, descriptions, verbosity): - unittest._TextTestResult.__init__(self, stream, descriptions, verbosity) - - def _GetTestURI(self, test): - if sys.version_info[:2] <= (2, 4): - return '%s.%s' % (unittest._strclass(test.__class__), - test._TestCase__testMethodName) - return '%s.%s.%s' % (test.__class__.__module__, - test.__class__.__name__, - test._testMethodName) - - def getDescription(self, test): - return '%s: "%s"' % (self._GetTestURI(test), test.shortDescription()) - - def startTest(self, test): - unittest.TestResult.startTest(self, test) - self.stream.writeln('[ RUN ] %s' % self.getDescription(test)) - - def addSuccess(self, test): - unittest.TestResult.addSuccess(self, test) - self.stream.writeln('[ OK ] %s' % self._GetTestURI(test)) - - def addError(self, test, err): - unittest.TestResult.addError(self, test, err) - self.stream.writeln('[ ERROR ] %s' % self._GetTestURI(test)) - - def addFailure(self, test, err): - unittest.TestResult.addFailure(self, test, err) - self.stream.writeln('[ FAILED ] %s' % self._GetTestURI(test)) - - -class PyAutoTextTestRunner(unittest.TextTestRunner): - """Test Runner for PyAuto tests that displays results in textual format. - - Results are displayed in conformance with gtest output. - """ - def __init__(self, verbosity=1): - unittest.TextTestRunner.__init__(self, - stream=sys.stderr, - verbosity=verbosity) - - def _makeResult(self): - return _GTestTextTestResult(self.stream, self.descriptions, self.verbosity) - - -# Implementation inspired from unittest.main() -class Main(object): - """Main program for running PyAuto tests.""" - - _options, _args = None, None - _tests_filename = 'PYAUTO_TESTS' - _platform_map = { - 'win32': 'win', - 'darwin': 'mac', - 'linux2': 'linux', - 'linux3': 'linux', - 'chromeos': 'chromeos', - } - - def __init__(self): - self._ParseArgs() - self._Run() - - def _ParseArgs(self): - """Parse command line args.""" - parser = optparse.OptionParser() - parser.add_option( - '', '--channel-id', type='string', default='', - help='Name of channel id, if using named interface.') - parser.add_option( - '', '--chrome-flags', type='string', default='', - help='Flags passed to Chrome. This is in addition to the usual flags ' - 'like suppressing first-run dialogs, enabling automation. ' - 'See chrome/common/chrome_switches.cc for the list of flags ' - 'chrome understands.') - parser.add_option( - '', '--http-data-dir', type='string', - default=os.path.join('chrome', 'test', 'data'), - help='Relative path from which http server should serve files.') - parser.add_option( - '-L', '--list-tests', action='store_true', default=False, - help='List all tests, and exit.') - parser.add_option( - '--shard', - help='Specify sharding params. Example: 1/3 implies split the list of ' - 'tests into 3 groups of which this is the 1st.') - parser.add_option( - '', '--log-file', type='string', default=None, - help='Provide a path to a file to which the logger will log') - parser.add_option( - '', '--no-http-server', action='store_true', default=False, - help='Do not start an http server to serve files in data dir.') - parser.add_option( - '', '--remote-host', type='string', default=None, - help='Connect to remote hosts for remote automation. If "localhost" ' - '"127.0.0.1" is specified, a remote host will be launched ' - 'automatically on the local machine.') - parser.add_option( - '', '--repeat', type='int', default=1, - help='Number of times to repeat the tests. Useful to determine ' - 'flakiness. Defaults to 1.') - parser.add_option( - '-S', '--suite', type='string', default='FULL', - help='Name of the suite to load. Defaults to "FULL".') - parser.add_option( - '-v', '--verbose', action='store_true', default=False, - help='Make PyAuto verbose.') - parser.add_option( - '-D', '--wait-for-debugger', action='store_true', default=False, - help='Block PyAuto on startup for attaching debugger.') - - self._options, self._args = parser.parse_args() - global _OPTIONS - _OPTIONS = self._options # Export options so other classes can access. - - # Set up logging. All log messages will be prepended with a timestamp. - format = '%(asctime)s %(levelname)-8s %(message)s' - - level = logging.INFO - if self._options.verbose: - level=logging.DEBUG - - logging.basicConfig(level=level, format=format, - filename=self._options.log_file) - - def TestsDir(self): - """Returns the path to dir containing tests. - - This is typically the dir containing the tests description file. - This method should be overridden by derived class to point to other dirs - if needed. - """ - return os.path.dirname(__file__) - - @staticmethod - def _ImportTestsFromName(name): - """Get a list of all test names from the given string. - - Args: - name: dot-separated string for a module, a test case or a test method. - Examples: omnibox (a module) - omnibox.OmniboxTest (a test case) - omnibox.OmniboxTest.testA (a test method) - - Returns: - [omnibox.OmniboxTest.testA, omnibox.OmniboxTest.testB, ...] - """ - def _GetTestsFromTestCase(class_obj): - """Return all test method names from given class object.""" - return [class_obj.__name__ + '.' + x for x in dir(class_obj) if - x.startswith('test')] - - def _GetTestsFromModule(module): - """Return all test method names from the given module object.""" - tests = [] - for name in dir(module): - obj = getattr(module, name) - if (isinstance(obj, (type, types.ClassType)) and - issubclass(obj, PyUITest) and obj != PyUITest): - tests.extend([module.__name__ + '.' + x for x in - _GetTestsFromTestCase(obj)]) - return tests - - module = None - # Locate the module - parts = name.split('.') - parts_copy = parts[:] - while parts_copy: - try: - module = __import__('.'.join(parts_copy)) - break - except ImportError: - del parts_copy[-1] - if not parts_copy: raise - # We have the module. Pick the exact test method or class asked for. - parts = parts[1:] - obj = module - for part in parts: - obj = getattr(obj, part) - - if type(obj) == types.ModuleType: - return _GetTestsFromModule(obj) - elif (isinstance(obj, (type, types.ClassType)) and - issubclass(obj, PyUITest) and obj != PyUITest): - return [module.__name__ + '.' + x for x in _GetTestsFromTestCase(obj)] - elif type(obj) == types.UnboundMethodType: - return [name] - else: - logging.warn('No tests in "%s"', name) - return [] - - def _HasTestCases(self, module_string): - """Determines if we have any PyUITest test case classes in the module - identified by |module_string|.""" - module = __import__(module_string) - for name in dir(module): - obj = getattr(module, name) - if (isinstance(obj, (type, types.ClassType)) and - issubclass(obj, PyUITest)): - return True - return False - - def _ExpandTestNames(self, args): - """Returns a list of tests loaded from the given args. - - The given args can be either a module (ex: module1) or a testcase - (ex: module2.MyTestCase) or a test (ex: module1.MyTestCase.testX) - or a suite name (ex: @FULL). If empty, the tests in the already imported - modules are loaded. - - Args: - args: [module1, module2, module3.testcase, module4.testcase.testX] - These modules or test cases or tests should be importable. - Suites can be specified by prefixing @. Example: @FULL - - Returns: - a list of expanded test names. Example: - [ - 'module1.TestCase1.testA', - 'module1.TestCase1.testB', - 'module2.TestCase2.testX', - 'module3.testcase.testY', - 'module4.testcase.testX' - ] - """ - - def _TestsFromDescriptionFile(suite): - pyauto_tests_file = os.path.join(self.TestsDir(), self._tests_filename) - if suite: - logging.debug("Reading %s (@%s)", pyauto_tests_file, suite) - else: - logging.debug("Reading %s", pyauto_tests_file) - if not os.path.exists(pyauto_tests_file): - logging.warn("%s missing. Cannot load tests.", pyauto_tests_file) - return [] - else: - return self._ExpandTestNamesFrom(pyauto_tests_file, suite) - - if not args: # Load tests ourselves - if self._HasTestCases('__main__'): # we are running a test script - module_name = os.path.splitext(os.path.basename(sys.argv[0]))[0] - args.append(module_name) # run the test cases found in it - else: # run tests from the test description file - args = _TestsFromDescriptionFile(self._options.suite) - else: # Check args with @ prefix for suites - out_args = [] - for arg in args: - if arg.startswith('@'): - suite = arg[1:] - out_args += _TestsFromDescriptionFile(suite) - else: - out_args.append(arg) - args = out_args - return args - - def _ExpandTestNamesFrom(self, filename, suite): - """Load test names from the given file. - - Args: - filename: the file to read the tests from - suite: the name of the suite to load from |filename|. - - Returns: - a list of test names - [module.testcase.testX, module.testcase.testY, ..] - """ - suites = PyUITest.EvalDataFrom(filename) - platform = sys.platform - if PyUITest.IsChromeOS(): # check if it's chromeos - platform = 'chromeos' - assert platform in self._platform_map, '%s unsupported' % platform - def _NamesInSuite(suite_name): - logging.debug('Expanding suite %s', suite_name) - platforms = suites.get(suite_name) - names = platforms.get('all', []) + \ - platforms.get(self._platform_map[platform], []) - ret = [] - # Recursively include suites if any. Suites begin with @. - for name in names: - if name.startswith('@'): # Include another suite - ret.extend(_NamesInSuite(name[1:])) - else: - ret.append(name) - return ret - - assert suite in suites, '%s: No such suite in %s' % (suite, filename) - all_names = _NamesInSuite(suite) - args = [] - excluded = [] - # Find all excluded tests. Excluded tests begin with '-'. - for name in all_names: - if name.startswith('-'): # Exclude - excluded.extend(self._ImportTestsFromName(name[1:])) - else: - args.extend(self._ImportTestsFromName(name)) - for name in excluded: - if name in args: - args.remove(name) - else: - logging.warn('Cannot exclude %s. Not included. Ignoring', name) - if excluded: - logging.debug('Excluded %d test(s): %s', len(excluded), excluded) - return args - - def _Run(self): - """Run the tests.""" - if self._options.wait_for_debugger: - raw_input('Attach debugger to process %s and hit <enter> ' % os.getpid()) - - suite_args = [sys.argv[0]] - chrome_flags = self._options.chrome_flags - # Set CHROME_HEADLESS. It enables crash reporter on posix. - os.environ['CHROME_HEADLESS'] = '1' - os.environ['EXTRA_CHROME_FLAGS'] = chrome_flags - test_names = self._ExpandTestNames(self._args) - - # Shard, if requested (--shard). - if self._options.shard: - matched = re.match('(\d+)/(\d+)', self._options.shard) - if not matched: - print >>sys.stderr, 'Invalid sharding params: %s' % self._options.shard - sys.exit(1) - shard_index = int(matched.group(1)) - 1 - num_shards = int(matched.group(2)) - if shard_index < 0 or shard_index >= num_shards: - print >>sys.stderr, 'Invalid sharding params: %s' % self._options.shard - sys.exit(1) - test_names = pyauto_utils.Shard(test_names, shard_index, num_shards) - - test_names *= self._options.repeat - logging.debug("Loading %d tests from %s", len(test_names), test_names) - if self._options.list_tests: # List tests and exit - for name in test_names: - print name - sys.exit(0) - pyauto_suite = PyUITestSuite(suite_args) - loaded_tests = unittest.defaultTestLoader.loadTestsFromNames(test_names) - pyauto_suite.addTests(loaded_tests) - verbosity = 1 - if self._options.verbose: - verbosity = 2 - result = PyAutoTextTestRunner(verbosity=verbosity).run(pyauto_suite) - del loaded_tests # Need to destroy test cases before the suite - del pyauto_suite - successful = result.wasSuccessful() - if not successful: - pyauto_tests_file = os.path.join(self.TestsDir(), self._tests_filename) - print >>sys.stderr, 'Tests can be disabled by editing %s. ' \ - 'Ref: %s' % (pyauto_tests_file, _PYAUTO_DOC_URL) - sys.exit(not successful) - - -if __name__ == '__main__': - Main() diff --git a/chrome/test/pyautolib/pyauto_errors.py b/chrome/test/pyautolib/pyauto_errors.py deleted file mode 100644 index 5b56aa9..0000000 --- a/chrome/test/pyautolib/pyauto_errors.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2012 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. - -"""PyAuto Errors.""" - -class JavascriptRuntimeError(RuntimeError): - """Represent an error raised by injected Javascript.""" - pass - - -class JSONInterfaceError(RuntimeError): - """Represent an error in the JSON IPC interface.""" - pass - - -class AutomationCommandFail(JSONInterfaceError): - """Represent an automation command failure. - - These failures are passed back from the Chrome side of the IPC. - """ - pass - - -class AutomationCommandTimeout(JSONInterfaceError): - """Represent an automation command failure due to timeout.""" - pass - - -class NTPThumbnailNotShownError(RuntimeError): - """Represent an error while attempting to manipulate a NTP thumbnail. - - This is due to it not being visible to a real user. - """ - pass diff --git a/chrome/test/pyautolib/pyauto_paths.py b/chrome/test/pyautolib/pyauto_paths.py deleted file mode 100644 index 412b661..0000000 --- a/chrome/test/pyautolib/pyauto_paths.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2012 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. - -"""Common paths for pyauto tests.""" - -import os -import sys - - -def GetSourceDir(): - """Returns src/ directory.""" - script_dir = os.path.abspath(os.path.dirname(__file__)) - return os.path.join(script_dir, os.pardir, os.pardir, os.pardir) - - -def GetThirdPartyDir(): - """Returns src/third_party directory.""" - return os.path.join(GetSourceDir(), 'third_party') - - -def GetBuildDirs(): - """Returns list of possible build directories.""" - # List of dirs that can contain a Debug/Release build. - outer_dirs = { - 'linux2': ['out'], - 'linux3': ['out'], - 'darwin': ['out', 'xcodebuild'], - 'win32': ['chrome', 'build', 'out'], - 'cygwin': ['chrome'], - }.get(sys.platform, []) - src_dir = GetSourceDir() - build_dirs = [] - for dir in outer_dirs: - build_dirs += [os.path.join(src_dir, dir, 'Debug')] - build_dirs += [os.path.join(src_dir, dir, 'Release')] - return build_dirs - - -def GetChromeDriverExe(): - """Returns path to ChromeDriver executable, or None if cannot be found.""" - exe_name = 'chromedriver' - if sys.platform == 'win32': - exe_name += '.exe' - - import pyautolib - dir = os.path.dirname(pyautolib.__file__) - exe = os.path.join(dir, exe_name) - if os.path.exists(exe): - return exe - return None diff --git a/chrome/test/pyautolib/pyauto_utils.py b/chrome/test/pyautolib/pyauto_utils.py deleted file mode 100644 index 8b16a8e..0000000 --- a/chrome/test/pyautolib/pyauto_utils.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright (c) 2012 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. - -"""Utilities for PyAuto.""" - -import httplib -import logging -import os -import shutil -import socket -import sys -import tempfile -import unittest -import urlparse -import zipfile - - -class ExistingPathReplacer(object): - """Facilitates backing up a given path (file or dir).. - - Often you want to manipulate a directory or file for testing but don't want to - meddle with the existing contents. This class lets you make a backup, and - reinstate the backup when done. A backup is made in an adjacent directory, - so you need to make sure you have write permissions to the parent directory. - - Works seemlessly in cases where the requested path already exists, or not. - - Automatically reinstates the backed up path (if any) when object is deleted. - """ - _path = '' - _backup_dir = None # dir to which existing content is backed up - _backup_basename = '' - - def __init__(self, path, path_type='dir'): - """Initialize the object, making backups if necessary. - - Args: - path: the requested path to file or directory - path_type: path type. Options: 'file', 'dir'. Default: 'dir' - """ - assert path_type in ('file', 'dir'), 'Invalid path_type: %s' % path_type - self._path_type = path_type - self._path = path - if os.path.exists(self._path): - if 'dir' == self._path_type: - assert os.path.isdir(self._path), '%s is not a directory' % self._path - else: - assert os.path.isfile(self._path), '%s is not a file' % self._path - # take a backup - self._backup_basename = os.path.basename(self._path) - self._backup_dir = tempfile.mkdtemp(dir=os.path.dirname(self._path), - prefix='bkp-' + self._backup_basename) - logging.info('Backing up %s in %s' % (self._path, self._backup_dir)) - shutil.move(self._path, - os.path.join(self._backup_dir, self._backup_basename)) - self._CreateRequestedPath() - - def __del__(self): - """Cleanup. Reinstate backup.""" - self._CleanupRequestedPath() - if self._backup_dir: # Reinstate, if backed up. - from_path = os.path.join(self._backup_dir, self._backup_basename) - logging.info('Reinstating backup from %s to %s' % (from_path, self._path)) - shutil.move(from_path, self._path) - self._RemoveBackupDir() - - def _CreateRequestedPath(self): - # Create intermediate dirs if needed. - if not os.path.exists(os.path.dirname(self._path)): - os.makedirs(os.path.dirname(self._path)) - if 'dir' == self._path_type: - os.mkdir(self._path) - else: - open(self._path, 'w').close() - - def _CleanupRequestedPath(self): - if os.path.exists(self._path): - if os.path.isdir(self._path): - shutil.rmtree(self._path, ignore_errors=True) - else: - os.remove(self._path) - - def _RemoveBackupDir(self): - if self._backup_dir and os.path.isdir(self._backup_dir): - shutil.rmtree(self._backup_dir, ignore_errors=True) - - -def RemovePath(path): - """Remove the given path (file or dir).""" - if os.path.isdir(path): - shutil.rmtree(path, ignore_errors=True) - return - try: - os.remove(path) - except OSError: - pass - - -def UnzipFilenameToDir(filename, dir): - """Unzip |filename| to directory |dir|. - - This works with as low as python2.4 (used on win). - """ - zf = zipfile.ZipFile(filename) - pushd = os.getcwd() - if not os.path.isdir(dir): - os.mkdir(dir) - os.chdir(dir) - # Extract files. - for info in zf.infolist(): - name = info.filename - if name.endswith('/'): # dir - if not os.path.isdir(name): - os.makedirs(name) - else: # file - dir = os.path.dirname(name) - if not os.path.isdir(dir): - os.makedirs(dir) - out = open(name, 'wb') - out.write(zf.read(name)) - out.close() - # Set permissions. Permission info in external_attr is shifted 16 bits. - os.chmod(name, info.external_attr >> 16L) - os.chdir(pushd) - - -def GetCurrentPlatform(): - """Get a string representation for the current platform. - - Returns: - 'mac', 'win' or 'linux' - """ - if sys.platform == 'darwin': - return 'mac' - if sys.platform == 'win32': - return 'win' - if sys.platform.startswith('linux'): - return 'linux' - raise RuntimeError('Unknown platform') - - -def PrintPerfResult(graph_name, series_name, data_point, units, - show_on_waterfall=False): - """Prints a line to stdout that is specially formatted for the perf bots. - - Args: - graph_name: String name for the graph on which to plot the data. - series_name: String name for the series (line on the graph) associated with - the data. This is also the string displayed on the waterfall - if |show_on_waterfall| is True. - data_point: Numeric data value to plot on the graph for the current build. - This can be a single value or an array of values. If an array, - the graph will plot the average of the values, along with error - bars. - units: The string unit of measurement for the given |data_point|. - show_on_waterfall: Whether or not to display this result directly on the - buildbot waterfall itself (in the buildbot step running - this test on the waterfall page, not the stdio page). - """ - waterfall_indicator = ['', '*'][show_on_waterfall] - print '%sRESULT %s: %s= %s %s' % ( - waterfall_indicator, graph_name, series_name, - str(data_point).replace(' ', ''), units) - sys.stdout.flush() - - -def Shard(ilist, shard_index, num_shards): - """Shard a given list and return the group at index |shard_index|. - - Args: - ilist: input list - shard_index: 0-based sharding index - num_shards: shard count - """ - chunk_size = len(ilist) / num_shards - chunk_start = shard_index * chunk_size - if shard_index == num_shards - 1: # Exhaust the remainder in the last shard. - chunk_end = len(ilist) - else: - chunk_end = chunk_start + chunk_size - return ilist[chunk_start:chunk_end] - - -def WaitForDomElement(pyauto, driver, xpath): - """Wait for the UI element to appear. - - Args: - pyauto: an instance of pyauto.PyUITest. - driver: an instance of chrome driver or a web element. - xpath: the xpath of the element to wait for. - - Returns: - The element if it is found. - NoSuchElementException if it is not found. - """ - pyauto.WaitUntil(lambda: len(driver.find_elements_by_xpath(xpath)) > 0) - return driver.find_element_by_xpath(xpath) - - -def DoesUrlExist(url): - """Determines whether a resource exists at the given URL. - - Args: - url: URL to be verified. - - Returns: - True if url exists, otherwise False. - """ - parsed = urlparse.urlparse(url) - try: - conn = httplib.HTTPConnection(parsed.netloc) - conn.request('HEAD', parsed.path) - response = conn.getresponse() - except (socket.gaierror, socket.error): - return False - finally: - conn.close() - # Follow both permanent (301) and temporary (302) redirects. - if response.status == 302 or response.status == 301: - return DoesUrlExist(response.getheader('location')) - return response.status == 200 - - -class _GTestTextTestResult(unittest._TextTestResult): - """A test result class that can print formatted text results to a stream. - - Results printed in conformance with gtest output format, like: - [ RUN ] autofill.AutofillTest.testAutofillInvalid: "test desc." - [ OK ] autofill.AutofillTest.testAutofillInvalid - [ RUN ] autofill.AutofillTest.testFillProfile: "test desc." - [ OK ] autofill.AutofillTest.testFillProfile - [ RUN ] autofill.AutofillTest.testFillProfileCrazyCharacters: "Test." - [ OK ] autofill.AutofillTest.testFillProfileCrazyCharacters - """ - - def __init__(self, stream, descriptions, verbosity): - unittest._TextTestResult.__init__(self, stream, descriptions, verbosity) - - def _GetTestURI(self, test): - if sys.version_info[:2] <= (2, 4): - return '%s.%s' % (unittest._strclass(test.__class__), - test._TestCase__testMethodName) - return '%s.%s' % (unittest._strclass(test.__class__), test._testMethodName) - - def getDescription(self, test): - return '%s: "%s"' % (self._GetTestURI(test), test.shortDescription()) - - def startTest(self, test): - unittest.TestResult.startTest(self, test) - self.stream.writeln('[ RUN ] %s' % self.getDescription(test)) - - def addSuccess(self, test): - unittest.TestResult.addSuccess(self, test) - self.stream.writeln('[ OK ] %s' % self._GetTestURI(test)) - - def addError(self, test, err): - unittest.TestResult.addError(self, test, err) - self.stream.writeln('[ ERROR ] %s' % self._GetTestURI(test)) - - def addFailure(self, test, err): - unittest.TestResult.addFailure(self, test, err) - self.stream.writeln('[ FAILED ] %s' % self._GetTestURI(test)) - - -class GTestTextTestRunner(unittest.TextTestRunner): - """Test Runner for displaying test results in textual format. - - Results are displayed in conformance with gtest output. - """ - - def __init__(self, verbosity=1): - unittest.TextTestRunner.__init__(self, stream=sys.stderr, - verbosity=verbosity) - - def _makeResult(self): - return _GTestTextTestResult(self.stream, self.descriptions, self.verbosity) diff --git a/chrome/test/pyautolib/pyauto_utils_test.py b/chrome/test/pyautolib/pyauto_utils_test.py deleted file mode 100755 index 3a3a85c..0000000 --- a/chrome/test/pyautolib/pyauto_utils_test.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 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. - -"""Tests for pyauto_utils.""" - -import glob -import os -import shutil -import tempfile -import unittest - -import pyauto_utils - - -class ExistingPathReplacerTest(unittest.TestCase): - """Tests for ExistingPathReplacer.""" - - def setUp(self): - self._workdir = tempfile.mkdtemp() - self.assertEqual(0, len(os.listdir(self._workdir))) - - def tearDown(self): - shutil.rmtree(self._workdir, ignore_errors=True) - - def _CreateFile(self, path): - fp = open(path, 'w') - fp.write('magic') - fp.close() - - def _IsOrigFile(self, path): - if not os.path.isfile(path): - return False - return open(path).read() == 'magic' - - def testNonExistingFile(self): - """Test when the requested file does not exist.""" - myfile = os.path.join(self._workdir, 'myfile.txt') - self.assertFalse(os.path.isfile(myfile)) - r = pyauto_utils.ExistingPathReplacer(myfile, path_type='file') - self.assertTrue(os.path.isfile(myfile)) - del r - self.assertEqual(0, len(os.listdir(self._workdir))) - - def testExistingFile(self): - """Test when the requested file exists.""" - myfile = os.path.join(self._workdir, 'myfile.txt') - self._CreateFile(myfile) - self.assertTrue(self._IsOrigFile(myfile)) - r = pyauto_utils.ExistingPathReplacer(myfile, path_type='file') - self.assertFalse(self._IsOrigFile(myfile)) - self.assertEqual(2, len(os.listdir(self._workdir))) - del r - self.assertEqual(1, len(os.listdir(self._workdir))) - self.assertTrue(self._IsOrigFile(myfile)) - - def testNonExistingDir(self): - """Test when the requested dir does not exist.""" - mydir = os.path.join(self._workdir, 'mydir') - self.assertFalse(os.path.isdir(mydir)) - r = pyauto_utils.ExistingPathReplacer(mydir, path_type='dir') - self.assertTrue(os.path.isdir(mydir)) - self.assertEqual(0, len(os.listdir(mydir))) - del r - self.assertFalse(os.path.isdir(mydir)) - - def testExistingDir(self): - """Test when the requested dir exists.""" - # Create a dir with one file - mydir = os.path.join(self._workdir, 'mydir') - os.makedirs(mydir) - self.assertEqual(1, len(os.listdir(self._workdir))) - myfile = os.path.join(mydir, 'myfile.txt') - open(myfile, 'w').close() - self.assertTrue(os.path.isfile(myfile)) - r = pyauto_utils.ExistingPathReplacer(mydir) - self.assertEqual(2, len(os.listdir(self._workdir))) - self.assertFalse(os.path.isfile(myfile)) - del r - self.assertEqual(1, len(os.listdir(self._workdir))) - self.assertTrue(os.path.isfile(myfile)) - - -if __name__ == '__main__': - unittest.main() diff --git a/chrome/test/pyautolib/pyautolib.cc b/chrome/test/pyautolib/pyautolib.cc deleted file mode 100644 index 1a523953..0000000 --- a/chrome/test/pyautolib/pyautolib.cc +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) 2012 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. - -#include "base/base_paths.h" -#include "base/json/json_writer.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/path_service.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/time/time.h" -#include "base/values.h" -#include "chrome/common/automation_messages.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/test/automation/automation_proxy.h" -#include "chrome/test/automation/tab_proxy.h" -#include "chrome/test/pyautolib/pyautolib.h" -#include "url/gurl.h" - -// PyUITestSuiteBase -PyUITestSuiteBase::PyUITestSuiteBase(int argc, char** argv) - : UITestSuite(argc, argv) { -} - -PyUITestSuiteBase::~PyUITestSuiteBase() { -#if defined(OS_MACOSX) - pool_.Recycle(); -#endif - Shutdown(); -} - -void PyUITestSuiteBase::InitializeWithPath(const base::FilePath& browser_dir) { - SetBrowserDirectory(browser_dir); - UITestSuite::Initialize(); -} - -void PyUITestSuiteBase::SetCrSourceRoot(const base::FilePath& path) { - PathService::Override(base::DIR_SOURCE_ROOT, path); -} - -// PyUITestBase -PyUITestBase::PyUITestBase(bool clear_profile, std::wstring homepage) - : UITestBase() { - set_clear_profile(clear_profile); - set_homepage(base::WideToUTF8(homepage)); - // We add this so that pyauto can execute javascript in the renderer and - // read values back. - dom_automation_enabled_ = true; - message_loop_ = GetSharedMessageLoop(base::MessageLoop::TYPE_DEFAULT); -} - -PyUITestBase::~PyUITestBase() { -} - -// static, refer .h for why it needs to be static -base::MessageLoop* PyUITestBase::message_loop_ = NULL; - -// static -base::MessageLoop* PyUITestBase::GetSharedMessageLoop( - base::MessageLoop::Type msg_loop_type) { - if (!message_loop_) // Create a shared instance of MessageLoop - message_loop_ = new base::MessageLoop(msg_loop_type); - return message_loop_; -} - -void PyUITestBase::Initialize(const base::FilePath& browser_dir) { - UITestBase::SetBrowserDirectory(browser_dir); -} - -ProxyLauncher* PyUITestBase::CreateProxyLauncher() { - if (named_channel_id_.empty()) - return new AnonymousProxyLauncher(false); - return new NamedProxyLauncher(named_channel_id_, false, false); -} - -void PyUITestBase::SetUp() { - UITestBase::SetUp(); -} - -void PyUITestBase::TearDown() { - UITestBase::TearDown(); -} - -void PyUITestBase::SetLaunchSwitches() { - // Clear the homepage because some of the pyauto tests don't work correctly - // if a URL argument is passed. - std::string homepage_original; - std::swap(homepage_original, homepage_); - - UITestBase::SetLaunchSwitches(); - - // However, we *do* want the --homepage switch. - std::swap(homepage_original, homepage_); - launch_arguments_.AppendSwitchASCII(switches::kHomePage, homepage_); -} - -AutomationProxy* PyUITestBase::automation() const { - AutomationProxy* automation_proxy = UITestBase::automation(); - if (!automation_proxy) { - LOG(FATAL) << "The automation proxy is NULL."; - } - return automation_proxy; -} - -std::string PyUITestBase::_SendJSONRequest(int window_index, - const std::string& request, - int timeout) { - std::string response; - bool success; - AutomationProxy* automation_sender = automation(); - base::TimeTicks time = base::TimeTicks::Now(); - - if (!automation_sender) { - ErrorResponse("Automation proxy does not exist", request, false, &response); - } else if (!automation_sender->channel()) { - ErrorResponse("Chrome automation IPC channel was found already broken", - request, false, &response); - } else if (!automation_sender->Send( - new AutomationMsg_SendJSONRequest(window_index, request, &response, - &success), - timeout)) { - RequestFailureResponse(request, base::TimeTicks::Now() - time, - base::TimeDelta::FromMilliseconds(timeout), - &response); - } - return response; -} - -void PyUITestBase::ErrorResponse( - const std::string& error_string, - const std::string& request, - bool is_timeout, - std::string* response) { - base::DictionaryValue error_dict; - std::string error_msg = base::StringPrintf("%s for %s", error_string.c_str(), - request.c_str()); - LOG(ERROR) << "Error during automation: " << error_msg; - error_dict.SetString("error", error_msg); - error_dict.SetBoolean("is_interface_error", true); - error_dict.SetBoolean("is_interface_timeout", is_timeout); - base::JSONWriter::Write(&error_dict, response); -} - -void PyUITestBase::RequestFailureResponse( - const std::string& request, - const base::TimeDelta& duration, - const base::TimeDelta& timeout, - std::string* response) { - // TODO(craigdh): Determine timeout directly from IPC's Send(). - if (duration >= timeout) { - ErrorResponse( - base::StringPrintf("Chrome automation timed out after %d seconds", - static_cast<int>(duration.InSeconds())), - request, true, response); - } else { - // TODO(craigdh): Determine specific cause. - ErrorResponse( - "Chrome automation failed prior to timing out, did chrome crash?", - request, false, response); - } -} diff --git a/chrome/test/pyautolib/pyautolib.h b/chrome/test/pyautolib/pyautolib.h deleted file mode 100644 index f4bcb27..0000000 --- a/chrome/test/pyautolib/pyautolib.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2012 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. -// -// This file declares the C++ side of PyAuto, the python interface to -// Chromium automation. It access Chromium's internals using Automation Proxy. - -#ifndef CHROME_TEST_PYAUTOLIB_PYAUTOLIB_H_ -#define CHROME_TEST_PYAUTOLIB_PYAUTOLIB_H_ - -#include "base/compiler_specific.h" -#include "base/message_loop/message_loop.h" -#include "base/test/test_timeouts.h" -#include "base/time/time.h" -#include "chrome/test/ui/ui_test.h" -#include "chrome/test/ui/ui_test_suite.h" - -#if defined(OS_MACOSX) -#include "base/mac/scoped_nsautorelease_pool.h" -#endif - -class AutomationProxy; - -// The C++ style guide forbids using default arguments but I'm taking the -// liberty of allowing it in this file. The sole purpose of this (and the -// .cc) is to support the python interface, and default args are allowed in -// python. Strictly adhering to the guide here would mean having to re-define -// all methods in python just for the sake of providing default args. This -// seems cumbersome and unwanted. - -// Test Suite for Pyauto tests. All one-time initializations go here. -class PyUITestSuiteBase : public UITestSuite { - public: - PyUITestSuiteBase(int argc, char** argv); - virtual ~PyUITestSuiteBase(); - - void InitializeWithPath(const base::FilePath& browser_dir); - - void SetCrSourceRoot(const base::FilePath& path); - - private: -#if defined(OS_MACOSX) - base::mac::ScopedNSAutoreleasePool pool_; -#endif -}; - -// The primary class that interfaces with Automation Proxy. -// This class is accessed from python using swig. -class PyUITestBase : public UITestBase { - public: - // Only public methods are accessible from swig. - - // Constructor. Lookup pyauto.py for doc on these args. - PyUITestBase(bool clear_profile, std::wstring homepage); - virtual ~PyUITestBase(); - - // Initialize the setup. Should be called before launching the browser. - // |browser_dir| is the path to dir containing chromium binaries. - void Initialize(const base::FilePath& browser_dir); - - void UseNamedChannelID(const std::string& named_channel_id) { - named_channel_id_ = named_channel_id; - launcher_.reset(CreateProxyLauncher()); - } - - virtual ProxyLauncher* CreateProxyLauncher() OVERRIDE; - - // SetUp,TearDown is redeclared as public to make it accessible from swig. - virtual void SetUp() OVERRIDE; - virtual void TearDown() OVERRIDE; - - // AutomationProxy methods - - // Meta-methods. Generic pattern of passing args and response as - // JSON dict to avoid future use of the SWIG interface and - // automation proxy additions. Returns response as JSON dict. - // Use -ve window_index for automation calls not targetted at a browser - // window. Example: Login call for chromeos. - std::string _SendJSONRequest(int window_index, - const std::string& request, - int timeout); - - // Sets a cookie value for a url. Returns true on success. - bool SetCookie(const GURL& cookie_url, const std::string& value, - int window_index = 0, int tab_index = 0); - // Gets a cookie value for the given url. - std::string GetCookie(const GURL& cookie_url, int window_index = 0, - int tab_index = 0); - - protected: - // Gets the automation proxy and checks that it exists. - virtual AutomationProxy* automation() const OVERRIDE; - - virtual void SetLaunchSwitches() OVERRIDE; - - private: - // Create JSON error responses. - void ErrorResponse(const std::string& error_string, - const std::string& request, - bool is_timeout, - std::string* response); - void RequestFailureResponse( - const std::string& request, - const base::TimeDelta& duration, - const base::TimeDelta& timeout, - std::string* response); - - // Enables PostTask to main thread. - // Should be shared across multiple instances of PyUITestBase so that this - // class is re-entrant and multiple instances can be created. - // This is necessary since python's unittest module creates instances of - // TestCase at load time itself. - static base::MessageLoop* GetSharedMessageLoop( - base::MessageLoop::Type msg_loop_type); - static base::MessageLoop* message_loop_; - - // Path to named channel id. - std::string named_channel_id_; -}; - -#endif // CHROME_TEST_PYAUTOLIB_PYAUTOLIB_H_ diff --git a/chrome/test/pyautolib/pyautolib.i b/chrome/test/pyautolib/pyautolib.i deleted file mode 100644 index e73637d..0000000 --- a/chrome/test/pyautolib/pyautolib.i +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2013 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. -// -// Swig Interface for PyAuto. -// PyAuto makes the Automation Proxy interface available in Python -// -// Running swig as: -// swig -python -c++ chrome/test/pyautolib/pyautolib.i -// would generate pyautolib.py, pyautolib_wrap.cxx - -// When adding a new class or method, make sure you specify the doc string using -// %feature("docstring", "doc string goes here") NODENAME; -// and attach it to your node (class or method). This doc string will be -// copied over in the generated python classes/methods. - -%module(docstring="Python interface to Automation Proxy.") pyautolib -%feature("autodoc", "1"); - -%include <cpointer.i> -%include <std_string.i> -%include <std_wstring.i> - -%include "chrome/test/pyautolib/argc_argv.i" - -// NOTE: All files included in this file should also be listed under -// pyautolib_sources in chrome_tests.gypi. - -// Headers that can be swigged directly. -%include "chrome/app/chrome_command_ids.h" -%include "chrome/app/chrome_dll_resource.h" -%include "chrome/common/automation_constants.h" -%include "chrome/common/pref_names.h" -%include "content/public/common/page_type.h" -%include "content/public/common/security_style.h" -// Must come before cert_status_flags.h -%include "net/base/net_export.h" -%ignore net::MapNetErrorToCertStatus(int); -%include "net/cert/cert_status_flags.h" - -%{ -#include "chrome/common/automation_constants.h" -#include "chrome/common/pref_names.h" -#include "chrome/test/automation/browser_proxy.h" -#include "chrome/test/automation/tab_proxy.h" -#include "chrome/test/pyautolib/pyautolib.h" -#include "content/public/common/security_style.h" -#include "net/test/spawned_test_server/spawned_test_server.h" -%} - -// Handle type uint32 conversions as int -%apply int { uint32 }; - -// scoped_refptr -template <class T> -class scoped_refptr { - public: - scoped_refptr(); - scoped_refptr(T* p); - ~scoped_refptr(); - - T* get() const; - T* operator->() const; -}; - -// GURL -%feature("docstring", "Represent a URL. Call spec() to get the string.") GURL; -class GURL { - public: - GURL(); - explicit GURL(const std::string& url_string); - %feature("docstring", "Get the string representation.") spec; - const std::string& spec() const; -}; - -// FilePath -namespace base { -%feature("docstring", - "Represent a file path. Call value() to get the string.") FilePath; -class FilePath { - public: - %feature("docstring", "Get the string representation.") value; -#ifdef SWIGWIN - typedef std::wstring StringType; -#else - typedef std::string StringType; -#endif // SWIGWIN - const StringType& value() const; - %feature("docstring", "Construct an empty FilePath from a string.") - FilePath; - FilePath(); - explicit FilePath(const StringType& path); -}; -} // namespace base - -class PyUITestSuiteBase { - public: - %feature("docstring", "Create the suite.") PyUITestSuiteBase; - PyUITestSuiteBase(int argc, char** argv); - virtual ~PyUITestSuiteBase(); - - %feature("docstring", "Initialize from the path to browser dir.") - InitializeWithPath; - void InitializeWithPath(const base::FilePath& browser_dir); - %feature("docstring", "Set chrome source root path, used in some tests") - SetCrSourceRoot; - void SetCrSourceRoot(const base::FilePath& path); -}; - -class PyUITestBase { - public: - PyUITestBase(bool clear_profile, std::wstring homepage); - - %feature("docstring", "Initialize the entire setup. Should be called " - "before launching the browser. For internal use.") Initialize; - void Initialize(const base::FilePath& browser_dir); - - %feature("docstring", "Appends a command-line switch (with associated value " - "if given) to the list of switches to be passed to the browser " - "upon launch. Should be called before launching the browser. " - "For internal use only.") - AppendBrowserLaunchSwitch; - void AppendBrowserLaunchSwitch(const char* name); - void AppendBrowserLaunchSwitch(const char* name, const char* value); - - %feature("docstring", "Begins tracing with the given category_patterns " - "string.") - BeginTracing; - bool BeginTracing(const std::string& category_patterns); - - %feature("docstring", "Ends tracing and returns the collected events.") - EndTracing; - std::string EndTracing(); - - void UseNamedChannelID(const std::string& named_channel_id); - - %feature("docstring", - "Fires up the browser and opens a window.") SetUp; - virtual void SetUp(); - %feature("docstring", - "Closes all windows and destroys the browser.") TearDown; - virtual void TearDown(); - - %feature("docstring", "Launches the browser and IPC testing server.") - LaunchBrowserAndServer; - void LaunchBrowserAndServer(); - %feature("docstring", "Closes the browser and IPC testing server.") - CloseBrowserAndServer; - void CloseBrowserAndServer(); - - %feature("docstring", "Determine if the profile is set to be cleared on " - "next startup.") get_clear_profile; - bool get_clear_profile() const; - %feature("docstring", "If False, sets the flag so that the profile is " - "not cleared on next startup. Useful for persisting profile " - "across restarts. By default the state is True, to clear profile.") - set_clear_profile; - void set_clear_profile(bool clear_profile); - - %feature("docstring", "Get the path to profile directory.") user_data_dir; - base::FilePath user_data_dir() const; - - // Meta-method - %feature("docstring", "Send a sync JSON request to Chrome. " - "Returns a JSON dict as a response. " - "Given timeout in milliseconds." - "Internal method.") - _SendJSONRequest; - std::string _SendJSONRequest(int window_index, - const std::string& request, - int timeout); - - %feature("docstring", - "Returns empty string if there were no unexpected Chrome asserts or " - "crashes, a string describing the failures otherwise. As a side " - "effect, it will fail with EXPECT_EQ macros if this code runs " - "within a gtest harness.") GetErrorsAndCrashes; - std::string CheckErrorsAndCrashes() const; -}; - -namespace net { -// SpawnedTestServer -%feature("docstring", - "SpawnedTestServer. Serves files in data dir over a local http server") - SpawnedTestServer; -class SpawnedTestServer { - public: - enum Type { - TYPE_FTP, - TYPE_HTTP, - TYPE_HTTPS, - }; - - // Initialize a SpawnedTestServer listening on the specified host - // (IP or hostname). - SpawnedTestServer(Type type, const std::string& host, - const base::FilePath& document_root); - // Initialize a SpawnedTestServer with a specific set of SSLOptions. - SpawnedTestServer(Type type, - const SSLOptions& ssl_options, - const base::FilePath& document_root); - - %feature("docstring", "Start SpawnedTestServer over an ephemeral port") Start; - bool Start(); - - %feature("docstring", "Stop SpawnedTestServer") Stop; - bool Stop(); - - %feature("docstring", "Get FilePath to the document root") document_root; - const base::FilePath& document_root() const; - - std::string GetScheme() const; - - %feature("docstring", "Get URL for a file path") GetURL; - GURL GetURL(const std::string& path) const; -}; - -%extend SpawnedTestServer { - %feature("docstring", "Get port number.") GetPort; - int GetPort() const { - int val = 0; - $self->server_data().GetInteger("port", &val); - return val; - } -}; - -} -// SSLOptions -%feature("docstring", - "SSLOptions. Sets one of three types of a cert") - SSLOptions; -struct SSLOptions { - enum ServerCertificate { - CERT_OK, - CERT_MISMATCHED_NAME, - CERT_EXPIRED, - }; - - // Initialize a new SSLOptions that will use the specified certificate. - explicit SSLOptions(ServerCertificate cert); -}; - -%{ -typedef net::SpawnedTestServer::SSLOptions SSLOptions; -%} - -%pointer_class(int, int_ptr); -%pointer_class(uint32, uint32_ptr); diff --git a/chrome/test/pyautolib/remote_host.py b/chrome/test/pyautolib/remote_host.py deleted file mode 100755 index c51a6e0..0000000 --- a/chrome/test/pyautolib/remote_host.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import cStringIO -import os -import pickle -import socket -import sys - -import pyauto - -class RemoteHost(object): - """Class used as a host for tests that use the PyAuto RemoteProxy. - - This class fires up a listener which waits for a connection from a RemoteProxy - and receives method call requests. Run python remote_host.py - remote_host.RemoteHost.RunHost to start up a PyAuto remote instance that you - can connect to and automate using pyauto.RemoteProxy. - """ - def __init__(self, host, *args, **kwargs): - self.StartSocketServer(host) - - def StartSocketServer(self, host): - listening_socket = socket.socket() - listening_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - listening_socket.bind(host) - listening_socket.listen(1) - print 'Listening for incoming connections on port %d.' % host[1] - self._socket, address = listening_socket.accept() - print 'Accepted connection from %s:%d.' % address - - while self.Connected(): - self._HandleRPC() - - def StopSocketServer(self): - if self._socket: - try: - self._socket.shutdown(socket.SHUT_RDWR) - self._socket.close() - except socket.error: - pass - self._socket = None - - def Connected(self): - return self._socket - - def CreateTarget(self, target_class): - """Creates an instance of the specified class to serve as the RPC target. - - RPC calls can be made on the target. - """ - self.target = target_class() - - def _HandleRPC(self): - """Receives a method call request over the socket and executes the method. - - This method captures stdout and stderr for the duration of the method call, - and sends those, the return value, and any thrown exceptions back to the - RemoteProxy. - """ - # Receive request. - request = self._socket.recv(4096) - if not request: - self.StopSocketServer() - return - request = pickle.loads(request) - - # Redirect output to strings. - old_stdout = sys.stdout - old_stderr = sys.stderr - sys.stdout = stdout = cStringIO.StringIO() - sys.stderr = stderr = cStringIO.StringIO() - - # Make requested method call. - result = None - exception = None - try: - if getattr(self, request[0], None): - result = getattr(self, request[0])(*request[1], **request[2]) - else: - result = getattr(self.target, request[0])(*request[1], **request[2]) - except BaseException, e: - exception = (e.__class__.__name__, str(e)) - - # Put output back to the way it was before. - sys.stdout = old_stdout - sys.stderr = old_stderr - - # Package up and send the result of the method call. - response = pickle.dumps((result, stdout.getvalue(), stderr.getvalue(), - exception)) - if self._socket.send(response) != len(response): - self.StopSocketServer() - - -if __name__ == '__main__': - pyauto_suite = pyauto.PyUITestSuite(sys.argv) - RemoteHost(('', 7410)) - del pyauto_suite diff --git a/chrome/test/pyautolib/remote_inspector_client.py b/chrome/test/pyautolib/remote_inspector_client.py deleted file mode 100755 index 95b9cf6..0000000 --- a/chrome/test/pyautolib/remote_inspector_client.py +++ /dev/null @@ -1,1211 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""Chrome remote inspector utility for pyauto tests. - -This script provides a python interface that acts as a front-end for Chrome's -remote inspector module, communicating via sockets to interact with Chrome in -the same way that the Developer Tools does. This -- in theory -- should allow -a pyauto test to do anything that Chrome's Developer Tools does, as long as the -appropriate communication with the remote inspector is implemented in this -script. - -This script assumes that Chrome is already running on the local machine with -flag '--remote-debugging-port=9222' to enable remote debugging on port 9222. - -To use this module, first create an instance of class RemoteInspectorClient; -doing this sets up a connection to Chrome's remote inspector. Then call the -appropriate functions on that object to perform the desired actions with the -remote inspector. When done, call Stop() on the RemoteInspectorClient object -to stop communication with the remote inspector. - -For example, to take v8 heap snapshots from a pyauto test: - -import remote_inspector_client -my_client = remote_inspector_client.RemoteInspectorClient() -snapshot_info = my_client.HeapSnapshot(include_summary=True) -// Do some stuff... -new_snapshot_info = my_client.HeapSnapshot(include_summary=True) -my_client.Stop() - -It is expected that a test will only use one instance of RemoteInspectorClient -at a time. If a second instance is instantiated, a RuntimeError will be raised. -RemoteInspectorClient could be made into a singleton in the future if the need -for it arises. -""" - -import asyncore -import datetime -import logging -import optparse -import pprint -import re -import simplejson -import socket -import sys -import threading -import time -import urllib2 -import urlparse - - -class _DevToolsSocketRequest(object): - """A representation of a single DevToolsSocket request. - - A DevToolsSocket request is used for communication with a remote Chrome - instance when interacting with the renderer process of a given webpage. - Requests and results are passed as specially-formatted JSON messages, - according to a communication protocol defined in WebKit. The string - representation of this request will be a JSON message that is properly - formatted according to the communication protocol. - - Public Attributes: - method: The string method name associated with this request. - id: A unique integer id associated with this request. - params: A dictionary of input parameters associated with this request. - results: A dictionary of relevant results obtained from the remote Chrome - instance that are associated with this request. - is_fulfilled: A boolean indicating whether or not this request has been sent - and all relevant results for it have been obtained (i.e., this value is - True only if all results for this request are known). - is_fulfilled_condition: A threading.Condition for waiting for the request to - be fulfilled. - """ - - def __init__(self, method, params, message_id): - """Initialize. - - Args: - method: The string method name for this request. - message_id: An integer id for this request, which is assumed to be unique - from among all requests. - """ - self.method = method - self.id = message_id - self.params = params - self.results = {} - self.is_fulfilled = False - self.is_fulfilled_condition = threading.Condition() - - def __repr__(self): - json_dict = {} - json_dict['method'] = self.method - json_dict['id'] = self.id - if self.params: - json_dict['params'] = self.params - return simplejson.dumps(json_dict, separators=(',', ':')) - - -class _DevToolsSocketClient(asyncore.dispatcher): - """Client that communicates with a remote Chrome instance via sockets. - - This class works in conjunction with the _RemoteInspectorThread class to - communicate with a remote Chrome instance following the remote debugging - communication protocol in WebKit. This class performs the lower-level work - of socket communication. - - Public Attributes: - handshake_done: A boolean indicating whether or not the client has completed - the required protocol handshake with the remote Chrome instance. - inspector_thread: An instance of the _RemoteInspectorThread class that is - working together with this class to communicate with a remote Chrome - instance. - """ - - def __init__(self, verbose, show_socket_messages, hostname, port, path): - """Initialize. - - Args: - verbose: A boolean indicating whether or not to use verbose logging. - show_socket_messages: A boolean indicating whether or not to show the - socket messages sent/received when communicating with the remote - Chrome instance. - hostname: The string hostname of the DevToolsSocket to which to connect. - port: The integer port number of the DevToolsSocket to which to connect. - path: The string path of the DevToolsSocket to which to connect. - """ - asyncore.dispatcher.__init__(self) - - self._logger = logging.getLogger('_DevToolsSocketClient') - self._logger.setLevel([logging.WARNING, logging.DEBUG][verbose]) - - self._show_socket_messages = show_socket_messages - - self._read_buffer = '' - self._write_buffer = '' - - self._socket_buffer_lock = threading.Lock() - - self.handshake_done = False - self.inspector_thread = None - - # Connect to the remote Chrome instance and initiate the protocol handshake. - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.connect((hostname, port)) - - fields = [ - 'Upgrade: WebSocket', - 'Connection: Upgrade', - 'Host: %s:%d' % (hostname, port), - 'Origin: http://%s:%d' % (hostname, port), - 'Sec-WebSocket-Key1: 4k0L66E ZU 8 5 <18 <TK 7 7', - 'Sec-WebSocket-Key2: s2 20 `# 4| 3 9 U_ 1299', - ] - handshake_msg = ('GET %s HTTP/1.1\r\n%s\r\n\r\n\x47\x30\x22\x2D\x5A\x3F' - '\x47\x58' % (path, '\r\n'.join(fields))) - self._Write(handshake_msg.encode('utf-8')) - - def SendMessage(self, msg): - """Causes a request message to be sent to the remote Chrome instance. - - Args: - msg: A string message to be sent; assumed to be a JSON message in proper - format according to the remote debugging protocol in WebKit. - """ - # According to the communication protocol, each request message sent over - # the wire must begin with '\x00' and end with '\xff'. - self._Write('\x00' + msg.encode('utf-8') + '\xff') - - def _Write(self, msg): - """Causes a raw message to be sent to the remote Chrome instance. - - Args: - msg: A raw string message to be sent. - """ - self._write_buffer += msg - self.handle_write() - - def handle_write(self): - """Called if a writable socket can be written; overridden from asyncore.""" - self._socket_buffer_lock.acquire() - if self._write_buffer: - sent = self.send(self._write_buffer) - if self._show_socket_messages: - msg_type = ['Handshake', 'Message'][self._write_buffer[0] == '\x00' and - self._write_buffer[-1] == '\xff'] - msg = ('========================\n' - 'Sent %s:\n' - '========================\n' - '%s\n' - '========================') % (msg_type, - self._write_buffer[:sent-1]) - print msg - self._write_buffer = self._write_buffer[sent:] - self._socket_buffer_lock.release() - - def handle_read(self): - """Called when a socket can be read; overridden from asyncore.""" - self._socket_buffer_lock.acquire() - if self.handshake_done: - # Process a message reply from the remote Chrome instance. - self._read_buffer += self.recv(4096) - pos = self._read_buffer.find('\xff') - while pos >= 0: - pos += len('\xff') - data = self._read_buffer[:pos-len('\xff')] - pos2 = data.find('\x00') - if pos2 >= 0: - data = data[pos2 + 1:] - self._read_buffer = self._read_buffer[pos:] - if self._show_socket_messages: - msg = ('========================\n' - 'Received Message:\n' - '========================\n' - '%s\n' - '========================') % data - print msg - if self.inspector_thread: - self.inspector_thread.NotifyReply(data) - pos = self._read_buffer.find('\xff') - else: - # Process a handshake reply from the remote Chrome instance. - self._read_buffer += self.recv(4096) - pos = self._read_buffer.find('\r\n\r\n') - if pos >= 0: - pos += len('\r\n\r\n') - data = self._read_buffer[:pos] - self._read_buffer = self._read_buffer[pos:] - self.handshake_done = True - if self._show_socket_messages: - msg = ('=========================\n' - 'Received Handshake Reply:\n' - '=========================\n' - '%s\n' - '=========================') % data - print msg - self._socket_buffer_lock.release() - - def handle_close(self): - """Called when the socket is closed; overridden from asyncore.""" - if self._show_socket_messages: - msg = ('=========================\n' - 'Socket closed.\n' - '=========================') - print msg - self.close() - - def writable(self): - """Determines if writes can occur for this socket; overridden from asyncore. - - Returns: - True, if there is something to write to the socket, or - False, otherwise. - """ - return len(self._write_buffer) > 0 - - def handle_expt(self): - """Called when out-of-band data exists; overridden from asyncore.""" - self.handle_error() - - def handle_error(self): - """Called when an exception is raised; overridden from asyncore.""" - if self._show_socket_messages: - msg = ('=========================\n' - 'Socket error.\n' - '=========================') - print msg - self.close() - self.inspector_thread.ClientSocketExceptionOccurred() - asyncore.dispatcher.handle_error(self) - - -class _RemoteInspectorThread(threading.Thread): - """Manages communication using Chrome's remote inspector protocol. - - This class works in conjunction with the _DevToolsSocketClient class to - communicate with a remote Chrome instance following the remote inspector - communication protocol in WebKit. This class performs the higher-level work - of managing request and reply messages, whereas _DevToolsSocketClient handles - the lower-level work of socket communication. - """ - - def __init__(self, url, tab_index, tab_filter, verbose, show_socket_messages, - agent_name): - """Initialize. - - Args: - url: The base URL to connent to. - tab_index: The integer index of the tab in the remote Chrome instance to - use for snapshotting. - tab_filter: When specified, is run over tabs of the remote Chrome - instances to choose which one to connect to. - verbose: A boolean indicating whether or not to use verbose logging. - show_socket_messages: A boolean indicating whether or not to show the - socket messages sent/received when communicating with the remote - Chrome instance. - """ - threading.Thread.__init__(self) - self._logger = logging.getLogger('_RemoteInspectorThread') - self._logger.setLevel([logging.WARNING, logging.DEBUG][verbose]) - - self._killed = False - self._requests = [] - self._action_queue = [] - self._action_queue_condition = threading.Condition() - self._action_specific_callback = None # Callback only for current action. - self._action_specific_callback_lock = threading.Lock() - self._general_callbacks = [] # General callbacks that can be long-lived. - self._general_callbacks_lock = threading.Lock() - self._condition_to_wait = None - self._agent_name = agent_name - - # Create a DevToolsSocket client and wait for it to complete the remote - # debugging protocol handshake with the remote Chrome instance. - result = self._IdentifyDevToolsSocketConnectionInfo( - url, tab_index, tab_filter) - self._client = _DevToolsSocketClient( - verbose, show_socket_messages, result['host'], result['port'], - result['path']) - self._client.inspector_thread = self - while asyncore.socket_map: - if self._client.handshake_done or self._killed: - break - asyncore.loop(timeout=1, count=1, use_poll=True) - - def ClientSocketExceptionOccurred(self): - """Notifies that the _DevToolsSocketClient encountered an exception.""" - self.Kill() - - def NotifyReply(self, msg): - """Notifies of a reply message received from the remote Chrome instance. - - Args: - msg: A string reply message received from the remote Chrome instance; - assumed to be a JSON message formatted according to the remote - debugging communication protocol in WebKit. - """ - reply_dict = simplejson.loads(msg) - - # Notify callbacks of this message received from the remote inspector. - self._action_specific_callback_lock.acquire() - if self._action_specific_callback: - self._action_specific_callback(reply_dict) - self._action_specific_callback_lock.release() - - self._general_callbacks_lock.acquire() - if self._general_callbacks: - for callback in self._general_callbacks: - callback(reply_dict) - self._general_callbacks_lock.release() - - if 'result' in reply_dict: - # This is the result message associated with a previously-sent request. - request = self.GetRequestWithId(reply_dict['id']) - if request: - request.is_fulfilled_condition.acquire() - request.is_fulfilled_condition.notify() - request.is_fulfilled_condition.release() - - def run(self): - """Start this thread; overridden from threading.Thread.""" - while not self._killed: - self._action_queue_condition.acquire() - if self._action_queue: - # There's a request to the remote inspector that needs to be processed. - messages, callback = self._action_queue.pop(0) - self._action_specific_callback_lock.acquire() - self._action_specific_callback = callback - self._action_specific_callback_lock.release() - - # Prepare the request list. - for message_id, message in enumerate(messages): - self._requests.append( - _DevToolsSocketRequest(message[0], message[1], message_id)) - - # Send out each request. Wait until each request is complete before - # sending the next request. - for request in self._requests: - self._FillInParams(request) - self._client.SendMessage(str(request)) - - request.is_fulfilled_condition.acquire() - self._condition_to_wait = request.is_fulfilled_condition - request.is_fulfilled_condition.wait() - request.is_fulfilled_condition.release() - - if self._killed: - self._client.close() - return - - # Clean up so things are ready for the next request. - self._requests = [] - - self._action_specific_callback_lock.acquire() - self._action_specific_callback = None - self._action_specific_callback_lock.release() - - # Wait until there is something to process. - self._condition_to_wait = self._action_queue_condition - self._action_queue_condition.wait() - self._action_queue_condition.release() - self._client.close() - - def Kill(self): - """Notify this thread that it should stop executing.""" - self._killed = True - # The thread might be waiting on a condition. - if self._condition_to_wait: - self._condition_to_wait.acquire() - self._condition_to_wait.notify() - self._condition_to_wait.release() - - def PerformAction(self, request_messages, reply_message_callback): - """Notify this thread of an action to perform using the remote inspector. - - Args: - request_messages: A list of strings representing the requests to make - using the remote inspector. - reply_message_callback: A callable to be invoked any time a message is - received from the remote inspector while the current action is - being performed. The callable should accept a single argument, - which is a dictionary representing a message received. - """ - self._action_queue_condition.acquire() - self._action_queue.append((request_messages, reply_message_callback)) - self._action_queue_condition.notify() - self._action_queue_condition.release() - - def AddMessageCallback(self, callback): - """Add a callback to invoke for messages received from the remote inspector. - - Args: - callback: A callable to be invoked any time a message is received from the - remote inspector. The callable should accept a single argument, which - is a dictionary representing a message received. - """ - self._general_callbacks_lock.acquire() - self._general_callbacks.append(callback) - self._general_callbacks_lock.release() - - def RemoveMessageCallback(self, callback): - """Remove a callback from the set of those to invoke for messages received. - - Args: - callback: A callable to remove from consideration. - """ - self._general_callbacks_lock.acquire() - self._general_callbacks.remove(callback) - self._general_callbacks_lock.release() - - def GetRequestWithId(self, request_id): - """Identifies the request with the specified id. - - Args: - request_id: An integer request id; should be unique for each request. - - Returns: - A request object associated with the given id if found, or - None otherwise. - """ - found_request = [x for x in self._requests if x.id == request_id] - if found_request: - return found_request[0] - return None - - def GetFirstUnfulfilledRequest(self, method): - """Identifies the first unfulfilled request with the given method name. - - An unfulfilled request is one for which all relevant reply messages have - not yet been received from the remote inspector. - - Args: - method: The string method name of the request for which to search. - - Returns: - The first request object in the request list that is not yet fulfilled - and is also associated with the given method name, or - None if no such request object can be found. - """ - for request in self._requests: - if not request.is_fulfilled and request.method == method: - return request - return None - - def _GetLatestRequestOfType(self, ref_req, method): - """Identifies the latest specified request before a reference request. - - This function finds the latest request with the specified method that - occurs before the given reference request. - - Args: - ref_req: A reference request from which to start looking. - method: The string method name of the request for which to search. - - Returns: - The latest _DevToolsSocketRequest object with the specified method, - if found, or None otherwise. - """ - start_looking = False - for request in self._requests[::-1]: - if request.id == ref_req.id: - start_looking = True - elif start_looking: - if request.method == method: - return request - return None - - def _FillInParams(self, request): - """Fills in parameters for requests as necessary before the request is sent. - - Args: - request: The _DevToolsSocketRequest object associated with a request - message that is about to be sent. - """ - if request.method == self._agent_name +'.takeHeapSnapshot': - # We always want detailed v8 heap snapshot information. - request.params = {'detailed': True} - elif request.method == self._agent_name + '.getHeapSnapshot': - # To actually request the snapshot data from a previously-taken snapshot, - # we need to specify the unique uid of the snapshot we want. - # The relevant uid should be contained in the last - # 'Profiler.takeHeapSnapshot' request object. - last_req = self._GetLatestRequestOfType(request, - self._agent_name + '.takeHeapSnapshot') - if last_req and 'uid' in last_req.results: - request.params = {'uid': last_req.results['uid']} - elif request.method == self._agent_name + '.getProfile': - # TODO(eustas): Remove this case after M27 is released. - last_req = self._GetLatestRequestOfType(request, - self._agent_name + '.takeHeapSnapshot') - if last_req and 'uid' in last_req.results: - request.params = {'type': 'HEAP', 'uid': last_req.results['uid']} - - @staticmethod - def _IdentifyDevToolsSocketConnectionInfo(url, tab_index, tab_filter): - """Identifies DevToolsSocket connection info from a remote Chrome instance. - - Args: - url: The base URL to connent to. - tab_index: The integer index of the tab in the remote Chrome instance to - which to connect. - tab_filter: When specified, is run over tabs of the remote Chrome instance - to choose which one to connect to. - - Returns: - A dictionary containing the DevToolsSocket connection info: - { - 'host': string, - 'port': integer, - 'path': string, - } - - Raises: - RuntimeError: When DevToolsSocket connection info cannot be identified. - """ - try: - f = urllib2.urlopen(url + '/json') - result = f.read() - logging.debug(result) - result = simplejson.loads(result) - except urllib2.URLError, e: - raise RuntimeError( - 'Error accessing Chrome instance debugging port: ' + str(e)) - - if tab_filter: - connect_to = filter(tab_filter, result)[0] - else: - if tab_index >= len(result): - raise RuntimeError( - 'Specified tab index %d doesn\'t exist (%d tabs found)' % - (tab_index, len(result))) - connect_to = result[tab_index] - - logging.debug(simplejson.dumps(connect_to)) - - if 'webSocketDebuggerUrl' not in connect_to: - raise RuntimeError('No socket URL exists for the specified tab.') - - socket_url = connect_to['webSocketDebuggerUrl'] - parsed = urlparse.urlparse(socket_url) - # On ChromeOS, the "ws://" scheme may not be recognized, leading to an - # incorrect netloc (and empty hostname and port attributes) in |parsed|. - # Change the scheme to "http://" to fix this. - if not parsed.hostname or not parsed.port: - socket_url = 'http' + socket_url[socket_url.find(':'):] - parsed = urlparse.urlparse(socket_url) - # Warning: |parsed.scheme| is incorrect after this point. - return ({'host': parsed.hostname, - 'port': parsed.port, - 'path': parsed.path}) - - -class _RemoteInspectorDriverThread(threading.Thread): - """Drives the communication service with the remote inspector.""" - - def __init__(self): - """Initialize.""" - threading.Thread.__init__(self) - - def run(self): - """Drives the communication service with the remote inspector.""" - try: - while asyncore.socket_map: - asyncore.loop(timeout=1, count=1, use_poll=True) - except KeyboardInterrupt: - pass - - -class _V8HeapSnapshotParser(object): - """Parses v8 heap snapshot data.""" - _CHILD_TYPES = ['context', 'element', 'property', 'internal', 'hidden', - 'shortcut', 'weak'] - _NODE_TYPES = ['hidden', 'array', 'string', 'object', 'code', 'closure', - 'regexp', 'number', 'native', 'synthetic'] - - @staticmethod - def ParseSnapshotData(raw_data): - """Parses raw v8 heap snapshot data and returns the summarized results. - - The raw heap snapshot data is represented as a JSON object with the - following keys: 'snapshot', 'nodes', and 'strings'. - - The 'snapshot' value provides the 'title' and 'uid' attributes for the - snapshot. For example: - { u'title': u'org.webkit.profiles.user-initiated.1', u'uid': 1} - - The 'nodes' value is a list of node information from the v8 heap, with a - special first element that describes the node serialization layout (see - HeapSnapshotJSONSerializer::SerializeNodes). All other list elements - contain information about nodes in the v8 heap, according to the - serialization layout. - - The 'strings' value is a list of strings, indexed by values in the 'nodes' - list to associate nodes with strings. - - Args: - raw_data: A string representing the raw v8 heap snapshot data. - - Returns: - A dictionary containing the summarized v8 heap snapshot data: - { - 'total_v8_node_count': integer, # Total number of nodes in the v8 heap. - 'total_shallow_size': integer, # Total heap size, in bytes. - } - """ - total_node_count = 0 - total_shallow_size = 0 - constructors = {} - - # TODO(dennisjeffrey): The following line might be slow, especially on - # ChromeOS. Investigate faster alternatives. - heap = simplejson.loads(raw_data) - - index = 1 # Bypass the special first node list item. - node_list = heap['nodes'] - while index < len(node_list): - node_type = node_list[index] - node_name = node_list[index + 1] - node_id = node_list[index + 2] - node_self_size = node_list[index + 3] - node_retained_size = node_list[index + 4] - node_dominator = node_list[index + 5] - node_children_count = node_list[index + 6] - index += 7 - - node_children = [] - for i in xrange(node_children_count): - child_type = node_list[index] - child_type_string = _V8HeapSnapshotParser._CHILD_TYPES[int(child_type)] - child_name_index = node_list[index + 1] - child_to_node = node_list[index + 2] - index += 3 - - child_info = { - 'type': child_type_string, - 'name_or_index': child_name_index, - 'to_node': child_to_node, - } - node_children.append(child_info) - - # Get the constructor string for this node so nodes can be grouped by - # constructor. - # See HeapSnapshot.js: WebInspector.HeapSnapshotNode.prototype. - type_string = _V8HeapSnapshotParser._NODE_TYPES[int(node_type)] - constructor_name = None - if type_string == 'hidden': - constructor_name = '(system)' - elif type_string == 'object': - constructor_name = heap['strings'][int(node_name)] - elif type_string == 'native': - pos = heap['strings'][int(node_name)].find('/') - if pos >= 0: - constructor_name = heap['strings'][int(node_name)][:pos].rstrip() - else: - constructor_name = heap['strings'][int(node_name)] - elif type_string == 'code': - constructor_name = '(compiled code)' - else: - constructor_name = '(' + type_string + ')' - - node_obj = { - 'type': type_string, - 'name': heap['strings'][int(node_name)], - 'id': node_id, - 'self_size': node_self_size, - 'retained_size': node_retained_size, - 'dominator': node_dominator, - 'children_count': node_children_count, - 'children': node_children, - } - - if constructor_name not in constructors: - constructors[constructor_name] = [] - constructors[constructor_name].append(node_obj) - - total_node_count += 1 - total_shallow_size += node_self_size - - # TODO(dennisjeffrey): Have this function also return more detailed v8 - # heap snapshot data when a need for it arises (e.g., using |constructors|). - result = {} - result['total_v8_node_count'] = total_node_count - result['total_shallow_size'] = total_shallow_size - return result - - -# TODO(dennisjeffrey): The "verbose" option used in this file should re-use -# pyauto's verbose flag. -class RemoteInspectorClient(object): - """Main class for interacting with Chrome's remote inspector. - - Upon initialization, a socket connection to Chrome's remote inspector will - be established. Users of this class should call Stop() to close the - connection when it's no longer needed. - - Public Methods: - Stop: Close the connection to the remote inspector. Should be called when - a user is done using this module. - HeapSnapshot: Takes a v8 heap snapshot and returns the summarized data. - GetMemoryObjectCounts: Retrieves memory object count information. - CollectGarbage: Forces a garbage collection. - StartTimelineEventMonitoring: Starts monitoring for timeline events. - StopTimelineEventMonitoring: Stops monitoring for timeline events. - """ - - # TODO(dennisjeffrey): Allow a user to specify a window index too (not just a - # tab index), when running through PyAuto. - def __init__(self, tab_index=0, tab_filter=None, - verbose=False, show_socket_messages=False, - url='http://localhost:9222'): - """Initialize. - - Args: - tab_index: The integer index of the tab in the remote Chrome instance to - which to connect. Defaults to 0 (the first tab). - tab_filter: When specified, is run over tabs of the remote Chrome - instance to choose which one to connect to. - verbose: A boolean indicating whether or not to use verbose logging. - show_socket_messages: A boolean indicating whether or not to show the - socket messages sent/received when communicating with the remote - Chrome instance. - """ - self._tab_index = tab_index - self._tab_filter = tab_filter - self._verbose = verbose - self._show_socket_messages = show_socket_messages - - self._timeline_started = False - - logging.basicConfig() - self._logger = logging.getLogger('RemoteInspectorClient') - self._logger.setLevel([logging.WARNING, logging.DEBUG][verbose]) - - # Creating _RemoteInspectorThread might raise an exception. This prevents an - # AttributeError in the destructor. - self._remote_inspector_thread = None - self._remote_inspector_driver_thread = None - - self._version = self._GetVersion(url) - - # TODO(loislo): Remove this hack after M28 is released. - self._agent_name = 'Profiler' - if self._IsBrowserDayNumberGreaterThan(1470): - self._agent_name = 'HeapProfiler' - - # Start up a thread for long-term communication with the remote inspector. - self._remote_inspector_thread = _RemoteInspectorThread( - url, tab_index, tab_filter, verbose, show_socket_messages, - self._agent_name) - self._remote_inspector_thread.start() - # At this point, a connection has already been made to the remote inspector. - - # This thread calls asyncore.loop, which activates the channel service. - self._remote_inspector_driver_thread = _RemoteInspectorDriverThread() - self._remote_inspector_driver_thread.start() - - def __del__(self): - """Called on destruction of this object.""" - self.Stop() - - def Stop(self): - """Stop/close communication with the remote inspector.""" - if self._remote_inspector_thread: - self._remote_inspector_thread.Kill() - self._remote_inspector_thread.join() - self._remote_inspector_thread = None - if self._remote_inspector_driver_thread: - self._remote_inspector_driver_thread.join() - self._remote_inspector_driver_thread = None - - def HeapSnapshot(self, include_summary=False): - """Takes a v8 heap snapshot. - - Returns: - A dictionary containing information for a single v8 heap - snapshot that was taken. - { - 'url': string, # URL of the webpage that was snapshotted. - 'raw_data': string, # The raw data as JSON string. - 'total_v8_node_count': integer, # Total number of nodes in the v8 heap. - # Only if |include_summary| is True. - 'total_heap_size': integer, # Total v8 heap size (number of bytes). - # Only if |include_summary| is True. - } - """ - HEAP_SNAPSHOT_MESSAGES = [ - ('Page.getResourceTree', {}), - ('Debugger.enable', {}), - (self._agent_name + '.clearProfiles', {}), - (self._agent_name + '.takeHeapSnapshot', {}), - (self._agent_name + '.getHeapSnapshot', {}), - ] - - self._current_heap_snapshot = [] - self._url = '' - self._collected_heap_snapshot_data = {} - - done_condition = threading.Condition() - - def HandleReply(reply_dict): - """Processes a reply message received from the remote Chrome instance. - - Args: - reply_dict: A dictionary object representing the reply message received - from the remote inspector. - """ - if 'result' in reply_dict: - # This is the result message associated with a previously-sent request. - request = self._remote_inspector_thread.GetRequestWithId( - reply_dict['id']) - if 'frameTree' in reply_dict['result']: - self._url = reply_dict['result']['frameTree']['frame']['url'] - elif request.method == self._agent_name + '.getHeapSnapshot': - # A heap snapshot has been completed. Analyze and output the data. - self._logger.debug('Heap snapshot taken: %s', self._url) - # TODO(dennisjeffrey): Parse the heap snapshot on-the-fly as the data - # is coming in over the wire, so we can avoid storing the entire - # snapshot string in memory. - raw_snapshot_data = ''.join(self._current_heap_snapshot) - self._collected_heap_snapshot_data = { - 'url': self._url, - 'raw_data': raw_snapshot_data} - if include_summary: - self._logger.debug('Now analyzing heap snapshot...') - parser = _V8HeapSnapshotParser() - time_start = time.time() - self._logger.debug('Raw snapshot data size: %.2f MB', - len(raw_snapshot_data) / (1024.0 * 1024.0)) - result = parser.ParseSnapshotData(raw_snapshot_data) - self._logger.debug('Time to parse data: %.2f sec', - time.time() - time_start) - count = result['total_v8_node_count'] - self._collected_heap_snapshot_data['total_v8_node_count'] = count - total_size = result['total_shallow_size'] - self._collected_heap_snapshot_data['total_heap_size'] = total_size - - done_condition.acquire() - done_condition.notify() - done_condition.release() - elif 'method' in reply_dict: - # This is an auxiliary message sent from the remote Chrome instance. - if reply_dict['method'] == self._agent_name + '.addProfileHeader': - snapshot_req = ( - self._remote_inspector_thread.GetFirstUnfulfilledRequest( - self._agent_name + '.takeHeapSnapshot')) - if snapshot_req: - snapshot_req.results['uid'] = reply_dict['params']['header']['uid'] - elif reply_dict['method'] == self._agent_name + '.addHeapSnapshotChunk': - self._current_heap_snapshot.append(reply_dict['params']['chunk']) - - # Tell the remote inspector to take a v8 heap snapshot, then wait until - # the snapshot information is available to return. - self._remote_inspector_thread.PerformAction(HEAP_SNAPSHOT_MESSAGES, - HandleReply) - - done_condition.acquire() - done_condition.wait() - done_condition.release() - - return self._collected_heap_snapshot_data - - def EvaluateJavaScript(self, expression): - """Evaluates a JavaScript expression and returns the result. - - Sends a message containing the expression to the remote Chrome instance we - are connected to, and evaluates it in the context of the tab we are - connected to. Blocks until the result is available and returns it. - - Returns: - A dictionary representing the result. - """ - EVALUATE_MESSAGES = [ - ('Runtime.evaluate', { 'expression': expression, - 'objectGroup': 'group', - 'returnByValue': True }), - ('Runtime.releaseObjectGroup', { 'objectGroup': 'group' }) - ] - - self._result = None - done_condition = threading.Condition() - - def HandleReply(reply_dict): - """Processes a reply message received from the remote Chrome instance. - - Args: - reply_dict: A dictionary object representing the reply message received - from the remote Chrome instance. - """ - if 'result' in reply_dict and 'result' in reply_dict['result']: - self._result = reply_dict['result']['result']['value'] - - done_condition.acquire() - done_condition.notify() - done_condition.release() - - # Tell the remote inspector to evaluate the given expression, then wait - # until that information is available to return. - self._remote_inspector_thread.PerformAction(EVALUATE_MESSAGES, - HandleReply) - - done_condition.acquire() - done_condition.wait() - done_condition.release() - - return self._result - - def GetMemoryObjectCounts(self): - """Retrieves memory object count information. - - Returns: - A dictionary containing the memory object count information: - { - 'DOMNodeCount': integer, # Total number of DOM nodes. - 'EventListenerCount': integer, # Total number of event listeners. - } - """ - MEMORY_COUNT_MESSAGES = [ - ('Memory.getDOMCounters', {}) - ] - - self._event_listener_count = None - self._dom_node_count = None - - done_condition = threading.Condition() - def HandleReply(reply_dict): - """Processes a reply message received from the remote Chrome instance. - - Args: - reply_dict: A dictionary object representing the reply message received - from the remote Chrome instance. - """ - if 'result' in reply_dict: - self._event_listener_count = reply_dict['result']['jsEventListeners'] - self._dom_node_count = reply_dict['result']['nodes'] - - done_condition.acquire() - done_condition.notify() - done_condition.release() - - # Tell the remote inspector to collect memory count info, then wait until - # that information is available to return. - self._remote_inspector_thread.PerformAction(MEMORY_COUNT_MESSAGES, - HandleReply) - - done_condition.acquire() - done_condition.wait() - done_condition.release() - - return { - 'DOMNodeCount': self._dom_node_count, - 'EventListenerCount': self._event_listener_count, - } - - def CollectGarbage(self): - """Forces a garbage collection.""" - COLLECT_GARBAGE_MESSAGES = [ - ('Profiler.collectGarbage', {}) - ] - - # Tell the remote inspector to do a garbage collect. We can return - # immediately, since there is no result for which to wait. - self._remote_inspector_thread.PerformAction(COLLECT_GARBAGE_MESSAGES, None) - - def StartTimelineEventMonitoring(self, event_callback): - """Starts timeline event monitoring. - - Args: - event_callback: A callable to invoke whenever a timeline event is observed - from the remote inspector. The callable should take a single input, - which is a dictionary containing the detailed information of a - timeline event. - """ - if self._timeline_started: - self._logger.warning('Timeline monitoring already started.') - return - TIMELINE_MESSAGES = [ - ('Timeline.start', {}) - ] - - self._event_callback = event_callback - - done_condition = threading.Condition() - def HandleReply(reply_dict): - """Processes a reply message received from the remote Chrome instance. - - Args: - reply_dict: A dictionary object representing the reply message received - from the remote Chrome instance. - """ - if 'result' in reply_dict: - done_condition.acquire() - done_condition.notify() - done_condition.release() - if reply_dict.get('method') == 'Timeline.eventRecorded': - self._event_callback(reply_dict['params']['record']) - - # Tell the remote inspector to start the timeline. - self._timeline_callback = HandleReply - self._remote_inspector_thread.AddMessageCallback(self._timeline_callback) - self._remote_inspector_thread.PerformAction(TIMELINE_MESSAGES, None) - - done_condition.acquire() - done_condition.wait() - done_condition.release() - - self._timeline_started = True - - def StopTimelineEventMonitoring(self): - """Stops timeline event monitoring.""" - if not self._timeline_started: - self._logger.warning('Timeline monitoring already stopped.') - return - TIMELINE_MESSAGES = [ - ('Timeline.stop', {}) - ] - - done_condition = threading.Condition() - def HandleReply(reply_dict): - """Processes a reply message received from the remote Chrome instance. - - Args: - reply_dict: A dictionary object representing the reply message received - from the remote Chrome instance. - """ - if 'result' in reply_dict: - done_condition.acquire() - done_condition.notify() - done_condition.release() - - # Tell the remote inspector to stop the timeline. - self._remote_inspector_thread.RemoveMessageCallback(self._timeline_callback) - self._remote_inspector_thread.PerformAction(TIMELINE_MESSAGES, HandleReply) - - done_condition.acquire() - done_condition.wait() - done_condition.release() - - self._timeline_started = False - - def _ConvertByteCountToHumanReadableString(self, num_bytes): - """Converts an integer number of bytes into a human-readable string. - - Args: - num_bytes: An integer number of bytes. - - Returns: - A human-readable string representation of the given number of bytes. - """ - if num_bytes < 1024: - return '%d B' % num_bytes - elif num_bytes < 1048576: - return '%.2f KB' % (num_bytes / 1024.0) - else: - return '%.2f MB' % (num_bytes / 1048576.0) - - @staticmethod - def _GetVersion(endpoint): - """Fetches version information from a remote Chrome instance. - - Args: - endpoint: The base URL to connent to. - - Returns: - A dictionary containing Browser and Content version information: - { - 'Browser': { - 'major': integer, - 'minor': integer, - 'fix': integer, - 'day': integer - }, - 'Content': { - 'name': string, - 'major': integer, - 'minor': integer - } - } - - Raises: - RuntimeError: When Browser version info can't be fetched or parsed. - """ - try: - f = urllib2.urlopen(endpoint + '/json/version') - result = f.read(); - result = simplejson.loads(result) - except urllib2.URLError, e: - raise RuntimeError( - 'Error accessing Chrome instance debugging port: ' + str(e)) - - if 'Browser' not in result: - raise RuntimeError('Browser version is not specified.') - - parsed = re.search('^Chrome\/(\d+).(\d+).(\d+).(\d+)', result['Browser']) - if parsed is None: - raise RuntimeError('Browser-Version cannot be parsed.') - try: - day = int(parsed.group(3)) - browser_info = { - 'major': int(parsed.group(1)), - 'minor': int(parsed.group(2)), - 'day': day, - 'fix': int(parsed.group(4)), - } - except ValueError: - raise RuntimeError('Browser-Version cannot be parsed.') - - if 'WebKit-Version' not in result: - raise RuntimeError('Content-Version is not specified.') - - parsed = re.search('^(\d+)\.(\d+)', result['WebKit-Version']) - if parsed is None: - raise RuntimeError('Content-Version cannot be parsed.') - - try: - platform_info = { - 'name': 'Blink' if day > 1464 else 'WebKit', - 'major': int(parsed.group(1)), - 'minor': int(parsed.group(2)), - } - except ValueError: - raise RuntimeError('WebKit-Version cannot be parsed.') - - return { - 'browser': browser_info, - 'platform': platform_info - } - - def _IsContentVersionNotOlderThan(self, major, minor): - """Compares remote Browser Content version with specified one. - - Args: - major: Major Webkit version. - minor: Minor Webkit version. - - Returns: - True if remote Content version is same or newer than specified, - False otherwise. - - Raises: - RuntimeError: If remote Content version hasn't been fetched yet. - """ - if not hasattr(self, '_version'): - raise RuntimeError('Browser version has not been fetched yet.') - version = self._version['platform'] - - if version['major'] < major: - return False - elif version['major'] == major and version['minor'] < minor: - return False - else: - return True - - def _IsBrowserDayNumberGreaterThan(self, day_number): - """Compares remote Chromium day number with specified one. - - Args: - day_number: Forth part of the chromium version. - - Returns: - True if remote Chromium day number is same or newer than specified, - False otherwise. - - Raises: - RuntimeError: If remote Chromium version hasn't been fetched yet. - """ - if not hasattr(self, '_version'): - raise RuntimeError('Browser revision has not been fetched yet.') - version = self._version['browser'] - - return version['day'] > day_number diff --git a/chrome/test/pyautolib/timer_queue.py b/chrome/test/pyautolib/timer_queue.py deleted file mode 100644 index b7d668d..0000000 --- a/chrome/test/pyautolib/timer_queue.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import copy -import threading -import time - -class TimerQueue(threading.Thread): - """Executes timers at a given interval. - - This class provides the ability to run methods at a given interval. All - methods are fired synchronously. Only one method is running at a time. - - Example of using TimerQueue: - def _fooPrinter(word): - print('foo : %s' % word) - - timers = TimerQueue() - timers.addTimer(self._fooPrinter, 15, args=('hello',)) - timers.start() - - >> hello will be printed after 15 seconds - - Note: TimerQueue is a subclass of threading.Thread, call start() to activate; - do not call run() directly. - """ - - def __init__(self): - """Initializes a TimerQueue object.""" - threading.Thread.__init__(self, name='timer_thread') - self.timer_queue_lock = threading.Lock() - self.terminate = False - self.wait_time = 1 - self.timers = [] - - def AddTimer(self, method, interval, args=()): - """Adds a timer to the queue. - - Args: - method: the method to be called at the given interval - interval: delay between method runs, in seconds - args: arguments to be passed to the method - """ - self.timer_queue_lock.acquire() - next_time = time.time() + interval - self.timers.append({'method': method, 'interval': interval, - 'next time': next_time, 'args': copy.copy(args)}) - self.timer_queue_lock.release() - - def SetResolution(self, resolution): - """Sets the timer check frequency, in seconds.""" - self.wait_time = resolution - - def RemoveTimer(self, method): - """Removes a timer from the queue. - - Args: - method: the timer containing the given method to be removed - """ - self.timer_queue_lock.acquire() - for timer in self.timers: - if timer['method'] == method: - self.timers.remove(timer) - break - self.timer_queue_lock.release() - - def Stop(self): - """Stops the timer.""" - self.terminate = True - - def run(self): - """Primary run loop for the timer.""" - while True: - now = time.time() - self.timer_queue_lock.acquire() - for timer in self.timers: - if timer['next time'] <= now: - # Use * to break the list into separate arguments - timer['method'](*timer['args']) - timer['next time'] += timer['interval'] - self.timer_queue_lock.release() - if self.terminate: - return - time.sleep(self.wait_time) diff --git a/chrome/test/pyautolib/webdriver.DEPS/DEPS b/chrome/test/pyautolib/webdriver.DEPS/DEPS deleted file mode 100644 index eb9f473..0000000 --- a/chrome/test/pyautolib/webdriver.DEPS/DEPS +++ /dev/null @@ -1,5 +0,0 @@ -deps = { - # This is only used by the buildbot master config for chrome official. - 'src/third_party/webdriver/pylib/selenium': - 'http://selenium.googlecode.com/svn/trunk/py/selenium@18337', -} |