blob: 3cc210ca315e67dd9c74ac5561ada980ae1be2d4 [file] [log] [blame]
// 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/chrome_to_mobile_service.h"
#include "base/prefs/pref_service.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/chrome_to_mobile_service_factory.h"
#include "chrome/browser/command_updater.h"
#include "chrome/browser/signin/token_service.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_command_controller.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/extensions/feature_switch.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/web_contents.h"
#include "google_apis/gaia/gaia_constants.h"
namespace {
using extensions::FeatureSwitch;
class ChromeToMobileServiceTest : public BrowserWithTestWindowTest {
public:
ChromeToMobileServiceTest();
virtual ~ChromeToMobileServiceTest();
// Get the ChromeToMobileService for the browser's profile.
ChromeToMobileService* GetService() const;
// Ensures the returned command state matches UpdateAndGetCommandState().
bool UpdateAndGetVerifiedCommandState();
// Set |count| available mock mobile devices in the profile' prefs.
void SetDeviceCount(size_t count);
// Fulfill the requirements to enabling the feature.
void FulfillFeatureRequirements();
private:
FeatureSwitch::ScopedOverride enable_action_box_;
DISALLOW_COPY_AND_ASSIGN(ChromeToMobileServiceTest);
};
// Chrome To Mobile is currently gated on the Action Box UI,
// so need to enable this feature for the test.
ChromeToMobileServiceTest::ChromeToMobileServiceTest()
: enable_action_box_(FeatureSwitch::action_box(), true) {}
ChromeToMobileServiceTest::~ChromeToMobileServiceTest() {}
ChromeToMobileService* ChromeToMobileServiceTest::GetService() const {
return ChromeToMobileServiceFactory::GetForProfile(profile());
}
bool ChromeToMobileServiceTest::UpdateAndGetVerifiedCommandState() {
bool state = ChromeToMobileService::UpdateAndGetCommandState(browser());
CommandUpdater* updater = browser()->command_controller()->command_updater();
EXPECT_EQ(state, updater->IsCommandEnabled(IDC_CHROME_TO_MOBILE_PAGE));
return state;
}
void ChromeToMobileServiceTest::SetDeviceCount(size_t count) {
ListValue mobiles;
for (size_t i = 0; i < count; ++i)
mobiles.Append(new DictionaryValue());
profile()->GetPrefs()->Set(prefs::kChromeToMobileDeviceList, mobiles);
}
void ChromeToMobileServiceTest::FulfillFeatureRequirements() {
AddTab(browser(), GURL("http://foo"));
SetDeviceCount(1);
GetService()->OnInvalidatorStateChange(syncer::INVALIDATIONS_ENABLED);
EXPECT_TRUE(UpdateAndGetVerifiedCommandState());
}
// Test that GetMobiles and HasMobiles require Sync Invalidations being enabled.
TEST_F(ChromeToMobileServiceTest, GetMobiles) {
ChromeToMobileService* service = GetService();
// Send a fake notification that Sync Invalidations are disabled.
service->OnInvalidatorStateChange(syncer::TRANSIENT_INVALIDATION_ERROR);
EXPECT_EQ(NULL, service->GetMobiles());
EXPECT_FALSE(service->HasMobiles());
// Add a mock mobile device to the profile prefs.
SetDeviceCount(1);
// GetMobiles() still returns NULL until Sync Invalidations are enabled.
EXPECT_EQ(NULL, service->GetMobiles());
EXPECT_FALSE(service->HasMobiles());
service->OnInvalidatorStateChange(syncer::INVALIDATIONS_ENABLED);
EXPECT_EQ(1U, service->GetMobiles()->GetSize());
EXPECT_TRUE(service->HasMobiles());
// Adding and removing devices works while Sync Invalidations are enabled.
SetDeviceCount(0);
EXPECT_EQ(0U, service->GetMobiles()->GetSize());
EXPECT_FALSE(service->HasMobiles());
SetDeviceCount(2);
EXPECT_EQ(2U, service->GetMobiles()->GetSize());
EXPECT_TRUE(service->HasMobiles());
// GetMobiles() returns NULL after Sync Invalidations are disabled.
service->OnInvalidatorStateChange(syncer::TRANSIENT_INVALIDATION_ERROR);
EXPECT_EQ(NULL, service->GetMobiles());
EXPECT_FALSE(service->HasMobiles());
service->OnInvalidatorStateChange(syncer::INVALIDATION_CREDENTIALS_REJECTED);
EXPECT_EQ(NULL, service->GetMobiles());
EXPECT_FALSE(service->HasMobiles());
}
// Test fulfilling the requirements to enable the feature.
TEST_F(ChromeToMobileServiceTest, RequirementsToEnable) {
// Send a fake notification that Sync Invalidations are disabled.
GetService()->OnInvalidatorStateChange(syncer::TRANSIENT_INVALIDATION_ERROR);
// Navigate to a page with a URL that is valid to send.
AddTab(browser(), GURL("http://foo"));
EXPECT_FALSE(UpdateAndGetVerifiedCommandState());
// Add a mock mobile device to the profile prefs.
SetDeviceCount(1);
EXPECT_FALSE(UpdateAndGetVerifiedCommandState());
// Send a fake notification that Sync Invalidations are enabled.
GetService()->OnInvalidatorStateChange(syncer::INVALIDATIONS_ENABLED);
EXPECT_TRUE(UpdateAndGetVerifiedCommandState());
}
// Test that the feature handles mobile device count changes properly.
TEST_F(ChromeToMobileServiceTest, MobileDevicesAreRequired) {
FulfillFeatureRequirements();
// Removing all devices disables the feature.
SetDeviceCount(0);
EXPECT_FALSE(UpdateAndGetVerifiedCommandState());
// Restoring mobile devices re-enables the feature.
SetDeviceCount(1);
EXPECT_TRUE(UpdateAndGetVerifiedCommandState());
SetDeviceCount(3);
EXPECT_TRUE(UpdateAndGetVerifiedCommandState());
}
// Test that the feature handles Sync Invalidations state changes properly.
TEST_F(ChromeToMobileServiceTest, SyncInvalidationsAreRequired) {
FulfillFeatureRequirements();
// Disabling Sync Invalidations disables the feature.
GetService()->OnInvalidatorStateChange(syncer::TRANSIENT_INVALIDATION_ERROR);
EXPECT_FALSE(UpdateAndGetVerifiedCommandState());
// Re-enabling Sync Invalidations re-enables the feature.
GetService()->OnInvalidatorStateChange(syncer::INVALIDATIONS_ENABLED);
EXPECT_TRUE(UpdateAndGetVerifiedCommandState());
}
// Test that the feature handles various page URLs properly.
TEST_F(ChromeToMobileServiceTest, CertainSchemesAreRequired) {
FulfillFeatureRequirements();
// Only http:, https:, and ftp: URLs are valid to send.
struct {
std::string url;
bool enabled;
} cases[] = {
{ "http://foo", true }, { "https://foo", true }, { "ftp://foo", true },
{ "about:foo", false }, { "chrome://foo", false }, { "file://foo", false },
{ "data://foo", false }, { "view-source:foo", false },
};
content::NavigationController* controller =
&browser()->tab_strip_model()->GetActiveWebContents()->GetController();
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
NavigateAndCommit(controller, GURL(cases[i].url));
EXPECT_EQ(cases[i].enabled, UpdateAndGetVerifiedCommandState());
}
}
// Ensure that only relevant notifications invalidate the access token.
TEST_F(ChromeToMobileServiceTest, TokenNotifications) {
const char dummy_string[] = "dummy";
ChromeToMobileService* service = GetService();
service->SetAccessTokenForTest(dummy_string);
ASSERT_FALSE(service->GetAccessTokenForTest().empty());
// Send dummy service/token details (should not clear the token).
TokenService::TokenAvailableDetails dummy_details(dummy_string, dummy_string);
service->Observe(chrome::NOTIFICATION_TOKEN_AVAILABLE,
content::Source<ChromeToMobileServiceTest>(this),
content::Details<TokenService::TokenAvailableDetails>(&dummy_details));
EXPECT_FALSE(service->GetAccessTokenForTest().empty());
// Send a Gaia OAuth2 Login service dummy token (should clear the token).
TokenService::TokenAvailableDetails login_details(
GaiaConstants::kGaiaOAuth2LoginRefreshToken, dummy_string);
service->Observe(chrome::NOTIFICATION_TOKEN_AVAILABLE,
content::Source<ChromeToMobileServiceTest>(this),
content::Details<TokenService::TokenAvailableDetails>(&login_details));
EXPECT_TRUE(service->GetAccessTokenForTest().empty());
}
} // namespace