| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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 <string> | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/bind_helpers.h" | |
| 9 #include "base/file_path.h" | |
| 10 #include "base/memory/scoped_ptr.h" | |
| 11 #include "base/synchronization/waitable_event.h" | |
| 12 #include "chrome/common/automation_messages.h" | |
| 13 #include "chrome_frame/cfproxy_private.h" | |
| 14 #include "chrome_frame/test/chrome_frame_test_utils.h" | |
| 15 #include "chrome_frame/test/test_scrubber.h" | |
| 16 #include "testing/gtest/include/gtest/gtest.h" | |
| 17 #include "testing/gmock/include/gmock/gmock.h" | |
| 18 #include "testing/gmock_mutant.h" | |
| 19 | |
| 20 using testing::_; | |
| 21 using testing::DoAll; | |
| 22 using testing::NotNull; | |
| 23 using testing::Return; | |
| 24 using testing::StrictMock; | |
| 25 using testing::InvokeWithoutArgs; | |
| 26 using testing::WithoutArgs; | |
| 27 using testing::CreateFunctor; | |
| 28 using testing::StrEq; | |
| 29 using testing::Eq; | |
| 30 | |
| 31 // There is not much to test here since CFProxy is pretty dumb. | |
| 32 struct MockFactory : public ChromeProxyFactory { | |
| 33 MOCK_METHOD0(CreateProxy, ChromeProxy*()); | |
| 34 }; | |
| 35 | |
| 36 struct MockChromeProxyDelegate : public ChromeProxyDelegate { | |
| 37 MOCK_METHOD1(OnMessageReceived, bool(const IPC::Message& message)); | |
| 38 MOCK_METHOD1(Connected, void(ChromeProxy* proxy)); | |
| 39 MOCK_METHOD2(PeerLost, void(ChromeProxy*, enum DisconnectReason reason)); | |
| 40 MOCK_METHOD0(Disconnected, void()); | |
| 41 MOCK_METHOD0(tab_handle, int()); | |
| 42 | |
| 43 MOCK_METHOD5(Completed_CreateTab, void(bool success, HWND chrome_wnd, | |
| 44 HWND tab_window, int tab_handle, int session_id)); | |
| 45 MOCK_METHOD5(Completed_ConnectToTab, void(bool success, HWND chrome_window, | |
| 46 HWND tab_window, int tab_handle, int session_id)); | |
| 47 MOCK_METHOD2(Completed_Navigate, void(bool success, | |
| 48 enum AutomationMsg_NavigationResponseValues res)); | |
| 49 | |
| 50 // Network requests from Chrome. | |
| 51 MOCK_METHOD2(Network_Start, void(int request_id, | |
| 52 const AutomationURLRequest& request_info)); | |
| 53 MOCK_METHOD2(Network_Read, void(int request_id, int bytes_to_read)); | |
| 54 MOCK_METHOD2(Network_End, void(int request_id, | |
| 55 const net::URLRequestStatus& s)); | |
| 56 MOCK_METHOD1(Network_DownloadInHost, void(int request_id)); | |
| 57 MOCK_METHOD2(GetCookies, void(const GURL& url, int cookie_id)); | |
| 58 MOCK_METHOD2(SetCookie, void(const GURL& url, const std::string& cookie)); | |
| 59 | |
| 60 // Navigation progress notifications. | |
| 61 MOCK_METHOD2(NavigationStateChanged, void(int flags, | |
| 62 const NavigationInfo& nav_info)); | |
| 63 MOCK_METHOD1(UpdateTargetUrl, void(const std::wstring& url)); | |
| 64 MOCK_METHOD2(NavigationFailed, void(int error_code, const GURL& gurl)); | |
| 65 MOCK_METHOD1(DidNavigate, void(const NavigationInfo& navigation_info)); | |
| 66 MOCK_METHOD1(TabLoaded, void(const GURL& url)); | |
| 67 | |
| 68 // | |
| 69 MOCK_METHOD3(OpenURL, void(const GURL& url_to_open, const GURL& referrer, | |
| 70 int open_disposition)); | |
| 71 MOCK_METHOD1(GoToHistoryOffset, void(int offset)); | |
| 72 MOCK_METHOD3(MessageToHost, void(const std::string& message, | |
| 73 const std::string& origin, const std::string& target)); | |
| 74 | |
| 75 // Misc. UI. | |
| 76 MOCK_METHOD1(HandleAccelerator, void(const MSG& accel_message)); | |
| 77 MOCK_METHOD3(HandleContextMenu, void(HANDLE menu_handle, int align_flags, | |
| 78 const MiniContextMenuParams& params)); | |
| 79 MOCK_METHOD1(TabbedOut, void(bool reverse)); | |
| 80 | |
| 81 // | |
| 82 MOCK_METHOD0(TabClosed, void()); | |
| 83 MOCK_METHOD1(AttachTab, void(const AttachExternalTabParams& attach_params)); | |
| 84 }; | |
| 85 | |
| 86 struct MockSender : public IPC::Message::Sender { | |
| 87 MOCK_METHOD1(Send, bool(IPC::Message* m)); | |
| 88 }; | |
| 89 | |
| 90 struct MockCFProxyTraits : public CFProxyTraits { | |
| 91 MOCK_METHOD2(DoCreateChannel, IPC::Message::Sender*(const std::string& id, | |
| 92 IPC::Channel::Listener* l)); | |
| 93 MOCK_METHOD1(CloseChannel, void(IPC::Message::Sender* s)); | |
| 94 MOCK_METHOD1(LaunchApp, bool(const std::wstring& cmd_line)); | |
| 95 | |
| 96 // Forward the CreateChannel to DoCreateChannel, but save the ipc_thread | |
| 97 // and the listener (i.e. proxy implementation of Channel::Listener) | |
| 98 virtual IPC::Message::Sender* CreateChannel(const std::string& id, | |
| 99 IPC::Channel::Listener* l) { | |
| 100 ipc_loop = MessageLoop::current(); | |
| 101 listener = l; | |
| 102 return this->DoCreateChannel(id, l); | |
| 103 } | |
| 104 | |
| 105 // Simulate some activity in the IPC thread. | |
| 106 // You may find API_FIRE_XXXX macros (see below) handy instead. | |
| 107 void FireConnect(base::TimeDelta t) { | |
| 108 ASSERT_TRUE(ipc_loop != NULL); | |
| 109 ipc_loop->PostDelayedTask( | |
| 110 FROM_HERE, base::Bind(&IPC::Channel::Listener::OnChannelConnected, | |
| 111 base::Unretained(listener), 0), | |
| 112 t.InMilliseconds()); | |
| 113 } | |
| 114 | |
| 115 void FireError(base::TimeDelta t) { | |
| 116 ASSERT_TRUE(ipc_loop != NULL); | |
| 117 ipc_loop->PostDelayedTask( | |
| 118 FROM_HERE, base::Bind(&IPC::Channel::Listener::OnChannelError, | |
| 119 base::Unretained(listener)), | |
| 120 t.InMilliseconds()); | |
| 121 } | |
| 122 | |
| 123 void FireMessage(const IPC::Message& m, base::TimeDelta t) { | |
| 124 ASSERT_TRUE(ipc_loop != NULL); | |
| 125 ipc_loop->PostDelayedTask( | |
| 126 FROM_HERE, | |
| 127 base::Bind( | |
| 128 base::IgnoreResult(&IPC::Channel::Listener::OnMessageReceived), | |
| 129 base::Unretained(listener), m), | |
| 130 t.InMilliseconds()); | |
| 131 } | |
| 132 | |
| 133 MockCFProxyTraits() : ipc_loop(NULL) {} | |
| 134 MockSender sender; | |
| 135 private: | |
| 136 MessageLoop* ipc_loop; | |
| 137 IPC::Channel::Listener* listener; | |
| 138 }; | |
| 139 | |
| 140 // Handy macros when we want so simulate something on the IPC thread. | |
| 141 #define API_FIRE_CONNECT(api, t) InvokeWithoutArgs(CreateFunctor(&api, \ | |
| 142 &MockCFProxyTraits::FireConnect, t)) | |
| 143 #define API_FIRE_ERROR(api, t) InvokeWithoutArgs(CreateFunctor(&api, \ | |
| 144 &MockCFProxyTraits::FireError, t)) | |
| 145 #define API_FIRE_MESSAGE(api, t) InvokeWithoutArgs(CreateFunctor(&api, \ | |
| 146 &MockCFProxyTraits::FireMessage, t)) | |
| 147 | |
| 148 class ChromeProxyTest : public ::testing::Test { | |
| 149 protected: | |
| 150 virtual void SetUp() OVERRIDE { | |
| 151 chrome_frame_test::OverrideDataDirectoryForThisTest( | |
| 152 chrome_frame_test::GetProfilePath(L"").value()); | |
| 153 params_.profile = "Adam N. Epilinter"; | |
| 154 params_.timeout = base::TimeDelta::FromSeconds(4); | |
| 155 } | |
| 156 | |
| 157 ProxyParams params_; | |
| 158 }; | |
| 159 | |
| 160 TEST_F(ChromeProxyTest, DelegateAddRemove) { | |
| 161 StrictMock<MockCFProxyTraits> api; | |
| 162 StrictMock<MockChromeProxyDelegate> delegate; | |
| 163 StrictMock<MockFactory> factory; // to be destroyed before other mocks | |
| 164 CFProxy* proxy = new CFProxy(&api); | |
| 165 | |
| 166 EXPECT_CALL(factory, CreateProxy()).WillOnce(Return(proxy)); | |
| 167 EXPECT_CALL(api, DoCreateChannel(_, proxy)).WillOnce(Return(&api.sender)); | |
| 168 EXPECT_CALL(api, LaunchApp(_)).WillOnce(Return(true)); | |
| 169 EXPECT_CALL(api, CloseChannel(&api.sender)); | |
| 170 | |
| 171 EXPECT_CALL(delegate, tab_handle()).WillRepeatedly(Return(0)); | |
| 172 EXPECT_CALL(delegate, Disconnected()); | |
| 173 | |
| 174 factory.GetProxy(&delegate, params_); | |
| 175 factory.ReleaseProxy(&delegate, params_.profile); | |
| 176 } | |
| 177 | |
| 178 // Not very useful test. Just for illustration. :) | |
| 179 TEST_F(ChromeProxyTest, SharedProxy) { | |
| 180 base::WaitableEvent done1(false, false); | |
| 181 base::WaitableEvent done2(false, false); | |
| 182 StrictMock<MockCFProxyTraits> api; | |
| 183 StrictMock<MockChromeProxyDelegate> delegate1; | |
| 184 StrictMock<MockChromeProxyDelegate> delegate2; | |
| 185 StrictMock<MockFactory> factory; | |
| 186 CFProxy* proxy = new CFProxy(&api); | |
| 187 | |
| 188 EXPECT_CALL(factory, CreateProxy()).WillOnce(Return(proxy)); | |
| 189 EXPECT_CALL(api, DoCreateChannel(_, proxy)).WillOnce(Return(&api.sender)); | |
| 190 EXPECT_CALL(api, LaunchApp(_)).WillOnce(DoAll( | |
| 191 API_FIRE_CONNECT(api, base::TimeDelta::FromMilliseconds(150)), | |
| 192 Return(true))); | |
| 193 EXPECT_CALL(api, CloseChannel(&api.sender)); | |
| 194 | |
| 195 EXPECT_CALL(delegate1, tab_handle()).WillRepeatedly(Return(0)); | |
| 196 EXPECT_CALL(delegate2, tab_handle()).WillRepeatedly(Return(0)); | |
| 197 | |
| 198 EXPECT_CALL(delegate1, Connected(proxy)) | |
| 199 .WillOnce(InvokeWithoutArgs(&done1, &base::WaitableEvent::Signal)); | |
| 200 EXPECT_CALL(delegate2, Connected(proxy)) | |
| 201 .WillOnce(InvokeWithoutArgs(&done2, &base::WaitableEvent::Signal)); | |
| 202 | |
| 203 factory.GetProxy(&delegate1, params_); | |
| 204 params_.timeout = base::TimeDelta::FromSeconds(2); | |
| 205 factory.GetProxy(&delegate2, params_); | |
| 206 | |
| 207 EXPECT_TRUE(done1.TimedWait(base::TimeDelta::FromSeconds(1))); | |
| 208 EXPECT_TRUE(done2.TimedWait(base::TimeDelta::FromSeconds(1))); | |
| 209 | |
| 210 EXPECT_CALL(delegate2, Disconnected()); | |
| 211 EXPECT_CALL(delegate1, Disconnected()); | |
| 212 | |
| 213 factory.ReleaseProxy(&delegate2, params_.profile); | |
| 214 factory.ReleaseProxy(&delegate1, params_.profile); | |
| 215 } | |
| 216 | |
| 217 TEST_F(ChromeProxyTest, LaunchTimeout) { | |
| 218 base::WaitableEvent done(true, false); | |
| 219 StrictMock<MockCFProxyTraits> api; | |
| 220 StrictMock<MockChromeProxyDelegate> delegate; | |
| 221 StrictMock<MockFactory> factory; | |
| 222 CFProxy* proxy = new CFProxy(&api); | |
| 223 | |
| 224 EXPECT_CALL(delegate, tab_handle()).WillRepeatedly(Return(0)); | |
| 225 EXPECT_CALL(factory, CreateProxy()).WillOnce(Return(proxy)); | |
| 226 EXPECT_CALL(api, DoCreateChannel(_, proxy)).WillOnce(Return(&api.sender)); | |
| 227 EXPECT_CALL(api, LaunchApp(_)).WillOnce(Return(true)); | |
| 228 EXPECT_CALL(api, CloseChannel(&api.sender)); | |
| 229 | |
| 230 EXPECT_CALL(delegate, PeerLost(_, | |
| 231 ChromeProxyDelegate::CHROME_EXE_LAUNCH_TIMEOUT)) | |
| 232 .WillOnce(InvokeWithoutArgs(&done, &base::WaitableEvent::Signal)); | |
| 233 params_.timeout = base::TimeDelta::FromMilliseconds(300); | |
| 234 factory.GetProxy(&delegate, params_); | |
| 235 EXPECT_TRUE(done.TimedWait(base::TimeDelta::FromSeconds(1))); | |
| 236 | |
| 237 EXPECT_CALL(delegate, Disconnected()); | |
| 238 factory.ReleaseProxy(&delegate, params_.profile); | |
| 239 } | |
| 240 | |
| 241 TEST_F(ChromeProxyTest, LaunchChrome) { | |
| 242 base::WaitableEvent connected(false, false); | |
| 243 StrictMock<MockChromeProxyDelegate> delegate; | |
| 244 ChromeProxyFactory factory; | |
| 245 | |
| 246 params_.timeout = base::TimeDelta::FromSeconds(10); | |
| 247 | |
| 248 EXPECT_CALL(delegate, tab_handle()).WillRepeatedly(Return(0)); | |
| 249 EXPECT_CALL(delegate, Connected(NotNull())) | |
| 250 .WillOnce(InvokeWithoutArgs(&connected, &base::WaitableEvent::Signal)); | |
| 251 | |
| 252 factory.GetProxy(&delegate, params_); | |
| 253 EXPECT_TRUE(connected.TimedWait(base::TimeDelta::FromSeconds(15))); | |
| 254 | |
| 255 EXPECT_CALL(delegate, Disconnected()); | |
| 256 factory.ReleaseProxy(&delegate, params_.profile); | |
| 257 } | |
| 258 | |
| 259 // Test that a channel error results in Completed_XYZ(false, ) called if | |
| 260 // the synchronious XYZ message has been sent. | |
| 261 TEST_F(ChromeProxyTest, ChannelError) { | |
| 262 base::WaitableEvent connected(false, false); | |
| 263 StrictMock<MockCFProxyTraits> api; | |
| 264 StrictMock<MockChromeProxyDelegate> delegate; | |
| 265 StrictMock<MockFactory> factory; | |
| 266 CFProxy* proxy = new CFProxy(&api); | |
| 267 | |
| 268 params_.timeout = base::TimeDelta::FromMilliseconds(300); | |
| 269 | |
| 270 testing::InSequence s; | |
| 271 | |
| 272 EXPECT_CALL(factory, CreateProxy()).WillOnce(Return(proxy)); | |
| 273 EXPECT_CALL(api, DoCreateChannel(_, proxy)).WillOnce(Return(&api.sender)); | |
| 274 EXPECT_CALL(api, LaunchApp(_)).WillOnce(DoAll( | |
| 275 API_FIRE_CONNECT(api, base::TimeDelta::FromMilliseconds(10)), | |
| 276 Return(true))); | |
| 277 EXPECT_CALL(delegate, Connected(proxy)) | |
| 278 .WillOnce(DoAll( | |
| 279 InvokeWithoutArgs(CreateFunctor(proxy, &ChromeProxy::ConnectTab, | |
| 280 &delegate, HWND(6), 512)), | |
| 281 InvokeWithoutArgs(&connected, &base::WaitableEvent::Signal))); | |
| 282 | |
| 283 EXPECT_CALL(api.sender, Send(_)); | |
| 284 EXPECT_CALL(delegate, Completed_ConnectToTab(false, _, _, _, _)); | |
| 285 EXPECT_CALL(api, CloseChannel(&api.sender)); | |
| 286 EXPECT_CALL(delegate, PeerLost(_, ChromeProxyDelegate::CHANNEL_ERROR)); | |
| 287 | |
| 288 factory.GetProxy(&delegate, params_); | |
| 289 EXPECT_TRUE(connected.TimedWait(base::TimeDelta::FromSeconds(15))); | |
| 290 // Simulate a channel error. | |
| 291 api.FireError(base::TimeDelta::FromMilliseconds(0)); | |
| 292 | |
| 293 // Expectations when the Proxy is destroyed. | |
| 294 EXPECT_CALL(delegate, tab_handle()).WillOnce(Return(0)); | |
| 295 EXPECT_CALL(delegate, Disconnected()); | |
| 296 factory.ReleaseProxy(&delegate, params_.profile); | |
| 297 } | |
| 298 /////////////////////////////////////////////////////////////////////////////// | |
| 299 namespace { | |
| 300 template <typename M, typename A> | |
| 301 inline IPC::Message* CreateReply(M* m, const A& a) { | |
| 302 IPC::Message* r = IPC::SyncMessage::GenerateReply(m); | |
| 303 if (r) { | |
| 304 M::WriteReplyParams(r, a); | |
| 305 } | |
| 306 return r; | |
| 307 } | |
| 308 | |
| 309 template <typename M, typename A, typename B> | |
| 310 inline IPC::Message* CreateReply(M* m, const A& a, const B& b) { | |
| 311 IPC::Message* r = IPC::SyncMessage::GenerateReply(m); | |
| 312 if (r) { | |
| 313 M::WriteReplyParams(r, a, b); | |
| 314 } | |
| 315 return r; | |
| 316 } | |
| 317 | |
| 318 template <typename M, typename A, typename B, typename C> | |
| 319 inline IPC::Message* CreateReply(M* m, const A& a, const B& b, const C& c) { | |
| 320 IPC::Message* r = IPC::SyncMessage::GenerateReply(m); | |
| 321 if (r) { | |
| 322 M::WriteReplyParams(r, a, b, c); | |
| 323 } | |
| 324 return r; | |
| 325 } | |
| 326 | |
| 327 template <typename M, typename A, typename B, typename C, typename D> | |
| 328 inline IPC::Message* CreateReply(M* m, const A& a, const B& b, const C& c, | |
| 329 const D& d) { | |
| 330 IPC::Message* r = IPC::SyncMessage::GenerateReply(m); | |
| 331 if (r) { | |
| 332 M::WriteReplyParams(r, a, b, c, d); | |
| 333 } | |
| 334 return r; | |
| 335 }} // namespace | |
| 336 | |
| 337 TEST(SyncMsgSender, Deserialize) { | |
| 338 // Note the ipc thread is not actually needed, but we try to be close | |
| 339 // to real-world conditions - that SyncMsgSender works from multiple threads. | |
| 340 base::Thread ipc("ipc"); | |
| 341 ipc.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0)); | |
| 342 | |
| 343 StrictMock<MockChromeProxyDelegate> d1; | |
| 344 TabsMap tab2delegate; | |
| 345 SyncMsgSender queue(&tab2delegate); | |
| 346 | |
| 347 const int kTabHandle = 6; | |
| 348 const int kSessionId = 8; | |
| 349 | |
| 350 // Create a sync message and its reply. | |
| 351 AutomationMsg_CreateExternalTab m(ExternalTabSettings(), 0, 0, 0, 0); | |
| 352 scoped_ptr<IPC::Message> r(CreateReply(&m, (HWND)1, (HWND)2, kTabHandle, | |
| 353 kSessionId)); | |
| 354 | |
| 355 queue.QueueSyncMessage(&m, &d1, NULL); | |
| 356 | |
| 357 testing::InSequence s; | |
| 358 EXPECT_CALL(d1, Completed_CreateTab(true, (HWND)1, (HWND)2, kTabHandle, | |
| 359 kSessionId)); | |
| 360 | |
| 361 // Execute replies in a worker thread. | |
| 362 ipc.message_loop()->PostTask( | |
| 363 FROM_HERE, | |
| 364 base::Bind(base::IgnoreResult(&SyncMsgSender::OnReplyReceived), | |
| 365 base::Unretained(&queue), r.get())); | |
| 366 ipc.Stop(); | |
| 367 | |
| 368 // Expect that tab 6 has been associated with the delegate. | |
| 369 EXPECT_EQ(&d1, tab2delegate[6]); | |
| 370 } | |
| 371 | |
| 372 TEST(SyncMsgSender, OnChannelClosed) { | |
| 373 // TODO(stoyan): implement. | |
| 374 } | |
| OLD | NEW |