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 23 namespace { 24 25 using browser_sync::HasArgsAsList; 26 using browser_sync::JsArgList; 27 using testing::NiceMock; 28 using testing::Return; 29 using testing::StrictMock; 30 31 // Subclass of SyncInternalsUI to mock out ExecuteJavascript. 32 class 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 41 class 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 136 TEST_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 150 TEST_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 164 TEST_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 180 TEST_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 193 namespace { 194 const char kAboutInfoCall[] = 195 "onGetAboutInfoFinished({\"summary\":\"SYNC DISABLED\"});"; 196 } // namespace 197 198 TEST_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 212 TEST_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