// Copyright (c) 2011 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/ui/webui/sync_internals_ui.h"
#include <cstddef>
#include <string>
#include "base/message_loop.h"
#include "base/values.h"
#include "chrome/browser/sync/js_arg_list.h"
#include "chrome/browser/sync/js_test_util.h"
#include "chrome/browser/sync/profile_sync_service_mock.h"
#include "chrome/common/extensions/extension_messages.h"
#include "chrome/test/profile_mock.h"
#include "content/browser/browser_thread.h"
#include "content/browser/renderer_host/test_render_view_host.h"
#include "content/browser/tab_contents/test_tab_contents.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
using browser_sync::HasArgsAsList;
using browser_sync::JsArgList;
using testing::NiceMock;
using testing::Return;
using testing::StrictMock;
// Subclass of SyncInternalsUI to mock out ExecuteJavascript.
class TestSyncInternalsUI : public SyncInternalsUI {
public:
explicit TestSyncInternalsUI(TabContents* contents)
: SyncInternalsUI(contents) {}
virtual ~TestSyncInternalsUI() {}
MOCK_METHOD1(ExecuteJavascript, void(const string16&));
};
class SyncInternalsUITest : public RenderViewHostTestHarness {
protected:
// We allocate memory for |sync_internals_ui_| but we don't
// construct it. This is because we want to set mock expectations
// with its address before we construct it, and its constructor
// calls into our mocks.
SyncInternalsUITest()
// The message loop is provided by RenderViewHostTestHarness.
: ui_thread_(BrowserThread::UI, MessageLoopForUI::current()),
test_sync_internals_ui_buf_(NULL),
test_sync_internals_ui_constructor_called_(false) {}
virtual void SetUp() {
test_sync_internals_ui_buf_ = operator new(sizeof(TestSyncInternalsUI));
test_sync_internals_ui_constructor_called_ = false;
profile_.reset(new NiceMock<ProfileMock>());
RenderViewHostTestHarness::SetUp();
}
virtual void TearDown() {
if (test_sync_internals_ui_constructor_called_) {
GetTestSyncInternalsUI()->~TestSyncInternalsUI();
}
operator delete(test_sync_internals_ui_buf_);
RenderViewHostTestHarness::TearDown();
}
NiceMock<ProfileMock>* GetProfileMock() {
return static_cast<NiceMock<ProfileMock>*>(profile());
}
// Set up boilerplate expectations for calls done during
// SyncInternalUI's construction/destruction.
void ExpectSetupTeardownCalls() {
EXPECT_CALL(*GetProfileMock(), GetProfileSyncService())
.WillRepeatedly(Return(&profile_sync_service_mock_));
EXPECT_CALL(profile_sync_service_mock_, GetJsFrontend())
.WillRepeatedly(Return(&mock_js_backend_));
// Called by sync_ui_util::ConstructAboutInformation().
EXPECT_CALL(profile_sync_service_mock_, HasSyncSetupCompleted())
.WillRepeatedly(Return(false));
// Called by SyncInternalsUI's constructor.
EXPECT_CALL(mock_js_backend_,
AddHandler(GetTestSyncInternalsUIAddress()));
// Called by SyncInternalUI's destructor.
EXPECT_CALL(mock_js_backend_,
RemoveHandler(GetTestSyncInternalsUIAddress()));
}
// Like ExpectSetupTeardownCalls() but with a NULL
// ProfileSyncService.
void ExpectSetupTeardownCallsNullService() {
EXPECT_CALL(*GetProfileMock(), GetProfileSyncService())
.WillRepeatedly(Return(static_cast<ProfileSyncService*>(NULL)));
}
void ConstructTestSyncInternalsUI() {
if (test_sync_internals_ui_constructor_called_) {
ADD_FAILURE() << "ConstructTestSyncInternalsUI() should be called "
<< "at most once per test";
return;
}
new(test_sync_internals_ui_buf_) TestSyncInternalsUI(contents());
test_sync_internals_ui_constructor_called_ = true;
}
TestSyncInternalsUI* GetTestSyncInternalsUI() {
if (!test_sync_internals_ui_constructor_called_) {
ADD_FAILURE() << "ConstructTestSyncInternalsUI() should be called "
<< "before GetTestSyncInternalsUI()";
return NULL;
}
return GetTestSyncInternalsUIAddress();
}
// Used for passing into EXPECT_CALL().
TestSyncInternalsUI* GetTestSyncInternalsUIAddress() {
EXPECT_TRUE(test_sync_internals_ui_buf_);
return static_cast<TestSyncInternalsUI*>(test_sync_internals_ui_buf_);
}
StrictMock<ProfileSyncServiceMock> profile_sync_service_mock_;
StrictMock<browser_sync::MockJsFrontend> mock_js_backend_;
private:
// Needed by |contents()|.
BrowserThread ui_thread_;
void* test_sync_internals_ui_buf_;
bool test_sync_internals_ui_constructor_called_;
};
TEST_F(SyncInternalsUITest, HandleJsEvent) {
ExpectSetupTeardownCalls();
ConstructTestSyncInternalsUI();
EXPECT_CALL(*GetTestSyncInternalsUI(),
ExecuteJavascript(ASCIIToUTF16("testMessage(5,true);")));
ListValue args;
args.Append(Value::CreateIntegerValue(5));
args.Append(Value::CreateBooleanValue(true));
GetTestSyncInternalsUI()->HandleJsEvent("testMessage", JsArgList(args));
}
TEST_F(SyncInternalsUITest, HandleJsEventNullService) {
ExpectSetupTeardownCallsNullService();
ConstructTestSyncInternalsUI();
EXPECT_CALL(*GetTestSyncInternalsUI(),
ExecuteJavascript(ASCIIToUTF16("testMessage(5,true);")));
ListValue args;
args.Append(Value::CreateIntegerValue(5));
args.Append(Value::CreateBooleanValue(true));
GetTestSyncInternalsUI()->HandleJsEvent("testMessage", JsArgList(args));
}
TEST_F(SyncInternalsUITest, ProcessWebUIMessageBasic) {
ExpectSetupTeardownCalls();
ExtensionHostMsg_DomMessage_Params params;
params.name = "testName";
params.arguments.Append(Value::CreateIntegerValue(10));
EXPECT_CALL(mock_js_backend_,
ProcessMessage(params.name, HasArgsAsList(params.arguments),
GetTestSyncInternalsUIAddress()));
ConstructTestSyncInternalsUI();
GetTestSyncInternalsUI()->ProcessWebUIMessage(params);
}
TEST_F(SyncInternalsUITest, ProcessWebUIMessageBasicNullService) {
ExpectSetupTeardownCallsNullService();
ConstructTestSyncInternalsUI();
ExtensionHostMsg_DomMessage_Params params;
params.name = "testName";
params.arguments.Append(Value::CreateIntegerValue(5));
// Should drop the message.
GetTestSyncInternalsUI()->ProcessWebUIMessage(params);
}
namespace {
const char kAboutInfoCall[] =
"onGetAboutInfoFinished({\"summary\":\"SYNC DISABLED\"});";
} // namespace
TEST_F(SyncInternalsUITest, ProcessWebUIMessageGetAboutInfo) {
ExpectSetupTeardownCalls();
ExtensionHostMsg_DomMessage_Params params;
params.name = "getAboutInfo";
ConstructTestSyncInternalsUI();
EXPECT_CALL(*GetTestSyncInternalsUI(),
ExecuteJavascript(ASCIIToUTF16(kAboutInfoCall)));
GetTestSyncInternalsUI()->ProcessWebUIMessage(params);
}
TEST_F(SyncInternalsUITest, ProcessWebUIMessageGetAboutInfoNullService) {
ExpectSetupTeardownCallsNullService();
ExtensionHostMsg_DomMessage_Params params;
params.name = "getAboutInfo";
ConstructTestSyncInternalsUI();
EXPECT_CALL(*GetTestSyncInternalsUI(),
ExecuteJavascript(ASCIIToUTF16(kAboutInfoCall)));
GetTestSyncInternalsUI()->ProcessWebUIMessage(params);
}
} // namespace