diff options
author | petewil@chromium.org <petewil@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-26 03:17:36 +0000 |
---|---|---|
committer | petewil@chromium.org <petewil@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-26 03:17:36 +0000 |
commit | 23ee535fbe4ce2c4e70d5b3b460590e8cee4823a (patch) | |
tree | c68582c77c7deb6384740d2d2fd30b6f315063c6 /chrome/browser | |
parent | 580f17d6c756c68a78d379c6a2b2a11565566b24 (diff) | |
download | chromium_src-23ee535fbe4ce2c4e70d5b3b460590e8cee4823a.zip chromium_src-23ee535fbe4ce2c4e70d5b3b460590e8cee4823a.tar.gz chromium_src-23ee535fbe4ce2c4e70d5b3b460590e8cee4823a.tar.bz2 |
Canary test for push messaging.
This test will run on a modified buildbot (NOT in the normal checkin flow)
which our team will monitor. It will be used to detect checkins that pass
our unit tests, but break our scenario.
BUG=159757
Review URL: https://chromiumcodereview.appspot.com/11360142
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@184574 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
6 files changed, 396 insertions, 18 deletions
diff --git a/chrome/browser/extensions/api/push_messaging/push_messaging_api.cc b/chrome/browser/extensions/api/push_messaging/push_messaging_api.cc index 07ca1d3..4dcbee4 100644 --- a/chrome/browser/extensions/api/push_messaging/push_messaging_api.cc +++ b/chrome/browser/extensions/api/push_messaging/push_messaging_api.cc @@ -66,6 +66,11 @@ void PushMessagingEventRouter::OnMessage(const std::string& extension_id, message.subchannel_id = subchannel; message.payload = payload; + DVLOG(2) << "PushMessagingEventRouter::OnMessage" + << " payload = '" << payload + << "' subchannel = '" << subchannel + << "' extension = '" << extension_id << "'"; + scoped_ptr<base::ListValue> args(glue::OnMessage::Create(message)); scoped_ptr<extensions::Event> event(new extensions::Event( event_names::kOnPushMessage, args.Pass())); @@ -222,6 +227,7 @@ void PushMessagingGetChannelIdFunction::OnObfuscatedGaiaIdFetchFailure( } ReportResult(std::string(), error_text); + DVLOG(1) << "GetChannelId status: '" << error_text << "'"; } PushMessagingAPI::PushMessagingAPI(Profile* profile) : profile_(profile) { diff --git a/chrome/browser/extensions/api/push_messaging/push_messaging_canary_test.cc b/chrome/browser/extensions/api/push_messaging/push_messaging_canary_test.cc new file mode 100644 index 0000000..22350f2 --- /dev/null +++ b/chrome/browser/extensions/api/push_messaging/push_messaging_canary_test.cc @@ -0,0 +1,184 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> + +#include "base/string16.h" +#include "base/stringprintf.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/extensions/api/push_messaging/sync_setup_helper.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_test_message_listener.h" +#include "chrome/browser/extensions/platform_app_launcher.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_tabstrip.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/test/browser_test_utils.h" +#include "net/base/mock_host_resolver.h" + +namespace switches { +const char kPasswordFileForTest[] = "password-file-for-test"; +const char kOverrideUserDataDir[] = "override-user-data-dir"; +} + +namespace extensions { + +// This class provides tests specific to the push messaging +// canary test server. These tests require network access, +// and should not be run by normal buildbots as part of the normal +// checkin process. +class PushMessagingCanaryTest : public ExtensionApiTest { + public: + PushMessagingCanaryTest() { + sync_setup_helper_.reset(new SyncSetupHelper()); + } + + ~PushMessagingCanaryTest() { + } + + virtual void SetUp() OVERRIDE { + CommandLine* command_line = CommandLine::ForCurrentProcess(); + + if (command_line->HasSwitch(switches::kPasswordFileForTest)) { + base::FilePath password_file = + command_line->GetSwitchValuePath(switches::kPasswordFileForTest); + ASSERT_TRUE(sync_setup_helper_->ReadPasswordFile(password_file)); + } + + // The test framework overrides any command line user-data-dir + // argument with a /tmp/.org.chromium.Chromium.XXXXXX directory. + // That happens in the ChromeTestLauncherDelegate, and affects + // all unit tests (no opt out available). It intentionally erases + // any --user-data-dir switch if present and appends a new one. + // Re-override the default data dir for our test so we can persist + // the profile for this particular test so we can persist the max + // invalidation version between runs. + base::FilePath user_data_dir = + command_line->GetSwitchValuePath(switches::kUserDataDir); + base::FilePath override_user_data_dir = + command_line->GetSwitchValuePath(switches::kOverrideUserDataDir); + + if (!override_user_data_dir.empty() && + override_user_data_dir != user_data_dir) { + CommandLine::SwitchMap switches = + CommandLine::ForCurrentProcess()->GetSwitches(); + command_line->AppendSwitchPath(switches::kUserDataDir, + base::FilePath(override_user_data_dir)); + LOG(INFO) << "command line file override switch is " + << override_user_data_dir.AsUTF8Unsafe(); + } + + ExtensionApiTest::SetUp(); + } + + void InitializeSync(Profile* profile) { + ASSERT_TRUE(sync_setup_helper_->InitializeSync(profile)); + } + + // InProcessBrowserTest override. Destroys the sync client and sync + // profile created by the test. We must clean up ProfileSyncServiceHarness + // now before the profile is cleaned up. + virtual void CleanUpOnMainThread() OVERRIDE { + sync_setup_helper_.reset(); + } + + void StartRunningTest() { + // Get the credentials from the setup helper. + const std::string& client_id = sync_setup_helper_->client_id(); + const std::string& client_secret = sync_setup_helper_->client_secret(); + const std::string& refresh_token = sync_setup_helper_->refresh_token(); + + // Construct a JS string to pass in the parameters and start the test. + std::string script_string = StringPrintf( + "startTestWithCredentials('%s', '%s', '%s');", + client_id.c_str(), client_secret.c_str(), refresh_token.c_str()); + string16 script16 = UTF8ToUTF16(script_string); + + browser()->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost()-> + ExecuteJavascriptInWebFrame(string16(), script16); + } + + protected: + // Override InProcessBrowserTest. Change behavior of the default host + // resolver to avoid DNS lookup errors, so we can make network calls. + virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { + // The resolver object lifetime is managed by sync_test_setup, not here. + EnableDNSLookupForThisTest( + new net::RuleBasedHostResolverProc(host_resolver())); + } + + virtual void TearDownInProcessBrowserTestFixture() OVERRIDE { + DisableDNSLookupForThisTest(); + } + + + // Change behavior of the default host resolver to allow DNS lookup + // to proceed instead of being blocked by the test infrastructure. + void EnableDNSLookupForThisTest( + net::RuleBasedHostResolverProc* host_resolver) { + // mock_host_resolver_override_ takes ownership of the resolver. + scoped_refptr<net::RuleBasedHostResolverProc> resolver = + new net::RuleBasedHostResolverProc(host_resolver); + resolver->AllowDirectLookup("*.google.com"); + // On Linux, we use Chromium's NSS implementation which uses the following + // hosts for certificate verification. Without these overrides, running the + // integration tests on Linux causes error as we make external DNS lookups. + resolver->AllowDirectLookup("*.thawte.com"); + resolver->AllowDirectLookup("*.geotrust.com"); + resolver->AllowDirectLookup("*.gstatic.com"); + resolver->AllowDirectLookup("*.googleapis.com"); + mock_host_resolver_override_.reset( + new net::ScopedDefaultHostResolverProc(resolver)); + } + + // We need to reset the DNS lookup when we finish, or the test will fail. + void DisableDNSLookupForThisTest() { + mock_host_resolver_override_.reset(); + } + + private: + scoped_ptr<SyncSetupHelper> sync_setup_helper_; + + // This test needs to make live DNS requests for access to + // GAIA and sync server URLs under google.com. We use a scoped version + // to override the default resolver while the test is active. + scoped_ptr<net::ScopedDefaultHostResolverProc> mock_host_resolver_override_; +}; + +// Test that a push can make a round trip through the servers. +// This test is disabled to keep it from running on trybots since +// it requires network access, and it is not a good idea to run +// this test as part of a checkin or nightly test. +IN_PROC_BROWSER_TEST_F(PushMessagingCanaryTest, DISABLED_ReceivesPush) { + ResultCatcher catcher; + catcher.RestrictToProfile(browser()->profile()); + ExtensionTestMessageListener ready("ready", true); + + // Turn on sync so we can receive notifications over sync. + InitializeSync(browser()->profile()); + + const extensions::Extension* extension = + LoadExtension(test_data_dir_.AppendASCII("push_messaging_canary")); + ASSERT_TRUE(extension); + + DVLOG(1) << "extension dir is " << test_data_dir_.value(); + + ui_test_utils::NavigateToURL( + browser(), extension->GetResourceURL("push_messaging_canary.html")); + + EXPECT_TRUE(ready.WaitUntilSatisfied()); + + DVLOG(1) << "push_canary app done loading"; + + StartRunningTest(); + + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); + + LOG(INFO) << "Exiting Push Messaging Canary test"; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/push_messaging/sync_setup_helper.cc b/chrome/browser/extensions/api/push_messaging/sync_setup_helper.cc new file mode 100644 index 0000000..4ca347f --- /dev/null +++ b/chrome/browser/extensions/api/push_messaging/sync_setup_helper.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/api/push_messaging/sync_setup_helper.h" + +#include <vector> + +#include "base/file_util.h" +#include "base/string_util.h" +#include "chrome/browser/bookmarks/bookmark_model_factory.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/history/history_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/search_engines/template_url_service_factory.h" +#include "chrome/browser/sync/profile_sync_service_factory.h" +#include "chrome/browser/sync/profile_sync_service_harness.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/test/base/ui_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +SyncSetupHelper::SyncSetupHelper() {} + +SyncSetupHelper::~SyncSetupHelper() {} + +// Initialize sync clients and profiles for the case where we already +// have some valid sync data we wish to keep. +bool SyncSetupHelper::InitializeSync(Profile* profile) { + profile_ = profile; + client_.reset(new ProfileSyncServiceHarness(profile_, username_, password_)); + + // Hook up the sync listener. + if (!client_->InitializeSync()) { + LOG(ERROR) << "InitializeSync() failed."; + return false; + } + + // If the data is not already synced, setup sync. + if (!client_->IsDataSynced()) { + client_->SetupSync(); + } + + DVLOG(1) << "InitializeSync awaiting quiescence"; + + // Because clients may modify sync data as part of startup (for example local + // session-releated data is rewritten), we need to ensure all startup-based + // changes have propagated between the clients. + // This could take several seconds. + AwaitQuiescence(); + + DVLOG(1) << "InitializeSync reached quiescence"; + + return true; +} + +// Read the sync signin credentials from a file on the machine. +bool SyncSetupHelper::ReadPasswordFile(const base::FilePath& password_file) { + std::string file_contents; + bool success = file_util::ReadFileToString(password_file, &file_contents); + EXPECT_TRUE(success) + << "Password file \"" + << password_file.value() << "\" does not exist."; + if (!success) + return false; + + std::vector<std::string> tokens; + std::string delimiters = "\r\n"; + Tokenize(file_contents, delimiters, &tokens); + EXPECT_EQ(5U, tokens.size()) << "Password file \"" + << password_file.value() + << "\" must contain exactly five lines of text."; + if (5U != tokens.size()) + return false; + username_ = tokens[0]; + password_ = tokens[1]; + client_id_ = tokens[2]; + client_secret_ = tokens[3]; + refresh_token_ = tokens[4]; + return true; +} + +bool SyncSetupHelper::AwaitQuiescence() { + std::vector<ProfileSyncServiceHarness*> clients; + clients.push_back(client_.get()); + return ProfileSyncServiceHarness::AwaitQuiescence(clients); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/push_messaging/sync_setup_helper.h b/chrome/browser/extensions/api/push_messaging/sync_setup_helper.h new file mode 100644 index 0000000..9ea5709 --- /dev/null +++ b/chrome/browser/extensions/api/push_messaging/sync_setup_helper.h @@ -0,0 +1,79 @@ +// 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_PUSH_MESSAGING_SYNC_SETUP_HELPER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_PUSH_MESSAGING_SYNC_SETUP_HELPER_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/file_util.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/mock_host_resolver.h" + +class Profile; +class ProfileSyncServiceHarness; + +namespace extensions { + +class SyncSetupHelper { + public: + SyncSetupHelper(); + + ~SyncSetupHelper(); + + // Initialize sync clients and profiles for the case where we already + // have some valid sync data we wish to keep. + bool InitializeSync(Profile* profile); + + // Helper method used to read GAIA credentials from a local password file. + // Note: The password file must be a plain text file with two lines. + // The username is on the first line and the password is on the second line. + bool ReadPasswordFile(const base::FilePath& passwordFile); + + const std::string& client_id() const { return client_id_; } + const std::string& client_secret() const { return client_secret_; } + const std::string& refresh_token() const { return refresh_token_; } + + private: + // Block until all sync clients have completed their mutual sync cycles. + // Return true if a quiescent state was successfully reached. + bool AwaitQuiescence(); + + // GAIA account used by the test case. + std::string username_; + + // GAIA password used by the test case. + std::string password_; + + // GAIA client id for making the API call to push messaging. + std::string client_id_; + + // GAIA client secret for making the API call to push messaging. + std::string client_secret_; + + // GAIA refresh token for making the API call to push messaging. + std::string refresh_token_; + + // The sync profile used by a test. The profile is owned by the + // ProfileManager. + Profile* profile_; + + // Sync client used by a test. A sync client is associated with + // a sync profile, and implements methods that sync the contents of the + // profile with the server. + scoped_ptr<ProfileSyncServiceHarness> client_; + + // This test needs to make live DNS requests for access to + // GAIA and sync server URLs under google.com. We use a scoped version + // to override the default resolver while the test is active. + scoped_ptr<net::ScopedDefaultHostResolverProc> mock_host_resolver_override_; + + DISALLOW_COPY_AND_ASSIGN(SyncSetupHelper); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_PUSH_MESSAGING_SYNC_SETUP_HELPER_H_ diff --git a/chrome/browser/sync/profile_sync_service_harness.cc b/chrome/browser/sync/profile_sync_service_harness.cc index 7b5d337..6e74f58 100644 --- a/chrome/browser/sync/profile_sync_service_harness.cc +++ b/chrome/browser/sync/profile_sync_service_harness.cc @@ -162,17 +162,8 @@ bool ProfileSyncServiceHarness::SetupSync() { bool ProfileSyncServiceHarness::SetupSync( syncer::ModelTypeSet synced_datatypes) { - // Initialize the sync client's profile sync service object. - service_ = - ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_); - if (service_ == NULL) { - LOG(ERROR) << "SetupSync(): service_ is null."; + if (!InitializeSync()) return false; - } - - // Subscribe sync client to notifications from the profile sync service. - if (!service_->HasObserver(this)) - service_->AddObserver(this); // Tell the sync service that setup is in progress so we don't start syncing // until we've finished configuration. @@ -202,6 +193,8 @@ bool ProfileSyncServiceHarness::SetupSync( return false; } + DVLOG(2) << "Done waiting for sync backend"; + // Choose the datatypes to be synced. If all datatypes are to be synced, // set sync_everything to true; otherwise, set it to false. bool sync_everything = @@ -230,8 +223,10 @@ bool ProfileSyncServiceHarness::SetupSync( } // Wait for initial sync cycle to be completed. - DCHECK_EQ(wait_state_, WAITING_FOR_INITIAL_SYNC); - if (!AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, + DCHECK(wait_state_ == WAITING_FOR_INITIAL_SYNC || + wait_state_ == FULLY_SYNCED); + if (wait_state_ != FULLY_SYNCED && + !AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, "Waiting for initial sync cycle to complete.")) { LOG(ERROR) << "Initial sync cycle did not complete after " << kLiveSyncOperationTimeoutMs / 1000 @@ -258,14 +253,33 @@ bool ProfileSyncServiceHarness::SetupSync( return true; } +bool ProfileSyncServiceHarness::InitializeSync() { + // Initialize the sync client's profile sync service object. + service_ = + ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_); + if (service_ == NULL) { + LOG(ERROR) << "InitializeSync(): service_ is null."; + return false; + } + + // Subscribe sync client to notifications from the profile sync service. + if (!service_->HasObserver(this)) + service_->AddObserver(this); + + return true; +} + bool ProfileSyncServiceHarness::TryListeningToMigrationEvents() { browser_sync::BackendMigrator* migrator = service_->GetBackendMigratorForTest(); - if (migrator && !migrator->HasMigrationObserver(this)) { + + if (!migrator) + return false; + + if (!migrator->HasMigrationObserver(this)) { migrator->AddMigrationObserver(this); - return true; } - return false; + return true; } void ProfileSyncServiceHarness::SignalStateCompleteWithNextState( diff --git a/chrome/browser/sync/profile_sync_service_harness.h b/chrome/browser/sync/profile_sync_service_harness.h index 83989a3..f6b55a3 100644 --- a/chrome/browser/sync/profile_sync_service_harness.h +++ b/chrome/browser/sync/profile_sync_service_harness.h @@ -60,6 +60,12 @@ class ProfileSyncServiceHarness // in |synced_datatypes|. bool SetupSync(syncer::ModelTypeSet synced_datatypes); + // Prepare for setting up to sync, but without clearing the existing items. + bool InitializeSync(); + + // Returns true if the sync client has no unsynced items. + bool IsDataSynced(); + // ProfileSyncServiceObserver implementation. virtual void OnStateChanged() OVERRIDE; @@ -294,9 +300,6 @@ class ProfileSyncServiceHarness bool IsDataSyncedImpl( const syncer::sessions::SyncSessionSnapshot& snapshot); - // Returns true if the sync client has no unsynced items. - bool IsDataSynced(); - // Returns true if the sync client has no unsynced items and its progress // markers are believed to be up to date. // |