blob: bd5966301ac73ed74ee6e2179ec5fbe8235084af [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 "base/bind.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop.h"
#include "base/synchronization/waitable_event.h"
#include "chrome/browser/extensions/app_notify_channel_setup.h"
#include "chrome/browser/extensions/app_notify_channel_ui.h"
#include "chrome/browser/signin/token_service_factory.h"
#include "chrome/browser/signin/token_service_unittest.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_pref_service.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/test_browser_thread.h"
#include "google_apis/gaia/gaia_urls.h"
#include "googleurl/src/gurl.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using content::BrowserThread;
using testing::_;
using testing::Return;
namespace extensions {
namespace {
const int kRouteId = 4;
const int kCallbackId = 5;
const char* kFakeExtensionId = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
static const char kValidAccessTokenResponse[] =
"{"
" \"access_token\": \"at1\","
" \"expires_in\": 3600,"
" \"token_type\": \"Bearer\""
"}";
class MockTokenService : public TokenService {
public:
MockTokenService() : mockToken_("test_refresh_token") { }
virtual ~MockTokenService() { }
bool AreCredentialsValid() const OVERRIDE {
return true;
}
const std::string& GetOAuth2LoginRefreshToken() const OVERRIDE {
return mockToken_;
}
std::string mockToken_;
MOCK_CONST_METHOD0(HasOAuthLoginToken, bool());
};
ProfileKeyedService* BuildMockTokenService(Profile* profile) {
return new MockTokenService;
}
MockTokenService* BuildForProfile(Profile* profile) {
return static_cast<MockTokenService*>(
TokenServiceFactory::GetInstance()->SetTestingFactoryAndUse(
profile, BuildMockTokenService));
}
class TestProfile : public TestingProfile {
public:
TestProfile()
: TestingProfile(),
token_service_(BuildForProfile(this)) {
}
virtual ~TestProfile() { }
void SetTokenServiceHasTokenResult(bool result) {
EXPECT_CALL(*token_service_, HasOAuthLoginToken())
.WillRepeatedly(Return(result));
}
private:
MockTokenService* token_service_;
};
class TestDelegate : public AppNotifyChannelSetup::Delegate,
public base::SupportsWeakPtr<TestDelegate> {
public:
TestDelegate() : was_called_(false) {}
virtual ~TestDelegate() {}
virtual void AppNotifyChannelSetupComplete(
const std::string& channel_id,
const std::string& error,
const AppNotifyChannelSetup* setup) OVERRIDE {
EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
EXPECT_FALSE(was_called_);
was_called_ = true;
error_ = error;
route_id_ = setup->return_route_id();
callback_id_ = setup->callback_id();
MessageLoop::current()->Quit();
}
// Called to check that we were called with the expected arguments.
void ExpectWasCalled(const std::string& expected_channel_id,
const std::string& expected_error) {
EXPECT_TRUE(was_called_);
EXPECT_EQ(expected_error, error_);
EXPECT_EQ(kRouteId, route_id_);
EXPECT_EQ(kCallbackId, callback_id_);
}
private:
// Has our callback been called yet?
bool was_called_;
// When our AppNotifyChannelSetupComplete method is called, we copy the
// arguments into these member variables.
std::string channel_id_;
std::string error_;
int route_id_;
int callback_id_;
DISALLOW_COPY_AND_ASSIGN(TestDelegate);
};
class TestUI : public AppNotifyChannelUI {
public:
TestUI() : delegate_(NULL) {}
~TestUI() {}
// AppNotifyChannelUI.
virtual void PromptSyncSetup(Delegate* delegate) OVERRIDE {
CHECK(!delegate_);
delegate_ = delegate;
// If we have a result, post a task to call back the delegate with
// it. Otherwise ReportResult can be called manually at some later point.
if (setup_result_.get()) {
MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&TestUI::ReportResult,
base::Unretained(this),
*setup_result_));
}
}
// This will make us automatically call back the delegate with |result| after
// PromptSyncSetup is called.
void SetSyncSetupResult(bool result) {
setup_result_.reset(new bool);
*setup_result_ = result;
}
void ReportResult(bool result) {
CHECK(delegate_);
delegate_->OnSyncSetupResult(result);
}
private:
AppNotifyChannelUI::Delegate* delegate_;
scoped_ptr<bool> setup_result_;
DISALLOW_COPY_AND_ASSIGN(TestUI);
};
} // namespace
class AppNotifyChannelSetupTest : public testing::Test {
public:
AppNotifyChannelSetupTest() : ui_thread_(BrowserThread::UI, &message_loop_),
db_thread_(BrowserThread::DB),
ui_(new TestUI()) {
}
virtual ~AppNotifyChannelSetupTest() {}
virtual void SetLoggedInUser(const std::string username) {
TestingPrefService* prefs = profile_.GetTestingPrefService();
prefs->SetUserPref(prefs::kGoogleServicesUsername,
new StringValue(username));
}
virtual AppNotifyChannelSetup* CreateInstance() {
GURL page_url("http://www.google.com");
return new AppNotifyChannelSetup(&profile_,
kFakeExtensionId,
"1234",
page_url,
kRouteId,
kCallbackId,
ui_.release(),
delegate_.AsWeakPtr());
}
virtual void SetupLogin(bool should_prompt, bool should_succeed) {
if (should_succeed) {
SetLoggedInUser("user@gmail.com");
profile_.SetTokenServiceHasTokenResult(true);
}
if (should_prompt)
ui_->SetSyncSetupResult(should_succeed);
}
virtual void SetupFetchAccessToken(bool should_succeed) {
factory_.SetFakeResponse(
GaiaUrls::GetInstance()->oauth2_token_url(),
kValidAccessTokenResponse,
should_succeed);
}
virtual void SetupRecordGrant(bool should_succeed) {
factory_.SetFakeResponse(
AppNotifyChannelSetup::GetOAuth2IssueTokenURL().spec(),
"whatever",
should_succeed);
}
virtual void SetupGetChannelId(bool should_succeed) {
factory_.SetFakeResponse(
AppNotifyChannelSetup::GetCWSChannelServiceURL().spec(),
"{\"id\": \"dummy_do_not_use\"}",
should_succeed);
}
virtual void RunServerTest(AppNotifyChannelSetup* setup,
const std::string& expected_code,
const std::string& expected_error) {
setup->Start();
message_loop_.Run();
delegate_.ExpectWasCalled(expected_code, expected_error);
}
virtual void SetUp() OVERRIDE {
db_thread_.Start();
}
virtual void TearDown() OVERRIDE {
// Schedule another task on the DB thread to notify us that it's safe to
// carry on with the test.
base::WaitableEvent done(false, false);
BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done)));
done.Wait();
db_thread_.Stop();
}
protected:
MessageLoop message_loop_;
content::TestBrowserThread ui_thread_;
content::TestBrowserThread db_thread_;
TestProfile profile_;
TestDelegate delegate_;
scoped_ptr<TestUI> ui_;
net::FakeURLFetcherFactory factory_;
};
TEST_F(AppNotifyChannelSetupTest, LoginFailure) {
SetupLogin(true, false);
scoped_refptr<AppNotifyChannelSetup> setup = CreateInstance();
RunServerTest(setup, "", "canceled_by_user");
}
TEST_F(AppNotifyChannelSetupTest, DoubleFetchAccessTokenFailure) {
SetupLogin(false, true);
SetupFetchAccessToken(false);
SetupLogin(true, true);
SetupFetchAccessToken(false);
scoped_refptr<AppNotifyChannelSetup> setup = CreateInstance();
RunServerTest(setup, "", "internal_error");
}
TEST_F(AppNotifyChannelSetupTest, RecordGrantFailure) {
SetupLogin(false, true);
SetupFetchAccessToken(true);
SetupRecordGrant(false);
scoped_refptr<AppNotifyChannelSetup> setup = CreateInstance();
RunServerTest(setup, "", "internal_error");
}
TEST_F(AppNotifyChannelSetupTest, GetChannelIdFailure) {
SetupLogin(false, true);
SetupFetchAccessToken(true);
SetupRecordGrant(true);
SetupGetChannelId(false);
scoped_refptr<AppNotifyChannelSetup> setup = CreateInstance();
RunServerTest(setup, "", "internal_error");
}
TEST_F(AppNotifyChannelSetupTest, FirstFetchAccessTokenSuccess) {
SetupLogin(false, true);
SetupFetchAccessToken(true);
SetupRecordGrant(true);
SetupGetChannelId(true);
scoped_refptr<AppNotifyChannelSetup> setup = CreateInstance();
RunServerTest(setup, "dummy_do_not_use", "");
}
TEST_F(AppNotifyChannelSetupTest, SecondFetchAccessTokenSuccess) {
SetupLogin(false, true);
SetupFetchAccessToken(false);
SetupLogin(true, true);
SetupFetchAccessToken(true);
SetupRecordGrant(true);
SetupGetChannelId(true);
scoped_refptr<AppNotifyChannelSetup> setup = CreateInstance();
RunServerTest(setup, "dummy_do_not_use", "");
}
} // namespace extensions