summaryrefslogtreecommitdiff
path: root/chrome/test
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2013-08-23 16:39:15 +0100
committerTorne (Richard Coles) <torne@google.com>2013-08-23 16:39:15 +0100
commit3551c9c881056c480085172ff9840cab31610854 (patch)
tree23660320f5f4c279966609cf9da7491b96d10ca8 /chrome/test
parent4e9d9adbbb6cf287125ca44a0823791a570472f5 (diff)
downloadchromium_org-3551c9c881056c480085172ff9840cab31610854.tar.gz
Merge from Chromium at DEPS revision r219274
This commit was generated by merge_to_master.py. Change-Id: Ibb7f41396cadf4071e89153e1913c986d126f65d
Diffstat (limited to 'chrome/test')
-rw-r--r--chrome/test/base/chrome_render_view_test.cc4
-rw-r--r--chrome/test/base/chrome_render_view_test.h5
-rw-r--r--chrome/test/base/in_process_browser_test.h8
-rw-r--r--chrome/test/base/scoped_browser_locale.cc16
-rw-r--r--chrome/test/base/scoped_browser_locale.h24
-rw-r--r--chrome/test/base/test_browser_window.h9
-rw-r--r--chrome/test/base/testing_profile.cc57
-rw-r--r--chrome/test/base/testing_profile.h48
-rw-r--r--chrome/test/base/testing_profile_manager.cc13
-rw-r--r--chrome/test/base/view_event_test_base.cc3
-rw-r--r--chrome/test/chromedriver/capabilities.cc17
-rw-r--r--chrome/test/chromedriver/capabilities.h5
-rw-r--r--chrome/test/chromedriver/capabilities_unittest.cc16
-rw-r--r--chrome/test/chromedriver/chrome/adb_impl.cc14
-rw-r--r--chrome/test/chromedriver/chrome/adb_impl.h3
-rw-r--r--chrome/test/chromedriver/chrome/local_state.txt3
-rw-r--r--chrome/test/chromedriver/chrome/preferences.txt6
-rw-r--r--chrome/test/chromedriver/chrome_launcher.cc40
-rw-r--r--chrome/test/chromedriver/client/chromedriver.py9
-rw-r--r--chrome/test/chromedriver/client/webelement.py3
-rw-r--r--chrome/test/chromedriver/element_commands.cc25
-rw-r--r--chrome/test/chromedriver/element_util.cc26
-rw-r--r--chrome/test/chromedriver/element_util.h11
-rwxr-xr-xchrome/test/chromedriver/run_buildbot_steps.py15
-rw-r--r--chrome/test/chromedriver/server/chromedriver_server.cc17
-rw-r--r--chrome/test/chromedriver/server/http_handler.cc11
-rw-r--r--chrome/test/chromedriver/server/http_handler.h3
-rwxr-xr-xchrome/test/chromedriver/test/run_all_tests.py34
-rwxr-xr-xchrome/test/chromedriver/test/run_py_tests.py76
-rw-r--r--chrome/test/chromedriver/test/test_expectations5
-rw-r--r--chrome/test/chromedriver/window_commands.cc93
-rw-r--r--chrome/test/chromedriver/window_commands.h21
-rw-r--r--chrome/test/functional/PYAUTO_TESTS44
-rw-r--r--chrome/test/functional/ispy/ispy_core/app.yaml17
-rw-r--r--chrome/test/functional/ispy/ispy_core/handlers/__init__.py0
-rw-r--r--chrome/test/functional/ispy/ispy_core/handlers/command_handler.py357
-rw-r--r--chrome/test/functional/ispy/ispy_core/handlers/debug_view_handler.py43
-rw-r--r--chrome/test/functional/ispy/ispy_core/handlers/image_handler.py42
-rw-r--r--chrome/test/functional/ispy/ispy_core/handlers/main_view_handler.py117
-rw-r--r--chrome/test/functional/ispy/ispy_core/handlers/update_mask_handler.py64
-rw-r--r--chrome/test/functional/ispy/ispy_core/ispy_auth_constants.py12
-rw-r--r--chrome/test/functional/ispy/ispy_core/main.py18
-rwxr-xr-xchrome/test/functional/ispy/ispy_core/run_tests.py11
-rw-r--r--chrome/test/functional/ispy/ispy_core/tests/rendering_test_manager/cloud_bucket_impl.py13
-rw-r--r--chrome/test/functional/ispy/ispy_core/tests/rendering_test_manager/mock_cloud_bucket.py4
-rw-r--r--chrome/test/functional/ispy/ispy_core/tools/__init__.py24
-rw-r--r--chrome/test/functional/ispy/ispy_core/tools/api_command_runner.py58
-rw-r--r--chrome/test/functional/ispy/ispy_core/tools/api_command_runner_functionaltest.py111
-rw-r--r--chrome/test/functional/ispy/ispy_core/tools/image_tools_unittest.py8
-rw-r--r--chrome/test/functional/ispy/ispy_core/tools/rendering_test_manager.py91
-rw-r--r--chrome/test/functional/ispy/ispy_core/tools/rendering_test_manager_unittest.py152
-rw-r--r--chrome/test/functional/ispy/ispy_core/tools/reverse_port_forwarder.py14
-rw-r--r--chrome/test/functional/ispy/ispy_core/views/__init__.py0
-rw-r--r--chrome/test/functional/ispy/ispy_core/views/debug_view.html71
-rw-r--r--chrome/test/functional/ispy/ispy_core/views/diff_view.html62
-rw-r--r--chrome/test/functional/ispy/ispy_core/views/list_view.html36
-rw-r--r--chrome/test/functional/ispy/ispy_core/views/main_view.html57
-rwxr-xr-xchrome/test/functional/media_stream_infobar.py83
-rwxr-xr-xchrome/test/functional/webrtc_apprtc_call.py71
-rwxr-xr-xchrome/test/functional/webrtc_audio_quality.py184
-rwxr-xr-xchrome/test/functional/webrtc_brutality_test.py73
-rwxr-xr-xchrome/test/functional/webrtc_call.py224
-rwxr-xr-xchrome/test/functional/webrtc_test_base.py215
-rwxr-xr-xchrome/test/functional/webrtc_video_quality.py401
-rw-r--r--chrome/test/mini_installer/config/chrome_installed.prop9
-rw-r--r--chrome/test/mini_installer/config/chrome_not_installed.prop3
-rw-r--r--chrome/test/mini_installer/config/config.config3
-rw-r--r--chrome/test/mini_installer/file_verifier.py26
-rw-r--r--chrome/test/mini_installer/path_resolver.py45
-rw-r--r--chrome/test/mini_installer/test_installer.py7
-rw-r--r--chrome/test/mini_installer/uninstall_chrome.py41
-rw-r--r--chrome/test/mini_installer/verifier.py5
-rw-r--r--chrome/test/nacl/nacl_browsertest.cc63
-rw-r--r--chrome/test/nacl/nacl_browsertest_uma.cc2
-rw-r--r--chrome/test/nacl/nacl_browsertest_util.cc56
-rw-r--r--chrome/test/nacl/nacl_browsertest_util.h23
-rw-r--r--chrome/test/perf/rendering/throughput_tests.cc15
-rw-r--r--chrome/test/ppapi/ppapi_browsertest.cc24
-rw-r--r--chrome/test/ppapi/ppapi_test.cc6
79 files changed, 2027 insertions, 1618 deletions
diff --git a/chrome/test/base/chrome_render_view_test.cc b/chrome/test/base/chrome_render_view_test.cc
index b642275f89..545a4f5573 100644
--- a/chrome/test/base/chrome_render_view_test.cc
+++ b/chrome/test/base/chrome_render_view_test.cc
@@ -13,7 +13,9 @@
#include "chrome/renderer/extensions/event_bindings.h"
#include "chrome/renderer/extensions/extension_custom_bindings.h"
#include "chrome/renderer/spellchecker/spellcheck.h"
+#include "components/autofill/content/renderer/autofill_agent.h"
#include "components/autofill/content/renderer/password_autofill_agent.h"
+#include "components/autofill/content/renderer/test_password_autofill_agent.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/common/renderer_preferences.h"
#include "content/public/renderer/render_view.h"
@@ -64,7 +66,7 @@ void ChromeRenderViewTest::SetUp() {
// RenderView doesn't expose its PasswordAutofillAgent or AutofillAgent
// objects, because it has no need to store them directly (they're stored as
// RenderViewObserver*). So just create another set.
- password_autofill_ = new PasswordAutofillAgent(view_);
+ password_autofill_ = new autofill::TestPasswordAutofillAgent(view_);
autofill_agent_ = new AutofillAgent(view_, password_autofill_);
}
diff --git a/chrome/test/base/chrome_render_view_test.h b/chrome/test/base/chrome_render_view_test.h
index 056f626291..a1002d1536 100644
--- a/chrome/test/base/chrome_render_view_test.h
+++ b/chrome/test/base/chrome_render_view_test.h
@@ -9,12 +9,11 @@
#include "chrome/renderer/chrome_content_renderer_client.h"
#include "chrome/renderer/chrome_mock_render_thread.h"
-#include "components/autofill/content/renderer/autofill_agent.h"
#include "content/public/test/render_view_test.h"
namespace autofill {
class AutofillAgent;
-class PasswordAutofillAgent;
+class TestPasswordAutofillAgent;
}
namespace extensions {
@@ -34,7 +33,7 @@ class ChromeRenderViewTest : public content::RenderViewTest {
chrome::ChromeContentRendererClient chrome_content_renderer_client_;
extensions::Dispatcher* extension_dispatcher_;
- autofill::PasswordAutofillAgent* password_autofill_;
+ autofill::TestPasswordAutofillAgent* password_autofill_;
autofill::AutofillAgent* autofill_agent_;
// Naked pointer as ownership is with content::RenderViewTest::render_thread_.
diff --git a/chrome/test/base/in_process_browser_test.h b/chrome/test/base/in_process_browser_test.h
index 6786342132..8545846371 100644
--- a/chrome/test/base/in_process_browser_test.h
+++ b/chrome/test/base/in_process_browser_test.h
@@ -14,10 +14,6 @@
#include "content/public/test/browser_test_base.h"
#include "testing/gtest/include/gtest/gtest.h"
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/cros/network_library.h"
-#endif // defined(OS_CHROMEOS)
-
namespace base {
#if defined(OS_MACOSX)
namespace mac {
@@ -217,10 +213,6 @@ class InProcessBrowserTest : public content::BrowserTestBase {
// not ensure that Browsers are only created on the tested desktop).
bool multi_desktop_test_;
-#if defined(OS_CHROMEOS)
- chromeos::ScopedStubNetworkLibraryEnabler stub_network_library_enabler_;
-#endif // defined(OS_CHROMEOS)
-
#if defined(OS_MACOSX)
base::mac::ScopedNSAutoreleasePool* autorelease_pool_;
#endif // OS_MACOSX
diff --git a/chrome/test/base/scoped_browser_locale.cc b/chrome/test/base/scoped_browser_locale.cc
new file mode 100644
index 0000000000..c1538a1dbe
--- /dev/null
+++ b/chrome/test/base/scoped_browser_locale.cc
@@ -0,0 +1,16 @@
+// 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/base/scoped_browser_locale.h"
+
+#include "chrome/browser/browser_process.h"
+
+ScopedBrowserLocale::ScopedBrowserLocale(const std::string& new_locale)
+ : old_locale_(g_browser_process->GetApplicationLocale()) {
+ g_browser_process->SetApplicationLocale(new_locale);
+}
+
+ScopedBrowserLocale::~ScopedBrowserLocale() {
+ g_browser_process->SetApplicationLocale(old_locale_);
+}
diff --git a/chrome/test/base/scoped_browser_locale.h b/chrome/test/base/scoped_browser_locale.h
new file mode 100644
index 0000000000..0f49b8cc4d
--- /dev/null
+++ b/chrome/test/base/scoped_browser_locale.h
@@ -0,0 +1,24 @@
+// 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_BASE_SCOPED_BROWSER_LOCALE_H_
+#define CHROME_TEST_BASE_SCOPED_BROWSER_LOCALE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+// Helper class to temporarily set the locale of the browser process.
+class ScopedBrowserLocale {
+ public:
+ explicit ScopedBrowserLocale(const std::string& new_locale);
+ ~ScopedBrowserLocale();
+
+ private:
+ const std::string old_locale_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedBrowserLocale);
+};
+
+#endif // CHROME_TEST_BASE_SCOPED_BROWSER_LOCALE_H_
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index 71470582ac..40cff07126 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -70,8 +70,7 @@ class TestBrowserWindow : public BrowserWindow {
virtual LocationBar* GetLocationBar() const OVERRIDE;
virtual void SetFocusToLocationBar(bool select_all) OVERRIDE {}
virtual void UpdateReloadStopState(bool is_loading, bool force) OVERRIDE {}
- virtual void UpdateToolbar(content::WebContents* contents,
- bool should_restore_state) OVERRIDE {}
+ virtual void UpdateToolbar(content::WebContents* contents) OVERRIDE {}
virtual void FocusToolbar() OVERRIDE {}
virtual void FocusAppMenu() OVERRIDE {}
virtual void FocusBookmarksToolbar() OVERRIDE {}
@@ -107,7 +106,11 @@ class TestBrowserWindow : public BrowserWindow {
#endif
virtual bool IsDownloadShelfVisible() const OVERRIDE;
virtual DownloadShelf* GetDownloadShelf() OVERRIDE;
- virtual void ConfirmBrowserCloseWithPendingDownloads() OVERRIDE {}
+ virtual void ConfirmBrowserCloseWithPendingDownloads(
+ int download_count,
+ Browser::DownloadClosePreventionType dialog_type,
+ bool app_modal,
+ const base::Callback<void(bool)>& callback) OVERRIDE {}
virtual void UserChangedTheme() OVERRIDE {}
virtual int GetExtraRenderViewHeight() const OVERRIDE;
virtual void WebContentsFocused(content::WebContents* contents) OVERRIDE {}
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index 318c14c349..f53c423f91 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -167,6 +167,7 @@ TestingProfile::TestingProfile()
: start_time_(Time::Now()),
testing_prefs_(NULL),
incognito_(false),
+ force_incognito_(false),
original_profile_(NULL),
last_session_exited_cleanly_(true),
browser_context_dependency_manager_(
@@ -184,6 +185,7 @@ TestingProfile::TestingProfile(const base::FilePath& path)
: start_time_(Time::Now()),
testing_prefs_(NULL),
incognito_(false),
+ force_incognito_(false),
original_profile_(NULL),
last_session_exited_cleanly_(true),
profile_path_(path),
@@ -200,6 +202,7 @@ TestingProfile::TestingProfile(const base::FilePath& path,
: start_time_(Time::Now()),
testing_prefs_(NULL),
incognito_(false),
+ force_incognito_(false),
original_profile_(NULL),
last_session_exited_cleanly_(true),
profile_path_(path),
@@ -221,11 +224,14 @@ TestingProfile::TestingProfile(
const base::FilePath& path,
Delegate* delegate,
scoped_refptr<ExtensionSpecialStoragePolicy> extension_policy,
- scoped_ptr<PrefServiceSyncable> prefs)
+ scoped_ptr<PrefServiceSyncable> prefs,
+ bool incognito,
+ const TestingFactories& factories)
: start_time_(Time::Now()),
prefs_(prefs.release()),
testing_prefs_(NULL),
- incognito_(false),
+ incognito_(incognito),
+ force_incognito_(false),
original_profile_(NULL),
last_session_exited_cleanly_(true),
extension_special_storage_policy_(extension_policy),
@@ -241,6 +247,12 @@ TestingProfile::TestingProfile(
profile_path_ = temp_dir_.path();
}
+ // Set any testing factories prior to initializing the services.
+ for (TestingFactories::const_iterator it = factories.begin();
+ it != factories.end(); ++it) {
+ it->first->SetTestingFactory(this, it->second);
+ }
+
Init();
// If caller supplied a delegate, delay the FinishInit invocation until other
// tasks have run.
@@ -309,7 +321,11 @@ void TestingProfile::Init() {
extensions::ExtensionSystemFactory::GetInstance()->SetTestingFactory(
this, extensions::TestExtensionSystem::Build);
- browser_context_dependency_manager_->CreateBrowserContextServices(this, true);
+ // If there is no separate original profile specified for this profile, then
+ // force preferences to be registered - this allows tests to create a
+ // standalone incognito profile while still having prefs registered.
+ browser_context_dependency_manager_->CreateBrowserContextServicesForTest(
+ this, !original_profile_);
#if defined(ENABLE_NOTIFICATIONS)
// Install profile keyed service factory hooks for dummy/test services
@@ -330,6 +346,9 @@ void TestingProfile::FinishInit() {
}
TestingProfile::~TestingProfile() {
+ // Revert to non-incognito mode before shutdown.
+ force_incognito_ = false;
+
// Any objects holding live URLFetchers should be deleted before teardown.
TemplateURLFetcherFactory::ShutdownForProfile(this);
@@ -497,8 +516,6 @@ void TestingProfile::BlockUntilTopSitesLoaded() {
content::WindowedNotificationObserver top_sites_loaded_observer(
chrome::NOTIFICATION_TOP_SITES_LOADED,
content::NotificationService::AllSources());
- if (!HistoryServiceFactory::GetForProfile(this, Profile::EXPLICIT_ACCESS))
- GetTopSites()->HistoryLoaded();
top_sites_loaded_observer.Wait();
}
@@ -526,18 +543,22 @@ std::string TestingProfile::GetProfileName() {
}
bool TestingProfile::IsOffTheRecord() const {
- return incognito_;
+ return force_incognito_ || incognito_;
}
-void TestingProfile::SetOffTheRecordProfile(Profile* profile) {
- incognito_profile_.reset(profile);
+void TestingProfile::SetOffTheRecordProfile(scoped_ptr<Profile> profile) {
+ DCHECK(!IsOffTheRecord());
+ incognito_profile_ = profile.Pass();
}
void TestingProfile::SetOriginalProfile(Profile* profile) {
+ DCHECK(IsOffTheRecord());
original_profile_ = profile;
}
Profile* TestingProfile::GetOffTheRecordProfile() {
+ if (IsOffTheRecord())
+ return this;
return incognito_profile_.get();
}
@@ -552,8 +573,7 @@ Profile* TestingProfile::GetOriginalProfile() {
}
bool TestingProfile::IsManaged() {
- return GetPrefs()->GetBoolean(prefs::kProfileIsManaged) ||
- !GetPrefs()->GetString(prefs::kManagedUserId).empty();
+ return !GetPrefs()->GetString(prefs::kManagedUserId).empty();
}
ExtensionService* TestingProfile::GetExtensionService() {
@@ -797,7 +817,8 @@ Profile::ExitType TestingProfile::GetLastSessionExitType() {
TestingProfile::Builder::Builder()
: build_called_(false),
- delegate_(NULL) {
+ delegate_(NULL),
+ incognito_(false) {
}
TestingProfile::Builder::~Builder() {
@@ -821,6 +842,16 @@ void TestingProfile::Builder::SetPrefService(
pref_service_ = prefs.Pass();
}
+void TestingProfile::Builder::SetIncognito() {
+ incognito_ = true;
+}
+
+void TestingProfile::Builder::AddTestingFactory(
+ BrowserContextKeyedServiceFactory* service_factory,
+ BrowserContextKeyedServiceFactory::FactoryFunction callback) {
+ testing_factories_.push_back(std::make_pair(service_factory, callback));
+}
+
scoped_ptr<TestingProfile> TestingProfile::Builder::Build() {
DCHECK(!build_called_);
build_called_ = true;
@@ -828,5 +859,7 @@ scoped_ptr<TestingProfile> TestingProfile::Builder::Build() {
path_,
delegate_,
extension_policy_,
- pref_service_.Pass()));
+ pref_service_.Pass(),
+ incognito_,
+ testing_factories_));
}
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index 8e63e5a71b..03c95d463a 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -11,6 +11,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/profiles/profile.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h"
namespace content {
class MockResourceContext;
@@ -59,6 +60,10 @@ class TestingProfile : public Profile {
// Default constructor that cannot be used with multi-profiles.
TestingProfile();
+ typedef std::vector<std::pair<
+ BrowserContextKeyedServiceFactory*,
+ BrowserContextKeyedServiceFactory::FactoryFunction> > TestingFactories;
+
// Helper class for building an instance of TestingProfile (allows injecting
// mocks for various services prior to profile initialization).
// TODO(atwilson): Remove non-default constructors and various setters in
@@ -68,13 +73,19 @@ class TestingProfile : public Profile {
Builder();
~Builder();
- // Sets a Delegate to be called back when the Profile is fully initialized.
- // This causes the final initialization to be performed via a task so the
- // caller must run a MessageLoop. Caller maintains ownership of the Delegate
+ // Sets a Delegate to be called back during profile init. This causes the
+ // final initialization to be performed via a task so the caller must run
+ // a MessageLoop. Caller maintains ownership of the Delegate
// and must manage its lifetime so it continues to exist until profile
// initialization is complete.
void SetDelegate(Delegate* delegate);
+ // Adds a testing factory to the TestingProfile. These testing factories
+ // are applied before the ProfileKeyedServices are created.
+ void AddTestingFactory(
+ BrowserContextKeyedServiceFactory* service_factory,
+ BrowserContextKeyedServiceFactory::FactoryFunction callback);
+
// Sets the ExtensionSpecialStoragePolicy to be returned by
// GetExtensionSpecialStoragePolicy().
void SetExtensionSpecialStoragePolicy(
@@ -86,6 +97,9 @@ class TestingProfile : public Profile {
// Sets the PrefService to be used by this profile.
void SetPrefService(scoped_ptr<PrefServiceSyncable> prefs);
+ // Makes the Profile being built an incognito profile.
+ void SetIncognito();
+
// Creates the TestingProfile using previously-set settings.
scoped_ptr<TestingProfile> Build();
@@ -98,6 +112,8 @@ class TestingProfile : public Profile {
scoped_refptr<ExtensionSpecialStoragePolicy> extension_policy_;
base::FilePath path_;
Delegate* delegate_;
+ bool incognito_;
+ TestingFactories testing_factories_;
DISALLOW_COPY_AND_ASSIGN(Builder);
};
@@ -120,7 +136,9 @@ class TestingProfile : public Profile {
TestingProfile(const base::FilePath& path,
Delegate* delegate,
scoped_refptr<ExtensionSpecialStoragePolicy> extension_policy,
- scoped_ptr<PrefServiceSyncable> prefs);
+ scoped_ptr<PrefServiceSyncable> prefs,
+ bool incognito,
+ const TestingFactories& factories);
virtual ~TestingProfile();
@@ -186,9 +204,26 @@ class TestingProfile : public Profile {
virtual TestingProfile* AsTestingProfile() OVERRIDE;
virtual std::string GetProfileName() OVERRIDE;
- void set_incognito(bool incognito) { incognito_ = incognito; }
+
+ // DEPRECATED, because it's fragile to change a profile from non-incognito
+ // to incognito after the ProfileKeyedServices have been created (some
+ // ProfileKeyedServices either should not exist in incognito mode, or will
+ // crash when they try to get references to other services they depend on,
+ // but do not exist in incognito mode).
+ // TODO(atwilson): Remove this API (http://crbug.com/277296).
+ //
+ // Changes a profile's to/from incognito mode temporarily - profile will be
+ // returned to non-incognito before destruction to allow services to
+ // properly shutdown. This is only supported for legacy tests - new tests
+ // should create a true incognito profile using Builder::SetIncognito() or
+ // by using the TestingProfile constructor that allows setting the incognito
+ // flag.
+ void ForceIncognito(bool force_incognito) {
+ force_incognito_ = force_incognito;
+ }
+
// Assumes ownership.
- virtual void SetOffTheRecordProfile(Profile* profile);
+ virtual void SetOffTheRecordProfile(scoped_ptr<Profile> profile);
virtual void SetOriginalProfile(Profile* profile);
virtual Profile* GetOffTheRecordProfile() OVERRIDE;
virtual void DestroyOffTheRecordProfile() OVERRIDE {}
@@ -306,6 +341,7 @@ class TestingProfile : public Profile {
std::wstring id_;
bool incognito_;
+ bool force_incognito_;
scoped_ptr<Profile> incognito_profile_;
Profile* original_profile_;
diff --git a/chrome/test/base/testing_profile_manager.cc b/chrome/test/base/testing_profile_manager.cc
index e6f4748a30..644760cc22 100644
--- a/chrome/test/base/testing_profile_manager.cc
+++ b/chrome/test/base/testing_profile_manager.cc
@@ -16,10 +16,10 @@
namespace testing {
-class ProfileManager : public ::ProfileManager {
+class ProfileManager : public ::ProfileManagerWithoutInit {
public:
explicit ProfileManager(const base::FilePath& user_data_dir)
- : ::ProfileManager(user_data_dir) {}
+ : ::ProfileManagerWithoutInit(user_data_dir) {}
protected:
virtual Profile* CreateProfileHelper(
@@ -57,11 +57,10 @@ TestingProfile* TestingProfileManager::CreateTestingProfile(
profile_path = profile_path.AppendASCII(profile_name);
// Create the profile and register it.
- TestingProfile* profile = new TestingProfile(
- profile_path,
- NULL,
- scoped_refptr<ExtensionSpecialStoragePolicy>(),
- prefs.Pass());
+ TestingProfile::Builder builder;
+ builder.SetPath(profile_path);
+ builder.SetPrefService(prefs.Pass());
+ TestingProfile* profile = builder.Build().release();
profile_manager_->AddProfile(profile); // Takes ownership.
// Update the user metadata.
diff --git a/chrome/test/base/view_event_test_base.cc b/chrome/test/base/view_event_test_base.cc
index 4654a7e288..c37fe93dd2 100644
--- a/chrome/test/base/view_event_test_base.cc
+++ b/chrome/test/base/view_event_test_base.cc
@@ -36,6 +36,7 @@
#if defined(OS_CHROMEOS)
#include "chromeos/audio/cras_audio_handler.h"
+#include "chromeos/network/network_handler.h"
#endif
namespace {
@@ -116,6 +117,7 @@ void ViewEventTestBase::SetUp() {
message_center::MessageCenter::Initialize();
#if defined(OS_CHROMEOS)
chromeos::CrasAudioHandler::InitializeForTesting();
+ chromeos::NetworkHandler::Initialize();
#endif // OS_CHROMEOS
ash::test::TestShellDelegate* shell_delegate =
new ash::test::TestShellDelegate();
@@ -150,6 +152,7 @@ void ViewEventTestBase::TearDown() {
#else
ash::Shell::DeleteInstance();
#if defined(OS_CHROMEOS)
+ chromeos::NetworkHandler::Shutdown();
chromeos::CrasAudioHandler::Shutdown();
#endif
// Ash Shell can't just live on its own without a browser process, we need to
diff --git a/chrome/test/chromedriver/capabilities.cc b/chrome/test/chromedriver/capabilities.cc
index f5f1a6b7ad..b052f0f7f7 100644
--- a/chrome/test/chromedriver/capabilities.cc
+++ b/chrome/test/chromedriver/capabilities.cc
@@ -182,6 +182,22 @@ Status ParseProxy(const base::Value& option, Capabilities* capabilities) {
return Status(kOk);
}
+Status ParseExcludeSwitches(const base::Value& option,
+ Capabilities* capabilities) {
+ const base::ListValue* switches = NULL;
+ if (!option.GetAsList(&switches))
+ return Status(kUnknownError, "'excludeSwitches' must be a list");
+ for (size_t i = 0; i < switches->GetSize(); ++i) {
+ std::string switch_name;
+ if (!switches->GetString(i, &switch_name)) {
+ return Status(kUnknownError,
+ "each switch to be removed must be a string");
+ }
+ capabilities->exclude_switches.insert(switch_name);
+ }
+ return Status(kOk);
+}
+
Status ParseDesktopChromeCapabilities(
Log* log,
const base::Value& capability,
@@ -201,6 +217,7 @@ Status ParseDesktopChromeCapabilities(
parser_map["prefs"] = base::Bind(&ParsePrefs);
parser_map["localState"] = base::Bind(&ParseLocalState);
parser_map["extensions"] = base::Bind(&ParseExtensions);
+ parser_map["excludeSwitches"] = base::Bind(&ParseExcludeSwitches);
for (base::DictionaryValue::Iterator it(*chrome_options); !it.IsAtEnd();
it.Advance()) {
diff --git a/chrome/test/chromedriver/capabilities.h b/chrome/test/chromedriver/capabilities.h
index 2ef9bb0613..c5e243ea76 100644
--- a/chrome/test/chromedriver/capabilities.h
+++ b/chrome/test/chromedriver/capabilities.h
@@ -5,6 +5,7 @@
#ifndef CHROME_TEST_CHROMEDRIVER_CAPABILITIES_H_
#define CHROME_TEST_CHROMEDRIVER_CAPABILITIES_H_
+#include <set>
#include <string>
#include <vector>
@@ -45,6 +46,10 @@ struct Capabilities {
scoped_ptr<base::DictionaryValue> local_state;
std::vector<std::string> extensions;
scoped_ptr<base::DictionaryValue> logging_prefs;
+
+ // Set of switches which should be removed from default list when launching
+ // Chrome.
+ std::set<std::string> exclude_switches;
};
#endif // CHROME_TEST_CHROMEDRIVER_CAPABILITIES_H_
diff --git a/chrome/test/chromedriver/capabilities_unittest.cc b/chrome/test/chromedriver/capabilities_unittest.cc
index c5294ee518..759646885e 100644
--- a/chrome/test/chromedriver/capabilities_unittest.cc
+++ b/chrome/test/chromedriver/capabilities_unittest.cc
@@ -317,3 +317,19 @@ TEST(ParseCapabilities, LoggingPrefsNotDict) {
Status status = capabilities.Parse(caps, &log);
ASSERT_FALSE(status.IsOk());
}
+
+TEST(ParseCapabilities, ExcludeSwitches) {
+ Capabilities capabilities;
+ base::ListValue exclude_switches;
+ exclude_switches.AppendString("switch1");
+ exclude_switches.AppendString("switch2");
+ base::DictionaryValue caps;
+ caps.Set("chromeOptions.excludeSwitches", exclude_switches.DeepCopy());
+ Logger log(Log::kError);
+ Status status = capabilities.Parse(caps, &log);
+ ASSERT_TRUE(status.IsOk());
+ ASSERT_EQ(2u, capabilities.exclude_switches.size());
+ const std::set<std::string>& switches = capabilities.exclude_switches;
+ ASSERT_TRUE(switches.find("switch1") != switches.end());
+ ASSERT_TRUE(switches.find("switch2") != switches.end());
+}
diff --git a/chrome/test/chromedriver/chrome/adb_impl.cc b/chrome/test/chromedriver/chrome/adb_impl.cc
index 363790c700..5432d18f1e 100644
--- a/chrome/test/chromedriver/chrome/adb_impl.cc
+++ b/chrome/test/chromedriver/chrome/adb_impl.cc
@@ -62,9 +62,10 @@ class ResponseBuffer : public base::RefCountedThreadSafe<ResponseBuffer> {
};
void ExecuteCommandOnIOThread(
- const std::string& command, scoped_refptr<ResponseBuffer> response_buffer) {
+ const std::string& command, scoped_refptr<ResponseBuffer> response_buffer,
+ int port) {
CHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_IO));
- AdbClientSocket::AdbQuery(5037, command,
+ AdbClientSocket::AdbQuery(port, command,
base::Bind(&ResponseBuffer::OnResponse, response_buffer));
}
@@ -72,8 +73,8 @@ void ExecuteCommandOnIOThread(
AdbImpl::AdbImpl(
const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
- Log* log)
- : io_task_runner_(io_task_runner), log_(log) {
+ Log* log, int port)
+ : io_task_runner_(io_task_runner), log_(log), port_(port) {
CHECK(io_task_runner_.get());
}
@@ -165,8 +166,7 @@ Status AdbImpl::Launch(
std::string response;
Status status = ExecuteHostShellCommand(
device_serial,
- "am start -W -n " + package + "/" + activity +
- " -d \"data:text/html;charset=utf-8,\"",
+ "am start -W -n " + package + "/" + activity + " -d about:blank",
&response);
if (!status.IsOk())
return status;
@@ -221,7 +221,7 @@ Status AdbImpl::ExecuteCommand(
log_->AddEntry(Log::kDebug, "Sending adb command: " + command);
io_task_runner_->PostTask(
FROM_HERE,
- base::Bind(&ExecuteCommandOnIOThread, command, response_buffer));
+ base::Bind(&ExecuteCommandOnIOThread, command, response_buffer, port_));
Status status = response_buffer->GetResponse(
response, base::TimeDelta::FromSeconds(30));
if (status.IsOk())
diff --git a/chrome/test/chromedriver/chrome/adb_impl.h b/chrome/test/chromedriver/chrome/adb_impl.h
index 155af1cb2f..80ce305ed9 100644
--- a/chrome/test/chromedriver/chrome/adb_impl.h
+++ b/chrome/test/chromedriver/chrome/adb_impl.h
@@ -23,7 +23,7 @@ class AdbImpl : public Adb {
public:
explicit AdbImpl(
const scoped_refptr<base::SingleThreadTaskRunner>& io_message_loop_proxy,
- Log* log);
+ Log* log, int port);
virtual ~AdbImpl();
// Overridden from Adb:
@@ -61,6 +61,7 @@ class AdbImpl : public Adb {
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
Log* log_;
+ int port_;
};
#endif // CHROME_TEST_CHROMEDRIVER_CHROME_ADB_IMPL_H_
diff --git a/chrome/test/chromedriver/chrome/local_state.txt b/chrome/test/chromedriver/chrome/local_state.txt
index 75b3067d88..a0d20c1bbb 100644
--- a/chrome/test/chromedriver/chrome/local_state.txt
+++ b/chrome/test/chromedriver/chrome/local_state.txt
@@ -1,4 +1,7 @@
{
+ "background_mode": {
+ "enabled": false
+ },
"ssl": {
"rev_checking": {
"enabled": false
diff --git a/chrome/test/chromedriver/chrome/preferences.txt b/chrome/test/chromedriver/chrome/preferences.txt
index 32f4cb12cd..1ad1f7a15c 100644
--- a/chrome/test/chromedriver/chrome/preferences.txt
+++ b/chrome/test/chromedriver/chrome/preferences.txt
@@ -2,6 +2,9 @@
"alternate_error_pages": {
"enabled": false
},
+ "autofill": {
+ "enabled": false
+ },
"browser": {
"check_default_browser": false
},
@@ -33,7 +36,8 @@
"notifications": 1,
"popups": 1,
"ppapi-broker": 1
- }
+ },
+ "password_manager_enabled": false
},
"safebrowsing": {
"enabled": false
diff --git a/chrome/test/chromedriver/chrome_launcher.cc b/chrome/test/chromedriver/chrome_launcher.cc
index d151d5d682..6987a2644c 100644
--- a/chrome/test/chromedriver/chrome_launcher.cc
+++ b/chrome/test/chromedriver/chrome_launcher.cc
@@ -64,6 +64,16 @@ Status UnpackAutomationExtension(const base::FilePath& temp_dir,
return Status(kOk);
}
+void AddSwitches(CommandLine* command,
+ const char* switches[],
+ size_t switch_count,
+ const std::set<std::string>& exclude_switches) {
+ for (size_t i = 0; i < switch_count; ++i) {
+ if (exclude_switches.find(switches[i]) == exclude_switches.end())
+ command->AppendSwitch(switches[i]);
+ }
+}
+
Status PrepareCommandLine(int port,
const Capabilities& capabilities,
CommandLine* prepared_command,
@@ -81,13 +91,35 @@ Status PrepareCommandLine(int port,
program.value().c_str()));
}
- command.AppendSwitchASCII("remote-debugging-port", base::IntToString(port));
- command.AppendSwitch("no-first-run");
+ const char* excludable_switches[] = {
+ "disable-hang-monitor",
+ "disable-prompt-on-repost",
+ "full-memory-crash-report",
+ "no-first-run",
+ "disable-background-networking",
+ // TODO(chrisgao): Add "disable-sync" when chrome 30- is not supported.
+ // For chrome 30-, it leads to crash when opening chrome://settings.
+ "disable-web-resources",
+ "safebrowsing-disable-auto-update",
+ "safebrowsing-disable-download-protection",
+ "disable-client-side-phishing-detection",
+ "disable-component-update",
+ "disable-default-apps",
+ };
+
+ AddSwitches(&command, excludable_switches, arraysize(excludable_switches),
+ capabilities.exclude_switches);
+ AddSwitches(&command, kCommonSwitches, arraysize(kCommonSwitches),
+ capabilities.exclude_switches);
+
command.AppendSwitch("enable-logging");
command.AppendSwitchASCII("logging-level", "1");
- command.AppendArg("data:text/html;charset=utf-8,");
+ command.AppendSwitchASCII("password-store", "basic");
+ command.AppendSwitch("use-mock-keychain");
+ command.AppendSwitchASCII("remote-debugging-port", base::IntToString(port));
if (!command.HasSwitch("user-data-dir")) {
+ command.AppendArg("about:blank");
if (!user_data_dir->CreateUniqueTempDir())
return Status(kUnknownError, "cannot create temp dir for user data dir");
command.AppendSwitchPath("user-data-dir", user_data_dir->path());
@@ -156,8 +188,6 @@ Status LaunchDesktopChrome(
if (status.IsError())
return status;
- for (size_t i = 0; i < arraysize(kCommonSwitches); i++)
- command.AppendSwitch(kCommonSwitches[i]);
base::LaunchOptions options;
#if !defined(OS_WIN)
diff --git a/chrome/test/chromedriver/client/chromedriver.py b/chrome/test/chromedriver/client/chromedriver.py
index a0fe44a31c..ac5c478a45 100644
--- a/chrome/test/chromedriver/client/chromedriver.py
+++ b/chrome/test/chromedriver/client/chromedriver.py
@@ -217,6 +217,15 @@ class ChromeDriver(object):
def MouseDoubleClick(self, button=0):
self.ExecuteCommand(Command.MOUSE_DOUBLE_CLICK, {'button': button})
+ def TouchDown(self, x, y):
+ self.ExecuteCommand(Command.TOUCH_DOWN, {'x': x, 'y': y})
+
+ def TouchUp(self, x, y):
+ self.ExecuteCommand(Command.TOUCH_UP, {'x': x, 'y': y})
+
+ def TouchMove(self, x, y):
+ self.ExecuteCommand(Command.TOUCH_MOVE, {'x': x, 'y': y})
+
def GetCookies(self):
return self.ExecuteCommand(Command.GET_COOKIES)
diff --git a/chrome/test/chromedriver/client/webelement.py b/chrome/test/chromedriver/client/webelement.py
index a5a706aa35..b3e6a641f5 100644
--- a/chrome/test/chromedriver/client/webelement.py
+++ b/chrome/test/chromedriver/client/webelement.py
@@ -45,3 +45,6 @@ class WebElement(object):
for i in range(len(value)):
typing.append(value[i])
self._Execute(Command.SEND_KEYS_TO_ELEMENT, {'value': typing})
+
+ def GetLocation(self):
+ return self._Execute(Command.GET_ELEMENT_LOCATION)
diff --git a/chrome/test/chromedriver/element_commands.cc b/chrome/test/chromedriver/element_commands.cc
index 008c08644e..81e83a36dd 100644
--- a/chrome/test/chromedriver/element_commands.cc
+++ b/chrome/test/chromedriver/element_commands.cc
@@ -33,6 +33,7 @@ Status SendKeysToElement(
const std::string& element_id,
const ListValue* key_list) {
bool is_displayed = false;
+ bool is_focused = false;
base::Time start_time = base::Time::Now();
while (true) {
Status status = IsElementDisplayed(
@@ -41,25 +42,35 @@ Status SendKeysToElement(
return status;
if (is_displayed)
break;
+ status = IsElementFocused(session, web_view, element_id, &is_focused);
+ if (status.IsError())
+ return status;
+ if (is_focused)
+ break;
if ((base::Time::Now() - start_time).InMilliseconds() >=
session->implicit_wait) {
return Status(kElementNotVisible);
}
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
}
+
bool is_enabled = false;
Status status = IsElementEnabled(session, web_view, element_id, &is_enabled);
if (status.IsError())
return status;
if (!is_enabled)
return Status(kInvalidElementState);
- base::ListValue args;
- args.Append(CreateElement(element_id));
- scoped_ptr<base::Value> result;
- status = web_view->CallFunction(
- session->GetCurrentFrameId(), kFocusScript, args, &result);
- if (status.IsError())
- return status;
+
+ if (!is_focused) {
+ base::ListValue args;
+ args.Append(CreateElement(element_id));
+ scoped_ptr<base::Value> result;
+ status = web_view->CallFunction(
+ session->GetCurrentFrameId(), kFocusScript, args, &result);
+ if (status.IsError())
+ return status;
+ }
+
return SendKeysOnWindow(web_view, key_list, true, &session->sticky_modifiers);
}
diff --git a/chrome/test/chromedriver/element_util.cc b/chrome/test/chromedriver/element_util.cc
index ccf5409db3..6c4bca00c2 100644
--- a/chrome/test/chromedriver/element_util.cc
+++ b/chrome/test/chromedriver/element_util.cc
@@ -286,6 +286,32 @@ Status FindElement(
return Status(kUnknownError);
}
+Status GetActiveElement(
+ Session* session,
+ WebView* web_view,
+ scoped_ptr<base::Value>* value) {
+ base::ListValue args;
+ return web_view->CallFunction(
+ session->GetCurrentFrameId(),
+ "function() { return document.activeElement || document.body }",
+ args,
+ value);
+}
+
+Status IsElementFocused(
+ Session* session,
+ WebView* web_view,
+ const std::string& element_id,
+ bool* is_focused) {
+ scoped_ptr<base::Value> result;
+ Status status = GetActiveElement(session, web_view, &result);
+ if (status.IsError())
+ return status;
+ scoped_ptr<base::Value> element_dict(CreateElement(element_id));
+ *is_focused = result->Equals(element_dict.get());
+ return Status(kOk);
+}
+
Status GetElementAttribute(
Session* session,
WebView* web_view,
diff --git a/chrome/test/chromedriver/element_util.h b/chrome/test/chromedriver/element_util.h
index 86bb2d1568..0fbd766179 100644
--- a/chrome/test/chromedriver/element_util.h
+++ b/chrome/test/chromedriver/element_util.h
@@ -34,6 +34,17 @@ Status FindElement(
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value);
+Status GetActiveElement(
+ Session* session,
+ WebView* web_view,
+ scoped_ptr<base::Value>* value);
+
+Status IsElementFocused(
+ Session* session,
+ WebView* web_view,
+ const std::string& element_id,
+ bool* is_focused);
+
Status GetElementAttribute(
Session* session,
WebView* web_view,
diff --git a/chrome/test/chromedriver/run_buildbot_steps.py b/chrome/test/chromedriver/run_buildbot_steps.py
index af4a10b811..2b1765dbb3 100755
--- a/chrome/test/chromedriver/run_buildbot_steps.py
+++ b/chrome/test/chromedriver/run_buildbot_steps.py
@@ -178,18 +178,19 @@ def WaitForLatestSnapshot(revision):
def main():
parser = optparse.OptionParser()
parser.add_option(
- '', '--android-package',
- help='Application package name, if running tests on Android.')
+ '', '--android-packages',
+ help='Comma separated list of application package names, '
+ 'if running tests on Android.')
parser.add_option(
'-r', '--revision', type='string', default=None,
help='Chromium revision')
options, _ = parser.parse_args()
- if not options.android_package:
+ if not options.android_packages:
KillChromes()
CleanTmpDir()
- if options.android_package:
+ if options.android_packages:
Download()
else:
if not options.revision:
@@ -204,12 +205,12 @@ def main():
sys.executable,
os.path.join(_THIS_DIR, 'test', 'run_all_tests.py'),
]
- if options.android_package:
- cmd.append('--android-package=' + options.android_package)
+ if options.android_packages:
+ cmd.append('--android-packages=' + options.android_packages)
passed = (util.RunCommand(cmd) == 0)
- if not options.android_package and passed:
+ if not options.android_packages and passed:
MaybeRelease(options.revision)
diff --git a/chrome/test/chromedriver/server/chromedriver_server.cc b/chrome/test/chromedriver/server/chromedriver_server.cc
index c036814086..6c402474d8 100644
--- a/chrome/test/chromedriver/server/chromedriver_server.cc
+++ b/chrome/test/chromedriver/server/chromedriver_server.cc
@@ -139,7 +139,8 @@ void StartServerOnIOThread(int port,
lazy_tls_server.Pointer()->Set(temp_server.release());
}
-void RunServer(Log::Level log_level, int port, const std::string& url_base) {
+void RunServer(Log::Level log_level, int port, const std::string& url_base,
+ int adb_port) {
base::Thread io_thread("ChromeDriver IO");
CHECK(io_thread.StartWithOptions(
base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
@@ -150,7 +151,8 @@ void RunServer(Log::Level log_level, int port, const std::string& url_base) {
HttpHandler handler(cmd_run_loop.QuitClosure(),
io_thread.message_loop_proxy(),
&log,
- url_base);
+ url_base,
+ adb_port);
HttpRequestHandlerFunc handle_request_func =
base::Bind(&HandleRequestOnCmdThread, &handler);
@@ -181,6 +183,7 @@ int main(int argc, char *argv[]) {
// Parse command line flags.
int port = 9515;
+ int adb_port = 5037;
std::string url_base;
base::FilePath log_path;
Log::Level log_level = Log::kError;
@@ -188,6 +191,7 @@ int main(int argc, char *argv[]) {
std::string options;
const char* kOptionAndDescriptions[] = {
"port=PORT", "port to listen on",
+ "adb-port=PORT", "adb server port",
"log-path=FILE", "write server log to file instead of stderr, "
"increases log level to INFO",
"verbose", "log verbosely",
@@ -208,6 +212,13 @@ int main(int argc, char *argv[]) {
return 1;
}
}
+ if (cmd_line->HasSwitch("adb-port")) {
+ if (!base::StringToInt(cmd_line->GetSwitchValueASCII("adb-port"),
+ &adb_port)) {
+ printf("Invalid adb-port. Exiting...\n");
+ return 1;
+ }
+ }
if (cmd_line->HasSwitch("url-base"))
url_base = cmd_line->GetSwitchValueASCII("url-base");
if (url_base.empty() || url_base[0] != '/')
@@ -255,6 +266,6 @@ int main(int argc, char *argv[]) {
if (!cmd_line->HasSwitch("verbose"))
logging::SetMinLogLevel(logging::LOG_FATAL);
- RunServer(log_level, port, url_base);
+ RunServer(log_level, port, url_base, adb_port);
return 0;
}
diff --git a/chrome/test/chromedriver/server/http_handler.cc b/chrome/test/chromedriver/server/http_handler.cc
index e619ce7f68..1211bbcc26 100644
--- a/chrome/test/chromedriver/server/http_handler.cc
+++ b/chrome/test/chromedriver/server/http_handler.cc
@@ -67,7 +67,8 @@ HttpHandler::HttpHandler(
const base::Closure& quit_func,
const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
Log* log,
- const std::string& url_base)
+ const std::string& url_base,
+ int adb_port)
: quit_func_(quit_func),
log_(log),
url_base_(url_base),
@@ -78,7 +79,7 @@ HttpHandler::HttpHandler(
#endif
context_getter_ = new URLRequestContextGetter(io_task_runner);
socket_factory_ = CreateSyncWebSocketFactory(context_getter_.get());
- adb_.reset(new AdbImpl(io_task_runner, log_));
+ adb_.reset(new AdbImpl(io_task_runner, log_, adb_port));
device_manager_.reset(new DeviceManager(adb_.get()));
CommandMapping commands[] = {
@@ -395,13 +396,13 @@ HttpHandler::HttpHandler(
WrapToCommand(base::Bind(&ExecuteTouchSingleTap))),
CommandMapping(kPost,
"session/:sessionId/touch/down",
- base::Bind(&UnimplementedCommand)),
+ WrapToCommand(base::Bind(&ExecuteTouchDown))),
CommandMapping(kPost,
"session/:sessionId/touch/up",
- base::Bind(&UnimplementedCommand)),
+ WrapToCommand(base::Bind(&ExecuteTouchUp))),
CommandMapping(kPost,
"session/:sessionId/touch/move",
- base::Bind(&UnimplementedCommand)),
+ WrapToCommand(base::Bind(&ExecuteTouchMove))),
CommandMapping(kPost,
"session/:sessionId/touch/scroll",
base::Bind(&UnimplementedCommand)),
diff --git a/chrome/test/chromedriver/server/http_handler.h b/chrome/test/chromedriver/server/http_handler.h
index 733255489e..13298311dd 100644
--- a/chrome/test/chromedriver/server/http_handler.h
+++ b/chrome/test/chromedriver/server/http_handler.h
@@ -64,7 +64,8 @@ class HttpHandler {
HttpHandler(const base::Closure& quit_func,
const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
Log* log,
- const std::string& url_base);
+ const std::string& url_base,
+ int adb_port);
~HttpHandler();
void Handle(const net::HttpServerRequestInfo& request,
diff --git a/chrome/test/chromedriver/test/run_all_tests.py b/chrome/test/chromedriver/test/run_all_tests.py
index 7236c86b83..d8d6852895 100755
--- a/chrome/test/chromedriver/test/run_all_tests.py
+++ b/chrome/test/chromedriver/test/run_all_tests.py
@@ -65,7 +65,7 @@ def RunPythonTests(chromedriver, ref_chromedriver,
chrome_version_name=None, android_package=None):
version_info = ''
if chrome_version_name:
- version_info = '(v%s)' % chrome_version_name
+ version_info = '(%s)' % chrome_version_name
util.MarkBuildStepStart('python_tests%s' % version_info)
code = util.RunCommand(
_GenerateTestCommand('run_py_tests.py',
@@ -83,7 +83,7 @@ def RunJavaTests(chromedriver, chrome=None, chrome_version=None,
chrome_version_name=None, android_package=None):
version_info = ''
if chrome_version_name:
- version_info = '(v%s)' % chrome_version_name
+ version_info = '(%s)' % chrome_version_name
util.MarkBuildStepStart('java_tests%s' % version_info)
code = util.RunCommand(
_GenerateTestCommand('run_java_tests.py',
@@ -113,8 +113,9 @@ def DownloadChrome(version_name, revision, download_site):
def main():
parser = optparse.OptionParser()
parser.add_option(
- '', '--android-package',
- help='Application package name, if running tests on Android.')
+ '', '--android-packages',
+ help='Comma separated list of application package names, '
+ 'if running tests on Android.')
# Option 'chrome-version' is for desktop only.
parser.add_option(
'', '--chrome-version',
@@ -130,7 +131,7 @@ def main():
server_name = 'chromedriver2_server' + exe_postfix
required_build_outputs = [server_name]
- if not options.android_package:
+ if not options.android_packages:
required_build_outputs += [cpp_tests_name]
build_dir = chrome_paths.GetBuildDir(required_build_outputs)
print 'Using build outputs from', build_dir
@@ -153,15 +154,20 @@ def main():
# For Windows bots: add ant, java(jre) and the like to system path.
_AddToolsToSystemPathForWindows()
- if options.android_package:
+ if options.android_packages:
os.environ['PATH'] += os.pathsep + os.path.join(
_THIS_DIR, os.pardir, 'chrome')
- code1 = RunPythonTests(chromedriver,
- ref_chromedriver,
- android_package=options.android_package)
- code2 = RunJavaTests(chromedriver,
- android_package=options.android_package)
- return code1 or code2
+ code = 0
+ for package in options.android_packages.split(','):
+ code1 = RunPythonTests(chromedriver,
+ ref_chromedriver,
+ chrome_version_name=package,
+ android_package=package)
+ code2 = RunJavaTests(chromedriver,
+ chrome_version_name=package,
+ android_package=package)
+ code = code or code1 or code2
+ return code
else:
latest_snapshot_revision = archive.GetLatestRevision(archive.Site.SNAPSHOT)
versions = [
@@ -184,10 +190,10 @@ def main():
ref_chromedriver,
chrome=chrome_path,
chrome_version=version[0],
- chrome_version_name=version_name)
+ chrome_version_name='v%s' % version_name)
code2 = RunJavaTests(chromedriver, chrome=chrome_path,
chrome_version=version[0],
- chrome_version_name=version_name)
+ chrome_version_name='v%s' % version_name)
code = code or code1 or code2
cpp_tests = os.path.join(build_dir, cpp_tests_name)
return RunCppTests(cpp_tests) or code
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 2a19c6c655..c347ce2cf9 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -35,6 +35,15 @@ if util.IsLinux():
from pylib import valgrind_tools
+_NEGATIVE_FILTER = {}
+_NEGATIVE_FILTER['HEAD'] = [
+ # https://code.google.com/p/chromedriver/issues/detail?id=213
+ 'ChromeDriverTest.testClickElementInSubFrame',
+ # This test is flaky since it uses setTimeout.
+ # Re-enable once crbug.com/177511 is fixed and we can remove setTimeout.
+ 'ChromeDriverTest.testAlert',
+]
+
_DESKTOP_OS_SPECIFIC_FILTER = []
if util.IsWindows():
_DESKTOP_OS_SPECIFIC_FILTER = [
@@ -60,14 +69,12 @@ elif util.IsMac():
_DESKTOP_NEGATIVE_FILTER = {}
_DESKTOP_NEGATIVE_FILTER['HEAD'] = (
+ _NEGATIVE_FILTER['HEAD'] +
_DESKTOP_OS_SPECIFIC_FILTER + [
- # https://code.google.com/p/chromedriver/issues/detail?id=213
- 'ChromeDriverTest.testClickElementInSubFrame',
- # This test is flaky since it uses setTimeout.
- # Re-enable once crbug.com/177511 is fixed and we can remove setTimeout.
- 'ChromeDriverTest.testAlert',
- # Desktop doesn't support TAP.
+ # Desktop doesn't support touch (without --touch-events).
'ChromeDriverTest.testSingleTapElement',
+ 'ChromeDriverTest.testTouchDownUpElement',
+ 'ChromeDriverTest.testTouchMovedElement',
]
)
@@ -80,15 +87,14 @@ def _GetDesktopNegativeFilter(version_name):
_ANDROID_NEGATIVE_FILTER = {}
_ANDROID_NEGATIVE_FILTER['com.google.android.apps.chrome'] = (
- _DESKTOP_NEGATIVE_FILTER['HEAD'] + [
+ _NEGATIVE_FILTER['HEAD'] + [
# Android doesn't support switches and extensions.
'ChromeSwitchesCapabilityTest.*',
'ChromeExtensionsCapabilityTest.*',
- # https://code.google.com/p/chromedriver/issues/detail?id=262
- 'ChromeDriverTest.testCloseWindow',
- 'ChromeDriverTest.testGetWindowHandles',
- 'ChromeDriverTest.testSwitchToWindow',
+ # 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
@@ -104,8 +110,21 @@ _ANDROID_NEGATIVE_FILTER['com.google.android.apps.chrome'] = (
'PerfTest.testColdExecuteScript',
]
)
+_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.chrome.beta'] = (
+ _ANDROID_NEGATIVE_FILTER['com.google.android.apps.chrome'])
_ANDROID_NEGATIVE_FILTER['org.chromium.chrome.testshell'] = (
- _ANDROID_NEGATIVE_FILTER['com.google.android.apps.chrome'] + []
+ _ANDROID_NEGATIVE_FILTER['com.google.android.apps.chrome'] + [
+ # ChromiumTestShell doesn't support multiple tabs.
+ 'ChromeDriverTest.testGetWindowHandles',
+ 'ChromeDriverTest.testSwitchToWindow',
+ ]
)
@@ -353,7 +372,6 @@ class ChromeDriverTest(ChromeDriverBaseTest):
'document.body.innerHTML = "<div>old</div>";'
'var div = document.getElementsByTagName("div")[0];'
'div.addEventListener("click", function() {'
- ' var div = document.getElementsByTagName("div")[0];'
' div.innerHTML="new<br>";'
'});'
'return div;')
@@ -364,14 +382,40 @@ class ChromeDriverTest(ChromeDriverBaseTest):
div = self._driver.ExecuteScript(
'document.body.innerHTML = "<div>old</div>";'
'var div = document.getElementsByTagName("div")[0];'
- 'div.addEventListener("click", function() {'
- ' var div = document.getElementsByTagName("div")[0];'
+ 'div.addEventListener("touchend", function() {'
' div.innerHTML="new<br>";'
'});'
'return div;')
div.SingleTap()
self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
+ def testTouchDownUpElement(self):
+ div = self._driver.ExecuteScript(
+ 'document.body.innerHTML = "<div>old</div>";'
+ 'var div = document.getElementsByTagName("div")[0];'
+ 'div.addEventListener("touchend", function() {'
+ ' div.innerHTML="new<br>";'
+ '});'
+ 'return div;')
+ loc = div.GetLocation()
+ self._driver.TouchDown(loc['x'], loc['y'])
+ self._driver.TouchUp(loc['x'], loc['y'])
+ self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
+
+ def testTouchMovedElement(self):
+ div = self._driver.ExecuteScript(
+ 'document.body.innerHTML = "<div>old</div>";'
+ 'var div = document.getElementsByTagName("div")[0];'
+ 'div.addEventListener("touchmove", function() {'
+ ' div.innerHTML="new<br>";'
+ '});'
+ 'return div;')
+ loc = div.GetLocation()
+ self._driver.TouchDown(loc['x'], loc['y'])
+ self._driver.TouchMove(loc['x'] + 1, loc['y'] + 1)
+ self._driver.TouchUp(loc['x'] + 1, loc['y'] + 1)
+ self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
+
def testClickElementInSubFrame(self):
self._driver.Load(self.GetHttpUrlForFile('/chromedriver/frame_test.html'))
frame = self._driver.FindElement('tag name', 'iframe')
@@ -404,7 +448,7 @@ class ChromeDriverTest(ChromeDriverBaseTest):
self.assertEquals('0123456789+-*/ Hi, there!', value)
def testGetCurrentUrl(self):
- self.assertTrue('data:' in self._driver.GetCurrentUrl())
+ self.assertTrue('about:blank' in self._driver.GetCurrentUrl())
def testGoBackAndGoForward(self):
self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations
index b63c41e279..80cbf0083c 100644
--- a/chrome/test/chromedriver/test/test_expectations
+++ b/chrome/test/chromedriver/test/test_expectations
@@ -161,10 +161,11 @@ _OS_NEGATIVE_FILTER['android'] = [
# http://crbug.com/156390
'DragAndDropTest.*',
- # Touch events are not yet supported.
+ # Flick touch events are not yet implemented.
'TouchFlickTest.*',
+
+ # Scrolling touch events are not supported.
'TouchScrollTest.*',
- 'TouchSingleTapTest.*',
# These tests start multiple sessions, which is not supported on a single
# Android device.
diff --git a/chrome/test/chromedriver/window_commands.cc b/chrome/test/chromedriver/window_commands.cc
index cec7479624..ad62a9ef54 100644
--- a/chrome/test/chromedriver/window_commands.cc
+++ b/chrome/test/chromedriver/window_commands.cc
@@ -5,6 +5,7 @@
#include "chrome/test/chromedriver/window_commands.h"
#include <list>
+#include <string>
#include "base/callback.h"
#include "base/strings/string_number_conversions.h"
@@ -121,6 +122,67 @@ Status GetVisibleCookies(WebView* web_view,
return Status(kOk);
}
+Status ScrollCoordinateInToView(
+ Session* session, WebView* web_view, int x, int y, int* offset_x,
+ int* offset_y) {
+ scoped_ptr<base::Value> value;
+ base::ListValue args;
+ args.AppendInteger(x);
+ args.AppendInteger(y);
+ Status status = web_view->CallFunction(
+ std::string(),
+ "function(x, y) {"
+ " if (x < window.pageXOffset ||"
+ " x >= window.pageXOffset + window.innerWidth ||"
+ " y < window.pageYOffset ||"
+ " y >= window.pageYOffset + window.innerHeight) {"
+ " window.scrollTo(x - window.innerWidth/2, y - window.innerHeight/2);"
+ " }"
+ " return {"
+ " view_x: Math.floor(window.pageXOffset),"
+ " view_y: Math.floor(window.pageYOffset),"
+ " view_width: Math.floor(window.innerWidth),"
+ " view_height: Math.floor(window.innerHeight)};"
+ "}",
+ args,
+ &value);
+ if (!status.IsOk())
+ return status;
+ base::DictionaryValue* view_attrib;
+ value->GetAsDictionary(&view_attrib);
+ int view_x, view_y, view_width, view_height;
+ view_attrib->GetInteger("view_x", &view_x);
+ view_attrib->GetInteger("view_y", &view_y);
+ view_attrib->GetInteger("view_width", &view_width);
+ view_attrib->GetInteger("view_height", &view_height);
+ *offset_x = x - view_x;
+ *offset_y = y - view_y;
+ if (*offset_x < 0 || *offset_x >= view_width || *offset_y < 0 ||
+ *offset_y >= view_height)
+ return Status(kUnknownError, "Failed to scroll coordinate into view");
+ return Status(kOk);
+}
+
+Status ExecuteTouchEvent(
+ Session* session, WebView* web_view, TouchEventType type,
+ const base::DictionaryValue& params) {
+ int x, y;
+ if (!params.GetInteger("x", &x))
+ return Status(kUnknownError, "'x' must be an integer");
+ if (!params.GetInteger("y", &y))
+ return Status(kUnknownError, "'y' must be an integer");
+ int relative_x = x;
+ int relative_y = y;
+ Status status = ScrollCoordinateInToView(
+ session, web_view, x, y, &relative_x, &relative_y);
+ if (!status.IsOk())
+ return status;
+ std::list<TouchEvent> events;
+ events.push_back(
+ TouchEvent(type, relative_x, relative_y));
+ return web_view->DispatchTouchEvents(events);
+}
+
} // namespace
Status ExecuteWindowCommand(
@@ -499,17 +561,36 @@ Status ExecuteMouseDoubleClick(
return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
}
+Status ExecuteTouchDown(
+ Session* session,
+ WebView* web_view,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value) {
+ return ExecuteTouchEvent(session, web_view, kTouchStart, params);
+}
+
+Status ExecuteTouchUp(
+ Session* session,
+ WebView* web_view,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value) {
+ return ExecuteTouchEvent(session, web_view, kTouchEnd, params);
+}
+
+Status ExecuteTouchMove(
+ Session* session,
+ WebView* web_view,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value) {
+ return ExecuteTouchEvent(session, web_view, kTouchMove, params);
+}
+
Status ExecuteGetActiveElement(
Session* session,
WebView* web_view,
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value) {
- base::ListValue args;
- return web_view->CallFunction(
- session->GetCurrentFrameId(),
- "function() { return document.activeElement || document.body }",
- args,
- value);
+ return GetActiveElement(session, web_view, value);
}
Status ExecuteSendKeysToActiveElement(
diff --git a/chrome/test/chromedriver/window_commands.h b/chrome/test/chromedriver/window_commands.h
index 8d0ee64969..017813628b 100644
--- a/chrome/test/chromedriver/window_commands.h
+++ b/chrome/test/chromedriver/window_commands.h
@@ -153,6 +153,27 @@ Status ExecuteMouseDoubleClick(
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value);
+// Touch press at a given coordinate.
+Status ExecuteTouchDown(
+ Session* session,
+ WebView* web_view,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value);
+
+// Touch release at a given coordinate.
+Status ExecuteTouchUp(
+ Session* session,
+ WebView* web_view,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value);
+
+// Touch move at a given coordinate.
+Status ExecuteTouchMove(
+ Session* session,
+ WebView* web_view,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value);
+
Status ExecuteGetActiveElement(
Session* session,
WebView* web_view,
diff --git a/chrome/test/functional/PYAUTO_TESTS b/chrome/test/functional/PYAUTO_TESTS
index 69167c5401..9cac8a97ec 100644
--- a/chrome/test/functional/PYAUTO_TESTS
+++ b/chrome/test/functional/PYAUTO_TESTS
@@ -472,50 +472,6 @@
],
},
- # WebRTC MediaStream tests. Requires webcam and audio device to be present on
- # the test machine.
- 'WEBRTC': {
- 'all': [
- 'media_stream_infobar',
- 'webrtc_brutality_test',
- 'webrtc_call',
-
- # ==================================================
- # Disabled tests that need to be investigated/fixed.
- # ==================================================
- # crbug.com/248079
- '-webrtc_brutality_test.WebrtcBrutalityTest.testReloadsAfterGetUserMedia',
- ],
- },
-
- # WebRTC / AppRTC Integration tests.
- 'WEBRTC_APPRTC': {
- 'all': [
- 'webrtc_apprtc_call',
- # ==================================================
- # Disabled tests that need to be investigated/fixed.
- # ==================================================
- # crbug.com/254412
- '-webrtc_apprtc_call.WebrtcApprtcCallTest.testApprtcTabToTabCall',
- '-webrtc_apprtc_call.WebrtcApprtcCallTest.testApprtcLoopbackCall',
- ],
- },
-
- # WebRTC quality tests. The video quality test requires a webcam, an audio
- # device and a special setup where the webcam plays a barcode-encoded video.
- # The webrtc_audio_quality test requires additional configuration as described
- # in the test's class comments.
- 'WEBRTC_QUALITY': {
- 'all': [
- 'webrtc_video_quality',
- # Disabled until crbug.com/254437 is resolved.
- '-webrtc_audio_quality',
- ],
- 'mac': [
- '-webrtc_audio_quality', # Not implemented.
- ],
- },
-
# Trace event tests.
'TRACING': {
'all': [
diff --git a/chrome/test/functional/ispy/ispy_core/app.yaml b/chrome/test/functional/ispy/ispy_core/app.yaml
new file mode 100644
index 0000000000..cbed5f9011
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/app.yaml
@@ -0,0 +1,17 @@
+application: google.com:ispy
+version: 1
+runtime: python27
+api_version: 1
+threadsafe: True
+
+handlers:
+- url: /.*
+ script: main.application
+
+libraries:
+- name: webapp2
+ version: latest
+- name: jinja2
+ version: latest
+- name: PIL
+ version: latest
diff --git a/chrome/test/functional/ispy/ispy_core/handlers/__init__.py b/chrome/test/functional/ispy/ispy_core/handlers/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/handlers/__init__.py
diff --git a/chrome/test/functional/ispy/ispy_core/handlers/command_handler.py b/chrome/test/functional/ispy/ispy_core/handlers/command_handler.py
new file mode 100644
index 0000000000..fb625ee80e
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/handlers/command_handler.py
@@ -0,0 +1,357 @@
+# 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 restful interaction with tests and runs."""
+
+import json
+import os
+import sys
+import webapp2
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
+
+from tests.rendering_test_manager import cloud_bucket
+from tests.rendering_test_manager import cloud_bucket_impl
+from tools import image_tools
+from tools import rendering_test_manager
+
+import ispy_auth_constants
+
+class MissingArgError(Exception):
+ pass
+
+
+def Command(*expected_parameters):
+ """Convenient decorator for Command functions the CommandHandler.
+
+ Args:
+ *expected_parameters: the expected parameters for the command.
+
+ Returns:
+ A decorated method that accepts a request object and returns nothing.
+ """
+ def _Decorate(method):
+ def _Wrapper(self, request):
+ # Try accessing all expected_parameters, if any are absent from the
+ # request raise MissingArgError.
+ try:
+ for param in expected_parameters:
+ if not request.get(param):
+ raise MissingArgError('Argument: missing %s' % param)
+ # If a MissingArgError was raised, write an error response.
+ except MissingArgError, e:
+ self.response.headers['Content-Type'] = 'application/json'
+ self.response.out.write(json.dumps({'success': False, 'error': str(e)}))
+ else:
+ # If all parameters are present run the decorated method.
+ results = method(self, request)
+ self.response.headers['Content-Type'] = 'application/json'
+ self.response.out.write(json.dumps(results))
+ return _Wrapper
+ return _Decorate
+
+
+class CommandHandler(webapp2.RequestHandler):
+ """Command handler that allows restful interaction with tests and runs."""
+
+ def __init__(self, *args, **kwargs):
+ super(CommandHandler, self).__init__(*args, **kwargs)
+ self.command_functions = {
+ 'upload_test': self._UploadTest,
+ 'test_exists': self._TestExists,
+ 'failure_exists': self._FailureExists,
+ 'run_test': self._RunTest,
+ 'upload_test_pink_out': self._UploadTestPinkOut,
+ 'remove_test': self._RemoveTest,
+ 'remove_failure': self._RemoveFailure,
+ 'add_to_test_mask': self._AddToTestMask}
+
+ def post(self):
+ """Handles post requests.
+
+ This method accepts a post request that minimally has a 'command' parameter
+ which indicates which of the handler's commands to run. If the
+ command is not in the valid_commands dictionary, then an error
+ will be returned. All responses are json-encoded objects that
+ have either a Succeeded or Failed parameter indicating success or
+ failure in addition to other returned parameters specific to the command.
+ """
+ cmd = self.request.get('command')
+ if not cmd:
+ self._Error(self.request)
+ return
+ if cmd not in self.command_functions:
+ self._InvalidCommand(self.request)
+ return
+ self.bucket = cloud_bucket_impl.CloudBucketImpl(
+ ispy_auth_constants.KEY, ispy_auth_constants.SECRET,
+ ispy_auth_constants.BUCKET)
+ self.manager = rendering_test_manager.RenderingTestManager(self.bucket)
+ self.command_functions.get(cmd)(self.request)
+
+ @Command()
+ def _Error(self, request):
+ """This command returns an error when no command is given.
+
+ This command has no expected parameters.
+
+ Args:
+ request: A request object.
+
+ Returns:
+ A dictionary indicating a failure, and an error message indicating
+ that no command was specified.
+ """
+ return {'success': False, 'error': 'No command was specified.'}
+
+ @Command()
+ def _InvalidCommand(self, request):
+ """This command returns an error when an invalid command is given.
+
+ This command has no expected parameters.
+
+ Args:
+ request: a request object.
+
+ Returns:
+ A dictionary indicating a failure, and an error message
+ containing all possible valid commands.
+ """
+ return {'success': False,
+ 'error': 'Invalid command. Valid commands are: %s.' %
+ ' '.join(self.command_functions.keys())}
+
+ @Command('batch_name', 'test_name', 'images')
+ def _UploadTest(self, request):
+ """Uploads an ispy-test to GCS.
+
+ This function uploads a collection of images as a 'test' to the
+ ispy server. A mask is then computed from these images, and the
+ first image in the images list and the mask are stored in the
+ GCS as an ispy-test.
+
+ This function is called for the command 'upload_test'.
+ Request Parameters:
+ test_name: The name of the test to be uploaded.
+ images: a json encoded list of base64 encoded png images.
+ Response JSON:
+ succeeded: True.
+
+ Args:
+ request: a request object.
+
+ Returns:
+ A dictionary indicating success or failure.
+ """
+ batch_name = request.get('batch_name')
+ test_name = request.get('test_name')
+ raw_images = request.get('images')
+ images = json.loads(raw_images)
+ self.manager.UploadTest(
+ batch_name, test_name,
+ [image_tools.DeserializeImage(image) for image in images])
+ return {'success': True}
+
+ @Command('batch_name', 'test_name', 'images', 'pink_out', 'RGB')
+ def _UploadTestPinkOut(self, request):
+ """Uploads an ispy-test to GCS with the pink_out workaround.
+
+ This function is called for the command 'upload_test_pink_out'.
+ Request Parameters:
+ test_name: The name of the test to be uploaded.
+ images: a json encoded list of base64 encoded png images.
+ pink_out: a base64 encoded png image.
+ RGB: a json list representing the RGB values of a color to mask out.
+ Response JSON:
+ succeeded: True.
+
+ Args:
+ request: a request object.
+
+ Returns:
+ A dictionary indicating success or failure.
+ """
+ batch_name = request.get('batch_name')
+ test_name = request.get('test_name')
+ rgb = json.loads(request.get('RGB'))
+ images = [image_tools.DeserializeImage(i)
+ for i in json.loads(request.get('images'))]
+ pink_out = image_tools.DeserializeImage(request.get('pink_out'))
+ # 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])
+ path = 'tests/%s/%s/' % (batch_name, test_name)
+ self.manager.UploadImage(path + 'expected.png',
+ images[0])
+ self.manager.UploadImage(path + 'mask.png',
+ combined_mask)
+ return {'success': True}
+
+ @Command('mask', 'batch_name', 'test_name')
+ def _AddToTestMask(self, request):
+ """Adds a another mask image to the existing mask for a given test.
+
+ Request Parameters:
+ mask: A RGBA png white/black mask image.
+ test_name: The name of a test in i-spy to add to.
+ Response JSON:
+ succeeded: True
+ Response Error:
+ error: if the test was not found in GCS.
+
+ Args:
+ request: A request object.
+
+ Returns:
+ A dictionary indicating success or failure.
+ """
+ batch_name = request.get('batch_name')
+ test_name = request.get('test_name')
+ mask_to_be_added = image_tools.InflateMask(
+ image_tools.DeserializeImage(request.get('mask')), 7)
+ if not self.manager.TestExists(batch_name, test_name):
+ return {'success': False, 'error': 'Test does not exist.'}
+ path = 'tests/%s/%s/mask.png' % (batch_name, test_name)
+ test_mask = self.manager.DownloadImage(path)
+ combined_mask = image_tools.AddMasks([test_mask, mask_to_be_added])
+ self.manager.UploadImage(path, combined_mask)
+ return {'success': True}
+
+ @Command('image', 'batch_name', 'test_name')
+ def _RunTest(self, request):
+ """Runs a test on GCS and stores a failure if it doesn't pass.
+
+ This method compares the submitted image with the expected and mask
+ images stored in GCS under the submitted test name. If there are
+ any differences between the submitted image and the expected test
+ image (with respect to the mask), the submitted image is stored
+ as a failure in GCS.
+
+ This method is run if the command parameter is set to 'run_test'.
+ Request Parameters:
+ 'image': A base64 encoded screenshot that corresponds to a test in GCS.
+ 'test_name': The name of the test that 'image' corresponds to.
+ Response JSON:
+ 'succeeded': True
+
+ Args:
+ request: a request object
+
+ Returns:
+ A dictionary indicating success or failure.
+ """
+ raw_image = request.get('image')
+ batch_name = request.get('batch_name')
+ test_name = request.get('test_name')
+ image = image_tools.DeserializeImage(raw_image)
+ try:
+ self.manager.RunTest(batch_name, test_name, image)
+ except cloud_bucket.FileNotFoundError, e:
+ return {'success': False, 'error': str(e)}
+ else:
+ return {'success': True}
+
+ @Command('batch_name', 'test_name')
+ def _TestExists(self, request):
+ """Checks to see if a test exists in GCS.
+
+ This method confirms whether or not an expected image, and
+ mask exist under the given test_name in GCS. Returning true
+ only if all components of a test exist.
+
+ This method is run if the command parameter is 'test_exists'.
+ Request Parameters:
+ 'test_name': The name of the test to look for.
+ Response JSON:
+ 'exists': boolean indicating whether the test exists.
+ 'succeeded': True.
+
+ Args:
+ request: a request object.
+
+ Returns:
+ A dictionary indicating success or failure, and if the command was
+ successful, whether the test exists.
+ """
+ batch_name = request.get('batch_name')
+ test_name = request.get('test_name')
+ return {'success': True, 'exists': self.manager.TestExists(batch_name,
+ test_name)}
+
+ @Command('batch_name', 'test_name')
+ def _FailureExists(self, request):
+ """Checks to see if a particular failed run exists in GCS.
+
+ This method is run if the command parameter is 'failure_exists'.
+ Request Parameters:
+ 'test_name': the name of the test that failure occurred on.
+ Response JSON:
+ 'exists': boolean indicating whether the failure exists.
+ 'succeeded': True.
+
+ Args:
+ request: a request object.
+
+ Returns:
+ A dictionary indicating success or failure, and if the command was
+ successful, whether the failed run exists.
+ """
+ batch_name = request.get('batch_name')
+ test_name = request.get('test_name')
+ return {'success': True,
+ 'exists': self.manager.FailureExists(
+ batch_name, test_name)}
+
+ @Command('batch_name', 'test_name')
+ def _RemoveTest(self, request):
+ """Removes a test and associated runs from GCS.
+
+ This method will locate all files in ispy's GCS datastore
+ associated with the given test_name, and remove them from GCS.
+ This includes the test's mask, expected image, and all failures
+ on the given test.
+
+ This method is run if the command parameter is 'remove_test'.
+ Request Parameters:
+ 'test_name': the name of the test to remove.
+ Response JSON:
+ succeeded: True.
+
+ Args:
+ request: a request object.
+
+ Returns:
+ A dictionary indicating success or failure.
+ """
+ batch_name = request.get('batch_name')
+ test_name = request.get('test_name')
+ self.manager.RemoveTest(batch_name, test_name)
+ return {'success': True}
+
+ @Command('batch_name', 'test_name')
+ def _RemoveFailure(self, request):
+ """Removes a failure from GCS.
+
+ This method is run if the command parameter is 'remove_failure'.
+ Request Parameters:
+ 'test_name': the name of the test in which the failure occurred.
+ Response JSON:
+ succeeded: True.
+
+ Args:
+ request: a request object.
+
+ Returns:
+ A dictionary indicating success or failure.
+ """
+ batch_name = request.get('batch_name')
+ test_name = request.get('test_name')
+ self.manager.RemoveFailure(batch_name, test_name)
+ return {'success': True}
diff --git a/chrome/test/functional/ispy/ispy_core/handlers/debug_view_handler.py b/chrome/test/functional/ispy/ispy_core/handlers/debug_view_handler.py
new file mode 100644
index 0000000000..6ef4e9cd64
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/handlers/debug_view_handler.py
@@ -0,0 +1,43 @@
+# 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
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
+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:
+ diff: The path to the diff image on a failure.
+ expected: The path to the expected image on a failure.
+ actual: The path to the actual image on a failure.
+ """
+ diff_path = self.request.get('diff')
+ expected_path = self.request.get('expected')
+ actual_path = self.request.get('actual')
+ data = {}
+
+ def _Encode(url):
+ return '/image?file_path=%s' % url
+
+ data['diff'] = _Encode(diff_path)
+ data['expected'] = _Encode(expected_path)
+ data['actual'] = _Encode(actual_path)
+ template = JINJA.get_template('debug_view.html')
+ self.response.write(template.render(data))
diff --git a/chrome/test/functional/ispy/ispy_core/handlers/image_handler.py b/chrome/test/functional/ispy/ispy_core/handlers/image_handler.py
new file mode 100644
index 0000000000..331519666f
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/handlers/image_handler.py
@@ -0,0 +1,42 @@
+# 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 ispy.googleplex.com."""
+
+import json
+import os
+import sys
+import webapp2
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
+
+from tests.rendering_test_manager import cloud_bucket
+from tests.rendering_test_manager import cloud_bucket_impl
+
+import ispy_auth_constants
+
+
+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 = cloud_bucket_impl.CloudBucketImpl(
+ ispy_auth_constants.KEY, ispy_auth_constants.SECRET,
+ ispy_auth_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/ispy_core/handlers/main_view_handler.py b/chrome/test/functional/ispy/ispy_core/handlers/main_view_handler.py
new file mode 100644
index 0000000000..032cbab17a
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/handlers/main_view_handler.py
@@ -0,0 +1,117 @@
+# 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
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
+
+import views
+
+from tests.rendering_test_manager import cloud_bucket_impl
+from tools import rendering_test_manager
+
+import ispy_auth_constants
+
+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 batch_name parameter is specified, then a page displaying all of
+ the failed runs in the batch will be shown. Otherwise a view listing
+ all of the batches available for viewing will be displayed.
+ """
+ # Get parameters.
+ batch_name = self.request.get('batch_name')
+ # Set up rendering test manager.
+ bucket = cloud_bucket_impl.CloudBucketImpl(
+ ispy_auth_constants.KEY, ispy_auth_constants.SECRET,
+ ispy_auth_constants.BUCKET)
+ manager = rendering_test_manager.RenderingTestManager(bucket)
+ # Load the view.
+ if batch_name:
+ self._GetForBatch(batch_name, manager)
+ return
+ self._GetAllBatches(manager)
+
+ def _GetAllBatches(self, manager):
+ """Renders a list view of all of the batches available in GCS.
+
+ Args:
+ manager: An instance of rendering_test_manager.RenderingTestManager.
+ """
+ template = JINJA.get_template('list_view.html')
+ data = {}
+ batches = set([re.match(r'^tests/([^/]+)/.+$', path).groups()[0]
+ for path in manager.GetAllPaths('tests/')])
+ base_url = '/?batch_name=%s'
+ data['links'] = [(batch, base_url % batch) for batch in batches]
+ self.response.write(template.render(data))
+
+ def _GetForBatch(self, batch_name, manager):
+ """Renders a sorted list of failure-rows for a given batch_name.
+
+ This method will produce a list of failure-rows that are sorted
+ in descending order by number of different pixels.
+
+ Args:
+ batch_name: The name of the batch to render failure rows from.
+ manager: An instance of rendering_test_manager.RenderingTestManager.
+ """
+ template = JINJA.get_template('main_view.html')
+ paths = set([path for path in manager.GetAllPaths('failures/' + batch_name)
+ if path.endswith('actual.png')])
+ rows = [self._CreateRow(batch_name, path, manager)
+ for path in paths]
+ # Function that sorts by the different_pixels field in the failure-info.
+ def _Sorter(x, y):
+ return cmp(y['info']['different_pixels'],
+ x['info']['different_pixels'])
+ self.response.write(template.render({'comparisons': sorted(rows, _Sorter)}))
+
+ def _CreateRow(self, batch_name, path, manager):
+ """Creates one failure-row.
+
+ This method builds a dictionary with the data necessary to display a
+ failure in the main_view html template.
+
+ Args:
+ batch_name: The name of the batch the failure is in.
+ path: A path to the failure's actual.png file.
+ manager: An instance of rendering_test_manager.RenderingTestManager.
+
+ Returns:
+ A dictionary with fields necessary to render a failure-row
+ in the main_view html template.
+ """
+ res = {}
+ pattern = r'^failures/%s/([^/]+)/.+$' % batch_name
+ res['test_name'] = re.match(
+ pattern, path).groups()[0]
+ res['batch_name'] = batch_name
+ res['info'] = json.loads(manager.cloud_bucket.DownloadFile(
+ '/failures/%s/%s/info.txt' % (res['batch_name'], res['test_name'])))
+ expected = 'tests/%s/%s/expected.png' % (batch_name, res['test_name'])
+ diff = 'failures/%s/%s/diff.png' % (batch_name, res['test_name'])
+ res['expected_path'] = expected
+ res['diff_path'] = diff
+ res['actual_path'] = path
+ res['expected'] = manager.cloud_bucket.GetURL(expected)
+ res['diff'] = manager.cloud_bucket.GetURL(diff)
+ res['actual'] = manager.cloud_bucket.GetURL(path)
+ return res
+
diff --git a/chrome/test/functional/ispy/ispy_core/handlers/update_mask_handler.py b/chrome/test/functional/ispy/ispy_core/handlers/update_mask_handler.py
new file mode 100644
index 0000000000..c17d5d2a92
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/handlers/update_mask_handler.py
@@ -0,0 +1,64 @@
+# 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
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
+from tools import rendering_test_manager
+from tools import image_tools
+from tests.rendering_test_manager import cloud_bucket_impl
+
+import ispy_auth_constants
+
+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.
+ """
+ batch_name = self.request.get('batch_name')
+ test_name = self.request.get('test_name')
+
+ # Short-circuit if a parameter is missing.
+ if not (batch_name and test_name):
+ self.response.header['Content-Type'] = 'json/application'
+ self.response.write(json.dumps(
+ {'error': 'batch_name, and test_name, must be '
+ 'supplied to update a mask.'}))
+ return
+ # Otherwise, set up the rendering_test_manager.
+ self.bucket = cloud_bucket_impl.CloudBucketImpl(
+ ispy_auth_constants.KEY, ispy_auth_constants.SECRET,
+ ispy_auth_constants.BUCKET)
+ self.manager = rendering_test_manager.RenderingTestManager(self.bucket)
+ # Short-circuit if the failure does not exist.
+ if not self.manager.FailureExists(batch_name, test_name):
+ 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.manager.GetFailure(batch_name, test_name)
+ # Get the mask of the image.
+ path = 'tests/%s/%s/mask.png' % (batch_name, test_name)
+ mask = self.manager.DownloadImage(path)
+ # Merge the masks.
+ combined_mask = image_tools.AddMasks([mask, failure.diff])
+ # Upload the combined mask in place of the original.
+ self.manager.UploadImage(path, combined_mask)
+ # Now that there is no div for the two images, remove the failure.
+ self.manager.RemoveFailure(batch_name, test_name)
+ # Redirect back to the sites list for the device.
+ self.redirect('/?batch_name=%s' % batch_name)
diff --git a/chrome/test/functional/ispy/ispy_core/ispy_auth_constants.py b/chrome/test/functional/ispy/ispy_core/ispy_auth_constants.py
new file mode 100644
index 0000000000..c4263a8715
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/ispy_auth_constants.py
@@ -0,0 +1,12 @@
+# 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.
+
+"""Module to hold authentication constants used across handlers."""
+
+# This is a dummy file, when updating the I-Spy server, replace these
+# empty strings with their correct values.
+
+KEY = 'GOOGxxxxxxxxxxxxxxxxxxx'
+SECRET = 'xxxxxxxxxxxxxxxx/xxxxxxxxxx/xxxxxxxxxxx'
+BUCKET = 'i-spy'
diff --git a/chrome/test/functional/ispy/ispy_core/main.py b/chrome/test/functional/ispy/ispy_core/main.py
new file mode 100644
index 0000000000..5f9665e1c2
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/main.py
@@ -0,0 +1,18 @@
+# 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 webapp2
+
+from handlers import command_handler
+from handlers import debug_view_handler
+from handlers import image_handler
+from handlers import main_view_handler
+from handlers import update_mask_handler
+
+application = webapp2.WSGIApplication(
+ [('/run_command?', command_handler.CommandHandler),
+ ('/update_mask?', update_mask_handler.UpdateMaskHandler),
+ ('/debug_view?', debug_view_handler.DebugViewHandler),
+ ('/image?', image_handler.ImageHandler),
+ ('/', main_view_handler.MainViewHandler)], debug=True)
diff --git a/chrome/test/functional/ispy/ispy_core/run_tests.py b/chrome/test/functional/ispy/ispy_core/run_tests.py
new file mode 100755
index 0000000000..7b083fbd3e
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/run_tests.py
@@ -0,0 +1,11 @@
+#!/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 unittest
+
+if __name__ == '__main__':
+ suite = unittest.TestLoader().discover('.', pattern='*test.py')
+ unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/chrome/test/functional/ispy/ispy_core/tests/rendering_test_manager/cloud_bucket_impl.py b/chrome/test/functional/ispy/ispy_core/tests/rendering_test_manager/cloud_bucket_impl.py
index e28fc7fc99..ebf18f02e0 100644
--- a/chrome/test/functional/ispy/ispy_core/tests/rendering_test_manager/cloud_bucket_impl.py
+++ b/chrome/test/functional/ispy/ispy_core/tests/rendering_test_manager/cloud_bucket_impl.py
@@ -3,12 +3,15 @@
# found in the LICENSE file.
"""Implementation of CloudBucket using Google Cloud Storage as the backend."""
-
+import os
import sys
-sys.path.append(sys.path.join('gsutil', 'third_party', 'boto'))
-# TODO(chris) check boto into ispy/third_party/
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
+ 'gsutil', 'third_party', 'boto'))
import boto
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
from tests.rendering_test_manager import cloud_bucket
@@ -63,7 +66,9 @@ class CloudBucketImpl(cloud_bucket.CloudBucket):
key = boto.gs.key.Key(self.bucket)
key.key = path
if key.exists():
- return key.generate_url(3600)
+ # 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)
diff --git a/chrome/test/functional/ispy/ispy_core/tests/rendering_test_manager/mock_cloud_bucket.py b/chrome/test/functional/ispy/ispy_core/tests/rendering_test_manager/mock_cloud_bucket.py
index 7ae1def9b5..5753a981dc 100644
--- a/chrome/test/functional/ispy/ispy_core/tests/rendering_test_manager/mock_cloud_bucket.py
+++ b/chrome/test/functional/ispy/ispy_core/tests/rendering_test_manager/mock_cloud_bucket.py
@@ -4,6 +4,10 @@
"""Subclass of CloudBucket used for testing."""
+import os
+import sys
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
from tests.rendering_test_manager import cloud_bucket
diff --git a/chrome/test/functional/ispy/ispy_core/tools/__init__.py b/chrome/test/functional/ispy/ispy_core/tools/__init__.py
index e69de29bb2..d7a0128100 100644
--- a/chrome/test/functional/ispy/ispy_core/tools/__init__.py
+++ b/chrome/test/functional/ispy/ispy_core/tools/__init__.py
@@ -0,0 +1,24 @@
+# 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 module provides a set of tools that I-Spy uses to perform tests.
+
+
+I-Spy breaks rendering-validations down into a hierarchy of batches,
+ tests, and failures.
+ A batch is a group of tests that is run together.
+ A test consists of two images, one image of the page that is known
+ to be rendered correctly, and another that is a white and black mask,
+ where white regions represent areas that should be exempted from pixel-
+ by-pixel comparisons. These masks are generated by taking a
+ succession of images from a view which is known to be rendered correctly,
+ and masking the regions that change to prevent animated regions of the
+ page from breaking the tests. Tests can be run by submitting an image to
+ them. The submitted image will be compared against the regions of the
+ test's image (which is known to be rendered correctly) that are not
+ excluded by the test's mask. If the comparison fails, a failure is stored
+ in Google Cloud Storage.
+ A failure consists of the image which failed to compare to the test's image
+ and mask, and a black and white image representing the difference between
+ the failure's image and the test's."""
diff --git a/chrome/test/functional/ispy/ispy_core/tools/api_command_runner.py b/chrome/test/functional/ispy/ispy_core/tools/api_command_runner.py
new file mode 100644
index 0000000000..e6cdb787d4
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/tools/api_command_runner.py
@@ -0,0 +1,58 @@
+# 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.
+
+"""RunAPICommand function to authenticate with Ispy and run a command."""
+
+import re
+import requests
+
+
+class ServerSideError(Exception):
+ pass
+
+
+class UnknownError(Exception):
+ pass
+
+
+def RunAPICommand(command, parameters, email, app_specific_password):
+ """Performs the necessary authentication and runs an ispy api command.
+
+ Args:
+ command: The name of the command to run.
+ parameters: A List of 2-tuples (parameter-name, parameter-value).
+ email: A google.com email to connect to ispy with.
+ app_specific_password: An application specific password registered
+ to the given email account.
+
+ Returns:
+ A JSON representation of the result of the command.
+ """
+ app_name = 'google.com:ispy'
+ base_url = 'http://ispy.googleplex.com/'
+ # Use Requests to get an Auth key for the specified email/password.
+ data = {'Email': email, 'Passwd': app_specific_password, 'service': 'ah',
+ 'source': app_name, 'accountType': 'GOOGLE'}
+ auth_keys_text = requests.post('https://google.com/accounts/ClientLogin',
+ data=data).text
+ auth_keys = dict(line.split('=')
+ for line in auth_keys_text.split('\n') if line)
+ # Establish a session by logging into _ah/login
+ serv_args = {'continue': '', 'auth': auth_keys['Auth']}
+ r2 = requests.get(base_url + '_ah/login',
+ params=serv_args, allow_redirects=False)
+ r3 = requests.post(base_url+'run_command',
+ data=dict([('command', command)] + parameters.items()),
+ cookies=r2.cookies)
+ try:
+ return r3.json()
+ except ValueError:
+ if '<html>' in r3.text:
+ match = re.match(r'^.+?<pre>(.+?)</pre>.+?$', r3.text, re.DOTALL)
+ if match:
+ raise ServerSideError(match.groups()[0])
+ else:
+ raise ServerSideError(r3.text)
+ else:
+ raise UnknownError(r3.text)
diff --git a/chrome/test/functional/ispy/ispy_core/tools/api_command_runner_functionaltest.py b/chrome/test/functional/ispy/ispy_core/tools/api_command_runner_functionaltest.py
new file mode 100644
index 0000000000..c16238343d
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/tools/api_command_runner_functionaltest.py
@@ -0,0 +1,111 @@
+# 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.
+
+from PIL import Image
+import json
+import os
+import sys
+import unittest
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
+from tools import api_command_runner
+from tools import image_tools
+import auth_constants
+
+class APICommandRunnerFunctionalTest(unittest.TestCase):
+ def setUp(self):
+ self.u = auth_constants.api_username
+ self.p = auth_constants.api_password
+
+ def Run(self, command, params, expected):
+ res = api_command_runner.RunAPICommand(command, params, self.u, self.p)
+ for item in expected.items():
+ self.assertTrue(res.has_key(item[0]))
+ self.assertEquals(res[item[0]], item[1])
+
+ def testRunner(self):
+ batch_test = {'batch_name': 'batch', 'test_name': 'test'}
+ white = image_tools.SerializeImage(Image.new(
+ 'RGBA', (25, 25), (255, 255, 255, 255)))
+ black = image_tools.SerializeImage(Image.new(
+ 'RGBA', (25, 25), (0, 0, 0, 255)))
+ pink = image_tools.SerializeImage(Image.new(
+ 'RGBA', (25, 25), (255, 71, 191, 255)))
+ test_set = json.dumps([white, white])
+ # Removing a test.
+ self.Run('remove_test', batch_test, {'success': True})
+ # Uploading a test.
+ self.Run('upload_test', dict(batch_test.items() + [('images', test_set)]),
+ {'success': True})
+ # Check that the test we uploaded is there.
+ self.Run('test_exists', batch_test, {'success': True, 'exists': True})
+ # Check that we can run the test we uploaded.
+ self.Run('run_test', dict(batch_test.items() + [('image', black)]),
+ {'success': True})
+ # Check that the test we ran failed.
+ self.Run('failure_exists', batch_test, {'success': True, 'exists': True})
+ # Remove the failure.
+ self.Run('remove_failure', batch_test, {'success': True})
+ # Check that the failure was removed.
+ self.Run('failure_exists', batch_test, {'success': True, 'exists': False})
+ # Remove the test we added.
+ self.Run('remove_test', batch_test, {'success': True})
+ # Check that the test was removed.
+ self.Run('test_exists', batch_test, {'success': True, 'exists': False})
+ # Check that a bad command returns a failure.
+ self.Run('bad_command', {}, {'success': False})
+ # Check that missing parameters returns a failure.
+ self.Run('run_test', batch_test, {'success': False})
+ # Check that running a bad test returns a failure.
+ self.Run('run_test', {'batch_name': 'batch', 'test_name': 'doesnt_exist',
+ 'image': white},
+ {'success': False})
+ # Upload a test with the pinkout workaround (pink).
+ self.Run('upload_test_pink_out',
+ dict(batch_test.items() +
+ [('images', test_set), ('pink_out', pink),
+ ('RGB', json.dumps([255, 71, 191, 255]))]),
+ {'success': True})
+ # Check that the test is there.
+ self.Run('test_exists', batch_test, {'success': True})
+ # Run the test against a black image (traditionally fails).
+ self.Run('run_test', dict(batch_test.items() + [('image', black)]),
+ {'success': True})
+ # Check that the failure exists.
+ self.Run('failure_exists', batch_test, {'success': True, 'exists': True})
+ # Remove the test.
+ self.Run('remove_test', batch_test, {'success': True})
+ # Upload a test with the pinkout workaround (not pink).
+ self.Run('upload_test_pink_out',
+ dict(batch_test.items() +
+ [('images', test_set), ('pink_out', black),
+ ('RGB', json.dumps([255, 71, 191, 255]))]),
+ {'success': True})
+ # Check that the test exists.
+ self.Run('test_exists', batch_test, {'success': True})
+ # Run the test against a black image (traditionally fails).
+ self.Run('run_test', dict(batch_test.items() + [('image', black)]),
+ {'success': True})
+ # Check that a failure doesn't exist.
+ self.Run('failure_exists', batch_test, {'success': True, 'exists': False})
+ # Remove the test.
+ self.Run('remove_test', batch_test, {'success': True})
+ # Check that hte test doesn't exist.
+ self.Run('test_exists', batch_test, {'success': True, 'exists': False})
+ # Add a test.
+ self.Run('upload_test', dict(batch_test.items() + [('images', test_set)]),
+ {'success': True})
+ # At this point the mask is black, add a white image to the mask.
+ self.Run('add_to_test_mask', dict(batch_test.items() + [('mask', white)]),
+ {'success': True})
+ # Run the test against a black image.
+ self.Run('run_test', dict(batch_test.items() + [('image', black)]),
+ {'success': True})
+ # Check that a failure doesn't exist (because of the added mask).
+ self.Run('failure_exists', batch_test, {'success': True, 'exists': False})
+ # Remove the test.
+ self.Run('remove_test', batch_test, {'success': True})
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chrome/test/functional/ispy/ispy_core/tools/image_tools_unittest.py b/chrome/test/functional/ispy/ispy_core/tools/image_tools_unittest.py
index 46f3702a8f..52e4825f3d 100644
--- a/chrome/test/functional/ispy/ispy_core/tools/image_tools_unittest.py
+++ b/chrome/test/functional/ispy/ispy_core/tools/image_tools_unittest.py
@@ -1,10 +1,14 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
+# 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.
import unittest
+import sys
+import os
from PIL import Image
-import image_tools
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
+from tools import image_tools
def _GenImage(size, color):
diff --git a/chrome/test/functional/ispy/ispy_core/tools/rendering_test_manager.py b/chrome/test/functional/ispy/ispy_core/tools/rendering_test_manager.py
index 9fd584c3d8..d5f370cb87 100644
--- a/chrome/test/functional/ispy/ispy_core/tools/rendering_test_manager.py
+++ b/chrome/test/functional/ispy/ispy_core/tools/rendering_test_manager.py
@@ -4,10 +4,15 @@
"""Utilities for managing files in Google Cloud Storage."""
-from tools import image_tools
import collections
-import posixpath
import itertools
+import json
+import os
+import posixpath
+import sys
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
+from tools import image_tools
class RenderingTestManager(object):
@@ -32,7 +37,8 @@ class RenderingTestManager(object):
image: a RGB PIL.Image to be uploaded.
"""
self.cloud_bucket.UploadFile(
- full_path, image_tools.SerializeImage(image), 'image/png')
+ full_path, image_tools.SerializeImage(image).decode('base64'),
+ 'image/png')
def DownloadImage(self, full_path):
"""Downloads an image from a location in GCS.
@@ -47,27 +53,29 @@ class RenderingTestManager(object):
cloud_bucket.NotFoundError: if the path to the image is not valid.
"""
return image_tools.DeserializeImage(
- self.cloud_bucket.DownloadFile(full_path))
+ self.cloud_bucket.DownloadFile(full_path).encode('base64'))
- def UploadTest(self, test_name, images):
+ def UploadTest(self, batch_name, test_name, images):
"""Creates and uploads a test to GCS 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 GCS as a test.
Args:
+ batch_name: the name of the batch.
test_name: the name of the test.
images: a list of RGB encoded PIL.Images
"""
- path = posixpath.join('tests', test_name)
- mask = image_tools.CreateMask(images)
+ path = posixpath.join('tests', batch_name, test_name)
+ mask = image_tools.InflateMask(image_tools.CreateMask(images), 7)
self.UploadImage(posixpath.join(path, 'expected.png'), images[0])
self.UploadImage(posixpath.join(path, 'mask.png'), mask)
- def RunTest(self, test_name, run_name, actual):
+ def RunTest(self, batch_name, test_name, actual):
"""Runs an image comparison, and uploads discrepancies to GCS.
Args:
+ batch_name: the name of the batch.
test_name: the name of the test to run.
run_name: the name of this run of the test.
actual: an RGB-encoded PIL.Image that is the actual result of the
@@ -76,15 +84,24 @@ class RenderingTestManager(object):
Raises:
cloud_bucket.NotFoundError: if the given test_name is not found.
"""
- path = posixpath.join('failures', test_name, run_name)
- test = self.GetTest(test_name)
- if not image_tools.SameImage(actual, test.expected):
+ path = posixpath.join('failures', batch_name, test_name)
+ test = self.GetTest(batch_name, test_name)
+ if not image_tools.SameImage(actual, test.expected, mask=test.mask):
self.UploadImage(posixpath.join(path, 'actual.png'), actual)
-
- def GetTest(self, test_name):
+ diff = image_tools.VisualizeImageDifferences(
+ test.expected, actual, mask=test.mask)
+ diff_pxls = sum(1 if pxl == (255, 255, 255, 255) else 0
+ for pxl in diff.getdata())
+ self.UploadImage(posixpath.join(path, 'diff.png'), diff)
+ self.cloud_bucket.UploadFile(
+ posixpath.join(path, 'info.txt'),
+ json.dumps({'different_pixels': diff_pxls}), 'application/json')
+
+ def GetTest(self, batch_name, test_name):
"""Returns given test from GCS.
Args:
+ batch_name: the name of the batch.
test_name: the name of the test to get from GCS.
Returns:
@@ -93,71 +110,79 @@ class RenderingTestManager(object):
Raises:
cloud_bucket.NotFoundError: if the test is not found in GCS.
"""
- path = posixpath.join('tests', test_name)
+ path = posixpath.join('tests', batch_name, test_name)
Test = collections.namedtuple('Test', ['expected', 'mask'])
return Test(self.DownloadImage(posixpath.join(path, 'expected.png')),
self.DownloadImage(posixpath.join(path, 'mask.png')))
- def TestExists(self, test_name):
+ def TestExists(self, batch_name, test_name):
"""Returns whether the given test exists in GCS.
Args:
+ batch_name: the name of the batch.
test_name: the name of the test to look for.
Returns:
A boolean indicating whether the test exists.
"""
- path = posixpath.join('tests', test_name)
+ path = posixpath.join('tests', batch_name, test_name)
expected_image_exists = self.cloud_bucket.FileExists(
posixpath.join(path, 'expected.png'))
mask_image_exists = self.cloud_bucket.FileExists(
posixpath.join(path, 'mask.png'))
return expected_image_exists and mask_image_exists
- def FailureExists(self, test_name, run_name):
+ def FailureExists(self, batch_name, test_name):
"""Returns whether the given run exists in GCS.
Args:
+ batch_name: the name of the batch.
test_name: the name of the test that failed.
run_name: the name of the run that the given test failed on.
Returns:
A boolean indicating whether the failure exists.
"""
- failure_path = posixpath.join('failures', test_name, run_name)
+ failure_path = posixpath.join('failures', batch_name, test_name)
actual_image_exists = self.cloud_bucket.FileExists(
posixpath.join(failure_path, 'actual.png'))
- return self.TestExists(test_name) and actual_image_exists
+ test_exists = self.TestExists(batch_name, test_name)
+ info_exists = self.cloud_bucket.FileExists(
+ posixpath.join(failure_path, 'info.txt'))
+ return test_exists and actual_image_exists and info_exists
- def RemoveTest(self, test_name):
+ def RemoveTest(self, batch_name, test_name):
"""Removes a Test from GCS, and all associated failures with that test.
Args:
+ batch_name: the name of the batch.
test_name: the name of the test to remove.
"""
- test_path = posixpath.join('tests', test_name)
- failure_path = posixpath.join('failures', test_name)
+ test_path = posixpath.join('tests', batch_name, test_name)
+ failure_path = posixpath.join('failures', batch_name, test_name)
test_paths = self.cloud_bucket.GetAllPaths(test_path)
failure_paths = self.cloud_bucket.GetAllPaths(failure_path)
for path in itertools.chain(failure_paths, test_paths):
self.cloud_bucket.RemoveFile(path)
- def RemoveFailure(self, test_name, run_name):
+ def RemoveFailure(self, batch_name, test_name):
"""Removes a failure from GCS.
Args:
+ batch_name: the name of the batch.
test_name: the test on which the failure to be removed occured.
run_name: the name of the run on the given test that failed.
"""
- failure_path = posixpath.join('failures', test_name, run_name)
+ failure_path = posixpath.join('failures', batch_name, test_name)
failure_paths = self.cloud_bucket.GetAllPaths(failure_path)
for path in failure_paths:
self.cloud_bucket.RemoveFile(path)
- def GetFailure(self, test_name, run_name):
+ def GetFailure(self, batch_name, test_name):
"""Returns a given test failure's expected, diff, and actual images.
Args:
+ batch_name: the name of the batch.
test_name: the name of the test the result corresponds to.
run_name: the name of the result on the given test.
@@ -168,15 +193,17 @@ class RenderingTestManager(object):
Raises:
cloud_bucket.NotFoundError: if the result is not found in GCS.
"""
- test_path = posixpath.join('tests', test_name)
- failure_path = posixpath.join('failures', test_name, run_name)
+ test_path = posixpath.join('tests', batch_name, test_name)
+ failure_path = posixpath.join('failures', batch_name, test_name)
expected = self.DownloadImage(posixpath.join(test_path, 'expected.png'))
- mask = self.DownloadImage(posixpath.join(test_path, 'mask.png'))
actual = self.DownloadImage(posixpath.join(failure_path, 'actual.png'))
- diff = image_tools.VisualizeImageDifferences(
- expected, actual, mask=mask)
- Failure = collections.namedtuple('Failure', ['expected', 'diff', 'actual'])
- return Failure(expected, diff, actual)
+ diff = self.DownloadImage(posixpath.join(failure_path, 'diff.png'))
+ info = json.loads(self.cloud_bucket.DownloadFile(
+ posixpath.join(failure_path, '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 GCS whose path starts with a given prefix.
diff --git a/chrome/test/functional/ispy/ispy_core/tools/rendering_test_manager_unittest.py b/chrome/test/functional/ispy/ispy_core/tools/rendering_test_manager_unittest.py
index 843ab38835..a0661f0a71 100644
--- a/chrome/test/functional/ispy/ispy_core/tools/rendering_test_manager_unittest.py
+++ b/chrome/test/functional/ispy/ispy_core/tools/rendering_test_manager_unittest.py
@@ -2,10 +2,14 @@
# 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 unittest
import sets
+import sys
+import unittest
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
from tests.rendering_test_manager import mock_cloud_bucket
from tests.rendering_test_manager import cloud_bucket
from tools import rendering_test_manager
@@ -18,9 +22,9 @@ class BucketRenderingTestManagerUnitTest(unittest.TestCase):
# Set up structures that will be reused throughout testing.
self.bucket = mock_cloud_bucket.MockCloudBucket()
self.manager = rendering_test_manager.RenderingTestManager(self.bucket)
- self.white = Image.new('RGB', (25, 25), (255, 255, 255))
- self.red = Image.new('RGB', (25, 25), (255, 0, 0))
- self.black = Image.new('RGB', (25, 25), (0, 0, 0))
+ 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))
def testUploadImage(self):
self.bucket.Reset()
@@ -30,11 +34,11 @@ class BucketRenderingTestManagerUnitTest(unittest.TestCase):
self.manager.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.SerializeImage(self.white))
+ image_tools.SerializeImage(self.white).decode('base64'))
self.assertEquals(self.bucket.datastore['path/to/black.png'],
- image_tools.SerializeImage(self.black))
+ image_tools.SerializeImage(self.black).decode('base64'))
self.assertEquals(self.bucket.datastore['path/to/red.png'],
- image_tools.SerializeImage(self.red))
+ image_tools.SerializeImage(self.red).decode('base64'))
def testDownloadImage(self):
self.bucket.Reset()
@@ -64,39 +68,39 @@ class BucketRenderingTestManagerUnitTest(unittest.TestCase):
def testUploadTest(self):
self.bucket.Reset()
# Upload some tests to the datastore.
- self.manager.UploadTest('test1', [self.white, self.black])
- self.manager.UploadTest('test2', [self.black, self.black])
+ self.manager.UploadTest('batch', 'test1', [self.white, self.black])
+ self.manager.UploadTest('batch', 'test2', [self.black, self.black])
# Confirm that the tests were successfully uploaded.
- self.assertEquals(self.bucket.datastore['tests/test1/expected.png'],
- image_tools.SerializeImage(self.white))
- self.assertEquals(self.bucket.datastore['tests/test1/mask.png'],
- image_tools.SerializeImage(self.white))
- self.assertEquals(self.bucket.datastore['tests/test2/expected.png'],
- image_tools.SerializeImage(self.black))
- self.assertEquals(self.bucket.datastore['tests/test2/mask.png'],
- image_tools.SerializeImage(self.black))
+ self.assertEquals(self.bucket.datastore['tests/batch/test1/expected.png'],
+ image_tools.SerializeImage(self.white).decode('base64'))
+ self.assertEquals(self.bucket.datastore['tests/batch/test1/mask.png'],
+ image_tools.SerializeImage(self.white).decode('base64'))
+ self.assertEquals(self.bucket.datastore['tests/batch/test2/expected.png'],
+ image_tools.SerializeImage(self.black).decode('base64'))
+ self.assertEquals(self.bucket.datastore['tests/batch/test2/mask.png'],
+ image_tools.SerializeImage(self.black).decode('base64'))
def testRunTest(self):
self.bucket.Reset()
- self.manager.UploadTest('test1', [self.red, self.red])
- self.manager.RunTest('test1', 'run1', self.black)
+ self.manager.UploadTest('batch', 'test1', [self.red, self.red])
+ self.manager.RunTest('batch', 'test1', 'run1', self.black)
self.assertEquals(
- self.bucket.datastore['failures/test1/run1/actual.png'],
- image_tools.SerializeImage(self.black))
- self.manager.RunTest('test1', 'run2', self.red)
+ self.bucket.datastore['failures/batch/test1/run1/actual.png'],
+ image_tools.SerializeImage(self.black).decode('base64'))
+ self.manager.RunTest('batch', 'test1', 'run2', self.red)
self.assertFalse(
- self.bucket.datastore.has_key('failures/test1/run2/actual.png'))
+ self.bucket.datastore.has_key('failures/batch/test1/run2/actual.png'))
self.assertRaises(cloud_bucket.FileNotFoundError, self.manager.RunTest,
- 'test2', 'run1', self.black)
+ 'batch', 'test2', 'run1', self.black)
def testGetTest(self):
self.bucket.Reset()
# Upload some tests to the datastore
- self.manager.UploadTest('test1', [self.white, self.black])
- self.manager.UploadTest('test2', [self.red, self.white])
- test1 = self.manager.GetTest('test1')
- test2 = self.manager.GetTest('test2')
+ self.manager.UploadTest('batch', 'test1', [self.white, self.black])
+ self.manager.UploadTest('batch', 'test2', [self.red, self.white])
+ test1 = self.manager.GetTest('batch', 'test1')
+ test2 = self.manager.GetTest('batch', 'test2')
# Check that GetTest gets the appropriate tests.
self.assertEquals(image_tools.SerializeImage(test1.expected),
image_tools.SerializeImage(self.white))
@@ -108,55 +112,55 @@ class BucketRenderingTestManagerUnitTest(unittest.TestCase):
image_tools.SerializeImage(self.white))
# Check that GetTest throws an error for a nonexistant test.
self.assertRaises(
- cloud_bucket.FileNotFoundError, self.manager.GetTest, 'test3')
+ cloud_bucket.FileNotFoundError, self.manager.GetTest, 'batch', 'test3')
def testTestExists(self):
self.bucket.Reset()
- self.manager.UploadTest('test1', [self.white, self.black])
- self.manager.UploadTest('test2', [self.white, self.black])
- self.assertTrue(self.manager.TestExists('test1'))
- self.assertTrue(self.manager.TestExists('test2'))
- self.assertFalse(self.manager.TestExists('test3'))
+ self.manager.UploadTest('batch', 'test1', [self.white, self.black])
+ self.manager.UploadTest('batch', 'test2', [self.white, self.black])
+ self.assertTrue(self.manager.TestExists('batch', 'test1'))
+ self.assertTrue(self.manager.TestExists('batch', 'test2'))
+ self.assertFalse(self.manager.TestExists('batch', 'test3'))
def testFailureExists(self):
self.bucket.Reset()
- self.manager.UploadTest('test1', [self.white, self.white])
- self.manager.RunTest('test1', 'run1', self.black)
- self.manager.RunTest('test1', 'run2', self.white)
- self.assertTrue(self.manager.FailureExists('test1', 'run1'))
- self.assertFalse(self.manager.FailureExists('test1', 'run2'))
+ self.manager.UploadTest('batch', 'test1', [self.white, self.white])
+ self.manager.RunTest('batch', 'test1', 'run1', self.black)
+ self.manager.RunTest('batch', 'test1', 'run2', self.white)
+ self.assertTrue(self.manager.FailureExists('batch', 'test1', 'run1'))
+ self.assertFalse(self.manager.FailureExists('batch', 'test1', 'run2'))
def testRemoveTest(self):
self.bucket.Reset()
- self.manager.UploadTest('test1', [self.white, self.white])
- self.manager.UploadTest('test2', [self.white, self.white])
- self.assertTrue(self.manager.TestExists('test1'))
- self.assertTrue(self.manager.TestExists('test2'))
- self.manager.RemoveTest('test1')
- self.assertFalse(self.manager.TestExists('test1'))
- self.assertTrue(self.manager.TestExists('test2'))
- self.manager.RemoveTest('test2')
- self.assertFalse(self.manager.TestExists('test1'))
- self.assertFalse(self.manager.TestExists('test2'))
+ self.manager.UploadTest('batch', 'test1', [self.white, self.white])
+ self.manager.UploadTest('batch', 'test2', [self.white, self.white])
+ self.assertTrue(self.manager.TestExists('batch', 'test1'))
+ self.assertTrue(self.manager.TestExists('batch', 'test2'))
+ self.manager.RemoveTest('batch', 'test1')
+ self.assertFalse(self.manager.TestExists('batch', 'test1'))
+ self.assertTrue(self.manager.TestExists('batch', 'test2'))
+ self.manager.RemoveTest('batch', 'test2')
+ self.assertFalse(self.manager.TestExists('batch', 'test1'))
+ self.assertFalse(self.manager.TestExists('batch', 'test2'))
def testRemoveFailure(self):
self.bucket.Reset()
- self.manager.UploadTest('test1', [self.white, self.white])
- self.manager.UploadTest('test2', [self.white, self.white])
- self.manager.RunTest('test1', 'run1', self.black)
- self.manager.RunTest('test1', 'run2', self.black)
- self.manager.RemoveFailure('test1', 'run1')
- self.assertFalse(self.manager.FailureExists('test1', 'run1'))
- self.assertTrue(self.manager.TestExists('test1'))
- self.assertTrue(self.manager.FailureExists('test1', 'run2'))
- self.assertTrue(self.manager.TestExists('test2'))
+ self.manager.UploadTest('batch', 'test1', [self.white, self.white])
+ self.manager.UploadTest('batch', 'test2', [self.white, self.white])
+ self.manager.RunTest('batch', 'test1', 'run1', self.black)
+ self.manager.RunTest('batch', 'test1', 'run2', self.black)
+ self.manager.RemoveFailure('batch', 'test1', 'run1')
+ self.assertFalse(self.manager.FailureExists('batch', 'test1', 'run1'))
+ self.assertTrue(self.manager.TestExists('batch', 'test1'))
+ self.assertTrue(self.manager.FailureExists('batch', 'test1', 'run2'))
+ self.assertTrue(self.manager.TestExists('batch', 'test2'))
def testGetFailure(self):
self.bucket.Reset()
# Upload a result
- self.manager.UploadTest('test1', [self.red, self.red])
- self.manager.RunTest('test1', 'run1', self.black)
- res = self.manager.GetFailure('test1', 'run1')
+ self.manager.UploadTest('batch', 'test1', [self.red, self.red])
+ self.manager.RunTest('batch', 'test1', 'run1', self.black)
+ res = self.manager.GetFailure('batch', 'test1', 'run1')
# Check that the function correctly got the result.
self.assertEquals(image_tools.SerializeImage(res.expected),
image_tools.SerializeImage(self.red))
@@ -167,27 +171,27 @@ class BucketRenderingTestManagerUnitTest(unittest.TestCase):
# Check that the function raises an error when given non-existant results.
self.assertRaises(cloud_bucket.FileNotFoundError,
self.manager.GetFailure,
- 'test1', 'run2')
+ 'batch', 'test1', 'run2')
self.assertRaises(cloud_bucket.FileNotFoundError,
self.manager.GetFailure,
- 'test2', 'run1')
+ 'batch', 'test2', 'run1')
def testGetAllPaths(self):
self.bucket.Reset()
# Upload some tests.
- self.manager.UploadTest('test1', [self.white, self.black])
- self.manager.UploadTest('test2', [self.red, self.white])
+ self.manager.UploadTest('batch', 'test1', [self.white, self.black])
+ self.manager.UploadTest('batch', 'test2', [self.red, self.white])
# Check that the function gets all urls matching the prefix.
self.assertEquals(
- sets.Set(self.manager.GetAllPaths('tests/test')),
- sets.Set(['tests/test1/expected.png',
- 'tests/test1/mask.png',
- 'tests/test2/expected.png',
- 'tests/test2/mask.png']))
- self.assertEquals(sets.Set(self.manager.GetAllPaths('tests/test1')),
- sets.Set(['tests/test1/expected.png',
- 'tests/test1/mask.png']))
- self.assertEquals(sets.Set(self.manager.GetAllPaths('tests/test3')),
+ sets.Set(self.manager.GetAllPaths('tests/batch/test')),
+ sets.Set(['tests/batch/test1/expected.png',
+ 'tests/batch/test1/mask.png',
+ 'tests/batch/test2/expected.png',
+ 'tests/batch/test2/mask.png']))
+ self.assertEquals(sets.Set(self.manager.GetAllPaths('tests/batch/test1')),
+ sets.Set(['tests/batch/test1/expected.png',
+ 'tests/batch/test1/mask.png']))
+ self.assertEquals(sets.Set(self.manager.GetAllPaths('tests/batch/test3')),
sets.Set())
diff --git a/chrome/test/functional/ispy/ispy_core/tools/reverse_port_forwarder.py b/chrome/test/functional/ispy/ispy_core/tools/reverse_port_forwarder.py
index 289c116ddf..0c45152d4a 100644
--- a/chrome/test/functional/ispy/ispy_core/tools/reverse_port_forwarder.py
+++ b/chrome/test/functional/ispy/ispy_core/tools/reverse_port_forwarder.py
@@ -14,7 +14,6 @@ sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)),
os.pardir, os.pardir, 'build', 'android'))
from pylib import android_commands
from pylib import forwarder
-from pylib import valgrind_tools
class ReversePortForwarder(object):
@@ -54,16 +53,15 @@ class ReversePortForwarder(object):
self._adb = android_commands.AndroidCommands(self._device_serial)
self._adb.StartAdbServer()
# Begin forwarding the device_ports to the host_ports.
- self._forwarder = forwarder.Forwarder(self._cmd, 'Release')
- self._forwarder.Run([
- (self._device_http, self._host_http),
- (self._device_https, self._host_https)],
- valgrind_tools.BaseTool())
+ forwarder.Forwarder.Map([(self._device_http, self._host_http),
+ (self._device_https, self._host_https)],
+ self._adb, build_type='Release', tool=None)
def Stop(self):
"""Cleans up after the start call by closing the forwarder."""
# shut down the forwarder.
- self._forwarder.Close()
+ forwarder.Forwarder.UnmapDevicePort(self._device_http, self._adb)
+ forwarder.Forwarder.UnmapDevicePort(self._device_https, self._adb)
def GetChromeArgs(self):
"""Makes a list of arguments to enable reverse port forwarding on chrome.
@@ -73,5 +71,5 @@ class ReversePortForwarder(object):
"""
args = ['testing-fixed-http-port=%s' % self._device_http,
'testing-fixed-https-port=%s' % self._device_https,
- 'host-resolver-rules=\'MAP * 127.0.0.1,EXCEPT, localhost\'']
+ 'host-resolver-rules=\"MAP * 127.0.0.1,EXCEPT, localhost\"']
return args
diff --git a/chrome/test/functional/ispy/ispy_core/views/__init__.py b/chrome/test/functional/ispy/ispy_core/views/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/views/__init__.py
diff --git a/chrome/test/functional/ispy/ispy_core/views/debug_view.html b/chrome/test/functional/ispy/ispy_core/views/debug_view.html
new file mode 100644
index 0000000000..e5eb1cf8fa
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/views/debug_view.html
@@ -0,0 +1,71 @@
+<html>
+ <head>
+ <style>
+ .hidden {
+ display: none;
+ }
+ </style>
+ <script>
+ // Set up a getImageData function.
+ var getImageData = function(id) {
+ // Set up an image with the src from the img_id.
+ var image = new Image();
+ image.src = document.getElementById(id).src;
+ // Create a canvas in memeory to hold the image.
+ var mem_canvas = document.createElement('canvas');
+ mem_canvas.setAttribute('width', image.width.toString());
+ mem_canvas.setAttribute('height', image.height.toString());
+ var mem_context = mem_canvas.getContext('2d');
+ // Draw the image into the canvas.
+ mem_context.drawImage(image, 0, 0);
+ // Extract the image from the canvas in editable form.
+ return mem_context.getImageData(0, 0, image.width, image.height);
+ };
+ // Set up a function to generate the image to be displayed.
+ var computeDiffAlpha = function(expected, diff, actual) {
+ // Go through all of the pixels in the diff.
+ for(var i=0; i<diff.data.length; i += 4) {
+ // If the pixel in the diff isn't white, use the expected pixel.
+ if ((diff.data[i] == 255 && diff.data[i + 1] == 255 &&
+ diff.data[i + 2] == 255 && diff.data[i + 3] == 255)) {
+ // If the diff pixel is not white, it should not be seen.
+ expected.data[i] = actual.data[i];
+ expected.data[i + 1] = actual.data[i + 1];
+ expected.data[i + 2] = actual.data[i + 2];
+ expected.data[i + 3] = actual.data[i + 3];
+ }
+ // Otherwise don't touch the actual_data pixel.
+ }
+ // Return the modified pixel data.
+ return expected;
+ };
+ // Set everything up to run after the document loads.
+ var loader = function() {
+ var canvas = document.getElementById('image');
+ var context = canvas.getContext('2d');
+ var expected = getImageData('expected');
+ var diff = getImageData('diff');
+ var debug = computeDiffAlpha(getImageData('expected'), diff, getImageData('actual'));
+ var state = 'expected';
+ canvas.width = diff.width;
+ canvas.height = diff.height;
+ var swapper = setInterval(function() {
+ if (state === 'expected') {
+ state = 'debug';
+ context.putImageData(expected, 0, 0);
+ }
+ else {
+ state = 'expected';
+ context.putImageData(debug, 0, 0);
+ }
+ }, 1000);
+ };
+ </script>
+ </head>
+ <body onload="loader();">
+ <img class='hidden' id='expected' src="{{ expected }}"/>
+ <img class='hidden' id='diff' src="{{ diff }}"/>
+ <img class='hidden' id='actual' src="{{ actual }}"/>
+ <canvas id='image'></canvas>
+ </body>
+</html>
diff --git a/chrome/test/functional/ispy/ispy_core/views/diff_view.html b/chrome/test/functional/ispy/ispy_core/views/diff_view.html
new file mode 100644
index 0000000000..6cf10835da
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/views/diff_view.html
@@ -0,0 +1,62 @@
+<!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/ispy_core/views/list_view.html b/chrome/test/functional/ispy/ispy_core/views/list_view.html
new file mode 100644
index 0000000000..fdbcab0044
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/views/list_view.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+{% autoescape on %}
+<html>
+ <head>
+ <style>
+ #container {
+ display: table;
+ background-color:#A8A8A8;
+ border:2px solid black;
+ width: 600px
+ }
+ #row {
+ display: table-row;
+ border 1px solid gray;
+ width: 400px;
+ height: 25px;
+ border: 2px solid black;
+ }
+ #col {
+ display: table-cell;
+ padding-right: 25px;
+ padding-left: 25px;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="container">
+ {% for link in links %}
+ <span id="row">
+ <a href="{{ link[1] }}">{{ link[0] }}</a>
+ </span>
+ {% endfor %}
+ </div>
+ </body>
+</html>
+{% endautoescape %}
diff --git a/chrome/test/functional/ispy/ispy_core/views/main_view.html b/chrome/test/functional/ispy/ispy_core/views/main_view.html
new file mode 100644
index 0000000000..cd61d0c019
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/views/main_view.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+ .image {
+ height: 325px;
+ }
+ .cell {
+ padding-right: 25px;
+ padding-left: 25px;
+ float: left;
+ }
+ .info {
+ background-color:#A8A8A8;
+ width: 300px;
+ }
+ .row {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ border-bottom: 2px solid black;
+ height: 350px;
+ }
+ </style>
+ </head>
+ <body>
+ {% for comp in comparisons %}
+ <div class="row">
+ <div class="cell">
+ Diff<br>
+ <img class="image" src={{ comp['diff'] }}>
+ </div>
+ <div class="cell">
+ Expected<br>
+ <img class="image" src={{ comp['expected'] }}>
+ </div>
+ <div class="cell">
+ Actual<br>
+ <img class="image" src={{ comp['actual'] }}>
+ </div>
+ <div class="cell">
+ <br>
+ <div class="info">
+ Test Name: {{ comp['test_name'] }}<br>
+ Run Name: {{ comp['run_name'] }}
+ </div>
+ <form action="/update_mask" method="post">
+ <input type="hidden" name="batch_name" value="{{ comp['batch_name'] }}"/>
+ <input type="hidden" name="test_name" value="{{ comp['test_name'] }}"/>
+ <input type="hidden" name="run_name" value="{{ comp['run_name'] }}"/>
+ <input type="submit" value="Ignore Diff"/>
+ </form>
+ <a href='/debug_view?diff={{ comp['diff_path'] }}&actual={{ comp['actual_path'] }}&expected={{ comp['expected_path'] }}'>Debug View</a>
+ </div>
+ </div>
+ {% endfor %}
+ </body>
+</html>
diff --git a/chrome/test/functional/media_stream_infobar.py b/chrome/test/functional/media_stream_infobar.py
deleted file mode 100755
index 686882af6a..0000000000
--- a/chrome/test/functional/media_stream_infobar.py
+++ /dev/null
@@ -1,83 +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
-import pyauto
-import webrtc_test_base
-
-
-class MediaStreamInfobarTest(webrtc_test_base.WebrtcTestBase):
- """Performs basic tests on the media stream infobar.
-
- This infobar is used to grant or deny access to WebRTC capabilities for a
- webpage. If a page calls the getUserMedia function the infobar will ask the
- user if it is OK for the webpage to use the webcam or microphone on the user's
- machine. These tests ensure that the infobar works as intended.
- """
-
- def ExtraChromeFlags(self):
- """Adds flags to the Chrome command line."""
- extra_flags = ['--enable-media-stream']
- return pyauto.PyUITest.ExtraChromeFlags(self) + extra_flags
-
- def testAllowingUserMedia(self):
- """Test that selecting 'accept' gives us a media stream.
-
- When the user clicks allow, the javascript should have the success callback
- called with a media stream.
- """
- self.assertEquals('ok-got-stream',
- self._TestGetUserMedia(with_action='accept'))
-
- def testDenyingUserMedia(self):
- """Tests that selecting 'cancel' actually denies access to user media.
-
- When the user clicks deny in the user media bar, the javascript should have
- the error callback called with an error specification instead of the success
- callback with a media stream. This is important since the user should be
- able to deny the javascript to access the webcam.
- """
- # Error 1 = Permission denied
- self.assertEquals('failed-with-error-PERMISSION_DENIED',
- self._TestGetUserMedia(with_action='cancel'))
-
- def testDismissingUserMedia(self):
- """Dismiss should be treated just like deny, which is described above."""
- # Error 1 = Permission denied
- self.assertEquals('failed-with-error-PERMISSION_DENIED',
- self._TestGetUserMedia(with_action='dismiss'))
-
- def testConsecutiveGetUserMediaCalls(self):
- """Ensures we deal appropriately with several consecutive requests."""
- self.assertEquals('failed-with-error-PERMISSION_DENIED',
- self._TestGetUserMedia(with_action='dismiss'))
- self.assertEquals('failed-with-error-PERMISSION_DENIED',
- self._TestGetUserMedia(with_action='cancel'))
- self.assertEquals('ok-got-stream',
- self._TestGetUserMedia(with_action='accept'))
- self.assertEquals('failed-with-error-PERMISSION_DENIED',
- self._TestGetUserMedia(with_action='cancel'))
- self.assertEquals('ok-got-stream',
- self._TestGetUserMedia(with_action='accept'))
- self.assertEquals('failed-with-error-PERMISSION_DENIED',
- self._TestGetUserMedia(with_action='dismiss'))
-
- def _TestGetUserMedia(self, with_action):
- """Runs getUserMedia in the test page and returns the result."""
- url = self.GetFileURLForDataPath('webrtc', 'webrtc_jsep01_test.html')
- self.NavigateToURL(url)
-
- self.assertEquals('ok-requested', self.ExecuteJavascript(
- 'getUserMedia("{ audio: true, video: true, }")'))
-
- self.WaitForInfobarCount(1)
- self.PerformActionOnInfobar(with_action, infobar_index=0)
- self.WaitForGetUserMediaResult(tab_index=0)
-
- return self.GetUserMediaResult(tab_index=0)
-
-
-if __name__ == '__main__':
- pyauto_functional.Main()
diff --git a/chrome/test/functional/webrtc_apprtc_call.py b/chrome/test/functional/webrtc_apprtc_call.py
deleted file mode 100755
index 986fa21554..0000000000
--- a/chrome/test/functional/webrtc_apprtc_call.py
+++ /dev/null
@@ -1,71 +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 random
-import time
-
-# Note: pyauto_functional must come before pyauto.
-import pyauto_functional
-import pyauto
-import webrtc_test_base
-
-
-class WebrtcApprtcCallTest(webrtc_test_base.WebrtcTestBase):
- """Tests calling apprtc.appspot.com and setting up a call.
-
- Prerequisites: This test case must run on a machine with a webcam, either
- fake or real, and with some kind of audio device. The machine must have access
- to the public Internet.
-
- This should be considered an integration test: test failures could mean
- that the AppRTC reference is broken, that WebRTC is broken, or both.
- """
-
- def tearDown(self):
- pyauto.PyUITest.tearDown(self)
- self.assertEquals('', self.CheckErrorsAndCrashes(),
- 'Chrome crashed or hit a critical error during test.')
-
- def testApprtcLoopbackCall(self):
- self.NavigateToURL('http://apprtc.appspot.com/?debug=loopback')
- self.WaitForInfobarCount(1, tab_index=0)
- self.PerformActionOnInfobar('accept', infobar_index=0, tab_index=0)
-
- self._WaitForCallEstablishment(tab_index=0)
-
- def testApprtcTabToTabCall(self):
- # Randomize the call session id. If we would use the same id we would risk
- # getting problems with hung calls and lingering state in AppRTC.
- random_call_id = 'pyauto%d' % random.randint(0, 65536)
- apprtc_url = 'http://apprtc.appspot.com/?r=%s' % random_call_id
-
- self.NavigateToURL(apprtc_url)
- self.AppendTab(pyauto.GURL(apprtc_url))
-
- self.WaitForInfobarCount(1, tab_index=0)
- self.WaitForInfobarCount(1, tab_index=1)
-
- self.PerformActionOnInfobar('accept', infobar_index=0, tab_index=0)
- # TODO(phoglund): workaround for
- # https://code.google.com/p/webrtc/issues/detail?id=1742
- time.sleep(1)
- self.PerformActionOnInfobar('accept', infobar_index=0, tab_index=1)
-
- self._WaitForCallEstablishment(tab_index=0)
- self._WaitForCallEstablishment(tab_index=1)
-
- def _WaitForCallEstablishment(self, tab_index):
- # AppRTC will set opacity to 1 for remote video when the call is up.
- video_playing = self.WaitUntil(
- function=lambda: self.GetDOMValue('remoteVideo.style.opacity',
- tab_index=tab_index),
- expect_retval='1')
- self.assertTrue(video_playing,
- msg=('Timed out while waiting for '
- 'remoteVideo.style.opacity to return 1.'))
-
-
-if __name__ == '__main__':
- pyauto_functional.Main()
diff --git a/chrome/test/functional/webrtc_audio_quality.py b/chrome/test/functional/webrtc_audio_quality.py
deleted file mode 100755
index 8e1cff4560..0000000000
--- a/chrome/test/functional/webrtc_audio_quality.py
+++ /dev/null
@@ -1,184 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-import os
-import sys
-import tempfile
-import time
-
-import media.audio_tools as audio_tools
-
-# Note: pyauto_functional must come before pyauto.
-import pyauto_functional
-import pyauto
-import pyauto_utils
-import webrtc_test_base
-
-_MEDIA_PATH = os.path.abspath(os.path.join(pyauto.PyUITest.DataDir(),
- 'pyauto_private', 'webrtc'))
-if 'win32' in sys.platform:
- _REFERENCE_FILE = os.path.join(_MEDIA_PATH, 'human-voice-win.wav')
-else:
- _REFERENCE_FILE = os.path.join(_MEDIA_PATH, 'human-voice-linux.wav')
-_JAVASCRIPT_PATH = os.path.abspath(os.path.join(pyauto.PyUITest.DataDir(),
- 'webrtc'))
-
-
-class WebrtcAudioQualityTest(webrtc_test_base.WebrtcTestBase):
- """Test we can set up a WebRTC call and play audio through it.
-
- This test will only work on machines that have been configured to record their
- own input*.
-
- * On Linux:
- 1. # sudo apt-get install pavucontrol
- 2. For the user who will run the test: # pavucontrol
- 3. In a separate terminal, # arecord dummy
- 4. In pavucontrol, go to the recording tab.
- 5. For the ALSA plug-in [aplay]: ALSA Capture from, change from <x> to
- <Monitor of x>, where x is whatever your primary sound device is called.
- 6. Try launching chrome as the target user on the target machine, try
- playing, say, a YouTube video, and record with # arecord -f dat mine.dat.
- Verify the recording with aplay (should have recorded what you played
- from chrome).
-
- * On Windows 7:
- 1. Control panel > Sound > Manage audio devices.
- 2. In the recording tab, right-click in an empty space in the pane with the
- devices. Tick 'show disabled devices'.
- 3. You should see a 'stero mix' device - this is what your speakers output.
- Right click > Properties.
- 4. In the Listen tab for the mix device, check the 'listen to this device'
- checkbox. Ensure the mix device is the default recording device.
- 5. Launch chrome and try playing a video with sound. You should see movement
- in the volume meter for the mix device. Configure the mix device to have
- 50 / 100 in level. Also go into the playback tab, right-click Speakers,
- and set that level to 50 / 100. Otherwise you will get distortion in
- the recording.
- """
- def setUp(self):
- pyauto.PyUITest.setUp(self)
- self.StartPeerConnectionServer()
-
- def tearDown(self):
- self.StopPeerConnectionServer()
-
- pyauto.PyUITest.tearDown(self)
- self.assertEquals('', self.CheckErrorsAndCrashes())
-
- def testWebrtcAudioCallAndMeasureQuality(self):
- """Measures how much WebRTC distorts speech.
-
- The input file is about 9.3 seconds long and has had silence trimmed on both
- sides. We will set up a WebRTC call, load the file with WebAudio in the
- javascript, connect the WebAudio buffer node to the peer connection and play
- it out on the other side (in a video tag).
-
- We originally got the input file by playing a file through this test and
- using the resulting file. The purpose is to lessen the impact on the score
- from known distortions such as comfort noise. You can do such a rebase on
- the _REFERENCE_FILE by setting REBASE=1 before running the test. The file
- will end up in the system temp folder and will end with _webrtc.wav.
-
- We then record what Chrome plays out. We give it plenty of time to play
- the whole file over the connection, and then we trim silence on both ends.
- That is finally fed into PESQ for comparison.
- """
- # We'll use a relative path since the javascript will be loading the file
- # relative to where the javascript itself is.
- self.assertTrue(os.path.exists(_MEDIA_PATH),
- msg='Missing pyauto_private in chrome/test/data: you need '
- 'to check out src_internal in your .gclient to run '
- 'this test.')
-
- input_relative_path = os.path.relpath(_REFERENCE_FILE, _JAVASCRIPT_PATH)
-
- def CallWithWebAudio():
- self._AudioCallWithWebAudio(duration_seconds=15,
- input_relative_path=input_relative_path)
-
- def MeasureQuality(output_no_silence):
- results = audio_tools.RunPESQ(_REFERENCE_FILE, output_no_silence,
- sample_rate=16000)
- self.assertTrue(results, msg=('Failed to compute PESQ (most likely, we '
- 'recorded only silence)'))
- pyauto_utils.PrintPerfResult('audio_pesq', 'raw_mos', results[0], 'score')
- pyauto_utils.PrintPerfResult('audio_pesq', 'mos_lqo', results[1], 'score')
-
- self._RecordAndVerify(record_duration_seconds=20,
- sound_producing_function=CallWithWebAudio,
- verification_function=MeasureQuality)
-
- def _AudioCallWithWebAudio(self, duration_seconds, input_relative_path):
- self.LoadTestPageInTwoTabs(test_page='webrtc_audio_quality_test.html');
-
- self.Connect('user_1', tab_index=0)
- self.Connect('user_2', tab_index=1)
-
- self.CreatePeerConnection(tab_index=0)
- self.AddWebAudioFile(tab_index=0, input_relative_path=input_relative_path)
-
- self.EstablishCall(from_tab_with_index=0, to_tab_with_index=1)
-
- # Note: the media flow isn't necessarily established on the connection just
- # because the ready state is ok on both sides. We sleep a bit between call
- # establishment and playing to avoid cutting of the beginning of the audio
- # file.
- time.sleep(2)
- self.PlayWebAudioFile(tab_index=0)
-
- # Keep the call up while we detect audio.
- time.sleep(duration_seconds)
-
- # The hang-up will automatically propagate to the second tab.
- self.HangUp(from_tab_with_index=0)
- self.WaitUntilHangUpVerified(tab_index=1)
-
- self.Disconnect(tab_index=0)
- self.Disconnect(tab_index=1)
-
- # Ensure we didn't miss any errors.
- self.AssertNoFailures(tab_index=0)
- self.AssertNoFailures(tab_index=1)
-
- def _RecordAndVerify(self, record_duration_seconds, sound_producing_function,
- verification_function):
- audio_tools.ForceMicrophoneVolumeTo100Percent()
- rebase = 'REBASE' in os.environ
-
- # The two temp files that will be potentially used in the test.
- temp_file = None
- file_no_silence = None
- try:
- temp_file = self._CreateTempFile()
- record_thread = audio_tools.AudioRecorderThread(record_duration_seconds,
- temp_file,
- record_mono=True)
- record_thread.start()
- sound_producing_function()
- record_thread.join()
-
- if record_thread.error:
- self.fail(record_thread.error)
- file_no_silence = self._CreateTempFile()
- audio_tools.RemoveSilence(temp_file, file_no_silence)
-
- verification_function(file_no_silence)
- finally:
- # Delete the temporary files used by the test.
- if temp_file:
- os.remove(temp_file)
- if file_no_silence and not rebase:
- os.remove(file_no_silence)
-
- def _CreateTempFile(self):
- """Returns an absolute path to an empty temp file."""
- file_handle, path = tempfile.mkstemp(suffix='_webrtc.wav')
- os.close(file_handle)
- return path
-
-
-if __name__ == '__main__':
- pyauto_functional.Main() \ No newline at end of file
diff --git a/chrome/test/functional/webrtc_brutality_test.py b/chrome/test/functional/webrtc_brutality_test.py
deleted file mode 100755
index b5852e2124..0000000000
--- a/chrome/test/functional/webrtc_brutality_test.py
+++ /dev/null
@@ -1,73 +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
-import webrtc_test_base
-
-
-class WebrtcBrutalityTest(webrtc_test_base.WebrtcTestBase):
- """Tests how WebRTC deals with inconvenient reloads, etc."""
-
- def testReloadsAfterGetUserMedia(self):
- """Tests how we deal with reloads.
-
- This test will quickly reload the page after running getUserMedia, which
- will remove the pending request. This crashed the browser before the fix
- for crbug.com/135043.
-
- The test will make repeated getUserMedia requests with refreshes between
- them. Sometimes it will click past the bar and then refresh.
- """
- if self.PlatformIsWinXP():
- print 'Skipping this test on Windows XP due to flakiness.'
- return
- self.LoadTestPageInOneTab()
- for i in range(1, 100):
- if i % 10 == 0:
- self.GetUserMedia(tab_index=0, action='accept')
- else:
- self._GetUserMediaWithoutTakingAction(tab_index=0)
- self.ReloadTab(tab_index=0)
-
- def testRepeatedGetUserMediaRequests(self):
- """Tests how we deal with lots of consecutive getUserMedia requests.
-
- The test will alternate unanswered requests with requests that get answered.
- """
- if self.PlatformIsWinXP():
- print 'Skipping this test on Windows XP due to flakiness.'
- return
- self.LoadTestPageInOneTab()
- for i in range(1, 100):
- if i % 10 == 0:
- self.GetUserMedia(tab_index=0, action='accept')
- else:
- self._GetUserMediaWithoutTakingAction(tab_index=0)
-
- def testSuccessfulGetUserMediaAndThenReload(self):
- """Waits for WebRTC to respond, and immediately reloads the tab."""
- self.LoadTestPageInOneTab()
- self.GetUserMedia(tab_index=0, action='accept')
- self.ReloadTab(tab_index=0)
-
- def testClosingTabAfterGetUserMedia(self):
- """Tests closing the tab right after a getUserMedia call."""
- self.LoadTestPageInOneTab()
- self._GetUserMediaWithoutTakingAction(tab_index=0)
- self.CloseTab(tab_index=0)
-
- def testSuccessfulGetUserMediaAndThenClose(self):
- """Waits for WebRTC to respond, and closes the tab."""
- self.LoadTestPageInOneTab()
- self.GetUserMedia(tab_index=0, action='accept')
- self.CloseTab(tab_index=0)
-
- def _GetUserMediaWithoutTakingAction(self, tab_index):
- self.assertEquals('ok-requested', self.ExecuteJavascript(
- 'getUserMedia("{ audio: true, video: true, }")', tab_index=0))
-
-
-if __name__ == '__main__':
- pyauto_functional.Main()
diff --git a/chrome/test/functional/webrtc_call.py b/chrome/test/functional/webrtc_call.py
deleted file mode 100755
index 8c54b114d5..0000000000
--- a/chrome/test/functional/webrtc_call.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.
-
-import time
-
-# This little construct ensures we can run even if we have a bad version of
-# psutil installed. If so, we'll just skip the test that needs it.
-_HAS_CORRECT_PSUTIL_VERSION = False
-try:
- import psutil
- if 'version_info' in dir(psutil):
- # If psutil has any version info at all, it's recent enough.
- _HAS_CORRECT_PSUTIL_VERSION = True
-except ImportError, e:
- pass
-
-
-# Note: pyauto_functional must come before pyauto.
-import pyauto_functional
-import pyauto
-import pyauto_utils
-import webrtc_test_base
-
-
-class WebrtcCallTest(webrtc_test_base.WebrtcTestBase):
- """Test we can set up a WebRTC call and disconnect it.
-
- Prerequisites: This test case must run on a machine with a webcam, either
- fake or real, and with some kind of audio device. You must make the
- peerconnection_server target before you run.
-
- The test case will launch a custom binary
- (peerconnection_server) which will allow two WebRTC clients to find each
- other. For more details, see the source code which is available at the site
- http://code.google.com/p/libjingle/source/browse/ (make sure to browse to
- trunk/talk/examples/peerconnection/server).
- """
-
- def setUp(self):
- pyauto.PyUITest.setUp(self)
- self.StartPeerConnectionServer()
-
- def tearDown(self):
- self.StopPeerConnectionServer()
-
- pyauto.PyUITest.tearDown(self)
- self.assertEquals('', self.CheckErrorsAndCrashes())
-
- def _SimpleWebrtcCall(self, request_video, request_audio, duration_seconds=0):
- """Tests we can call and hang up with WebRTC.
-
- This test exercises pretty much the whole happy-case for the WebRTC
- JavaScript API. Currently, it exercises a normal call setup using the API
- defined at http://dev.w3.org/2011/webrtc/editor/webrtc.html. The API is
- still evolving.
-
- The test will load the supplied HTML file, which in turn will load different
- javascript files depending on which version of the signaling protocol
- we are running.
- The supplied HTML file will be loaded in two tabs and tell the web
- pages to start up WebRTC, which will acquire video and audio devices on the
- system. This will launch a dialog in Chrome which we click past using the
- automation controller. Then, we will order both tabs to connect the server,
- which will make the two tabs aware of each other. Once that is done we order
- one tab to call the other.
-
- We make sure that the javascript tells us that the call succeeded, lets it
- run for a while and try to hang up the call after that. We verify video is
- playing by using the video detector.
-
- Args:
- request_video: Whether to request video.
- request_audio: Whether to request audio.
- duration_seconds: The number of seconds to keep the call up before
- shutting it down.
- """
- self._SetupCall(request_video=request_video, request_audio=request_audio)
-
- if duration_seconds:
- print 'Call up: sleeping %d seconds...' % duration_seconds
- time.sleep(duration_seconds);
-
- # The hang-up will automatically propagate to the second tab.
- self.HangUp(from_tab_with_index=0)
- self.WaitUntilHangUpVerified(tab_index=1)
-
- self.Disconnect(tab_index=0)
- self.Disconnect(tab_index=1)
-
- # Ensure we didn't miss any errors.
- self.AssertNoFailures(tab_index=0)
- self.AssertNoFailures(tab_index=1)
-
- def testWebrtcCall(self):
- self.LoadTestPageInTwoTabs()
- self._SimpleWebrtcCall(request_video=True, request_audio=True)
-
- def testWebrtcVideoOnlyCall(self):
- self.LoadTestPageInTwoTabs()
- self._SimpleWebrtcCall(request_video=True, request_audio=False)
-
- def testWebrtcAudioOnlyCall(self):
- self.LoadTestPageInTwoTabs()
- self._SimpleWebrtcCall(request_video=False, request_audio=True)
-
- def testWebrtcJsep01CallAndMeasureCpu20Seconds(self):
- if not _HAS_CORRECT_PSUTIL_VERSION:
- print ('WARNING: Can not run cpu/mem measurements with this version of '
- 'psutil. You must have at least psutil 0.4.1 installed for the '
- 'version of python you are running this test with.')
- return
-
- self.LoadTestPageInTwoTabs(test_page='webrtc_jsep01_test.html')
-
- # Prepare CPU measurements.
- renderer_process = self._GetChromeRendererProcess(tab_index=0)
- renderer_process.get_cpu_percent()
-
- self._SimpleWebrtcCall(request_video=True,
- request_audio=True,
- duration_seconds=20)
-
- cpu_usage = renderer_process.get_cpu_percent(interval=0)
- mem_usage_bytes = renderer_process.get_memory_info()[0]
- mem_usage_kb = float(mem_usage_bytes) / 1024
- pyauto_utils.PrintPerfResult('cpu', 'jsep01_call', cpu_usage, '%')
- pyauto_utils.PrintPerfResult('memory', 'jsep01_call', mem_usage_kb, 'KiB')
-
- def testLocalPreview(self):
- """Brings up a local preview and ensures video is playing.
-
- This test will launch a window with a single tab and run a getUserMedia call
- which will give us access to the webcam and microphone. Then the javascript
- code will hook up the webcam data to the local-view video tag. We will
- detect video in that tag using the video detector, and if we see video
- moving the test passes.
- """
- self.LoadTestPageInOneTab()
- self.assertEquals('ok-got-stream', self.GetUserMedia(tab_index=0))
- self._StartDetectingVideo(tab_index=0, video_element='local-view')
-
- self._WaitForVideo(tab_index=0, expect_playing=True)
-
- def testHandlesNewGetUserMediaRequestSeparately(self):
- """Ensures WebRTC doesn't allow new requests to piggy-back on old ones."""
- self.LoadTestPageInTwoTabs()
-
- self.GetUserMedia(tab_index=0)
- self.GetUserMedia(tab_index=1)
- self.Connect("user_1", tab_index=0)
- self.Connect("user_2", tab_index=1)
-
- self.CreatePeerConnection(tab_index=0)
- self.AddUserMediaLocalStream(tab_index=0)
- self.EstablishCall(from_tab_with_index=0, to_tab_with_index=1)
-
- self.assertEquals('failed-with-error-PERMISSION_DENIED',
- self.GetUserMedia(tab_index=0, action='cancel'))
- self.assertEquals('failed-with-error-PERMISSION_DENIED',
- self.GetUserMedia(tab_index=0, action='dismiss'))
-
- def _SetupCall(self, request_video, request_audio):
- """Gets user media and establishes a call.
-
- Assumes that two tabs are already opened with a suitable test page.
-
- Args:
- request_video: Whether to request video.
- request_audio: Whether to request audio.
- """
- self.assertEquals('ok-got-stream', self.GetUserMedia(
- tab_index=0, request_video=request_video, request_audio=request_audio))
- self.assertEquals('ok-got-stream', self.GetUserMedia(
- tab_index=1, request_video=request_video, request_audio=request_audio))
- self.Connect('user_1', tab_index=0)
- self.Connect('user_2', tab_index=1)
-
- self.CreatePeerConnection(tab_index=0)
- self.AddUserMediaLocalStream(tab_index=0)
- self.EstablishCall(from_tab_with_index=0, to_tab_with_index=1)
-
- if request_video:
- self._StartDetectingVideo(tab_index=0, video_element='remote-view')
- self._StartDetectingVideo(tab_index=1, video_element='remote-view')
-
- self._WaitForVideo(tab_index=0, expect_playing=True)
- self._WaitForVideo(tab_index=1, expect_playing=True)
-
- def _StartDetectingVideo(self, tab_index, video_element):
- self.assertEquals('ok-started', self.ExecuteJavascript(
- 'startDetection("%s", "frame-buffer", 320, 240)' % video_element,
- tab_index=tab_index));
-
- def _WaitForVideo(self, tab_index, expect_playing):
- # TODO(phoglund): Remove this hack if we manage to get a more stable Linux
- # bot to run these tests.
- if self.IsLinux():
- print "Linux; pretending to wait for video..."
- time.sleep(1)
- return
-
- expect_retval='video-playing' if expect_playing else 'video-not-playing'
-
- video_playing = self.WaitUntil(
- function=lambda: self.ExecuteJavascript('isVideoPlaying()',
- tab_index=tab_index),
- expect_retval=expect_retval)
- self.assertTrue(video_playing,
- msg= 'Timed out while waiting for isVideoPlaying to ' +
- 'return ' + expect_retval + '.')
-
- def _GetChromeRendererProcess(self, tab_index):
- """Returns the Chrome renderer process as a psutil process wrapper."""
- tab_info = self.GetBrowserInfo()['windows'][0]['tabs'][tab_index]
- renderer_id = tab_info['renderer_pid']
- if not renderer_id:
- self.fail('Can not find the tab renderer process.')
- return psutil.Process(renderer_id)
-
-
-if __name__ == '__main__':
- pyauto_functional.Main()
diff --git a/chrome/test/functional/webrtc_test_base.py b/chrome/test/functional/webrtc_test_base.py
deleted file mode 100755
index 973691c5b8..0000000000
--- a/chrome/test/functional/webrtc_test_base.py
+++ /dev/null
@@ -1,215 +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 platform
-import re
-import subprocess
-
-import pyauto
-
-
-class MissingRequiredBinaryException(Exception):
- pass
-
-
-class WebrtcTestBase(pyauto.PyUITest):
- """This base class provides helpers for WebRTC calls."""
-
- DEFAULT_TEST_PAGE = 'webrtc_jsep01_test.html'
-
- def ExtraChromeFlags(self):
- """Adds flags to the Chrome command line."""
- extra_flags = ['--enable-data-channels', '--enable-dcheck']
- return pyauto.PyUITest.ExtraChromeFlags(self) + extra_flags
-
- def LoadTestPageInTwoTabs(self, test_page=DEFAULT_TEST_PAGE):
- url = self.GetFileURLForDataPath('webrtc', test_page)
- self.NavigateToURL(url)
- self.AppendTab(pyauto.GURL(url))
-
- def LoadTestPageInOneTab(self, test_page=DEFAULT_TEST_PAGE):
- url = self.GetFileURLForDataPath('webrtc', test_page)
- self.NavigateToURL(url)
-
- def GetUserMedia(self, tab_index, action='accept',
- request_video=True, request_audio=True):
- """Acquires webcam or mic for one tab and returns the result.
-
- Args:
- tab_index: The tab to request user media on.
- action: The action to take on the info bar. Can be 'accept', 'cancel' or
- 'dismiss'.
- request_video: Whether to request video.
- request_audio: Whether to request audio.
-
- Returns:
- A string as specified by the getUserMedia javascript function.
- """
- constraints = '{ video: %s, audio: %s }' % (str(request_video).lower(),
- str(request_audio).lower())
- self.assertEquals('ok-requested', self.ExecuteJavascript(
- 'getUserMedia("%s")' % constraints, tab_index=tab_index))
-
- self.WaitForInfobarCount(1, tab_index=tab_index)
- self.PerformActionOnInfobar(action, infobar_index=0, tab_index=tab_index)
- self.WaitForGetUserMediaResult(tab_index=0)
-
- result = self.GetUserMediaResult(tab_index=0)
- self.AssertNoFailures(tab_index)
- return result
-
- def WaitForGetUserMediaResult(self, tab_index):
- """Waits until WebRTC has responded to a getUserMedia query.
-
- Fails an assert if WebRTC doesn't respond within the default timeout.
-
- Args:
- tab_index: the tab to query.
- """
- def HasResult():
- return self.GetUserMediaResult(tab_index) != 'not-called-yet'
- self.assertTrue(self.WaitUntil(HasResult),
- msg='Timed out while waiting for getUserMedia callback.')
-
- def GetUserMediaResult(self, tab_index):
- """Retrieves WebRTC's answer to a user media query.
-
- Args:
- tab_index: the tab to query.
-
- Returns:
- Specified in obtainGetUserMediaResult() in getusermedia.js.
- """
- return self.ExecuteJavascript(
- 'obtainGetUserMediaResult()', tab_index=tab_index)
-
- def AssertNoFailures(self, tab_index):
- """Ensures the javascript hasn't registered any asynchronous errors.
-
- Args:
- tab_index: The tab to check.
- """
- self.assertEquals('ok-no-errors', self.ExecuteJavascript(
- 'getAnyTestFailures()', tab_index=tab_index))
-
- def Connect(self, user_name, tab_index):
- self.assertEquals('ok-connected', self.ExecuteJavascript(
- 'connect("http://localhost:8888", "%s")' % user_name,
- tab_index=tab_index))
- self.AssertNoFailures(tab_index)
-
- def CreatePeerConnection(self, tab_index):
- self.assertEquals('ok-peerconnection-created', self.ExecuteJavascript(
- 'preparePeerConnection()', tab_index=tab_index))
-
- def AddUserMediaLocalStream(self, tab_index):
- self.assertEquals('ok-added', self.ExecuteJavascript(
- 'addLocalStream()', tab_index=tab_index))
-
- def AddWebAudioFile(self, tab_index, input_relative_path):
- """The path must be relative to where the javascript is.
-
- This call just loads and adds a file to a peer connection, but it doesn't
- start to play it until you call PlayWebAudioFile.
- """
- self.assertEquals('ok-added', self.ExecuteJavascript(
- 'addAudioFile("%s")' % re.escape(input_relative_path),
- tab_index=tab_index))
-
- def PlayWebAudioFile(self, tab_index):
- """Plays a web audio file which was added earlier."""
- self.assertEquals('ok-playing', self.ExecuteJavascript(
- 'playAudioFile()', tab_index=tab_index))
-
- def EstablishCall(self, from_tab_with_index, to_tab_with_index):
- self.WaitUntilPeerConnects(tab_index=from_tab_with_index)
-
- self.assertEquals('ok-negotiating', self.ExecuteJavascript(
- 'negotiateCall()', tab_index=from_tab_with_index))
- self.AssertNoFailures(from_tab_with_index)
-
- self.WaitUntilReadyState(ready_state='active',
- tab_index=from_tab_with_index)
-
- # Double-check the call reached the other side.
- self.WaitUntilReadyState(ready_state='active',
- tab_index=to_tab_with_index)
-
- def HangUp(self, from_tab_with_index):
- self.assertEquals('ok-call-hung-up', self.ExecuteJavascript(
- 'hangUp()', tab_index=from_tab_with_index))
- self.WaitUntilHangUpVerified(tab_index=from_tab_with_index)
- self.AssertNoFailures(tab_index=from_tab_with_index)
-
- def WaitUntilPeerConnects(self, tab_index):
- peer_connected = self.WaitUntil(
- function=lambda: self.ExecuteJavascript('remotePeerIsConnected()',
- tab_index=tab_index),
- expect_retval='peer-connected')
- self.assertTrue(peer_connected,
- msg='Timed out while waiting for peer to connect.')
-
- def WaitUntilReadyState(self, ready_state, tab_index):
- got_ready_state = self.WaitUntil(
- function=lambda: self.ExecuteJavascript('getPeerConnectionReadyState()',
- tab_index=tab_index),
- expect_retval=ready_state)
- self.assertTrue(got_ready_state,
- msg=('Timed out while waiting for peer connection ready '
- 'state to change to %s for tab %d.' % (ready_state,
- tab_index)))
-
- def WaitUntilHangUpVerified(self, tab_index):
- self.WaitUntilReadyState('no-peer-connection', tab_index=tab_index)
-
- def Disconnect(self, tab_index):
- self.assertEquals('ok-disconnected', self.ExecuteJavascript(
- 'disconnect()', tab_index=tab_index))
-
- def BinPathForPlatform(self, path):
- """Form a platform specific path to a binary.
-
- Args:
- path(string): The path to the binary without an extension.
- Return:
- (string): The platform-specific bin path.
- """
- if self.IsWin():
- path += '.exe'
- return path
-
- def PlatformIsWinXP(self):
- """Check if the executing platform is Windows XP.
-
- Return:
- True if the platform is Windows XP.
- """
- return platform.system() == 'Windows' and platform.release() == 'XP'
-
- def StartPeerConnectionServer(self):
- """Starts peerconnection_server.
-
- Peerconnection_server is a custom binary allowing two WebRTC clients to find
- each other. For more details, see the source code which is available at the
- site http://code.google.com/p/libjingle/source/browse/ (make sure to browse
- to trunk/talk/examples/peerconnection/server).
- """
- # Start the peerconnection_server. It should be next to chrome.
- binary_path = os.path.join(self.BrowserPath(), 'peerconnection_server')
- binary_path = self.BinPathForPlatform(binary_path)
-
- if not os.path.exists(binary_path):
- raise MissingRequiredBinaryException(
- 'Could not locate peerconnection_server. Have you built the '
- 'peerconnection_server target? We expect to have a '
- 'peerconnection_server binary next to the chrome binary.')
-
- self._server_process = subprocess.Popen(binary_path)
-
- def StopPeerConnectionServer(self):
- """Stops the peerconnection_server."""
- assert self._server_process
- self._server_process.kill()
diff --git a/chrome/test/functional/webrtc_video_quality.py b/chrome/test/functional/webrtc_video_quality.py
deleted file mode 100755
index 2b20f42ccd..0000000000
--- a/chrome/test/functional/webrtc_video_quality.py
+++ /dev/null
@@ -1,401 +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 subprocess
-import sys
-
-import pyauto_functional
-import pyauto
-import pyauto_paths
-import pyauto_utils
-import webrtc_test_base
-
-# If you change the port number, don't forget to modify video_extraction.js too.
-_PYWEBSOCKET_PORT_NUMBER = '12221'
-
-_HOME_ENV_NAME = 'HOMEPATH' if pyauto.PyUITest.IsWin() else 'HOME'
-_WORKING_DIR = os.path.join(os.environ[_HOME_ENV_NAME], 'webrtc_video_quality')
-
-# This is the reference file that is being played by the virtual web camera.
-_REFERENCE_YUV_FILE = os.path.join(_WORKING_DIR, 'reference_video.yuv')
-
-# The YUV file is the file produced by rgba_to_i420_converter.
-_OUTPUT_YUV_FILE = os.path.join(_WORKING_DIR, 'captured_video.yuv')
-
-
-class MissingRequiredToolException(Exception):
- pass
-
-
-class FailedToRunToolException(Exception):
- pass
-
-
-class WebrtcVideoQualityTest(webrtc_test_base.WebrtcTestBase):
- """Test the video quality of the WebRTC output.
-
- Prerequisites: This test case must run on a machine with a virtual webcam that
- plays video from the reference file located in the location defined by
- _REFERENCE_YUV_FILE. You must also compile the chromium_builder_webrtc target
- before you run this test to get all the tools built.
- The external compare_videos.py script also depends on two external executables
- which must be located in the PATH when running this test.
- * zxing (see the CPP version at https://code.google.com/p/zxing)
- * ffmpeg 0.11.1 or compatible version (see http://www.ffmpeg.org)
-
- The test case will launch a custom binary (peerconnection_server) which will
- allow two WebRTC clients to find each other.
-
- The test also runs several other custom binaries - rgba_to_i420 converter and
- frame_analyzer. Both tools can be found under third_party/webrtc/tools. The
- test also runs a stand alone Python implementation of a WebSocket server
- (pywebsocket) and a barcode_decoder script.
- """
-
- def setUp(self):
- pyauto.PyUITest.setUp(self)
- if not os.path.exists(_WORKING_DIR):
- self.fail('Cannot find the working directory for the reference video and '
- 'the temporary files: %s' % _WORKING_DIR)
- if not os.path.exists(_REFERENCE_YUV_FILE):
- self.fail('Cannot find the reference file to be used for video quality '
- 'comparison: %s' % _REFERENCE_YUV_FILE)
- self.StartPeerConnectionServer()
-
- def tearDown(self):
- self._StopPywebsocketServer()
- self.StopPeerConnectionServer()
-
- pyauto.PyUITest.tearDown(self)
- self.assertEquals('', self.CheckErrorsAndCrashes())
-
- def _WebRtcCallWithHelperPage(self, test_page, helper_page):
-
- """Tests we can call, let run for some time and hang up with WebRTC.
-
- This test exercises pretty much the whole happy-case for the WebRTC
- JavaScript API. Currently, it exercises a normal call setup using the API
- defined at http://dev.w3.org/2011/webrtc/editor/webrtc.html. The API is
- still evolving.
-
- The test will load the supplied HTML file, which in turn will load different
- javascript files depending on which version of the signaling protocol
- we are running.
- The supplied HTML files will be loaded in two tabs and tell the web
- pages to start up WebRTC, which will acquire video and audio devices on the
- system. This will launch a dialog in Chrome which we click past using the
- automation controller. Then, we will order both tabs to connect the server,
- which will make the two tabs aware of each other. Once that is done we order
- one tab to call the other.
-
- We make sure that the javascript tells us that the call succeeded, lets it
- run for some time and try to hang up the call after that. While the call is
- running, we capture frames with the help of the functions in the
- video_extraction.js file.
-
- Args:
- test_page(string): The name of the test HTML page. It is looked for in the
- webrtc directory under chrome/test/data.
- helper_page(string): The name of the helper HTML page. It is looked for in
- the same directory as the test_page.
- """
- assert helper_page
- url = self.GetFileURLForDataPath('webrtc', test_page)
- helper_url = self.GetFileURLForDataPath('webrtc', helper_page)
-
- # Start the helper page in the first tab
- self.NavigateToURL(helper_url)
-
- # Start the test page in the second page.
- self.AppendTab(pyauto.GURL(url))
-
- self.assertEquals('ok-got-stream', self.GetUserMedia(tab_index=0))
- self.assertEquals('ok-got-stream', self.GetUserMedia(tab_index=1))
- self.Connect('user_1', tab_index=0)
- self.Connect('user_2', tab_index=1)
-
- self.CreatePeerConnection(tab_index=0)
- self.AddUserMediaLocalStream(tab_index=0)
- self.EstablishCall(from_tab_with_index=0, to_tab_with_index=1)
-
- # Wait for JavaScript to capture all the frames. In the HTML file we specify
- # how many seconds to capture frames.
- done_capturing = self.WaitUntil(
- function=lambda: self.ExecuteJavascript('doneFrameCapturing()',
- tab_index=1),
- expect_retval='done-capturing', retry_sleep=1.0,
- # TODO(phoglund): Temporary fix; remove after 2013-04-01
- timeout=90)
-
- self.assertTrue(done_capturing,
- msg='Timed out while waiting frames to be captured.')
-
- # The hang-up will automatically propagate to the second tab.
- self.HangUp(from_tab_with_index=0)
- self.WaitUntilHangUpVerified(tab_index=1)
-
- self.Disconnect(tab_index=0)
- self.Disconnect(tab_index=1)
-
- # Ensure we didn't miss any errors.
- self.AssertNoFailures(tab_index=0)
- self.AssertNoFailures(tab_index=1)
-
- def testVgaVideoQuality(self):
- """Tests the WebRTC video output for a VGA video input.
-
- On the bots we will be running fake webcam driver and we will feed a video
- with overlaid barcodes. In order to run the analysis on the output, we need
- to use the original input video as a reference video.
- """
- helper_page = webrtc_test_base.WebrtcTestBase.DEFAULT_TEST_PAGE
- self._StartVideoQualityTest(test_page='webrtc_video_quality_test.html',
- helper_page=helper_page,
- reference_yuv=_REFERENCE_YUV_FILE, width=640,
- height=480)
-
- def _StartVideoQualityTest(self, reference_yuv,
- test_page='webrtc_video_quality_test.html',
- helper_page='webrtc_jsep01_test.html',
- width=640, height=480):
- """Captures video output into a canvas and sends it to a server.
-
- This test captures the output frames of a WebRTC connection to a canvas and
- later sends them over WebSocket to a WebSocket server implemented in Python.
- At the server side we can store the frames for subsequent quality analysis.
-
- After the frames are sent to the pywebsocket server, we run the RGBA to I420
- converter, the barcode decoder and finally the frame analyzer. We also print
- everything to the Perf Graph for visualization
-
- Args:
- reference_yuv(string): The name of the reference YUV video that will be
- used in the analysis.
- test_page(string): The name of the test HTML page. To be looked for in the
- webrtc directory under chrome/test/data.
- helper_page(string): The name of the HTML helper page. To be looked for in
- the same directory as the test_page.
- width(int): The width of the test video frames.
- height(int): The height of the test video frames.
- """
- self._StartPywebsocketServer()
-
- self._WebRtcCallWithHelperPage(test_page, helper_page)
-
- # Wait for JavaScript to send all the frames to the server. The test will
- # have quite a lot of frames to send, so it will take at least several
- # seconds.
- no_more_frames = self.WaitUntil(
- function=lambda: self.ExecuteJavascript('haveMoreFramesToSend()',
- tab_index=1),
- expect_retval='no-more-frames', retry_sleep=1, timeout=150)
- self.assertTrue(no_more_frames,
- msg='Timed out while waiting for frames to send.')
-
- self.assertTrue(self._RunRGBAToI420Converter(width, height))
-
- stats_file = os.path.join(_WORKING_DIR, 'pyauto_stats.txt')
- analysis_result = self._CompareVideos(width, height, _OUTPUT_YUV_FILE,
- reference_yuv, stats_file)
- self._ProcessPsnrAndSsimOutput(analysis_result)
- self._ProcessFramesCountOutput(analysis_result)
-
- def _StartPywebsocketServer(self):
- """Starts the pywebsocket server."""
- print 'Starting pywebsocket server.'
-
- # Pywebsocket source directory.
- path_pyws_dir = os.path.join(pyauto_paths.GetThirdPartyDir(), 'pywebsocket',
- 'src')
-
- # Pywebsocket standalone server.
- path_to_pywebsocket= os.path.join(path_pyws_dir, 'mod_pywebsocket',
- 'standalone.py')
-
- # Path to the data handler to handle data received by the server.
- path_to_handler = os.path.join(pyauto_paths.GetSourceDir(), 'chrome',
- 'test', 'functional')
-
- # The python interpreter binary.
- python_interp = sys.executable
-
- # The pywebsocket start command - we could add --log-level=debug for debug.
- # -p stands for port, -d stands for root_directory (where the data handlers
- # are).
- start_cmd = [python_interp, path_to_pywebsocket,
- '-p', _PYWEBSOCKET_PORT_NUMBER,
- '-d', path_to_handler,]
- env = os.environ
- # Set PYTHONPATH to include the pywebsocket base directory.
- env['PYTHONPATH'] = (path_pyws_dir + os.path.pathsep +
- env.get('PYTHONPATH', ''))
-
- # Start the pywebsocket server. The server will not start instantly, so the
- # code opening websockets to it should take this into account.
- self._pywebsocket_server = subprocess.Popen(start_cmd, env=env)
-
- def _StopPywebsocketServer(self):
- """Stops the running instance of pywebsocket server."""
- print 'Stopping pywebsocket server.'
- if self._pywebsocket_server:
- self._pywebsocket_server.kill()
-
- def _RunRGBAToI420Converter(self, width, height):
- """Runs the RGBA to I420 converter.
-
- The rgba_to_i420_converter is part of the webrtc_test_tools target which
- should be build prior to running this test. The resulting binary should live
- next to Chrome.
-
- Args:
- width(int): The width of the frames to be converted and stitched together.
- height(int): The height of the frames to be converted and stitched.
-
- Returns:
- (bool): True if the conversion is successful, false otherwise.
- """
- path_to_rgba_converter = os.path.join(self.BrowserPath(),
- 'rgba_to_i420_converter')
- path_to_rgba_converter = os.path.abspath(path_to_rgba_converter)
- path_to_rgba_converter = self.BinPathForPlatform(path_to_rgba_converter)
-
- if not os.path.exists(path_to_rgba_converter):
- raise webrtc_test_base.MissingRequiredBinaryException(
- 'Could not locate rgba_to_i420_converter! Did you build the '
- 'webrtc_test_tools target?')
-
- # We produce an output file that will later be used as an input to the
- # barcode decoder and frame analyzer tools.
- start_cmd = [path_to_rgba_converter, '--frames_dir=%s' % _WORKING_DIR,
- '--output_file=%s' % _OUTPUT_YUV_FILE, '--width=%d' % width,
- '--height=%d' % height, '--delete_frames']
- print 'Start command: ', ' '.join(start_cmd)
- rgba_converter = subprocess.Popen(start_cmd, stdout=sys.stdout,
- stderr=sys.stderr)
- rgba_converter.wait()
- return rgba_converter.returncode == 0
-
- def _CompareVideos(self, width, height, captured_video_filename,
- reference_video_filename, stats_filename):
- """Compares the captured video with the reference video.
-
- The barcode decoder decodes the captured video containing barcodes overlaid
- into every frame of the video (produced by rgba_to_i420_converter). It
- produces a set of PNG images and a stats file that describes the relation
- between the filenames and the (decoded) frame number of each frame.
-
- Args:
- width(int): The frames width of the video to be decoded.
- height(int): The frames height of the video to be decoded.
- captured_video_filename(string): The captured video file we want to
- extract frame images and decode frame numbers from.
- reference_video_filename(string): The reference video file we want to
- compare the captured video quality with.
- stats_filename(string): Filename for the output file containing
- data that shows the relation between each frame filename and the
- reference file's frame numbers.
-
- Returns:
- (string): The output of the script.
-
- Raises:
- FailedToRunToolException: If the script fails to run.
- """
- path_to_analyzer = os.path.join(self.BrowserPath(), 'frame_analyzer')
- path_to_analyzer = os.path.abspath(path_to_analyzer)
- path_to_analyzer = self.BinPathForPlatform(path_to_analyzer)
-
- path_to_compare_script = os.path.join(pyauto_paths.GetThirdPartyDir(),
- 'webrtc', 'tools',
- 'compare_videos.py')
- if not os.path.exists(path_to_compare_script):
- raise MissingRequiredToolException('Cannot find the script at %s' %
- path_to_compare_script)
- python_interp = sys.executable
- cmd = [
- python_interp,
- path_to_compare_script,
- '--ref_video=%s' % reference_video_filename,
- '--test_video=%s' % captured_video_filename,
- '--frame_analyzer=%s' % path_to_analyzer,
- '--yuv_frame_width=%d' % width,
- '--yuv_frame_height=%d' % height,
- '--stats_file=%s' % stats_filename,
- ]
- print 'Start command: ', ' '.join(cmd)
-
- compare_videos = subprocess.Popen(cmd, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- output, error = compare_videos.communicate()
- if compare_videos.returncode != 0:
- raise FailedToRunToolException('Failed to run compare videos script!')
-
- return output
-
- def _ProcessFramesCountOutput(self, output):
- """Processes the analyzer output for the different frame counts.
-
- The frame analyzer outputs additional information about the number of unique
- frames captured, The max number of repeated frames in a sequence and the
- max number of skipped frames. These values are then written to the Perf
- Graph. (Note: Some of the repeated or skipped frames will probably be due to
- the imperfection of JavaScript timers.)
-
- Args:
- output(string): The output from the frame analyzer to be processed.
- """
- # The output from frame analyzer will be in the format:
- # <PSNR and SSIM stats>
- # Unique_frames_count:<value>
- # Max_repeated:<value>
- # Max_skipped:<value>
- unique_fr_pos = output.rfind('Unique_frames_count')
- result_str = output[unique_fr_pos:]
-
- result_list = result_str.split()
-
- for result in result_list:
- colon_pos = result.find(':')
- key = result[:colon_pos]
- value = result[colon_pos+1:]
- pyauto_utils.PrintPerfResult(key, 'VGA', value, '')
-
- def _ProcessPsnrAndSsimOutput(self, output):
- """Processes the analyzer output to extract the PSNR and SSIM values.
-
- The frame analyzer produces PSNR and SSIM results for every unique frame
- that has been captured. This method forms a list of all the psnr and ssim
- values and passes it to PrintPerfResult() for printing on the Perf Graph.
-
- Args:
- output(string): The output from the frame analyzer to be processed.
- """
- # The output is in the format:
- # BSTATS
- # psnr ssim; psnr ssim; ... psnr ssim;
- # ESTATS
- stats_beginning = output.find('BSTATS') # Get the beginning of the stats
- stats_ending = output.find('ESTATS') # Get the end of the stats
- stats_str = output[(stats_beginning + len('BSTATS')):stats_ending]
-
- stats_list = stats_str.split(';')
-
- psnr = []
- ssim = []
-
- for item in stats_list:
- item = item.strip()
- if item != '':
- entry = item.split(' ')
- psnr.append(float(entry[0]))
- ssim.append(float(entry[1]))
-
- pyauto_utils.PrintPerfResult('PSNR', 'VGA', psnr, '')
- pyauto_utils.PrintPerfResult('SSIM', 'VGA', ssim, '')
-
-
-if __name__ == '__main__':
- pyauto_functional.Main()
diff --git a/chrome/test/mini_installer/config/chrome_installed.prop b/chrome/test/mini_installer/config/chrome_installed.prop
index 9db2a192c9..1b95385a76 100644
--- a/chrome/test/mini_installer/config/chrome_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_installed.prop
@@ -1,4 +1,13 @@
{
+ "Files": {
+ "$LOCAL_APPDATA\\Google\\Chrome\\Application\\chrome.exe": {"exists": true},
+ "$LOCAL_APPDATA\\Google\\Chrome\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome.dll":
+ {"exists": true},
+ "$LOCAL_APPDATA\\Google\\Chrome\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\chrome.7z":
+ {"exists": true},
+ "$LOCAL_APPDATA\\Google\\Chrome\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe":
+ {"exists": true}
+ },
"RegistryEntries": {
"HKEY_CURRENT_USER\\Software\\Google\\Update\\Clients\\{8A69D345-D564-463c-AFF1-A69D9E530F96}":
{"exists": true}
diff --git a/chrome/test/mini_installer/config/chrome_not_installed.prop b/chrome/test/mini_installer/config/chrome_not_installed.prop
index a9f7685aff..dfa4ffd84b 100644
--- a/chrome/test/mini_installer/config/chrome_not_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_not_installed.prop
@@ -1,4 +1,7 @@
{
+ "Files": {
+ "$LOCAL_APPDATA\\Google\\Chrome\\Application": {"exists": false}
+ },
"RegistryEntries": {
"HKEY_CURRENT_USER\\Software\\Google\\Update\\Clients\\{8A69D345-D564-463c-AFF1-A69D9E530F96}":
{"exists": false}
diff --git a/chrome/test/mini_installer/config/config.config b/chrome/test/mini_installer/config/config.config
index ab7093801c..94725f6d59 100644
--- a/chrome/test/mini_installer/config/config.config
+++ b/chrome/test/mini_installer/config/config.config
@@ -4,7 +4,8 @@
["chrome_installed", ["chrome_installed.prop"]]
],
"actions": [
- ["install chrome", "mini_installer.exe --chrome --multi-install"],
+ ["install chrome",
+ "\"$MINI_INSTALLER\" --chrome --multi-install --do-not-launch-chrome"],
["uninstall chrome", "python uninstall_chrome.py"]
],
"tests": [
diff --git a/chrome/test/mini_installer/file_verifier.py b/chrome/test/mini_installer/file_verifier.py
new file mode 100644
index 0000000000..045cc4c96c
--- /dev/null
+++ b/chrome/test/mini_installer/file_verifier.py
@@ -0,0 +1,26 @@
+# 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 path_resolver
+
+
+def VerifyFiles(files):
+ """Verifies that the current files match the expectation dictionaries.
+
+ This method will throw an AssertionError if file state doesn't match the
+ provided expectation.
+
+ Args:
+ files: A dictionary whose keys are file paths and values are expectation
+ dictionaries. An expectation dictionary is a dictionary with the
+ following key and value:
+ 'exists' a boolean indicating whether the file should exist.
+ """
+ for file_path, expectation in files.iteritems():
+ file_exists = os.path.exists(path_resolver.ResolvePath(file_path))
+ assert expectation['exists'] == file_exists, \
+ ('File %s exists' % file_path) if file_exists else \
+ ('File %s is missing' % file_path)
diff --git a/chrome/test/mini_installer/path_resolver.py b/chrome/test/mini_installer/path_resolver.py
new file mode 100644
index 0000000000..570a584c11
--- /dev/null
+++ b/chrome/test/mini_installer/path_resolver.py
@@ -0,0 +1,45 @@
+# 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 string
+import win32api
+import win32com.client
+from win32com.shell import shell, shellcon
+
+
+def ResolvePath(path):
+ """Resolve variables in a file path, and return the resolved path.
+
+ This method resolves only variables defined below. It does not resolve
+ environment variables. Any dollar signs that are not part of variables must be
+ escaped with $$, otherwise a KeyError or a ValueError will be raised.
+
+ Args:
+ path: An absolute or relative path.
+
+ Returns:
+ A new path created by replacing
+ * $PROGRAM_FILES with the path to the Program Files folder,
+ * $LOCAL_APPDATA with the path to the Local Application Data folder,
+ * $MINI_INSTALLER with the path to the mini_installer, and
+ * $MINI_INSTALLER_FILE_VERSION with the file version of the
+ mini_installer.
+ """
+ program_files_path = shell.SHGetFolderPath(0, shellcon.CSIDL_PROGRAM_FILES,
+ None, 0)
+ local_appdata_path = shell.SHGetFolderPath(0, shellcon.CSIDL_LOCAL_APPDATA,
+ None, 0)
+ #TODO(sukolsak): Copy the mini_installer.exe from the build output into here.
+ mini_installer_path = os.path.abspath('mini_installer.exe')
+ mini_installer_file_version = win32com.client.Dispatch(
+ 'Scripting.FileSystemObject').GetFileVersion(mini_installer_path)
+
+ variable_mapping = {
+ 'PROGRAM_FILES': program_files_path,
+ 'LOCAL_APPDATA': local_appdata_path,
+ 'MINI_INSTALLER': mini_installer_path,
+ 'MINI_INSTALLER_FILE_VERSION': mini_installer_file_version,
+ }
+ return string.Template(path).substitute(variable_mapping)
diff --git a/chrome/test/mini_installer/test_installer.py b/chrome/test/mini_installer/test_installer.py
index a98347872f..8f5d9af182 100644
--- a/chrome/test/mini_installer/test_installer.py
+++ b/chrome/test/mini_installer/test_installer.py
@@ -15,6 +15,7 @@ import os
import subprocess
import unittest
+import path_resolver
import verifier
@@ -102,7 +103,11 @@ class InstallerTest(unittest.TestCase):
raise AssertionError("In state '%s', %s" % (state, e))
def _RunCommand(self, command):
- subprocess.call(command, shell=True)
+ exit_status = subprocess.call(path_resolver.ResolvePath(command),
+ shell=True)
+ if exit_status != 0:
+ self.fail('Command %s returned non-zero exit status %s' % (command,
+ exit_status))
def MergePropertyDictionaries(current_property, new_property):
diff --git a/chrome/test/mini_installer/uninstall_chrome.py b/chrome/test/mini_installer/uninstall_chrome.py
index fe2e8708eb..3680940870 100644
--- a/chrome/test/mini_installer/uninstall_chrome.py
+++ b/chrome/test/mini_installer/uninstall_chrome.py
@@ -2,9 +2,42 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+"""Uninstall Chrome.
+
+This script reads the uninstall command from registry, calls it, and verifies
+the output status code.
+"""
+
+import _winreg
+import argparse
import subprocess
+import sys
+
+
+def main():
+ parser = argparse.ArgumentParser(description='Uninstall Chrome.')
+ parser.add_argument('--system-level', dest='system_level',
+ action='store_const', const=True, default=False,
+ help='Uninstall Chrome at system level.')
+ args = parser.parse_args()
+
+ # TODO(sukolsak): Add support for uninstalling MSI-based Chrome installs when
+ # we support testing MSIs.
+ if args.system_level:
+ root_key = _winreg.HKEY_LOCAL_MACHINE
+ else:
+ root_key = _winreg.HKEY_CURRENT_USER
+ sub_key = ('SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\'
+ 'Google Chrome')
+ key = _winreg.OpenKey(root_key, sub_key, 0, _winreg.KEY_QUERY_VALUE)
+ uninstall_string, _ = _winreg.QueryValueEx(key, 'UninstallString')
+ exit_status = subprocess.call(uninstall_string, shell=True)
+ # The exit status for successful uninstallation of Chrome is 19 (see
+ # chrome/installer/util/util_constants.h).
+ if exit_status != 19:
+ raise Exception('Could not uninstall Chrome. The installer exited with '
+ 'status %d.' % exit_status)
+ return 0
-# TODO(sukolsak): This should read the uninstall command from the registry and
-# run that instead.
-subprocess.call('mini_installer.exe --uninstall --multi-install --chrome',
- shell=True)
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/chrome/test/mini_installer/verifier.py b/chrome/test/mini_installer/verifier.py
index 8a735ba19c..1436040e36 100644
--- a/chrome/test/mini_installer/verifier.py
+++ b/chrome/test/mini_installer/verifier.py
@@ -2,6 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import file_verifier
import registry_verifier
@@ -16,7 +17,9 @@ def Verify(property):
property: A property dictionary.
"""
for verifier_name, value in property.iteritems():
- if verifier_name == 'RegistryEntries':
+ if verifier_name == 'Files':
+ file_verifier.VerifyFiles(value)
+ elif verifier_name == 'RegistryEntries':
registry_verifier.VerifyRegistryEntries(value)
else:
# TODO(sukolsak): Implement other verifiers
diff --git a/chrome/test/nacl/nacl_browsertest.cc b/chrome/test/nacl/nacl_browsertest.cc
index e16aae4fa9..2cad78ab96 100644
--- a/chrome/test/nacl/nacl_browsertest.cc
+++ b/chrome/test/nacl/nacl_browsertest.cc
@@ -15,40 +15,13 @@
namespace {
-// These tests fail on Linux ASAN bots: <http://crbug.com/161709>.
-#if defined(OS_LINUX) && defined(ADDRESS_SANITIZER)
-#define MAYBE_SimpleLoad DISABLED_SimpleLoad
-#define MAYBE_ExitStatus0 DISABLED_ExitStatus0
-#define MAYBE_ExitStatus254 DISABLED_ExitStatus254
-#define MAYBE_ExitStatusNeg2 DISABLED_ExitStatusNeg2
-#define MAYBE_PPAPICore DISABLED_PPAPICore
-#define MAYBE_ProgressEvents DISABLED_ProgressEvents
-#define MAYBE_PnaclMimeType DISABLED_PnaclMimeType
-#define MAYBE_CrossOriginCORS DISABLED_CrossOriginCORS
-#define MAYBE_CrossOriginFail DISABLED_CrossOriginFail
-#define MAYBE_SameOriginCookie DISABLED_SameOriginCookie
-#define MAYBE_CORSNoCookie DISABLED_CORSNoCookie
-#define MAYBE_SysconfNprocessorsOnln DISABLED_SysconfNprocessorsOnln
-#else
-#define MAYBE_SimpleLoad SimpleLoad
-#define MAYBE_ExitStatus0 ExitStatus0
-#define MAYBE_ExitStatus254 ExitStatus254
-#define MAYBE_ExitStatusNeg2 ExitStatusNeg2
-#define MAYBE_PPAPICore PPAPICore
-#define MAYBE_ProgressEvents ProgressEvents
-#define MAYBE_PnaclMimeType PnaclMimeType
-#define MAYBE_CrossOriginCORS CrossOriginCORS
-#define MAYBE_CrossOriginFail CrossOriginFail
-#define MAYBE_SameOriginCookie SameOriginCookie
-#define MAYBE_CORSNoCookie CORSNoCookie
-# if defined(OS_WIN)
+#if defined(OS_WIN)
# define MAYBE_SysconfNprocessorsOnln DISABLED_SysconfNprocessorsOnln
-# else
+#else
# define MAYBE_SysconfNprocessorsOnln SysconfNprocessorsOnln
-# endif
#endif
-NACL_BROWSER_TEST_F(NaClBrowserTest, MAYBE_SimpleLoad, {
+NACL_BROWSER_TEST_F(NaClBrowserTest, SimpleLoad, {
RunLoadTest(FILE_PATH_LITERAL("nacl_load_test.html"));
})
@@ -62,33 +35,29 @@ IN_PROC_BROWSER_TEST_F(NaClBrowserTestPnaclWithOldCache,
RunNaClIntegrationTest(FILE_PATH_LITERAL("pnacl_error_handling.html"));
}
-NACL_BROWSER_TEST_F(NaClBrowserTest, MAYBE_ExitStatus0, {
+NACL_BROWSER_TEST_F(NaClBrowserTest, ExitStatus0, {
RunNaClIntegrationTest(FILE_PATH_LITERAL(
"pm_exit_status_test.html?trigger=exit0&expected_exit=0"));
})
-NACL_BROWSER_TEST_F(NaClBrowserTest, MAYBE_ExitStatus254, {
+NACL_BROWSER_TEST_F(NaClBrowserTest, ExitStatus254, {
RunNaClIntegrationTest(FILE_PATH_LITERAL(
"pm_exit_status_test.html?trigger=exit254&expected_exit=254"));
})
-NACL_BROWSER_TEST_F(NaClBrowserTest, MAYBE_ExitStatusNeg2, {
+NACL_BROWSER_TEST_F(NaClBrowserTest, ExitStatusNeg2, {
RunNaClIntegrationTest(FILE_PATH_LITERAL(
"pm_exit_status_test.html?trigger=exitneg2&expected_exit=254"));
})
-NACL_BROWSER_TEST_F(NaClBrowserTest, MAYBE_PPAPICore, {
+NACL_BROWSER_TEST_F(NaClBrowserTest, PPAPICore, {
RunNaClIntegrationTest(FILE_PATH_LITERAL("ppapi_ppb_core.html"));
})
-NACL_BROWSER_TEST_F(NaClBrowserTest, MAYBE_ProgressEvents, {
+NACL_BROWSER_TEST_F(NaClBrowserTest, ProgressEvents, {
RunNaClIntegrationTest(FILE_PATH_LITERAL("ppapi_progress_events.html"));
})
-NACL_BROWSER_TEST_F(NaClBrowserTest, MAYBE_PnaclMimeType, {
- RunLoadTest(FILE_PATH_LITERAL("pnacl_mime_type.html"));
-})
-
// Some versions of Visual Studio does not like preprocessor
// conditionals inside the argument of a macro, so we put the
// conditionals on a helper function. We are already in an anonymous
@@ -144,19 +113,19 @@ NACL_BROWSER_TEST_F(NaClBrowserTest, MAYBE_SysconfNprocessorsOnln, {
RunNaClIntegrationTest(path);
})
-IN_PROC_BROWSER_TEST_F(NaClBrowserTestStatic, MAYBE_CrossOriginCORS) {
+IN_PROC_BROWSER_TEST_F(NaClBrowserTestStatic, CrossOriginCORS) {
RunLoadTest(FILE_PATH_LITERAL("cross_origin/cors.html"));
}
-IN_PROC_BROWSER_TEST_F(NaClBrowserTestStatic, MAYBE_CrossOriginFail) {
+IN_PROC_BROWSER_TEST_F(NaClBrowserTestStatic, CrossOriginFail) {
RunLoadTest(FILE_PATH_LITERAL("cross_origin/fail.html"));
}
-IN_PROC_BROWSER_TEST_F(NaClBrowserTestStatic, MAYBE_SameOriginCookie) {
+IN_PROC_BROWSER_TEST_F(NaClBrowserTestStatic, SameOriginCookie) {
RunLoadTest(FILE_PATH_LITERAL("cross_origin/same_origin_cookie.html"));
}
-IN_PROC_BROWSER_TEST_F(NaClBrowserTestStatic, MAYBE_CORSNoCookie) {
+IN_PROC_BROWSER_TEST_F(NaClBrowserTestStatic, CORSNoCookie) {
RunLoadTest(FILE_PATH_LITERAL("cross_origin/cors_no_cookie.html"));
}
@@ -192,4 +161,12 @@ IN_PROC_BROWSER_TEST_F(NaClBrowserTestPnacl,
"pnacl_exception_handling_disabled.html"));
}
+IN_PROC_BROWSER_TEST_F(NaClBrowserTestPnacl, PnaclMimeType) {
+ RunLoadTest(FILE_PATH_LITERAL("pnacl_mime_type.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(NaClBrowserTestPnaclDisabled, PnaclMimeType) {
+ RunLoadTest(FILE_PATH_LITERAL("pnacl_mime_type.html"));
+}
+
} // namespace
diff --git a/chrome/test/nacl/nacl_browsertest_uma.cc b/chrome/test/nacl/nacl_browsertest_uma.cc
index eaabd8a150..a76776cc98 100644
--- a/chrome/test/nacl/nacl_browsertest_uma.cc
+++ b/chrome/test/nacl/nacl_browsertest_uma.cc
@@ -34,7 +34,7 @@ NACL_BROWSER_TEST_F(NaClBrowserTest, MAYBE_SuccessfulLoadUMA, {
LOAD_OK, 1);
// Make sure we have other important histograms.
- if (!IsPnacl()) {
+ if (!IsAPnaclTest()) {
histograms.ExpectTotalCount("NaCl.Perf.StartupTime.LoadModule", 1);
histograms.ExpectTotalCount("NaCl.Perf.StartupTime.Total", 1);
histograms.ExpectTotalCount("NaCl.Perf.Size.Manifest", 1);
diff --git a/chrome/test/nacl/nacl_browsertest_util.cc b/chrome/test/nacl/nacl_browsertest_util.cc
index 1c171a75e4..ace7305d6a 100644
--- a/chrome/test/nacl/nacl_browsertest_util.cc
+++ b/chrome/test/nacl/nacl_browsertest_util.cc
@@ -187,6 +187,15 @@ static void AddPnaclParm(const base::FilePath::StringType& url,
}
}
+static void AddPnaclDisabledParm(const base::FilePath::StringType& url,
+ base::FilePath::StringType* url_with_parm) {
+ if (url.find(FILE_PATH_LITERAL("?")) == base::FilePath::StringType::npos) {
+ *url_with_parm = url + FILE_PATH_LITERAL("?pnacl_disabled=1");
+ } else {
+ *url_with_parm = url + FILE_PATH_LITERAL("&pnacl_disabled=1");
+ }
+}
+
NaClBrowserTestBase::NaClBrowserTestBase() {
}
@@ -210,7 +219,11 @@ bool NaClBrowserTestBase::GetDocumentRoot(base::FilePath* document_root) {
return GetNaClVariantRoot(Variant(), document_root);
}
-bool NaClBrowserTestBase::IsPnacl() {
+bool NaClBrowserTestBase::IsAPnaclTest() {
+ return false;
+}
+
+bool NaClBrowserTestBase::IsPnaclDisabled() {
return false;
}
@@ -233,11 +246,15 @@ bool NaClBrowserTestBase::RunJavascriptTest(const GURL& url,
void NaClBrowserTestBase::RunLoadTest(
const base::FilePath::StringType& test_file) {
LoadTestMessageHandler handler;
- base::FilePath::StringType test_file_with_parm = test_file;
- if (IsPnacl()) {
- AddPnaclParm(test_file, &test_file_with_parm);
+ base::FilePath::StringType test_file_with_pnacl = test_file;
+ if (IsAPnaclTest()) {
+ AddPnaclParm(test_file, &test_file_with_pnacl);
+ }
+ base::FilePath::StringType test_file_with_both = test_file_with_pnacl;
+ if (IsPnaclDisabled()) {
+ AddPnaclDisabledParm(test_file_with_pnacl, &test_file_with_both);
}
- bool ok = RunJavascriptTest(TestURL(test_file_with_parm), &handler);
+ bool ok = RunJavascriptTest(TestURL(test_file_with_both), &handler);
ASSERT_TRUE(ok) << handler.error_message();
ASSERT_TRUE(handler.test_passed()) << "Test failed.";
}
@@ -245,11 +262,15 @@ void NaClBrowserTestBase::RunLoadTest(
void NaClBrowserTestBase::RunNaClIntegrationTest(
const base::FilePath::StringType& url_fragment) {
NaClIntegrationMessageHandler handler;
- base::FilePath::StringType url_fragment_with_parm = url_fragment;
- if (IsPnacl()) {
- AddPnaclParm(url_fragment, &url_fragment_with_parm);
+ base::FilePath::StringType url_fragment_with_pnacl = url_fragment;
+ if (IsAPnaclTest()) {
+ AddPnaclParm(url_fragment, &url_fragment_with_pnacl);
}
- bool ok = RunJavascriptTest(TestURL(url_fragment_with_parm), &handler);
+ base::FilePath::StringType url_fragment_with_both = url_fragment_with_pnacl;
+ if (IsPnaclDisabled()) {
+ AddPnaclDisabledParm(url_fragment_with_pnacl, &url_fragment_with_both);
+ }
+ bool ok = RunJavascriptTest(TestURL(url_fragment_with_both), &handler);
ASSERT_TRUE(ok) << handler.error_message();
ASSERT_TRUE(handler.test_passed()) << "Test failed.";
}
@@ -278,13 +299,24 @@ base::FilePath::StringType NaClBrowserTestPnacl::Variant() {
return FILE_PATH_LITERAL("pnacl");
}
-bool NaClBrowserTestPnacl::IsPnacl() {
+bool NaClBrowserTestPnacl::IsAPnaclTest() {
return true;
}
-void NaClBrowserTestPnacl::SetUpCommandLine(CommandLine* command_line) {
+base::FilePath::StringType NaClBrowserTestPnaclDisabled::Variant() {
+ return FILE_PATH_LITERAL("pnacl");
+}
+
+bool NaClBrowserTestPnaclDisabled::IsAPnaclTest() {
+ return true;
+}
+
+bool NaClBrowserTestPnaclDisabled::IsPnaclDisabled() {
+ return true;
+}
+void NaClBrowserTestPnaclDisabled::SetUpCommandLine(CommandLine* command_line) {
NaClBrowserTestBase::SetUpCommandLine(command_line);
- command_line->AppendSwitch(switches::kEnablePnacl);
+ command_line->AppendSwitch(switches::kDisablePnacl);
}
NaClBrowserTestPnaclWithOldCache::NaClBrowserTestPnaclWithOldCache() {
diff --git a/chrome/test/nacl/nacl_browsertest_util.h b/chrome/test/nacl/nacl_browsertest_util.h
index e79b5657c3..27e638b02e 100644
--- a/chrome/test/nacl/nacl_browsertest_util.h
+++ b/chrome/test/nacl/nacl_browsertest_util.h
@@ -74,7 +74,9 @@ class NaClBrowserTestBase : public InProcessBrowserTest {
// Where are the files for this class of test located on disk?
virtual bool GetDocumentRoot(base::FilePath* document_root);
- virtual bool IsPnacl();
+ virtual bool IsAPnaclTest();
+
+ virtual bool IsPnaclDisabled();
// Map a file relative to the variant directory to a URL served by the test
// web server.
@@ -116,11 +118,22 @@ class NaClBrowserTestGLibc : public NaClBrowserTestBase {
class NaClBrowserTestPnacl : public NaClBrowserTestBase {
public:
+ virtual base::FilePath::StringType Variant() OVERRIDE;
+
+ virtual bool IsAPnaclTest() OVERRIDE;
+};
+
+// Class used to test that when --disable-pnacl is specified the PNaCl mime
+// type is not available.
+class NaClBrowserTestPnaclDisabled : public NaClBrowserTestBase {
+ public:
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE;
virtual base::FilePath::StringType Variant() OVERRIDE;
- virtual bool IsPnacl() OVERRIDE;
+ virtual bool IsAPnaclTest() OVERRIDE;
+
+ virtual bool IsPnaclDisabled() OVERRIDE;
};
// Temporary class for running tests with the old cache enabled. Once all the
@@ -137,12 +150,10 @@ class NaClBrowserTestStatic : public NaClBrowserTestBase {
virtual bool GetDocumentRoot(base::FilePath* document_root) OVERRIDE;
};
-// PNaCl's cache and PPB_FileIO currently trip up under ASAN:
-// https://code.google.com/p/chromium/issues/detail?id=171810
// PNaCl tests take a long time on windows debug builds
// and sometimes time out. Disable until it is made faster:
// https://code.google.com/p/chromium/issues/detail?id=177555
-#if defined(ADDRESS_SANITIZER) || (defined(OS_WIN) && !defined(NDEBUG))
+#if (defined(OS_WIN) && !defined(NDEBUG))
#define MAYBE_PNACL(test_name) DISABLED_##test_name
#else
#define MAYBE_PNACL(test_name) test_name
@@ -157,7 +168,7 @@ body
#else
-// Otherwise, we have Glibc, Newlib and PNaCl tests
+// Otherwise, we have Glibc, Newlib and Pnacl tests
#define NACL_BROWSER_TEST_F(suite, name, body) \
IN_PROC_BROWSER_TEST_F(suite##Newlib, name) \
body \
diff --git a/chrome/test/perf/rendering/throughput_tests.cc b/chrome/test/perf/rendering/throughput_tests.cc
index 4aab2e766a..a21c6857ad 100644
--- a/chrome/test/perf/rendering/throughput_tests.cc
+++ b/chrome/test/perf/rendering/throughput_tests.cc
@@ -50,7 +50,8 @@ enum RunTestFlags {
kNone = 0,
kInternal = 1 << 0, // Test uses internal test data.
kAllowExternalDNS = 1 << 1, // Test needs external DNS lookup.
- kIsGpuCanvasTest = 1 << 2 // Test uses GPU accelerated canvas features.
+ kIsGpuCanvasTest = 1 << 2, // Test uses GPU accelerated canvas features.
+ kIsFlaky = 1 << 3
};
enum ThroughputTestFlags {
@@ -369,7 +370,9 @@ class ThroughputTest : public BrowserPerfTest {
frames = &events_sw;
EXPECT_EQ(0u, events_gpu.size());
}
- ASSERT_GT(frames->size(), 20u);
+ if (!(flags & kIsFlaky)) {
+ ASSERT_GT(frames->size(), 20u);
+ }
// Cull a few leading and trailing events as they might be unreliable.
TraceEventVector rate_events(frames->begin() + kIgnoreSomeFrames,
frames->end() - kIgnoreSomeFrames);
@@ -379,7 +382,7 @@ class ThroughputTest : public BrowserPerfTest {
// Print perf results.
double mean_ms = stats.mean_us / 1000.0;
- double std_dev_ms = stats.standard_deviation_us / 1000.0 / 1000.0;
+ double std_dev_ms = stats.standard_deviation_us / 1000.0;
std::string trace_name = use_compositor_thread_? "gpu_thread" :
ran_on_gpu ? "gpu" : "software";
std::string mean_and_error = base::StringPrintf("%f,%f", mean_ms,
@@ -536,11 +539,13 @@ IN_PROC_BROWSER_TEST_F(ThroughputTestSW, DrawImageShadowSW) {
}
IN_PROC_BROWSER_TEST_F(ThroughputTestGPU, DrawImageShadowGPU) {
- RunTest("canvas2d_balls_with_shadow", kNone | kIsGpuCanvasTest);
+ // TODO(junov): Fix test flakiness crbug.com/272383
+ RunTest("canvas2d_balls_with_shadow", kNone | kIsGpuCanvasTest | kIsFlaky);
}
IN_PROC_BROWSER_TEST_F(ThroughputTestThread, DrawImageShadowGPU) {
- RunTest("canvas2d_balls_with_shadow", kNone | kIsGpuCanvasTest);
+ // TODO(junov): Fix test flakiness crbug.com/272383
+ RunTest("canvas2d_balls_with_shadow", kNone | kIsGpuCanvasTest | kIsFlaky);
}
IN_PROC_BROWSER_TEST_F(ThroughputTestSW, CanvasToCanvasDrawSW) {
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index 0aef3d5aff..5952b08e8f 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -134,11 +134,6 @@ using content::RenderViewHost;
// Interface tests.
//
-// Disable tests under ASAN. http://crbug.com/104832.
-// This is a bit heavy handed, but the majority of these tests fail under ASAN.
-// See bug for history.
-#if !defined(ADDRESS_SANITIZER)
-
TEST_PPAPI_IN_PROCESS(Broker)
// Flaky, http://crbug.com/111355
TEST_PPAPI_OUT_OF_PROCESS(DISABLED_Broker)
@@ -477,7 +472,13 @@ IN_PROC_BROWSER_TEST_F(PPAPITest, URLLoader) {
LIST_TEST(URLLoader_PrefetchBufferThreshold)
);
}
-IN_PROC_BROWSER_TEST_F(OutOfProcessPPAPITest, URLLoader) {
+// Timing out on Windows dbg. http://crbug.com/95005
+#if defined(OS_WIN) && !defined(NDEBUG)
+#define MAYBE_URLLoader DISABLED_URLLoader
+#else
+#define MAYBE_URLLoader URLLoader
+#endif
+IN_PROC_BROWSER_TEST_F(OutOfProcessPPAPITest, MAYBE_URLLoader) {
RunTestViaHTTP(
LIST_TEST(URLLoader_BasicGET)
LIST_TEST(URLLoader_BasicPOST)
@@ -1312,8 +1313,13 @@ IN_PROC_BROWSER_TEST_F(PPAPINaClPNaClTest, AudioConfig) {
LIST_TEST(AudioConfig_InvalidConfigs));
}
-
-IN_PROC_BROWSER_TEST_F(PPAPITest, Audio) {
+// Flaky on ChromeOS dbg, http://crbug.com/277564.
+#if defined(OS_CHROMEOS) && !defined(NDEBUG)
+#define MAYBE_Audio DISABLED_Audio
+#else
+#define MAYBE_Audio Audio
+#endif
+IN_PROC_BROWSER_TEST_F(PPAPITest, MAYBE_Audio) {
RunTest(LIST_TEST(Audio_Creation)
LIST_TEST(Audio_DestroyNoStop)
LIST_TEST(Audio_Failures)
@@ -1521,5 +1527,3 @@ IN_PROC_BROWSER_TEST_F(OutOfProcessPPAPITest, FlashDRM) {
TEST_PPAPI_IN_PROCESS(TalkPrivate)
TEST_PPAPI_OUT_OF_PROCESS(TalkPrivate)
-
-#endif // ADDRESS_SANITIZER
diff --git a/chrome/test/ppapi/ppapi_test.cc b/chrome/test/ppapi/ppapi_test.cc
index 28f3eff211..0456e1ecf0 100644
--- a/chrome/test/ppapi/ppapi_test.cc
+++ b/chrome/test/ppapi/ppapi_test.cc
@@ -328,9 +328,8 @@ void PPAPINaClTest::SetUpCommandLine(CommandLine* command_line) {
EXPECT_TRUE(PathService::Get(chrome::FILE_NACL_PLUGIN, &plugin_lib));
EXPECT_TRUE(base::PathExists(plugin_lib));
- // Enable running NaCl outside of the store.
+ // Enable running (non-portable) NaCl outside of the Chrome web store.
command_line->AppendSwitch(switches::kEnableNaCl);
- command_line->AppendSwitch(switches::kEnablePnacl);
command_line->AppendSwitchASCII(switches::kAllowNaClSocketAPI, "127.0.0.1");
command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream);
command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
@@ -365,9 +364,8 @@ void PPAPINaClTestDisallowedSockets::SetUpCommandLine(
EXPECT_TRUE(PathService::Get(chrome::FILE_NACL_PLUGIN, &plugin_lib));
EXPECT_TRUE(base::PathExists(plugin_lib));
- // Enable running NaCl outside of the store.
+ // Enable running (non-portable) NaCl outside of the Chrome web store.
command_line->AppendSwitch(switches::kEnableNaCl);
- command_line->AppendSwitch(switches::kEnablePnacl);
}
// Append the correct mode and testcase string