diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-10-22 16:41:35 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-10-22 16:41:35 +0100 |
commit | 8bcbed890bc3ce4d7a057a8f32cab53fa534672e (patch) | |
tree | 1390b6675d21328859f01f50203d9bde09105298 /chrome/test | |
parent | 116fa16b45c9efe30e785b9fc32f09780ca23bec (diff) | |
download | chromium_org-8bcbed890bc3ce4d7a057a8f32cab53fa534672e.tar.gz |
Merge from Chromium at DEPS revision 230120
This commit was generated by merge_to_master.py.
Change-Id: I54bc06b7ee8a07092e74ce3b68c6893508349042
Diffstat (limited to 'chrome/test')
25 files changed, 775 insertions, 459 deletions
diff --git a/chrome/test/android/OWNERS b/chrome/test/android/OWNERS index a223621c02..df798f4460 100644 --- a/chrome/test/android/OWNERS +++ b/chrome/test/android/OWNERS @@ -1,6 +1,8 @@ aruslan@chromium.org bulach@chromium.org +dfalcantara@chromium.org dtrainor@chromium.org +miguelg@chromium.org nyquist@chromium.org tedchoc@chromium.org yfriedman@chromium.org diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/InfoBarTestAnimationListener.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/InfoBarTestAnimationListener.java new file mode 100644 index 0000000000..97b39a3800 --- /dev/null +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/InfoBarTestAnimationListener.java @@ -0,0 +1,98 @@ +// 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. +package org.chromium.chrome.test.util; + +import android.os.SystemClock; + +import java.util.concurrent.TimeUnit; + +import org.chromium.chrome.browser.infobar.AnimationHelper; +import org.chromium.chrome.browser.infobar.InfoBarContainer; + +/** + * Allow tests to register for animation finished notifications. + */ +public class InfoBarTestAnimationListener implements InfoBarContainer.InfoBarAnimationListener { + + private static long WAIT_MILLIS = TimeUnit.SECONDS.toMillis(5); + + private static class ConditionalWait { + private volatile Boolean mCondition; + private Object mLock; + + ConditionalWait() { + mCondition = false; + mLock = new Object(); + } + + /** + * Waits for {@code millis} or until the value of the condition becomes true + * if it does it resets it to false before returning so it can be reused. + * + * @return true if the condition becomes true before the specified {@code millis}. + */ + public boolean waitAndExpire(long millis) throws InterruptedException { + synchronized(mLock) { + while (!mCondition && millis > 0) { + long start = SystemClock.elapsedRealtime(); + mLock.wait(millis); + millis -= (SystemClock.elapsedRealtime() - start); + } + boolean result = mCondition; + mCondition = false; + return result; + } + } + + public void set(boolean value) { + synchronized(mLock) { + mCondition = value; + if (value) { + mLock.notify(); + } + } + } + } + + private ConditionalWait mAddAnimationFinished; + private ConditionalWait mSwapAnimationFinished; + private ConditionalWait mRemoveAnimationFinished; + + + public InfoBarTestAnimationListener() { + mAddAnimationFinished = new ConditionalWait(); + mSwapAnimationFinished = new ConditionalWait(); + mRemoveAnimationFinished = new ConditionalWait(); + } + + @Override + public void notifyAnimationFinished(int animationType) { + switch(animationType) { + case AnimationHelper.ANIMATION_TYPE_SHOW: + mAddAnimationFinished.set(true); + break; + case AnimationHelper.ANIMATION_TYPE_SWAP: + mSwapAnimationFinished.set(true); + break; + case AnimationHelper.ANIMATION_TYPE_HIDE: + mRemoveAnimationFinished.set(true); + break; + default: + throw new UnsupportedOperationException( + "Animation finished for unknown type " + animationType); + } + } + + public boolean addInfoBarAnimationFinished() throws InterruptedException { + return mAddAnimationFinished.waitAndExpire(WAIT_MILLIS); + } + + public boolean swapInfoBarAnimationFinished() throws InterruptedException { + return mSwapAnimationFinished.waitAndExpire(WAIT_MILLIS); + } + + public boolean removeInfoBarAnimationFinished() throws InterruptedException { + return mRemoveAnimationFinished.waitAndExpire(WAIT_MILLIS); + } +} diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/InfoBarUtil.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/InfoBarUtil.java new file mode 100644 index 0000000000..70a9f740dc --- /dev/null +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/InfoBarUtil.java @@ -0,0 +1,76 @@ +// 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. + +package org.chromium.chrome.test.util; + +import android.test.ActivityInstrumentationTestCase2; +import android.test.TouchUtils; +import android.view.View; + +import org.chromium.chrome.R; + +import org.chromium.chrome.browser.infobar.InfoBar; +import org.chromium.content.browser.test.util.TestTouchUtils; + +/** + * Utility functions for dealing with InfoBars. + */ +public class InfoBarUtil { + /** + * Finds, and optionally clicks, the button with the specified ID in the given InfoBar. + * @return True if the View was found. + */ + private static boolean findButton(ActivityInstrumentationTestCase2<?> test, + InfoBar infoBar, int buttonId, boolean click) { + View button = infoBar.getContentWrapper().findViewById(buttonId); + if (button == null) return false; + if (click) TestTouchUtils.singleClickView(test.getInstrumentation(), button); + return true; + } + + /** + * Checks if the primary button exists on the InfoBar. + * @return True if the View was found. + */ + public static boolean hasPrimaryButton(ActivityInstrumentationTestCase2<?> test, + InfoBar infoBar) { + return findButton(test, infoBar, R.id.button_primary, false); + } + + /** + * Checks if the secondary button exists on the InfoBar. + * @return True if the View was found. + */ + public static boolean hasSecondaryButton(ActivityInstrumentationTestCase2<?> test, + InfoBar infoBar) { + return findButton(test, infoBar, R.id.button_secondary, false); + } + + /** + * Simulates clicking the Close button in the specified infobar. + * @return True if the View was found. + */ + public static boolean clickCloseButton(ActivityInstrumentationTestCase2<?> test, + InfoBar infoBar) { + return findButton(test, infoBar, R.id.infobar_close_button, true); + } + + /** + * Simulates clicking the primary button in the specified infobar. + * @return True if the View was found. + */ + public static boolean clickPrimaryButton(ActivityInstrumentationTestCase2<?> test, + InfoBar infoBar) { + return findButton(test, infoBar, R.id.button_primary, true); + } + + /** + * Simulates clicking the secondary button in the specified infobar. + * @return True if the View was found. + */ + public static boolean clickSecondaryButton(ActivityInstrumentationTestCase2<?> test, + InfoBar infoBar) { + return findButton(test, infoBar, R.id.button_secondary, true); + } +} diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/TranslateUtil.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/TranslateUtil.java new file mode 100644 index 0000000000..7abc29f9eb --- /dev/null +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/TranslateUtil.java @@ -0,0 +1,78 @@ +// 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. + +package org.chromium.chrome.test.util; + +import android.content.Context; +import android.test.ActivityInstrumentationTestCase2; +import android.test.TouchUtils; +import android.text.SpannableString; +import android.text.style.ClickableSpan; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.chromium.chrome.R; + +import org.chromium.chrome.browser.infobar.InfoBar; +import org.chromium.content.browser.test.util.TestTouchUtils; + + +/** + * Utility functions for dealing with Translate InfoBars. + */ +public class TranslateUtil { + /** + * Finds the first clickable span inside a TextView and clicks it. + * + * @return True if the panel is opened. + */ + public static boolean openLanguagePanel(ActivityInstrumentationTestCase2<?> test, + InfoBar infoBar) { + View view = infoBar.getContentWrapper().findViewById(R.id.infobar_message); + if (view == null) { + return false; + } + + TextView text = (TextView) view.findViewById(R.id.infobar_message); + + SpannableString spannable = (SpannableString) text.getText(); + ClickableSpan[] clickable = + spannable.getSpans(0, spannable.length() -1, ClickableSpan.class); + if (clickable.length <= 0) { + return false; + } + + // Find the approximate coordinates of the first link of the first line of text so we can + // click there. Clicking on any character of the link will work so instead of focusing on + // the beginning of the link we add one more character so that finding a valid coordinate + // is more reliable. + int x = spannable.getSpanStart(clickable[0]) + 1; + float nChars = text.getLayout().getLineVisibleEnd(0); + + // Not all characters have the same width but this is a good approximation. + float sizePerChar = text.getLayout().getLineWidth(0) / nChars; + float xPos = text.getPaddingLeft() + (sizePerChar * x); + float yPos = text.getHeight() / (float) 2; + + TestTouchUtils.singleClickView(test.getInstrumentation(), text, (int) xPos, (int) yPos); + + return verifyInfoBarText(infoBar, + test.getActivity().getString(R.string.translate_infobar_change_languages)); + } + + public static boolean verifyInfoBarText(InfoBar infoBar, String text) { + View view = infoBar.getContentWrapper().findViewById(R.id.infobar_message); + if (view == null) { + return false; + } + String infoBarText = findInfoBarText(view); + return text.equals(infoBarText); + } + + private static String findInfoBarText(View view) { + TextView text = (TextView) view.findViewById(R.id.infobar_message); + return text != null ? text.getText().toString() : null; + } +} diff --git a/chrome/test/base/in_process_browser_test_browsertest.cc b/chrome/test/base/in_process_browser_test_browsertest.cc index 2fbe4194db..5272c35bff 100644 --- a/chrome/test/base/in_process_browser_test_browsertest.cc +++ b/chrome/test/base/in_process_browser_test_browsertest.cc @@ -39,6 +39,7 @@ class LoadFailObserver : public content::WebContentsObserver { virtual void DidFailProvisionalLoad( int64 frame_id, + const string16& frame_unique_name, bool is_main_frame, const GURL& validated_url, int error_code, diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h index 5895ff1788..1f2804453c 100644 --- a/chrome/test/base/test_browser_window.h +++ b/chrome/test/base/test_browser_window.h @@ -90,9 +90,6 @@ class TestBrowserWindow : public BrowserWindow { bool* is_keyboard_shortcut) OVERRIDE; virtual void HandleKeyboardEvent( const content::NativeWebKeyboardEvent& event) OVERRIDE {} - virtual void ShowCreateChromeAppShortcutsDialog( - Profile* profile, - const extensions::Extension* app) OVERRIDE {} virtual bool IsBookmarkBarVisible() const OVERRIDE; virtual bool IsBookmarkBarAnimating() const OVERRIDE; diff --git a/chrome/test/base/test_launcher_utils.cc b/chrome/test/base/test_launcher_utils.cc index 8517cc9206..f5bf93c19b 100644 --- a/chrome/test/base/test_launcher_utils.cc +++ b/chrome/test/base/test_launcher_utils.cc @@ -47,12 +47,6 @@ void PrepareBrowserCommandLineForTests(CommandLine* command_line) { // Don't install default apps. command_line->AppendSwitch(switches::kDisableDefaultApps); -#if defined(OS_LINUX) - // Don't collect GPU info, load GPU blacklist, or schedule a GPU blacklist - // auto-update on Linux bots for now (http://crbug.com/304833). - command_line->AppendSwitch(switches::kSkipGpuDataLoading); -#endif - #if defined(USE_AURA) // Disable window animations under Ash as the animations effect the // coordinates returned and result in flake. diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc index 138bb1639b..bc655aa35e 100644 --- a/chrome/test/base/testing_profile.cc +++ b/chrome/test/base/testing_profile.cc @@ -47,6 +47,7 @@ #include "chrome/browser/prefs/pref_service_syncable.h" #include "chrome/browser/prerender/prerender_manager.h" #include "chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.h" +#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/storage_partition_descriptor.h" #include "chrome/browser/search_engines/template_url_fetcher_factory.h" #include "chrome/browser/webdata/web_data_service.h" @@ -230,6 +231,7 @@ TestingProfile::TestingProfile( scoped_refptr<ExtensionSpecialStoragePolicy> extension_policy, scoped_ptr<PrefServiceSyncable> prefs, bool incognito, + const std::string& managed_user_id, const TestingFactories& factories) : start_time_(Time::Now()), prefs_(prefs.release()), @@ -237,6 +239,7 @@ TestingProfile::TestingProfile( incognito_(incognito), force_incognito_(false), original_profile_(NULL), + managed_user_id_(managed_user_id), last_session_exited_cleanly_(true), extension_special_storage_policy_(extension_policy), profile_path_(path), @@ -355,6 +358,10 @@ void TestingProfile::FinishInit() { content::Source<Profile>(static_cast<Profile*>(this)), content::NotificationService::NoDetails()); + ProfileManager* profile_manager = g_browser_process->profile_manager(); + if (profile_manager) + profile_manager->InitProfileUserPrefs(this); + if (delegate_) delegate_->OnProfileCreated(this, true, false); } @@ -586,7 +593,7 @@ Profile* TestingProfile::GetOriginalProfile() { } bool TestingProfile::IsManaged() { - return !GetPrefs()->GetString(prefs::kManagedUserId).empty(); + return !managed_user_id_.empty(); } ExtensionService* TestingProfile::GetExtensionService() { @@ -866,6 +873,11 @@ void TestingProfile::Builder::SetIncognito() { incognito_ = true; } +void TestingProfile::Builder::SetManagedUserId( + const std::string& managed_user_id) { + managed_user_id_ = managed_user_id; +} + void TestingProfile::Builder::AddTestingFactory( BrowserContextKeyedServiceFactory* service_factory, BrowserContextKeyedServiceFactory::FactoryFunction callback) { @@ -875,11 +887,13 @@ void TestingProfile::Builder::AddTestingFactory( scoped_ptr<TestingProfile> TestingProfile::Builder::Build() { DCHECK(!build_called_); build_called_ = true; + return scoped_ptr<TestingProfile>(new TestingProfile( path_, delegate_, extension_policy_, pref_service_.Pass(), incognito_, + managed_user_id_, testing_factories_)); } diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h index 4bcd052d69..297cb9529f 100644 --- a/chrome/test/base/testing_profile.h +++ b/chrome/test/base/testing_profile.h @@ -100,6 +100,10 @@ class TestingProfile : public Profile { // Makes the Profile being built an incognito profile. void SetIncognito(); + // Sets the managed user ID (which is empty by default). If it is set to a + // non-empty string, the profile is managed. + void SetManagedUserId(const std::string& managed_user_id); + // Creates the TestingProfile using previously-set settings. scoped_ptr<TestingProfile> Build(); @@ -113,6 +117,7 @@ class TestingProfile : public Profile { base::FilePath path_; Delegate* delegate_; bool incognito_; + std::string managed_user_id_; TestingFactories testing_factories_; DISALLOW_COPY_AND_ASSIGN(Builder); @@ -138,6 +143,7 @@ class TestingProfile : public Profile { scoped_refptr<ExtensionSpecialStoragePolicy> extension_policy, scoped_ptr<PrefServiceSyncable> prefs, bool incognito, + const std::string& managed_user_id, const TestingFactories& factories); virtual ~TestingProfile(); @@ -348,6 +354,8 @@ class TestingProfile : public Profile { scoped_ptr<Profile> incognito_profile_; Profile* original_profile_; + std::string managed_user_id_; + // Did the last session exit cleanly? Default is true. bool last_session_exited_cleanly_; diff --git a/chrome/test/base/testing_profile_manager.cc b/chrome/test/base/testing_profile_manager.cc index 644760cc22..a5ef121bfe 100644 --- a/chrome/test/base/testing_profile_manager.cc +++ b/chrome/test/base/testing_profile_manager.cc @@ -60,6 +60,8 @@ TestingProfile* TestingProfileManager::CreateTestingProfile( TestingProfile::Builder builder; builder.SetPath(profile_path); builder.SetPrefService(prefs.Pass()); + builder.SetManagedUserId(managed_user_id); + TestingProfile* profile = builder.Build().release(); profile_manager_->AddProfile(profile); // Takes ownership. diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py index ff0ba43bde..dd38b4b1d5 100755 --- a/chrome/test/chromedriver/test/run_py_tests.py +++ b/chrome/test/chromedriver/test/run_py_tests.py @@ -96,12 +96,8 @@ _ANDROID_NEGATIVE_FILTER['com.google.android.apps.chrome'] = ( # Android doesn't support switches and extensions. 'ChromeSwitchesCapabilityTest.*', 'ChromeExtensionsCapabilityTest.*', - # https://code.google.com/p/chromedriver/issues/detail?id=459 - 'ChromeDriverTest.testShouldHandleNewWindowLoadingProperly', # https://crbug.com/274650 'ChromeDriverTest.testCloseWindow', - # https://code.google.com/p/chromedriver/issues/detail?id=259 - 'ChromeDriverTest.testSendKeysToElement', # https://code.google.com/p/chromedriver/issues/detail?id=270 'ChromeDriverTest.testPopups', # https://code.google.com/p/chromedriver/issues/detail?id=298 @@ -117,12 +113,7 @@ _ANDROID_NEGATIVE_FILTER['com.google.android.apps.chrome'] = ( ] ) _ANDROID_NEGATIVE_FILTER['com.android.chrome'] = ( - _ANDROID_NEGATIVE_FILTER['com.google.android.apps.chrome'] + [ - # Touch support was added to devtools in Chrome v30. - 'ChromeDriverTest.testTouchDownUpElement', - 'ChromeDriverTest.testTouchMovedElement', - ] -) + _ANDROID_NEGATIVE_FILTER['com.google.android.apps.chrome']) _ANDROID_NEGATIVE_FILTER['com.chrome.beta'] = ( _ANDROID_NEGATIVE_FILTER['com.google.android.apps.chrome']) _ANDROID_NEGATIVE_FILTER['org.chromium.chrome.testshell'] = ( @@ -130,6 +121,7 @@ _ANDROID_NEGATIVE_FILTER['org.chromium.chrome.testshell'] = ( # ChromiumTestShell doesn't support multiple tabs. 'ChromeDriverTest.testGetWindowHandles', 'ChromeDriverTest.testSwitchToWindow', + 'ChromeDriverTest.testShouldHandleNewWindowLoadingProperly', ] ) @@ -166,11 +158,14 @@ class ChromeDriverTest(ChromeDriverBaseTest): def GlobalSetUp(): ChromeDriverTest._http_server = webserver.WebServer( chrome_paths.GetTestData()) + ChromeDriverTest._sync_server = webserver.SyncWebServer() if _ANDROID_PACKAGE: ChromeDriverTest._adb = android_commands.AndroidCommands() - host_port = ChromeDriverTest._http_server._server.server_port + http_host_port = ChromeDriverTest._http_server._server.server_port + sync_host_port = ChromeDriverTest._sync_server._server.server_port forwarder.Forwarder.Map( - [(host_port, host_port)], ChromeDriverTest._adb) + [(http_host_port, http_host_port), (sync_host_port, sync_host_port)], + ChromeDriverTest._adb) @staticmethod def GlobalTearDown(): @@ -544,7 +539,6 @@ class ChromeDriverTest(ChromeDriverBaseTest): def testShouldHandleNewWindowLoadingProperly(self): """Tests that ChromeDriver determines loading correctly for new windows.""" - sync_server = webserver.SyncWebServer() self._http_server.SetDataForPath( '/newwindow', """ @@ -552,7 +546,7 @@ class ChromeDriverTest(ChromeDriverBaseTest): <body> <a href='%s' target='_blank'>new window/tab</a> </body> - </html>""" % sync_server.GetUrl()) + </html>""" % self._sync_server.GetUrl()) self._driver.Load(self._http_server.GetUrl() + '/newwindow') old_windows = self._driver.GetWindowHandles() self._driver.FindElement('tagName', 'a').Click() @@ -562,7 +556,7 @@ class ChromeDriverTest(ChromeDriverBaseTest): self.assertFalse(self._driver.IsLoading()) self._driver.SwitchToWindow(new_window) self.assertTrue(self._driver.IsLoading()) - sync_server.RespondWithContent('<html>new window</html>') + self._sync_server.RespondWithContent('<html>new window</html>') self._driver.ExecuteScript('return 1') # Shouldn't hang. def testPopups(self): diff --git a/chrome/test/functional/ispy/common/ispy_utils.py b/chrome/test/functional/ispy/common/ispy_utils.py index db8f895822..10669513f0 100644 --- a/chrome/test/functional/ispy/common/ispy_utils.py +++ b/chrome/test/functional/ispy/common/ispy_utils.py @@ -13,32 +13,31 @@ import sys import image_tools -def GetTestPath(test_run, test_name, file_name=''): - """Get the path to a test file in the given test run and test. +def GetExpectationPath(expectation, file_name=''): + """Get the path to a test file in the given test run and expectation. Args: - test_run: name of the test run. - test_name: name of the test. + expectation: name of the expectation. file_name: name of the file. Returns: the path as a string relative to the bucket. """ - return 'tests/%s/%s/%s' % (test_run, test_name, file_name) + return 'expectations/%s/%s' % (expectation, file_name) -def GetFailurePath(test_run, test_name, 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. - test_name: name of the test. + expectation: name of the expectation. file_name: name of the file. Returns: the path as a string relative to the bucket. """ - return 'failures/%s/%s/%s' % (test_run, test_name, file_name) + return 'failures/%s/%s/%s' % (test_run, expectation, file_name) class ISpyUtils(object): @@ -87,120 +86,115 @@ class ISpyUtils(object): self.cloud_bucket.UpdateFile(full_path, image_tools.EncodePNG(image)) - def UploadTest(self, test_run, test_name, images): - """Creates and uploads a test to GS from a set of images and name. + def UploadExpectation(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 test. + uploads the mask and first of the images to GS as a expectation. Args: - test_run: the name of the test_run. - test_name: the name of the test. + expectation: name for this expectation, any existing expectation with the + name will be replaced. images: a list of RGB encoded PIL.Images """ mask = image_tools.InflateMask(image_tools.CreateMask(images), 7) self.UploadImage( - GetTestPath(test_run, test_name, 'expected.png'), images[0]) - self.UploadImage(GetTestPath(test_run, test_name, 'mask.png'), mask) + GetExpectationPath(expectation, 'expected.png'), images[0]) + self.UploadImage(GetExpectationPath(expectation, 'mask.png'), mask) - def RunTest(self, test_run, test_name, actual): + def RunTest(self, test_run, expectation, actual): """Runs an image comparison, and uploads discrepancies to GS. Args: test_run: the name of the test_run. - test_name: the name of the test to run. - actual: an RGB-encoded PIL.Image that is the actual result of the - test. + 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 test_name is not found. + cloud_bucket.NotFoundError: if the given expectation is not found. """ - test = self.GetTest(test_run, test_name) - if not image_tools.SameImage(actual, test.expected, mask=test.mask): + expectation_tuple = self.GetExpectation(expectation) + if not image_tools.SameImage( + actual, expectation_tuple.expected, mask=expectation_tuple.mask): self.UploadImage( - GetFailurePath(test_run, test_name, 'actual.png'), actual) + GetFailurePath(test_run, expectation, 'actual.png'), actual) diff, diff_pxls = image_tools.VisualizeImageDifferences( - test.expected, actual, mask=test.mask) - self.UploadImage(GetFailurePath(test_run, test_name, 'diff.png'), diff) + expectation_tuple.expected, actual, mask=expectation_tuple.mask) + self.UploadImage(GetFailurePath(test_run, expectation, 'diff.png'), diff) self.cloud_bucket.UploadFile( - GetFailurePath(test_run, test_name, 'info.txt'), + 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 GetTest(self, test_run, test_name): - """Returns given test from GS. + def GetExpectation(self, expectation): + """Returns the given expectation from GS. Args: - test_run: the name of the test_run. - test_name: the name of the test to get from GS. + expectation: the name of the expectation to get. Returns: - A named tuple: 'Test', containing two images: expected and mask. + A named tuple: 'Expectation', containing two images: expected and mask. Raises: cloud_bucket.NotFoundError: if the test is not found in GS. """ - Test = collections.namedtuple('Test', ['expected', 'mask']) - return Test(self.DownloadImage(GetTestPath(test_run, test_name, - 'expected.png')), - self.DownloadImage(GetTestPath(test_run, test_name, - 'mask.png'))) + Expectation = collections.namedtuple('Expectation', ['expected', 'mask']) + return Expectation(self.DownloadImage(GetExpectationPath(expectation, + 'expected.png')), + self.DownloadImage(GetExpectationPath(expectation, + 'mask.png'))) - def TestExists(self, test_run, test_name): - """Returns whether the given test exists in GS. + def ExpectationExists(self, expectation): + """Returns whether the given expectation exists in GS. Args: - test_run: the name of the test_run. - test_name: the name of the test to look for. + expectation: the name of the expectation to check. Returns: A boolean indicating whether the test exists. """ expected_image_exists = self.cloud_bucket.FileExists( - GetTestPath(test_run, test_name, 'expected.png')) + GetExpectationPath(expectation, 'expected.png')) mask_image_exists = self.cloud_bucket.FileExists( - GetTestPath(test_run, test_name, 'mask.png')) + GetExpectationPath(expectation, 'mask.png')) return expected_image_exists and mask_image_exists - def FailureExists(self, test_run, test_name): - """Returns whether the given run exists in GS. + 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. - test_name: the name of the test that failed. + 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, test_name, 'actual.png')) - test_exists = self.TestExists(test_run, test_name) + GetFailurePath(test_run, expectation, 'actual.png')) + test_exists = self.ExpectationExists(expectation) info_exists = self.cloud_bucket.FileExists( - GetFailurePath(test_run, test_name, 'info.txt')) + GetFailurePath(test_run, expectation, 'info.txt')) return test_exists and actual_image_exists and info_exists - def RemoveTest(self, test_run, test_name): - """Removes a Test from GS, and all associated failures with that test. + def RemoveExpectation(self, expectation): + """Removes an expectation and all associated failures with that test. Args: - test_run: the name of the test_run. - test_name: the name of the test to remove. + expectation: the name of the expectation to remove. """ test_paths = self.cloud_bucket.GetAllPaths( - GetTestPath(test_run, test_name)) - failure_paths = self.cloud_bucket.GetAllPaths( - GetFailurePath(test_run, test_name)) - for path in itertools.chain(failure_paths, test_paths): + GetExpectationPath(expectation)) + for path in test_paths: self.cloud_bucket.RemoveFile(path) - def UploadTestPinkOut(self, test_run, test_name, images, pint_out, rgb): + def UploadExpectationPinkOut(self, expectation, images, pint_out, rgb): """Uploads an ispy-test to GS with the pink_out workaround. Args: - test_name: The name of the test to be uploaded. + 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. @@ -214,29 +208,27 @@ class ISpyUtils(object): mask = image_tools.CreateMask(images) mask = image_tools.InflateMask(image_tools.CreateMask(images), 7) combined_mask = image_tools.AddMasks([mask, pink_out]) - self.UploadImage(GetTestPath(test_run, test_name, 'expected.png'), - images[0]) - self.UploadImage(GetTestPath(test_run, test_name, 'mask.png'), - combined_mask) + self.UploadImage(GetExpectationPath(expectation, 'expected.png'), images[0]) + self.UploadImage(GetExpectationPath(expectation, 'mask.png'), combined_mask) - def RemoveFailure(self, test_run, test_name): + def RemoveFailure(self, test_run, expectation): """Removes a failure from GS. Args: test_run: the name of the test_run. - test_name: the test on which the failure to be removed occured. + expectation: the expectation on which the failure to be removed occured. """ failure_paths = self.cloud_bucket.GetAllPaths( - GetFailurePath(test_run, test_name)) + GetFailurePath(test_run, expectation)) for path in failure_paths: self.cloud_bucket.RemoveFile(path) - def GetFailure(self, test_run, test_name): + 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. - test_name: the name of the test the result corresponds to. + expectation: the name of the expectation the result corresponds to. Returns: A named tuple: Failure containing three images: expected, diff, and @@ -245,16 +237,14 @@ class ISpyUtils(object): Raises: cloud_bucket.NotFoundError: if the result is not found in GS. """ - test_path = GetTestPath(test_run, test_name) - failure_path = GetFailurePath(test_run, test_name) expected = self.DownloadImage( - GetTestPath(test_run, test_name, 'expected.png')) + GetExpectationPath(expectation, 'expected.png')) actual = self.DownloadImage( - GetFailurePath(test_run, test_name, 'actual.png')) + GetFailurePath(test_run, expectation, 'actual.png')) diff = self.DownloadImage( - GetFailurePath(test_run, test_name, 'diff.png')) + GetFailurePath(test_run, expectation, 'diff.png')) info = json.loads(self.cloud_bucket.DownloadFile( - GetFailurePath(test_run, test_name, 'info.txt'))) + GetFailurePath(test_run, expectation, 'info.txt'))) Failure = collections.namedtuple( 'Failure', ['expected', 'diff', 'actual', 'info']) return Failure(expected, diff, actual, info) diff --git a/chrome/test/functional/ispy/common/ispy_utils_unittest.py b/chrome/test/functional/ispy/common/ispy_utils_unittest.py index 14dbb7d4b0..5e66a418a0 100644 --- a/chrome/test/functional/ispy/common/ispy_utils_unittest.py +++ b/chrome/test/functional/ispy/common/ispy_utils_unittest.py @@ -4,7 +4,6 @@ import os from PIL import Image -import sets import sys import unittest @@ -75,41 +74,44 @@ class ISpyUtilsUnitTest(unittest.TestCase): self.assertEquals(self.bucket.datastore['path/to/image.png'], image_tools.EncodePNG(self.black)) - def testUploadTest(self): + def testUploadExpectation(self): self.bucket.Reset() # Upload some tests to the datastore. - self.ispy_utils.UploadTest('test', 'test1', [self.white, self.black]) - self.ispy_utils.UploadTest('test', 'test2', [self.black, self.black]) + self.ispy_utils.UploadExpectation('test', [self.white, self.black]) + self.ispy_utils.UploadExpectation('test1', [self.black, self.black]) # Confirm that the tests were successfully uploaded. - self.assertEquals(self.bucket.datastore['tests/test/test1/expected.png'], - image_tools.EncodePNG(self.white)) - self.assertEquals(self.bucket.datastore['tests/test/test1/mask.png'], - image_tools.EncodePNG(self.white)) - self.assertEquals(self.bucket.datastore['tests/test/test2/expected.png'], - image_tools.EncodePNG(self.black)) - self.assertEquals(self.bucket.datastore['tests/test/test2/mask.png'], - image_tools.EncodePNG(self.black)) + 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)) def testRunTest(self): self.bucket.Reset() - - self.ispy_utils.UploadTest('test', 'test1', [self.red, self.red]) + self.ispy_utils.UploadExpectation('test1', [self.red, self.red]) self.ispy_utils.RunTest('test', 'test1', self.black) - self.assertEquals( - self.bucket.datastore['failures/test/test1/actual.png'], + self.assertEquals(self.bucket.datastore[ + ispy_utils.GetFailurePath('test', 'test1', 'actual.png')], image_tools.EncodePNG(self.black)) self.ispy_utils.RunTest('test', 'test1', self.red) - self.assertTrue( - self.bucket.datastore.has_key('failures/test/test1/actual.png')) + self.assertTrue(self.bucket.datastore.has_key( + ispy_utils.GetFailurePath('test', 'test1', 'actual.png'))) - def testGetTest(self): + def testGetExpectation(self): self.bucket.Reset() # Upload some tests to the datastore - self.ispy_utils.UploadTest('test', 'test1', [self.white, self.black]) - self.ispy_utils.UploadTest('test', 'test2', [self.red, self.white]) - test1 = self.ispy_utils.GetTest('test', 'test1') - test2 = self.ispy_utils.GetTest('test', 'test2') - # Check that GetTest gets the appropriate tests. + self.ispy_utils.UploadExpectation('test1', [self.white, self.black]) + self.ispy_utils.UploadExpectation('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), @@ -118,55 +120,54 @@ class ISpyUtilsUnitTest(unittest.TestCase): image_tools.EncodePNG(self.red)) self.assertEquals(image_tools.EncodePNG(test2.mask), image_tools.EncodePNG(self.white)) - # Check that GetTest throws an error for a nonexistant test. + # Check that GetExpectation throws an error for a nonexistant test. self.assertRaises( - cloud_bucket.FileNotFoundError, self.ispy_utils.GetTest, 'test', - 'test3') + cloud_bucket.FileNotFoundError, self.ispy_utils.GetExpectation, 'test3') - def testTestExists(self): + def testExpectationExists(self): self.bucket.Reset() - self.ispy_utils.UploadTest('test', 'test1', [self.white, self.black]) - self.ispy_utils.UploadTest('test', 'test2', [self.white, self.black]) - self.assertTrue(self.ispy_utils.TestExists('test', 'test1')) - self.assertTrue(self.ispy_utils.TestExists('test', 'test2')) - self.assertFalse(self.ispy_utils.TestExists('test', 'test3')) + self.ispy_utils.UploadExpectation('test1', [self.white, self.black]) + self.ispy_utils.UploadExpectation('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.UploadTest('test', 'test1', [self.white, self.white]) + self.ispy_utils.UploadExpectation('test1', [self.white, self.white]) self.ispy_utils.RunTest('test', 'test1', self.black) self.ispy_utils.RunTest('test', 'test1', self.white) self.assertTrue(self.ispy_utils.FailureExists('test', 'test1')) self.assertFalse(self.ispy_utils.FailureExists('test', 'test2')) - def testRemoveTest(self): + def testRemoveExpectation(self): self.bucket.Reset() - self.ispy_utils.UploadTest('test', 'test1', [self.white, self.white]) - self.ispy_utils.UploadTest('test', 'test2', [self.white, self.white]) - self.assertTrue(self.ispy_utils.TestExists('test', 'test1')) - self.assertTrue(self.ispy_utils.TestExists('test', 'test2')) - self.ispy_utils.RemoveTest('test', 'test1') - self.assertFalse(self.ispy_utils.TestExists('test', 'test1')) - self.assertTrue(self.ispy_utils.TestExists('test', 'test2')) - self.ispy_utils.RemoveTest('test', 'test2') - self.assertFalse(self.ispy_utils.TestExists('test', 'test1')) - self.assertFalse(self.ispy_utils.TestExists('test', 'test2')) + self.ispy_utils.UploadExpectation('test1', [self.white, self.white]) + self.ispy_utils.UploadExpectation('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.UploadTest('test', 'test1', [self.white, self.white]) - self.ispy_utils.UploadTest('test', 'test2', [self.white, self.white]) + self.ispy_utils.UploadExpectation('test1', [self.white, self.white]) + self.ispy_utils.UploadExpectation('test2', [self.white, self.white]) self.ispy_utils.RunTest('test', 'test1', self.black) self.ispy_utils.RemoveFailure('test', 'test1') self.assertFalse(self.ispy_utils.FailureExists('test', 'test1')) - self.assertTrue(self.ispy_utils.TestExists('test', 'test1')) + self.assertTrue(self.ispy_utils.ExpectationExists('test1')) self.assertFalse(self.ispy_utils.FailureExists('test', 'test2')) - self.assertTrue(self.ispy_utils.TestExists('test', 'test2')) + self.assertTrue(self.ispy_utils.ExpectationExists('test2')) def testGetFailure(self): self.bucket.Reset() # Upload a result - self.ispy_utils.UploadTest('test', 'test1', [self.red, self.red]) + self.ispy_utils.UploadExpectation('test1', [self.red, self.red]) self.ispy_utils.RunTest('test', 'test1', self.black) res = self.ispy_utils.GetFailure('test', 'test1') # Check that the function correctly got the result. @@ -183,22 +184,16 @@ class ISpyUtilsUnitTest(unittest.TestCase): def testGetAllPaths(self): self.bucket.Reset() # Upload some tests. - self.ispy_utils.UploadTest('test', 'test1', [self.white, self.black]) - self.ispy_utils.UploadTest('test', 'test2', [self.red, self.white]) + self.ispy_utils.UploadExpectation('test1', [self.white, self.black]) # Check that the function gets all urls matching the prefix. self.assertEquals( - sets.Set(self.ispy_utils.GetAllPaths('tests/test/test')), - sets.Set(['tests/test/test1/expected.png', - 'tests/test/test1/mask.png', - 'tests/test/test2/expected.png', - 'tests/test/test2/mask.png'])) - self.assertEquals( - sets.Set(self.ispy_utils.GetAllPaths('tests/test/test1')), - sets.Set(['tests/test/test1/expected.png', - 'tests/test/test1/mask.png'])) + set(self.ispy_utils.GetAllPaths( + ispy_utils.GetExpectationPath('test1'))), + set([ispy_utils.GetExpectationPath('test1', 'expected.png'), + ispy_utils.GetExpectationPath('test1', 'mask.png')])) self.assertEquals( - sets.Set(self.ispy_utils.GetAllPaths('tests/test/test3')), - sets.Set()) + set(self.ispy_utils.GetAllPaths( + ispy_utils.GetExpectationPath('test3'))), set()) if __name__ == '__main__': diff --git a/chrome/test/functional/ispy/server/debug_view_handler.py b/chrome/test/functional/ispy/server/debug_view_handler.py index 6d74e78776..96bfe3cff1 100644 --- a/chrome/test/functional/ispy/server/debug_view_handler.py +++ b/chrome/test/functional/ispy/server/debug_view_handler.py @@ -26,12 +26,12 @@ class DebugViewHandler(webapp2.RequestHandler): GET Parameters: test_run: The test run. - test_name: The test name. + expectation: The expectation name. """ test_run = self.request.get('test_run') - test_name = self.request.get('test_name') - expected_path = ispy_utils.GetTestPath(test_run, test_name, 'expected.png') - actual_path = ispy_utils.GetFailurePath(test_run, test_name, 'actual.png') + 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): @@ -40,6 +40,6 @@ class DebugViewHandler(webapp2.RequestHandler): data['expected'] = _ImagePath(expected_path) data['actual'] = _ImagePath(actual_path) data['test_run'] = test_run - data['test_name'] = test_name + data['expectation'] = expectation template = JINJA.get_template('debug_view.html') self.response.write(template.render(data)) diff --git a/chrome/test/functional/ispy/server/main_view_handler.py b/chrome/test/functional/ispy/server/main_view_handler.py index 8254033754..4b3ec103f8 100644 --- a/chrome/test/functional/ispy/server/main_view_handler.py +++ b/chrome/test/functional/ispy/server/main_view_handler.py @@ -49,8 +49,8 @@ class MainViewHandler(webapp2.RequestHandler): """ template = JINJA.get_template('list_view.html') data = {} - test_runs = set([re.match(r'^tests/([^/]+)/.+$', path).groups()[0] - for path in ispy.GetAllPaths('tests/')]) + 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)) @@ -67,13 +67,12 @@ class MainViewHandler(webapp2.RequestHandler): """ paths = set([path for path in ispy.GetAllPaths('failures/' + test_run) if path.endswith('actual.png')]) - rows = [self._CreateRow(test_run, path, ispy) - for path in paths] + rows = [self._CreateRow(test_run, path, ispy) for path in paths] if rows: # Function that sorts by the different_pixels field in the failure-info. - def _Sorter(x, y): - return cmp(y['info']['fraction_different'], - x['info']['fraction_different']) + 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), @@ -98,16 +97,15 @@ class MainViewHandler(webapp2.RequestHandler): in the main_view html template. """ res = {} - pattern = r'^failures/%s/([^/]+)/.+$' % test_run - res['test_name'] = re.match( - pattern, path).groups()[0] + 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['test_name'], + ispy_utils.GetFailurePath(res['test_run'], res['expectation'], 'info.txt'))) - expected = ispy_utils.GetTestPath(test_run, res['test_name'], - 'expected.png') - diff = ispy_utils.GetFailurePath(test_run, res['test_name'], 'diff.png') + 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 diff --git a/chrome/test/functional/ispy/server/update_mask_handler.py b/chrome/test/functional/ispy/server/update_mask_handler.py index 3c0fb6a80d..4a2a2dabb7 100644 --- a/chrome/test/functional/ispy/server/update_mask_handler.py +++ b/chrome/test/functional/ispy/server/update_mask_handler.py @@ -29,31 +29,31 @@ class UpdateMaskHandler(webapp2.RequestHandler): to the device list view. """ test_run = self.request.get('test_run') - test_name = self.request.get('test_name') + expectation = self.request.get('expectation') # Short-circuit if a parameter is missing. - if not (test_run and test_name): + if not (test_run and expectation): self.response.header['Content-Type'] = 'json/application' self.response.write(json.dumps( - {'error': 'test_run, and test_name, must be ' + {'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, test_name): + if not self.ispy.FailureExists(test_run, expectation): self.response.header['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, test_name) + failure = self.ispy.GetFailure(test_run, expectation) # Upload the new mask in place of the original. self.ispy.UpdateImage( - ispy_utils.GetTestPath(test_run, test_name, 'mask.png'), + ispy_utils.GetExpectationPath(expectation, 'mask.png'), image_tools.ConvertDiffToMask(failure.diff)) - # Now that there is no div for the two images, remove the failure. - self.ispy.RemoveFailure(test_run, test_name) - # Redirect back to the sites list for the device. + # 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/debug_view.html b/chrome/test/functional/ispy/server/views/debug_view.html index f0a67ac01f..8371280d7b 100644 --- a/chrome/test/functional/ispy/server/views/debug_view.html +++ b/chrome/test/functional/ispy/server/views/debug_view.html @@ -1,6 +1,6 @@ <html> <head> - <title>Debug {{ test_name }}</title> + <title>Debug {{ expectation }}</title> <script language="javascript"> var current = 0; var toggle_interval = null; @@ -38,7 +38,7 @@ <br> <form action="/update_mask" method="post" onsubmit="return confirmSubmit();"> <input type="hidden" name="test_run" value="{{ test_run }}"/> - <input type="hidden" name="test_name" value="{{ test_name }}"/> + <input type="hidden" name="expectation" value="{{ expectation }}"/> <input type="submit" value="Ignore similar diffs in the future"/> </form> <br> diff --git a/chrome/test/functional/ispy/server/views/diff_view.html b/chrome/test/functional/ispy/server/views/diff_view.html deleted file mode 100644 index 6cf10835da..0000000000 --- a/chrome/test/functional/ispy/server/views/diff_view.html +++ /dev/null @@ -1,62 +0,0 @@ -<!DOCTYPE html> -{% autoescape true %} -<html> - <head> - <style> - #container { - display: table; - background-color:#A8A8A8; - border:2px solid black; - } - #row { - display: table-row; - } - #left, #right, #middle { - display: table-cell; - padding-right: 25px; - padding-left: 25px; - } - #left p, #right p, #middle p { - margin: 1px 1px; - } - #title { - display: block; - text-align: center; - } - #info-box { - background-color:#A8A8A8; - border:2px solid black; - display: block; - width: 400px; - } - .image { - width: 300px; - } - </style> - </head> - <body> - <span id="info-box"> Device: {{ device }}<br> - Site: {{ site }}<br> - Device_ID: {{ device_id }}</span> - <form action="/update_mask?{{ encoded_parameters }}" method="post"> - <input type="submit" value="Ignore Diff"/> - </form> - <div id="container"> - <div id="row"> - <div id="left"> - <span id="title">Expected</span><br> - <img class="image" src="{{ expected }}"> - </div> - <div id="center"> - <span id="title">Diff</span><br> - <img class="image" src="{{ diff }}"> - </div> - <div id="right"> - <span id="title">Actual</span><br> - <img class="image" src="{{ actual }}"> - </div> - </div> - </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 index 4cfee440d2..4bc95ad7a8 100644 --- a/chrome/test/functional/ispy/server/views/main_view.html +++ b/chrome/test/functional/ispy/server/views/main_view.html @@ -29,7 +29,7 @@ {% for comp in comparisons %} <div class="row"> <div class="cell"> - Diff<br> + Diff ({{ "%.1f"|format(comp['percent_different']) }}%)<br> <a class="imagelink" href="{{ comp['diff'] }}"> <img class="image" src={{ comp['diff'] }}> </a> @@ -49,8 +49,8 @@ <div class="cell"> <br> <div class="info"> - {{ comp['test_name'] }}<br> - <a href='/debug_view?test_run={{ comp['test_run'] }}&test_name={{ comp['test_name'] }}'>Debug View</a> + {{ comp['expectation'] }}<br> + <a href='/debug_view?test_run={{ comp['test_run'] }}&expectation={{ comp['expectation'] }}'>Debug View</a> </div> </div> </div> diff --git a/chrome/test/gpu/gpu_feature_browsertest.cc b/chrome/test/gpu/gpu_feature_browsertest.cc index e3eecb5eb5..2192831a7c 100644 --- a/chrome/test/gpu/gpu_feature_browsertest.cc +++ b/chrome/test/gpu/gpu_feature_browsertest.cc @@ -369,8 +369,7 @@ IN_PROC_BROWSER_TEST_F(GpuFeatureTest, Canvas2DAllowed) { } expected_state = ENABLED; #if defined(OS_LINUX) && !defined(OS_CHROMEOS) // Blacklist rule #24 disables accelerated_2d_canvas on Linux. - // TODO(gab): Enable GPU control lists on Linux. - // expected_state = BLACKLISTED; + expected_state = BLACKLISTED; #elif defined(OS_WIN) // Blacklist rule #67 disables accelerated_2d_canvas on XP. if (base::win::GetVersion() < base::win::VERSION_VISTA) diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc index 921436804a..66524df496 100644 --- a/chrome/test/ppapi/ppapi_browsertest.cc +++ b/chrome/test/ppapi/ppapi_browsertest.cc @@ -315,7 +315,13 @@ TEST_PPAPI_OUT_OF_PROCESS(ImageData) TEST_PPAPI_NACL(ImageData) TEST_PPAPI_IN_PROCESS(BrowserFont) -TEST_PPAPI_OUT_OF_PROCESS(BrowserFont) +// crbug.com/308949 +#if defined(OS_WIN) +#define MAYBE_OUT_BrowserFont DISABLED_BrowserFont +#else +#define MAYBE_OUT_BrowserFont BrowserFont +#endif +TEST_PPAPI_OUT_OF_PROCESS(MAYBE_OUT_BrowserFont) TEST_PPAPI_IN_PROCESS(Buffer) TEST_PPAPI_OUT_OF_PROCESS(Buffer) @@ -561,7 +567,8 @@ IN_PROC_BROWSER_TEST_F(PPAPINaClNewlibTest, URLLoader) { LIST_TEST(URLLoader_PrefetchBufferThreshold) ); } -IN_PROC_BROWSER_TEST_F(PPAPINaClGLibcTest, MAYBE_GLIBC(URLLoader)) { +// http://crbug.com/308906 +IN_PROC_BROWSER_TEST_F(PPAPINaClGLibcTest, DISABLED_URLLoader) { RunTestViaHTTP( LIST_TEST(URLLoader_BasicGET) LIST_TEST(URLLoader_BasicPOST) @@ -676,6 +683,10 @@ TEST_PPAPI_IN_PROCESS(Var) TEST_PPAPI_OUT_OF_PROCESS(Var) TEST_PPAPI_NACL(Var) +TEST_PPAPI_IN_PROCESS(VarResource) +TEST_PPAPI_OUT_OF_PROCESS(VarResource) +TEST_PPAPI_NACL(VarResource) + // Flaky on mac, http://crbug.com/121107 #if defined(OS_MACOSX) #define MAYBE_VarDeprecated DISABLED_VarDeprecated @@ -822,7 +833,8 @@ IN_PROC_BROWSER_TEST_F(PPAPINaClNewlibTest, FileIO) { LIST_TEST(FileIO_Mmap) ); } -IN_PROC_BROWSER_TEST_F(PPAPINaClGLibcTest, MAYBE_GLIBC(FileIO)) { +// http://crbug.com/308905 +IN_PROC_BROWSER_TEST_F(PPAPINaClGLibcTest, DISABLED_FileIO) { RunTestViaHTTP( LIST_TEST(FileIO_Open) LIST_TEST(FileIO_AbortCalls) @@ -903,7 +915,8 @@ IN_PROC_BROWSER_TEST_F(PPAPINaClNewlibTest, FileRef) { LIST_TEST(DISABLED_FileRef_ReadDirectoryEntries) ); } -IN_PROC_BROWSER_TEST_F(PPAPINaClGLibcTest, MAYBE_GLIBC(FileRef)) { +// http://crbug.com/308908 +IN_PROC_BROWSER_TEST_F(PPAPINaClGLibcTest, DISABLED_FileRef) { RunTestViaHTTP( LIST_TEST(FileRef_Create) LIST_TEST(FileRef_GetFileSystemType) diff --git a/chrome/test/remoting/page_load_notification_observer.cc b/chrome/test/remoting/page_load_notification_observer.cc new file mode 100644 index 0000000000..4898680bc8 --- /dev/null +++ b/chrome/test/remoting/page_load_notification_observer.cc @@ -0,0 +1,29 @@ +// 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. + +#include "chrome/test/remoting/page_load_notification_observer.h" + +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/notification_types.h" +#include "content/public/browser/web_contents.h" + +namespace remoting { + +PageLoadNotificationObserver::PageLoadNotificationObserver(const GURL& target) + : WindowedNotificationObserver( + content::NOTIFICATION_LOAD_STOP, + base::Bind(&PageLoadNotificationObserver::IsTargetLoaded, + base::Unretained(this))), + target_(target) { +} + +PageLoadNotificationObserver::~PageLoadNotificationObserver() {} + +bool PageLoadNotificationObserver::IsTargetLoaded() { + content::NavigationController* controller = + content::Source<content::NavigationController>(source()).ptr(); + return controller->GetWebContents()->GetURL() == target_; +} + +} // namespace remoting diff --git a/chrome/test/remoting/page_load_notification_observer.h b/chrome/test/remoting/page_load_notification_observer.h new file mode 100644 index 0000000000..42e3a6cbdd --- /dev/null +++ b/chrome/test/remoting/page_load_notification_observer.h @@ -0,0 +1,34 @@ +// 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. + +#ifndef CHROME_TEST_REMOTING_PAGE_LOAD_NOTIFICATION_OBSERVER_H_ +#define CHROME_TEST_REMOTING_PAGE_LOAD_NOTIFICATION_OBSERVER_H_ + +#include "content/public/test/test_utils.h" +#include "url/gurl.h" + +namespace remoting { + +// A PageLoadNotificationObserver allows code to wait until a give URL is loaded +// in any tab in any browser window, i.e. NotificationService::AllSources(). +// This simple pattern is not easy to implement using +// WindowedNotificationObserver because we need to bind the observer object +// in the callback but we also need to provide the callback when constructing +// the observer object. +class PageLoadNotificationObserver + : public content::WindowedNotificationObserver { + public: + explicit PageLoadNotificationObserver(const GURL& target); + + virtual ~PageLoadNotificationObserver(); + + private: + bool IsTargetLoaded(); + GURL target_; + DISALLOW_COPY_AND_ASSIGN(PageLoadNotificationObserver); +}; + +} // namespace remoting + +#endif // CHROME_TEST_REMOTING_PAGE_LOAD_NOTIFICATION_OBSERVER_H_ diff --git a/chrome/test/remoting/remote_desktop_browsertest.cc b/chrome/test/remoting/remote_desktop_browsertest.cc index f56bacc0ad..156d532f2a 100644 --- a/chrome/test/remoting/remote_desktop_browsertest.cc +++ b/chrome/test/remoting/remote_desktop_browsertest.cc @@ -6,35 +6,41 @@ #include "base/command_line.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/unpacked_installer.h" +#include "chrome/browser/ui/extensions/application_launch.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_file_util.h" #include "chrome/test/remoting/key_code_conv.h" +#include "chrome/test/remoting/page_load_notification_observer.h" #include "chrome/test/remoting/waiter.h" #include "content/public/browser/native_web_keyboard_event.h" #include "content/public/browser/render_view_host.h" #include "content/public/test/test_utils.h" - -using extensions::Extension; +#include "ui/base/window_open_disposition.h" namespace remoting { -RemoteDesktopBrowserTest::RemoteDesktopBrowserTest() {} +RemoteDesktopBrowserTest::RemoteDesktopBrowserTest() + : extension_(NULL) { +} RemoteDesktopBrowserTest::~RemoteDesktopBrowserTest() {} void RemoteDesktopBrowserTest::SetUp() { ParseCommandLine(); - ExtensionBrowserTest::SetUp(); + PlatformAppBrowserTest::SetUp(); } void RemoteDesktopBrowserTest::SetUpOnMainThread() { - ExtensionBrowserTest::SetUpOnMainThread(); + PlatformAppBrowserTest::SetUpOnMainThread(); - // Initializing to browser() before RunTestOnMainThread() and after - // |InProcessBrowserTest::browser_| is initialized in - // InProcessBrowserTest::RunTestOnMainThreadLoop() - active_browser_ = browser(); + // Pushing the initial WebContents instance onto the stack before + // RunTestOnMainThread() and after |InProcessBrowserTest::browser_| + // is initialized in InProcessBrowserTest::RunTestOnMainThreadLoop() + web_contents_stack_.push_back( + browser()->tab_strip_model()->GetActiveWebContents()); } // Change behavior of the default host resolver to avoid DNS lookup errors, @@ -50,8 +56,8 @@ void RemoteDesktopBrowserTest::TearDownInProcessBrowserTestFixture() { } void RemoteDesktopBrowserTest::VerifyInternetAccess() { - GURL google_url("http://www.google.com"); - NavigateToURLAndWaitForPageLoad(google_url); + ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( + browser(), GURL("http://www.google.com"), 1); EXPECT_EQ(GetCurrentURL().host(), "www.google.com"); } @@ -76,17 +82,37 @@ bool RemoteDesktopBrowserTest::HtmlElementVisible(const std::string& name) { "isElementVisible(\"" + name + "\")"); } -void RemoteDesktopBrowserTest::InstallChromotingApp() { +void RemoteDesktopBrowserTest::InstallChromotingAppCrx() { + ASSERT_TRUE(!is_unpacked()); + base::FilePath install_dir(WebAppCrxPath()); scoped_refptr<const Extension> extension(InstallExtensionWithUIAutoConfirm( - install_dir, 1, active_browser_)); + install_dir, 1, browser())); EXPECT_FALSE(extension.get() == NULL); + + extension_ = extension.get(); +} + +void RemoteDesktopBrowserTest::InstallChromotingAppUnpacked() { + ASSERT_TRUE(is_unpacked()); + + scoped_refptr<extensions::UnpackedInstaller> installer = + extensions::UnpackedInstaller::Create(extension_service()); + installer->set_prompt_for_plugins(false); + + content::WindowedNotificationObserver observer( + chrome::NOTIFICATION_EXTENSION_LOADED, + content::NotificationService::AllSources()); + + installer->Load(webapp_unpacked_); + + observer.Wait(); } void RemoteDesktopBrowserTest::UninstallChromotingApp() { UninstallExtension(ChromotingID()); - chromoting_id_.clear(); + extension_ = NULL; } void RemoteDesktopBrowserTest::VerifyChromotingLoaded(bool expected) { @@ -107,10 +133,15 @@ void RemoteDesktopBrowserTest::VerifyChromotingLoaded(bool expected) { } if (installed) { - chromoting_id_ = extension->id(); + if (extension_) + EXPECT_EQ(extension, extension_); + else + extension_ = extension.get(); - EXPECT_EQ(extension->GetType(), - extensions::Manifest::TYPE_LEGACY_PACKAGED_APP); + // Either a V1 (TYPE_LEGACY_PACKAGED_APP) or a V2 (TYPE_PLATFORM_APP ) app. + extensions::Manifest::Type type = extension->GetType(); + EXPECT_TRUE(type == extensions::Manifest::TYPE_PLATFORM_APP || + type == extensions::Manifest::TYPE_LEGACY_PACKAGED_APP); EXPECT_TRUE(extension->ShouldDisplayInAppLauncher()); } @@ -119,36 +150,62 @@ void RemoteDesktopBrowserTest::VerifyChromotingLoaded(bool expected) { } void RemoteDesktopBrowserTest::LaunchChromotingApp() { - ASSERT_FALSE(ChromotingID().empty()); + ASSERT_TRUE(extension_); + + GURL chromoting_main = Chromoting_Main_URL(); + // We cannot simply wait for any page load because the first page + // loaded could be the generated background page. We need to wait + // till the chromoting main page is loaded. + PageLoadNotificationObserver observer(chromoting_main); + + OpenApplication(AppLaunchParams( + browser()->profile(), + extension_, + is_platform_app() ? extension_misc::LAUNCH_NONE : + extension_misc::LAUNCH_TAB, + is_platform_app() ? NEW_WINDOW : CURRENT_TAB)); + + observer.Wait(); - const GURL chromoting_main = Chromoting_Main_URL(); - NavigateToURLAndWaitForPageLoad(chromoting_main); - EXPECT_EQ(GetCurrentURL(), chromoting_main); + // The active WebContents instance should be the source of the LOAD_STOP + // notification. + content::NavigationController* controller = + content::Source<content::NavigationController>(observer.source()).ptr(); + + content::WebContents* web_contents = controller->GetWebContents(); + if (web_contents != active_web_contents()) + web_contents_stack_.push_back(web_contents); + + if (is_platform_app()) { + EXPECT_EQ(GetFirstShellWindowWebContents(), active_web_contents()); + } else { + // For apps v1 only, the DOMOperationObserver is not ready at the LOAD_STOP + // event. A half second wait is necessary for the subsequent javascript + // injection to work. + // TODO(weitaosu): Find out whether there is a more appropriate notification + // to wait for so we can get rid of this wait. + ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromMilliseconds(500)).Wait()); + } + + EXPECT_EQ(Chromoting_Main_URL(), GetCurrentURL()); } void RemoteDesktopBrowserTest::Authorize() { // The chromoting extension should be installed. - ASSERT_FALSE(ChromotingID().empty()); + ASSERT_TRUE(extension_); // The chromoting main page should be loaded in the current tab // and isAuthenticated() should be false (auth dialog visible). ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL()); - ASSERT_FALSE(ExecuteScriptAndExtractBool( - "remoting.oauth2.isAuthenticated()")); - - // The first observer monitors the creation of the new window for - // the Google loging page. - content::WindowedNotificationObserver observer( - chrome::NOTIFICATION_BROWSER_OPENED, - content::NotificationService::AllSources()); + ASSERT_FALSE(IsAuthenticated()); // The second observer monitors the loading of the Google login page. // Unfortunately we cannot specify a source in this observer because // we can't get a handle of the new window until the first observer // has finished waiting. But we will assert that the source of the // load stop event is indeed the newly created browser window. - content::WindowedNotificationObserver observer2( + content::WindowedNotificationObserver observer( content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); @@ -156,19 +213,10 @@ void RemoteDesktopBrowserTest::Authorize() { observer.Wait(); - const content::Source<Browser>* source = - static_cast<const content::Source<Browser>*>(&observer.source()); - active_browser_ = source->ptr(); - - observer2.Wait(); - - const content::Source<content::NavigationController>* source2 = - static_cast<const content::Source<content::NavigationController>*>( - &observer2.source()); - content::NavigationController* controller = source2->ptr(); + content::NavigationController* controller = + content::Source<content::NavigationController>(observer.source()).ptr(); - EXPECT_EQ(controller->GetWebContents(), - active_browser_->tab_strip_model()->GetActiveWebContents()); + web_contents_stack_.push_back(controller->GetWebContents()); // Verify the active tab is at the "Google Accounts" login page. EXPECT_EQ("accounts.google.com", GetCurrentURL().host()); @@ -178,7 +226,7 @@ void RemoteDesktopBrowserTest::Authorize() { void RemoteDesktopBrowserTest::Authenticate() { // The chromoting extension should be installed. - ASSERT_FALSE(ChromotingID().empty()); + ASSERT_TRUE(extension_); // The active tab should have the "Google Accounts" login page loaded. ASSERT_EQ("accounts.google.com", GetCurrentURL().host()); @@ -191,59 +239,79 @@ void RemoteDesktopBrowserTest::Authenticate() { "document.getElementById(\"Passwd\").value = \"" + password_ +"\";" + "document.forms[\"gaia_loginform\"].submit();"); - EXPECT_EQ(GetCurrentURL().host(), "accounts.google.com"); - // TODO(weitaosu): Is there a better way to verify we are on the // "Request for Permission" page? - EXPECT_TRUE(HtmlElementExists("submit_approve_access")); + // V2 app won't ask for approval here because the chromoting test account + // has already been granted permissions. + if (!is_platform_app()) { + EXPECT_EQ(GetCurrentURL().host(), "accounts.google.com"); + EXPECT_TRUE(HtmlElementExists("submit_approve_access")); + } } void RemoteDesktopBrowserTest::Approve() { // The chromoting extension should be installed. - ASSERT_FALSE(ChromotingID().empty()); - - // The active tab should have the chromoting app loaded. - ASSERT_EQ("accounts.google.com", GetCurrentURL().host()); - - // Is there a better way to verify we are on the "Request for Permission" - // page? - ASSERT_TRUE(HtmlElementExists("submit_approve_access")); - - const GURL chromoting_main = Chromoting_Main_URL(); - - // |active_browser_| is still set to the login window but the observer - // should be set up to observe the chromoting window because that is - // where we'll receive the message from the login window and reload the - // chromoting app. - content::WindowedNotificationObserver observer( - content::NOTIFICATION_LOAD_STOP, + ASSERT_TRUE(extension_); + + if (is_platform_app()) { + // Popping the login window off the stack to return to the chromoting + // window. + web_contents_stack_.pop_back(); + + // There is nothing for the V2 app to approve because the chromoting test + // account has already been granted permissions. + // TODO(weitaosu): Revoke the permission at the beginning of the test so + // that we can test first-time experience here. + ConditionalTimeoutWaiter waiter( + base::TimeDelta::FromSeconds(3), + base::TimeDelta::FromSeconds(1), base::Bind( - &RemoteDesktopBrowserTest::IsAuthenticated, browser())); - - ExecuteScript( - "lso.approveButtonAction();" - "document.forms[\"connect-approve\"].submit();"); - - observer.Wait(); - - // Resetting |active_browser_| to browser() now that the Google login - // window is closed and the focus has returned to the chromoting window. - active_browser_ = browser(); + &RemoteDesktopBrowserTest::IsAuthenticatedInWindow, + active_web_contents())); + + ASSERT_TRUE(waiter.Wait()); + } else { + ASSERT_EQ("accounts.google.com", GetCurrentURL().host()); + + // Is there a better way to verify we are on the "Request for Permission" + // page? + ASSERT_TRUE(HtmlElementExists("submit_approve_access")); + + const GURL chromoting_main = Chromoting_Main_URL(); + + // active_web_contents() is still the login window but the observer + // should be set up to observe the chromoting window because that is + // where we'll receive the message from the login window and reload the + // chromoting app. + content::WindowedNotificationObserver observer( + content::NOTIFICATION_LOAD_STOP, + base::Bind( + &RemoteDesktopBrowserTest::IsAuthenticatedInWindow, + browser()->tab_strip_model()->GetActiveWebContents())); + + ExecuteScript( + "lso.approveButtonAction();" + "document.forms[\"connect-approve\"].submit();"); + + observer.Wait(); + + // Popping the login window off the stack to return to the chromoting + // window. + web_contents_stack_.pop_back(); + } - ASSERT_TRUE(GetCurrentURL() == chromoting_main); + ASSERT_TRUE(GetCurrentURL() == Chromoting_Main_URL()); - EXPECT_TRUE(ExecuteScriptAndExtractBool( - "remoting.oauth2.isAuthenticated()")); + EXPECT_TRUE(IsAuthenticated()); } void RemoteDesktopBrowserTest::StartMe2Me() { // The chromoting extension should be installed. - ASSERT_FALSE(ChromotingID().empty()); + ASSERT_TRUE(extension_); // The active tab should have the chromoting app loaded. ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL()); - EXPECT_TRUE(ExecuteScriptAndExtractBool( - "remoting.oauth2.isAuthenticated()")); + EXPECT_TRUE(IsAuthenticated()); // The Me2Me host list should be hidden. ASSERT_FALSE(HtmlElementVisible("me2me-content")); @@ -272,7 +340,7 @@ void RemoteDesktopBrowserTest::StartMe2Me() { void RemoteDesktopBrowserTest::DisconnectMe2Me() { // The chromoting extension should be installed. - ASSERT_FALSE(ChromotingID().empty()); + ASSERT_TRUE(extension_); // The active tab should have the chromoting app loaded. ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL()); @@ -307,7 +375,7 @@ void RemoteDesktopBrowserTest::SimulateKeyPressWithCode( bool alt, bool command) { content::SimulateKeyPressWithCode( - active_browser_->tab_strip_model()->GetActiveWebContents(), + active_web_contents(), keyCode, code, control, @@ -336,6 +404,8 @@ void RemoteDesktopBrowserTest::SimulateMouseLeftClickAt(int x, int y) { void RemoteDesktopBrowserTest::SimulateMouseClickAt( int modifiers, WebKit::WebMouseEvent::Button button, int x, int y) { + // TODO(weitaosu): The coordinate translation doesn't work correctly for + // apps v2. ExecuteScript( "var clientPluginElement = " "document.getElementById('session-client-plugin');" @@ -359,10 +429,12 @@ void RemoteDesktopBrowserTest::SimulateMouseClickAt( } void RemoteDesktopBrowserTest::Install() { - // TODO(weitaosu): add support for unpacked extension (the v2 app needs it). if (!NoInstall()) { VerifyChromotingLoaded(false); - InstallChromotingApp(); + if (is_unpacked()) + InstallChromotingAppUnpacked(); + else + InstallChromotingAppCrx(); } VerifyChromotingLoaded(true); @@ -372,9 +444,8 @@ void RemoteDesktopBrowserTest::Cleanup() { // TODO(weitaosu): Remove this hack by blocking on the appropriate // notification. // The browser may still be loading images embedded in the webapp. If we - // uinstall it now those load will fail. Navigating away to avoid the load - // failures. - ui_test_utils::NavigateToURL(active_browser_, GURL("about:blank")); + // uinstall it now those load will fail. + ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromSeconds(2)).Wait()); if (!NoCleanup()) { UninstallChromotingApp(); @@ -477,13 +548,14 @@ void RemoteDesktopBrowserTest::ParseCommandLine() { if (!no_install_) { webapp_crx_ = command_line->GetSwitchValuePath(kWebAppCrx); - ASSERT_FALSE(webapp_crx_.empty()); + webapp_unpacked_ = command_line->GetSwitchValuePath(kWebAppUnpacked); + // One and only one of these two arguments should be provided. + ASSERT_NE(webapp_crx_.empty(), webapp_unpacked_.empty()); } } void RemoteDesktopBrowserTest::ExecuteScript(const std::string& script) { - ASSERT_TRUE(content::ExecuteScript( - active_browser_->tab_strip_model()->GetActiveWebContents(), script)); + ASSERT_TRUE(content::ExecuteScript(active_web_contents(), script)); } void RemoteDesktopBrowserTest::ExecuteScriptAndWaitForAnyPageLoad( @@ -491,7 +563,7 @@ void RemoteDesktopBrowserTest::ExecuteScriptAndWaitForAnyPageLoad( content::WindowedNotificationObserver observer( content::NOTIFICATION_LOAD_STOP, content::Source<content::NavigationController>( - &active_browser_->tab_strip_model()->GetActiveWebContents()-> + &active_web_contents()-> GetController())); ExecuteScript(script); @@ -499,67 +571,42 @@ void RemoteDesktopBrowserTest::ExecuteScriptAndWaitForAnyPageLoad( observer.Wait(); } -void RemoteDesktopBrowserTest::ExecuteScriptAndWaitForPageLoad( - const std::string& script, - const GURL& target) { - content::WindowedNotificationObserver observer( - content::NOTIFICATION_LOAD_STOP, - base::Bind(&RemoteDesktopBrowserTest::IsURLLoadedInWindow, - active_browser_, - target)); - - ExecuteScript(script); - - observer.Wait(); -} - // static bool RemoteDesktopBrowserTest::ExecuteScriptAndExtractBool( - Browser* browser, const std::string& script) { + content::WebContents* web_contents, const std::string& script) { bool result; - _ASSERT_TRUE(content::ExecuteScriptAndExtractBool( - browser->tab_strip_model()->GetActiveWebContents(), + EXPECT_TRUE(content::ExecuteScriptAndExtractBool( + web_contents, "window.domAutomationController.send(" + script + ");", &result)); return result; } +// static int RemoteDesktopBrowserTest::ExecuteScriptAndExtractInt( - const std::string& script) { + content::WebContents* web_contents, const std::string& script) { int result; _ASSERT_TRUE(content::ExecuteScriptAndExtractInt( - active_browser_->tab_strip_model()->GetActiveWebContents(), + web_contents, "window.domAutomationController.send(" + script + ");", &result)); return result; } +// static std::string RemoteDesktopBrowserTest::ExecuteScriptAndExtractString( - const std::string& script) { + content::WebContents* web_contents, const std::string& script) { std::string result; _ASSERT_TRUE(content::ExecuteScriptAndExtractString( - active_browser_->tab_strip_model()->GetActiveWebContents(), + web_contents, "window.domAutomationController.send(" + script + ");", &result)); return result; } -// Helper to navigate to a given url. -void RemoteDesktopBrowserTest::NavigateToURLAndWaitForPageLoad( - const GURL& url) { - content::WindowedNotificationObserver observer( - content::NOTIFICATION_LOAD_STOP, - content::Source<content::NavigationController>( - &active_browser_->tab_strip_model()->GetActiveWebContents()-> - GetController())); - - ui_test_utils::NavigateToURL(active_browser_, url); - observer.Wait(); -} - void RemoteDesktopBrowserTest::ClickOnControl(const std::string& name) { ASSERT_TRUE(HtmlElementVisible(name)); @@ -624,15 +671,10 @@ bool RemoteDesktopBrowserTest::IsPinFormVisible() { } // static -bool RemoteDesktopBrowserTest::IsURLLoadedInWindow(Browser* browser, - const GURL& url) { - return GetCurrentURLInBrowser(browser) == url; -} - -// static -bool RemoteDesktopBrowserTest::IsAuthenticated(Browser* browser) { +bool RemoteDesktopBrowserTest::IsAuthenticatedInWindow( + content::WebContents* web_contents) { return ExecuteScriptAndExtractBool( - browser, "remoting.oauth2.isAuthenticated()"); + web_contents, "remoting.identity.isAuthenticated()"); } } // namespace remoting diff --git a/chrome/test/remoting/remote_desktop_browsertest.h b/chrome/test/remoting/remote_desktop_browsertest.h index 32c291157f..faa3a895a7 100644 --- a/chrome/test/remoting/remote_desktop_browsertest.h +++ b/chrome/test/remoting/remote_desktop_browsertest.h @@ -5,8 +5,8 @@ #ifndef CHROME_TEST_REMOTING_REMOTE_DESKTOP_BROWSERTEST_H_ #define CHROME_TEST_REMOTING_REMOTE_DESKTOP_BROWSERTEST_H_ +#include "chrome/browser/apps/app_browsertest_util.h" #include "chrome/browser/chrome_notification_types.h" -#include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/notification_service.h" @@ -19,6 +19,7 @@ const char kOverrideUserDataDir[] = "override-user-data-dir"; const char kNoCleanup[] = "no-cleanup"; const char kNoInstall[] = "no-install"; const char kWebAppCrx[] = "webapp-crx"; +const char kWebAppUnpacked[] = "webapp-unpacked"; const char kUsername[] = "username"; const char kkPassword[] = "password"; const char kMe2MePin[] = "me2me-pin"; @@ -33,9 +34,11 @@ inline void _ASSERT_TRUE(bool condition) { } // namespace +using extensions::Extension; + namespace remoting { -class RemoteDesktopBrowserTest : public ExtensionBrowserTest { +class RemoteDesktopBrowserTest : public extensions::PlatformAppBrowserTest { public: RemoteDesktopBrowserTest(); virtual ~RemoteDesktopBrowserTest(); @@ -57,7 +60,10 @@ class RemoteDesktopBrowserTest : public ExtensionBrowserTest { void VerifyInternetAccess(); // Install the chromoting extension from a crx file. - void InstallChromotingApp(); + void InstallChromotingAppCrx(); + + // Install the unpacked chromoting extension. + void InstallChromotingAppUnpacked(); // Uninstall the chromoting extension. void UninstallChromotingApp(); @@ -151,7 +157,23 @@ class RemoteDesktopBrowserTest : public ExtensionBrowserTest { base::FilePath WebAppCrxPath() { return webapp_crx_; } // Helper to get the extension ID of the installed chromoting webapp. - std::string ChromotingID() { return chromoting_id_; } + std::string ChromotingID() { return extension_->id(); } + + // Is this a appsv2 web app? + bool is_platform_app() { + return extension_->GetType() == extensions::Manifest::TYPE_PLATFORM_APP; + } + + // Are we testing an unpacked extension? + bool is_unpacked() { + return !webapp_unpacked_.empty(); + } + + // The "active" WebContents instance the test needs to interact with. + content::WebContents* active_web_contents() { + DCHECK(!web_contents_stack_.empty()); + return web_contents_stack_.back(); + } // Whether to perform the cleanup tasks (uninstalling chromoting, etc). // This is useful for diagnostic purposes. @@ -166,67 +188,62 @@ class RemoteDesktopBrowserTest : public ExtensionBrowserTest { return GURL("chrome-extension://" + ChromotingID() + "/main.html"); } - // Helper to retrieve the current URL of the active tab in the active browser - // window. + // Helper to retrieve the current URL in the active WebContents. GURL GetCurrentURL() { - return GetCurrentURLInBrowser(active_browser_); - } - - // Helper to retrieve the current URL of the active tab in the given browser - // window. - static GURL GetCurrentURLInBrowser(Browser* browser) { - return browser->tab_strip_model()->GetActiveWebContents()->GetURL(); + return active_web_contents()->GetURL(); } // Helpers to execute javascript code on a web page. - // Helper to execute a javascript code snippet on the current page of - // the active browser window. + // Helper to execute a javascript code snippet in the active WebContents. void ExecuteScript(const std::string& script); - // Helper to execute a javascript code snippet one the current page of - // the active browser window and wait for page load to complete. + // Helper to execute a javascript code snippet in the active WebContents + // and wait for page load to complete. void ExecuteScriptAndWaitForAnyPageLoad(const std::string& script); - // Helper to execute a javascript code snippet one the current page of - // the active browser window and wait until the target url is loaded. - // This is used when the target page is loaded after one or more redirections. - void ExecuteScriptAndWaitForPageLoad(const std::string& script, - const GURL& target); - - // Helper to execute a javascript code snippet on the current page of - // the active browser window and extract the boolean result. + // Helper to execute a javascript code snippet in the active WebContents + // and extract the boolean result. bool ExecuteScriptAndExtractBool(const std::string& script) { - return ExecuteScriptAndExtractBool(active_browser_, script); + return ExecuteScriptAndExtractBool(active_web_contents(), script); } - // Helper to execute a javascript code snippet one the current page of - // the active browser window and extract the boolean result. - static bool ExecuteScriptAndExtractBool(Browser* browser, + // Helper to execute a javascript code snippet and extract the boolean result. + static bool ExecuteScriptAndExtractBool(content::WebContents* web_contents, const std::string& script); - // Helper to execute a javascript code snippet one the current page of - // the active browser window and extract the int result. - int ExecuteScriptAndExtractInt(const std::string& script); + // Helper to execute a javascript code snippet in the active WebContents + // and extract the int result. + int ExecuteScriptAndExtractInt(const std::string& script) { + return ExecuteScriptAndExtractInt(active_web_contents(), script); + } + + // Helper to execute a javascript code snippet and extract the int result. + static int ExecuteScriptAndExtractInt(content::WebContents* web_contents, + const std::string& script); - // Helper to execute a javascript code snippet one the current page of - // the active browser window and extract the string result. - std::string ExecuteScriptAndExtractString(const std::string& script); + // Helper to execute a javascript code snippet in the active WebContents + // and extract the string result. + std::string ExecuteScriptAndExtractString(const std::string& script) { + return ExecuteScriptAndExtractString(active_web_contents(), script); + } - // Helper to navigate to a given url. - void NavigateToURLAndWaitForPageLoad(const GURL& url); + // Helper to execute a javascript code snippet and extract the string result. + static std::string ExecuteScriptAndExtractString( + content::WebContents* web_contents, const std::string& script); - // Helper to check whether an html element with the given name exists on - // the current page of the active browser window. + // Helper to check whether an html element with the given name exists in + // the active WebContents. bool HtmlElementExists(const std::string& name) { return ExecuteScriptAndExtractBool( "document.getElementById(\"" + name + "\") != null"); } - // Helper to check whether a html element with the given name is visible. + // Helper to check whether a html element with the given name is visible in + // the active WebContents. bool HtmlElementVisible(const std::string& name); - // Click on the named HTML control. + // Click on the named HTML control in the active WebContents. void ClickOnControl(const std::string& name); // Wait for the me2me connection to be established. @@ -243,13 +260,15 @@ class RemoteDesktopBrowserTest : public ExtensionBrowserTest { // has been established. bool IsSessionConnected(); - // Callback used by ExecuteScriptAndWaitForPageLoad to check whether - // the given page is currently loaded in the given browser window. - static bool IsURLLoadedInWindow(Browser* browser, const GURL& url); + // Callback used by Approve to check whether the chromoting app has + // successfully authenticated with the Google services. + bool IsAuthenticated() { + return IsAuthenticatedInWindow(active_web_contents()); + } // Callback used by Approve to check whether the chromoting app has - // successfully authenticated with the google services. - static bool IsAuthenticated(Browser* browser); + // successfully authenticated with the Google services. + static bool IsAuthenticatedInWindow(content::WebContents* web_contents); // Fields @@ -258,27 +277,22 @@ class RemoteDesktopBrowserTest : public ExtensionBrowserTest { // to override the default resolver while the test is active. scoped_ptr<net::ScopedDefaultHostResolverProc> mock_host_resolver_override_; - // The "active" browser window the test needs to interact with. - // We initialize |active_browser_| to the browser instance created by - // InProcessBrowserTest as the initial browser window to run test in. - // Whenever a new browser window is spawned and needs attention - // |active_browser_| is set to that browser window and all subsequent - // test actions happen there. - // And when the focus is returned to the original browser window - // |active_browser_| is reset to browser(). - // This pattern is sufficient for simple streamlined workflows where all - // the browser windows form a LIFO stack. The test always interacts - // with the "active" window which is always on the top of the "browser - // stack". See also http://crrev.com/chrome/browser/ui/browser_list.h - // If we ever need to deal with more complicated workflows the test - // code will need to explicitly pass browser instances to the helper - // routines. - Browser* active_browser_; + // Stores all the WebContents instance in a stack so that we can easily + // return to the previous instance. + // The active WebContents instance is always stored at the top of the stack. + // Initially the stack contains the WebContents instance created by + // InProcessBrowserTest as the initial context to run test in. + // Whenever a WebContents instance is spawned and needs attention we + // push it onto the stack and that becomes the active instance. + // And once we are done with the current WebContents instance + // we pop it off the stack, returning to the previous instance. + std::vector<content::WebContents*> web_contents_stack_; bool no_cleanup_; bool no_install_; - std::string chromoting_id_; + const Extension* extension_; base::FilePath webapp_crx_; + base::FilePath webapp_unpacked_; std::string username_; std::string password_; std::string me2me_pin_; |