1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/webui/sync_internals_ui.h"
6
7#include <cstddef>
8#include <string>
9
10#include "base/message_loop.h"
11#include "base/values.h"
12#include "chrome/browser/sync/js_arg_list.h"
13#include "chrome/browser/sync/js_test_util.h"
14#include "chrome/browser/sync/profile_sync_service_mock.h"
15#include "chrome/common/extensions/extension_messages.h"
16#include "chrome/test/profile_mock.h"
17#include "content/browser/browser_thread.h"
18#include "content/browser/renderer_host/test_render_view_host.h"
19#include "content/browser/tab_contents/test_tab_contents.h"
20#include "testing/gmock/include/gmock/gmock.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23namespace {
24
25using browser_sync::HasArgsAsList;
26using browser_sync::JsArgList;
27using testing::NiceMock;
28using testing::Return;
29using testing::StrictMock;
30
31// Subclass of SyncInternalsUI to mock out ExecuteJavascript.
32class TestSyncInternalsUI : public SyncInternalsUI {
33 public:
34  explicit TestSyncInternalsUI(TabContents* contents)
35      : SyncInternalsUI(contents) {}
36  virtual ~TestSyncInternalsUI() {}
37
38  MOCK_METHOD1(ExecuteJavascript, void(const string16&));
39};
40
41class SyncInternalsUITest : public RenderViewHostTestHarness {
42 protected:
43  // We allocate memory for |sync_internals_ui_| but we don't
44  // construct it.  This is because we want to set mock expectations
45  // with its address before we construct it, and its constructor
46  // calls into our mocks.
47  SyncInternalsUITest()
48      // The message loop is provided by RenderViewHostTestHarness.
49      : ui_thread_(BrowserThread::UI, MessageLoopForUI::current()),
50        test_sync_internals_ui_buf_(NULL),
51        test_sync_internals_ui_constructor_called_(false) {}
52
53  virtual void SetUp() {
54    test_sync_internals_ui_buf_ = operator new(sizeof(TestSyncInternalsUI));
55    test_sync_internals_ui_constructor_called_ = false;
56    profile_.reset(new NiceMock<ProfileMock>());
57    RenderViewHostTestHarness::SetUp();
58  }
59
60  virtual void TearDown() {
61    if (test_sync_internals_ui_constructor_called_) {
62      GetTestSyncInternalsUI()->~TestSyncInternalsUI();
63    }
64    operator delete(test_sync_internals_ui_buf_);
65    RenderViewHostTestHarness::TearDown();
66  }
67
68  NiceMock<ProfileMock>* GetProfileMock() {
69    return static_cast<NiceMock<ProfileMock>*>(profile());
70  }
71
72  // Set up boilerplate expectations for calls done during
73  // SyncInternalUI's construction/destruction.
74  void ExpectSetupTeardownCalls() {
75    EXPECT_CALL(*GetProfileMock(), GetProfileSyncService())
76        .WillRepeatedly(Return(&profile_sync_service_mock_));
77
78    EXPECT_CALL(profile_sync_service_mock_, GetJsFrontend())
79        .WillRepeatedly(Return(&mock_js_backend_));
80
81    // Called by sync_ui_util::ConstructAboutInformation().
82    EXPECT_CALL(profile_sync_service_mock_, HasSyncSetupCompleted())
83        .WillRepeatedly(Return(false));
84
85    // Called by SyncInternalsUI's constructor.
86    EXPECT_CALL(mock_js_backend_,
87                AddHandler(GetTestSyncInternalsUIAddress()));
88
89    // Called by SyncInternalUI's destructor.
90    EXPECT_CALL(mock_js_backend_,
91                RemoveHandler(GetTestSyncInternalsUIAddress()));
92  }
93
94  // Like ExpectSetupTeardownCalls() but with a NULL
95  // ProfileSyncService.
96  void ExpectSetupTeardownCallsNullService() {
97    EXPECT_CALL(*GetProfileMock(), GetProfileSyncService())
98        .WillRepeatedly(Return(static_cast<ProfileSyncService*>(NULL)));
99  }
100
101  void ConstructTestSyncInternalsUI() {
102    if (test_sync_internals_ui_constructor_called_) {
103      ADD_FAILURE() << "ConstructTestSyncInternalsUI() should be called "
104                    << "at most once per test";
105      return;
106    }
107    new(test_sync_internals_ui_buf_) TestSyncInternalsUI(contents());
108    test_sync_internals_ui_constructor_called_ = true;
109  }
110
111  TestSyncInternalsUI* GetTestSyncInternalsUI() {
112    if (!test_sync_internals_ui_constructor_called_) {
113      ADD_FAILURE() << "ConstructTestSyncInternalsUI() should be called "
114                    << "before GetTestSyncInternalsUI()";
115      return NULL;
116    }
117    return GetTestSyncInternalsUIAddress();
118  }
119
120  // Used for passing into EXPECT_CALL().
121  TestSyncInternalsUI* GetTestSyncInternalsUIAddress() {
122    EXPECT_TRUE(test_sync_internals_ui_buf_);
123    return static_cast<TestSyncInternalsUI*>(test_sync_internals_ui_buf_);
124  }
125
126  StrictMock<ProfileSyncServiceMock> profile_sync_service_mock_;
127  StrictMock<browser_sync::MockJsFrontend> mock_js_backend_;
128
129 private:
130  // Needed by |contents()|.
131  BrowserThread ui_thread_;
132  void* test_sync_internals_ui_buf_;
133  bool test_sync_internals_ui_constructor_called_;
134};
135
136TEST_F(SyncInternalsUITest, HandleJsEvent) {
137  ExpectSetupTeardownCalls();
138
139  ConstructTestSyncInternalsUI();
140
141  EXPECT_CALL(*GetTestSyncInternalsUI(),
142              ExecuteJavascript(ASCIIToUTF16("testMessage(5,true);")));
143
144  ListValue args;
145  args.Append(Value::CreateIntegerValue(5));
146  args.Append(Value::CreateBooleanValue(true));
147  GetTestSyncInternalsUI()->HandleJsEvent("testMessage", JsArgList(args));
148}
149
150TEST_F(SyncInternalsUITest, HandleJsEventNullService) {
151  ExpectSetupTeardownCallsNullService();
152
153  ConstructTestSyncInternalsUI();
154
155  EXPECT_CALL(*GetTestSyncInternalsUI(),
156              ExecuteJavascript(ASCIIToUTF16("testMessage(5,true);")));
157
158  ListValue args;
159  args.Append(Value::CreateIntegerValue(5));
160  args.Append(Value::CreateBooleanValue(true));
161  GetTestSyncInternalsUI()->HandleJsEvent("testMessage", JsArgList(args));
162}
163
164TEST_F(SyncInternalsUITest, ProcessWebUIMessageBasic) {
165  ExpectSetupTeardownCalls();
166
167  ExtensionHostMsg_DomMessage_Params params;
168  params.name = "testName";
169  params.arguments.Append(Value::CreateIntegerValue(10));
170
171  EXPECT_CALL(mock_js_backend_,
172              ProcessMessage(params.name, HasArgsAsList(params.arguments),
173                             GetTestSyncInternalsUIAddress()));
174
175  ConstructTestSyncInternalsUI();
176
177  GetTestSyncInternalsUI()->ProcessWebUIMessage(params);
178}
179
180TEST_F(SyncInternalsUITest, ProcessWebUIMessageBasicNullService) {
181  ExpectSetupTeardownCallsNullService();
182
183  ConstructTestSyncInternalsUI();
184
185  ExtensionHostMsg_DomMessage_Params params;
186  params.name = "testName";
187  params.arguments.Append(Value::CreateIntegerValue(5));
188
189  // Should drop the message.
190  GetTestSyncInternalsUI()->ProcessWebUIMessage(params);
191}
192
193namespace {
194const char kAboutInfoCall[] =
195    "onGetAboutInfoFinished({\"summary\":\"SYNC DISABLED\"});";
196}  // namespace
197
198TEST_F(SyncInternalsUITest, ProcessWebUIMessageGetAboutInfo) {
199  ExpectSetupTeardownCalls();
200
201  ExtensionHostMsg_DomMessage_Params params;
202  params.name = "getAboutInfo";
203
204  ConstructTestSyncInternalsUI();
205
206  EXPECT_CALL(*GetTestSyncInternalsUI(),
207              ExecuteJavascript(ASCIIToUTF16(kAboutInfoCall)));
208
209  GetTestSyncInternalsUI()->ProcessWebUIMessage(params);
210}
211
212TEST_F(SyncInternalsUITest, ProcessWebUIMessageGetAboutInfoNullService) {
213  ExpectSetupTeardownCallsNullService();
214
215  ExtensionHostMsg_DomMessage_Params params;
216  params.name = "getAboutInfo";
217
218  ConstructTestSyncInternalsUI();
219
220  EXPECT_CALL(*GetTestSyncInternalsUI(),
221              ExecuteJavascript(ASCIIToUTF16(kAboutInfoCall)));
222
223  GetTestSyncInternalsUI()->ProcessWebUIMessage(params);
224}
225
226}  // namespace
227